Package Exports
- @peac/mcp-server
- @peac/mcp-server/package.json
Readme
@peac/mcp-server
Verify, issue, and bundle PEAC receipts in any MCP client (Claude Desktop, Cursor, Windsurf): locally, offline, no API keys.
Quick Start
1. Add to your AI tool (read-only tools)
Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"peac": {
"command": "npx",
"args": ["-y", "@peac/mcp-server"]
}
}
}Cursor / Windsurf (.mcp.json at project root):
{
"mcpServers": {
"peac": {
"command": "npx",
"args": ["-y", "@peac/mcp-server"]
}
}
}1b. Full configuration (with issuing and bundles)
Claude Desktop with all tools enabled:
{
"mcpServers": {
"peac": {
"command": "npx",
"args": [
"-y",
"@peac/mcp-server",
"--issuer-key",
"env:PEAC_ISSUER_KEY",
"--issuer-id",
"https://your-service.example.com",
"--bundle-dir",
"/tmp/peac-bundles",
"--jwks-file",
"./keys/jwks.json"
],
"env": {
"PEAC_ISSUER_KEY": "{\"kty\":\"OKP\",\"crv\":\"Ed25519\",\"d\":\"...\",\"x\":\"...\"}"
}
}
}
}Cursor / Windsurf (.mcp.json at project root):
{
"mcpServers": {
"peac": {
"command": "npx",
"args": [
"-y",
"@peac/mcp-server",
"--issuer-key",
"file:./keys/issuer.jwk",
"--issuer-id",
"https://your-service.example.com",
"--bundle-dir",
"./bundles"
]
}
}
}2. Try it (15 seconds)
Decode a demo receipt from the command line:
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"demo","version":"1.0"}}}
{"jsonrpc":"2.0","method":"notifications/initialized"}
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"peac_decode","arguments":{"jws":"eyJhbGciOiJFZERTQSIsInR5cCI6InBlYWMtcmVjZWlwdC8wLjEiLCJraWQiOiJkZW1vIn0.eyJpc3MiOiJodHRwczovL2FwaS5leGFtcGxlLmNvbSIsImF1ZCI6Imh0dHBzOi8vY2xpZW50LmV4YW1wbGUuY29tIiwiYW10IjoxMDAsImN1ciI6IlVTRCIsInJhaWwiOiJzdHJpcGUiLCJyZWYiOiJ0eF9kZW1vIiwiaWF0IjoxNzM5MDAwMDAwfQ.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"}}}' | npx -y @peac/mcp-server 2>/dev/null | tail -1 | node -e "process.stdin.on('data',d=>{const r=JSON.parse(d).result;console.log(JSON.stringify(r.structuredContent,null,2))})"Or ask your agent directly:
"Decode this PEAC receipt: eyJhbGciOiJFZERTQSIsInR5cCI6InBlYWMt..."
The agent will use peac_verify (with a public key), peac_inspect (metadata only), or peac_decode (raw header + payload) depending on context.
Tools
| Tool | What it does | Needs key? | Needs bundle-dir? |
|---|---|---|---|
peac_verify |
Cryptographic signature verification + claim validation | No* | No |
peac_inspect |
Decode metadata without verifying signature | No | No |
peac_decode |
Raw JWS header + payload dump | No | No |
peac_issue |
Sign and return a PEAC receipt JWS | Yes | No |
peac_create_bundle |
Create a signed evidence bundle directory | Yes | Yes |
* peac_verify needs a public key (inline, via JWKS, or --jwks-file) but not an issuer key.
Privileged tools (peac_issue, peac_create_bundle) only appear in tools/list when the server is started with --issuer-key and --issuer-id. The bundle tool additionally requires --bundle-dir.
Note: stdout is reserved for JSON-RPC messages. All diagnostics, banners, and errors go to stderr.
Two Operating Modes
Pure mode (default): verify, inspect, and decode receipts anywhere. No keys, no filesystem writes, no configuration needed. Safe for any environment.
npx -y @peac/mcp-serverIssuer mode: additionally issue receipts and create signed evidence bundles. Requires explicit operator opt-in via CLI flags. Privileged tools are only visible and callable when their prerequisites are configured:
--issuer-key+--issuer-idenablespeac_issue--bundle-diradditionally enablespeac_create_bundle
npx -y @peac/mcp-server \
--issuer-key env:PEAC_KEY \
--issuer-id https://your-service.example.com \
--bundle-dir ./bundlesEvidence bundles are self-contained directories with canonical manifests (sorted keys, SHA-256 receipt hashes, content-addressable bundle_id) and signed provenance (manifest.jws). The bundle_id is deterministic: same receipts and policy always produce the same ID regardless of when the bundle is created. Manifest content includes created_at, so the signature differs across runs. Bundles can be verified offline, diffed, cached, and used for audits, disputes, or SOC 2 evidence collection.
Transports
stdio (default)
Standard MCP transport for local/CLI usage. JSON-RPC over stdin/stdout.
npx -y @peac/mcp-serverStreamable HTTP (v0.11.0+)
Remote transport for network-accessible MCP servers. Session-isolated with per-session McpServer + transport pairs (CVE-2026-25536 defense).
npx -y @peac/mcp-server --transport http --port 3000Endpoints:
POST /mcp: JSON-RPC tool calls (requiresMcp-Session-Idafter initialization)DELETE /mcp: terminate sessionGET /health: health check (no auth)GET /.well-known/oauth-protected-resource[/<path>]: RFC 9728 PRM (when configured)
Security defaults: localhost-only bind, CORS deny-all, 1MB request body limit, per-session + per-IP rate limiting (100 req/min), session TTL (30 min), max 100 concurrent sessions.
See HTTP Transport Security for the full security model.
CLI Options
peac-mcp-server [options]
Options:
--issuer-key <ref> Issuer signing key (env:VAR or file:/path to Ed25519 JWK)
--issuer-id <uri> Issuer identifier URI (required with --issuer-key)
--bundle-dir <path> Directory for evidence bundle output
--policy <path> Policy configuration file (JSON)
--jwks-file <path> JWKS file for verifier key resolution
--transport <type> Transport: stdio (default) or http
--port <number> HTTP port (default: 3000, http only)
--host <address> HTTP bind address (default: 127.0.0.1)
--cors-origins <list> Allowed CORS origins (comma-separated)
--authorization-servers <l> OAuth authorization server URIs (enables PRM)
--public-url <url> Canonical public URL (required for PRM)
--trust-proxy <preset> Trust X-Forwarded-For (off/loopback/linklocal/private/all)
-V, --version Output version number
-h, --help Display helpPolicy Configuration
Optional JSON file to customize behavior:
{
"version": "1",
"allow_network": false,
"redaction": {
"strip_evidence": false,
"strip_payment": false,
"inspect_full_claims": false
},
"tools": {
"peac_verify": { "enabled": true },
"peac_inspect": { "enabled": true },
"peac_decode": { "enabled": true },
"peac_issue": { "enabled": true },
"peac_create_bundle": { "enabled": true }
},
"limits": {
"max_jws_bytes": 16384,
"max_response_bytes": 65536,
"tool_timeout_ms": 30000,
"max_concurrency": 10,
"max_claims_bytes": 262144,
"max_bundle_receipts": 256,
"max_bundle_bytes": 16777216,
"max_ttl_seconds": 86400
}
}Library Usage
Handlers can be used directly without the MCP server:
import { handleVerify, getDefaultPolicy, computePolicyHash } from '@peac/mcp-server';
const policy = getDefaultPolicy();
const policyHash = await computePolicyHash(JSON.stringify(policy));
const result = await handleVerify({
input: { jws: 'eyJ...', public_key_base64url: '...' },
policy,
context: {
version: '0.11.0',
policyHash,
protocolVersion: '2025-11-25',
},
});
if (result.structured.ok) {
// Signature valid, claims verified
}MCP SDK Compatibility
This package pins @modelcontextprotocol/sdk at ~1.27.0 (>= 1.26.0 required for CVE-2026-25536 fix). The SDK peer dependency accepts Zod ^3.25 || ^4.0; the workspace uses Zod 4. If upgrading the SDK, verify tool registration still works by running pnpm --filter @peac/mcp-server test. See package.json for actual pinned versions.
How to Verify a Receipt
- Add the MCP server to your agent (see Quick Start above).
- Provide the receipt JWS and a public key (base64url-encoded Ed25519).
- Call
peac_verifyand check theokfield in the response.
> "Verify this receipt: eyJhbGci... with public key MCowBQYDK2..."The tool returns structured content with ok: true/false, claim details, and the verification report.
How to Issue a Receipt
Issuance requires an Ed25519 signing key. Start the server with --issuer-key and --issuer-id:
npx -y @peac/mcp-server --issuer-key env:PEAC_KEY --issuer-id https://your-service.example.comThen call peac_issue with the receipt claims (amount, currency, rail, reference, audience).
Use with Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"peac": {
"command": "npx",
"args": ["-y", "@peac/mcp-server"]
}
}
}Restart Claude Desktop. The PEAC tools appear in the tool picker.
Use with Cursor
Add to .cursor/mcp.json at your project root:
{
"mcpServers": {
"peac": {
"command": "npx",
"args": ["-y", "@peac/mcp-server"]
}
}
}Restart Cursor. PEAC tools are available in chat.
Architecture
- DD-51: Pure handlers with no MCP SDK dependency
- DD-52: No ambient key discovery: explicit
--issuer-keyonly - DD-53: Static policy loaded once at startup with SHA-256 hash
- DD-54: Structured outputs (
structuredContent+text) on every response - DD-55: No URLs resolved from tool inputs (SSRF prevention)
- DD-57: Core modules (
handlers/,schemas/,infra/) have zero MCP SDK imports - DD-58: Line-buffered stdout fence validates JSON-RPC 2.0 output (stdio only)
- DD-119: Streamable HTTP as additive remote transport (session-isolated)
- DD-123: HTTP security boundaries (CORS, rate limiting, Origin/Host validation)
Links
- Protocol: peacprotocol.org
- GitHub: peacprotocol/peac
- npm: @peac/mcp-server
- Built by Originary
License
Apache-2.0