MCP + xLink in 5 Minutes
Add identity-based authentication to Model Context Protocol servers. Replace localhost trust assumptions with cryptographic identity verification using XLinkTransport.
Secure MCP Server with Identity
By the end of this guide, you'll have an MCP server that verifies caller identity before executing tool calls. Every JSON-RPC request is cryptographically signed and verified — no more implicit localhost trust.
Standard MCP servers assume all connections are trusted. XLinkTransport wraps the MCP protocol with xLink's identity layer, so your server knows who is calling each tool — not just that a request arrived on the correct port.
Five Steps to Production
Install the Package
Add the MCP transport adapter to your project:
npm install @private.me/mcp-transport @modelcontextprotocol/sdk
Create an MCP Server with Identity
Wrap your MCP server with MCPServerAdapter to enforce identity verification:
import { Agent, MemoryTrustRegistry, LoopbackTransport } from '@private.me/xlink'; import { MCPServerAdapter } from '@private.me/mcp-transport'; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; // Shared infrastructure (use HttpTrustRegistry in production) const registry = new MemoryTrustRegistry(); const transport = new LoopbackTransport(); // Create server identity const serverAgent = await Agent.quickstart({ name: 'mcp-server', registry, transport, }); // Create standard MCP server const mcpServer = new Server( { name: 'secure-tools', version: '1.0.0' }, { capabilities: { tools: {} } } ); // Register tools mcpServer.setRequestHandler('tools/list', async () => ({ tools: [ { name: 'get_user_data', description: 'Retrieve sensitive user data', inputSchema: { type: 'object', properties: { userId: { type: 'string' }, }, required: ['userId'], }, }, ], })); mcpServer.setRequestHandler('tools/call', async (request) => { if (request.params.name === 'get_user_data') { return { content: [ { type: 'text', text: `User data for ${request.params.arguments.userId}`, }, ], }; } throw new Error(`Unknown tool: ${request.params.name}`); }); // Wrap with identity verification const adapter = new MCPServerAdapter({ server: mcpServer, agent: serverAgent, policyConstraints: { allowedCallers: ['did:key:z6Mk...'], // Only allow specific clients requireSignatures: true, allowedScopes: ['tools:read', 'tools:execute'], }, }); // Start the server const stdio = new StdioServerTransport(); await mcpServer.connect(stdio); console.log('Secure MCP server running with identity verification');
Create a Client with Identity
Connect to the MCP server with cryptographic identity:
import { Agent } from '@private.me/xlink'; import { MCPClientAdapter } from '@private.me/mcp-transport'; import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'; // Create client identity (using same registry as server) const clientAgent = await Agent.quickstart({ name: 'mcp-client', registry, // Same registry as server transport, }); // Create standard MCP client const mcpClient = new Client( { name: 'ai-agent', version: '1.0.0' }, { capabilities: {} } ); // Wrap with identity layer const adapter = new MCPClientAdapter({ client: mcpClient, agent: clientAgent, serverDID: 'did:key:z6Mk...', // Server's DID from registry scope: 'tools:execute', }); // Connect to server const stdio = new StdioClientTransport({ command: 'node', args: ['./server.js'], }); await mcpClient.connect(stdio); console.log('Connected to secure MCP server');
Call Tools with Automatic Verification
Every tool call is cryptographically signed and verified:
// List available tools const tools = await mcpClient.request( { method: 'tools/list' }, ListToolsResultSchema ); console.log('Available tools:', tools.tools); // Available tools: [ { name: 'get_user_data', description: '...' } ] // Call a tool (automatically signed with client's identity) const result = await mcpClient.request( { method: 'tools/call', params: { name: 'get_user_data', arguments: { userId: 'user_123' }, }, }, CallToolResultSchema ); console.log('Result:', result); // Result: { content: [ { type: 'text', text: 'User data for user_123' } ] }
Add Audit Logging (Optional)
Track all MCP calls with structured audit logs:
import { AuditLogger } from '@private.me/mcp-transport'; // Create audit logger const auditLogger = new AuditLogger({ output: './audit.jsonl', // JSONL append-only log }); // Add to server adapter const adapter = new MCPServerAdapter({ server: mcpServer, agent: serverAgent, auditLogger, // Logs every call policyConstraints: { allowedCallers: ['did:key:z6Mk...'], requireSignatures: true, }, }); // Audit log format: // {"timestamp":"2024-04-28T10:30:00.000Z","caller":"did:key:z6Mk...","method":"tools/call","params":{"name":"get_user_data"},"direction":"inbound","verified":true}
What You Get
- Identity Verification: Every JSON-RPC call is cryptographically signed with the caller's DID (Decentralized Identifier)
- Policy Enforcement: Restrict tool access to specific callers, scopes, or capabilities
- Audit Logging: JSONL append-only logs for compliance and forensics
- Zero Config:
Agent.quickstart()handles identity generation, key agreement (X25519 + ML-KEM-768), and registry registration - Standard MCP Protocol: Works with Claude Desktop, MCP SDK, and all JSON-RPC 2.0 compatible clients
- Replay Protection: Nonce store prevents message replay attacks
How It Works
XLinkTransport wraps the MCP protocol with xLink's identity layer. Every JSON-RPC request is signed, verified, and logged before reaching your tool handlers.
Request Flow
- Client signs request: MCPClientAdapter wraps the JSON-RPC request in a signed xLink envelope
- Transport delivers: Standard MCP transport (stdio, HTTP, SSE) delivers the envelope
- Server verifies signature: MCPServerAdapter extracts the caller's DID and verifies the signature using the trust registry
- Policy check: PolicyEnforcer validates the caller against allowedCallers, scopes, and capabilities
- Audit log: AuditLogger records the call (timestamp, caller, method, params, verification status)
- Tool execution: If all checks pass, the request reaches your MCP server's tool handler
- Response signing: Server's response is signed and returned to the client
XLinkTransport is fully compatible with the official @modelcontextprotocol/sdk. It wraps the standard MCP protocol without breaking compatibility — clients and servers that don't use xLink can still communicate using standard MCP transports.
Production Checklist
- Use HttpTrustRegistry: Replace
MemoryTrustRegistrywithHttpTrustRegistryfor multi-node deployments - Use HttpsTransportAdapter: Replace
LoopbackTransportwith HTTPS-based transport for remote agents - Enable audit logging: Rotate
audit.jsonldaily, ship to SIEM for compliance - Restrict allowedCallers: Only whitelist DIDs for authorized clients
- Define scopes: Use fine-grained scopes like
tools:read,tools:execute:sensitive - Monitor policy violations: Alert on signature verification failures or policy denials
Learn More
Explore the full xLink documentation for advanced patterns:
- xLink White Paper — Complete technical documentation
- Haystack + xLink — AI pipelines with identity
- AutoGen + xLink — Multi-agent systems with identity
- CrewAI + xLink — Agent crews with identity
- White Papers Index — All 186 ACIs