diff --git a/package-lock.json b/package-lock.json index 40370fe..d6af582 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "a2amcp", - "version": "0.1.5", + "version": "0.1.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "a2amcp", - "version": "0.1.5", + "version": "0.1.6", "license": "Apache-2.0", "dependencies": { "@modelcontextprotocol/sdk": "^1.0.4", diff --git a/package.json b/package.json index 957482c..51b15d8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "a2amcp", - "version": "0.1.5", + "version": "0.1.6", "description": "MCP gateway for a2acloud agents. Run as a local MCP server; expose any number of deployed A2A agents to Claude Code, Cursor, and other MCP clients.", "type": "module", "bin": { diff --git a/src/gateway.ts b/src/gateway.ts index 39286cf..d38e234 100644 --- a/src/gateway.ts +++ b/src/gateway.ts @@ -95,7 +95,7 @@ export async function buildGateway(opts: GatewayOptions = {}): Promise { + server.setRequestHandler(CallToolRequestSchema, async (req, extra) => { const fullName = req.params.name; const idx = fullName.indexOf(SEP); if (idx < 0) { @@ -107,10 +107,30 @@ export async function buildGateway(opts: GatewayOptions = {}): Promise) ?? {}, + { + progressToken, + onNotification: async (notif) => { + try { + await extra.sendNotification(notif as any); + } catch { + // best effort + } + }, + }, ); // Pass through the upstream's CallToolResult as-is. Upstreams already // populate content / structuredContent / isError per the MCP spec. diff --git a/src/upstream.ts b/src/upstream.ts index 248dc49..bcdbffb 100644 --- a/src/upstream.ts +++ b/src/upstream.ts @@ -99,14 +99,22 @@ export class UpstreamAgent { async callTool( name: string, arguments_: Record, + opts: { + onNotification?: (notif: { + method: string; + params?: Record; + }) => void | Promise; + progressToken?: string | number; + } = {}, ): Promise { const meta: Record = {}; if (this.cfg.cpJwt) meta.cp_jwt = this.cfg.cpJwt; if (this.cfg.cpUrl) meta.cp_url = this.cfg.cpUrl; if (this.cfg.bucket) meta.bucket = this.cfg.bucket; + if (opts.progressToken !== undefined) meta.progressToken = opts.progressToken; const params: Record = { name, arguments: arguments_ }; if (Object.keys(meta).length > 0) params._meta = meta; - return this.streamingCallTool(params); + return this.streamingCallTool(params, opts.onNotification); } /** @@ -122,6 +130,10 @@ export class UpstreamAgent { */ private async streamingCallTool( params: Record, + onNotification?: (notif: { + method: string; + params?: Record; + }) => void | Promise, ): Promise { const id = _nextId++; const headers: Record = { @@ -188,8 +200,21 @@ export class UpstreamAgent { } return msg.result as UpstreamCallResult; } - // else: server→client request (elicitation/create) or another - // frame; ignore — elicit relaying not wired yet. + // Otherwise it's a server→client message. Notifications + // (no id) → forward to the MCP client so long-running + // skills keep the client's tool-call timer alive. Requests + // (has id, has method) — e.g. elicitation/create — are + // not relayed yet, so they will time out upstream. + if (msg && typeof msg.method === "string" && !("id" in msg) && onNotification) { + try { + await onNotification({ + method: msg.method, + params: msg.params as Record | undefined, + }); + } catch { + // Best-effort; never break the stream on a notify error. + } + } } sep = buf.indexOf("\n\n"); }