Skip to main content
Glama
magves56

attestive-mcp-example

by magves56

attestive-mcp-example

A thin wrapper that hooks Attestive's AuditClient.record() into an MCP server's tool-call lifecycle — the natural interception point on the server side of MCP, the same way attestive-agent-sdk-example's PostToolUse hook is the natural interception point on the client side. One line — auditMcpServer(server, auditClient) — and every tool registered on that server from then on gets its calls recorded automatically, no matter which MCP host (Claude Desktop, Claude Code, or anything else speaking MCP) ends up calling it.

This wrapper is fail-open, stated plainly up front: the real tool runs first; logging happens after, not atomically with it; and if logging itself fails, that failure is swallowed to stderr while the tool's actual result still goes back to the caller unchanged. See "Fail-open, stated plainly" below for exactly what that means and why.

Extracted from Attestive's main repo (git subtree split) so it's readable and runnable on its own. The hosted product's implementation lives in a private repository — this example is complete and independently runnable on its own, not a partial extract of something you'd need private access to fully evaluate; see "Why this includes a copy of AuditClient" below for what that means for attestive-client.ts specifically.

  • attestive-audit.ts — the reusable wrapper. withAudit() wraps one tool callback; auditMcpServer() wraps server.registerTool so every future registration gets withAudit() applied automatically. This is the file to read if you're integrating this into your own server — everything else here is example scaffolding.

  • demo-server.ts — a real MCP server with two tools a support agent might call (check_order_status, a read-only lookup; issue_refund, an actual decision with a real approve/deny outcome), both audited via one call to auditMcpServer(). Runs on stdio, exactly like any MCP server a real host spawns.

  • run-example.ts — the real thing: spawns demo-server.ts as an actual subprocess, connects a real MCP Client over StdioClientTransport, calls both tools through the real MCP protocol, then exports and verifies the resulting chain with the actual published attestive-verify package.

  • smoke-test.ts — exercises withAudit() directly against fake tool callbacks (both MCP callback shapes, plus a callback that throws), no MCP server or subprocess involved. Useful for iterating on the wrapper itself.

  • attestive-client.ts — a self-contained copy of AuditClient and the hash-chain/framework-citation logic it depends on, so this repo runs on its own with nothing but npm install. See its own header comment, and "Why this includes a copy of AuditClient" below.

Steps: npm install → first verified logged decision

npm install
npm run smoke-test    # proves the wrapping logic works in isolation
npm run start         # the real thing -- spawns a real MCP server, calls real tools, verifies the chain

Neither command needs any credentials — MCP tool-calling doesn't require an LLM in the loop to demonstrate, unlike the Claude Agent SDK example, which needs ANTHROPIC_API_KEY or a Claude Code login to run the live agent path. That's a real difference worth knowing if you're choosing which example to try first: this one you can fully verify end to end with nothing but Node installed.

Related MCP server: GoLogX (logx-mcp)

Honest timing

Measured against this repo, standalone, on a clean install:

Step

Time

npm install (97 packages, cold)

~9s

npm run smoke-test

instant; proves withAudit()record()exportEvidence()verifyChain(), including a simulated thrown error

npx tsc --noEmit against the installed SDK's real .d.ts

clean, no errors

npm run start — spawn a real subprocess, connect a real MCP Client, call two real tools through the real protocol, export, verify

~2.4s wall clock, all in

Total, start to a verified logged decision: under 15 seconds, no external accounts needed.

What "real running MCP server" means here

run-example.ts doesn't mock anything: demo-server.ts is spawned as a genuine child process (node demo-server.ts, via the SDK's own StdioClientTransport, which is exactly how Claude Desktop and Claude Code launch MCP servers), and every tool call crosses a real stdio pipe as real JSON-RPC messages, dispatched by the real @modelcontextprotocol/sdk McpServer. The one piece of demo-only plumbing is _export_chain, a third tool registered before auditMcpServer() runs (so it isn't itself audited — exporting a chain isn't a decision) purely so run-example.ts has a way to read back what the subprocess recorded in its own memory, the same way a real integrator's own tooling would need to reach into wherever they point ChainStore.

Fail-open, stated plainly

