{"record":{"id":"1441dc27d61a2dca","repo":"different-ai/openwork","slug":"provider-authorization","errorCode":"PROVIDER_AUTHORIZATION","errorMessage":"${message} (HTTP 403)","messagePattern":"(.+?) \\(HTTP 403\\)","errorType":"exception","errorClass":"ProbeFailure","httpStatus":403,"severity":"error","filePath":"packages/enterprise-mcp-mock-server/src/testing/probe.ts","lineNumber":811,"sourceCode":"    startedAt = Date.now()\n    const toolNames = new Set<string>()\n    const discoveredTools: MockTool[] = []\n    const cursors = new Set<string>()\n    let cursor: string | undefined\n    let catalogComplete = false\n    for (let page = 0; page < 25; page += 1) {\n      if (cursor) {\n        if (cursors.has(cursor)) throw new ProbeFailure(\"MCP_TOOL_DISCOVERY\", \"mcp_pagination_loop\", \"Tool catalog repeated a cursor\")\n        cursors.add(cursor)\n      }\n      const listRawResponse = await fetchStep(mcpUrl, {\n          method: \"POST\",\n          headers: sessionHeaders,\n          body: JSON.stringify({ jsonrpc: \"2.0\", id: 10 + page, method: \"tools/list\", params: cursor ? { cursor } : {} }),\n        }, \"MCP_TOOL_DISCOVERY\", overallDeadline)\n      if (listRawResponse.status === 403) {\n        const message = await safeHttpErrorMessage(listRawResponse, \"PROVIDER_AUTHORIZATION\", \"provider_per_user_403\")\n        throw new ProbeFailure(\"PROVIDER_AUTHORIZATION\", \"provider_per_user_403\", `${message} (HTTP 403)`)\n      }\n      const listResponse = await expectOk(listRawResponse, \"MCP_TOOL_DISCOVERY\")\n      const envelope = await parseRpc(listResponse, \"MCP_TOOL_DISCOVERY\")\n      if (envelope.id !== 10 + page) {\n        throw new ProbeFailure(\"MCP_TOOL_DISCOVERY\", \"mcp_tools_discovery\", \"tools/list response JSON-RPC id did not match the request\")\n      }\n      if (envelope.error) throw new ProbeFailure(\"MCP_TOOL_DISCOVERY\", \"mcp_tools_discovery\", envelope.error.message)\n      const result = parseAt(\n        toolsListResultSchema,\n        envelope.result,\n        \"MCP_TOOL_DISCOVERY\",\n        \"mcp_tools_discovery\",\n        \"tools/list result did not match the required shape\",\n      )\n      if (result.nextCursor && cursors.has(result.nextCursor)) {\n        throw new ProbeFailure(\"MCP_TOOL_DISCOVERY\", \"mcp_pagination_loop\", \"Tool catalog repeated a cursor\")\n      }\n      for (const rawTool of result.tools) {","sourceCodeStart":793,"sourceCodeEnd":829,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/packages/enterprise-mcp-mock-server/src/testing/probe.ts#L793-L829","documentation":"Thrown when a tools/list request is answered with HTTP 403, wrapping the server's error message (read via safeHttpErrorMessage) as `${message} (HTTP 403)` under the PROVIDER_AUTHORIZATION code with hint provider_per_user_403. This distinguishes per-user authorization failures during tool discovery from protocol-level discovery errors.","triggerScenarios":"The synthetic access token used by the probe lacks the scope/entitlement required to list tools on the target MCP resource, and the server responds 403 to the tools/list POST.","commonSituations":"OAuth token issued without the required tool-catalog scope; per-user entitlement not granted to the probe's test account; admin disabled tool access for the user; wrong audience/resource indicator so authorization falls through to a blanket denial.","solutions":["Grant the probe's identity the scopes/entitlements needed for tools/list","Regenerate the synthetic access token with the correct scopes and audience","Verify server-side authorization policy for per-user tool access","Check token audience/resource claims match what the MCP resource expects"],"exampleFix":"// before\ntoken = issueToken({ scopes: [\"mcp:read\"] })\n// after\ntoken = issueToken({ scopes: [\"mcp:read\", \"mcp:tools:list\"] })","handlingStrategy":"try-catch","validationCode":"const requiredScopes = [\"mcp:tools:list\"]\nconst missing = requiredScopes.filter((s) => !tokenScopes.includes(s))\nif (missing.length) throw new Error(`token missing scopes: ${missing.join(\",\")}`)","typeGuard":"null","tryCatchPattern":"try {\n  await probeEnterpriseMcpMockServer(scenario)\n} catch (e) {\n  if (e instanceof ProbeFailure && e.code === \"PROVIDER_AUTHORIZATION\") {\n    console.error(\"Per-user authorization denied (403):\", e.message)\n  }\n}","preventionTips":["Issue probe tokens with the scopes/entitlements the resource requires","Verify token audience/resource indicator before running the probe","Grant the test account tool-catalog access in admin config"],"tags":["authorization","http-403","oauth-scope","mcp"],"backgroundTag":"http-403-forbidden","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}