different-ai/openwork · error

MCP tool catalog response was incomplete.

Error message

MCP tool catalog response was incomplete.

What it means

useMcpConnectionTools inspects an external MCP connection's tool catalog via a 30s requestJson call. Two separate checks throw this same message: (1) payload is not a record or payload.tools is not an array; (2) the parsed policy is null or any entry of payload.tools fails the isExternalMcpTool shape check. It means the server replied ok but the catalog body does not match the expected { tools: [...], policy: {...} } contract.

Source

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

  };
}

export function useMcpConnectionTools(connectionId: string, enabled: boolean) {
  const { orgId } = useOrgDashboard();
  return useQuery({
    enabled: enabled && Boolean(orgId),
    queryKey: mcpConnectionQueryKeys.tools(orgId, connectionId),
    queryFn: async (): Promise<ExternalMcpToolCatalog> => {
      const { response, payload } = await requestJson(
        `/v1/mcp-connections/${encodeURIComponent(connectionId)}/tools`,
        { headers: getOrgScopeHeaders(requireOrgId(orgId)) },
        30000,
      );
      if (!response.ok) {
        throw getRequestError(payload, response, `Failed to inspect MCP tools (${response.status}).`);
      }
      if (!isRecord(payload) || !Array.isArray(payload.tools)) {
        throw new Error("MCP tool catalog response was incomplete.");
      }
      const policy = parseExternalMcpToolPolicy(payload.policy);
      if (!policy || !payload.tools.every(isExternalMcpTool)) {
        throw new Error("MCP tool catalog response was incomplete.");
      }
      return { tools: payload.tools, policy };
    },
  });
}

export function useUpdateMcpConnectionToolPolicy(connectionId: string) {
  const queryClient = useQueryClient();
  const { orgId } = useOrgDashboard();
  return useMutation({
    mutationFn: async (input: Pick<ExternalMcpToolPolicyView, "allDisabled" | "disabledTools">): Promise<ExternalMcpToolPolicyView> => {
      const { response, payload } = await requestJson(
        `/v1/mcp-connections/${encodeURIComponent(connectionId)}/tool-policy`,
        {

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Log payload and compare each tool against the isExternalMcpTool required fields
  2. Upgrade den-api server to match the dashboard's expected catalog schema (or vice versa)
  3. Check parseExternalMcpToolPolicy against the actual payload.policy shape
  4. If a single malformed tool is the cause, fix or filter it server-side before returning the catalog

Example fix

// before
if (!policy || !payload.tools.every(isExternalMcpTool)) {
  throw new Error("MCP tool catalog response was incomplete.");
}
// after
const badIndex = payload.tools.findIndex((t) => !isExternalMcpTool(t));
if (badIndex >= 0) {
  console.warn("Skipping malformed MCP tool at index", badIndex, payload.tools[badIndex]);
}
const tools = payload.tools.filter(isExternalMcpTool);
if (!policy || tools.length === 0) {
  throw new Error("MCP tool catalog response was incomplete.");
}
return { tools, policy };
Defensive patterns

Strategy: type-guard

Validate before calling

if (!isRecord(payload) || !Array.isArray(payload.tools)) {
  // request a refetch or show a catalog-unavailable state
}

Type guard

function isExternalMcpTool(v: unknown): v is ExternalMcpTool {
  return isRecord(v) && typeof v.name === "string" && isRecord(v.inputSchema);
}

Try / catch

try {
  const { tools, policy } = await refetchCatalog();
} catch (err) {
  if (err.message === "MCP tool catalog response was incomplete.") {
    showCatalogUnavailable(err); // offer retry
  }
}

Prevention

When it happens

Trigger: POST inspect-tools returns 200 with {tools: '...'} or missing tools; a tool object missing required fields (name/inputSchema etc.) failing isExternalMcpTool; payload.policy absent or unparseable by parseExternalMcpToolPolicy.

Common situations: MCP server exposing tools with non-standard metadata; den-api version older than the dashboard expecting new tool fields; policy feature-flag disabled server-side so 'policy' is omitted from the response.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


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