Look at withAudit() in attestive-audit.ts and this is exactly what it does — this section just says it out loud instead of leaving it to be discovered by reading the source:

  1. The real tool handler runs first. Its result (or thrown error) is captured, but nothing about the audit trail has happened yet.

  2. After the handler has already run, withAudit() calls auditClient.record() to log the decision.

  3. If that record() call itself fails — the backing ChainStore is unreachable, a database outage, a bug — the failure is caught and printed to stderr. It is not retried, not queued, not surfaced to the MCP client in any way.

  4. Either way, the tool's original result (or error) is what the MCP client receives. A logging failure never blocks, delays, or changes the underlying action.

The consequence: this wrapper does not guarantee that every tool call gets captured, only that calls which are successfully captured are recorded faithfully. If record() fails, the refund still gets issued, the order still gets updated — the action completes normally — but no decision record exists for it, and nothing tells you that gap happened unless you're watching that stderr output yourself. This is a deliberate trade-off, not an oversight: the alternative (blocking or failing a real action because logging hiccupped) is usually worse for whatever the tool actually does. But it means audit-trail completeness depends on your ChainStore staying up, not just on this wrapper being installed — verified directly in smoke-test.ts's thrown-error case, which exercises exactly this failure mode.

Coverage and limits, stated plainly

  • Only the current, recommended registerTool() API is wrapped by auditMcpServer(). The deprecated .tool() overloads are not intercepted automatically — migrate to registerTool() first (the MCP SDK already recommends this regardless of auditing), or call withAudit() directly around a .tool() callback if you can't migrate yet: server.tool("name", schema, withAudit("name", auditClient, handler)).

  • The default agentId is the MCP transport's sessionId if one is available (Streamable HTTP provides this), falling back to "mcp-server" otherwise. Stdio transports are typically one server process per session already, so one chain per process is a reasonable default there — but for a multi-tenant server where sessionId alone isn't the right audit boundary, pass agentId: (extra) => yourOwnId(extra) to auditMcpServer().

  • The default decisionType is "automated_decision" — the same value the Claude Agent SDK example uses, which suggestControlsForDecisionType() maps to human-oversight and record-keeping citations. Pass decisionType to auditMcpServer() (or per-tool to withAudit()) if a more specific type fits your tools better.

  • See "Fail-open, stated plainly" above for how audit-logging failures are handled — the short version is: swallowed to stderr, never allowed to change what the MCP client receives, which also means never guaranteed to actually get recorded.

Why this includes a copy of AuditClient

attestive-client.ts in this repo is a deliberate copy of AuditClient, not an import of it — AuditClient isn't published as an installable package yet (unlike attestive-verify, which this example genuinely depends on via npm install), and the monorepo paths it lives at only resolve inside that monorepo's own layout. The hosted product's implementation lives in a private repository, so there's no public copy to import from or link to instead — this example is complete and independently runnable on its own, not a partial extract of something else. The monorepo guards the two from silently drifting apart with a cross-check test that isn't (and can't be) included here — see attestive-client.ts's own header comment.

Swapping in the real backend

This example uses InMemoryChainStore — the point is proving the MCP integration works, not standing up a database. For a real deployment, swap in a persistent ChainStore implementation (see attestive-client.ts's ChainStore interface — the hosted product backs its own chain with Postgres, in a private repository not included here), and pass a real organizationId/apiKey to AuditClient instead of the "local-demo" placeholder used here.

License

MIT — see LICENSE.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    C
    maintenance
    Security gateway that wraps any MCP server with per-tool policies, approval gates, and optional Ed25519-signed decision receipts. Shadow mode logs every tool call without blocking; enforce mode applies block, rate-limit, and minimum-tier rules. Receipts are independently verifiable offline with no accounts needed.
    5
    693
    10
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Tamper-evident audit logging for AI agents. Append-only, hash-chained, optionally Ed25519-signed log. The MCP server lets an agent keep and verify a record of what it actually did.
    7
    MIT
  • F
    license
    A
    quality
    C
    maintenance
    MCP server that auto-emits tamper-evident receipts for every tool call, enabling EU AI Act Article 12 compliance with signed, chain-linked receipts.
    1
  • -
    license
    Not graded
    quality
    Not graded
    maintenance
    An MCP server that validates tool calls against JSON Schema, performs deterministic repair, redacts secrets, and maintains a hash-chained audit ledger.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/magves56/attestive-mcp-example'

If you have feedback or need assistance with the MCP directory API, please join our Discord server