{"record":{"id":"0eda8092509d7d17","repo":"different-ai/openwork","slug":"mcp-provider-http-403","errorCode":"MCP_PROVIDER_HTTP_403","errorMessage":"MCP_PROVIDER_HTTP_403","messagePattern":"MCP_PROVIDER_HTTP_403","errorType":"error_code","errorClass":null,"httpStatus":403,"severity":"error","filePath":"ee/apps/den-api/src/capability-sources/external-mcp-diagnostics.ts","lineNumber":965,"sourceCode":"    // enterprise providers: only a Bearer/insufficient_scope challenge means\n    // token validation; an ordinary tools/* 403 is usually an ACL/role denial.\n    if (status === 401 || input.bearerChallenge || input.insufficientScope) {\n      return {\n        phase: \"AUTH_RESOURCE_VALIDATION\",\n        category: input.insufficientScope ? \"oauth_insufficient_scope\" : \"oauth_resource_rejected\",\n        code: input.insufficientScope ? \"MCP_OAUTH_INSUFFICIENT_SCOPE\" : `MCP_OAUTH_HTTP_${status}`,\n        retryable: status === 401,\n        actionOwner: input.insufficientScope ? \"organization_admin\" : \"member\",\n        operatorAction: input.insufficientScope\n          ? \"Grant the provider scopes required by this MCP resource and reconnect.\"\n          : \"Reconnect the provider account and verify token audience, tenant, and resource binding.\",\n      }\n    }\n    if (status === 403 && (phase === \"MCP_TOOL_DISCOVERY\" || phase === \"MCP_TOOL_EXECUTION\")) {\n      return {\n        phase: \"PROVIDER_AUTHORIZATION\",\n        category: \"provider_policy_denied\",\n        code: \"MCP_PROVIDER_HTTP_403\",\n        retryable: false,\n        actionOwner: \"provider_admin\",\n        operatorAction: \"Grant the provider role, ACL, or application permission required for this operation.\",\n      }\n    }\n  }\n  if (status >= 400) {\n    return {\n      phase,\n      category: \"http_failure\",\n      code: `MCP_HTTP_${status}`,\n      retryable: status === 408,\n      actionOwner: \"provider_admin\",\n      operatorAction: \"Inspect provider and proxy logs for the failing HTTP request using the diagnostic reference.\",\n    }\n  }\n  if (phase.startsWith(\"MCP_\") && input.contentType === \"text/html\") {\n    return {","sourceCodeStart":947,"sourceCodeEnd":983,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-api/src/capability-sources/external-mcp-diagnostics.ts#L947-L983","documentation":"MCP_PROVIDER_HTTP_403 is raised when an MCP tool discovery or tool execution request receives HTTP 403 from the provider. It is classified as PROVIDER_AUTHORIZATION / provider_policy_denied and is non-retryable with the provider admin as owner: authentication passed far enough to reach an authorization check, but the provider's role, ACL, or application permission policy denies this specific operation.","triggerScenarios":"Calling tools/list (MCP_TOOL_DISCOVERY) or a tool invocation (MCP_TOOL_EXECUTION) against a provider that returns 403 — e.g. the provider token authenticates but the account lacks the role/permission for that tool, or the app is not allow-listed for that operation.","commonSituations":"Provider API key issued for a read-only role used for a write tool; provider-side ACL changes removing a service account's access; a new tool added on the provider that the existing integration app is not permissioned for; expired or rotated grants on the provider tenant.","solutions":["Grant the provider role, ACL, or application permission required for this specific discovery/execution operation.","Confirm which identity the gateway presents to the provider and check its permissions in the provider's admin console.","Re-run the operation after the grant is applied; retries without a permission change will keep failing with 403."],"exampleFix":"// before: token scoped to viewer role calling a mutating tool\nawait client.callTool({ name: 'delete-record', arguments })\n// after: provider admin grants editor role / tool permission, then it succeeds\nawait client.callTool({ name: 'delete-record', arguments })","handlingStrategy":"try-catch","validationCode":"// fail fast: check the identity's granted scopes against the tool's requirement before executing\nif (!grantedScovers.includes(tool.requiredPermission)) {\n  throw new Error(`missing provider permission ${tool.requiredPermission}`)\n}","typeGuard":"function isProviderAuthzDenied(d: { code: string }): boolean {\n  return d.code === 'MCP_PROVIDER_HTTP_403'\n}","tryCatchPattern":"try {\n  return await client.callTool(req)\n} catch (e) {\n  if (isProviderAuthzDenied(e.diagnostic)) {\n    // non-retryable: escalate to provider admin for role/ACL grant\n    escalateToProviderAdmin({ operation: req.name, diagnostic: e.diagnostic })\n  }\n  throw e\n}","preventionTips":["Provision the provider service account with the roles/ACLs for every tool the integration exposes.","Re-audit permissions whenever new tools are enabled on the provider side.","Test discovery (tools/list) and one representative execution after any provider access change."],"tags":["mcp","http-403","authorization","permissions","non-retryable"],"backgroundTag":"http-403-forbidden","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}