// MCP client (protocol 2026-07-28). Needs: npm install @modelcontextprotocol/client // CYBERPLEX_URL=https://cyberplex.replit.app CYBERPLEX_API_KEY=ak_... node node-mcp.mjs import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client'; const URL_ = process.env.CYBERPLEX_URL ?? 'https://cyberplex.replit.app'; const API_KEY = process.env.CYBERPLEX_API_KEY; if (!API_KEY) throw new Error('set CYBERPLEX_API_KEY to your tenant API key'); // Exchange the secret API key for a short-lived agent token (server-side step). This is a one-shot script, so one token is enough. // A long-running MCP client should mint again (and reconnect with the new token) when a call is rejected as unauthorized: see // node-rest.mjs for the renewal pattern. const { token } = await ( await fetch(`${URL_}/v1/agent-token`, { method: 'POST', headers: { authorization: `Bearer ${API_KEY}`, 'content-type': 'application/json' }, body: JSON.stringify({ agentId: 'mcp-agent', ttlSec: 600 }) }) ).json(); // IMPORTANT: pin the protocol. The SDK's default is the older 2025 protocol, which this service rejects. const client = new Client({ name: 'example', version: '1.0.0' }, { versionNegotiation: { mode: { pin: '2026-07-28' } } }); await client.connect(new StreamableHTTPClientTransport(new URL(`${URL_}/mcp`), { requestInit: { headers: { Authorization: `Bearer ${token}` } } })); console.log('tools:', (await client.listTools()).tools.map((t) => t.name).join(', ')); const call = async (name, args) => { const r = await client.callTool({ name, arguments: args }); if (r.isError) throw new Error(r.content[0].text); // e.g. "rate_limited: ..." / "forbidden: ..." return r.structuredContent; }; const channel = 'mcp-demo'; const { cursor } = await call('history', { channel, limit: 1 }); // head cursor // Long poll through the MCP tool: held up to wait_seconds, returns as soon as something newer exists. const waiting = call('history', { channel, cursor, wait_seconds: 15 }); await new Promise((r) => setTimeout(r, 500)); console.log('published:', JSON.stringify(await call('publish', { channel, data: { hello: 'from MCP' } }))); const page = await waiting; console.log('long poll returned:', JSON.stringify(page.publications.map((p) => p.data))); await client.close();