Skip to content

Commit 5a2a032

Browse files
authored
Point MCP sign-ins with no organization at Executor v2 (#2494)
1 parent 3cf6e54 commit 5a2a032

1 file changed

Lines changed: 14 additions & 2 deletions

File tree

‎apps/cloud/src/mcp/auth-provider.ts‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
// (an organization the mirror has never seen). Either is TRANSIENT, not
1717
// evidence the org is gone, and must NOT reach the Forbidden/destroy
1818
// path below.
19-
// - no org / revoked org -> Forbidden ("No organization in session …", -32001).
19+
// - no org selected -> Forbidden (connect Executor v2's MCP URL, -32001).
20+
// - revoked org -> Forbidden ("No organization in session …", -32001).
2021
// This requires a POSITIVE determination (the lookup SUCCEEDED and the org
2122
// is absent), never a failed lookup. Because authenticate reads the
2223
// mcp-session-id header to do the membership check, the envelope's
@@ -74,6 +75,17 @@ const TOOLKIT_PROTECTED_RESOURCE_METADATA_PATH = `${PROTECTED_RESOURCE_METADATA_
7475

7576
const NO_ORGANIZATION_MESSAGE = "No organization in session — log in via the web app first";
7677

78+
/** Executor v2's MCP endpoint, the one to connect instead of this server. */
79+
const V2_MCP_URL = "https://mcp.executor.sh/mcp";
80+
81+
// A token that names no organization, on a URL that names none either. New
82+
// sign-ups happen on Executor v2, so nearly every such account was created by
83+
// an MCP client's sign-in here and has no v1 organization at all: the fix is
84+
// to connect v2's MCP server. The rarer account that joined a v1 organization
85+
// after signing in holds a token minted before that membership; signing in
86+
// again gives it one that names the organization.
87+
const NO_ORGANIZATION_SELECTED_MESSAGE = `Executor has moved. This account has no organization on executor.sh/mcp: connect ${V2_MCP_URL} instead. If you belong to an organization on executor.sh, reconnect this server and sign in again to choose it.`;
88+
7789
// A transient failure during the membership lookup must NOT masquerade as
7890
// "org revoked". The lookup reads the local mirror, so in steady state its
7991
// only failure is the database; the one path that still asks WorkOS (an
@@ -201,7 +213,7 @@ export const cloudMcpAuthProviderLayer: Layer.Layer<
201213
const organizationSelector = mcpOrganizationFromRequest(request) ?? token.organizationId;
202214
if (!organizationSelector) {
203215
yield* annotateMcpRequest(request, { token, parseBody });
204-
return forbidden(NO_ORGANIZATION_MESSAGE, -32001);
216+
return forbidden(NO_ORGANIZATION_SELECTED_MESSAGE, -32001);
205217
}
206218

207219
// Capture success-vs-failure explicitly instead of collapsing both into

0 commit comments

Comments
 (0)