UNPKG

@adonis-agora/agent

Version:

Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.

57 lines • 3.06 kB
import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; import { pickApi } from './api.js'; import { ResourceAuthError } from './errors.js'; import { wwwAuthError } from './util.js'; /** * Connects to a listed MCP server over Streamable HTTP with a Bearer Session Token issued for * that server's `url` (4.3 "Bearer tokens", section 6). On `invalid_token` the token is * re-minted once and the request retried; a `403` `insufficient_scope` / `sign_in_required` * rejects the call with a {@link ResourceAuthError} carrying the needed `scope`. */ export async function connectMcp(company, apiIndex) { const api = pickApi(company, 'mcp', apiIndex); // `resource` MUST be the MCP server's `url` from poppy.json (4.3). const resource = api.url; let token = await company.bearerFor(resource); const authedFetch = async (input, init) => { for (let attempt = 0;; attempt++) { const headers = new Headers(init?.headers); headers.set('authorization', `Bearer ${token}`); const res = await company.http.fetch(input, { ...init, headers }); const err = wwwAuthError(res.headers.get('www-authenticate')); if (res.status === 401 && attempt === 0 && (!err.error || err.error === 'invalid_token')) { token = await company.bearerFor(resource, true); continue; } // A signed-in Bearer whose Session was signed out meanwhile (4.4): the Account Token signs // it back in — or, revoked, answers invalid_grant and the client continues signed out. if (res.status === 403 && attempt === 0 && err.error === 'sign_in_required' && company.accountToken) { await res.body?.cancel().catch(() => { }); token = await company.bearerFor(resource, true); continue; } // A tool behind a scope the user did not grant (MCP scope step-up, RFC 6750 §3.1): surface it // like any other resource, so the caller can sign in again with `scope`. if (res.status === 403 && (err.error === 'insufficient_scope' || err.error === 'sign_in_required')) { await res.body?.cancel().catch(() => { }); throw new ResourceAuthError(err.error, { status: 403, description: err.description, scope: err.scope, }); } return res; } }; const transport = new StreamableHTTPClientTransport(new URL(api.url), { fetch: authedFetch }); const client = new Client({ name: 'poppy-agent', version: '0.0.0' }); // The SDK's own types disagree under `exactOptionalPropertyTypes`; the transport is the SDK's. await client.connect(transport); return { client, api, close: () => client.close() }; } //# sourceMappingURL=mcp.js.map