🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

mcp-tool-guard

Package Overview
Dependencies
Maintainers
1
Versions
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mcp-tool-guard

Wrap an MCP client so a silently-skipped or discarded tool call throws instead of letting the agent report success on nothing.

latest
Source
npmnpm
Version
0.1.0
Version published
Maintainers
1
Created
Source

mcp-tool-guard

An agent calls a tool over MCP, the spawn or transport hiccups, the call never actually runs — and the agent reports "done" anyway, because nothing in the loop threw. That's not hypothetical: it's a reproduced bug in NanoClaw #2968 (a discarded write the agent claimed succeeded), a Cursor forum thread where MCP tool calls silently stopped firing across models, and Codex #31374. A 2026 study of 11,755 agent trajectories found false-success reporting made up 45-78% of failures depending on the benchmark (arXiv:2606.09863).

mcp-tool-guard wraps your MCP client's callTool so a call that returns nothing, times out, or reports isError throws immediately instead of letting execution continue past it. It also keeps a ledger you can check right before the agent reports success, so a spawn failure that swallowed the whole call — not just a bad response — still gets caught.

Install

npm install mcp-tool-guard

Use

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { guard } from "mcp-tool-guard";

const raw = new Client({ name: "my-agent", version: "1.0.0" });
await raw.connect(transport);

const { client, ledger } = guard(raw, { timeoutMs: 15_000 });

// use `client` exactly like you'd use `raw` — same callTool signature
await client.callTool({ name: "write_file", arguments: { path: "out.txt" } });

// right before the agent reports success on this turn:
ledger.assertAllSucceeded(["write_file"]); // throws if it never ran, errored, or timed out

If the write silently didn't happen, assertAllSucceeded throws instead of letting your agent loop tell the user it's done.

Postconditions

A call can return a well-formed, non-error response and still not have done anything — run_migration that reports success on 0 rows changed, say. Pass a postcondition to check the result, not just its shape:

const { client } = guard(raw, {
  postcondition: (toolName, args, result) => {
    if (toolName === "run_migration") {
      return !result.content[0].text.includes("0 rows");
    }
    return true;
  },
});

What it catches

  • Empty/no-content results (server discarded the call or never ran it)
  • Transport errors and rejected promises (spawn/connect failures)
  • isError: true responses
  • Hangs past a configurable timeout
  • Tools that never got called at all, via ledger.assertAllSucceeded(expectedNames)
  • Calls that "succeed" but fail a caller-supplied postcondition

What it does not do

It's a runtime guard around calls you make, not a static analyzer or a security scanner (see mcp-conform for spec-conformance linting instead). It can't infer what a tool call was supposed to do — the postcondition and expectedToolNames are how you tell it. If you need call-order assertions or full session recording, this is a narrower, dependency-free complement to tools like mcp-assert and Agentprobe, not a replacement.

License

MIT

Built autonomously by an AI agent.

Keywords

mcp

FAQs

Package last updated on 14 Jul 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts