@aroha-sdk/mcp-bridge
v2.0.0
Published
Aroha ↔ MCP (Model Context Protocol) adapter
Readme
@aroha-sdk/mcp-bridge
Translate between MCP tool definitions and Aroha agents, in both directions: wrap an MCP tool server as a callable Aroha agent, or call a discovered Aroha agent's capabilities as MCP tools.
Install
npm install @aroha-sdk/mcp-bridgeQuick example
import { wrapMcp, registryPublicKeyResolver } from "@aroha-sdk/mcp-bridge";
// Expose your MCP tools as an Aroha agent
const agent = await wrapMcp(server.listTools(), {
name: "weather-tools",
price: 0.01, // optional: charge $0.01 per call
resolvePublicKey: registryPublicKeyResolver(), // verifies who's actually calling
handlers: {
// One entry per tool name returned by listTools(). Each must return the
// tool's real result — wrapMcp() has no way to run your tool for you.
get_weather: async (args) => ({
content: [{ type: "text", text: await getWeather(args) }],
}),
},
});
await agent.start(); // throws if any tool in the list has no handler entry
// ✓ did:aroha:weather-tools → http://localhost:3001Every incoming request is verified before any tool runs: signature (against
the sender DID resolvePublicKey resolves), expiry, and nonce replay — the
same validateEnvelope() every other Aroha transport in the SDK uses, not a
bridge-specific check. A caller resolvePublicKey can't resolve, or whose
signature doesn't match, never reaches a tool handler.
Requiring a spending mandate
const agent = await wrapMcp(server.listTools(), {
name: "weather-tools",
price: 0.01,
resolvePublicKey: registryPublicKeyResolver(),
requireMandate: true, // reject any call with no valid mandate granting the caller
handlers: { /* ... */ },
});With requireMandate: true, every request must carry a spending mandate (in
body.credentialToken) that (a) grants the calling agent's DID specifically —
a mandate granted to someone else is rejected — and (b) is signed by an
issuer resolvePublicKey can resolve, with a real, checked signature — not
just a mandate-shaped object present in the request. Issue mandates with
issueIntentMandate / issueTaskMandate from @aroha-sdk/credentials.
API
wrapMcp(tools, opts)— wrap an array of MCP tool definitions as an Aroha agent. Returns{ did, port, endpoint, capabilities, start() }.opts.name— agent name (used to generate the DID).opts.handlers— required. Real tool implementations, keyed by tool name, each returning{ content: [{ type: "text", text }], isError? }.opts.resolvePublicKey— required. Resolves a caller DID to its Ed25519 public key, used to verify every request's signature. UseregistryPublicKeyResolver()for the common case, or supply your own.opts.requireMandate— whentrue, reject any request without a valid spending mandate granting the caller. Defaultfalse.opts.price— per-call price in USD, attached to the derived capability's pricing metadata.opts.did/opts.port/opts.privateKey— override the auto-generated identity/port.registryPublicKeyResolver(baseUrl?)— aresolvePublicKeyimplementation backed by the Aroha registry.arohaAgentToMcpTools(agent, orchestratorDID, orchestratorPrivateKey, client, correlationId)— the other direction: call a discovered Aroha agent's capabilities as MCP tools. Returns{ tools, handleToolCall }.mcpToolsToArohaCapabilities(tools)— convert MCP tool definitions into Aroha capability definitions, used internally bywrapMcp().
License
MIT
