tmcp 1.10.1 → 1.10.3
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 +375 -28
- package/package.json +2 -2
- package/src/index.js +240 -93
- package/src/internal/internal.d.ts +4 -0
- package/src/types/index.d.ts +67 -10
- package/src/types/index.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -249,6 +249,67 @@ Send notifications for subscriptions.
|
|
|
249
249
|
server.changed('resource', 'file://path/to/resource');
|
|
250
250
|
```
|
|
251
251
|
|
|
252
|
+
##### `progress(progress, total?, message?)`
|
|
253
|
+
|
|
254
|
+
Report progress during long-running operations. Progress notifications are only sent when a progress token is provided by the client in the request's `_meta.progressToken` field.
|
|
255
|
+
|
|
256
|
+
```javascript
|
|
257
|
+
server.tool(
|
|
258
|
+
{
|
|
259
|
+
name: 'process-large-file',
|
|
260
|
+
description: 'Process a large file with progress updates',
|
|
261
|
+
},
|
|
262
|
+
async (input) => {
|
|
263
|
+
const totalSteps = 100;
|
|
264
|
+
|
|
265
|
+
for (let i = 0; i <= totalSteps; i++) {
|
|
266
|
+
// Report progress with current step, total steps, and optional message
|
|
267
|
+
server.progress(
|
|
268
|
+
i,
|
|
269
|
+
totalSteps,
|
|
270
|
+
`Processing step ${i}/${totalSteps}`,
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
// Simulate work
|
|
274
|
+
await processStep(i);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
content: [{ type: 'text', text: 'Processing complete!' }],
|
|
279
|
+
};
|
|
280
|
+
},
|
|
281
|
+
);
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Parameters:**
|
|
285
|
+
|
|
286
|
+
- `progress` (number): Current progress value (should be ≤ total and always increase)
|
|
287
|
+
- `total` (number, optional): Maximum progress value (defaults to 1)
|
|
288
|
+
- `message` (string, optional): Descriptive message about current progress
|
|
289
|
+
|
|
290
|
+
**Notes:**
|
|
291
|
+
|
|
292
|
+
- Progress notifications are only sent when the client provides a `progressToken` in the request
|
|
293
|
+
- Progress values should be monotonically increasing within a single operation
|
|
294
|
+
- Each session maintains its own progress context
|
|
295
|
+
|
|
296
|
+
##### `log(level, data, logger?)`
|
|
297
|
+
|
|
298
|
+
Send log messages to connected clients when logging is enabled.
|
|
299
|
+
|
|
300
|
+
```javascript
|
|
301
|
+
// Log at different severity levels
|
|
302
|
+
server.log('info', 'Server started successfully');
|
|
303
|
+
server.log('warning', 'Configuration missing, using defaults');
|
|
304
|
+
server.log('error', 'Failed to connect to database', 'database-logger');
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**Parameters:**
|
|
308
|
+
|
|
309
|
+
- `level` (string): Log level ('debug', 'info', 'notice', 'warning', 'error', 'critical', 'alert', 'emergency')
|
|
310
|
+
- `data` (any): Data to log
|
|
311
|
+
- `logger` (string, optional): Logger name/category
|
|
312
|
+
|
|
252
313
|
##### `on(event, callback)`
|
|
253
314
|
|
|
254
315
|
Listen to server events.
|
|
@@ -265,6 +326,94 @@ server.on('send', ({ request, context }) => {
|
|
|
265
326
|
|
|
266
327
|
## Advanced Examples
|
|
267
328
|
|
|
329
|
+
### Progress Reporting
|
|
330
|
+
|
|
331
|
+
Provide real-time progress updates for long-running operations:
|
|
332
|
+
|
|
333
|
+
```javascript
|
|
334
|
+
server.tool(
|
|
335
|
+
{
|
|
336
|
+
name: 'analyze-codebase',
|
|
337
|
+
description: 'Analyze a large codebase with progress tracking',
|
|
338
|
+
schema: z.object({
|
|
339
|
+
path: z.string(),
|
|
340
|
+
includeTests: z.boolean().default(false),
|
|
341
|
+
}),
|
|
342
|
+
},
|
|
343
|
+
async ({ path, includeTests }) => {
|
|
344
|
+
// Discover files to analyze
|
|
345
|
+
const files = await discoverFiles(path, includeTests);
|
|
346
|
+
const totalFiles = files.length;
|
|
347
|
+
|
|
348
|
+
server.progress(0, totalFiles, 'Starting analysis...');
|
|
349
|
+
|
|
350
|
+
const results = [];
|
|
351
|
+
for (let i = 0; i < files.length; i++) {
|
|
352
|
+
const file = files[i];
|
|
353
|
+
|
|
354
|
+
// Update progress with current file being processed
|
|
355
|
+
server.progress(
|
|
356
|
+
i + 1,
|
|
357
|
+
totalFiles,
|
|
358
|
+
`Analyzing ${file} (${i + 1}/${totalFiles})`,
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
// Analyze the file
|
|
362
|
+
const analysis = await analyzeFile(file);
|
|
363
|
+
results.push(analysis);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Final progress update
|
|
367
|
+
server.progress(totalFiles, totalFiles, 'Analysis complete!');
|
|
368
|
+
|
|
369
|
+
return {
|
|
370
|
+
content: [
|
|
371
|
+
{
|
|
372
|
+
type: 'text',
|
|
373
|
+
text: `Analyzed ${totalFiles} files. Found ${results.length} issues.`,
|
|
374
|
+
},
|
|
375
|
+
],
|
|
376
|
+
structuredContent: {
|
|
377
|
+
totalFiles,
|
|
378
|
+
results,
|
|
379
|
+
issues: results.length,
|
|
380
|
+
},
|
|
381
|
+
};
|
|
382
|
+
},
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
// Progress also works in prompts and resources
|
|
386
|
+
server.prompt(
|
|
387
|
+
{
|
|
388
|
+
name: 'generate-report',
|
|
389
|
+
description: 'Generate a comprehensive report',
|
|
390
|
+
schema: z.object({
|
|
391
|
+
sections: z.array(z.string()),
|
|
392
|
+
}),
|
|
393
|
+
},
|
|
394
|
+
async ({ sections }) => {
|
|
395
|
+
const messages = [];
|
|
396
|
+
|
|
397
|
+
for (let i = 0; i < sections.length; i++) {
|
|
398
|
+
server.progress(
|
|
399
|
+
i,
|
|
400
|
+
sections.length,
|
|
401
|
+
`Generating section: ${sections[i]}`,
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
const content = await generateSection(sections[i]);
|
|
405
|
+
messages.push({
|
|
406
|
+
role: 'user',
|
|
407
|
+
content: { type: 'text', text: content },
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
server.progress(sections.length, sections.length, 'Report complete');
|
|
412
|
+
return { messages };
|
|
413
|
+
},
|
|
414
|
+
);
|
|
415
|
+
```
|
|
416
|
+
|
|
268
417
|
### Client Interaction Features
|
|
269
418
|
|
|
270
419
|
```javascript
|
|
@@ -333,34 +482,6 @@ server.resource(
|
|
|
333
482
|
server.changed('resource', 'file://watched/file.txt');
|
|
334
483
|
```
|
|
335
484
|
|
|
336
|
-
### Multiple Schema Libraries
|
|
337
|
-
|
|
338
|
-
```javascript
|
|
339
|
-
// Use different schemas for different tools
|
|
340
|
-
import { z } from 'zod';
|
|
341
|
-
import * as v from 'valibot';
|
|
342
|
-
|
|
343
|
-
server.tool(
|
|
344
|
-
{
|
|
345
|
-
name: 'zod-tool',
|
|
346
|
-
schema: z.object({ name: z.string() }),
|
|
347
|
-
},
|
|
348
|
-
async ({ name }) => ({
|
|
349
|
-
content: [{ type: 'text', text: `Hello ${name}` }],
|
|
350
|
-
}),
|
|
351
|
-
);
|
|
352
|
-
|
|
353
|
-
server.tool(
|
|
354
|
-
{
|
|
355
|
-
name: 'valibot-tool',
|
|
356
|
-
schema: v.object({ age: v.number() }),
|
|
357
|
-
},
|
|
358
|
-
async ({ age }) => ({
|
|
359
|
-
content: [{ type: 'text', text: `Age: ${age}` }],
|
|
360
|
-
}),
|
|
361
|
-
);
|
|
362
|
-
```
|
|
363
|
-
|
|
364
485
|
### Completion API
|
|
365
486
|
|
|
366
487
|
The completion API allows you to provide auto-completion suggestions for prompt and template parameters. Completion functions return an object with `completion` containing `values`, `total`, and `hasMore` properties.
|
|
@@ -653,6 +774,232 @@ server.tool(
|
|
|
653
774
|
);
|
|
654
775
|
```
|
|
655
776
|
|
|
777
|
+
## Dynamic Enabling/Disabling
|
|
778
|
+
|
|
779
|
+
All MCP capabilities (tools, prompts, resources, templates) support dynamic enabling and disabling through the `enabled` function. This allows you to conditionally show or hide capabilities based on runtime conditions, user permissions, or any other logic.
|
|
780
|
+
|
|
781
|
+
### Basic Usage
|
|
782
|
+
|
|
783
|
+
The `enabled` function is called before each list operation and determines whether the capability should be included in the response:
|
|
784
|
+
|
|
785
|
+
```javascript
|
|
786
|
+
server.tool(
|
|
787
|
+
{
|
|
788
|
+
name: 'admin-only-tool',
|
|
789
|
+
description: 'Administrative tool',
|
|
790
|
+
enabled: () => {
|
|
791
|
+
// Only show this tool if user is admin
|
|
792
|
+
return getCurrentUser().isAdmin;
|
|
793
|
+
},
|
|
794
|
+
},
|
|
795
|
+
async (input) => {
|
|
796
|
+
return { content: [{ type: 'text', text: 'Admin action completed' }] };
|
|
797
|
+
},
|
|
798
|
+
);
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
### Async Enabled Functions
|
|
802
|
+
|
|
803
|
+
The `enabled` function can be synchronous or asynchronous:
|
|
804
|
+
|
|
805
|
+
```javascript
|
|
806
|
+
server.resource(
|
|
807
|
+
{
|
|
808
|
+
name: 'private-document',
|
|
809
|
+
description: 'Access private documents',
|
|
810
|
+
uri: 'private://document.txt',
|
|
811
|
+
enabled: async () => {
|
|
812
|
+
// Check permissions asynchronously
|
|
813
|
+
const user = await getCurrentUser();
|
|
814
|
+
return await hasPermission(user.id, 'read-private-docs');
|
|
815
|
+
},
|
|
816
|
+
},
|
|
817
|
+
async (uri) => {
|
|
818
|
+
return {
|
|
819
|
+
contents: [
|
|
820
|
+
{
|
|
821
|
+
uri,
|
|
822
|
+
mimeType: 'text/plain',
|
|
823
|
+
text: await readPrivateDocument(uri),
|
|
824
|
+
},
|
|
825
|
+
],
|
|
826
|
+
};
|
|
827
|
+
},
|
|
828
|
+
);
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
### Context-Aware Enabling
|
|
832
|
+
|
|
833
|
+
Within the `enabled` function you can read the session context with `server.ctx`, allowing for user-specific or session-specific logic:
|
|
834
|
+
|
|
835
|
+
```javascript
|
|
836
|
+
server.prompt(
|
|
837
|
+
{
|
|
838
|
+
name: 'personalized-prompt',
|
|
839
|
+
description: 'User-specific prompt template',
|
|
840
|
+
enabled: () => {
|
|
841
|
+
// Access session information
|
|
842
|
+
const sessionId = server.ctx.sessionId;
|
|
843
|
+
const userPrefs = getUserPreferences(sessionId);
|
|
844
|
+
return userPrefs.enablePersonalization;
|
|
845
|
+
},
|
|
846
|
+
},
|
|
847
|
+
async (input) => {
|
|
848
|
+
return {
|
|
849
|
+
messages: [
|
|
850
|
+
{
|
|
851
|
+
role: 'user',
|
|
852
|
+
content: { type: 'text', text: 'Personalized content...' },
|
|
853
|
+
},
|
|
854
|
+
],
|
|
855
|
+
};
|
|
856
|
+
},
|
|
857
|
+
);
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
or enable a something based on client capabilities or info
|
|
861
|
+
|
|
862
|
+
```javascript
|
|
863
|
+
server.prompt(
|
|
864
|
+
{
|
|
865
|
+
name: 'personalized-prompt',
|
|
866
|
+
description: 'Claude Code prompt',
|
|
867
|
+
enabled: () => {
|
|
868
|
+
// Access session information
|
|
869
|
+
const clientInfo = server.currentClientInfo();
|
|
870
|
+
return clientInfo.name === 'Claude Code';
|
|
871
|
+
},
|
|
872
|
+
},
|
|
873
|
+
async (input) => {
|
|
874
|
+
return {
|
|
875
|
+
messages: [
|
|
876
|
+
{
|
|
877
|
+
role: 'user',
|
|
878
|
+
content: { type: 'text', text: 'Personalized content...' },
|
|
879
|
+
},
|
|
880
|
+
],
|
|
881
|
+
};
|
|
882
|
+
},
|
|
883
|
+
);
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
or
|
|
887
|
+
|
|
888
|
+
```javascript
|
|
889
|
+
server.prompt(
|
|
890
|
+
{
|
|
891
|
+
name: 'fetch-repositories',
|
|
892
|
+
description: 'fetch the repositories of the user',
|
|
893
|
+
enabled: () => {
|
|
894
|
+
// Access session information
|
|
895
|
+
const clientInfo = server.currentClientCapabilities();
|
|
896
|
+
return clientInfo.elicitation != null;
|
|
897
|
+
},
|
|
898
|
+
},
|
|
899
|
+
async (input) => {
|
|
900
|
+
const username = await server.elicitation(
|
|
901
|
+
v.object({
|
|
902
|
+
value: v.string(),
|
|
903
|
+
}),
|
|
904
|
+
);
|
|
905
|
+
const repos = await fetchRepos(username.value);
|
|
906
|
+
return {
|
|
907
|
+
messages: [
|
|
908
|
+
{
|
|
909
|
+
role: 'user',
|
|
910
|
+
content: {
|
|
911
|
+
type: 'text',
|
|
912
|
+
text: `Your repositories are ${repos.join(', ')}`,
|
|
913
|
+
},
|
|
914
|
+
},
|
|
915
|
+
],
|
|
916
|
+
};
|
|
917
|
+
},
|
|
918
|
+
);
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
### Template Enabling
|
|
922
|
+
|
|
923
|
+
URI templates also support the `enabled` function:
|
|
924
|
+
|
|
925
|
+
```javascript
|
|
926
|
+
server.template(
|
|
927
|
+
{
|
|
928
|
+
name: 'user-files',
|
|
929
|
+
description: 'Access user-specific files',
|
|
930
|
+
uri: 'users/{userId}/files/{filename}',
|
|
931
|
+
enabled: async () => {
|
|
932
|
+
// Check if file system is available
|
|
933
|
+
return await isFileSystemMounted();
|
|
934
|
+
},
|
|
935
|
+
complete: {
|
|
936
|
+
userId: (arg, context) => ({
|
|
937
|
+
completion: {
|
|
938
|
+
values: ['user1', 'user2', 'user3'],
|
|
939
|
+
total: 3,
|
|
940
|
+
hasMore: false,
|
|
941
|
+
},
|
|
942
|
+
}),
|
|
943
|
+
},
|
|
944
|
+
},
|
|
945
|
+
async (uri, params) => {
|
|
946
|
+
const content = await readUserFile(params.userId, params.filename);
|
|
947
|
+
return {
|
|
948
|
+
contents: [
|
|
949
|
+
{
|
|
950
|
+
uri,
|
|
951
|
+
mimeType: 'text/plain',
|
|
952
|
+
text: content,
|
|
953
|
+
},
|
|
954
|
+
],
|
|
955
|
+
};
|
|
956
|
+
},
|
|
957
|
+
);
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
### Error Handling
|
|
961
|
+
|
|
962
|
+
If an `enabled` function throws an error, the capability will be treated as disabled:
|
|
963
|
+
|
|
964
|
+
```javascript
|
|
965
|
+
server.tool(
|
|
966
|
+
{
|
|
967
|
+
name: 'network-tool',
|
|
968
|
+
description: 'Tool requiring network access',
|
|
969
|
+
enabled: () => {
|
|
970
|
+
// If network check fails, tool is disabled
|
|
971
|
+
if (!checkNetworkConnection()) {
|
|
972
|
+
throw new Error('Network unavailable');
|
|
973
|
+
}
|
|
974
|
+
return true;
|
|
975
|
+
},
|
|
976
|
+
},
|
|
977
|
+
async (input) => {
|
|
978
|
+
return {
|
|
979
|
+
content: [{ type: 'text', text: 'Network operation completed' }],
|
|
980
|
+
};
|
|
981
|
+
},
|
|
982
|
+
);
|
|
983
|
+
```
|
|
984
|
+
|
|
985
|
+
### Performance Considerations
|
|
986
|
+
|
|
987
|
+
- The `enabled` function is called every time a list is requested
|
|
988
|
+
- Keep enabled functions lightweight to avoid performance issues
|
|
989
|
+
- Consider caching expensive checks when possible
|
|
990
|
+
- Async functions add latency to list operations
|
|
991
|
+
|
|
992
|
+
### Use Cases
|
|
993
|
+
|
|
994
|
+
Common scenarios where `enabled` functions are useful:
|
|
995
|
+
|
|
996
|
+
1. **Permission-based access**: Show tools only to authorized users
|
|
997
|
+
2. **Feature flags**: Enable/disable features based on configuration
|
|
998
|
+
3. **Resource availability**: Hide resources when underlying systems are unavailable
|
|
999
|
+
4. **User preferences**: Customize available capabilities per user
|
|
1000
|
+
5. **Time-based access**: Enable tools only during specific hours
|
|
1001
|
+
6. **License restrictions**: Limit features based on subscription level
|
|
1002
|
+
|
|
656
1003
|
## Contributing
|
|
657
1004
|
|
|
658
1005
|
Contributions are welcome! Please see our [contributing guidelines](../../CONTRIBUTING.md) for details.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tmcp",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.3",
|
|
4
4
|
"description": "The main tmcp library",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@standard-schema/spec": "^1.0.0",
|
|
32
32
|
"json-rpc-2.0": "^1.7.1",
|
|
33
33
|
"sqids": "^0.3.0",
|
|
34
|
-
"uri-template-matcher": "^1.1.
|
|
34
|
+
"uri-template-matcher": "^1.1.1",
|
|
35
35
|
"valibot": "^1.1.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
package/src/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @import SqidsType from "sqids";
|
|
4
4
|
* @import { JSONRPCRequest, JSONRPCParams } from "json-rpc-2.0";
|
|
5
5
|
* @import { ExtractURITemplateVariables } from "./internal/uri-template.js";
|
|
6
|
-
* @import { CallToolResult, ReadResourceResult, GetPromptResult, ClientCapabilities as ClientCapabilitiesType, JSONRPCRequest as JSONRPCRequestType, JSONRPCResponse, CreateMessageRequestParams, CreateMessageResult, Resource, LoggingLevel, ToolAnnotations } from "./validation/index.js";
|
|
6
|
+
* @import { CallToolResult, ReadResourceResult, GetPromptResult, ClientCapabilities as ClientCapabilitiesType, JSONRPCRequest as JSONRPCRequestType, JSONRPCResponse, CreateMessageRequestParams, CreateMessageResult, Resource, LoggingLevel, ToolAnnotations, ClientInfo } from "./validation/index.js";
|
|
7
7
|
* @import { Tool, Completion, Prompt, StoredResource, ServerOptions, ServerInfo, SubscriptionsKeys, McpEvents } from "./internal/internal.js";
|
|
8
8
|
*/
|
|
9
9
|
import { JSONRPCClient, JSONRPCServer } from 'json-rpc-2.0';
|
|
@@ -77,6 +77,17 @@ async function decode_cursor(cursor) {
|
|
|
77
77
|
return decoded;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* @param {()=>boolean | Promise<boolean>} enabled
|
|
82
|
+
*/
|
|
83
|
+
async function safe_enabled(enabled) {
|
|
84
|
+
try {
|
|
85
|
+
return await enabled();
|
|
86
|
+
} catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
80
91
|
/**
|
|
81
92
|
* @typedef {ClientCapabilitiesType} ClientCapabilities
|
|
82
93
|
*/
|
|
@@ -129,7 +140,7 @@ export class McpServer {
|
|
|
129
140
|
};
|
|
130
141
|
|
|
131
142
|
/**
|
|
132
|
-
* @type {AsyncLocalStorage<Context>}
|
|
143
|
+
* @type {AsyncLocalStorage<Context & { progress_token?: string }>}
|
|
133
144
|
*/
|
|
134
145
|
#ctx_storage = new AsyncLocalStorage();
|
|
135
146
|
|
|
@@ -138,6 +149,11 @@ export class McpServer {
|
|
|
138
149
|
*/
|
|
139
150
|
#client_capabilities_map = new Map();
|
|
140
151
|
|
|
152
|
+
/**
|
|
153
|
+
* @type {Map<string|undefined, ClientInfo>}
|
|
154
|
+
*/
|
|
155
|
+
#client_info_map = new Map();
|
|
156
|
+
|
|
141
157
|
/**
|
|
142
158
|
* @type {Map<string|undefined, string>}
|
|
143
159
|
*/
|
|
@@ -195,6 +211,12 @@ export class McpServer {
|
|
|
195
211
|
validated_initialize.capabilities,
|
|
196
212
|
);
|
|
197
213
|
|
|
214
|
+
// Store client info
|
|
215
|
+
this.#client_info_map.set(
|
|
216
|
+
session_id,
|
|
217
|
+
validated_initialize.clientInfo,
|
|
218
|
+
);
|
|
219
|
+
|
|
198
220
|
// Dispatch initialization event
|
|
199
221
|
this.#event_target.dispatchEvent(
|
|
200
222
|
new CustomEvent('initialize', {
|
|
@@ -258,14 +280,34 @@ export class McpServer {
|
|
|
258
280
|
return this.#ctx_storage.getStore()?.sessionId;
|
|
259
281
|
}
|
|
260
282
|
|
|
283
|
+
get #progress_token() {
|
|
284
|
+
return this.#ctx_storage.getStore()?.progress_token;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* The context of the current request, include the session ID and any auth information.
|
|
289
|
+
* @type {Context}
|
|
290
|
+
*/
|
|
261
291
|
get ctx() {
|
|
262
|
-
|
|
292
|
+
// eslint-disable-next-line no-unused-vars
|
|
293
|
+
const { progress_token, ...rest } = this.#ctx_storage.getStore() ?? {};
|
|
294
|
+
return rest;
|
|
263
295
|
}
|
|
264
296
|
|
|
265
297
|
get #client_capabilities() {
|
|
266
298
|
return this.#client_capabilities_map.get(this.#session_id);
|
|
267
299
|
}
|
|
268
300
|
|
|
301
|
+
/**
|
|
302
|
+
* Get the client information (name, version, etc.) of the client that initiated the current request...useful if you want to do something different based on the client.
|
|
303
|
+
*/
|
|
304
|
+
currentClientInfo() {
|
|
305
|
+
return this.#client_info_map.get(this.#session_id);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Get the client capabilities of the client that initiated the current request, you can use this to verify the client support something before invoking the respective method.
|
|
310
|
+
*/
|
|
269
311
|
currentClientCapabilities() {
|
|
270
312
|
return this.#client_capabilities;
|
|
271
313
|
}
|
|
@@ -322,34 +364,41 @@ export class McpServer {
|
|
|
322
364
|
#init_tools() {
|
|
323
365
|
if (!this.#options.capabilities?.tools) return;
|
|
324
366
|
this.#server.addMethod('tools/list', async () => {
|
|
325
|
-
const available_tools =
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
:
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
367
|
+
const available_tools = (
|
|
368
|
+
await Promise.all(
|
|
369
|
+
[...this.#tools].map(async ([name, tool]) => {
|
|
370
|
+
if (
|
|
371
|
+
tool.enabled != null &&
|
|
372
|
+
(await safe_enabled(tool.enabled)) === false
|
|
373
|
+
)
|
|
374
|
+
return null;
|
|
375
|
+
return {
|
|
376
|
+
name,
|
|
377
|
+
title: tool.title || tool.description,
|
|
378
|
+
description: tool.description,
|
|
379
|
+
|
|
380
|
+
inputSchema: tool.schema
|
|
381
|
+
? await this.#options.adapter.toJsonSchema(
|
|
382
|
+
tool.schema,
|
|
383
|
+
)
|
|
384
|
+
: { type: 'object', properties: {} },
|
|
385
|
+
...(tool.outputSchema
|
|
386
|
+
? {
|
|
387
|
+
outputSchema:
|
|
388
|
+
await this.#options.adapter.toJsonSchema(
|
|
389
|
+
tool.outputSchema,
|
|
390
|
+
),
|
|
391
|
+
}
|
|
392
|
+
: {}),
|
|
393
|
+
...(tool.annotations
|
|
394
|
+
? {
|
|
395
|
+
annotations: tool.annotations,
|
|
396
|
+
}
|
|
397
|
+
: {}),
|
|
398
|
+
};
|
|
399
|
+
}),
|
|
400
|
+
)
|
|
401
|
+
).filter((tool) => tool !== null);
|
|
353
402
|
return {
|
|
354
403
|
tools: available_tools,
|
|
355
404
|
};
|
|
@@ -419,39 +468,49 @@ export class McpServer {
|
|
|
419
468
|
#init_prompts() {
|
|
420
469
|
if (!this.#options.capabilities?.prompts) return;
|
|
421
470
|
this.#server.addMethod('prompts/list', async ({ cursor } = {}) => {
|
|
422
|
-
const all_prompts =
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
)
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
471
|
+
const all_prompts = (
|
|
472
|
+
await Promise.all(
|
|
473
|
+
[...this.#prompts].map(async ([name, prompt]) => {
|
|
474
|
+
if (
|
|
475
|
+
prompt.enabled != null &&
|
|
476
|
+
(await safe_enabled(prompt.enabled)) === false
|
|
477
|
+
)
|
|
478
|
+
return null;
|
|
479
|
+
const arguments_schema = prompt.schema
|
|
480
|
+
? await this.#options.adapter.toJsonSchema(
|
|
481
|
+
prompt.schema,
|
|
482
|
+
)
|
|
483
|
+
: {
|
|
484
|
+
type: 'object',
|
|
485
|
+
properties:
|
|
486
|
+
/** @type {Record<string, {description: string}>} */ ({}),
|
|
487
|
+
required: [],
|
|
488
|
+
};
|
|
489
|
+
const keys = Object.keys(
|
|
490
|
+
arguments_schema.properties ?? {},
|
|
491
|
+
);
|
|
492
|
+
const required = arguments_schema.required ?? [];
|
|
493
|
+
return {
|
|
494
|
+
name,
|
|
495
|
+
title: prompt.title || prompt.description,
|
|
496
|
+
description: prompt.description,
|
|
497
|
+
arguments: keys.map((key) => {
|
|
498
|
+
const property =
|
|
499
|
+
arguments_schema.properties?.[key];
|
|
500
|
+
const description =
|
|
501
|
+
property && property !== true
|
|
502
|
+
? property.description
|
|
503
|
+
: key;
|
|
504
|
+
return {
|
|
505
|
+
name: key,
|
|
506
|
+
required: required.includes(key),
|
|
507
|
+
description,
|
|
508
|
+
};
|
|
509
|
+
}),
|
|
510
|
+
};
|
|
511
|
+
}),
|
|
512
|
+
)
|
|
513
|
+
).filter((prompt) => prompt !== null);
|
|
455
514
|
|
|
456
515
|
const pagination_options = this.#options.pagination?.prompts;
|
|
457
516
|
if (!pagination_options || pagination_options.size == null) {
|
|
@@ -528,6 +587,11 @@ export class McpServer {
|
|
|
528
587
|
for (const [uri, { description, name, title, ...resource }] of this
|
|
529
588
|
.#resources) {
|
|
530
589
|
if (!resource.template) {
|
|
590
|
+
if (
|
|
591
|
+
resource.enabled != null &&
|
|
592
|
+
(await safe_enabled(resource.enabled)) === false
|
|
593
|
+
)
|
|
594
|
+
continue;
|
|
531
595
|
all_resources.push({
|
|
532
596
|
name,
|
|
533
597
|
title: title || description,
|
|
@@ -535,6 +599,11 @@ export class McpServer {
|
|
|
535
599
|
uri,
|
|
536
600
|
});
|
|
537
601
|
} else if (resource.list_resources) {
|
|
602
|
+
if (
|
|
603
|
+
resource.enabled != null &&
|
|
604
|
+
(await safe_enabled(resource.enabled)) === false
|
|
605
|
+
)
|
|
606
|
+
continue;
|
|
538
607
|
const template_resources = await resource.list_resources();
|
|
539
608
|
all_resources.push(...template_resources);
|
|
540
609
|
}
|
|
@@ -563,20 +632,29 @@ export class McpServer {
|
|
|
563
632
|
});
|
|
564
633
|
this.#server.addMethod('resources/templates/list', async () => {
|
|
565
634
|
return {
|
|
566
|
-
resourceTemplates:
|
|
567
|
-
(
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
635
|
+
resourceTemplates: (
|
|
636
|
+
await Promise.all(
|
|
637
|
+
[...this.#resources].map(
|
|
638
|
+
async ([
|
|
639
|
+
uri,
|
|
640
|
+
{ description, name, title, template, enabled },
|
|
641
|
+
]) => {
|
|
642
|
+
if (!template) return null;
|
|
643
|
+
if (
|
|
644
|
+
enabled != null &&
|
|
645
|
+
(await safe_enabled(enabled)) === false
|
|
646
|
+
)
|
|
647
|
+
return null;
|
|
648
|
+
return {
|
|
649
|
+
name,
|
|
650
|
+
title: title || description,
|
|
651
|
+
description,
|
|
652
|
+
uriTemplate: uri,
|
|
653
|
+
};
|
|
654
|
+
},
|
|
655
|
+
),
|
|
656
|
+
)
|
|
657
|
+
).filter((resource) => resource != null),
|
|
580
658
|
};
|
|
581
659
|
});
|
|
582
660
|
this.#server.addMethod('resources/read', async ({ uri }) => {
|
|
@@ -640,9 +718,7 @@ export class McpServer {
|
|
|
640
718
|
this.roots = [];
|
|
641
719
|
}
|
|
642
720
|
}
|
|
643
|
-
|
|
644
|
-
*
|
|
645
|
-
*/
|
|
721
|
+
|
|
646
722
|
#init_completion() {
|
|
647
723
|
this.#server.addMethod(
|
|
648
724
|
'completion/complete',
|
|
@@ -660,9 +736,7 @@ export class McpServer {
|
|
|
660
736
|
},
|
|
661
737
|
);
|
|
662
738
|
}
|
|
663
|
-
|
|
664
|
-
*
|
|
665
|
-
*/
|
|
739
|
+
|
|
666
740
|
#init_logging() {
|
|
667
741
|
if (!this.#options.capabilities?.logging) return;
|
|
668
742
|
|
|
@@ -672,13 +746,25 @@ export class McpServer {
|
|
|
672
746
|
});
|
|
673
747
|
}
|
|
674
748
|
/**
|
|
749
|
+
* Add a tool to the server. If you want to receive any input you need to provide a schema. The schema needs to be a valid Standard Schema V1 schema and needs to be an Object with the properties you need,
|
|
750
|
+
* Use the description and title to help the LLM to understand what the tool does and when to use it. If you provide an outputSchema, you need to return a structuredContent that matches the schema.
|
|
751
|
+
*
|
|
752
|
+
* Tools will be invoked by the LLM when it thinks it needs to use them, you can use the annotations to provide additional information about the tool, like what it does, how to use it, etc.
|
|
675
753
|
* @template {StandardSchema | undefined} [TSchema=undefined]
|
|
676
754
|
* @template {StandardSchema | undefined} [TOutputSchema=undefined]
|
|
677
|
-
* @param {{ name: string; description: string; title?: string; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never; outputSchema?: StandardSchemaV1.InferOutput<TOutputSchema extends undefined ? never : TOutputSchema> extends Record<string, unknown> ? TOutputSchema : never; annotations?: ToolAnnotations }} options
|
|
755
|
+
* @param {{ name: string; description: string; title?: string; enabled?: ()=>boolean | Promise<boolean>; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never; outputSchema?: StandardSchemaV1.InferOutput<TOutputSchema extends undefined ? never : TOutputSchema> extends Record<string, unknown> ? TOutputSchema : never; annotations?: ToolAnnotations }} options
|
|
678
756
|
* @param {TSchema extends undefined ? (()=>Promise<CallToolResult<TOutputSchema extends undefined ? undefined : StandardSchemaV1.InferInput<TOutputSchema extends undefined ? never : TOutputSchema>>> | CallToolResult<TOutputSchema extends undefined ? undefined : StandardSchemaV1.InferInput<TOutputSchema extends undefined ? never : TOutputSchema>>) : ((input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<CallToolResult<TOutputSchema extends undefined ? undefined : StandardSchemaV1.InferInput<TOutputSchema extends undefined ? never : TOutputSchema>>> | CallToolResult<TOutputSchema extends undefined ? undefined : StandardSchemaV1.InferInput<TOutputSchema extends undefined ? never : TOutputSchema>>)} execute
|
|
679
757
|
*/
|
|
680
758
|
tool(
|
|
681
|
-
{
|
|
759
|
+
{
|
|
760
|
+
name,
|
|
761
|
+
description,
|
|
762
|
+
title,
|
|
763
|
+
schema,
|
|
764
|
+
outputSchema,
|
|
765
|
+
annotations,
|
|
766
|
+
enabled,
|
|
767
|
+
},
|
|
682
768
|
execute,
|
|
683
769
|
) {
|
|
684
770
|
if (this.#options.capabilities?.tools?.listChanged) {
|
|
@@ -687,6 +773,7 @@ export class McpServer {
|
|
|
687
773
|
this.#tools.set(name, {
|
|
688
774
|
description,
|
|
689
775
|
title,
|
|
776
|
+
enabled,
|
|
690
777
|
schema,
|
|
691
778
|
outputSchema,
|
|
692
779
|
execute,
|
|
@@ -694,11 +781,16 @@ export class McpServer {
|
|
|
694
781
|
});
|
|
695
782
|
}
|
|
696
783
|
/**
|
|
784
|
+
* Add a prompt to the server. Prompts are used to provide the user with pre-defined messages that adds context to the LLM.
|
|
785
|
+
* Use the description and title to help the user to understand what the prompt does and when to use it.
|
|
786
|
+
*
|
|
787
|
+
* A prompt can also have a schema that defines the input it expects, the user will be prompted to enter the inputs you request. It can also have a complete function
|
|
788
|
+
* for each input that will be used to provide completions for the user.
|
|
697
789
|
* @template {StandardSchema | undefined} [TSchema=undefined]
|
|
698
|
-
* @param {{ name: string; description: string; title?: string; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never; complete?: NoInfer<TSchema extends undefined ? never : Partial<Record<keyof (StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>), Completion>>> }} options
|
|
790
|
+
* @param {{ name: string; description: string; title?: string; enabled?: ()=>boolean | Promise<boolean>; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never; complete?: NoInfer<TSchema extends undefined ? never : Partial<Record<keyof (StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>), Completion>>> }} options
|
|
699
791
|
* @param {TSchema extends undefined ? (()=>Promise<GetPromptResult> | GetPromptResult) : (input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<GetPromptResult> | GetPromptResult} execute
|
|
700
792
|
*/
|
|
701
|
-
prompt({ name, description, title, schema, complete }, execute) {
|
|
793
|
+
prompt({ name, description, title, schema, complete, enabled }, execute) {
|
|
702
794
|
if (complete) {
|
|
703
795
|
this.#completions['ref/prompt'].set(name, complete);
|
|
704
796
|
}
|
|
@@ -710,6 +802,7 @@ export class McpServer {
|
|
|
710
802
|
title,
|
|
711
803
|
schema,
|
|
712
804
|
execute,
|
|
805
|
+
enabled,
|
|
713
806
|
});
|
|
714
807
|
}
|
|
715
808
|
/**
|
|
@@ -727,32 +820,50 @@ export class McpServer {
|
|
|
727
820
|
}
|
|
728
821
|
this.#resources.set(uri, resource);
|
|
729
822
|
}
|
|
823
|
+
|
|
730
824
|
/**
|
|
731
|
-
*
|
|
825
|
+
* Add a resource to the server. Resources are added manually to the context by the user to provide the LLM with additional context.
|
|
826
|
+
* Use the description and title to help the user to understand what the resource is.
|
|
827
|
+
* @param {{ name: string; description: string; title?: string; uri: string, enabled?: ()=>boolean | Promise<boolean>; }} options
|
|
732
828
|
* @param {(uri: string) => Promise<ReadResourceResult> | ReadResourceResult} execute
|
|
733
829
|
*/
|
|
734
|
-
resource({ name, description, title, uri }, execute) {
|
|
830
|
+
resource({ name, description, title, uri, enabled }, execute) {
|
|
735
831
|
this.#resource({
|
|
736
832
|
name,
|
|
737
833
|
description,
|
|
738
834
|
title,
|
|
739
835
|
uri,
|
|
740
836
|
execute,
|
|
837
|
+
enabled,
|
|
741
838
|
template: false,
|
|
742
839
|
});
|
|
743
840
|
}
|
|
744
841
|
/**
|
|
842
|
+
* Add a resource template to the server. Resources are added manually to the context by the user to provide the LLM with additional context.
|
|
843
|
+
* Resource templates are used to create resources dynamically based on a URI template. The URI template should be a valid URI template as defined in RFC 6570.
|
|
844
|
+
* Resource templates can have a list method that returns a list of resources that match the template and a complete method that returns a list of resources given one of the template variables, this method will
|
|
845
|
+
* be invoked to provide completions for the template variables to the user.
|
|
846
|
+
* Use the description and title to help the user to understand what the resource is.
|
|
745
847
|
* @template {string} TUri
|
|
746
848
|
* @template {ExtractURITemplateVariables<TUri>} TVariables
|
|
747
|
-
* @param {{ name: string; description: string; title?: string; uri: TUri; complete?: NoInfer<TVariables extends never ? never : Partial<Record<TVariables, Completion>>>; list?: () => Promise<Array<Resource>> | Array<Resource> }} options
|
|
849
|
+
* @param {{ name: string; description: string; title?: string; enabled?: ()=>boolean | Promise<boolean>; uri: TUri; complete?: NoInfer<TVariables extends never ? never : Partial<Record<TVariables, Completion>>>; list?: () => Promise<Array<Resource>> | Array<Resource> }} options
|
|
748
850
|
* @param {(uri: string, params: Record<TVariables, string | string[]>) => Promise<ReadResourceResult> | ReadResourceResult} execute
|
|
749
851
|
*/
|
|
750
852
|
template(
|
|
751
|
-
{
|
|
853
|
+
{
|
|
854
|
+
name,
|
|
855
|
+
description,
|
|
856
|
+
title,
|
|
857
|
+
uri,
|
|
858
|
+
complete,
|
|
859
|
+
list: list_resources,
|
|
860
|
+
enabled,
|
|
861
|
+
},
|
|
752
862
|
execute,
|
|
753
863
|
) {
|
|
754
864
|
this.#resource({
|
|
755
865
|
name,
|
|
866
|
+
enabled,
|
|
756
867
|
description,
|
|
757
868
|
title,
|
|
758
869
|
uri,
|
|
@@ -763,6 +874,8 @@ export class McpServer {
|
|
|
763
874
|
});
|
|
764
875
|
}
|
|
765
876
|
/**
|
|
877
|
+
* The main function that receive a JSONRpc message and either dispatch a `send` event or process the request.
|
|
878
|
+
*
|
|
766
879
|
* @param {JSONRPCResponse | JSONRPCRequest} message
|
|
767
880
|
* @param {Context} [ctx]
|
|
768
881
|
* @returns {ReturnType<JSONRPCServer['receive']> | ReturnType<JSONRPCClient['receive'] | undefined>}
|
|
@@ -788,8 +901,11 @@ export class McpServer {
|
|
|
788
901
|
this.#options.logging?.default ?? 'info',
|
|
789
902
|
);
|
|
790
903
|
}
|
|
904
|
+
const progress_token = /** @type {string | undefined} */ (
|
|
905
|
+
validated_message.output.params?._meta?.progressToken
|
|
906
|
+
);
|
|
791
907
|
return this.#ctx_storage.run(
|
|
792
|
-
ctx ?? {},
|
|
908
|
+
{ ...(ctx ?? {}), progress_token },
|
|
793
909
|
async () =>
|
|
794
910
|
await this.#server.receive(validated_message.output),
|
|
795
911
|
);
|
|
@@ -833,6 +949,12 @@ export class McpServer {
|
|
|
833
949
|
}
|
|
834
950
|
|
|
835
951
|
/**
|
|
952
|
+
* Emit an elicitation request to the client. Elicitations are used to ask the user for input in a structured way, the client will show a UI to the user to fill the input.
|
|
953
|
+
* The schema should be a valid Standard Schema V1 schema and should be an Object with the properties you need.
|
|
954
|
+
* The client will return the validated input as a JSON object that matches the schema.
|
|
955
|
+
*
|
|
956
|
+
* If the client doesn't support elicitation, it will throw an error.
|
|
957
|
+
*
|
|
836
958
|
* @template {StandardSchema} TSchema
|
|
837
959
|
* @param {TSchema} schema
|
|
838
960
|
* @returns {Promise<StandardSchemaV1.InferOutput<TSchema>>}
|
|
@@ -894,8 +1016,33 @@ export class McpServer {
|
|
|
894
1016
|
return v.parse(CreateMessageResultSchema, response);
|
|
895
1017
|
}
|
|
896
1018
|
|
|
1019
|
+
/**
|
|
1020
|
+
* Send a progress notification to the client. This is useful for long-running operations where you want to inform the user about the progress.
|
|
1021
|
+
*
|
|
1022
|
+
* @param {number} progress The current progress value, it should be between 0 and total and should always increase
|
|
1023
|
+
* @param {number} [total] The total value, defaults to 1
|
|
1024
|
+
* @param {string} [message] An optional message to accompany the progress update
|
|
1025
|
+
*/
|
|
1026
|
+
progress(progress, total = 1, message = undefined) {
|
|
1027
|
+
if (this.#progress_token != null) {
|
|
1028
|
+
this.#notify(
|
|
1029
|
+
'notifications/progress',
|
|
1030
|
+
{
|
|
1031
|
+
progress,
|
|
1032
|
+
total,
|
|
1033
|
+
message,
|
|
1034
|
+
progressToken: this.#progress_token,
|
|
1035
|
+
},
|
|
1036
|
+
{
|
|
1037
|
+
sessions: this.#session_id ? [this.#session_id] : undefined,
|
|
1038
|
+
},
|
|
1039
|
+
);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
|
|
897
1043
|
/**
|
|
898
1044
|
* Log a message to the client if logging is enabled and the level is appropriate
|
|
1045
|
+
*
|
|
899
1046
|
* @param {LoggingLevel} level
|
|
900
1047
|
* @param {unknown} data
|
|
901
1048
|
* @param {string} [logger]
|
|
@@ -19,6 +19,7 @@ export type Tool<TSchema extends StandardSchemaV1 = StandardSchemaV1<any>, TOutp
|
|
|
19
19
|
outputSchema?: TOutputSchema;
|
|
20
20
|
title?: string;
|
|
21
21
|
annotations?: ToolAnnotations;
|
|
22
|
+
enabled?: () => boolean | Promise<boolean>;
|
|
22
23
|
execute: (
|
|
23
24
|
input?: StandardSchemaV1.InferInput<TSchema>,
|
|
24
25
|
) => Promise<CallToolResult> | CallToolResult;
|
|
@@ -33,6 +34,7 @@ export type Prompt<TSchema extends StandardSchemaV1 = StandardSchemaV1<any>> = {
|
|
|
33
34
|
description: string;
|
|
34
35
|
schema: TSchema;
|
|
35
36
|
title?: string;
|
|
37
|
+
enabled?: () => boolean | Promise<boolean>;
|
|
36
38
|
execute: (
|
|
37
39
|
input?: StandardSchemaV1.InferInput<TSchema>,
|
|
38
40
|
) => Promise<GetPromptResult> | GetPromptResult;
|
|
@@ -44,6 +46,7 @@ export type StoredResource =
|
|
|
44
46
|
name: string;
|
|
45
47
|
template: true;
|
|
46
48
|
title?: string;
|
|
49
|
+
enabled?: () => boolean | Promise<boolean>;
|
|
47
50
|
list_resources?: () =>
|
|
48
51
|
| Promise<Array<Resource>>
|
|
49
52
|
| Array<Resource>;
|
|
@@ -57,6 +60,7 @@ export type StoredResource =
|
|
|
57
60
|
name: string;
|
|
58
61
|
template: false;
|
|
59
62
|
title?: string;
|
|
63
|
+
enabled?: () => boolean | Promise<boolean>;
|
|
60
64
|
execute: (
|
|
61
65
|
uri: string,
|
|
62
66
|
) => Promise<ReadResourceResult> | ReadResourceResult;
|
package/src/types/index.d.ts
CHANGED
|
@@ -11,7 +11,21 @@ declare module 'tmcp' {
|
|
|
11
11
|
uri: string;
|
|
12
12
|
name?: string;
|
|
13
13
|
}>;
|
|
14
|
+
/**
|
|
15
|
+
* The context of the current request, include the session ID and any auth information.
|
|
16
|
+
* */
|
|
14
17
|
get ctx(): Context;
|
|
18
|
+
/**
|
|
19
|
+
* Get the client information (name, version, etc.) of the client that initiated the current request...useful if you want to do something different based on the client.
|
|
20
|
+
*/
|
|
21
|
+
currentClientInfo(): {
|
|
22
|
+
version: string;
|
|
23
|
+
name: string;
|
|
24
|
+
title?: string | undefined;
|
|
25
|
+
} | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Get the client capabilities of the client that initiated the current request, you can use this to verify the client support something before invoking the respective method.
|
|
28
|
+
*/
|
|
15
29
|
currentClientCapabilities(): {
|
|
16
30
|
experimental?: {} | undefined;
|
|
17
31
|
sampling?: {} | undefined;
|
|
@@ -22,40 +36,67 @@ declare module 'tmcp' {
|
|
|
22
36
|
} | undefined;
|
|
23
37
|
|
|
24
38
|
on<TEvent extends keyof McpEvents>(event: TEvent, callback: McpEvents[TEvent], options?: AddEventListenerOptions): () => void;
|
|
25
|
-
|
|
26
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Add a tool to the server. If you want to receive any input you need to provide a schema. The schema needs to be a valid Standard Schema V1 schema and needs to be an Object with the properties you need,
|
|
41
|
+
* Use the description and title to help the LLM to understand what the tool does and when to use it. If you provide an outputSchema, you need to return a structuredContent that matches the schema.
|
|
42
|
+
*
|
|
43
|
+
* Tools will be invoked by the LLM when it thinks it needs to use them, you can use the annotations to provide additional information about the tool, like what it does, how to use it, etc.
|
|
44
|
+
* */
|
|
45
|
+
tool<TSchema extends StandardSchema | undefined = undefined, TOutputSchema extends StandardSchema | undefined = undefined>({ name, description, title, schema, outputSchema, annotations, enabled, }: {
|
|
27
46
|
name: string;
|
|
28
47
|
description: string;
|
|
29
48
|
title?: string;
|
|
49
|
+
enabled?: () => boolean | Promise<boolean>;
|
|
30
50
|
schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never;
|
|
31
51
|
outputSchema?: StandardSchemaV1.InferOutput<TOutputSchema extends undefined ? never : TOutputSchema> extends Record<string, unknown> ? TOutputSchema : never;
|
|
32
52
|
annotations?: ToolAnnotations;
|
|
33
53
|
}, execute: TSchema extends undefined ? (() => Promise<CallToolResult<TOutputSchema extends undefined ? undefined : StandardSchemaV1.InferInput<TOutputSchema extends undefined ? never : TOutputSchema>>> | CallToolResult<TOutputSchema extends undefined ? undefined : StandardSchemaV1.InferInput<TOutputSchema extends undefined ? never : TOutputSchema>>) : ((input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<CallToolResult<TOutputSchema extends undefined ? undefined : StandardSchemaV1.InferInput<TOutputSchema extends undefined ? never : TOutputSchema>>> | CallToolResult<TOutputSchema extends undefined ? undefined : StandardSchemaV1.InferInput<TOutputSchema extends undefined ? never : TOutputSchema>>)): void;
|
|
34
|
-
|
|
35
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Add a prompt to the server. Prompts are used to provide the user with pre-defined messages that adds context to the LLM.
|
|
56
|
+
* Use the description and title to help the user to understand what the prompt does and when to use it.
|
|
57
|
+
*
|
|
58
|
+
* A prompt can also have a schema that defines the input it expects, the user will be prompted to enter the inputs you request. It can also have a complete function
|
|
59
|
+
* for each input that will be used to provide completions for the user.
|
|
60
|
+
* */
|
|
61
|
+
prompt<TSchema extends StandardSchema | undefined = undefined>({ name, description, title, schema, complete, enabled }: {
|
|
36
62
|
name: string;
|
|
37
63
|
description: string;
|
|
38
64
|
title?: string;
|
|
65
|
+
enabled?: () => boolean | Promise<boolean>;
|
|
39
66
|
schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never;
|
|
40
67
|
complete?: NoInfer<TSchema extends undefined ? never : Partial<Record<keyof StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>, Completion>>>;
|
|
41
68
|
}, execute: TSchema extends undefined ? (() => Promise<GetPromptResult> | GetPromptResult) : (input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<GetPromptResult> | GetPromptResult): void;
|
|
42
|
-
|
|
43
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Add a resource to the server. Resources are added manually to the context by the user to provide the LLM with additional context.
|
|
71
|
+
* Use the description and title to help the user to understand what the resource is.
|
|
72
|
+
* */
|
|
73
|
+
resource({ name, description, title, uri, enabled }: {
|
|
44
74
|
name: string;
|
|
45
75
|
description: string;
|
|
46
76
|
title?: string;
|
|
47
77
|
uri: string;
|
|
78
|
+
enabled?: () => boolean | Promise<boolean>;
|
|
48
79
|
}, execute: (uri: string) => Promise<ReadResourceResult> | ReadResourceResult): void;
|
|
49
|
-
|
|
50
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Add a resource template to the server. Resources are added manually to the context by the user to provide the LLM with additional context.
|
|
82
|
+
* Resource templates are used to create resources dynamically based on a URI template. The URI template should be a valid URI template as defined in RFC 6570.
|
|
83
|
+
* Resource templates can have a list method that returns a list of resources that match the template and a complete method that returns a list of resources given one of the template variables, this method will
|
|
84
|
+
* be invoked to provide completions for the template variables to the user.
|
|
85
|
+
* Use the description and title to help the user to understand what the resource is.
|
|
86
|
+
* */
|
|
87
|
+
template<TUri extends string, TVariables extends ExtractURITemplateVariables<TUri>>({ name, description, title, uri, complete, list: list_resources, enabled, }: {
|
|
51
88
|
name: string;
|
|
52
89
|
description: string;
|
|
53
90
|
title?: string;
|
|
91
|
+
enabled?: () => boolean | Promise<boolean>;
|
|
54
92
|
uri: TUri;
|
|
55
93
|
complete?: NoInfer<TVariables extends never ? never : Partial<Record<TVariables, Completion>>>;
|
|
56
94
|
list?: () => Promise<Array<Resource>> | Array<Resource>;
|
|
57
95
|
}, execute: (uri: string, params: Record<TVariables, string | string[]>) => Promise<ReadResourceResult> | ReadResourceResult): void;
|
|
58
|
-
|
|
96
|
+
/**
|
|
97
|
+
* The main function that receive a JSONRpc message and either dispatch a `send` event or process the request.
|
|
98
|
+
*
|
|
99
|
+
* */
|
|
59
100
|
receive(message: JSONRPCResponse | JSONRPCRequest, ctx?: Context): ReturnType<JSONRPCServer["receive"]> | ReturnType<JSONRPCClient["receive"] | undefined>;
|
|
60
101
|
/**
|
|
61
102
|
* Send a notification for subscriptions
|
|
@@ -65,14 +106,30 @@ declare module 'tmcp' {
|
|
|
65
106
|
* Refresh roots list from client
|
|
66
107
|
*/
|
|
67
108
|
refreshRoots(): Promise<void>;
|
|
68
|
-
|
|
109
|
+
/**
|
|
110
|
+
* Emit an elicitation request to the client. Elicitations are used to ask the user for input in a structured way, the client will show a UI to the user to fill the input.
|
|
111
|
+
* The schema should be a valid Standard Schema V1 schema and should be an Object with the properties you need.
|
|
112
|
+
* The client will return the validated input as a JSON object that matches the schema.
|
|
113
|
+
*
|
|
114
|
+
* If the client doesn't support elicitation, it will throw an error.
|
|
115
|
+
*
|
|
116
|
+
* */
|
|
69
117
|
elicitation<TSchema extends StandardSchema>(schema: TSchema): Promise<StandardSchemaV1.InferOutput<TSchema>>;
|
|
70
118
|
/**
|
|
71
119
|
* Request language model sampling from the client
|
|
72
120
|
* */
|
|
73
121
|
message(request: CreateMessageRequestParams): Promise<CreateMessageResult>;
|
|
122
|
+
/**
|
|
123
|
+
* Send a progress notification to the client. This is useful for long-running operations where you want to inform the user about the progress.
|
|
124
|
+
*
|
|
125
|
+
* @param progress The current progress value, it should be between 0 and total and should always increase
|
|
126
|
+
* @param total The total value, defaults to 1
|
|
127
|
+
* @param message An optional message to accompany the progress update
|
|
128
|
+
*/
|
|
129
|
+
progress(progress: number, total?: number, message?: string): void;
|
|
74
130
|
/**
|
|
75
131
|
* Log a message to the client if logging is enabled and the level is appropriate
|
|
132
|
+
*
|
|
76
133
|
*
|
|
77
134
|
*/
|
|
78
135
|
log(level: LoggingLevel, data: unknown, logger?: string): void;
|
package/src/types/index.d.ts.map
CHANGED
|
@@ -55,6 +55,6 @@
|
|
|
55
55
|
null,
|
|
56
56
|
null
|
|
57
57
|
],
|
|
58
|
-
"mappings": ";;;;;
|
|
58
|
+
"mappings": ";;;;;cAiGaA,SAASA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aCs2CsCC,kBAAkBA;MC56ClEC,UAAUA;;;;;MAyCVC,aAAaA;;;;;;;;;;;;;MAabC,UAAUA;;;;;;MAMVC,iBAAiBA;;MAEjBC,SAASA;;;;;;;;;;;;ODWRC,qBAAqBA;;;;;;;;;;;;;;OAuGrBC,wBAAwBA;;;;;;;;;;;;;;;;;;;;;;;OA6BxBC,6BAA6BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAuB7BC,wBAAwBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmMxBC,cAAcA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4GdC,wBAAwBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8PxBC,qBAAqBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BrBC,qBAAqBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2GrBC,oBAAoBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAoEpBC,kBAAkBA;OAuGlBC,gCAAgCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8ChCC,yBAAyBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+LzBC,oBAAoBA;;;;;;;;;;;;;;;;;;;;;;MA8H2BC,kBAAkBA;MASbC,uBAAuBA;;;;;;MAO5BC,kBAAkBA;MAGrBC,eAAeA;MAGhBC,cAAcA;MAGFC,0BAA0BA;MAGjCC,mBAAmBA;MAY9BC,QAAQA;MAMDC,eAAeA;MAGlBC,YAAYA;MAGTC,eAAeA;;ME7/CnEC,IAAIA;;;;;;;MAOJC,cAAcA;;;;;;;MAOdC,cAAcA;;;;;MAKdC,qBAAqBA;;;;;;;;;;;;;;;;;MAiBrBC,4BAA4BA;;;;;;MAMrBC,2BAA2BA;;;;;;;OCjC1BC,iBAAiBA;;;;;;;;;;;;;;;;cAAjBA,iBAAiBA",
|
|
59
59
|
"ignoreList": []
|
|
60
60
|
}
|