DEV Community

willamhou
willamhou

Posted on

Is that MCP request actually from your AI agent

Is that MCP request actually from your AI agent?

Last week we open-sourced Signet — cryptographic signing for every AI agent tool call.

Someone asked a good question: the agent signs, but does the server verify?

Fair point. v0.1 was one-sided. The agent signed every request, but the server didn't check. Like mailing a signed contract that nobody verifies on the other end. Better than nothing, but the trust chain is broken.

This week we closed the loop.

From one-sided to bilateral

v0.1  Agent signs (one-sided)
      → proves the agent sent the request

v0.2  Compound receipt (request + response bound)
      → proves the request and response are paired

v0.3  Server verification
      → server can verify "this request was signed by a specific agent"

v0.4  Bilateral co-signing
      → agent signs the request, server signs the response, both hold proof
Enter fullscreen mode Exit fullscreen mode

Server verification: 3 lines

Add this to your MCP server handler:

import { verifyRequest } from "@signet-auth/mcp-server";

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const v = verifyRequest(request, {
    trustedKeys: ["ed25519:..."],
    maxAge: 300,
  });
  if (!v.ok) return { content: [{ type: "text", text: v.error }], isError: true };

  // verified — proceed
  console.log(`Request from: ${v.signerName}`);
});
Enter fullscreen mode Exit fullscreen mode

One function. Six checks: signature validity, trusted key list, freshness window (anti-replay), target binding (anti-cross-server), tool name match, params match. ~50μs.

Bilateral co-signing: 3 more lines

After verifying and processing the request, the server signs the response:

import { signResponse } from "@signet-auth/mcp-server";

const bilateral = signResponse(request, response, {
  serverKey: process.env.SIGNET_SERVER_KEY,
  serverName: "my-server",
});
Enter fullscreen mode Exit fullscreen mode

This produces a BilateralReceipt:

{
  "v": 3,
  "agent_receipt": {
    "v": 1,
    "action": { "tool": "create_issue", "params_hash": "sha256:..." },
    "signer": { "pubkey": "ed25519:...", "name": "deploy-bot" },
    "sig": "ed25519:..."
  },
  "response": { "content_hash": "sha256:..." },
  "server": { "pubkey": "ed25519:...", "name": "github-mcp" },
  "ts_response": "...",
  "sig": "ed25519:..."
}
Enter fullscreen mode Exit fullscreen mode

Two signatures, two keys, one record. The agent says "I sent this request." The server says "I received this request and returned this response." Both parties hold proof. Neither can deny it.

Why bilateral matters

One-sided signing proves "what the agent did." Bilateral signing proves "what happened between the agent and the server."

The difference:

Scenario: an agent places a purchase order, but the amount is wrong.

One-sided: the agent says "I sent price=100." But the server might have received price=10000. You can't prove what happened in between.

Bilateral: the agent's signature binds the params_hash. The server's signature binds the response content_hash. Any tampering in between breaks one of the signatures. Both parties signed the same facts — when something goes wrong, the signatures tell you who's right.

Python support

from signet_auth import SigningAgent

agent = SigningAgent.create("my-agent", owner="willamhou")
receipt = agent.sign("create_issue", params={"title": "fix bug"})

# Verify a bilateral receipt
from signet_auth import verify_bilateral
result = verify_bilateral(bilateral_receipt_json, agent_pubkey, server_pubkey)
Enter fullscreen mode Exit fullscreen mode

Where this fits in the MCP ecosystem

In the MCP spec discussion (SEP-1763), five independent projects were identified as layers of an enforcement stack:

Layer Project What it does
Transport integrity Signet Agent signs request, server verifies + co-signs
Policy enforcement APS Authorization checks, policy engine
External anchoring ArkForge Receipt anchoring to transparency logs
Spend control AgentPay Budget caps
Output verification veroq Response content validation

Signet went from a one-sided signing tool to the transport layer of a bilateral trust protocol. Each layer does its own thing. Together they form a complete agent security stack.

Try it

Claude Code (on the official Anthropic plugin marketplace):


/plugin install signet@claude-plugins-official
Enter fullscreen mode Exit fullscreen mode

Every tool call is signed and audited. Zero config.

MCP server (on the MCP Registry, Glama indexed):

npm install @signet-auth/mcp-server
Enter fullscreen mode Exit fullscreen mode

Agent side:

# TypeScript
npm install @signet-auth/core @signet-auth/mcp

# Python (LangGraph, LlamaIndex, Pydantic AI, Google ADK, OpenAI Agents, Semantic Kernel, smolagents)
pip install signet-auth
Enter fullscreen mode Exit fullscreen mode

GitHub: github.com/Prismer-AI/signet


@willamhou

Top comments (0)