tmcp 1.0.0 โ†’ 1.0.2

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 ADDED
@@ -0,0 +1,277 @@
1
+ # tmcp
2
+
3
+ A lightweight, schema-agnostic Model Context Protocol (MCP) server implementation with unified API design.
4
+
5
+ ## Why tmcp?
6
+
7
+ tmcp offers significant advantages over the official MCP SDK:
8
+
9
+ - **๐Ÿ”„ Schema Agnostic**: Works with any validation library through adapters
10
+ - **๐Ÿ“ฆ No Weird Dependencies**: Minimal footprint with only essential dependencies (looking at you `express`)
11
+ - **๐ŸŽฏ Unified API**: Consistent, intuitive interface across all MCP capabilities
12
+ - **๐Ÿ”Œ Extensible**: Easy to add support for new schema libraries
13
+ - **โšก Lightweight**: No bloat, just what you need
14
+
15
+ ## Supported Schema Libraries
16
+
17
+ tmcp works with all major schema validation libraries through its adapter system:
18
+
19
+ - **Zod** - `@tmcpkit/adapter-zod`
20
+ - **Valibot** - `@tmcpkit/adapter-valibot`
21
+ - **ArkType** - `@tmcpkit/adapter-arktype`
22
+ - **Effect Schema** - `@tmcpkit/adapter-effect`
23
+ - **Zod v3** - `@tmcpkit/adapter-zod-v3`
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ pnpm install tmcp
29
+ # Choose your preferred schema library adapter
30
+ รจnpm install @tmcpkit/adapter-zod zod
31
+ ```
32
+
33
+ ## Quick Start
34
+
35
+ ```javascript
36
+ import { McpServer } from 'tmcp';
37
+ import { ZodJsonSchemaAdapter } from '@tmcpkit/adapter-zod';
38
+ import { z } from 'zod';
39
+
40
+ const adapter = new ZodJsonSchemaAdapter();
41
+ const server = new McpServer(
42
+ {
43
+ name: 'my-server',
44
+ version: '1.0.0',
45
+ description: 'My awesome MCP server',
46
+ },
47
+ {
48
+ adapter,
49
+ capabilities: {
50
+ tools: { listChanged: true },
51
+ prompts: { listChanged: true },
52
+ resources: { listChanged: true },
53
+ },
54
+ },
55
+ );
56
+
57
+ // Define a tool with type-safe schema
58
+ server.tool(
59
+ {
60
+ name: 'calculate',
61
+ description: 'Perform mathematical calculations',
62
+ schema: z.object({
63
+ operation: z.enum(['add', 'subtract', 'multiply', 'divide']),
64
+ a: z.number(),
65
+ b: z.number(),
66
+ }),
67
+ },
68
+ async ({ operation, a, b }) => {
69
+ switch (operation) {
70
+ case 'add':
71
+ return a + b;
72
+ case 'subtract':
73
+ return a - b;
74
+ case 'multiply':
75
+ return a * b;
76
+ case 'divide':
77
+ return a / b;
78
+ }
79
+ },
80
+ );
81
+
82
+ // Process incoming requests
83
+ server.receive(request);
84
+ ```
85
+
86
+ ## API Reference
87
+
88
+ ### McpServer
89
+
90
+ The main server class that handles MCP protocol communications.
91
+
92
+ #### Constructor
93
+
94
+ ```javascript
95
+ new McpServer(serverInfo, options);
96
+ ```
97
+
98
+ - `serverInfo`: Server metadata (name, version, description)
99
+ - `options`: Configuration object with adapter and capabilities
100
+
101
+ #### Methods
102
+
103
+ ##### `tool(definition, handler)`
104
+
105
+ Register a tool with optional schema validation.
106
+
107
+ ```javascript
108
+ server.tool(
109
+ {
110
+ name: 'tool-name',
111
+ description: 'Tool description',
112
+ schema: yourSchema, // optional
113
+ },
114
+ async (input) => {
115
+ // Tool implementation
116
+ return result;
117
+ },
118
+ );
119
+ ```
120
+
121
+ ##### `prompt(definition, handler)`
122
+
123
+ Register a prompt template with optional schema validation.
124
+
125
+ ```javascript
126
+ server.prompt(
127
+ {
128
+ name: 'prompt-name',
129
+ description: 'Prompt description',
130
+ schema: yourSchema, // optional
131
+ complete: (arg, context) => ['completion1', 'completion2'] // optional
132
+ },
133
+ async (input) => {
134
+ // Prompt implementation
135
+ return { messages: [...] };
136
+ }
137
+ );
138
+ ```
139
+
140
+ ##### `resource(definition, handler)`
141
+
142
+ Register a static resource.
143
+
144
+ ```javascript
145
+ server.resource(
146
+ {
147
+ name: 'resource-name',
148
+ description: 'Resource description',
149
+ uri: 'file://path/to/resource'
150
+ },
151
+ async (uri, params) => {
152
+ // Resource implementation
153
+ return { contents: [...] };
154
+ }
155
+ );
156
+ ```
157
+
158
+ ##### `template(definition, handler)`
159
+
160
+ Register a URI template for dynamic resources.
161
+
162
+ ```javascript
163
+ server.template(
164
+ {
165
+ name: 'template-name',
166
+ description: 'Template description',
167
+ uri: 'file://path/{id}/resource',
168
+ complete: (arg, context) => ['id1', 'id2'] // optional
169
+ },
170
+ async (uri, params) => {
171
+ // Template implementation using params.id
172
+ return { contents: [...] };
173
+ }
174
+ );
175
+ ```
176
+
177
+ ##### `receive(request)`
178
+
179
+ Process an incoming MCP request.
180
+
181
+ ```javascript
182
+ const response = server.receive(jsonRpcRequest);
183
+ ```
184
+
185
+ ## Advanced Examples
186
+
187
+ ### Multiple Schema Libraries
188
+
189
+ ```javascript
190
+ // Use different schemas for different tools
191
+ import { z } from 'zod';
192
+ import * as v from 'valibot';
193
+
194
+ server.tool(
195
+ {
196
+ name: 'zod-tool',
197
+ schema: z.object({ name: z.string() }),
198
+ },
199
+ async ({ name }) => `Hello ${name}`,
200
+ );
201
+
202
+ server.tool(
203
+ {
204
+ name: 'valibot-tool',
205
+ schema: v.object({ age: v.number() }),
206
+ },
207
+ async ({ age }) => `Age: ${age}`,
208
+ );
209
+ ```
210
+
211
+ ### Resource Templates with Completion
212
+
213
+ ```javascript
214
+ server.template(
215
+ {
216
+ name: 'user-profile',
217
+ description: 'Get user profile by ID',
218
+ uri: 'users/{userId}/profile',
219
+ complete: (arg, context) => {
220
+ // Provide completions for userId parameter
221
+ return ['user1', 'user2', 'user3'];
222
+ },
223
+ },
224
+ async (uri, params) => {
225
+ const user = await getUserById(params.userId);
226
+ return {
227
+ contents: [
228
+ {
229
+ uri,
230
+ mimeType: 'application/json',
231
+ text: JSON.stringify(user),
232
+ },
233
+ ],
234
+ };
235
+ },
236
+ );
237
+ ```
238
+
239
+ ### Complex Validation
240
+
241
+ ```javascript
242
+ const complexSchema = z.object({
243
+ user: z.object({
244
+ name: z.string().min(1),
245
+ email: z.string().email(),
246
+ age: z.number().min(18).max(120),
247
+ }),
248
+ preferences: z
249
+ .object({
250
+ theme: z.enum(['light', 'dark']),
251
+ notifications: z.boolean(),
252
+ })
253
+ .optional(),
254
+ tags: z.array(z.string()).default([]),
255
+ });
256
+
257
+ server.tool(
258
+ {
259
+ name: 'create-user',
260
+ description: 'Create a new user with preferences',
261
+ schema: complexSchema,
262
+ },
263
+ async (input) => {
264
+ // Input is fully typed and validated
265
+ const { user, preferences, tags } = input;
266
+ return await createUser(user, preferences, tags);
267
+ },
268
+ );
269
+ ```
270
+
271
+ ## Contributing
272
+
273
+ Contributions are welcome! Please see our [contributing guidelines](../../CONTRIBUTING.md) for details.
274
+
275
+ ## License
276
+
277
+ MIT ยฉ Paolo Ricciuti
package/package.json CHANGED
@@ -1,13 +1,40 @@
1
1
  {
2
- "name": "tmcp",
3
- "version": "1.0.0",
4
- "description": "",
5
- "main": "index.js",
6
- "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
8
- },
9
- "keywords": [],
10
- "author": "Paolo Ricciuti",
11
- "license": "MIT",
12
- "type": "module"
13
- }
2
+ "name": "tmcp",
3
+ "version": "1.0.2",
4
+ "description": "The main tmcp library",
5
+ "main": "src/index.js",
6
+ "type": "module",
7
+ "keywords": [],
8
+ "author": "Paolo Ricciuti",
9
+ "license": "MIT",
10
+ "files": [
11
+ "src/"
12
+ ],
13
+ "exports": {
14
+ ".": {
15
+ "types": "./src/index.d.ts",
16
+ "default": "./src/index.js"
17
+ },
18
+ "./adapter": {
19
+ "types": "./src/adapter.d.ts",
20
+ "default": "./src/adapter.js"
21
+ },
22
+ "./package.json": "./package.json"
23
+ },
24
+ "dependencies": {
25
+ "json-rpc-2.0": "^1.7.1",
26
+ "uri-template-matcher": "^1.1.0",
27
+ "valibot": "^1.1.0"
28
+ },
29
+ "devDependencies": {
30
+ "@standard-schema/spec": "^1.0.0",
31
+ "@types/json-schema": "^7.0.15",
32
+ "publint": "^0.3.12"
33
+ },
34
+ "scripts": {
35
+ "test": "echo \"Error: no test specified\" && exit 1",
36
+ "typecheck": "tsc --noEmit",
37
+ "generate:types": "rm -f ./src/*.d.ts && tsc ./src/*.js --declaration --emitDeclarationOnly --target esnext --allowJs --moduleresolution nodenext --module nodenext && publint",
38
+ "prepublish": "pnpm generate:types"
39
+ }
40
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @import { StandardSchemaV1 } from "@standard-schema/spec";
3
+ * @import { JSONSchema7 } from "json-schema";
4
+ */
5
+ /**
6
+ * @template {StandardSchemaV1} TSchema
7
+ */
8
+ export class JsonSchemaAdapter<TSchema extends StandardSchemaV1> {
9
+ /**
10
+ * @param {TSchema} schema
11
+ * @returns {Promise<JSONSchema7>}
12
+ */
13
+ toJsonSchema(schema: TSchema): Promise<JSONSchema7>;
14
+ }
15
+ import type { StandardSchemaV1 } from "@standard-schema/spec";
16
+ import type { JSONSchema7 } from "json-schema";
package/src/adapter.js ADDED
@@ -0,0 +1,19 @@
1
+ /* eslint-disable no-unused-vars */
2
+
3
+ /**
4
+ * @import { StandardSchemaV1 } from "@standard-schema/spec";
5
+ * @import { JSONSchema7 } from "json-schema";
6
+ */
7
+
8
+ /**
9
+ * @template {StandardSchemaV1} TSchema
10
+ */
11
+ export class JsonSchemaAdapter {
12
+ /**
13
+ * @param {TSchema} schema
14
+ * @returns {Promise<JSONSchema7>}
15
+ */
16
+ toJsonSchema(schema) {
17
+ throw new Error('toJsonSchema method not implemented');
18
+ }
19
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,62 @@
1
+ /**
2
+ * @template {StandardSchemaV1} StandardSchema
3
+ */
4
+ export class McpServer<StandardSchema extends StandardSchemaV1> {
5
+ /**
6
+ * @param {ServerInfo} server_info
7
+ * @param {ServerOptions<StandardSchema>} options
8
+ */
9
+ constructor(server_info: ServerInfo, options: ServerOptions<StandardSchema>);
10
+ /**
11
+ * @template {StandardSchema | undefined} [TSchema=undefined]
12
+ * @param {{ name: string; description: string; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never }} options
13
+ * @param {TSchema extends undefined ? (()=>Promise<unknown> | unknown) : ((input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<unknown> | unknown)} execute
14
+ */
15
+ tool<TSchema extends StandardSchema | undefined = undefined>({ name, description, schema }: {
16
+ name: string;
17
+ description: string;
18
+ schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never;
19
+ }, execute: TSchema extends undefined ? (() => Promise<unknown> | unknown) : ((input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<unknown> | unknown)): void;
20
+ /**
21
+ * @template {StandardSchema | undefined} [TSchema=undefined]
22
+ * @param {{ name: string; description: string; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never; complete?: Completion }} options
23
+ * @param {TSchema extends undefined ? (()=>Promise<unknown> | unknown) : (input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<unknown> | unknown} execute
24
+ */
25
+ prompt<TSchema extends StandardSchema | undefined = undefined>({ name, description, schema, complete }: {
26
+ name: string;
27
+ description: string;
28
+ schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never;
29
+ complete?: Completion;
30
+ }, execute: TSchema extends undefined ? (() => Promise<unknown> | unknown) : (input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<unknown> | unknown): void;
31
+ /**
32
+ * @param {{ name: string; description: string; uri: string }} options
33
+ * @param {(uri: string, params?: unknown) => Promise<unknown> | unknown} execute
34
+ */
35
+ resource({ name, description, uri }: {
36
+ name: string;
37
+ description: string;
38
+ uri: string;
39
+ }, execute: (uri: string, params?: unknown) => Promise<unknown> | unknown): void;
40
+ /**
41
+ * @param {{ name: string; description: string; uri: string; complete?: Completion }} options
42
+ * @param {(uri: string, params?: unknown) => Promise<unknown> | unknown} execute
43
+ */
44
+ template({ name, description, uri, complete }: {
45
+ name: string;
46
+ description: string;
47
+ uri: string;
48
+ complete?: Completion;
49
+ }, execute: (uri: string, params?: unknown) => Promise<unknown> | unknown): void;
50
+ /**
51
+ * @param {JSONRPCRequest} request
52
+ * @returns {ReturnType<JSONRPCServer['receive']>}
53
+ */
54
+ receive(request: JSONRPCRequest): ReturnType<JSONRPCServer["receive"]>;
55
+ #private;
56
+ }
57
+ import type { StandardSchemaV1 } from "@standard-schema/spec";
58
+ import type { Completion } from "./internal/internal.js";
59
+ import type { JSONRPCRequest } from "json-rpc-2.0";
60
+ import { JSONRPCServer } from 'json-rpc-2.0';
61
+ import type { ServerInfo } from "./internal/internal.js";
62
+ import type { ServerOptions } from "./internal/internal.js";
package/src/index.js ADDED
@@ -0,0 +1,342 @@
1
+ /**
2
+ * @import { StandardSchemaV1 } from "@standard-schema/spec";
3
+ * @import { JSONRPCRequest, JSONRPCParams } from "json-rpc-2.0";
4
+ * @import { Tool, Completion, Prompt, Resource, ServerOptions, ServerInfo } from "./internal/internal.js";
5
+ */
6
+ import { JSONRPCServer, JSONRPCClient } from 'json-rpc-2.0';
7
+ import { UriTemplateMatcher } from 'uri-template-matcher';
8
+
9
+ /**
10
+ * @template {StandardSchemaV1} StandardSchema
11
+ */
12
+ export class McpServer {
13
+ #server = new JSONRPCServer();
14
+ /**
15
+ * @type {JSONRPCClient | undefined}
16
+ */
17
+ #client;
18
+ #options;
19
+ /**
20
+ * @type {Map<string, Tool<any>>}
21
+ */
22
+ #tools = new Map();
23
+ /**
24
+ * @type {Map<string, Prompt<any>>}
25
+ */
26
+ #prompts = new Map();
27
+ /**
28
+ * @type {Map<string, Resource>}
29
+ */
30
+ #resources = new Map();
31
+ #templates = new UriTemplateMatcher();
32
+ /**
33
+ * @type {{ [ref: string]: Map<string, Completion> }}
34
+ */
35
+ #completions = {
36
+ 'ref/prompt': new Map(),
37
+ 'ref/resource': new Map(),
38
+ };
39
+ /**
40
+ * @param {ServerInfo} server_info
41
+ * @param {ServerOptions<StandardSchema>} options
42
+ */
43
+ constructor(server_info, options) {
44
+ this.#options = options;
45
+ if (options.send) {
46
+ this.#client = new JSONRPCClient((payload) => {
47
+ options.send?.(payload);
48
+ });
49
+ }
50
+ this.#server.addMethod('initialize', ({ protocolVersion }) => {
51
+ return {
52
+ protocolVersion,
53
+ ...options,
54
+ serverInfo: server_info,
55
+ };
56
+ });
57
+ this.#server.addMethod('ping', () => {
58
+ return {};
59
+ });
60
+ this.#server.addMethod('notifications/initialized', () => {
61
+ return null;
62
+ });
63
+ this.#init_tools();
64
+ this.#init_prompts();
65
+ this.#init_resources();
66
+ this.#init_completion();
67
+ }
68
+
69
+ /**
70
+ * @param {string} method
71
+ * @param {JSONRPCParams} [params]
72
+ */
73
+ #notify(method, params) {
74
+ this.#client?.notify(method, params);
75
+ }
76
+
77
+ /**
78
+ *
79
+ */
80
+ #init_tools() {
81
+ if (!this.#options.capabilities?.tools) return;
82
+ this.#server.addMethod('tools/list', async () => {
83
+ const available_tools = await Promise.all(
84
+ [...this.#tools].map(async ([name, tool]) => {
85
+ return {
86
+ name,
87
+ title: tool.description,
88
+ description: tool.description,
89
+ inputSchema: tool.schema
90
+ ? await this.#options.adapter.toJsonSchema(
91
+ tool.schema,
92
+ )
93
+ : { type: 'object', properties: {} },
94
+ };
95
+ }),
96
+ );
97
+ return {
98
+ tools: available_tools,
99
+ };
100
+ });
101
+ this.#server.addMethod(
102
+ 'tools/call',
103
+ async ({ name, arguments: args }) => {
104
+ const tool = this.#tools.get(name);
105
+ if (!tool) {
106
+ throw new Error(`Tool ${name} not found`);
107
+ }
108
+ if (!tool.schema) {
109
+ return tool.execute();
110
+ }
111
+ let validated_args = tool.schema['~standard'].validate(args);
112
+ if (validated_args instanceof Promise)
113
+ validated_args = await validated_args;
114
+ if (validated_args.issues) {
115
+ throw new Error(
116
+ `Invalid arguments for tool ${name}: ${JSON.stringify(validated_args.issues)}`,
117
+ );
118
+ }
119
+ return tool.execute(validated_args.value);
120
+ },
121
+ );
122
+ }
123
+ /**
124
+ *
125
+ */
126
+ #init_prompts() {
127
+ if (!this.#options.capabilities?.prompts) return;
128
+ this.#server.addMethod('prompts/list', async () => {
129
+ const available_prompts = await Promise.all(
130
+ [...this.#prompts].map(async ([name, prompt]) => {
131
+ const arguments_schema = prompt.schema
132
+ ? await this.#options.adapter.toJsonSchema(
133
+ prompt.schema,
134
+ )
135
+ : {
136
+ type: 'object',
137
+ properties:
138
+ /** @type {Record<string, {description: string}>} */ ({}),
139
+ required: [],
140
+ };
141
+ const keys = Object.keys(arguments_schema.properties ?? {});
142
+ const required = arguments_schema.required ?? [];
143
+ return {
144
+ name,
145
+ title: prompt.description,
146
+ description: prompt.description,
147
+ arguments: keys.map((key) => {
148
+ const property = arguments_schema.properties?.[key];
149
+ const description =
150
+ property && property !== true
151
+ ? property.description
152
+ : key;
153
+ return {
154
+ name: key,
155
+ required: required.includes(key),
156
+ description,
157
+ };
158
+ }),
159
+ };
160
+ }),
161
+ );
162
+ return {
163
+ prompts: available_prompts,
164
+ };
165
+ });
166
+ this.#server.addMethod(
167
+ 'prompts/get',
168
+ async ({ name, arguments: args }) => {
169
+ const prompt = this.#prompts.get(name);
170
+ if (!prompt) {
171
+ throw new Error(`Tool ${name} not found`);
172
+ }
173
+ if (!prompt.schema) {
174
+ return prompt.execute();
175
+ }
176
+ let validated_args = prompt.schema['~standard'].validate(args);
177
+ if (validated_args instanceof Promise)
178
+ validated_args = await validated_args;
179
+ if (validated_args.issues) {
180
+ throw new Error(
181
+ `Invalid arguments for tool ${name}: ${JSON.stringify(validated_args.issues)}`,
182
+ );
183
+ }
184
+ return prompt.execute(validated_args.value);
185
+ },
186
+ );
187
+ }
188
+ /**
189
+ *
190
+ */
191
+ #init_resources() {
192
+ if (!this.#options.capabilities?.resources) return;
193
+ this.#server.addMethod('resources/list', async () => {
194
+ return {
195
+ resources: [...this.#resources].reduce(
196
+ (arr, [uri, { description, name, template }]) => {
197
+ if (!template) {
198
+ arr.push({
199
+ name,
200
+ description,
201
+ uri,
202
+ });
203
+ }
204
+ return arr;
205
+ },
206
+ /** @type {Array<{name: string, description: string, uri: string}>} */ ([]),
207
+ ),
208
+ };
209
+ });
210
+ this.#server.addMethod('resources/templates/list', async () => {
211
+ return {
212
+ resourceTemplates: [...this.#resources].reduce(
213
+ (arr, [uri, { description, name, template }]) => {
214
+ if (template) {
215
+ arr.push({
216
+ name,
217
+ description,
218
+ uriTemplate: uri,
219
+ });
220
+ }
221
+ return arr;
222
+ },
223
+ /** @type {Array<{name: string, description: string, uriTemplate: string}>} */ ([]),
224
+ ),
225
+ };
226
+ });
227
+ this.#server.addMethod('resources/read', async ({ uri }) => {
228
+ let resource = this.#resources.get(uri);
229
+ let params;
230
+ if (!resource) {
231
+ const match = this.#templates.match(uri);
232
+ if (match) {
233
+ resource = this.#resources.get(match.template);
234
+ params = match.params;
235
+ }
236
+ if (!resource) {
237
+ throw new Error(`Resource ${uri} not found`);
238
+ }
239
+ }
240
+ return resource.execute(uri, params);
241
+ });
242
+ }
243
+ /**
244
+ *
245
+ */
246
+ #init_completion() {
247
+ this.#server.addMethod(
248
+ 'completion/complete',
249
+ ({ argument, ref, context }) => {
250
+ const completions = this.#completions[ref.type];
251
+ if (!completions) return null;
252
+ const complete = completions.get(ref.uri ?? ref.name);
253
+ if (!complete) return null;
254
+ return {
255
+ completion: {
256
+ values: complete(argument, context),
257
+ hasMore: false,
258
+ },
259
+ };
260
+ },
261
+ );
262
+ }
263
+ /**
264
+ * @template {StandardSchema | undefined} [TSchema=undefined]
265
+ * @param {{ name: string; description: string; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never }} options
266
+ * @param {TSchema extends undefined ? (()=>Promise<unknown> | unknown) : ((input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<unknown> | unknown)} execute
267
+ */
268
+ tool({ name, description, schema }, execute) {
269
+ if (this.#options.capabilities?.tools?.listChanged) {
270
+ this.#notify('notifications/tools/list_changed', {});
271
+ }
272
+ this.#tools.set(name, {
273
+ description,
274
+ schema,
275
+ execute,
276
+ });
277
+ }
278
+ /**
279
+ * @template {StandardSchema | undefined} [TSchema=undefined]
280
+ * @param {{ name: string; description: string; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never; complete?: Completion }} options
281
+ * @param {TSchema extends undefined ? (()=>Promise<unknown> | unknown) : (input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<unknown> | unknown} execute
282
+ */
283
+ prompt({ name, description, schema, complete }, execute) {
284
+ if (complete) {
285
+ this.#completions['ref/prompt'].set(name, complete);
286
+ }
287
+ if (this.#options.capabilities?.prompts?.listChanged) {
288
+ this.#notify('notifications/prompts/list_changed', {});
289
+ }
290
+ this.#prompts.set(name, {
291
+ description,
292
+ schema,
293
+ execute,
294
+ });
295
+ }
296
+ /**
297
+ * @param {string} name
298
+ * @param {string} description
299
+ * @param {string} uri
300
+ * @param {(uri: string, params?: unknown) => Promise<unknown> | unknown} execute
301
+ * @param {Completion} [complete]
302
+ * @param {boolean} [template=false]
303
+ */
304
+ #resource(name, description, uri, execute, complete, template = false) {
305
+ if (complete) {
306
+ this.#completions['ref/resource'].set(uri, complete);
307
+ }
308
+ if (template) {
309
+ this.#templates.add(uri);
310
+ }
311
+ if (this.#options.capabilities?.resources?.listChanged) {
312
+ this.#notify('notifications/resources/list_changed', {});
313
+ }
314
+ this.#resources.set(uri, {
315
+ description,
316
+ name,
317
+ execute,
318
+ template,
319
+ });
320
+ }
321
+ /**
322
+ * @param {{ name: string; description: string; uri: string }} options
323
+ * @param {(uri: string, params?: unknown) => Promise<unknown> | unknown} execute
324
+ */
325
+ resource({ name, description, uri }, execute) {
326
+ this.#resource(name, description, uri, execute, undefined, false);
327
+ }
328
+ /**
329
+ * @param {{ name: string; description: string; uri: string; complete?: Completion }} options
330
+ * @param {(uri: string, params?: unknown) => Promise<unknown> | unknown} execute
331
+ */
332
+ template({ name, description, uri, complete }, execute) {
333
+ this.#resource(name, description, uri, execute, complete, true);
334
+ }
335
+ /**
336
+ * @param {JSONRPCRequest} request
337
+ * @returns {ReturnType<JSONRPCServer['receive']>}
338
+ */
339
+ receive(request) {
340
+ return this.#server.receive(request);
341
+ }
342
+ }
@@ -0,0 +1,48 @@
1
+ import { StandardSchemaV1 } from '@standard-schema/spec';
2
+ import { JSONRPCRequest } from 'json-rpc-2.0';
3
+ import { JsonSchemaAdapter } from '../adapter.js';
4
+
5
+ export type Tool<TSchema extends StandardSchemaV1 = StandardSchemaV1<any>> = {
6
+ description: string;
7
+ schema: TSchema;
8
+ execute: (
9
+ input?: StandardSchemaV1.InferInput<TSchema>,
10
+ ) => Promise<unknown> | unknown;
11
+ };
12
+
13
+ export type Completion = (
14
+ argument: { name: string; value: string },
15
+ context: { arguments: Record<string, string> },
16
+ ) => string[];
17
+
18
+ export type Prompt<TSchema extends StandardSchemaV1 = StandardSchemaV1<any>> = {
19
+ description: string;
20
+ schema: TSchema;
21
+ execute: (
22
+ input?: StandardSchemaV1.InferInput<TSchema>,
23
+ ) => Promise<unknown> | unknown;
24
+ };
25
+
26
+ export type Resource = {
27
+ description: string;
28
+ name: string;
29
+ template: boolean;
30
+ execute: (uri: string, params?: unknown) => Promise<unknown> | unknown;
31
+ };
32
+
33
+ export type ServerOptions<TSchema extends StandardSchemaV1> = {
34
+ send?: (payload: JSONRPCRequest) => void;
35
+ capabilities?: {
36
+ tools?: { listChanged?: boolean };
37
+ resources?: { listChanged?: boolean; subscribe?: boolean };
38
+ prompts?: { listChanged?: boolean; subscribe?: boolean };
39
+ };
40
+ instructions?: string;
41
+ adapter: JsonSchemaAdapter<TSchema>;
42
+ };
43
+
44
+ export type ServerInfo = {
45
+ name: string;
46
+ version: string;
47
+ description: string;
48
+ };
@@ -1,8 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "mcp__npm__search_npm"
5
- ],
6
- "deny": []
7
- }
8
- }
package/index.js DELETED
File without changes