different-ai/openwork · error · ExternalMcpToolRunError

Failed to run MCP tool (${response.status}).

Error message

Failed to run MCP tool (${response.status}).

What it means

When a run-tool request fails with any non-403-policy status, the hook builds the message via getRequestError(payload, response, `Failed to run MCP tool (${response.status}).`) and wraps it in ExternalMcpToolRunError together with any inspection and diagnostic payloads the server attached. The template string is only the fallback — the real message comes from the response body — so this error represents a concrete server-side tool execution failure with attribution data.

Source

Thrown at ee/apps/den-web/app/(den)/dashboard/_components/mcp-connections-data.tsx:373

      const { response, payload } = await requestJson(
        `/v1/mcp-connections/${encodeURIComponent(connectionId)}/tools/call`,
        {
          method: "POST",
          headers: getOrgScopeHeaders(requireOrgId(orgId)),
          body: JSON.stringify(input),
        },
        RUN_TOOL_REQUEST_TIMEOUT_MS,
      );
      if (!response.ok) {
        if (response.status === 403 && isRecord(payload) && payload.error === "policy_blocked") {
          throw new ExternalMcpToolPolicyBlockedError(
            typeof payload.message === "string" ? payload.message : "This tool is disabled by organization policy.",
            typeof payload.disabledBy === "string" ? payload.disabledBy : null,
            typeof payload.disabledAt === "string" ? payload.disabledAt : null,
          );
        }
        const requestError = getRequestError(payload, response, `Failed to run MCP tool (${response.status}).`);
        throw new ExternalMcpToolRunError(
          requestError.message,
          isRecord(payload) ? parseToolCallInspection(payload.inspection) : null,
          isRecord(payload) ? parseExternalMcpDiagnostic(payload.diagnostic) : null,
        );
      }
      if (
        !isRecord(payload)
        || typeof payload.referenceId !== "string"
        || typeof payload.durationMs !== "number"
        || !("result" in payload)
      ) {
        throw new Error("MCP tool result was incomplete.");
      }
      return {
        referenceId: payload.referenceId,
        durationMs: payload.durationMs,
        result: payload.result,
        // A missing or unparseable inspection must not fail a tool run that

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Read error.inspection and error.diagnostic on ExternalMcpToolRunError for the server-side failure attribution
  2. Check the connection's credentialHealth; re-authorize if reconnect_required
  3. Validate the tool input against the catalog's inputSchema before calling
  4. Retry only after fixing the underlying cause (connectivity, credentials, or input)

Example fix

// before
await runTool({ tool: name, input });
// after
try {
  await runTool({ tool: name, input });
} catch (err) {
  if (err instanceof ExternalMcpToolRunError) {
    console.error("Tool run failed:", err.message, err.inspection, err.diagnostic);
  }
  throw err;
}
Defensive patterns

Strategy: try-catch

Validate before calling

function isValidToolInput(tool: ExternalMcpTool, input: unknown): boolean {
  // validate against tool.inputSchema (JSON Schema) before the run call
  return validateJsonSchema(input, tool.inputSchema);
}

Type guard

function isToolRunError(e: unknown): e is ExternalMcpToolRunError {
  return e instanceof ExternalMcpToolRunError;
}

Try / catch

try {
  await runTool({ tool, input });
} catch (err) {
  if (err instanceof ExternalMcpToolRunError) {
    console.error(err.message, { inspection: err.inspection, diagnostic: err.diagnostic });
    // route 401/403 to reconnect flow, 5xx to retry-after-check
  }
}

Prevention

When it happens

Trigger: MCP server unreachable or returned an error; auth to the external MCP failed (401/403 non-policy); tool input rejected (4xx); upstream timeout (504) within RUN_TOOL_REQUEST_TIMEOUT_MS; any non-ok status other than the 403 policy_blocked case.

Common situations: Expired OAuth credentials on the connection (needs reconnect); wrong tool input schema; external MCP server down or DNS/network failure from the den server; per-member credential mode where the calling member never connected.

Related errors


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