@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 +18 -0
- package/dist/configure.d.ts +4 -3
- package/dist/configure.d.ts.map +1 -1
- package/dist/configure.js +5 -3
- package/dist/configure.js.map +1 -1
- package/dist/providers/mcp_provider.d.ts +22 -0
- package/dist/providers/mcp_provider.d.ts.map +1 -0
- package/dist/providers/mcp_provider.js +229 -0
- package/dist/providers/mcp_provider.js.map +1 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/mcp/auth.d.ts +90 -0
- package/dist/src/mcp/auth.d.ts.map +1 -0
- package/dist/src/mcp/auth.js +108 -0
- package/dist/src/mcp/auth.js.map +1 -0
- package/dist/src/mcp/define_config.d.ts +64 -0
- package/dist/src/mcp/define_config.d.ts.map +1 -0
- package/dist/src/mcp/define_config.js +8 -0
- package/dist/src/mcp/define_config.js.map +1 -0
- package/dist/src/mcp/index.d.ts +7 -0
- package/dist/src/mcp/index.d.ts.map +1 -0
- package/dist/src/mcp/index.js +4 -0
- package/dist/src/mcp/index.js.map +1 -0
- package/dist/src/mcp/server.d.ts +47 -0
- package/dist/src/mcp/server.d.ts.map +1 -0
- package/dist/src/mcp/server.js +94 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/stubs/config/mcp.stub +42 -0
- package/package.json +13 -2
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
|
package/dist/configure.d.ts
CHANGED
|
@@ -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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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>;
|
package/dist/configure.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,mCAAmC,CAAC;AAG/D
|
|
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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', {});
|
package/dist/configure.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configure.js","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C
|
|
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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"}
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.js
CHANGED
|
@@ -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 @@
|
|
|
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.
|
|
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"
|