Skip to content

fix(den-mcp): capture allowlisted provider evidence on tool-error results#2757

Merged
benjaminshafii merged 1 commit into
devfrom
fix/mcp-provider-error-evidence
Jul 14, 2026
Merged

fix(den-mcp): capture allowlisted provider evidence on tool-error results#2757
benjaminshafii merged 1 commit into
devfrom
fix/mcp-provider-error-evidence

Conversation

@benjaminshafii

Copy link
Copy Markdown
Member

Summary

When an external MCP provider returns HTTP 200 with a tool result marked isError: true (exactly how ServiceNow's MCP tools fail), Den classifies it via ExternalMcpDiagnosticTracker.providerToolError(), which only read result.structuredContent.{providerStatus, category, requestId}. Real providers put the error in text content with no structuredContent, so classification always fell to the generic MCP_PROVIDER_TOOL_ERROR and the provider's actual error (a 403 ACL denial, a Redis cluster failure, SyntaxError: Empty JSON string) was discarded — absent from the diagnostic, absent from server logs, invisible to support.

This came directly out of an enterprise ServiceNow debugging session where the useful provider evidence was only ever visible through a direct (non-Cloud) connection, making the Cloud path undiagnosable.

What changed

  • providerToolError() now extracts bounded, allowlisted evidence from isError tool results:
    • providerStatus (400–599): from structuredContent, from top-level JSON fields (status/statusCode/httpStatus) in parsed text content, or from an explicit leading pattern (HTTP 403, 403 Forbidden). Mid-sentence numbers (e.g. "we found 403 records") never classify.
    • providerCode: identifier-shaped code/error JSON fields (max 64 chars, safe charset).
    • providerRequestId: existing structuredContent path plus requestId/request_id/transactionId/transaction_id JSON fields (same safeNativeToken validation).
    • payloadBytes: UTF-8 size of the serialized content array.
  • Derived text-level 403 → MCP_PROVIDER_HTTP_403 / PROVIDER_AUTHORIZATION / provider_admin (same classification as the structured path); 429 → throttled; everything else stays MCP_PROVIDER_TOOL_ERROR.
  • Restricted raw evidence: a sanitized excerpt (control chars stripped, whitespace collapsed, max 512 chars) is logged server-side as external_mcp_provider_tool_evidence keyed by the diagnostic referenceId — retrievable by operators, never included in any model-visible field.
  • The model-visible diagnostic message now appends scalars only, e.g. Provider evidence: provider status 403, code insufficient_acl.
  • Public schema mirrors kept in sync: externalMcpDiagnosticOutputSchema (src/mcp/agent.ts) and externalMcpDiagnosticSchema (src/routes/org/mcp-connections.ts) gain optional providerStatus, providerCode, payloadBytes.

Security boundaries

  • Raw provider text never reaches the model or API responses; only validated scalars do.
  • All identifiers pass the existing safe-token patterns; the excerpt lives only in server logs behind the diagnostic reference.

Testing

  • pnpm --filter @openwork-ee/den-api exec bun test test/external-mcp-diagnostics.test.ts57 pass, 0 fail (new coverage: ServiceNow-style JSON 403 with requestId, Redis-style plain text, mid-sentence 403 negative case, empty content, evidence-log capture; existing structuredContent tests unchanged).
  • pnpm --filter @openwork-ee/den-api exec bun test test/external-capabilities-search-divergence.test.ts17 pass, 0 fail.
  • pnpm --filter @openwork-ee/den-api exec tsc -p . --noEmit — clean.
  • Results were reproduced independently by a second run after implementation.

No video/fraimz attached: this is a Den API behavior change with no UI surface, covered by unit tests above. Reviewer repro: point an external MCP connection at a server whose tool returns { isError: true, content: [{ type: "text", text: '{"status":403,"error":"insufficient_acl","requestId":"TXN-1"}' }] }, call it via execute_capability, and observe (1) the diagnostic carrying MCP_PROVIDER_HTTP_403, providerStatus: 403, providerCode, providerRequestId, payloadBytes, and (2) the external_mcp_provider_tool_evidence log line with the sanitized excerpt and matching referenceId.

Notes

@vercel

vercel Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
openwork-app Ready Ready Preview, Comment Jul 14, 2026 8:52pm
openwork-den Ready Ready Preview, Comment Jul 14, 2026 8:52pm
openwork-den-worker-proxy Ready Ready Preview, Comment Jul 14, 2026 8:52pm
openwork-landing Ready Ready Preview, Comment, Open in v0 Jul 14, 2026 8:52pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
openwork-diagnostics Skipped Skipped Jul 14, 2026 8:52pm

@benjaminshafii
benjaminshafii merged commit fa67324 into dev Jul 14, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant