DEV Community

razashariff
razashariff

Posted on

How We Added Cryptographic Message Signing to OpenClaw's MCP Transport

MCP (Model Context Protocol) is how AI agents talk to tools. OpenClaw, the open source agent framework with 300K+ GitHub stars, uses it for every tool call -- get weather, query a database, transfer funds.

Every one of those messages travels unsigned.

No signature. No replay protection. No way to verify the response came from the server you actually called. In production, with multiple agents sharing server pools across trust boundaries, that's an attack surface.

We fixed it in 100 lines.

The Problem

When an OpenClaw agent calls a tool via MCP, the JSON-RPC message goes over stdio to the tool server. The response comes back the same way. At no point does either side prove its identity.

This means:

  • Replay attacks -- an intercepted tool call can be captured and replayed
  • Response forgery -- an attacker can send a fake response and the agent will trust it
  • Signature stripping -- if someone strips security headers, the agent has no way to notice
  • Tool poisoning -- a tool's description can be modified in transit without detection

These are documented in the OWASP MCP Top 10 and the OWASP Agentic AI Top 10.

The Fix

We built mcps-openclaw -- a transport wrapper that adds ECDSA P-256 message signing to OpenClaw's MCP layer.

It wraps the MCP SDK's StdioClientTransport:

OpenClaw Agent
    ↓
McpsSigningTransport (signs outgoing, verifies incoming)
    ↓
StdioClientTransport (unchanged)
    ↓
MCP Tool Server
Enter fullscreen mode Exit fullscreen mode

Every outgoing message gets:

  • An ECDSA P-256 digital signature
  • A unique nonce (replay protection)
  • A timestamp
  • A passport ID (agent identity)

Every incoming response is verified against the server's public key. Replayed messages are rejected. Forged signatures are caught. Stripped envelopes are blocked in strict mode.

User Experience

For an OpenClaw user, enabling this is three lines of config:

{
  "mcpServers": {
    "my-server": {
      "command": "node",
      "args": ["server.js"],
      "mcps": { "enabled": true }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Then install the signing library:

npm i mcp-secure
Enter fullscreen mode Exit fullscreen mode

That's it. All tool calls are now signed. Everything else works exactly as before. If mcps is not in the config, nothing changes -- fully backward compatible.

What We Tested

15 tests covering every attack vector, plus a 5-scenario demo:

  1. Legitimate tool call -- signed request, verified response, end-to-end
  2. Replay attack -- captured response replayed → blocked (nonce already consumed)
  3. Forged response -- attacker signs with wrong key → blocked (signature mismatch)
  4. Signature stripping -- MCPS envelope removed → blocked in strict mode
  5. Tool poisoning -- tool description modified → detected via hash comparison

All crypto is real ECDSA P-256 -- no mocks, no stubs.

Design Decisions

No hard dependency. The mcp-secure library is lazy-loaded. If it's not installed, signing is skipped with a warning. Existing OpenClaw installations are unaffected.

Ephemeral keys. A fresh key pair is generated per session. No key files to manage, no PKI to set up. For production deployments that need persistent identity, the config accepts a server public key for verification.

Verify before nonce. The signature is verified before the nonce is consumed. This prevents an attacker from burning valid nonces by sending messages with valid nonces but invalid signatures.

Fail closed. If signing fails, the message is not sent. No silent fallback to unsigned.

The Bigger Picture

This is part of MCPS (MCP Secure) -- a cryptographic security layer for the Model Context Protocol. We filed an IETF Internet-Draft for the specification and have been contributing to the OWASP MCP Top 10 and OWASP AI Security Verification Standard.

The OpenClaw integration is one piece. The same signing layer works with any MCP implementation -- we also have integrations for LangChain (Python) and enterprise HA deployments (mcps-ha).

Links

If you're running MCP servers in production, unsigned tool calls are an open attack surface. This closes it.

Top comments (0)