@rudderjs/mcp 0.0.1 → 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 +68 -0
- package/dist/Mcp.d.ts +21 -5
- package/dist/Mcp.d.ts.map +1 -1
- package/dist/Mcp.js +13 -1
- package/dist/Mcp.js.map +1 -1
- package/dist/McpPrompt.d.ts +8 -5
- package/dist/McpPrompt.d.ts.map +1 -1
- package/dist/McpPrompt.js.map +1 -1
- package/dist/McpResource.d.ts +9 -3
- package/dist/McpResource.d.ts.map +1 -1
- package/dist/McpResource.js +4 -0
- package/dist/McpResource.js.map +1 -1
- package/dist/McpTool.d.ts +17 -5
- package/dist/McpTool.d.ts.map +1 -1
- package/dist/McpTool.js.map +1 -1
- package/dist/auth/oauth2.d.ts +25 -0
- package/dist/auth/oauth2.d.ts.map +1 -0
- package/dist/auth/oauth2.js +128 -0
- package/dist/auth/oauth2.js.map +1 -0
- package/dist/commands/inspector-ui.d.ts +2 -0
- package/dist/commands/inspector-ui.d.ts.map +1 -0
- package/dist/commands/inspector-ui.js +300 -0
- package/dist/commands/inspector-ui.js.map +1 -0
- package/dist/commands/inspector.d.ts +5 -0
- package/dist/commands/inspector.d.ts.map +1 -0
- package/dist/commands/inspector.js +226 -0
- package/dist/commands/inspector.js.map +1 -0
- package/dist/commands/make-mcp-prompt.d.ts +3 -0
- package/dist/commands/make-mcp-prompt.d.ts.map +1 -0
- package/dist/commands/make-mcp-prompt.js +27 -0
- package/dist/commands/make-mcp-prompt.js.map +1 -0
- package/dist/commands/make-mcp-resource.d.ts +3 -0
- package/dist/commands/make-mcp-resource.d.ts.map +1 -0
- package/dist/commands/make-mcp-resource.js +26 -0
- package/dist/commands/make-mcp-resource.js.map +1 -0
- package/dist/commands/make-mcp-server.d.ts +3 -0
- package/dist/commands/make-mcp-server.d.ts.map +1 -0
- package/dist/commands/make-mcp-server.js +28 -0
- package/dist/commands/make-mcp-server.js.map +1 -0
- package/dist/commands/make-mcp-tool.d.ts +3 -0
- package/dist/commands/make-mcp-tool.d.ts.map +1 -0
- package/dist/commands/make-mcp-tool.js +25 -0
- package/dist/commands/make-mcp-tool.js.map +1 -0
- package/dist/decorators.d.ts +17 -0
- package/dist/decorators.d.ts.map +1 -1
- package/dist/decorators.js +22 -0
- package/dist/decorators.js.map +1 -1
- package/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/observers.d.ts +32 -0
- package/dist/observers.d.ts.map +1 -0
- package/dist/observers.js +31 -0
- package/dist/observers.js.map +1 -0
- package/dist/provider.d.ts +4 -2
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +78 -40
- package/dist/provider.js.map +1 -1
- package/dist/runtime.d.ts +30 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +267 -24
- package/dist/runtime.js.map +1 -1
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +3 -1
- package/dist/testing.js.map +1 -1
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/zod-to-json-schema.d.ts +4 -3
- package/dist/zod-to-json-schema.d.ts.map +1 -1
- package/dist/zod-to-json-schema.js +54 -24
- package/dist/zod-to-json-schema.js.map +1 -1
- package/package.json +41 -3
package/README.md
CHANGED
|
@@ -88,6 +88,70 @@ export class DescribeWeatherPrompt extends McpPrompt {
|
|
|
88
88
|
}
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
+
## Output Schemas
|
|
92
|
+
|
|
93
|
+
Tools can declare an output schema to advertise the structure of their response:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
@Description('Get current weather data.')
|
|
97
|
+
export class CurrentWeatherTool extends McpTool {
|
|
98
|
+
schema() {
|
|
99
|
+
return z.object({ location: z.string() })
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
outputSchema() {
|
|
103
|
+
return z.object({
|
|
104
|
+
temperature: z.number(),
|
|
105
|
+
conditions: z.string(),
|
|
106
|
+
humidity: z.number(),
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async handle(input: Record<string, unknown>) {
|
|
111
|
+
return McpResponse.json({ temperature: 22, conditions: 'sunny', humidity: 45 })
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Resource URI Templates
|
|
117
|
+
|
|
118
|
+
Resources can use `{param}` placeholders for dynamic data:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
@Description('Weather data for a specific city')
|
|
122
|
+
export class CityWeatherResource extends McpResource {
|
|
123
|
+
uri() { return 'weather://city/{name}' }
|
|
124
|
+
|
|
125
|
+
async handle(params?: Record<string, string>) {
|
|
126
|
+
const city = params?.name ?? 'unknown'
|
|
127
|
+
return `Weather in ${city}: sunny, 22°C`
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Template resources are automatically registered via `ListResourceTemplates`. Parameters are extracted from the URI and passed to `handle()`.
|
|
133
|
+
|
|
134
|
+
## Dependency Injection
|
|
135
|
+
|
|
136
|
+
When running inside a RudderJS app, tool/resource/prompt classes are resolved via the DI container — constructor dependencies are auto-injected:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
@Injectable()
|
|
140
|
+
@Description('Query the database')
|
|
141
|
+
export class DbQueryTool extends McpTool {
|
|
142
|
+
constructor(private db: DatabaseService) { super() }
|
|
143
|
+
|
|
144
|
+
schema() { return z.object({ query: z.string() }) }
|
|
145
|
+
|
|
146
|
+
async handle(input: Record<string, unknown>) {
|
|
147
|
+
const result = await this.db.query(input.query as string)
|
|
148
|
+
return McpResponse.json(result)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Falls back to plain `new T()` when the DI container is not available.
|
|
154
|
+
|
|
91
155
|
## Registration
|
|
92
156
|
|
|
93
157
|
```ts
|
|
@@ -98,6 +162,10 @@ import { WeatherServer } from '../app/Mcp/Servers/WeatherServer.js'
|
|
|
98
162
|
// HTTP endpoint (Streamable HTTP transport)
|
|
99
163
|
Mcp.web('/mcp/weather', WeatherServer)
|
|
100
164
|
|
|
165
|
+
// With middleware (auth, rate limiting, etc.)
|
|
166
|
+
Mcp.web('/mcp/weather', WeatherServer)
|
|
167
|
+
.middleware([authMiddleware])
|
|
168
|
+
|
|
101
169
|
// Local CLI command (stdio transport)
|
|
102
170
|
Mcp.local('weather', WeatherServer)
|
|
103
171
|
```
|
package/dist/Mcp.d.ts
CHANGED
|
@@ -1,16 +1,32 @@
|
|
|
1
1
|
import type { McpServer } from './McpServer.js';
|
|
2
|
+
import type { OAuth2McpOptions } from './auth/oauth2.js';
|
|
2
3
|
type ServerClass = new () => McpServer;
|
|
4
|
+
export interface McpWebEntry {
|
|
5
|
+
server: ServerClass;
|
|
6
|
+
middleware: unknown[];
|
|
7
|
+
/** Set when `.oauth2()` was chained on the builder. */
|
|
8
|
+
oauth2?: OAuth2McpOptions;
|
|
9
|
+
}
|
|
10
|
+
export interface McpWebBuilder {
|
|
11
|
+
/** Add middleware to this web MCP endpoint. */
|
|
12
|
+
middleware(mw: unknown[]): McpWebBuilder;
|
|
13
|
+
/**
|
|
14
|
+
* Protect this endpoint with OAuth 2.1 bearer tokens. Registers an RFC 9728
|
|
15
|
+
* Protected Resource Metadata endpoint alongside it.
|
|
16
|
+
*
|
|
17
|
+
* Requires `@rudderjs/passport` to be installed (used as the authorization
|
|
18
|
+
* server and token validator).
|
|
19
|
+
*/
|
|
20
|
+
oauth2(options?: OAuth2McpOptions): McpWebBuilder;
|
|
21
|
+
}
|
|
3
22
|
export declare class Mcp {
|
|
4
23
|
private static webServers;
|
|
5
24
|
private static localServers;
|
|
6
25
|
/** Register an MCP server on an HTTP endpoint (Streamable HTTP transport) */
|
|
7
|
-
static web(path: string, server: ServerClass, middleware?: unknown[]):
|
|
26
|
+
static web(path: string, server: ServerClass, middleware?: unknown[]): McpWebBuilder;
|
|
8
27
|
/** Register an MCP server as a local CLI command (stdio transport) */
|
|
9
28
|
static local(name: string, server: ServerClass): void;
|
|
10
|
-
static getWebServers(): Map<string,
|
|
11
|
-
server: ServerClass;
|
|
12
|
-
middleware: unknown[];
|
|
13
|
-
}>;
|
|
29
|
+
static getWebServers(): Map<string, McpWebEntry>;
|
|
14
30
|
static getLocalServers(): Map<string, ServerClass>;
|
|
15
31
|
}
|
|
16
32
|
export {};
|
package/dist/Mcp.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Mcp.d.ts","sourceRoot":"","sources":["../src/Mcp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"Mcp.d.ts","sourceRoot":"","sources":["../src/Mcp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAExD,KAAK,WAAW,GAAG,UAAU,SAAS,CAAA;AAEtC,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,WAAW,CAAA;IACnB,UAAU,EAAE,OAAO,EAAE,CAAA;IACrB,uDAAuD;IACvD,MAAM,CAAC,EAAE,gBAAgB,CAAA;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,+CAA+C;IAC/C,UAAU,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,aAAa,CAAA;IACxC;;;;;;OAMG;IACH,MAAM,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,aAAa,CAAA;CAClD;AAED,qBAAa,GAAG;IACd,OAAO,CAAC,MAAM,CAAC,UAAU,CAAsC;IAC/D,OAAO,CAAC,MAAM,CAAC,YAAY,CAAsC;IAEjE,6EAA6E;IAC7E,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,GAAE,OAAO,EAAO,GAAG,aAAa;IAgBxF,sEAAsE;IACtE,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI;IAIrD,MAAM,CAAC,aAAa,IAAI,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC;IAChD,MAAM,CAAC,eAAe,IAAI,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC;CACnD"}
|
package/dist/Mcp.js
CHANGED
|
@@ -3,7 +3,19 @@ export class Mcp {
|
|
|
3
3
|
static localServers = new Map();
|
|
4
4
|
/** Register an MCP server on an HTTP endpoint (Streamable HTTP transport) */
|
|
5
5
|
static web(path, server, middleware = []) {
|
|
6
|
-
|
|
6
|
+
const entry = { server, middleware };
|
|
7
|
+
this.webServers.set(path, entry);
|
|
8
|
+
const builder = {
|
|
9
|
+
middleware(mw) {
|
|
10
|
+
entry.middleware.push(...mw);
|
|
11
|
+
return builder;
|
|
12
|
+
},
|
|
13
|
+
oauth2(options = {}) {
|
|
14
|
+
entry.oauth2 = options;
|
|
15
|
+
return builder;
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
return builder;
|
|
7
19
|
}
|
|
8
20
|
/** Register an MCP server as a local CLI command (stdio transport) */
|
|
9
21
|
static local(name, server) {
|
package/dist/Mcp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Mcp.js","sourceRoot":"","sources":["../src/Mcp.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Mcp.js","sourceRoot":"","sources":["../src/Mcp.ts"],"names":[],"mappings":"AAyBA,MAAM,OAAO,GAAG;IACN,MAAM,CAAC,UAAU,GAA6B,IAAI,GAAG,EAAE,CAAA;IACvD,MAAM,CAAC,YAAY,GAA6B,IAAI,GAAG,EAAE,CAAA;IAEjE,6EAA6E;IAC7E,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,MAAmB,EAAE,aAAwB,EAAE;QACtE,MAAM,KAAK,GAAgB,EAAE,MAAM,EAAE,UAAU,EAAE,CAAA;QACjD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;QAChC,MAAM,OAAO,GAAkB;YAC7B,UAAU,CAAC,EAAa;gBACtB,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;gBAC5B,OAAO,OAAO,CAAA;YAChB,CAAC;YACD,MAAM,CAAC,UAA4B,EAAE;gBACnC,KAAK,CAAC,MAAM,GAAG,OAAO,CAAA;gBACtB,OAAO,OAAO,CAAA;YAChB,CAAC;SACF,CAAA;QACD,OAAO,OAAO,CAAA;IAChB,CAAC;IAED,sEAAsE;IACtE,MAAM,CAAC,KAAK,CAAC,IAAY,EAAE,MAAmB;QAC5C,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IACrC,CAAC;IAED,MAAM,CAAC,aAAa,KAA+B,OAAO,IAAI,CAAC,UAAU,CAAA,CAAC,CAAC;IAC3E,MAAM,CAAC,eAAe,KAA+B,OAAO,IAAI,CAAC,YAAY,CAAA,CAAC,CAAC"}
|
package/dist/McpPrompt.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ZodLikeObject } from './types.js';
|
|
2
2
|
export interface McpPromptMessage {
|
|
3
3
|
role: 'user' | 'assistant';
|
|
4
4
|
content: string;
|
|
@@ -8,9 +8,12 @@ export declare abstract class McpPrompt {
|
|
|
8
8
|
name(): string;
|
|
9
9
|
/** Description */
|
|
10
10
|
description(): string;
|
|
11
|
-
/** Arguments schema */
|
|
12
|
-
arguments?():
|
|
13
|
-
/**
|
|
14
|
-
|
|
11
|
+
/** Arguments schema — a Zod object (v3 or v4). */
|
|
12
|
+
arguments?(): ZodLikeObject;
|
|
13
|
+
/**
|
|
14
|
+
* Generate prompt messages. Extra parameters beyond `args` are resolved
|
|
15
|
+
* from the DI container when the method is decorated with `@Handle()`.
|
|
16
|
+
*/
|
|
17
|
+
abstract handle(args: Record<string, unknown>, ...deps: unknown[]): Promise<McpPromptMessage[]>;
|
|
15
18
|
}
|
|
16
19
|
//# sourceMappingURL=McpPrompt.d.ts.map
|
package/dist/McpPrompt.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpPrompt.d.ts","sourceRoot":"","sources":["../src/McpPrompt.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"McpPrompt.d.ts","sourceRoot":"","sources":["../src/McpPrompt.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,GAAG,WAAW,CAAA;IAC1B,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,8BAAsB,SAAS;IAC7B,kBAAkB;IAClB,IAAI,IAAI,MAAM;IAId,kBAAkB;IAClB,WAAW,IAAI,MAAM;IAIrB,kDAAkD;IAClD,SAAS,CAAC,IAAI,aAAa;IAE3B;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;CAChG"}
|
package/dist/McpPrompt.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpPrompt.js","sourceRoot":"","sources":["../src/McpPrompt.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"McpPrompt.js","sourceRoot":"","sources":["../src/McpPrompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAQhD,MAAM,OAAgB,SAAS;IAC7B,kBAAkB;IAClB,IAAI;QACF,OAAO,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAA;IAClE,CAAC;IAED,kBAAkB;IAClB,WAAW;QACT,OAAO,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAA;IAC/C,CAAC;CAUF"}
|
package/dist/McpResource.d.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
export declare abstract class McpResource {
|
|
2
|
-
/** Resource URI pattern */
|
|
2
|
+
/** Resource URI pattern — can contain `{param}` placeholders for templates */
|
|
3
3
|
abstract uri(): string;
|
|
4
4
|
/** MIME type */
|
|
5
5
|
mimeType(): string;
|
|
6
6
|
/** Resource description */
|
|
7
7
|
description(): string;
|
|
8
|
-
/**
|
|
9
|
-
|
|
8
|
+
/** Whether this resource uses URI templates (has `{param}` placeholders) */
|
|
9
|
+
isTemplate(): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Handle resource read. Receives extracted params if this is a template
|
|
12
|
+
* resource. Extra parameters beyond `params` are resolved from the DI
|
|
13
|
+
* container when the method is decorated with `@Handle()`.
|
|
14
|
+
*/
|
|
15
|
+
abstract handle(params?: Record<string, string>, ...deps: unknown[]): Promise<string>;
|
|
10
16
|
}
|
|
11
17
|
//# sourceMappingURL=McpResource.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpResource.d.ts","sourceRoot":"","sources":["../src/McpResource.ts"],"names":[],"mappings":"AAEA,8BAAsB,WAAW;IAC/B,
|
|
1
|
+
{"version":3,"file":"McpResource.d.ts","sourceRoot":"","sources":["../src/McpResource.ts"],"names":[],"mappings":"AAEA,8BAAsB,WAAW;IAC/B,8EAA8E;IAC9E,QAAQ,CAAC,GAAG,IAAI,MAAM;IAEtB,gBAAgB;IAChB,QAAQ,IAAI,MAAM;IAIlB,2BAA2B;IAC3B,WAAW,IAAI,MAAM;IAIrB,4EAA4E;IAC5E,UAAU,IAAI,OAAO;IAIrB;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;CACtF"}
|
package/dist/McpResource.js
CHANGED
|
@@ -8,5 +8,9 @@ export class McpResource {
|
|
|
8
8
|
description() {
|
|
9
9
|
return getDescription(this.constructor) ?? '';
|
|
10
10
|
}
|
|
11
|
+
/** Whether this resource uses URI templates (has `{param}` placeholders) */
|
|
12
|
+
isTemplate() {
|
|
13
|
+
return this.uri().includes('{');
|
|
14
|
+
}
|
|
11
15
|
}
|
|
12
16
|
//# sourceMappingURL=McpResource.js.map
|
package/dist/McpResource.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpResource.js","sourceRoot":"","sources":["../src/McpResource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,MAAM,OAAgB,WAAW;IAI/B,gBAAgB;IAChB,QAAQ;QACN,OAAO,YAAY,CAAA;IACrB,CAAC;IAED,2BAA2B;IAC3B,WAAW;QACT,OAAO,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAA;IAC/C,CAAC;
|
|
1
|
+
{"version":3,"file":"McpResource.js","sourceRoot":"","sources":["../src/McpResource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,MAAM,OAAgB,WAAW;IAI/B,gBAAgB;IAChB,QAAQ;QACN,OAAO,YAAY,CAAA;IACrB,CAAC;IAED,2BAA2B;IAC3B,WAAW;QACT,OAAO,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAA;IAC/C,CAAC;IAED,4EAA4E;IAC5E,UAAU;QACR,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;IACjC,CAAC;CAQF"}
|
package/dist/McpTool.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ZodLikeObject } from './types.js';
|
|
2
2
|
export interface McpToolResult {
|
|
3
3
|
content: Array<{
|
|
4
4
|
type: 'text';
|
|
@@ -15,9 +15,21 @@ export declare abstract class McpTool {
|
|
|
15
15
|
name(): string;
|
|
16
16
|
/** Tool description — override or use @Description decorator */
|
|
17
17
|
description(): string;
|
|
18
|
-
/** Input schema
|
|
19
|
-
abstract schema():
|
|
20
|
-
/**
|
|
21
|
-
|
|
18
|
+
/** Input schema — a Zod object (v3 or v4). */
|
|
19
|
+
abstract schema(): ZodLikeObject;
|
|
20
|
+
/** Optional output schema — advertises the structure of the tool's response. */
|
|
21
|
+
outputSchema?(): ZodLikeObject;
|
|
22
|
+
/**
|
|
23
|
+
* Handle the tool call.
|
|
24
|
+
*
|
|
25
|
+
* Extra parameters beyond `input` are resolved from the DI container when
|
|
26
|
+
* the method is decorated with `@Handle(Token1, …)`. Example:
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* @Handle(Logger)
|
|
30
|
+
* async handle(input, logger: Logger) { ... }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
abstract handle(input: Record<string, unknown>, ...deps: unknown[]): Promise<McpToolResult>;
|
|
22
34
|
}
|
|
23
35
|
//# sourceMappingURL=McpTool.d.ts.map
|
package/dist/McpTool.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpTool.d.ts","sourceRoot":"","sources":["../src/McpTool.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"McpTool.d.ts","sourceRoot":"","sources":["../src/McpTool.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAClG,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,8BAAsB,OAAO;IAC3B,yGAAyG;IACzG,IAAI,IAAI,MAAM;IAId,gEAAgE;IAChE,WAAW,IAAI,MAAM;IAIrB,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,IAAI,aAAa;IAEhC,gFAAgF;IAChF,YAAY,CAAC,IAAI,aAAa;IAE9B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC;CAC5F"}
|
package/dist/McpTool.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpTool.js","sourceRoot":"","sources":["../src/McpTool.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"McpTool.js","sourceRoot":"","sources":["../src/McpTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAQhD,MAAM,OAAgB,OAAO;IAC3B,yGAAyG;IACzG,IAAI;QACF,OAAO,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAA;IAChE,CAAC;IAED,gEAAgE;IAChE,WAAW;QACT,OAAO,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAA;IAC/C,CAAC;CAoBF"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { MiddlewareHandler } from '@rudderjs/core';
|
|
2
|
+
export interface OAuth2McpOptions {
|
|
3
|
+
/** Scopes required on the bearer token. Missing scopes → 403 `insufficient_scope`. */
|
|
4
|
+
scopes?: string[];
|
|
5
|
+
/** Canonical URL of this protected MCP resource. Defaults to the current request URL. */
|
|
6
|
+
resource?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Authorization server URL(s) advertised via RFC 9728. Defaults to the app
|
|
9
|
+
* origin when `@rudderjs/passport` is installed as the in-app AS.
|
|
10
|
+
*/
|
|
11
|
+
authorizationServers?: string[];
|
|
12
|
+
/** Scopes advertised in the protected-resource metadata document. */
|
|
13
|
+
scopesSupported?: string[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Protect an MCP web endpoint with OAuth 2.1 Bearer tokens issued by
|
|
17
|
+
* `@rudderjs/passport`. On failure, adds an RFC 9728 `WWW-Authenticate`
|
|
18
|
+
* header pointing clients at the protected-resource metadata document.
|
|
19
|
+
*/
|
|
20
|
+
export declare function oauth2McpMiddleware(mcpPath: string, options?: OAuth2McpOptions): MiddlewareHandler;
|
|
21
|
+
/** Register the RFC 9728 Protected Resource Metadata endpoint for an MCP path. */
|
|
22
|
+
export declare function registerOAuth2Metadata(router: {
|
|
23
|
+
get(path: string, handler: (req: unknown, res: unknown) => unknown, middleware?: unknown[]): unknown;
|
|
24
|
+
}, mcpPath: string, options: OAuth2McpOptions): void;
|
|
25
|
+
//# sourceMappingURL=oauth2.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth2.d.ts","sourceRoot":"","sources":["../../src/auth/oauth2.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,sFAAsF;IACtF,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,yFAAyF;IACzF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC/B,qEAAqE;IACrE,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;CAC3B;AAgCD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,iBAAiB,CA+DtG;AAED,kFAAkF;AAClF,wBAAgB,sBAAsB,CACpC,MAAM,EAAE;IACN,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,KAAK,OAAO,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;CACrG,EACD,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,gBAAgB,GACxB,IAAI,CAqBN"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
let passportPromise = null;
|
|
2
|
+
function loadPassport() {
|
|
3
|
+
if (!passportPromise) {
|
|
4
|
+
passportPromise = (async () => {
|
|
5
|
+
const { resolveOptionalPeer } = await import('@rudderjs/core');
|
|
6
|
+
return resolveOptionalPeer('@rudderjs/passport');
|
|
7
|
+
})().catch((err) => {
|
|
8
|
+
passportPromise = null;
|
|
9
|
+
throw err;
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
return passportPromise;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Protect an MCP web endpoint with OAuth 2.1 Bearer tokens issued by
|
|
16
|
+
* `@rudderjs/passport`. On failure, adds an RFC 9728 `WWW-Authenticate`
|
|
17
|
+
* header pointing clients at the protected-resource metadata document.
|
|
18
|
+
*/
|
|
19
|
+
export function oauth2McpMiddleware(mcpPath, options = {}) {
|
|
20
|
+
const metadataPath = `/.well-known/oauth-protected-resource${mcpPath}`;
|
|
21
|
+
const requiredScopes = options.scopes ?? [];
|
|
22
|
+
return async function OAuth2McpMiddleware(req, res, next) {
|
|
23
|
+
const authHeader = req.headers['authorization'];
|
|
24
|
+
const metadataUrl = absoluteUrl(req, metadataPath);
|
|
25
|
+
if (!authHeader?.startsWith('Bearer ')) {
|
|
26
|
+
challenge(res, metadataUrl, 'invalid_token', 'Bearer token required.');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
let passport;
|
|
30
|
+
try {
|
|
31
|
+
passport = await loadPassport();
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
challenge(res, metadataUrl, 'invalid_token', 'OAuth provider not configured.');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const jwt = authHeader.slice(7).trim();
|
|
38
|
+
let payload;
|
|
39
|
+
try {
|
|
40
|
+
payload = await passport.verifyToken(jwt);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
challenge(res, metadataUrl, 'invalid_token', 'Invalid or expired token.');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
let token;
|
|
47
|
+
try {
|
|
48
|
+
token = await passport.AccessToken.query().where('id', payload.jti).first();
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
challenge(res, metadataUrl, 'invalid_token', 'Token could not be verified.');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (!token || token.revoked) {
|
|
55
|
+
challenge(res, metadataUrl, 'invalid_token', 'Token has been revoked.');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (requiredScopes.length > 0) {
|
|
59
|
+
const tokenScopes = Array.isArray(payload.scopes) ? payload.scopes : [];
|
|
60
|
+
const granted = tokenScopes.includes('*');
|
|
61
|
+
if (!granted) {
|
|
62
|
+
const missing = requiredScopes.filter((s) => !tokenScopes.includes(s));
|
|
63
|
+
if (missing.length > 0) {
|
|
64
|
+
challenge(res, metadataUrl, 'insufficient_scope', `Missing scope(s): ${missing.join(', ')}`, requiredScopes.join(' '));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const raw = req.raw;
|
|
70
|
+
raw['__passport_token'] = token;
|
|
71
|
+
raw['__passport_scopes'] = payload.scopes;
|
|
72
|
+
raw['__passport_user_id'] = payload.sub;
|
|
73
|
+
await next();
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/** Register the RFC 9728 Protected Resource Metadata endpoint for an MCP path. */
|
|
77
|
+
export function registerOAuth2Metadata(router, mcpPath, options) {
|
|
78
|
+
const metadataPath = `/.well-known/oauth-protected-resource${mcpPath}`;
|
|
79
|
+
router.get(metadataPath, (req, res) => {
|
|
80
|
+
const origin = absoluteUrl(req, '');
|
|
81
|
+
const resource = options.resource ?? `${origin}${mcpPath}`;
|
|
82
|
+
const authServers = options.authorizationServers && options.authorizationServers.length > 0
|
|
83
|
+
? options.authorizationServers
|
|
84
|
+
: [origin];
|
|
85
|
+
const body = {
|
|
86
|
+
resource,
|
|
87
|
+
authorization_servers: authServers,
|
|
88
|
+
bearer_methods_supported: ['header'],
|
|
89
|
+
};
|
|
90
|
+
if (options.scopesSupported && options.scopesSupported.length > 0) {
|
|
91
|
+
body['scopes_supported'] = options.scopesSupported;
|
|
92
|
+
}
|
|
93
|
+
;
|
|
94
|
+
res.json(body);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function absoluteUrl(req, path) {
|
|
98
|
+
const host = getHeader(req, 'x-forwarded-host')
|
|
99
|
+
?? req.host
|
|
100
|
+
?? getHeader(req, 'host')
|
|
101
|
+
?? req.hostname
|
|
102
|
+
?? 'localhost';
|
|
103
|
+
const proto = getHeader(req, 'x-forwarded-proto')
|
|
104
|
+
?? req.protocol
|
|
105
|
+
?? 'http';
|
|
106
|
+
return `${proto}://${host}${path}`;
|
|
107
|
+
}
|
|
108
|
+
function getHeader(req, name) {
|
|
109
|
+
const v = req.headers[name];
|
|
110
|
+
if (Array.isArray(v))
|
|
111
|
+
return v[0];
|
|
112
|
+
return v;
|
|
113
|
+
}
|
|
114
|
+
function challenge(res, metadataUrl, error, description, scope) {
|
|
115
|
+
const r = res;
|
|
116
|
+
const parts = [`resource_metadata="${metadataUrl}"`, `error="${error}"`];
|
|
117
|
+
if (description)
|
|
118
|
+
parts.push(`error_description="${description.replace(/"/g, '\\"')}"`);
|
|
119
|
+
if (scope)
|
|
120
|
+
parts.push(`scope="${scope}"`);
|
|
121
|
+
r.header?.('WWW-Authenticate', `Bearer ${parts.join(', ')}`);
|
|
122
|
+
const statusCode = error === 'insufficient_scope' ? 403 : 401;
|
|
123
|
+
const body = { error, error_description: description };
|
|
124
|
+
if (scope)
|
|
125
|
+
body['scope'] = scope;
|
|
126
|
+
r.status(statusCode).json(body);
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=oauth2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth2.js","sourceRoot":"","sources":["../../src/auth/oauth2.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const INSPECTOR_HTML = "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\" />\n<title>MCP Inspector</title>\n<style>\n :root {\n --bg: #0f1115;\n --surface: #1a1d24;\n --border: #2a2f3a;\n --text: #e4e6eb;\n --muted: #8a919e;\n --accent: #5c9cf5;\n --error: #ff7b7b;\n --success: #8be78b;\n }\n * { box-sizing: border-box; }\n body {\n margin: 0; font: 14px/1.5 system-ui, sans-serif;\n background: var(--bg); color: var(--text);\n display: grid; grid-template-columns: 280px 1fr; height: 100vh;\n }\n aside {\n background: var(--surface); border-right: 1px solid var(--border);\n padding: 16px; overflow-y: auto;\n }\n aside h2 { font-size: 11px; text-transform: uppercase; color: var(--muted); margin: 0 0 8px; letter-spacing: .05em; }\n aside ul { list-style: none; padding: 0; margin: 0 0 20px; }\n aside li button {\n display: block; width: 100%; text-align: left;\n padding: 8px 12px; margin-bottom: 2px; border: 0; border-radius: 6px;\n background: transparent; color: var(--text); font: inherit; cursor: pointer;\n }\n aside li button:hover { background: rgba(255,255,255,.04); }\n aside li button.active { background: var(--accent); color: #fff; }\n main { padding: 24px 32px; overflow-y: auto; }\n h1 { margin: 0 0 4px; font-size: 20px; }\n .meta { color: var(--muted); margin-bottom: 24px; }\n section { margin-bottom: 32px; }\n section h3 { font-size: 12px; text-transform: uppercase; color: var(--muted); margin: 0 0 12px; letter-spacing: .05em; }\n .card {\n background: var(--surface); border: 1px solid var(--border); border-radius: 8px;\n padding: 14px 16px; margin-bottom: 8px;\n }\n .card-head { display: flex; justify-content: space-between; align-items: center; cursor: pointer; }\n .card-head strong { color: var(--text); }\n .card-head span { color: var(--muted); font-size: 13px; }\n .card-body { margin-top: 12px; border-top: 1px solid var(--border); padding-top: 12px; }\n .card-body[hidden] { display: none; }\n textarea, input {\n width: 100%; font: 12px ui-monospace, monospace; padding: 8px; border-radius: 6px;\n background: var(--bg); color: var(--text); border: 1px solid var(--border); resize: vertical;\n }\n button.run {\n margin-top: 10px; padding: 6px 14px; background: var(--accent); color: #fff; border: 0;\n border-radius: 6px; cursor: pointer; font: 13px system-ui;\n }\n button.run:hover { filter: brightness(1.1); }\n pre {\n background: var(--bg); padding: 12px; border-radius: 6px; overflow-x: auto;\n font: 12px ui-monospace, monospace; margin: 10px 0 0; max-height: 320px; overflow-y: auto;\n }\n .response.error { color: var(--error); }\n .response.ok { color: var(--success); }\n .empty { color: var(--muted); font-style: italic; }\n .pill {\n display: inline-block; padding: 1px 6px; border-radius: 3px; background: var(--border);\n color: var(--muted); font-size: 11px; margin-left: 6px;\n }\n</style>\n</head>\n<body>\n\n<aside>\n <h1 style=\"font-size:15px;margin:0 0 20px;\">\u26A1 MCP Inspector</h1>\n <h2>Web servers</h2>\n <ul id=\"web-list\"></ul>\n <h2>Local servers</h2>\n <ul id=\"local-list\"></ul>\n</aside>\n\n<main id=\"main\">\n <p class=\"empty\">Select a server from the sidebar.</p>\n</main>\n\n<script>\nasync function api(path, options) {\n const r = await fetch(path, options)\n if (!r.ok) throw new Error((await r.json().catch(() => ({}))).error || r.statusText)\n return r.json()\n}\n\nfunction el(tag, attrs, ...children) {\n const n = document.createElement(tag)\n if (attrs) for (const [k, v] of Object.entries(attrs)) {\n if (k === 'class') n.className = v\n else if (k.startsWith('on')) n[k] = v\n else if (v !== undefined && v !== null) n.setAttribute(k, v)\n }\n for (const c of children) {\n if (c == null) continue\n n.appendChild(typeof c === 'string' ? document.createTextNode(c) : c)\n }\n return n\n}\n\nlet activeKey = null\n\nasync function refreshList() {\n const { web, local } = await api('/api/servers')\n const render = (items, ul) => {\n ul.innerHTML = ''\n if (items.length === 0) {\n ul.appendChild(el('li', { class: 'empty' }, '(none)'))\n return\n }\n for (const s of items) {\n ul.appendChild(el('li', null, el('button', {\n class: s.key === activeKey ? 'active' : '',\n onclick: () => openServer(s.key),\n }, s.label)))\n }\n }\n render(web, document.getElementById('web-list'))\n render(local, document.getElementById('local-list'))\n}\n\nasync function openServer(key) {\n activeKey = key\n await refreshList()\n const main = document.getElementById('main')\n main.innerHTML = 'Loading\u2026'\n try {\n const detail = await api('/api/servers/' + encodeURIComponent(key))\n main.innerHTML = ''\n main.appendChild(el('h1', null, detail.metadata.name || detail.label))\n main.appendChild(el('div', { class: 'meta' },\n 'v' + detail.metadata.version + ' \u00B7 ' + detail.kind + ' \u00B7 ' + detail.key,\n ))\n if (detail.metadata.instructions) main.appendChild(el('p', { class: 'meta' }, detail.metadata.instructions))\n renderTools(main, key, detail.tools)\n renderResources(main, key, detail.resources)\n renderPrompts(main, key, detail.prompts)\n } catch (err) {\n main.innerHTML = ''\n main.appendChild(el('p', { class: 'response error' }, err.message))\n }\n}\n\nfunction renderTools(container, key, tools) {\n container.appendChild(el('section', null,\n el('h3', null, 'Tools (' + tools.length + ')'),\n ...(tools.length === 0 ? [el('p', { class: 'empty' }, 'No tools.')] : tools.map((t) => toolCard(key, t))),\n ))\n}\n\nfunction toolCard(key, t) {\n const inputEl = el('textarea', { rows: 4, 'aria-label': 'input' })\n inputEl.value = JSON.stringify(defaultFromSchema(t.inputSchema), null, 2)\n const outEl = el('pre', { class: 'response' })\n outEl.hidden = true\n const body = el('div', { class: 'card-body', hidden: true },\n el('div', { class: 'meta' }, t.description || '(no description)'),\n el('label', null, 'Input JSON'), inputEl,\n el('button', {\n class: 'run',\n onclick: async () => {\n outEl.hidden = false\n outEl.className = 'response'\n outEl.textContent = 'Running\u2026'\n try {\n const input = JSON.parse(inputEl.value)\n const r = await api('/api/servers/' + encodeURIComponent(key) + '/tools/' + encodeURIComponent(t.name), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(input),\n })\n outEl.className = 'response ' + (r.isError ? 'error' : 'ok')\n outEl.textContent = JSON.stringify(r, null, 2)\n } catch (e) {\n outEl.className = 'response error'\n outEl.textContent = e.message\n }\n },\n }, 'Call tool'),\n outEl,\n )\n const head = el('div', { class: 'card-head', onclick: () => { body.hidden = !body.hidden } },\n el('strong', null, t.name),\n el('span', null, (t.description || '').slice(0, 60)),\n )\n return el('div', { class: 'card' }, head, body)\n}\n\nfunction renderResources(container, key, resources) {\n container.appendChild(el('section', null,\n el('h3', null, 'Resources (' + resources.length + ')'),\n ...(resources.length === 0 ? [el('p', { class: 'empty' }, 'No resources.')] : resources.map((r) => resourceCard(key, r))),\n ))\n}\n\nfunction resourceCard(key, r) {\n const uriEl = el('input', { value: r.uri, 'aria-label': 'uri' })\n const outEl = el('pre', { class: 'response' })\n outEl.hidden = true\n const body = el('div', { class: 'card-body', hidden: true },\n el('label', null, 'URI' + (r.template ? ' (template \u2014 fill in {placeholders})' : '')),\n uriEl,\n el('button', {\n class: 'run',\n onclick: async () => {\n outEl.hidden = false\n outEl.className = 'response'\n outEl.textContent = 'Loading\u2026'\n try {\n const r2 = await api('/api/servers/' + encodeURIComponent(key) + '/resource?uri=' + encodeURIComponent(uriEl.value))\n outEl.className = 'response ok'\n outEl.textContent = JSON.stringify(r2, null, 2)\n } catch (e) {\n outEl.className = 'response error'\n outEl.textContent = e.message\n }\n },\n }, 'Read resource'),\n outEl,\n )\n return el('div', { class: 'card' },\n el('div', { class: 'card-head', onclick: () => { body.hidden = !body.hidden } },\n el('strong', null, r.uri),\n el('span', null, (r.description || '') + (r.template ? ' \u00B7 template' : '')),\n ),\n body,\n )\n}\n\nfunction renderPrompts(container, key, prompts) {\n container.appendChild(el('section', null,\n el('h3', null, 'Prompts (' + prompts.length + ')'),\n ...(prompts.length === 0 ? [el('p', { class: 'empty' }, 'No prompts.')] : prompts.map((p) => promptCard(key, p))),\n ))\n}\n\nfunction promptCard(key, p) {\n const argsEl = el('textarea', { rows: 3, 'aria-label': 'arguments' })\n argsEl.value = JSON.stringify(p.argumentSchema ? defaultFromSchema(p.argumentSchema) : {}, null, 2)\n const outEl = el('pre', { class: 'response' })\n outEl.hidden = true\n const body = el('div', { class: 'card-body', hidden: true },\n el('div', { class: 'meta' }, p.description || '(no description)'),\n el('label', null, 'Arguments JSON'), argsEl,\n el('button', {\n class: 'run',\n onclick: async () => {\n outEl.hidden = false\n outEl.className = 'response'\n outEl.textContent = 'Running\u2026'\n try {\n const args = JSON.parse(argsEl.value)\n const r = await api('/api/servers/' + encodeURIComponent(key) + '/prompts/' + encodeURIComponent(p.name), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(args),\n })\n outEl.className = 'response ok'\n outEl.textContent = JSON.stringify(r, null, 2)\n } catch (e) {\n outEl.className = 'response error'\n outEl.textContent = e.message\n }\n },\n }, 'Get prompt'),\n outEl,\n )\n return el('div', { class: 'card' },\n el('div', { class: 'card-head', onclick: () => { body.hidden = !body.hidden } },\n el('strong', null, p.name),\n el('span', null, (p.description || '').slice(0, 60)),\n ),\n body,\n )\n}\n\nfunction defaultFromSchema(schema) {\n if (!schema || schema.type !== 'object' || !schema.properties) return {}\n const out = {}\n for (const [k, v] of Object.entries(schema.properties)) {\n if (v.type === 'string') out[k] = ''\n else if (v.type === 'number' || v.type === 'integer') out[k] = 0\n else if (v.type === 'boolean') out[k] = false\n else if (v.type === 'array') out[k] = []\n else out[k] = null\n }\n return out\n}\n\nrefreshList()\n</script>\n</body>\n</html>";
|
|
2
|
+
//# sourceMappingURL=inspector-ui.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inspector-ui.d.ts","sourceRoot":"","sources":["../../src/commands/inspector-ui.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,ouVA0SnB,CAAA"}
|