@adonis-agora/agent 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @adonis-agora/agent
2
2
 
3
+ ## 0.19.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`9a91190`](https://github.com/DavideCarvalho/adonis-agent/commit/9a91190936f24f912ccb756c58f490381f2b13c7) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Add an MCP (Model Context Protocol) endpoint that exposes the agent's ToolRegistry over Streamable
8
+ HTTP.
9
+
10
+ - New `./mcp` subpath: `defineMcpConfig`, `createMcpServer`, and two auth strategies — `authKitAuth()`
11
+ (OAuth OIDC via `@adonis-agora/authkit-server`, resolved lazily) and `apiKeyAuth()` (constant-time
12
+ key compare). The acting `Actor` resolves from the verified auth and gates `tools/list` /
13
+ `tools/call` through the same role-checked registry the agent loop uses (fail-closed).
14
+ - New `./mcp_provider` subpath: an Adonis provider that mounts `POST|GET|DELETE /mcp` plus
15
+ `GET /.well-known/oauth-protected-resource/mcp` (RFC 9728 metadata when OAuth is configured), with
16
+ per-session Streamable HTTP transports.
17
+ - `configure` publishes `config/mcp.ts` via the new `config/mcp.stub`.
18
+
19
+ The published `dist` ships `./mcp` and `./mcp_provider` export maps (mirroring `./agent_provider`).
20
+
3
21
  ## 0.18.0
4
22
 
5
23
  ### Minor Changes
@@ -7,11 +7,12 @@ import type Configure from '@adonisjs/core/commands/configure';
7
7
  * build/dev time (the provider imports it instead of scanning at runtime; it falls back to the
8
8
  * runtime scan when the barrel is absent);
9
9
  * 3. publishes `config/agent.ts`;
