different-ai/openwork · error · ProbeFailure

MCP_TOOL_EXECUTION

MCP_TOOL_EXECUTION

Error message

No suitable tool was available for the requested probe

What it means

When the probe should execute a tool call, it auto-selects the first discovered tool of the required kind: a mutation tool for commit-then-disconnect faults, otherwise a read tool. If the discovered catalog contains no tool of that kind, the probe throws MCP_TOOL_EXECUTION / mcp_tool.

Source

Thrown at packages/enterprise-mcp-mock-server/src/testing/probe.ts:870

      if (missing.length > 0 || unexpected.length > 0) {
        throw new ProbeFailure(
          "MCP_TOOL_DISCOVERY",
          "profile_fixture_mismatch",
          `Catalog does not match pinned profile fixture ${profile.fixtureVersion}`,
        )
      }
    }
    recordPassed(phases, "MCP_TOOL_DISCOVERY", startedAt, `Retrieved ${toolNames.size} unique tools with bounded pagination`)

    const fault = activeFault
    const shouldCall = options.callTool !== undefined || mode === "safe-read" || fault?.phase === "PROVIDER_AUTHORIZATION" || fault?.phase === "PROVIDER_EXECUTION"
    if (shouldCall) {
      const selected = options.callTool
        ? { name: options.callTool.name, arguments: options.callTool.arguments }
        : (() => {
            const mutationRequired = fault?.effect === "commit-then-disconnect"
            const tool = discoveredTools.find((candidate) => candidate.kind === (mutationRequired ? "mutation" : "read"))
            if (!tool) throw new ProbeFailure("MCP_TOOL_EXECUTION", "mcp_tool", "No suitable tool was available for the requested probe")
            return { name: tool.name, arguments: defaultArguments(tool.inputSchema) }
          })()
      const selectedTool = profile.tools.find((tool) => tool.name === selected.name)
      if (mode === "safe-read" && selectedTool?.kind !== "read") {
        throw new ProbeFailure("CONFIGURATION", "configuration", "safe-read mode accepts only a declared read-only tool")
      }
      startedAt = Date.now()
      let toolResponse: Response
      try {
        toolResponse = await fetchStep(mcpUrl, {
          method: "POST",
          headers: sessionHeaders,
          body: JSON.stringify({ jsonrpc: "2.0", id: 100, method: "tools/call", params: selected }),
        }, "MCP_TOOL_EXECUTION", overallDeadline)
      } catch {
        throw new ProbeFailure(
          selectedTool?.kind === "mutation" ? "PROVIDER_EXECUTION" : "MCP_TOOL_EXECUTION",
          selectedTool?.kind === "mutation" ? "mutation_indeterminate" : "mcp_tool",

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Add a tool of the required kind (mutation for commit-then-disconnect, otherwise read) to the profile fixture.
  2. Pass options.callTool explicitly with a name and arguments matching a registered tool.
  3. Fix earlier discovery failures (invalid schemas) so tools actually land in discoveredTools.
  4. Verify the server's advertised tool kinds match the fixture kinds.

Example fix

// before: read-only profile used with a commit-then-disconnect fault
const profile = { tools: [{ name: "get_docs", kind: "read" }] };
// after: include a mutation tool
const profile = { tools: [{ name: "get_docs", kind: "read" }, { name: "create_doc", kind: "mutation" }] };
Defensive patterns

Strategy: validation

Validate before calling

const mutationRequired = fault?.effect === "commit-then-disconnect";
const kindNeeded = mutationRequired ? "mutation" : "read";
if (!options.callTool && !profile.tools.some((t) => t.kind === kindNeeded)) {
  throw new Error(`Profile lacks a '${kindNeeded}' tool required by this probe mode`);
}

Try / catch

try {
  await probeEnterpriseMcpMockServer(options);
} catch (e) {
  if (e instanceof ProbeFailure && e.code === "MCP_TOOL_EXECUTION" && e.reason === "mcp_tool") {
    // add the required tool kind to the fixture or pass options.callTool
  } else throw e;
}

Prevention

When it happens

Trigger: probeEnterpriseMcpMockServer with shouldCall true, no options.callTool override, and discoveredTools contains no candidate whose kind matches the requested ("mutation" for commit-then-disconnect faults, otherwise "read").

Common situations: Profile only defines read tools while the fault scenario needs a mutation tool; all tools failed schema validation earlier so discoveredTools is empty; custom callTool name not present in the profile.

Related errors


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