ChatGPT silently passes data between connectors and the bounty wants an ID card
Security: Follow-up tool call silently routed to a different connector In a conversation with two connectors enabled, ChatGPT made a follow-up tool call through a different connector than the one that started the flow. The user was not told about the switch. The call carried a one-time confirmation code from the first connector’s widget. Setup - One ChatGPT account with two connectors enabled that point to the same MCP server. - Connector A is a custom connector added in developer mode. - Connector B is the published app from the app directory. - Each connector has its own OAuth client, so the server sees two separate clients for the same user. Steps 1. In a conversation with both connectors enabled, ask for an action that needs the user’s confirmation. 2. The model calls a confirmation tool through connector A. Its MCP Apps widget shows the change. The user confirms, and the widget sends a one-time confirmation code to the conversation via ui/message. 3. The model makes the follow-up call with that code, but silently through connector B instead of connector A. Result The server rejects the call, because the code is bound to connector A’s OAuth client. Every attempt in that conversation failed. In the attempt we logged in detail, the code was confirmed through connector A and redeemed through connector B. Nothing told the user that the follow-up call went to another connector. Why this is a problem? The user starts the flow with connector A and confirms in connector A’s widget. The follow-up call then goes to connector B without the user knowing, and it carries a value produced in connector A’s widget. Connectors are separate apps with separate OAuth grants, so this silent switch passes data from one app to another without the user’s consent. Here both connectors came from the same provider, so our server-side binding stopped the call. With a third-party connector enabled in the same conversation, we expect the same silent switch to pass one app’s data to another provider if the server URL is shared. A shared MCP URL is not enough to treat two connectors as one. Behind the same URL, a server can act for different parties depending on the OAuth client, for example separate tenants, partners or environments. The boundary is the connector with its own OAuth client, not the URL. Expected behavior Tool calls are matched to a connector by its OAuth client, not by the MCP server URL. A follow-up call goes through the same connector and OAuth client that started the flow. Hi AldiPower, Thank you for documenting this so clearly. I think you’ve identified an important distinction between tool selection and authorization continuity that deserves attention. What makes this case particularly interesting is that the server-side protection appears to have worked: the confirmation code was bound to Connector A’s OAuth client, so the attempt to redeem it through Connector B was rejected. However, that rejection does not eliminate the underlying concern. The follow-up call was apparently routed through a different connector without informing the user. I believe this points to a broader architectural requirement: A multi-step tool workflow should preserve the identity of the connector and its authorization context throughout the operation, rather than relying solely on the MCP endpoint or tool name. A robust approach could include: Binding pending operations and confirmations to the originating connector and its verified OAuth grant. Binding pending operations and confirmations to the originating connector and its verified OAuth grant. Preventing automatic connector switching during confirmation or execution. Preventing automatic connector switching during confirmation or execution. Requiring explicit user approval if changing the connector becomes necessary. Requiring explicit user approval if changing the connector becomes necessary. Validating short-lived confirmation codes against the intended operation, client identity, and authorization context on the server. Validating short-lived confirmation codes against the intended operation, client identity, and authorization context on the server. Recording connector-routing decisions in diagnostic traces, without exposing credentials or sensitive confirmation values. Recording connector-routing decisions in diagnostic traces, without exposing credentials or sensitive confirmation values. Your example also highlights an important separation of responsibilities: the MCP server must enforce authorization, but the client-side orchestration layer should preserve the connector selected for the workflow. One question that could help narrow down the root cause: Do your traces indicate whether the connector identity changed during confirmation handling or only when the model selected the follow-up tool call? Since this may involve an authorization boundary, I would also encourage submitting the technical evidence through OpenAI’s coordinated vulnerability disclosure process, rather than relying solely on a public discussion. review this for more info Relevant documentation: OpenAI — MCP Authentication OpenAI — MCP Authentication OpenAI — Coordinated Vulnerability Disclosure OpenAI — Coordinated Vulnerability Disclosure Thanks again for sharing a concrete, reproducible case. This is exactly the kind of boundary that deserves careful investigation. Hi, I tried to disclose this via their bug bounty program. But they require to give my personal data like biometric identification and ID card up front even to another party I do nothing about, before I can submit the report. This is nothing I am willing to do. Related topics