tiny-stdio-mcp-server 0.1.6 → 0.1.8
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 +29 -2
- package/dist/schema.d.ts +1 -0
- package/dist/schema.js +7 -4
- package/dist/server.d.ts +2 -2
- package/dist/server.js +191 -28
- package/dist/types.d.ts +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,9 +42,9 @@ Creates a new MCP server.
|
|
|
42
42
|
const server = createServer({ name: "my-server", version: "1.0.0" });
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
### `.tool(name, description, schema, handler)`
|
|
45
|
+
### `.tool(name, description, schema, handler, outputSchema?)`
|
|
46
46
|
|
|
47
|
-
Register a tool. The handler receives typed args matching the schema and returns a string, content helper,
|
|
47
|
+
Register a tool. The handler receives typed args matching the schema and returns a string, content helper, array of content, or a typed object when `outputSchema` is supplied.
|
|
48
48
|
|
|
49
49
|
```ts
|
|
50
50
|
const schema = defineSchema({
|
|
@@ -58,6 +58,33 @@ server.tool("search", "Search for things", schema, async ({ query, limit }) => {
|
|
|
58
58
|
});
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
+
For structured-data tools, pass a root-object output schema. The server advertises it as MCP `Tool.outputSchema`, validates the handler result, returns it as `CallToolResult.structuredContent`, and also includes a JSON text backstop in `content[]` for older clients.
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
const input = defineSchema({
|
|
65
|
+
query: { type: "string" },
|
|
66
|
+
});
|
|
67
|
+
const output = defineSchema({
|
|
68
|
+
items: {
|
|
69
|
+
type: "array",
|
|
70
|
+
items: {
|
|
71
|
+
type: "object",
|
|
72
|
+
properties: {
|
|
73
|
+
title: { type: "string" },
|
|
74
|
+
score: { type: "number" },
|
|
75
|
+
},
|
|
76
|
+
required: ["title", "score"],
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
server.tool("search", "Search", input, async ({ query }) => ({
|
|
82
|
+
items: [{ title: query, score: 1 }],
|
|
83
|
+
}), output);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Output schemas must have `type: "object"` at the root. Tools whose natural result is prose, images, audio, files, or other content blocks should omit `outputSchema` and keep returning content.
|
|
87
|
+
|
|
61
88
|
### `.listen()`
|
|
62
89
|
|
|
63
90
|
Start listening on stdin/stdout (standard MCP stdio transport).
|
package/dist/schema.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ interface SchemaPropertyDef {
|
|
|
4
4
|
type: SchemaPropertyType;
|
|
5
5
|
description?: string;
|
|
6
6
|
optional?: boolean;
|
|
7
|
+
[keyword: string]: unknown;
|
|
7
8
|
}
|
|
8
9
|
type SchemaDefinition = Record<string, SchemaPropertyDef>;
|
|
9
10
|
type InferType<T extends SchemaPropertyType> = T extends "string" ? string : T extends "number" ? number : T extends "boolean" ? boolean : T extends "object" ? Record<string, unknown> : T extends "array" ? unknown[] : never;
|
package/dist/schema.js
CHANGED
|
@@ -2,14 +2,17 @@ export function defineSchema(definition) {
|
|
|
2
2
|
const properties = {};
|
|
3
3
|
const required = [];
|
|
4
4
|
for (const [key, prop] of Object.entries(definition)) {
|
|
5
|
+
const jsonSchemaProperty = {};
|
|
6
|
+
for (const [propertyKey, propertyValue] of Object.entries(prop)) {
|
|
7
|
+
if (propertyKey !== "optional") {
|
|
8
|
+
jsonSchemaProperty[propertyKey] = propertyValue;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
5
11
|
Object.defineProperty(properties, key, {
|
|
6
12
|
enumerable: true,
|
|
7
13
|
configurable: true,
|
|
8
14
|
writable: true,
|
|
9
|
-
value:
|
|
10
|
-
type: prop.type,
|
|
11
|
-
...(prop.description !== undefined && { description: prop.description }),
|
|
12
|
-
},
|
|
15
|
+
value: jsonSchemaProperty,
|
|
13
16
|
});
|
|
14
17
|
if (!prop.optional) {
|
|
15
18
|
required.push(key);
|
package/dist/server.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ServerOptions, ToolDefinition, ToolHandler, HandleResult, Prompt, PromptHandler, Resource, ResourceHandler, ResourceTemplate, Transport, SDKTransport, JSONRPCNotification } from "./types.js";
|
|
2
2
|
import type { TypedSchema } from "./schema.js";
|
|
3
3
|
export interface Server {
|
|
4
|
-
tool<
|
|
5
|
-
registerTool<
|
|
4
|
+
tool<TIn, TOut = never>(name: string, description: string, inputSchema: TypedSchema<TIn>, handler: ToolHandler<TIn, TOut>, outputSchema?: TypedSchema<TOut>): Server;
|
|
5
|
+
registerTool<TIn, TOut = never>(definition: Omit<ToolDefinition<TIn, TOut>, "handler">, handler: ToolHandler<TIn, TOut>): Server;
|
|
6
6
|
prompt(definition: Prompt, handler: PromptHandler): Server;
|
|
7
7
|
resource(definition: Resource, handler: ResourceHandler): Server;
|
|
8
8
|
resourceTemplate(definition: ResourceTemplate, handler: ResourceHandler): Server;
|
package/dist/server.js
CHANGED
|
@@ -131,16 +131,9 @@ export function createServer(options) {
|
|
|
131
131
|
}
|
|
132
132
|
try {
|
|
133
133
|
const handlerResult = await tool.handler(toolArgs);
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const result = isCallToolResult(handlerResult)
|
|
138
|
-
? handlerResult
|
|
139
|
-
: { content: toContentBlocks(handlerResult) };
|
|
140
|
-
if (tool.outputSchema !== undefined
|
|
141
|
-
&& (result.structuredContent === undefined
|
|
142
|
-
|| !jsonSchemaValidator.validate(tool.outputSchema, result.structuredContent))) {
|
|
143
|
-
throw new Error("Invalid structured tool result");
|
|
134
|
+
const result = normalizeToolResult(handlerResult, tool.outputSchema);
|
|
135
|
+
if (tool.outputSchema !== undefined && !jsonSchemaValidator.validate(tool.outputSchema, result.structuredContent)) {
|
|
136
|
+
throw new ToolError(JSON_RPC_ERROR_CODES.INTERNAL_ERROR, "Invalid structured tool result");
|
|
144
137
|
}
|
|
145
138
|
return { result };
|
|
146
139
|
}
|
|
@@ -326,16 +319,25 @@ export function createServer(options) {
|
|
|
326
319
|
}));
|
|
327
320
|
};
|
|
328
321
|
const server = {
|
|
329
|
-
tool(name, description, inputSchema, handler) {
|
|
322
|
+
tool(name, description, inputSchema, handler, outputSchema) {
|
|
323
|
+
assertNonEmptyName(name, "Tool name required");
|
|
324
|
+
if (outputSchema !== undefined) {
|
|
325
|
+
assertSupportedOutputSchema(outputSchema);
|
|
326
|
+
}
|
|
330
327
|
tools.set(name, {
|
|
331
328
|
name,
|
|
332
329
|
description,
|
|
333
330
|
inputSchema: inputSchema,
|
|
331
|
+
...(outputSchema === undefined ? {} : { outputSchema: outputSchema }),
|
|
334
332
|
handler: handler,
|
|
335
333
|
});
|
|
336
334
|
return server;
|
|
337
335
|
},
|
|
338
336
|
registerTool(definition, handler) {
|
|
337
|
+
assertNonEmptyName(definition.name, "Tool name required");
|
|
338
|
+
if (definition.outputSchema !== undefined) {
|
|
339
|
+
assertSupportedOutputSchema(definition.outputSchema);
|
|
340
|
+
}
|
|
339
341
|
tools.set(definition.name, {
|
|
340
342
|
...definition,
|
|
341
343
|
handler: handler,
|
|
@@ -343,6 +345,7 @@ export function createServer(options) {
|
|
|
343
345
|
return server;
|
|
344
346
|
},
|
|
345
347
|
prompt(definition, handler) {
|
|
348
|
+
assertNonEmptyName(definition.name, "Prompt name required");
|
|
346
349
|
prompts.set(definition.name, { ...definition, handler });
|
|
347
350
|
return server;
|
|
348
351
|
},
|
|
@@ -354,7 +357,7 @@ export function createServer(options) {
|
|
|
354
357
|
return server;
|
|
355
358
|
},
|
|
356
359
|
resourceTemplate(definition, handler) {
|
|
357
|
-
|
|
360
|
+
assertReadableUriTemplate(definition.uriTemplate);
|
|
358
361
|
new UriTemplate(definition.uriTemplate);
|
|
359
362
|
resourceTemplates.set(definition.uriTemplate, { ...definition, handler });
|
|
360
363
|
return server;
|
|
@@ -536,6 +539,20 @@ function isValidUri(uri) {
|
|
|
536
539
|
return false;
|
|
537
540
|
}
|
|
538
541
|
}
|
|
542
|
+
function assertNonEmptyName(name, message) {
|
|
543
|
+
if (name.length === 0) {
|
|
544
|
+
throw new Error(message);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
function assertReadableUriTemplate(uriTemplate) {
|
|
548
|
+
const parsed = uriTemplateParser.parse(uriTemplate);
|
|
549
|
+
const expanded = parsed.expand(new Proxy({}, {
|
|
550
|
+
get: (_target, property) => typeof property === "string" ? "value" : undefined,
|
|
551
|
+
}));
|
|
552
|
+
if (typeof expanded !== "string" || !isValidUri(expanded)) {
|
|
553
|
+
throw new Error(`Invalid resource URI template: ${uriTemplate}`);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
539
556
|
function toStringArguments(value) {
|
|
540
557
|
if (value === undefined) {
|
|
541
558
|
return {};
|
|
@@ -571,13 +588,136 @@ function matchesUriTemplate(template, uri) {
|
|
|
571
588
|
}
|
|
572
589
|
}
|
|
573
590
|
function isCallToolResult(value) {
|
|
574
|
-
|
|
591
|
+
if (!hasContentArray(value) || !value.content.every(isContentItem)) {
|
|
592
|
+
return false;
|
|
593
|
+
}
|
|
594
|
+
if (hasOwnProperty(value, "structuredContent")
|
|
595
|
+
&& value.structuredContent !== undefined
|
|
596
|
+
&& !isJsonObject(value.structuredContent)) {
|
|
597
|
+
return false;
|
|
598
|
+
}
|
|
599
|
+
return !(hasOwnProperty(value, "isError")
|
|
600
|
+
&& value.isError !== undefined
|
|
601
|
+
&& typeof value.isError !== "boolean");
|
|
602
|
+
}
|
|
603
|
+
function normalizeToolResult(handlerResult, outputSchema) {
|
|
604
|
+
if (hasContentArray(handlerResult) && !isCallToolResult(handlerResult)) {
|
|
605
|
+
throw new Error("Invalid tool result");
|
|
606
|
+
}
|
|
607
|
+
if (outputSchema === undefined) {
|
|
608
|
+
const result = isCallToolResult(handlerResult)
|
|
609
|
+
? handlerResult
|
|
610
|
+
: { content: toContentBlocks(handlerResult) };
|
|
611
|
+
if (!isCallToolResult(result)) {
|
|
612
|
+
throw new Error("Invalid tool result");
|
|
613
|
+
}
|
|
614
|
+
return result;
|
|
615
|
+
}
|
|
616
|
+
const structuredContent = isCallToolResult(handlerResult)
|
|
617
|
+
? handlerResult.structuredContent
|
|
618
|
+
: handlerResult;
|
|
619
|
+
if (!isJsonObject(structuredContent)) {
|
|
620
|
+
throw new ToolError(JSON_RPC_ERROR_CODES.INTERNAL_ERROR, "Structured tool result must be an object");
|
|
621
|
+
}
|
|
622
|
+
return {
|
|
623
|
+
content: [{ type: "text", text: JSON.stringify(structuredContent) }],
|
|
624
|
+
...(isCallToolResult(handlerResult) && handlerResult.isError !== undefined
|
|
625
|
+
? { isError: handlerResult.isError }
|
|
626
|
+
: {}),
|
|
627
|
+
structuredContent,
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
function assertSupportedOutputSchema(schema) {
|
|
631
|
+
assertObjectRootSchema(schema, "outputSchema");
|
|
632
|
+
assertSupportedJsonSchema(schema, "outputSchema");
|
|
633
|
+
}
|
|
634
|
+
function assertObjectRootSchema(schema, path) {
|
|
635
|
+
if (schema.type !== "object") {
|
|
636
|
+
throw new Error(`${path} root type must be "object"`);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
function assertSupportedJsonSchema(schema, path) {
|
|
640
|
+
for (const keyword of ["anyOf", "allOf", "not", "if", "then", "else", "contains", "prefixItems"]) {
|
|
641
|
+
if (schema[keyword] !== undefined) {
|
|
642
|
+
throw new Error(`${path} uses unsupported keyword "${keyword}"`);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
const type = schema.type;
|
|
646
|
+
if (Array.isArray(type)) {
|
|
647
|
+
const supported = new Set(["string", "number", "integer", "boolean", "object", "array", "null"]);
|
|
648
|
+
for (const item of type) {
|
|
649
|
+
if (!supported.has(item)) {
|
|
650
|
+
throw new Error(`${path} uses unsupported type "${item}"`);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
else if (type !== undefined &&
|
|
655
|
+
type !== "string" &&
|
|
656
|
+
type !== "number" &&
|
|
657
|
+
type !== "integer" &&
|
|
658
|
+
type !== "boolean" &&
|
|
659
|
+
type !== "object" &&
|
|
660
|
+
type !== "array") {
|
|
661
|
+
throw new Error(`${path} uses unsupported type "${type}"`);
|
|
662
|
+
}
|
|
663
|
+
if (isJsonObject(schema.properties)) {
|
|
664
|
+
for (const [key, child] of Object.entries(schema.properties)) {
|
|
665
|
+
if (isJsonObject(child)) {
|
|
666
|
+
assertSupportedJsonSchema(child, `${path}.properties.${key}`);
|
|
667
|
+
}
|
|
668
|
+
else {
|
|
669
|
+
throw new Error(`${path}.properties.${key} must be an object schema`);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
else if (schema.properties !== undefined) {
|
|
674
|
+
throw new Error(`${path}.properties must be an object`);
|
|
675
|
+
}
|
|
676
|
+
const additionalProperties = schema.additionalProperties;
|
|
677
|
+
if (typeof additionalProperties === "object" && additionalProperties !== null) {
|
|
678
|
+
if (Array.isArray(additionalProperties)) {
|
|
679
|
+
throw new Error(`${path}.additionalProperties must be an object schema or boolean`);
|
|
680
|
+
}
|
|
681
|
+
assertSupportedJsonSchema(additionalProperties, `${path}.additionalProperties`);
|
|
682
|
+
}
|
|
683
|
+
else if (additionalProperties !== undefined
|
|
684
|
+
&& typeof additionalProperties !== "boolean") {
|
|
685
|
+
throw new Error(`${path}.additionalProperties must be an object schema or boolean`);
|
|
686
|
+
}
|
|
687
|
+
const items = schema.items;
|
|
688
|
+
if (Array.isArray(items)) {
|
|
689
|
+
throw new Error(`${path}.items uses unsupported tuple array schemas`);
|
|
690
|
+
}
|
|
691
|
+
if (typeof items === "object" && items !== null && !Array.isArray(items)) {
|
|
692
|
+
assertSupportedJsonSchema(items, `${path}.items`);
|
|
693
|
+
}
|
|
694
|
+
else if (items !== undefined && typeof items !== "boolean") {
|
|
695
|
+
throw new Error(`${path}.items must be an object schema or boolean`);
|
|
696
|
+
}
|
|
697
|
+
const oneOf = schema.oneOf;
|
|
698
|
+
if (Array.isArray(oneOf)) {
|
|
699
|
+
for (const [index, child] of oneOf.entries()) {
|
|
700
|
+
if (typeof child === "object" && child !== null && !Array.isArray(child)) {
|
|
701
|
+
assertSupportedJsonSchema(child, `${path}.oneOf[${index}]`);
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
throw new Error(`${path}.oneOf[${index}] must be an object schema`);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
else if (oneOf !== undefined) {
|
|
709
|
+
throw new Error(`${path}.oneOf must be an array`);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
function isJsonObject(value) {
|
|
713
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
575
714
|
}
|
|
576
715
|
function isGetPromptResult(value) {
|
|
577
716
|
if (typeof value !== "object" || value === null || !hasOwnProperty(value, "messages")) {
|
|
578
717
|
return false;
|
|
579
718
|
}
|
|
580
|
-
return
|
|
719
|
+
return (!hasOwnProperty(value, "description") || value.description === undefined || typeof value.description === "string")
|
|
720
|
+
&& Array.isArray(value.messages)
|
|
581
721
|
&& value.messages.every((message) => typeof message === "object"
|
|
582
722
|
&& message !== null
|
|
583
723
|
&& hasOwnProperty(message, "role")
|
|
@@ -590,13 +730,7 @@ function isReadResourceResult(value) {
|
|
|
590
730
|
return false;
|
|
591
731
|
}
|
|
592
732
|
return Array.isArray(value.contents)
|
|
593
|
-
&& value.contents.every(
|
|
594
|
-
&& content !== null
|
|
595
|
-
&& hasOwnProperty(content, "uri")
|
|
596
|
-
&& typeof content.uri === "string"
|
|
597
|
-
&& isValidUri(content.uri)
|
|
598
|
-
&& ((hasOwnProperty(content, "text") && typeof content.text === "string")
|
|
599
|
-
|| (hasOwnProperty(content, "blob") && typeof content.blob === "string" && isBase64(content.blob))));
|
|
733
|
+
&& value.contents.every(isResourceContents);
|
|
600
734
|
}
|
|
601
735
|
function hasContentArray(value) {
|
|
602
736
|
return typeof value === "object" && value !== null && hasOwnProperty(value, "content")
|
|
@@ -607,6 +741,9 @@ function isContentItem(value) {
|
|
|
607
741
|
return false;
|
|
608
742
|
}
|
|
609
743
|
const block = value;
|
|
744
|
+
if (!hasValidContentAnnotations(block)) {
|
|
745
|
+
return false;
|
|
746
|
+
}
|
|
610
747
|
if (block.type === "text") {
|
|
611
748
|
return hasOwnProperty(block, "text") && typeof block.text === "string";
|
|
612
749
|
}
|
|
@@ -620,8 +757,13 @@ function isContentItem(value) {
|
|
|
620
757
|
if (block.type === "resource_link") {
|
|
621
758
|
return hasOwnProperty(block, "uri")
|
|
622
759
|
&& typeof block.uri === "string"
|
|
760
|
+
&& isValidUri(block.uri)
|
|
623
761
|
&& hasOwnProperty(block, "name")
|
|
624
|
-
&& typeof block.name === "string"
|
|
762
|
+
&& typeof block.name === "string"
|
|
763
|
+
&& (!hasOwnProperty(block, "title") || block.title === undefined || typeof block.title === "string")
|
|
764
|
+
&& (!hasOwnProperty(block, "description") || block.description === undefined || typeof block.description === "string")
|
|
765
|
+
&& (!hasOwnProperty(block, "mimeType") || block.mimeType === undefined || typeof block.mimeType === "string")
|
|
766
|
+
&& (!hasOwnProperty(block, "size") || block.size === undefined || typeof block.size === "number");
|
|
625
767
|
}
|
|
626
768
|
if (block.type !== "resource"
|
|
627
769
|
|| !hasOwnProperty(block, "resource")
|
|
@@ -629,12 +771,33 @@ function isContentItem(value) {
|
|
|
629
771
|
|| block.resource === null) {
|
|
630
772
|
return false;
|
|
631
773
|
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
774
|
+
return isResourceContents(block.resource);
|
|
775
|
+
}
|
|
776
|
+
function isResourceContents(value) {
|
|
777
|
+
if (typeof value !== "object"
|
|
778
|
+
|| value === null
|
|
779
|
+
|| !hasOwnProperty(value, "uri")
|
|
780
|
+
|| typeof value.uri !== "string"
|
|
781
|
+
|| !isValidUri(value.uri)) {
|
|
782
|
+
return false;
|
|
783
|
+
}
|
|
784
|
+
if (hasOwnProperty(value, "mimeType") && value.mimeType !== undefined && typeof value.mimeType !== "string") {
|
|
785
|
+
return false;
|
|
786
|
+
}
|
|
787
|
+
return (hasOwnProperty(value, "text") && typeof value.text === "string")
|
|
788
|
+
|| (hasOwnProperty(value, "blob") && typeof value.blob === "string" && isBase64(value.blob));
|
|
789
|
+
}
|
|
790
|
+
function hasValidContentAnnotations(value) {
|
|
791
|
+
if (!hasOwnProperty(value, "annotations") || value.annotations === undefined) {
|
|
792
|
+
return true;
|
|
793
|
+
}
|
|
794
|
+
if (!isJsonObject(value.annotations)) {
|
|
795
|
+
return false;
|
|
796
|
+
}
|
|
797
|
+
const { audience, priority, lastModified } = value.annotations;
|
|
798
|
+
return (audience === undefined || (Array.isArray(audience) && audience.every((item) => item === "user" || item === "assistant")))
|
|
799
|
+
&& (priority === undefined || typeof priority === "number")
|
|
800
|
+
&& (lastModified === undefined || typeof lastModified === "string");
|
|
638
801
|
}
|
|
639
802
|
function isBase64(value) {
|
|
640
803
|
if (value.length === 0) {
|
package/dist/types.d.ts
CHANGED
|
@@ -215,8 +215,8 @@ export interface ServerOptions {
|
|
|
215
215
|
supportResourceSubscriptions?: boolean;
|
|
216
216
|
}
|
|
217
217
|
import type { ToolReturn } from "./content/index.js";
|
|
218
|
-
export type ToolHandler<T = Record<string, unknown
|
|
219
|
-
export interface ToolDefinition<T = Record<string, unknown
|
|
218
|
+
export type ToolHandler<T = Record<string, unknown>, TOut = ToolReturn> = (args: T) => Promise<TOut | CallToolResult> | TOut | CallToolResult;
|
|
219
|
+
export interface ToolDefinition<T = Record<string, unknown>, TOut = ToolReturn> {
|
|
220
220
|
name: string;
|
|
221
221
|
title?: string;
|
|
222
222
|
description?: string;
|
|
@@ -226,7 +226,7 @@ export interface ToolDefinition<T = Record<string, unknown>> {
|
|
|
226
226
|
execution?: ToolExecution;
|
|
227
227
|
icons?: Icon[];
|
|
228
228
|
_meta?: Record<string, unknown>;
|
|
229
|
-
handler: ToolHandler<T>;
|
|
229
|
+
handler: ToolHandler<T, TOut>;
|
|
230
230
|
}
|
|
231
231
|
export interface Transport {
|
|
232
232
|
readable: NodeJS.ReadableStream;
|