10
- * 4. publishes the Lucid migration for the five agent tables (run `node ace migration:run`; delete it
10
+ * 4. publishes `config/mcp.ts` (the MCP endpoint config; the MCP provider is separate);
11
+ * 5. publishes the Lucid migration for the five agent tables (run `node ace migration:run`; delete it
11
12
  * if you only use the in-memory store);
12
- * 5. publishes the additive run-tracking migration (`agent_run` table + `run_id` columns) — run it
13
+ * 6. publishes the additive run-tracking migration (`agent_run` table + `run_id` columns) — run it
13
14
  * after the base migration; delete it with the base one if you only use the in-memory store;
14
- * 6. publishes the pgvector migration for the RAG chunk table (Postgres + pgvector only; delete it
15
+ * 7. publishes the pgvector migration for the RAG chunk table (Postgres + pgvector only; delete it
15
16
  * unless you use `retrievers.pgvector({...})`).
16
17
  */
17
18
  export declare function configure(command: Configure): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,mCAAmC,CAAC;AAG/D;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,SAAS,iBAajD"}
1
+ {"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,mCAAmC,CAAC;AAG/D;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,SAAS,iBAcjD"}
package/dist/configure.js CHANGED
@@ -7,11 +7,12 @@ import { stubsRoot } from './stubs/main.js';
7
7
  * build/dev time (the provider imports it instead of scanning at runtime; it falls back to the
8
8
  * runtime scan when the barrel is absent);
9
9
  * 3. publishes `config/agent.ts`;
10
- * 4. publishes the Lucid migration for the five agent tables (run `node ace migration:run`; delete it
10
+ * 4. publishes `config/mcp.ts` (the MCP endpoint config; the MCP provider is separate);
11
+ * 5. publishes the Lucid migration for the five agent tables (run `node ace migration:run`; delete it
11
12
  * if you only use the in-memory store);
12
- * 5. publishes the additive run-tracking migration (`agent_run` table + `run_id` columns) — run it
13
+ * 6. publishes the additive run-tracking migration (`agent_run` table + `run_id` columns) — run it
13
14
  * after the base migration; delete it with the base one if you only use the in-memory store;
14
- * 6. publishes the pgvector migration for the RAG chunk table (Postgres + pgvector only; delete it
15
+ * 7. publishes the pgvector migration for the RAG chunk table (Postgres + pgvector only; delete it
15
16
  * unless you use `retrievers.pgvector({...})`).
16
17
  */
17
18
  export async function configure(command) {
@@ -22,6 +23,7 @@ export async function configure(command) {
22
23
  rcFile.addAssemblerHook('init', '@adonis-agora/agent/hooks/tools');
23
24
  });
24
25
  await codemods.makeUsingStub(stubsRoot, 'config/agent.stub', {});
26
+ await codemods.makeUsingStub(stubsRoot, 'config/mcp.stub', {});
25
27
  await codemods.makeUsingStub(stubsRoot, 'database/migrations/create_agent_tables.stub', {});
26
28
  await codemods.makeUsingStub(stubsRoot, 'database/migrations/create_agent_run_tracking.stub', {});
27
29
  await codemods.makeUsingStub(stubsRoot, 'database/migrations/create_agent_rag_chunks.stub', {});
@@ -1 +1 @@
1
- {"version":3,"file":"configure.js","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAkB;IAChD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,cAAc,EAAE,CAAC;IAEhD,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,EAAE;QACrC,MAAM,CAAC,WAAW,CAAC,oCAAoC,CAAC,CAAC;QACzD,mGAAmG;QACnG,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,iCAAiC,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,mBAAmB,EAAE,EAAE,CAAC,CAAC;IACjE,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,8CAA8C,EAAE,EAAE,CAAC,CAAC;IAC5F,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,oDAAoD,EAAE,EAAE,CAAC,CAAC;IAClG,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,kDAAkD,EAAE,EAAE,CAAC,CAAC;AAClG,CAAC"}
1
+ {"version":3,"file":"configure.js","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAkB;IAChD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,cAAc,EAAE,CAAC;IAEhD,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,EAAE;QACrC,MAAM,CAAC,WAAW,CAAC,oCAAoC,CAAC,CAAC;QACzD,mGAAmG;QACnG,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,iCAAiC,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,mBAAmB,EAAE,EAAE,CAAC,CAAC;IACjE,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,iBAAiB,EAAE,EAAE,CAAC,CAAC;IAC/D,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,8CAA8C,EAAE,EAAE,CAAC,CAAC;IAC5F,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,oDAAoD,EAAE,EAAE,CAAC,CAAC;IAClG,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,kDAAkD,EAAE,EAAE,CAAC,CAAC;AAClG,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { ApplicationService } from '@adonisjs/core/types';
2
+ /**
3
+ * Wires the agent's {@link ToolRegistry} to the outside world over the Model Context Protocol
4
+ * (Streamable HTTP). Reads `config/mcp.ts` and mounts:
5
+ *
6
+ * - `POST|GET|DELETE {path}` — the MCP endpoint (initialize/session resume, SSE stream, close).
7
+ * - `GET /.well-known/oauth-protected-resource{path}` — RFC 9728 protected-resource metadata, mounted
8
+ * only when `auth.oauth` is set, so MCP clients can discover how to authenticate.
9
+ *
10
+ * Every request is authenticated: the bearer token is verified via `config.auth` (e.g. `authKitAuth()`),
11
+ * the resolved actor attached to the transport's `AuthInfo`, and `tools/list` / `tools/call` run against
12
+ * the SAME `ToolRegistry` singleton the agent loop uses — so a role-checked tool stays role-checked, and
13
+ * a fail-closed gate stays fail-closed, no matter the surface.
14
+ */
15
+ export default class McpProvider {
16
+ #private;
17
+ protected app: ApplicationService;
18
+ constructor(app: ApplicationService);
19
+ boot(): Promise<void>;
20
+ shutdown(): Promise<void>;
21
+ }
22
+ //# sourceMappingURL=mcp_provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp_provider.d.ts","sourceRoot":"","sources":["../../providers/mcp_provider.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAe/D;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,OAAO,OAAO,WAAW;;IAIlB,SAAS,CAAC,GAAG,EAAE,kBAAkB;gBAAvB,GAAG,EAAE,kBAAkB;IAEvC,IAAI;IAsCJ,QAAQ;CA+Kf"}
@@ -0,0 +1,229 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
3
+ import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
4
+ import { DefaultToolAuthorizer } from '../src/authorizer.js';
5
+ import { resolveMcpAuth } from '../src/mcp/auth.js';
6
+ import { createMcpServer } from '../src/mcp/server.js';
7
+ import { ToolRegistry } from '../src/tool-registry.js';
8
+ /**
9
+ * Wires the agent's {@link ToolRegistry} to the outside world over the Model Context Protocol
10
+ * (Streamable HTTP). Reads `config/mcp.ts` and mounts:
11
+ *
12
+ * - `POST|GET|DELETE {path}` — the MCP endpoint (initialize/session resume, SSE stream, close).
13
+ * - `GET /.well-known/oauth-protected-resource{path}` — RFC 9728 protected-resource metadata, mounted
14
+ * only when `auth.oauth` is set, so MCP clients can discover how to authenticate.
15
+ *
16
+ * Every request is authenticated: the bearer token is verified via `config.auth` (e.g. `authKitAuth()`),
17
+ * the resolved actor attached to the transport's `AuthInfo`, and `tools/list` / `tools/call` run against
18
+ * the SAME `ToolRegistry` singleton the agent loop uses — so a role-checked tool stays role-checked, and
19
+ * a fail-closed gate stays fail-closed, no matter the surface.
20
+ */
21
+ export default class McpProvider {
22
+ app;
23
+ /** Live session transports, keyed by MCP session id (per-provider in-memory state). */
24
+ #transports = new Map();
25
+ constructor(app) {
26
+ this.app = app;
27
+ }
28
+ async boot() {
29
+ const config = this.app.config.get('mcp', {});
30
+ const registry = await this.app.container.make(ToolRegistry);
31
+ const defaultRoles = config.defaultRoles ?? ['ADMIN'];
32
+ const authorizer = config.authorizer ?? new DefaultToolAuthorizer(defaultRoles);
33
+ const auth = config.auth !== undefined ? await resolveMcpAuth(config.auth, { app: this.app }) : undefined;
34
+ // The actor fallback for open/dev mode (no `auth`): a fixed identity, or fail-closed (reject).
35
+ const openActor = config.actor;
36
+ const router = await this.app.container.make('router');
37
+ const path = (config.path ?? 'mcp').replace(/^\/+|\/+$/g, '');
38
+ const route = `/${path}`;
39
+ router.post(route, (ctx) => this.#handlePost(ctx, config, registry, authorizer, auth, openActor));
40
+ router.get(route, (ctx) => this.#handleGet(ctx, auth, openActor));
41
+ router.delete(route, (ctx) => this.#handleDelete(ctx, auth, openActor));
42
+ if (auth?.oauth) {
43
+ const oauth = auth.oauth;
44
+ const metadataPath = `/.well-known/oauth-protected-resource/${path}`;
45
+ router.get(metadataPath, async (ctx) => {
46
+ // `oauth` may be lazy (authkit needs a fully booted app to resolve) — resolve it on request.
47
+ const meta = typeof oauth === 'function' ? await oauth() : oauth;
48
+ const origin = `${ctx.request.protocol()}://${ctx.request.headers().host ?? 'localhost'}`;
49
+ return ctx.response.json({
50
+ resource: `${origin}/${path}`,
51
+ authorization_servers: [meta.issuer],
52
+ scopes_supported: meta.scopesSupported,
53
+ resource_name: meta.resourceName,
54
+ });
55
+ });
56
+ }
57
+ }
58
+ async shutdown() {
59
+ await Promise.all([...this.#transports.values()].map((transport) => transport.close()));
60
+ this.#transports.clear();
61
+ }
62
+ // ── auth helpers ──────────────────────────────────────────────────────────
63
+ /**
64
+ * Verify the request's `Authorization: Bearer` token against the configured auth, or fall back to the
65
+ * open-mode `actor`. Returns the `AuthInfo` to attach to the transport, or `null` to reply 401.
66
+ */
67
+ async #authenticate(ctx, auth, openActor) {
68
+ const header = ctx.request.header('authorization');
69
+ const token = header?.startsWith('Bearer ') ? header.slice(7).trim() : undefined;
70
+ if (auth !== undefined) {
71
+ if (!token) {
72
+ ctx.response.status(401).json({ error: 'missing bearer token' });
73
+ return null;
74
+ }
75
+ try {
76
+ return await auth.verify(token);
77
+ }
78
+ catch (error) {
79
+ ctx.response
80
+ .status(401)
81
+ .json({ error: error instanceof Error ? error.message : 'unauthorized' });
82
+ return null;
83
+ }
84
+ }
85
+ if (openActor !== undefined) {
86
+ return { token: '', clientId: '', scopes: [], extra: { actor: openActor } };
87
+ }
88
+ ctx.response
89
+ .status(401)
90
+ .json({ error: 'unauthorized: no auth configured and no fallback actor' });
91
+ return null;
92
+ }
93
+ // ── route handlers ────────────────────────────────────────────────────────
94
+ async #handlePost(ctx, config, registry, authorizer, auth, openActor) {
95
+ const req = ctx.request.request;
96
+ const res = ctx.response.response;
97
+ const body = ctx.request.all();
98
+ const sessionId = ctx.request.header('mcp-session-id');
99
+ const authInfo = await this.#authenticate(ctx, auth, openActor);
100
+ if (authInfo === null)
101
+ return;
102
+ req.auth = authInfo;
103
+ try {
104
+ let transport;
105
+ if (sessionId && this.#transports.has(sessionId)) {
106
+ // Session resume — reuse the existing transport (already connected to its own server).
107
+ transport = this.#transports.get(sessionId);
108
+ await transport.handleRequest(req, res, body);
109
+ }
110
+ else if (!sessionId && isInitializeRequest(body)) {
111
+ // New session — create transport + server, connect, then handle so responses flow through it.
112
+ transport = new StreamableHTTPServerTransport({
113
+ sessionIdGenerator: () => randomUUID(),
114
+ onsessioninitialized: (sid) => {
115
+ if (transport)
116
+ this.#transports.set(sid, transport);
117
+ },
118
+ });
119
+ transport.onclose = () => {
120
+ const sid = transport?.sessionId;
121
+ if (sid && this.#transports.get(sid) === transport) {
122
+ this.#transports.delete(sid);
123
+ }
124
+ };
125
+ const server = createMcpServer({
126
+ name: config.name,
127
+ version: config.version,
128
+ registry,
129
+ policy: authorizer,
130
+ ...(config.allowedTools !== undefined ? { allowedTools: config.allowedTools } : {}),
131
+ actorFromAuth: actorFromAuthInfo,
132
+ });
133
+ await server.connect(transport);
134
+ await transport.handleRequest(req, res, body);
135
+ }
136
+ else {
137
+ res.statusCode = 400;
138
+ res.setHeader('content-type', 'application/json');
139
+ res.end(JSON.stringify({
140
+ jsonrpc: '2.0',
141
+ error: { code: -32000, message: 'Bad Request: no valid session ID' },
142
+ id: null,
143
+ }));
144
+ }
145
+ }
146
+ catch (error) {
147
+ if (!res.headersSent) {
148
+ res.statusCode = 500;
149
+ res.setHeader('content-type', 'application/json');
150
+ res.end(JSON.stringify({
151
+ jsonrpc: '2.0',
152
+ error: {
153
+ code: -32603,
154
+ message: error instanceof Error ? error.message : 'Internal error',
155
+ },
156
+ id: null,
157
+ }));
158
+ }
159
+ }
160
+ await this.#awaitFinish(res);
161
+ }
162
+ async #handleGet(ctx, auth, openActor) {
163
+ const req = ctx.request.request;
164
+ const res = ctx.response.response;
165
+ const sessionId = ctx.request.header('mcp-session-id');
166
+ const authInfo = await this.#authenticate(ctx, auth, openActor);
167
+ if (authInfo === null)
168
+ return;
169
+ req.auth = authInfo;
170
+ const transport = sessionId ? this.#transports.get(sessionId) : undefined;
171
+ if (transport === undefined) {
172
+ res.statusCode = 400;
173
+ res.end('Invalid or missing session ID');
174
+ await this.#awaitFinish(res);
175
+ return;
176
+ }
177
+ await transport.handleRequest(req, res);
178
+ await this.#awaitFinish(res);
179
+ }
180
+ async #handleDelete(ctx, auth, openActor) {
181
+ const req = ctx.request.request;
182
+ const res = ctx.response.response;
183
+ const sessionId = ctx.request.header('mcp-session-id');
184
+ const authInfo = await this.#authenticate(ctx, auth, openActor);
185
+ if (authInfo === null)
186
+ return;
187
+ req.auth = authInfo;
188
+ const transport = sessionId ? this.#transports.get(sessionId) : undefined;
189
+ if (transport === undefined) {
190
+ res.statusCode = 400;
191
+ res.end('Invalid or missing session ID');
192
+ await this.#awaitFinish(res);
193
+ return;
194
+ }
195
+ await transport.handleRequest(req, res);
196
+ await this.#awaitFinish(res);
197
+ }
198
+ /** Resolve only when the raw response is finished so Adonis doesn't try to handle the response again. */
199
+ #awaitFinish(res) {
200
+ return new Promise((resolve) => {
201
+ res.on('finish', () => resolve());
202
+ res.on('close', () => resolve());
203
+ });
204
+ }
205
+ }
206
+ /**
207
+ * Read the acting {@link Actor} from the transport's verified `AuthInfo` — the auth strategies
208
+ * (`authKitAuth`/`apiKeyAuth`) and the open-mode fallback all attach it to `extra.actor`. Throws when
209
+ * absent or malformed so a request without a resolvable identity never reaches the tool registry.
210
+ */
211
+ function actorFromAuthInfo(authInfo) {
212
+ const actor = authInfo?.extra?.actor;
213
+ if (!isActor(actor)) {
214
+ throw new Error('unauthorized: no actor associated with this MCP request');
215
+ }
216
+ return actor;
217
+ }
218
+ /** Runtime shape check for the {@link Actor} attached to an MCP `AuthInfo.extra`. */
219
+ function isActor(value) {
220
+ if (typeof value !== 'object' || value === null)
221
+ return false;
222
+ const candidate = value;
223
+ if (typeof candidate.id !== 'string')
224
+ return false;
225
+ if (candidate.roles === undefined)
226
+ return true;
227
+ return (Array.isArray(candidate.roles) && candidate.roles.every((role) => typeof role === 'string'));
228
+ }
229
+ //# sourceMappingURL=mcp_provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp_provider.js","sourceRoot":"","sources":["../../providers/mcp_provider.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- export declare const VERSION = "0.18.0";
1
+ export declare const VERSION = "0.19.0";
2
2
  export * from './types.js';
3
3
  export * from './spi/tool.js';
4
4
  export * from './base-tool.js';
package/dist/src/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export const VERSION = '0.18.0';
1
+ export const VERSION = '0.19.0';
2
2
  export * from './types.js';
3
3
  export * from './spi/tool.js';
4
4
  export * from './base-tool.js';
@@ -0,0 +1,90 @@
1
+ import type { ApplicationService } from '@adonisjs/core/types';
2
+ import type { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';
3
+ import type { Actor } from '../types.js';
4
+ /**
5
+ * Runtime context a {@link McpAuthFactory} thunk receives when the MCP provider builds the configured
6
+ * auth at boot. Carries the booted application so a driver can resolve a peer's service if it needs to.
7
+ */
8
+ export interface McpAuthContext {
9
+ app: ApplicationService;
10
+ }
11
+ /**
12
+ * OAuth metadata the MCP server advertises at `/.well-known/oauth-protected-resource{path}` (RFC 9728)
13
+ * so an MCP client can discover how to obtain a token. Omitted → the server exposes no OAuth metadata
14
+ * endpoint and clients must authenticate another way (e.g. a static API key).
15
+ */
16
+ export interface McpOAuthMetadata {
17
+ /** The OAuth 2.0 / OIDC authorization server (the authkit issuer, e.g. `http://localhost:3333/oidc`). */
18
+ issuer: string;
19
+ /** Authorization endpoint the MCP client redirects to (e.g. `${issuer}/auth`). */
20
+ authorizationEndpoint: string;
21
+ /** Token endpoint the MCP client exchanges the code at (e.g. `${issuer}/token`). */
22
+ tokenEndpoint: string;
23
+ /** Scopes supported by the authorization server. */
24
+ scopesSupported: string[];
25
+ /** Human label for the protected resource. */
26
+ resourceName: string;
27
+ }
28
+ /**
29
+ * A configured MCP auth strategy. `verify` validates a bearer token and resolves the acting {@link Actor}
30
+ * (attached to `AuthInfo.extra.actor`); it MUST throw when the token is missing/invalid/expired. The
31
+ * same `AuthInfo` is surfaced to the `tools/list` / `tools/call` handlers as `extra.authInfo`, so the
32
+ * actor the tool registry gates against is exactly the one the auth strategy resolved.
33
+ */
34
+ export interface McpAuth {
35
+ /** OAuth metadata to advertise, resolved lazily (the authkit peer may not be bootable until first use). */
36
+ oauth?: McpOAuthMetadata | (() => McpOAuthMetadata | Promise<McpOAuthMetadata>);
37
+ /** Validate `token` and resolve the acting actor. Throws `Error` on rejection. */
38
+ verify(token: string): Promise<AuthInfo>;
39
+ }
40
+ /**
41
+ * A configured MCP auth strategy, as a lazy thunk the provider calls at boot. Each factory lazily
42
+ * imports / resolves its peer inside the thunk, so nothing loads until the strategy is actually selected.
43
+ */
44
+ export type McpAuthFactory = (ctx: McpAuthContext) => McpAuth | Promise<McpAuth>;
45
+ /**
46
+ * Resolve a `McpAuth` config value (a ready instance or a lazy factory) at provider boot.
47
+ */
48
+ export declare function resolveMcpAuth(auth: McpAuth | McpAuthFactory, ctx: McpAuthContext): Promise<McpAuth>;
49
+ /** How `authKitAuth()` maps an access token to the MCP actor. Defaults to the account id. */
50
+ export type AuthKitActorResolver = (info: {
51
+ accountId: string;
52
+ scopes: string[];
53
+ clientId: string | undefined;
54
+ }) => Actor | Promise<Actor>;
55
+ /** Options for {@link authKitAuth}. */
56
+ export interface AuthKitMcpAuthOptions {
57
+ /** Scopes the authorization server advertises. Defaults to the standard authkit scopes. */
58
+ scopes?: string[];
59
+ /** Map a validated access token to the acting {@link Actor}. Defaults to `{ id: accountId }`. */
60
+ toActor?: AuthKitActorResolver;
61
+ /** Human label for the protected resource. Defaults to `'Agent MCP Server'`. */
62
+ resourceName?: string;
63
+ }
64
+ /**
65
+ * Authenticate MCP clients with `@adonis-agora/authkit-server`: verifies the bearer token against the
66
+ * authkit OIDC provider's stored access tokens (`provider.AccessToken.find`) and advertises OAuth
67
+ * metadata derived from the issuer so MCP clients can run the full OAuth login flow.
68
+ *
69
+ * The authkit peer is resolved lazily — the `verify`/`oauth` accessors touch the container only when
70
+ * a request actually arrives (by then the app is fully booted and the authkit keystore/encryption
71
+ * services are ready). `@adonis-agora/authkit-server` stays an optional dependency. Throws a clear
72
+ * error at first use if the binding isn't resolvable (authkit not installed / provider not registered).
73
+ */
74
+ export declare function authKitAuth(options?: AuthKitMcpAuthOptions): McpAuthFactory;
75
+ /** Resolve the MCP actor from a validated API key. Defaults to `{ id: apiKey }`. */
76
+ export type ApiKeyActorResolver = (apiKey: string) => Actor | Promise<Actor>;
77
+ /** Options for {@link apiKeyAuth}. */
78
+ export interface ApiKeyMcpAuthOptions {
79
+ /** Static API keys clients present in `Authorization: Bearer <key>`. */
80
+ apiKeys: string[];
81
+ /** Map a validated API key to the acting {@link Actor}. Defaults to `{ id: apiKey }`. */
82
+ toActor?: ApiKeyActorResolver;
83
+ }
84
+ /**
85
+ * Authenticate MCP clients with a static list of API keys (no OAuth metadata). Intended for trusted
86
+ * gateway / machine-to-machine integrations where the host proxies auth and this server just
87
+ * checks the key. Keys are compared with a constant-time compare to resist timing attacks.
88
+ */
89
+ export declare function apiKeyAuth(options: ApiKeyMcpAuthOptions): McpAuthFactory;
90
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/mcp/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gDAAgD,CAAC;AAC/E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,kBAAkB,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,yGAAyG;IACzG,MAAM,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,qBAAqB,EAAE,MAAM,CAAC;IAC9B,oFAAoF;IACpF,aAAa,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,8CAA8C;IAC9C,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB,2GAA2G;IAC3G,KAAK,CAAC,EAAE,gBAAgB,GAAG,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAChF,kFAAkF;IAClF,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CAC1C;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEjF;;GAEG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,OAAO,GAAG,cAAc,EAC9B,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,OAAO,CAAC,CAKlB;AAuBD,6FAA6F;AAC7F,MAAM,MAAM,oBAAoB,GAAG,CAAC,IAAI,EAAE;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B,KAAK,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;AAE7B,uCAAuC;AACvC,MAAM,WAAW,qBAAqB;IACpC,2FAA2F;IAC3F,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,iGAAiG;IACjG,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,gFAAgF;IAChF,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,OAAO,GAAE,qBAA0B,GAAG,cAAc,CAgD/E;AAmBD,oFAAoF;AACpF,MAAM,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;AAE7E,sCAAsC;AACtC,MAAM,WAAW,oBAAoB;IACnC,wEAAwE;IACxE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,yFAAyF;IACzF,OAAO,CAAC,EAAE,mBAAmB,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,oBAAoB,GAAG,cAAc,CAcxE"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Resolve a `McpAuth` config value (a ready instance or a lazy factory) at provider boot.
3
+ */
4
+ export async function resolveMcpAuth(auth, ctx) {
5
+ if (typeof auth === 'function') {
6
+ return auth(ctx);
7
+ }
8
+ return auth;
9
+ }
10
+ /**
11
+ * Authenticate MCP clients with `@adonis-agora/authkit-server`: verifies the bearer token against the
12
+ * authkit OIDC provider's stored access tokens (`provider.AccessToken.find`) and advertises OAuth
13
+ * metadata derived from the issuer so MCP clients can run the full OAuth login flow.
14
+ *
15
+ * The authkit peer is resolved lazily — the `verify`/`oauth` accessors touch the container only when
16
+ * a request actually arrives (by then the app is fully booted and the authkit keystore/encryption
17
+ * services are ready). `@adonis-agora/authkit-server` stays an optional dependency. Throws a clear
18
+ * error at first use if the binding isn't resolvable (authkit not installed / provider not registered).
19
+ */
20
+ export function authKitAuth(options = {}) {
21
+ const scopes = options.scopes ?? ['openid', 'profile', 'email', 'offline_access', 'roles'];
22
+ const toActor = options.toActor ?? (({ accountId }) => ({ id: accountId }));
23
+ return async ({ app }) => {
24
+ let servicePromise;
25
+ const getService = () => {
26
+ servicePromise ??= resolveAuthKitService(app);
27
+ return servicePromise;
28
+ };
29
+ const oauth = async () => {
30
+ const service = await getService();
31
+ const issuer = service.config.issuer;
32
+ return {
33
+ issuer,
34
+ authorizationEndpoint: `${issuer}/auth`,
35
+ tokenEndpoint: `${issuer}/token`,
36
+ scopesSupported: scopes,
37
+ resourceName: options.resourceName ?? 'Agent MCP Server',
38
+ };
39
+ };
40
+ return {
41
+ oauth,
42
+ async verify(token) {
43
+ const service = await getService();
44
+ const at = await service.provider.AccessToken.find(token);
45
+ if (!at || at.isExpired) {
46
+ throw new Error('invalid or expired access token');
47
+ }
48
+ const tokenScopes = (at.scope ?? '').split(' ').filter(Boolean);
49
+ const actor = await toActor({
50
+ accountId: at.accountId,
51
+ scopes: tokenScopes,
52
+ clientId: at.clientId,
53
+ });
54
+ return {
55
+ token,
56
+ clientId: at.clientId ?? '',
57
+ scopes: tokenScopes,
58
+ ...(at.exp !== undefined ? { expiresAt: at.exp } : {}),
59
+ extra: { actor },
60
+ };
61
+ },
62
+ };
63
+ };
64
+ }
65
+ /**
66
+ * Resolve the authkit `OidcService` from the container. `@adonis-agora/authkit-server` augments
67
+ * `ContainerBindings` with `'authkit.server'`, so `make` is fully typed with no cast.
68
+ */
69
+ async function resolveAuthKitService(app) {
70
+ try {
71
+ return await app.container.make('authkit.server');
72
+ }
73
+ catch (error) {
74
+ const message = error instanceof Error ? error.message : String(error);
75
+ throw new Error(`[@adonis-agora/agent] \`authKitAuth()\` could not resolve the authkit server (${message}). Is \`@adonis-agora/authkit-server\` installed and its provider registered in adonisrc.ts?`);
76
+ }
77
+ }
78
+ /**
79
+ * Authenticate MCP clients with a static list of API keys (no OAuth metadata). Intended for trusted
80
+ * gateway / machine-to-machine integrations where the host proxies auth and this server just
81
+ * checks the key. Keys are compared with a constant-time compare to resist timing attacks.
82
+ */
83
+ export function apiKeyAuth(options) {
84
+ const keys = new Set(options.apiKeys);
85
+ const toActor = options.toActor ?? ((apiKey) => ({ id: apiKey }));
86
+ return async () => {
87
+ return {
88
+ async verify(token) {
89
+ if (![...keys].some((key) => timingSafeEqual(key, token))) {
90
+ throw new Error('invalid API key');
91
+ }
92
+ const actor = await toActor(token);
93
+ return { token, clientId: token, scopes: [], extra: { actor } };
94
+ },
95
+ };
96
+ };
97
+ }
98
+ /** Constant-time string comparison (no `node:crypto` import at module top to keep the browser-safe build). */
99
+ function timingSafeEqual(a, b) {
100
+ if (a.length !== b.length)
101
+ return false;
102
+ let diff = 0;
103
+ for (let i = 0; i < a.length; i++) {
104
+ diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
105
+ }
106
+ return diff === 0;
107
+ }
108
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/mcp/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import type { RolesPolicy } from '../spi/roles-policy.js';
2
+ import { apiKeyAuth, authKitAuth } from './auth.js';
3
+ import type { McpAuth, McpAuthFactory } from './auth.js';
4
+ /**
5
+ * Shape of `config/mcp.ts`. The MCP provider exposes the agent's {@link ToolRegistry} over the Model
6
+ * Context Protocol (Streamable HTTP): `tools/list` mirrors the tools the acting actor may call
7
+ * (role-filtered), `tools/call` runs them through the registry (role re-check + schema re-validation).
8
+ *
9
+ * ```ts
10
+ * import { defineMcpConfig, authKitAuth } from '@adonis-agora/agent/mcp'
11
+ *
12
+ * export default defineMcpConfig({
13
+ * name: 'Lumen Agent',
14
+ * version: '1.0.0',
15
+ * auth: authKitAuth(),
16
+ * path: 'mcp',
17
+ * })
18
+ * ```
19
+ */
20
+ export interface McpConfig {
21
+ /** Server name reported to MCP clients in the initialize handshake. */
22
+ name: string;
23
+ /** Server version reported to MCP clients. */
24
+ version: string;
25
+ /**
26
+ * Route prefix the MCP Streamable HTTP endpoint mounts under. Defaults to `'mcp'` (→ `/mcp`).
27
+ * OAuth metadata (when `auth.oauth` is set) is served at `/.well-known/oauth-protected-resource{path}`.
28
+ */
29
+ path?: string;
30
+ /**
31
+ * How MCP clients authenticate. Pass a ready {@link McpAuth} or a lazy {@link McpAuthFactory} thunk
32
+ * (`authKitAuth()` / `apiKeyAuth()`). Omit → the server is open (no bearer check); the acting actor
33
+ * falls back to `actor`. Fail-closed: with no `auth` AND no `actor`, requests are rejected.
34
+ */
35
+ auth?: McpAuth | McpAuthFactory;
36
+ /**
37
+ * The acting actor when `auth` is omitted (dev/open mode). Fail-closed default: omitted → requests
38
+ * are rejected 401. Set it (e.g. `{ id: 'mcp', roles: ['ADMIN'] }`) to let unauthenticated clients
39
+ * run tools under that identity.
40
+ */
41
+ actor?: {
42
+ id: string;
43
+ roles?: string[];
44
+ tenantRef?: string;
45
+ };
46
+ /**
47
+ * Tool authorization gate. Defaults to `DefaultToolAuthorizer(config.defaultRoles ?? ['ADMIN'])` —
48
+ * same fail-closed default as the agent provider.
49
+ */
50
+ authorizer?: RolesPolicy;
51
+ /** Roles a tool requires when it declares none. Defaults to `['ADMIN']`. */
52
+ defaultRoles?: string[];
53
+ /**
54
+ * Restrict the exposed tools to this allow-list of names. Omit → all tools the acting actor's roles
55
+ * permit are exposed.
56
+ */
57
+ allowedTools?: string[];
58
+ }
59
+ /** Identity helper giving `config/mcp.ts` full type-checking. */
60
+ export declare function defineMcpConfig(config: McpConfig): McpConfig;
61
+ export { authKitAuth, apiKeyAuth };
62
+ export type { McpAuth, McpAuthFactory, McpAuthContext, McpOAuthMetadata, AuthKitMcpAuthOptions, AuthKitActorResolver, ApiKeyMcpAuthOptions, ApiKeyActorResolver, } from './auth.js';
63
+ export { resolveMcpAuth } from './auth.js';
64
+ //# sourceMappingURL=define_config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define_config.d.ts","sourceRoot":"","sources":["../../../src/mcp/define_config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEzD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,SAAS;IACxB,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,cAAc,CAAC;IAChC;;;;OAIG;IACH,KAAK,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D;;;OAGG;IACH,UAAU,CAAC,EAAE,WAAW,CAAC;IACzB,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,iEAAiE;AACjE,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,CAE5D;AAED,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;AACnC,YAAY,EACV,OAAO,EACP,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { apiKeyAuth, authKitAuth } from './auth.js';
2
+ /** Identity helper giving `config/mcp.ts` full type-checking. */
3
+ export function defineMcpConfig(config) {
4
+ return config;
5
+ }
6
+ export { authKitAuth, apiKeyAuth };
7
+ export { resolveMcpAuth } from './auth.js';
8
+ //# sourceMappingURL=define_config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define_config.js","sourceRoot":"","sources":["../../../src/mcp/define_config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAuDpD,iEAAiE;AACjE,MAAM,UAAU,eAAe,CAAC,MAAiB;IAC/C,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;AAWnC,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { defineMcpConfig } from './define_config.js';
2
+ export { authKitAuth, apiKeyAuth, resolveMcpAuth } from './auth.js';
3
+ export { createMcpServer } from './server.js';
4
+ export type { McpConfig } from './define_config.js';
5
+ export type { McpAuth, McpAuthFactory, McpAuthContext, McpOAuthMetadata, AuthKitMcpAuthOptions, AuthKitActorResolver, ApiKeyMcpAuthOptions, ApiKeyActorResolver, } from './auth.js';
6
+ export type { CreateMcpServerOptions, McpToolContextOptions } from './server.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/mcp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,YAAY,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,YAAY,EACV,OAAO,EACP,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { defineMcpConfig } from './define_config.js';
2
+ export { authKitAuth, apiKeyAuth, resolveMcpAuth } from './auth.js';
3
+ export { createMcpServer } from './server.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/mcp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,47 @@
1
+ import type { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import type { RolesPolicy } from '../spi/roles-policy.js';
4
+ import { ToolRegistry } from '../tool-registry.js';
5
+ import type { Actor } from '../types.js';
6
+ /**
7
+ * Runtime context a MCP handler synthesizes for {@link ToolRegistry.invoke}: the acting {@link Actor}
8
+ * comes from the verified auth, and the run/request ids from the MCP request so tool traces map back
9
+ * to the MCP session.
10
+ */
11
+ export interface McpToolContextOptions {
12
+ /** How the actor is resolved from the transport's verified auth info. */
13
+ actorFromAuth: (authInfo: AuthInfo | undefined) => Actor;
14
+ /** How run/thread/request ids are derived. Defaults to the MCP session + request ids. */
15
+ idsFromRequest?: (sessionId: string | undefined, requestId: string | number | undefined) => {
16
+ threadId: string;
17
+ runId: string;
18
+ requestId: string;
19
+ };
20
+ }
21
+ /** Options for {@link createMcpServer}. */
22
+ export interface CreateMcpServerOptions extends McpToolContextOptions {
23
+ /** Server name reported in the initialize handshake. */
24
+ name: string;
25
+ /** Server version reported in the initialize handshake. */
26
+ version: string;
27
+ /** The agent tool registry to expose. */
28
+ registry: ToolRegistry;
29
+ /** Tool authorization gate (role re-check happens per call, defense-in-depth). */
30
+ policy: RolesPolicy;
31
+ /** Optional allow-list restricting which tools are exposed. */
32
+ allowedTools?: string[];
33
+ }
34
+ /**
35
+ * Build a low-level MCP `Server` that exposes the agent's {@link ToolRegistry} over the wire:
36
+ *
37
+ * - `tools/list` → `registry.definitionsFor(actor, policy, allowedTools)` — the tools the acting
38
+ * actor's roles permit (fail-closed via the same `RolesPolicy` the agent loop uses), converted to
39
+ * MCP `Tool` descriptors with JSON-Schema inputs.
40
+ * - `tools/call` → `registry.invoke(name, args, ctx, policy)` — role re-check + input re-validation
41
+ * before the handler runs (the registry's own defense-in-depth), so a stale tool list can never
42
+ * bypass the gate.
43
+ *
44
+ * Use this in the MCP provider (one server per session, connected to that session's transport).
45
+ */
46
+ export declare function createMcpServer(options: CreateMcpServerOptions): Server;
47
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/mcp/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gDAAgD,CAAC;AAC/E,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAKnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,yEAAyE;IACzE,aAAa,EAAE,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,KAAK,KAAK,CAAC;IACzD,yFAAyF;IACzF,cAAc,CAAC,EAAE,CACf,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,KACnC;QACH,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,2CAA2C;AAC3C,MAAM,WAAW,sBAAuB,SAAQ,qBAAqB;IACnE,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,QAAQ,EAAE,YAAY,CAAC;IACvB,kFAAkF;IAClF,MAAM,EAAE,WAAW,CAAC;IACpB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AA4CD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,MAAM,CAgDvE"}
@@ -0,0 +1,94 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { toJsonSchemaCompat } from '@modelcontextprotocol/sdk/server/zod-json-schema-compat.js';
3
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
4
+ /**
5
+ * True for a Zod schema (tags its Standard Schema props with `vendor: 'zod'`). Narrows to the SDK's
6
+ * `AnyObjectSchema` so `toJsonSchemaCompat` can consume it without a cast.
7
+ */
8
+ function isZodSchema(schema) {
9
+ return schema['~standard'].vendor === 'zod';
10
+ }
11
+ /** True when the schema carries the Standard JSON Schema converter (`~standard.jsonSchema.input`). */
12
+ function hasStandardJsonSchema(schema) {
13
+ const standard = schema['~standard'];
14
+ if (!('jsonSchema' in standard)) {
15
+ return false;
16
+ }
17
+ const converter = standard.jsonSchema;
18
+ return (typeof converter === 'object' &&
19
+ converter !== null &&
20
+ 'input' in converter &&
21
+ typeof converter.input === 'function');
22
+ }
23
+ /**
24
+ * Convert a core `StandardSchemaV1` tool schema into the JSON Schema MCP clients see in `tools/list`.
25
+ * Zod schemas are converted with the SDK's compat helper (handles both v3 and v4); schemas that expose
26
+ * the Standard JSON Schema extension (Valibot/ArkType/Zod 4) hand their `input()` schema straight
27
+ * through. Anything else degrades to a permissive object schema — the loop still validates the real
28
+ * schema on call via `~standard.validate`.
29
+ */
30
+ function toJsonSchema(schema) {
31
+ if (isZodSchema(schema)) {
32
+ return toJsonSchemaCompat(schema);
33
+ }
34
+ if (hasStandardJsonSchema(schema)) {
35
+ return schema['~standard'].jsonSchema.input();
36
+ }
37
+ return { type: 'object', properties: {}, additionalProperties: true };
38
+ }
39
+ /**
40
+ * Build a low-level MCP `Server` that exposes the agent's {@link ToolRegistry} over the wire:
41
+ *
42
+ * - `tools/list` → `registry.definitionsFor(actor, policy, allowedTools)` — the tools the acting
43
+ * actor's roles permit (fail-closed via the same `RolesPolicy` the agent loop uses), converted to
44
+ * MCP `Tool` descriptors with JSON-Schema inputs.
45
+ * - `tools/call` → `registry.invoke(name, args, ctx, policy)` — role re-check + input re-validation
46
+ * before the handler runs (the registry's own defense-in-depth), so a stale tool list can never
47
+ * bypass the gate.
48
+ *
49
+ * Use this in the MCP provider (one server per session, connected to that session's transport).
50
+ */
51
+ export function createMcpServer(options) {
52
+ const { registry, policy, allowedTools, actorFromAuth, idsFromRequest } = options;
53
+ const server = new Server({ name: options.name, version: options.version }, { capabilities: { tools: {} } });
54
+ const buildCtx = (authInfo, sessionId) => {
55
+ const actor = actorFromAuth(authInfo);
56
+ const ids = idsFromRequest
57
+ ? idsFromRequest(sessionId, undefined)
58
+ : {
59
+ threadId: `mcp:${sessionId ?? actor.id}`,
60
+ runId: `mcp:${sessionId ?? 'run'}`,
61
+ requestId: `mcp:${sessionId ?? 'req'}`,
62
+ };
63
+ return { actor, ...ids };
64
+ };
65
+ server.setRequestHandler(ListToolsRequestSchema, async (request, extra) => {
66
+ const actor = actorFromAuth(extra.authInfo);
67
+ const defs = await registry.definitionsFor(actor, policy, allowedTools);
68
+ return {
69
+ tools: defs.map((definition) => ({
70
+ name: definition.name,
71
+ description: definition.description,
72
+ inputSchema: toJsonSchema(definition.inputSchema),
73
+ })),
74
+ };
75
+ });
76
+ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
77
+ const ctx = buildCtx(extra.authInfo, extra.sessionId);
78
+ const { name, arguments: args } = request.params;
79
+ try {
80
+ const output = await registry.invoke(name, args ?? {}, ctx, policy);
81
+ return {
82
+ content: [
83
+ { type: 'text', text: typeof output === 'string' ? output : JSON.stringify(output) },
84
+ ],
85
+ };
86
+ }
87
+ catch (error) {
88
+ const message = error instanceof Error ? error.message : String(error);
89
+ return { content: [{ type: 'text', text: message }], isError: true };
90
+ }
91
+ });
92
+ return server;
93
+ }
94
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/mcp/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ {{{
2
+ exports({ to: app.configPath('mcp.ts') })
3
+ }}}
4
+ import { defineMcpConfig, authKitAuth } from '@adonis-agora/agent/mcp'
5
+ // import { apiKeyAuth } from '@adonis-agora/agent/mcp'
6
+
7
+ /**
8
+ * Configuration for the MCP (Model Context Protocol) endpoint. The provider exposes the agent's tool
9
+ * registry over Streamable HTTP: `tools/list` mirrors the tools the acting actor may call and
10
+ * `tools/call` runs them through the same role-checked registry the agent loop uses.
11
+ *
12
+ * Requires `@adonis-agora/agent`'s agent provider (which owns the ToolRegistry singleton) to be
13
+ * registered. Register the MCP provider in adonisrc.ts:
14
+ *
15
+ * providers: [
16
+ * ...existing,
17
+ * () => import('@adonis-agora/agent/mcp_provider'),
18
+ * ]
19
+ *
20
+ * The endpoint mounts at `/mcp`. When `auth` exposes OAuth metadata (authKitAuth), MCP clients discover
21
+ * the login flow at `/.well-known/oauth-protected-resource/mcp`.
22
+ */
23
+ export default defineMcpConfig({
24
+ name: 'Agent MCP Server',
25
+ version: '1.0.0',
26
+
27
+ // path: 'mcp',
28
+
29
+ // Authenticate MCP clients against @adonis-agora/authkit-server (OAuth OIDC): verifies the bearer
30
+ // token against the authkit provider and advertises OAuth metadata so clients can log in.
31
+ auth: authKitAuth(),
32
+
33
+ // Or static API keys for trusted gateway integrations (no OAuth metadata):
34
+ // auth: apiKeyAuth({ apiKeys: ['mcp_xxx'] }),
35
+
36
+ // Open/dev mode — run every request under a fixed identity. Fail-closed: omit this (and auth) and
37
+ // requests are rejected 401.
38
+ // actor: { id: 'mcp', roles: ['ADMIN'] },
39
+
40
+ // Restrict the exposed tools to an allow-list by name.
41
+ // allowedTools: ['calcular_estatisticas', 'estatistica_inferencial'],
42
+ })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adonis-agora/agent",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "description": "Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -23,6 +23,16 @@
23
23
  "import": "./dist/src/ai-sdk/index.js",
24
24
  "default": "./dist/src/ai-sdk/index.js"
25
25
  },
26
+ "./mcp": {
27
+ "types": "./dist/src/mcp/index.d.ts",
28
+ "import": "./dist/src/mcp/index.js",
29
+ "default": "./dist/src/mcp/index.js"
30
+ },
31
+ "./mcp_provider": {
32
+ "types": "./dist/providers/mcp_provider.d.ts",
33
+ "import": "./dist/providers/mcp_provider.js",
34
+ "default": "./dist/providers/mcp_provider.js"
35
+ },
26
36
  "./testing": {
27
37
  "types": "./dist/src/testing/index.d.ts",
28
38
  "import": "./dist/src/testing/index.js",
@@ -96,6 +106,7 @@
96
106
  ],
97
107
  "sideEffects": false,
98
108
  "dependencies": {
109
+ "@modelcontextprotocol/sdk": "^1.30.0",
99
110
  "@standard-schema/spec": "^1.0.0"
100
111
  },
101
112
  "peerDependencies": {
@@ -193,7 +204,7 @@
193
204
  "build": "pnpm run clean && tsc -p tsconfig.build.json && pnpm run copy:stubs && pnpm run check:dist",
194
205
  "check:dist": "node ../../scripts/assert-build-output.mjs dist src/index.js",
195
206
  "clean": "rm -rf dist",
196
- "copy:stubs": "mkdir -p dist/stubs/config dist/stubs/database/migrations && cp stubs/config/agent.stub dist/stubs/config/agent.stub && cp -r stubs/database/migrations/. dist/stubs/database/migrations/",
207
+ "copy:stubs": "mkdir -p dist/stubs/config dist/stubs/database/migrations && cp stubs/config/agent.stub dist/stubs/config/agent.stub && cp stubs/config/mcp.stub dist/stubs/config/mcp.stub && cp -r stubs/database/migrations/. dist/stubs/database/migrations/",
197
208
  "test": "vitest run --passWithNoTests",
198
209
  "test:watch": "vitest",
199
210
  "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.tests.json"