@jrmc/adonis-mcp 1.0.0-beta.8 → 1.0.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/README.md +9 -10
- package/build/commands/commands.json +1 -1
- package/build/commands/inspector.d.ts +1 -0
- package/build/commands/inspector.d.ts.map +1 -1
- package/build/commands/inspector.js +1 -0
- package/build/commands/make/prompt.d.ts.map +1 -1
- package/build/commands/make/prompt.js +4 -1
- package/build/commands/make/tool.d.ts.map +1 -1
- package/build/commands/make/tool.js +4 -1
- package/build/configure.d.ts.map +1 -1
- package/build/configure.js +10 -0
- package/build/providers/mcp_provider.d.ts +5 -0
- package/build/providers/mcp_provider.d.ts.map +1 -1
- package/build/providers/mcp_provider.js +3 -1
- package/build/src/server/mcp_bouncer.d.ts +115 -0
- package/build/src/server/mcp_bouncer.d.ts.map +1 -0
- package/build/src/server/mcp_bouncer.js +123 -0
- package/build/src/server/transports/http_transport.d.ts.map +1 -1
- package/build/src/server/transports/http_transport.js +4 -3
- package/build/src/server.d.ts +7 -1
- package/build/src/server.d.ts.map +1 -1
- package/build/src/server.js +23 -6
- package/build/stubs/make/mcp/prompts/main.ts.stub +1 -1
- package/build/stubs/make/mcp/prompts/with_vine.ts.stub +40 -0
- package/build/stubs/make/mcp/tools/with_vine.ts.stub +37 -0
- package/build/stubs/make/middleware/mcp_middleware.ts.stub +6 -7
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +19 -9
package/README.md
CHANGED
|
@@ -28,16 +28,15 @@ AdonisJS MCP - Server MCP for your AdonisJS applications.
|
|
|
28
28
|
- [x] Documentation
|
|
29
29
|
- [x] Inject support
|
|
30
30
|
- [x] VineJs integration
|
|
31
|
-
- [
|
|
32
|
-
- [
|
|
33
|
-
- [
|
|
34
|
-
- [
|
|
35
|
-
- [
|
|
31
|
+
- [x] JSON Schema with VineJs
|
|
32
|
+
- [x] Bounce integration
|
|
33
|
+
- [x] Auth helpers
|
|
34
|
+
- [x] Starter kit
|
|
35
|
+
- [x] Demo applications
|
|
36
|
+
- [x] Events
|
|
37
|
+
- [x] Logger
|
|
36
38
|
- [ ] Alternative transports (SSE)
|
|
37
39
|
- [ ] Output tool
|
|
38
|
-
- [ ] Login flow
|
|
39
|
-
- [ ] Starter kit
|
|
40
|
-
- [ ] Demo applications
|
|
41
40
|
|
|
42
41
|
## Installation & Configuration
|
|
43
42
|
|
|
@@ -192,10 +191,10 @@ To use `auth` and `bouncer` in your MCP tools, prompts, and resources, add the f
|
|
|
192
191
|
```typescript
|
|
193
192
|
declare module '@jrmc/adonis-mcp/types/context' {
|
|
194
193
|
export interface McpContext {
|
|
195
|
-
auth
|
|
194
|
+
auth: {
|
|
196
195
|
user?: HttpContext['auth']['user']
|
|
197
196
|
}
|
|
198
|
-
bouncer
|
|
197
|
+
bouncer: McpBouncer<
|
|
199
198
|
Exclude<HttpContext['auth']['user'], undefined>,
|
|
200
199
|
typeof abilities,
|
|
201
200
|
typeof policies
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"commands":[{"commandName":"mcp:inspector","description":"Open the MCP Inspector tool to debug and test MCP Servers.","help":"","namespace":"mcp","aliases":[],"flags":[],"args":[{"name":"transport","argumentName":"transport","required":true,"description":"Transport type","default":"http","type":"string"}],"options":{"startApp":true},"filePath":"inspector.js"},{"commandName":"make:mcp-prompt","description":"Create a new MCP prompt","help":"","namespace":"make","aliases":[],"flags":[],"args":[{"name":"name","argumentName":"name","required":true,"description":"Name of the prompt","type":"string"}],"options":{"startApp":true},"filePath":"make/prompt.js"},{"commandName":"make:mcp-resource","description":"Create a new MCP resource","help":"","namespace":"make","aliases":[],"flags":[],"args":[{"name":"name","argumentName":"name","required":true,"description":"Name of the resource","type":"string"}],"options":{"startApp":true},"filePath":"make/resource.js"},{"commandName":"make:mcp-tool","description":"Create a new MCP tool","help":"","namespace":"make","aliases":[],"flags":[],"args":[{"name":"name","argumentName":"name","required":true,"description":"Name of the tool","type":"string"}],"options":{"startApp":true},"filePath":"make/tool.js"},{"commandName":"mcp:start","description":"Start the MCP server stdio transport","help":"","namespace":"mcp","aliases":[],"flags":[],"args":[],"options":{"startApp":true},"filePath":"start.js"}],"version":1}
|
|
1
|
+
{"commands":[{"commandName":"mcp:inspector","description":"Open the MCP Inspector tool to debug and test MCP Servers.","help":"","namespace":"mcp","aliases":["mcp:inspect"],"flags":[],"args":[{"name":"transport","argumentName":"transport","required":true,"description":"Transport type","default":"http","type":"string"}],"options":{"startApp":true},"filePath":"inspector.js"},{"commandName":"make:mcp-prompt","description":"Create a new MCP prompt","help":"","namespace":"make","aliases":[],"flags":[],"args":[{"name":"name","argumentName":"name","required":true,"description":"Name of the prompt","type":"string"}],"options":{"startApp":true},"filePath":"make/prompt.js"},{"commandName":"make:mcp-resource","description":"Create a new MCP resource","help":"","namespace":"make","aliases":[],"flags":[],"args":[{"name":"name","argumentName":"name","required":true,"description":"Name of the resource","type":"string"}],"options":{"startApp":true},"filePath":"make/resource.js"},{"commandName":"make:mcp-tool","description":"Create a new MCP tool","help":"","namespace":"make","aliases":[],"flags":[],"args":[{"name":"name","argumentName":"name","required":true,"description":"Name of the tool","type":"string"}],"options":{"startApp":true},"filePath":"make/tool.js"},{"commandName":"mcp:start","description":"Start the MCP server stdio transport","help":"","namespace":"mcp","aliases":[],"flags":[],"args":[],"options":{"startApp":true},"filePath":"start.js"}],"version":1}
|
|
@@ -9,6 +9,7 @@ import { BaseCommand } from '@adonisjs/core/ace';
|
|
|
9
9
|
export default class Inspector extends BaseCommand {
|
|
10
10
|
static commandName: string;
|
|
11
11
|
static description: string;
|
|
12
|
+
static aliases: string[];
|
|
12
13
|
static options: CommandOptions;
|
|
13
14
|
transport: string;
|
|
14
15
|
run(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inspector.d.ts","sourceRoot":"","sources":["../../commands/inspector.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAG9D,OAAO,EAAQ,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAEtD,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,WAAW;IAChD,MAAM,CAAC,WAAW,SAAkB;IACpC,MAAM,CAAC,WAAW,SAA+D;
|
|
1
|
+
{"version":3,"file":"inspector.d.ts","sourceRoot":"","sources":["../../commands/inspector.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAG9D,OAAO,EAAQ,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAEtD,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,WAAW;IAChD,MAAM,CAAC,WAAW,SAAkB;IACpC,MAAM,CAAC,WAAW,SAA+D;IACjF,MAAM,CAAC,OAAO,WAAkB;IAEhC,MAAM,CAAC,OAAO,EAAE,cAAc,CAE7B;IAGO,SAAS,EAAE,MAAM,CAAA;IAEnB,GAAG;CAoCV"}
|
|
@@ -15,6 +15,7 @@ import { args, BaseCommand } from '@adonisjs/core/ace';
|
|
|
15
15
|
export default class Inspector extends BaseCommand {
|
|
16
16
|
static commandName = 'mcp:inspector';
|
|
17
17
|
static description = 'Open the MCP Inspector tool to debug and test MCP Servers.';
|
|
18
|
+
static aliases = ['mcp:inspect'];
|
|
18
19
|
static options = {
|
|
19
20
|
startApp: true,
|
|
20
21
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../../commands/make/prompt.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,EAAQ,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAItD,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,WAAW;IACjD,MAAM,CAAC,WAAW,SAAoB;IACtC,MAAM,CAAC,WAAW,SAA4B;IAE9C,MAAM,CAAC,OAAO,EAAE,cAAc,CAE7B;IAGO,IAAI,EAAE,MAAM,CAAA;IAEd,GAAG;
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../../commands/make/prompt.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,EAAQ,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAItD,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,WAAW;IACjD,MAAM,CAAC,WAAW,SAAoB;IACtC,MAAM,CAAC,WAAW,SAA4B;IAE9C,MAAM,CAAC,OAAO,EAAE,cAAc,CAE7B;IAGO,IAAI,EAAE,MAAM,CAAA;IAEd,GAAG;CAcV"}
|
|
@@ -21,7 +21,10 @@ export default class MakePrompt extends BaseCommand {
|
|
|
21
21
|
};
|
|
22
22
|
async run() {
|
|
23
23
|
const codemods = await this.createCodemods();
|
|
24
|
-
|
|
24
|
+
let stubPath = `make/mcp/prompts/main.ts.stub`;
|
|
25
|
+
if (this.app.usingVineJS) {
|
|
26
|
+
stubPath = `make/mcp/prompts/with_vine.ts.stub`;
|
|
27
|
+
}
|
|
25
28
|
await codemods.makeUsingStub(stubsRoot, stubPath, {
|
|
26
29
|
name: string.pascalCase(this.name),
|
|
27
30
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../../commands/make/tool.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,EAAQ,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAItD,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,WAAW;IAC/C,MAAM,CAAC,WAAW,SAAkB;IACpC,MAAM,CAAC,WAAW,SAA0B;IAE5C,MAAM,CAAC,OAAO,EAAE,cAAc,CAE7B;IAGO,IAAI,EAAE,MAAM,CAAA;IAEd,GAAG;
|
|
1
|
+
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../../commands/make/tool.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,EAAQ,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAItD,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,WAAW;IAC/C,MAAM,CAAC,WAAW,SAAkB;IACpC,MAAM,CAAC,WAAW,SAA0B;IAE5C,MAAM,CAAC,OAAO,EAAE,cAAc,CAE7B;IAGO,IAAI,EAAE,MAAM,CAAA;IAEd,GAAG;CAcV"}
|
|
@@ -21,7 +21,10 @@ export default class MakeTool extends BaseCommand {
|
|
|
21
21
|
};
|
|
22
22
|
async run() {
|
|
23
23
|
const codemods = await this.createCodemods();
|
|
24
|
-
|
|
24
|
+
let stubPath = `make/mcp/tools/main.ts.stub`;
|
|
25
|
+
if (this.app.usingVineJS) {
|
|
26
|
+
stubPath = `make/mcp/tools/with_vine.ts.stub`;
|
|
27
|
+
}
|
|
25
28
|
await codemods.makeUsingStub(stubsRoot, stubPath, {
|
|
26
29
|
name: string.pascalCase(this.name),
|
|
27
30
|
});
|
package/build/configure.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,SAAS,MAAM,mCAAmC,CAAA;
|
|
1
|
+
{"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,SAAS,MAAM,mCAAmC,CAAA;AAI9D,wBAAsB,SAAS,CAAC,OAAO,EAAE,SAAS,iBAsCjD"}
|
package/build/configure.js
CHANGED
|
@@ -4,9 +4,19 @@
|
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
6
|
*/
|
|
7
|
+
import { mkdir } from 'node:fs/promises';
|
|
7
8
|
import { stubsRoot } from './stubs/main.js';
|
|
8
9
|
export async function configure(command) {
|
|
9
10
|
const codemods = await command.createCodemods();
|
|
11
|
+
/**
|
|
12
|
+
* Create mcp directory if it doesn't exist
|
|
13
|
+
*/
|
|
14
|
+
const mcpDirectory = command.app.rcFile.directories['mcp'] || 'app/mcp/';
|
|
15
|
+
await Promise.all([
|
|
16
|
+
mkdir(command.app.makePath(mcpDirectory, 'tools'), { recursive: true }),
|
|
17
|
+
mkdir(command.app.makePath(mcpDirectory, 'resources'), { recursive: true }),
|
|
18
|
+
mkdir(command.app.makePath(mcpDirectory, 'prompts'), { recursive: true }),
|
|
19
|
+
]);
|
|
10
20
|
/**
|
|
11
21
|
* Create default config file
|
|
12
22
|
*/
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { ApplicationService } from '@adonisjs/core/types';
|
|
8
8
|
import type { RouteGroup } from '@adonisjs/core/http';
|
|
9
|
+
import type { JsonRpcRequest, JsonRpcResponse } from '../src/types/jsonrpc.js';
|
|
9
10
|
import McpServer from '../src/server.js';
|
|
10
11
|
export default class McpProvider {
|
|
11
12
|
protected app: ApplicationService;
|
|
@@ -26,5 +27,9 @@ declare module '@adonisjs/core/types' {
|
|
|
26
27
|
interface ContainerBindings {
|
|
27
28
|
'jrmc.mcp': McpServer;
|
|
28
29
|
}
|
|
30
|
+
interface EventsList {
|
|
31
|
+
'mcp:request': JsonRpcRequest;
|
|
32
|
+
'mcp:response': JsonRpcResponse;
|
|
33
|
+
}
|
|
29
34
|
}
|
|
30
35
|
//# sourceMappingURL=mcp_provider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp_provider.d.ts","sourceRoot":"","sources":["../../providers/mcp_provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"mcp_provider.d.ts","sourceRoot":"","sources":["../../providers/mcp_provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAErD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAK9E,OAAO,SAAS,MAAM,kBAAkB,CAAA;AAExC,MAAM,CAAC,OAAO,OAAO,WAAW;IAClB,SAAS,CAAC,GAAG,EAAE,kBAAkB;gBAAvB,GAAG,EAAE,kBAAkB;IAE7C,QAAQ;IAUF,KAAK;IAeL,aAAa;IAIb,iBAAiB;IAIjB,eAAe;IAIf,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,QAAQ;CA6C3D;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,MAAM;QACd,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,UAAU,CAAA;KACtC;CACF;AAED,OAAO,QAAQ,sBAAsB,CAAC;IACpC,UAAU,iBAAiB;QACzB,UAAU,EAAE,SAAS,CAAA;KACtB;IAED,UAAU,UAAU;QAClB,aAAa,EAAE,cAAc,CAAA;QAC7B,cAAc,EAAE,eAAe,CAAA;KAChC;CACF"}
|
|
@@ -16,7 +16,9 @@ export default class McpProvider {
|
|
|
16
16
|
register() {
|
|
17
17
|
this.app.container.singleton('jrmc.mcp', async () => {
|
|
18
18
|
const config = this.app.config.get('mcp', {});
|
|
19
|
-
|
|
19
|
+
const logger = await this.app.container.make('logger');
|
|
20
|
+
const emitter = await this.app.container.make('emitter');
|
|
21
|
+
return new McpServer(config, { logger, emitter });
|
|
20
22
|
});
|
|
21
23
|
}
|
|
22
24
|
async start() {
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jrmc/adonis-mcp
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
|
+
*/
|
|
7
|
+
import { type Bouncer, AuthorizationResponse } from '@adonisjs/bouncer';
|
|
8
|
+
import type { LazyImport, Constructor, BouncerAbility, AuthorizerResponse, NarrowAbilitiesForAUser } from '@adonisjs/bouncer/types';
|
|
9
|
+
/**
|
|
10
|
+
* Given a Bouncer instance type, extract the return type of `.with(policy)`
|
|
11
|
+
* so we can wrap it without importing the unexported PolicyAuthorizer class.
|
|
12
|
+
*/
|
|
13
|
+
type PolicyAuthorizerOf<B extends Bouncer<any, any, any>, Policy> = B extends {
|
|
14
|
+
with(policy: Policy): infer R;
|
|
15
|
+
} ? R : never;
|
|
16
|
+
/**
|
|
17
|
+
* Replaces each async method's return‑type with one that converts
|
|
18
|
+
* E_AUTHORIZATION_FAILURE → JsonRpcException, while keeping the
|
|
19
|
+
* original parameter signatures intact.
|
|
20
|
+
*/
|
|
21
|
+
type WrappedPolicyAuthorizer<T> = {
|
|
22
|
+
[K in keyof T]: T[K] extends (...args: infer A) => Promise<infer R> ? (...args: A) => Promise<R> : T[K];
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* McpBouncer wraps the AdonisJS Bouncer instance and converts
|
|
26
|
+
* authorization exceptions (`E_AUTHORIZATION_FAILURE`) into
|
|
27
|
+
* `JsonRpcException` so they are properly serialized as JSON-RPC
|
|
28
|
+
* error responses instead of leaking HTTP-centric error handling.
|
|
29
|
+
*
|
|
30
|
+
* Every public method signature is kept identical to the original
|
|
31
|
+
* Bouncer class so that consumers get full type-safety.
|
|
32
|
+
*/
|
|
33
|
+
export default class McpBouncer<User extends Record<any, any>, Abilities extends Record<string, BouncerAbility<any>> | undefined = undefined, Policies extends Record<string, LazyImport<Constructor<any>>> | undefined = undefined> {
|
|
34
|
+
#private;
|
|
35
|
+
constructor(bouncer: Bouncer<User, Abilities, Policies>, requestId: string | number);
|
|
36
|
+
/**
|
|
37
|
+
* Access the underlying bouncer abilities.
|
|
38
|
+
*/
|
|
39
|
+
get abilities(): Abilities | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Access the underlying bouncer policies.
|
|
42
|
+
*/
|
|
43
|
+
get policies(): Policies | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Returns a wrapped PolicyAuthorizer for a pre-registered policy name.
|
|
46
|
+
*/
|
|
47
|
+
with<Policy extends keyof Policies>(policy: Policy): Policies extends Record<string, LazyImport<Constructor<any>>> ? WrappedPolicyAuthorizer<PolicyAuthorizerOf<Bouncer<User, Abilities, Policies>, Policy>> : never;
|
|
48
|
+
/**
|
|
49
|
+
* Returns a wrapped PolicyAuthorizer for a policy class constructor.
|
|
50
|
+
*/
|
|
51
|
+
with<Policy extends Constructor<any>>(policy: Policy): WrappedPolicyAuthorizer<PolicyAuthorizerOf<Bouncer<User, Abilities, Policies>, Policy>>;
|
|
52
|
+
/**
|
|
53
|
+
* Execute an ability by reference.
|
|
54
|
+
*/
|
|
55
|
+
execute<Ability extends BouncerAbility<User>>(ability: Ability, ...args: Ability extends {
|
|
56
|
+
original: (user: User, ...args: infer Args) => AuthorizerResponse | Promise<AuthorizerResponse>;
|
|
57
|
+
} ? Args : never): Promise<AuthorizationResponse>;
|
|
58
|
+
/**
|
|
59
|
+
* Execute an ability from the list of pre-defined abilities.
|
|
60
|
+
*/
|
|
61
|
+
execute<Ability extends NarrowAbilitiesForAUser<User, Abilities>>(ability: Ability, ...args: Abilities[Ability] extends {
|
|
62
|
+
original: (user: User, ...args: infer Args) => AuthorizerResponse | Promise<AuthorizerResponse>;
|
|
63
|
+
} ? Args : never): Promise<AuthorizationResponse>;
|
|
64
|
+
/**
|
|
65
|
+
* Check if a user is allowed to perform an action using
|
|
66
|
+
* the ability provided by reference.
|
|
67
|
+
*/
|
|
68
|
+
allows<Ability extends BouncerAbility<User>>(ability: Ability, ...args: Ability extends {
|
|
69
|
+
original: (user: User, ...args: infer Args) => AuthorizerResponse | Promise<AuthorizerResponse>;
|
|
70
|
+
} ? Args : never): Promise<boolean>;
|
|
71
|
+
/**
|
|
72
|
+
* Check if a user is allowed to perform an action using
|
|
73
|
+
* the ability from the pre-defined list of abilities.
|
|
74
|
+
*/
|
|
75
|
+
allows<Ability extends NarrowAbilitiesForAUser<User, Abilities>>(ability: Ability, ...args: Abilities[Ability] extends {
|
|
76
|
+
original: (user: User, ...args: infer Args) => AuthorizerResponse | Promise<AuthorizerResponse>;
|
|
77
|
+
} ? Args : never): Promise<boolean>;
|
|
78
|
+
/**
|
|
79
|
+
* Check if a user is denied from performing an action using
|
|
80
|
+
* the ability provided by reference.
|
|
81
|
+
*/
|
|
82
|
+
denies<Action extends BouncerAbility<User>>(action: Action, ...args: Action extends {
|
|
83
|
+
original: (user: User, ...args: infer Args) => AuthorizerResponse | Promise<AuthorizerResponse>;
|
|
84
|
+
} ? Args : never): Promise<boolean>;
|
|
85
|
+
/**
|
|
86
|
+
* Check if a user is denied from performing an action using
|
|
87
|
+
* the ability from the pre-defined list of abilities.
|
|
88
|
+
*/
|
|
89
|
+
denies<Action extends NarrowAbilitiesForAUser<User, Abilities>>(action: Action, ...args: Abilities[Action] extends {
|
|
90
|
+
original: (user: User, ...args: infer Args) => AuthorizerResponse | Promise<AuthorizerResponse>;
|
|
91
|
+
} ? Args : never): Promise<boolean>;
|
|
92
|
+
/**
|
|
93
|
+
* Authorize a user against a given ability.
|
|
94
|
+
*
|
|
95
|
+
* @throws {JsonRpcException}
|
|
96
|
+
*/
|
|
97
|
+
authorize<Action extends BouncerAbility<User>>(action: Action, ...args: Action extends {
|
|
98
|
+
original: (user: User, ...args: infer Args) => AuthorizerResponse | Promise<AuthorizerResponse>;
|
|
99
|
+
} ? Args : never): Promise<void>;
|
|
100
|
+
/**
|
|
101
|
+
* Authorize a user against a given ability.
|
|
102
|
+
*
|
|
103
|
+
* @throws {JsonRpcException}
|
|
104
|
+
*/
|
|
105
|
+
authorize<Ability extends NarrowAbilitiesForAUser<User, Abilities>>(ability: Ability, ...args: Abilities[Ability] extends {
|
|
106
|
+
original: (user: User, ...args: infer Args) => AuthorizerResponse | Promise<AuthorizerResponse>;
|
|
107
|
+
} ? Args : never): Promise<void>;
|
|
108
|
+
/**
|
|
109
|
+
* Create AuthorizationResponse to deny access.
|
|
110
|
+
* This is a pass-through since it doesn't throw.
|
|
111
|
+
*/
|
|
112
|
+
deny(message: string, status?: number): AuthorizationResponse;
|
|
113
|
+
}
|
|
114
|
+
export {};
|
|
115
|
+
//# sourceMappingURL=mcp_bouncer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp_bouncer.d.ts","sourceRoot":"","sources":["../../../src/server/mcp_bouncer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAU,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AAC/E,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,cAAc,EACd,kBAAkB,EAClB,uBAAuB,EACxB,MAAM,yBAAyB,CAAA;AAKhC;;;GAGG;AACH,KAAK,kBAAkB,CAAC,CAAC,SAAS,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC,SAAS;IAC5E,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;CAC9B,GACG,CAAC,GACD,KAAK,CAAA;AAET;;;;GAIG;AACH,KAAK,uBAAuB,CAAC,CAAC,IAAI;KAC/B,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC,GAC/D,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAC1B,CAAC,CAAC,CAAC,CAAC;CACT,CAAA;AAkDD;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,OAAO,UAAU,CAC7B,IAAI,SAAS,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,EAC7B,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,GAAG,SAAS,GAAG,SAAS,EAC7E,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,SAAS,GAAG,SAAS;;gBAKzE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAKnF;;OAEG;IACH,IAAI,SAAS,IAAI,SAAS,GAAG,SAAS,CAErC;IAED;;OAEG;IACH,IAAI,QAAQ,IAAI,QAAQ,GAAG,SAAS,CAEnC;IAMD;;OAEG;IACH,IAAI,CAAC,MAAM,SAAS,MAAM,QAAQ,EAChC,MAAM,EAAE,MAAM,GACb,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,GAC5D,uBAAuB,CAAC,kBAAkB,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC,GACvF,KAAK;IAET;;OAEG;IACH,IAAI,CAAC,MAAM,SAAS,WAAW,CAAC,GAAG,CAAC,EAClC,MAAM,EAAE,MAAM,GACb,uBAAuB,CAAC,kBAAkB,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;IAW1F;;OAEG;IACH,OAAO,CAAC,OAAO,SAAS,cAAc,CAAC,IAAI,CAAC,EAC1C,OAAO,EAAE,OAAO,EAChB,GAAG,IAAI,EAAE,OAAO,SAAS;QACvB,QAAQ,EAAE,CACR,IAAI,EAAE,IAAI,EACV,GAAG,IAAI,EAAE,MAAM,IAAI,KAChB,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;KACtD,GACG,IAAI,GACJ,KAAK,GACR,OAAO,CAAC,qBAAqB,CAAC;IAEjC;;OAEG;IACH,OAAO,CAAC,OAAO,SAAS,uBAAuB,CAAC,IAAI,EAAE,SAAS,CAAC,EAC9D,OAAO,EAAE,OAAO,EAChB,GAAG,IAAI,EAAE,SAAS,CAAC,OAAO,CAAC,SAAS;QAClC,QAAQ,EAAE,CACR,IAAI,EAAE,IAAI,EACV,GAAG,IAAI,EAAE,MAAM,IAAI,KAChB,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;KACtD,GACG,IAAI,GACJ,KAAK,GACR,OAAO,CAAC,qBAAqB,CAAC;IAcjC;;;OAGG;IACH,MAAM,CAAC,OAAO,SAAS,cAAc,CAAC,IAAI,CAAC,EACzC,OAAO,EAAE,OAAO,EAChB,GAAG,IAAI,EAAE,OAAO,SAAS;QACvB,QAAQ,EAAE,CACR,IAAI,EAAE,IAAI,EACV,GAAG,IAAI,EAAE,MAAM,IAAI,KAChB,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;KACtD,GACG,IAAI,GACJ,KAAK,GACR,OAAO,CAAC,OAAO,CAAC;IAEnB;;;OAGG;IACH,MAAM,CAAC,OAAO,SAAS,uBAAuB,CAAC,IAAI,EAAE,SAAS,CAAC,EAC7D,OAAO,EAAE,OAAO,EAChB,GAAG,IAAI,EAAE,SAAS,CAAC,OAAO,CAAC,SAAS;QAClC,QAAQ,EAAE,CACR,IAAI,EAAE,IAAI,EACV,GAAG,IAAI,EAAE,MAAM,IAAI,KAChB,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;KACtD,GACG,IAAI,GACJ,KAAK,GACR,OAAO,CAAC,OAAO,CAAC;IAcnB;;;OAGG;IACH,MAAM,CAAC,MAAM,SAAS,cAAc,CAAC,IAAI,CAAC,EACxC,MAAM,EAAE,MAAM,EACd,GAAG,IAAI,EAAE,MAAM,SAAS;QACtB,QAAQ,EAAE,CACR,IAAI,EAAE,IAAI,EACV,GAAG,IAAI,EAAE,MAAM,IAAI,KAChB,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;KACtD,GACG,IAAI,GACJ,KAAK,GACR,OAAO,CAAC,OAAO,CAAC;IAEnB;;;OAGG;IACH,MAAM,CAAC,MAAM,SAAS,uBAAuB,CAAC,IAAI,EAAE,SAAS,CAAC,EAC5D,MAAM,EAAE,MAAM,EACd,GAAG,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,SAAS;QACjC,QAAQ,EAAE,CACR,IAAI,EAAE,IAAI,EACV,GAAG,IAAI,EAAE,MAAM,IAAI,KAChB,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;KACtD,GACG,IAAI,GACJ,KAAK,GACR,OAAO,CAAC,OAAO,CAAC;IAcnB;;;;OAIG;IACH,SAAS,CAAC,MAAM,SAAS,cAAc,CAAC,IAAI,CAAC,EAC3C,MAAM,EAAE,MAAM,EACd,GAAG,IAAI,EAAE,MAAM,SAAS;QACtB,QAAQ,EAAE,CACR,IAAI,EAAE,IAAI,EACV,GAAG,IAAI,EAAE,MAAM,IAAI,KAChB,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;KACtD,GACG,IAAI,GACJ,KAAK,GACR,OAAO,CAAC,IAAI,CAAC;IAEhB;;;;OAIG;IACH,SAAS,CAAC,OAAO,SAAS,uBAAuB,CAAC,IAAI,EAAE,SAAS,CAAC,EAChE,OAAO,EAAE,OAAO,EAChB,GAAG,IAAI,EAAE,SAAS,CAAC,OAAO,CAAC,SAAS;QAClC,QAAQ,EAAE,CACR,IAAI,EAAE,IAAI,EACV,GAAG,IAAI,EAAE,MAAM,IAAI,KAChB,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;KACtD,GACG,IAAI,GACJ,KAAK,GACR,OAAO,CAAC,IAAI,CAAC;IAchB;;;OAGG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,qBAAqB;CAG9D"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jrmc/adonis-mcp
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
|
+
*/
|
|
7
|
+
import { errors } from '@adonisjs/bouncer';
|
|
8
|
+
import { ErrorCode } from '../enums/error.js';
|
|
9
|
+
import JsonRpcException from './exceptions/jsonrpc_exception.js';
|
|
10
|
+
/**
|
|
11
|
+
* Convert any caught error to a JsonRpcException.
|
|
12
|
+
*/
|
|
13
|
+
function handleError(error, requestId) {
|
|
14
|
+
if (error instanceof errors.E_AUTHORIZATION_FAILURE) {
|
|
15
|
+
const message = error.response?.message || error.message || 'Access denied';
|
|
16
|
+
throw new JsonRpcException(message, ErrorCode.InvalidRequest, requestId);
|
|
17
|
+
}
|
|
18
|
+
if (error instanceof JsonRpcException) {
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
const message = error instanceof Error ? error.message : 'Authorization error';
|
|
22
|
+
throw new JsonRpcException(message, ErrorCode.InternalError, requestId);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Wrap a PolicyAuthorizer so that every async method (`execute`,
|
|
26
|
+
* `allows`, `denies`, `authorize`) converts authorization failures
|
|
27
|
+
* into JsonRpcException.
|
|
28
|
+
*/
|
|
29
|
+
function wrapPolicyAuthorizer(authorizer, requestId) {
|
|
30
|
+
const wrapped = ['execute', 'allows', 'denies', 'authorize'];
|
|
31
|
+
return new Proxy(authorizer, {
|
|
32
|
+
get(target, prop, receiver) {
|
|
33
|
+
const value = Reflect.get(target, prop, receiver);
|
|
34
|
+
if (typeof value === 'function' && typeof prop === 'string' && wrapped.includes(prop)) {
|
|
35
|
+
const fn = value;
|
|
36
|
+
return async (...args) => {
|
|
37
|
+
try {
|
|
38
|
+
return await fn.apply(target, args);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
handleError(error, requestId);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return value;
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* McpBouncer wraps the AdonisJS Bouncer instance and converts
|
|
51
|
+
* authorization exceptions (`E_AUTHORIZATION_FAILURE`) into
|
|
52
|
+
* `JsonRpcException` so they are properly serialized as JSON-RPC
|
|
53
|
+
* error responses instead of leaking HTTP-centric error handling.
|
|
54
|
+
*
|
|
55
|
+
* Every public method signature is kept identical to the original
|
|
56
|
+
* Bouncer class so that consumers get full type-safety.
|
|
57
|
+
*/
|
|
58
|
+
export default class McpBouncer {
|
|
59
|
+
#bouncer;
|
|
60
|
+
#requestId;
|
|
61
|
+
constructor(bouncer, requestId) {
|
|
62
|
+
this.#bouncer = bouncer;
|
|
63
|
+
this.#requestId = requestId;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Access the underlying bouncer abilities.
|
|
67
|
+
*/
|
|
68
|
+
get abilities() {
|
|
69
|
+
return this.#bouncer.abilities;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Access the underlying bouncer policies.
|
|
73
|
+
*/
|
|
74
|
+
get policies() {
|
|
75
|
+
return this.#bouncer.policies;
|
|
76
|
+
}
|
|
77
|
+
with(policy) {
|
|
78
|
+
const authorizer = this.#bouncer.with(policy);
|
|
79
|
+
return wrapPolicyAuthorizer(authorizer, this.#requestId);
|
|
80
|
+
}
|
|
81
|
+
async execute(ability, ...args) {
|
|
82
|
+
try {
|
|
83
|
+
return await this.#bouncer.execute(ability, ...args);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
handleError(error, this.#requestId);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async allows(ability, ...args) {
|
|
90
|
+
try {
|
|
91
|
+
return await this.#bouncer.allows(ability, ...args);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
handleError(error, this.#requestId);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async denies(action, ...args) {
|
|
98
|
+
try {
|
|
99
|
+
return await this.#bouncer.denies(action, ...args);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
handleError(error, this.#requestId);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async authorize(ability, ...args) {
|
|
106
|
+
try {
|
|
107
|
+
return await this.#bouncer.authorize(ability, ...args);
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
handleError(error, this.#requestId);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
// deny()
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
/**
|
|
117
|
+
* Create AuthorizationResponse to deny access.
|
|
118
|
+
* This is a pass-through since it doesn't throw.
|
|
119
|
+
*/
|
|
120
|
+
deny(message, status) {
|
|
121
|
+
return this.#bouncer.deny(message, status);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http_transport.d.ts","sourceRoot":"","sources":["../../../../src/server/transports/http_transport.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"http_transport.d.ts","sourceRoot":"","sources":["../../../../src/server/transports/http_transport.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAEtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAI7D,MAAM,CAAC,OAAO,OAAO,aAAc,YAAW,SAAS;;gBAIzC,GAAG,EAAE,WAAW;IAK5B,IAAI,SAAS,uBAEZ;IAED,WAAW,CAAC,UAAU,EAAE,UAAU;IAOlC,QAAQ,CAAC,UAAU,EAAE,UAAU;IAM/B,IAAI,CAAC,OAAO,EAAE,eAAe;CAG9B"}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
6
|
*/
|
|
7
|
+
import McpBouncer from '../mcp_bouncer.js';
|
|
7
8
|
export default class HttpTransport {
|
|
8
9
|
#ctx;
|
|
9
10
|
#sessionId;
|
|
@@ -16,13 +17,13 @@ export default class HttpTransport {
|
|
|
16
17
|
}
|
|
17
18
|
bindBouncer(mcpContext) {
|
|
18
19
|
if ('bouncer' in this.#ctx) {
|
|
19
|
-
|
|
20
|
-
mcpContext.bouncer =
|
|
20
|
+
const bouncer = this.#ctx.bouncer;
|
|
21
|
+
mcpContext.bouncer = new McpBouncer(bouncer, mcpContext.request.id);
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
bindAuth(mcpContext) {
|
|
24
25
|
if ('auth' in this.#ctx) {
|
|
25
|
-
|
|
26
|
+
;
|
|
26
27
|
mcpContext.auth = this.#ctx.auth;
|
|
27
28
|
}
|
|
28
29
|
}
|
package/build/src/server.d.ts
CHANGED
|
@@ -8,7 +8,12 @@ import type { McpConfig } from './types/config.js';
|
|
|
8
8
|
import type { JsonRpcRequest } from './types/jsonrpc.js';
|
|
9
9
|
import type { ToolList, ResourceList, PromptList } from './types/method.js';
|
|
10
10
|
import type { Transport } from './server/contracts/transport.js';
|
|
11
|
+
import type { LoggerService, EmitterService } from '@adonisjs/core/types';
|
|
11
12
|
import ServerContext from './server/context.js';
|
|
13
|
+
type Services = {
|
|
14
|
+
logger: LoggerService;
|
|
15
|
+
emitter: EmitterService;
|
|
16
|
+
};
|
|
12
17
|
export default class Server {
|
|
13
18
|
#private;
|
|
14
19
|
config: McpConfig;
|
|
@@ -35,7 +40,7 @@ export default class Server {
|
|
|
35
40
|
'completion/complete': () => Promise<typeof import("./server/methods/completion.js")>;
|
|
36
41
|
ping: () => Promise<typeof import("./server/methods/ping.js")>;
|
|
37
42
|
};
|
|
38
|
-
constructor(config: McpConfig);
|
|
43
|
+
constructor(config: McpConfig, services?: Services);
|
|
39
44
|
addCapability(key: string, value?: boolean): void;
|
|
40
45
|
addTool(item: ToolList): void;
|
|
41
46
|
addResource(item: ResourceList): void;
|
|
@@ -45,4 +50,5 @@ export default class Server {
|
|
|
45
50
|
handle(jsonRequest: JsonRpcRequest): Promise<void | null>;
|
|
46
51
|
createContext(jsonRpcRequest: JsonRpcRequest): ServerContext;
|
|
47
52
|
}
|
|
53
|
+
export {};
|
|
48
54
|
//# sourceMappingURL=server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EACZ,UAAU,EAEX,MAAM,mBAAmB,CAAA;AAC1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAA;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EACZ,UAAU,EAEX,MAAM,mBAAmB,CAAA;AAC1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAA;AAChE,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAIzE,OAAO,aAAa,MAAM,qBAAqB,CAAA;AAG/C,KAAK,QAAQ,GAAG;IACd,MAAM,EAAE,aAAa,CAAA;IACrB,OAAO,EAAE,cAAc,CAAA;CACxB,CAAA;AAED,MAAM,CAAC,OAAO,OAAO,MAAM;;IAKzB,MAAM,EAAE,SAAS,CAAA;IACjB,IAAI,EAAE,MAAM,CAAwB;IACpC,OAAO,EAAE,MAAM,CAAU;IACzB,YAAY,EAAE,MAAM,CAA2E;IAE/F,wBAAwB,EAAE,MAAM,EAAE,CAA+B;IAEjE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAUhC;IAED,KAAK,EAAE,QAAQ,CAAK;IACpB,SAAS,EAAE,YAAY,CAAK;IAC5B,iBAAiB,EAAE,YAAY,CAAK;IACpC,OAAO,EAAE,UAAU,CAAK;IAExB,mBAAmB,EAAE,MAAM,CAAK;IAChC,uBAAuB,EAAE,MAAM,CAAK;IAEpC,OAAO;;;;;;;;;;;MAWN;gBAEW,MAAM,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,QAAQ;IA+BlD,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,GAAE,OAAc;IAchD,OAAO,CAAC,IAAI,EAAE,QAAQ;IAGtB,WAAW,CAAC,IAAI,EAAE,YAAY;IAG9B,mBAAmB,CAAC,IAAI,EAAE,YAAY;IAGtC,SAAS,CAAC,IAAI,EAAE,UAAU;IAIpB,OAAO,CAAC,SAAS,EAAE,SAAS;IAI5B,MAAM,CAAC,WAAW,EAAE,cAAc;IAyDxC,aAAa,CAAC,cAAc,EAAE,cAAc;CAgB7C"}
|
package/build/src/server.js
CHANGED
|
@@ -9,6 +9,8 @@ import { ErrorCode } from './enums/error.js';
|
|
|
9
9
|
import ServerContext from './server/context.js';
|
|
10
10
|
import JsonRpcException from './server/exceptions/jsonrpc_exception.js';
|
|
11
11
|
export default class Server {
|
|
12
|
+
#logger;
|
|
13
|
+
#emitter;
|
|
12
14
|
#transport;
|
|
13
15
|
config;
|
|
14
16
|
name = 'AdonisJS MCP Server';
|
|
@@ -44,7 +46,7 @@ export default class Server {
|
|
|
44
46
|
'completion/complete': () => import('./server/methods/completion.js'),
|
|
45
47
|
'ping': () => import('./server/methods/ping.js'),
|
|
46
48
|
};
|
|
47
|
-
constructor(config) {
|
|
49
|
+
constructor(config, services) {
|
|
48
50
|
this.config = config;
|
|
49
51
|
if (config.name) {
|
|
50
52
|
this.name = config.name;
|
|
@@ -64,6 +66,8 @@ export default class Server {
|
|
|
64
66
|
if (config.completions) {
|
|
65
67
|
this.addCapability('completions');
|
|
66
68
|
}
|
|
69
|
+
this.#logger = services?.logger;
|
|
70
|
+
this.#emitter = services?.emitter;
|
|
67
71
|
}
|
|
68
72
|
addCapability(key, value = true) {
|
|
69
73
|
if (key.includes('.')) {
|
|
@@ -91,6 +95,7 @@ export default class Server {
|
|
|
91
95
|
this.#transport = transport;
|
|
92
96
|
}
|
|
93
97
|
async handle(jsonRequest) {
|
|
98
|
+
this.#emitter?.emit('mcp:request', jsonRequest);
|
|
94
99
|
// INGORE NOTIFICATIONS FOR NOW
|
|
95
100
|
if (jsonRequest.method.startsWith('notifications/')) {
|
|
96
101
|
return null;
|
|
@@ -111,19 +116,31 @@ export default class Server {
|
|
|
111
116
|
const instance = new MethodClass();
|
|
112
117
|
// Ensure the instance is treated as MethodInterface when calling handle to satisfy TS
|
|
113
118
|
const response = await instance.handle(mcpContext);
|
|
119
|
+
this.#emitter?.emit('mcp:response', response);
|
|
114
120
|
this.#transport.send(response);
|
|
115
121
|
}
|
|
116
122
|
else {
|
|
117
|
-
|
|
123
|
+
const jsonRpcException = new JsonRpcException(`The method ${jsonRequest.method} was not found.`, ErrorCode.MethodNotFound, jsonRequest.id);
|
|
124
|
+
throw jsonRpcException;
|
|
118
125
|
}
|
|
119
126
|
}
|
|
120
127
|
catch (error) {
|
|
128
|
+
let response;
|
|
121
129
|
if (error instanceof JsonRpcException) {
|
|
122
|
-
|
|
130
|
+
response = error.toJsonRpcResponse();
|
|
131
|
+
this.#logger?.error(response, error.message);
|
|
123
132
|
}
|
|
124
|
-
|
|
125
|
-
error,
|
|
126
|
-
|
|
133
|
+
else {
|
|
134
|
+
response = new JsonRpcException('Internal error', ErrorCode.InternalError, jsonRequest.id, {
|
|
135
|
+
error: {
|
|
136
|
+
name: error.name,
|
|
137
|
+
message: error.message,
|
|
138
|
+
},
|
|
139
|
+
}).toJsonRpcResponse();
|
|
140
|
+
this.#logger?.error(error);
|
|
141
|
+
}
|
|
142
|
+
this.#emitter?.emit('mcp:response', response);
|
|
143
|
+
return this.#transport.send(response);
|
|
127
144
|
}
|
|
128
145
|
}
|
|
129
146
|
createContext(jsonRpcRequest) {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{{#var className = `${name}Prompt`}}
|
|
2
|
+
{{#var promptName = string(name).snakeCase()}}
|
|
3
|
+
{{#var resourceFileName = string(className).snakeCase().ext('.ts').toString()}}
|
|
4
|
+
{{{
|
|
5
|
+
exports({
|
|
6
|
+
to: app.makePath(app.rcFile.directories['mcp'] || 'app/mcp/', `prompts/`, resourceFileName)
|
|
7
|
+
})
|
|
8
|
+
}}}
|
|
9
|
+
import type { PromptContext } from '@jrmc/adonis-mcp/types/context'
|
|
10
|
+
import type { BaseSchema } from '@jrmc/adonis-mcp/types/method'
|
|
11
|
+
|
|
12
|
+
import { Prompt } from '@jrmc/adonis-mcp'
|
|
13
|
+
import vine from '@vinejs/vine'
|
|
14
|
+
|
|
15
|
+
type Schema = BaseSchema<{
|
|
16
|
+
text: { type: "string" }
|
|
17
|
+
}>
|
|
18
|
+
|
|
19
|
+
const vineSchema = vine.object({
|
|
20
|
+
text: vine.string().meta({
|
|
21
|
+
description: 'Description text argument',
|
|
22
|
+
}),
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
export default class {{ className }} extends Prompt<Schema> {
|
|
26
|
+
name = '{{ promptName }}'
|
|
27
|
+
title = 'Prompt title'
|
|
28
|
+
description = 'Prompt description'
|
|
29
|
+
|
|
30
|
+
async handle({ args, response }: PromptContext<Schema>) {
|
|
31
|
+
return [
|
|
32
|
+
response.text('Hello, world!'),
|
|
33
|
+
response.text(args?.text!)
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
schema() {
|
|
38
|
+
return vine.create(vineSchema).toJSONSchema() as Schema
|
|
39
|
+
}
|
|
40
|
+
}
|