different-ai/openwork · error · ProbeFailure

MCP resource rejected the synthetic access token with HTTP $

Error message

MCP resource rejected the synthetic access token with HTTP ${initializeRawResponse.status}

What it means

During MCP_INITIALIZE, the probe sends an initialize JSON-RPC request with the freshly obtained access token. If the MCP resource server rejects it with 401 or 403, the probe classifies it as AUTH_RESOURCE_VALIDATION: 403 maps to subcode oauth_insufficient_scope and 401 to oauth_wrong_audience. The message embeds the HTTP status: `"MCP resource rejected the synthetic access token with HTTP ${initializeRawResponse.status}"`. This means the token was issued but does not satisfy the resource server's audience or scope requirements.

Source

Thrown at packages/enterprise-mcp-mock-server/src/testing/probe.ts:738

      origin: baseUrl.origin,
    }
    startedAt = Date.now()
    const initializeRawResponse = await fetchStep(mcpUrl, {
        method: "POST",
        headers: rpcHeaders,
        body: JSON.stringify({
          jsonrpc: "2.0",
          id: 1,
          method: "initialize",
          params: {
            protocolVersion: scenario.protocol.version,
            capabilities: {},
            clientInfo: { name: "enterprise-mcp-probe", version: "0.1.0" },
          },
        }),
      }, "MCP_INITIALIZE", overallDeadline)
    if (initializeRawResponse.status === 401 || initializeRawResponse.status === 403) {
      throw new ProbeFailure(
        "AUTH_RESOURCE_VALIDATION",
        initializeRawResponse.status === 403 ? "oauth_insufficient_scope" : "oauth_wrong_audience",
        `MCP resource rejected the synthetic access token with HTTP ${initializeRawResponse.status}`,
      )
    }
    const initializeResponse = await expectOk(initializeRawResponse, "MCP_INITIALIZE")
    sessionId = initializeResponse.headers.get("mcp-session-id") ?? ""
    negotiatedProtocolHeader = initializeResponse.headers.get("mcp-protocol-version") ?? scenario.protocol.version
    const initializeEnvelope = await parseRpc(initializeResponse, "MCP_INITIALIZE")
    if (initializeEnvelope.error) {
      const versionEvidence = z.object({ supportedVersions: z.array(z.string()).min(1) }).safeParse(initializeEnvelope.error.data)
      throw versionEvidence.success || initializeEnvelope.error.message === "Unsupported MCP protocol version"
        ? new ProbeFailure("MCP_VERSION", "mcp_version", initializeEnvelope.error.message)
        : new ProbeFailure("MCP_INITIALIZE", "mcp_initialize", initializeEnvelope.error.message)
    }
    if (initializeEnvelope.id !== 1) {
      throw new ProbeFailure("MCP_INITIALIZE", "mcp_initialize", "Initialize response JSON-RPC id did not match the request")
    }

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Ensure the token request includes resource: <mcpUrl> and that the AS embeds that audience in the access token so the resource server's 401 audience check passes.
  2. For 403, add the required scopes to scenario.oauth.authorizationScopes (or grant them on the AS side) so the token carries the scopes the MCP resource enforces.
  3. Compare the resource server's token-validation config (issuer, audience, required scopes) with the mock AS's issued claims and reconcile the two.

Example fix

// before
authorizeUrl.searchParams.set("scope", "read") // resource requires mcp:tools
// after
authorizeUrl.searchParams.set("scope", "read mcp:tools")
Defensive patterns

Strategy: validation

Validate before calling

// decode the access token JWT and check claims before calling initialize
const claims = JSON.parse(Buffer.from(accessToken.split('.')[1], 'base64url').toString())
if (claims.aud !== mcpUrl) throw new Error(`token aud ${claims.aud} != resource ${mcpUrl}`)
if (!requiredScopes.every(s => (claims.scope ?? '').split(' ').includes(s))) throw new Error('token missing required scopes')

Try / catch

try {
  await probeEnterpriseMcpMockServer(scenario)
} catch (e) {
  if (e instanceof ProbeFailure && e.message.includes('rejected the synthetic access token with HTTP 403')) {
    console.error('insufficient scope: add required scopes to scenario.oauth.authorizationScopes', e)
  } else if (e instanceof ProbeFailure && e.message.includes('with HTTP 401')) {
    console.error('wrong audience: pass resource=<mcpUrl> in the token request', e)
  } else throw e
}

Prevention

When it happens

Trigger: initializeRawResponse.status is 401 or 403 when POSTing the MCP initialize request with `authorization: Bearer ${accessToken}` at probe.ts:734-740.

Common situations: Authorization server issuing tokens with the wrong `aud` (not equal to the MCP URL passed as the `resource` parameter); token missing the scopes the MCP resource requires; resource-server token-validation middleware (audience/scope checks) tightened while the mock AS lagged behind; clock skew making the token seem invalid.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/11517b84fe48c7ef. Report an issue: GitHub.