tiny-stdio-mcp-server 0.1.0 → 0.1.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 +180 -0
- package/dist/content/convert.d.ts +7 -1
- package/dist/content/convert.js +21 -4
- package/dist/index.d.ts +2 -4
- package/dist/index.js +1 -3
- package/dist/server.d.ts +3 -1
- package/dist/server.js +42 -21
- package/dist/types.d.ts +13 -5
- package/dist/types.js +9 -1
- package/package.json +13 -4
- package/dist/testing.d.ts +0 -7
- package/dist/testing.js +0 -20
package/README.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# tiny-stdio-mcp-server
|
|
2
|
+
|
|
3
|
+
Minimal [Model Context Protocol](https://modelcontextprotocol.io) server for Node.js. Zero runtime dependencies, type-safe tool definitions, rich content helpers for images/audio/files.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install tiny-stdio-mcp-server
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { createServer, defineSchema } from "tiny-stdio-mcp-server";
|
|
15
|
+
|
|
16
|
+
const schema = defineSchema({
|
|
17
|
+
text: { type: "string", description: "Text to reverse" },
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
createServer({ name: "my-server", version: "1.0.0" })
|
|
21
|
+
.tool("reverse", "Reverse a string", schema, ({ text }) => {
|
|
22
|
+
return text.split("").reverse().join("");
|
|
23
|
+
})
|
|
24
|
+
.listen();
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Run it:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
node my-server.js
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Any MCP client (Claude Code, Codex, etc.) can connect to it over stdio.
|
|
34
|
+
|
|
35
|
+
## API
|
|
36
|
+
|
|
37
|
+
### `createServer(options)`
|
|
38
|
+
|
|
39
|
+
Creates a new MCP server.
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
const server = createServer({ name: "my-server", version: "1.0.0" });
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### `.tool(name, description, schema, handler)`
|
|
46
|
+
|
|
47
|
+
Register a tool. The handler receives typed args matching the schema and returns a string, content helper, or array of either.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
const schema = defineSchema({
|
|
51
|
+
query: { type: "string", description: "Search query" },
|
|
52
|
+
limit: { type: "number", description: "Max results", optional: true },
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
server.tool("search", "Search for things", schema, async ({ query, limit }) => {
|
|
56
|
+
// `query` is string, `limit` is number | undefined
|
|
57
|
+
return `Found results for: ${query}`;
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### `.listen()`
|
|
62
|
+
|
|
63
|
+
Start listening on stdin/stdout (standard MCP stdio transport).
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
await server.listen();
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### `.connect(transport)`
|
|
70
|
+
|
|
71
|
+
Connect to a custom readable/writable stream pair.
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
await server.connect({ readable: process.stdin, writable: process.stdout });
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### `.connectSDK(transport)`
|
|
78
|
+
|
|
79
|
+
Connect using an SDK-compatible in-memory transport (for testing).
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
await server.connectSDK(sdkTransport);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### `.removeTool(name)` / `.notifyToolsChanged()`
|
|
86
|
+
|
|
87
|
+
Dynamically add/remove tools at runtime and notify connected clients.
|
|
88
|
+
|
|
89
|
+
## `defineSchema(definition)`
|
|
90
|
+
|
|
91
|
+
Type-safe schema builder. Returns a JSON Schema object with inferred TypeScript types.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
const schema = defineSchema({
|
|
95
|
+
name: { type: "string", description: "User name" },
|
|
96
|
+
age: { type: "number", description: "User age", optional: true },
|
|
97
|
+
});
|
|
98
|
+
// Handler receives: { name: string; age?: number }
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Supported types: `string`, `number`, `boolean`, `object`, `array`.
|
|
102
|
+
|
|
103
|
+
## Content helpers
|
|
104
|
+
|
|
105
|
+
Tool handlers can return rich content beyond plain text.
|
|
106
|
+
|
|
107
|
+
### Images
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { Image } from "tiny-stdio-mcp-server";
|
|
111
|
+
|
|
112
|
+
server.tool("screenshot", "Take a screenshot", schema, async () => {
|
|
113
|
+
return Image.fromBase64(base64Data, "image/png");
|
|
114
|
+
// or: await Image.fromUrl("https://example.com/image.png")
|
|
115
|
+
// or: Image.fromBytes(uint8Array)
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Supported formats: PNG, JPEG, GIF, WebP.
|
|
120
|
+
|
|
121
|
+
### Audio
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
import { Audio } from "tiny-stdio-mcp-server";
|
|
125
|
+
|
|
126
|
+
server.tool("speak", "Text to speech", schema, async () => {
|
|
127
|
+
return Audio.fromBase64(base64Data, "audio/mpeg");
|
|
128
|
+
// or: await Audio.fromUrl("https://example.com/audio.mp3")
|
|
129
|
+
// or: Audio.fromBytes(uint8Array, "mp3")
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Supported formats: MP3, WAV, OGG, M4A.
|
|
134
|
+
|
|
135
|
+
### Files
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import { File } from "tiny-stdio-mcp-server";
|
|
139
|
+
|
|
140
|
+
server.tool("export", "Export data", schema, async () => {
|
|
141
|
+
return File.fromText(csvContent, "text/csv");
|
|
142
|
+
// or: File.fromBytes(uint8Array, "application/pdf")
|
|
143
|
+
// or: await File.fromUrl("https://example.com/report.pdf")
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Mixed content
|
|
148
|
+
|
|
149
|
+
Return arrays to send multiple content blocks:
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
server.tool("analyze", "Analyze image", schema, async () => {
|
|
153
|
+
const image = await Image.fromUrl(url);
|
|
154
|
+
return [image, "Analysis complete: found 3 objects"];
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Testing
|
|
159
|
+
|
|
160
|
+
Use `createTestPair` with the official MCP SDK for in-memory testing:
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
import { createTestPair } from "tiny-stdio-mcp-server/testing";
|
|
164
|
+
|
|
165
|
+
const server = createServer({ name: "test", version: "1.0.0" })
|
|
166
|
+
.tool("ping", "Ping", defineSchema({}), () => "pong");
|
|
167
|
+
|
|
168
|
+
const { client, cleanup } = await createTestPair(server);
|
|
169
|
+
|
|
170
|
+
const result = await client.callTool({ name: "ping", arguments: {} });
|
|
171
|
+
// result.content === [{ type: "text", text: "pong" }]
|
|
172
|
+
|
|
173
|
+
await cleanup();
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Requires `@modelcontextprotocol/sdk` as a dev dependency.
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
|
|
180
|
+
MIT
|
|
@@ -6,5 +6,11 @@ export interface TextContent {
|
|
|
6
6
|
text: string;
|
|
7
7
|
}
|
|
8
8
|
export type ContentBlock = TextContent | ImageContent | AudioContent | EmbeddedResource;
|
|
9
|
-
|
|
9
|
+
type JsonPrimitive = string | number | boolean | null;
|
|
10
|
+
export type JsonValue = JsonPrimitive | JsonObject | JsonValue[];
|
|
11
|
+
export type JsonObject = {
|
|
12
|
+
[key: string]: JsonValue;
|
|
13
|
+
};
|
|
14
|
+
export type ToolReturn = undefined | JsonPrimitive | JsonObject | Image | Audio | File | ContentBlock | Array<undefined | JsonPrimitive | JsonObject | Image | Audio | File | ContentBlock>;
|
|
10
15
|
export declare function toContentBlocks(result: ToolReturn): ContentBlock[];
|
|
16
|
+
export {};
|
package/dist/content/convert.js
CHANGED
|
@@ -2,8 +2,11 @@ import { Image } from "./image.js";
|
|
|
2
2
|
import { Audio } from "./audio.js";
|
|
3
3
|
import { File } from "./file.js";
|
|
4
4
|
function convertSingleValue(value) {
|
|
5
|
-
if (typeof value === "string") {
|
|
6
|
-
return { type: "text", text: value };
|
|
5
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
6
|
+
return { type: "text", text: String(value) };
|
|
7
|
+
}
|
|
8
|
+
if (value === null) {
|
|
9
|
+
return { type: "text", text: "null" };
|
|
7
10
|
}
|
|
8
11
|
if (value instanceof Image) {
|
|
9
12
|
return value.toContentBlock();
|
|
@@ -14,12 +17,26 @@ function convertSingleValue(value) {
|
|
|
14
17
|
if (value instanceof File) {
|
|
15
18
|
return value.toContentBlock();
|
|
16
19
|
}
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
if (isContentBlock(value)) {
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
return { type: "text", text: JSON.stringify(value) };
|
|
19
24
|
}
|
|
20
25
|
export function toContentBlocks(result) {
|
|
26
|
+
if (result === undefined) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
21
29
|
if (Array.isArray(result)) {
|
|
22
30
|
return result.flatMap((item) => toContentBlocks(item));
|
|
23
31
|
}
|
|
24
32
|
return [convertSingleValue(result)];
|
|
25
33
|
}
|
|
34
|
+
function isContentBlock(value) {
|
|
35
|
+
if (!("type" in value) || typeof value.type !== "string") {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
return (value.type === "text" ||
|
|
39
|
+
value.type === "image" ||
|
|
40
|
+
value.type === "audio" ||
|
|
41
|
+
value.type === "resource");
|
|
42
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,10 +2,8 @@ export { createServer } from "./server.js";
|
|
|
2
2
|
export type { Server } from "./server.js";
|
|
3
3
|
export { defineSchema } from "./schema.js";
|
|
4
4
|
export type { TypedSchema } from "./schema.js";
|
|
5
|
-
export { createTestPair } from "./testing.js";
|
|
6
|
-
export type { TestPair } from "./testing.js";
|
|
7
5
|
export { Image, Audio, File, toContentBlocks, fileTypeFromBuffer, } from "./content/index.js";
|
|
8
6
|
export type { ImageContent, AudioContent, EmbeddedResource, TextResourceContents, BlobResourceContents, ContentBlock, TextContent, FileTypeResult, } from "./content/index.js";
|
|
9
7
|
export type { ToolReturn } from "./content/index.js";
|
|
10
|
-
export type { ServerOptions, ToolHandler, ToolDefinition, Tool, CallToolResult, ContentItem, JSONSchema, JSONSchemaProperty, Transport, SDKTransport, JSONRPCRequest, JSONRPCResponse, JSONRPCError, JSONRPCMessage, JSONRPCNotification, InitializeResult, } from "./types.js";
|
|
11
|
-
export { JSON_RPC_ERROR_CODES } from "./types.js";
|
|
8
|
+
export type { ServerOptions, ToolHandler, ToolDefinition, Tool, CallToolResult, HandleResult, ContentItem, JSONSchema, JSONSchemaProperty, Transport, SDKTransport, JSONRPCRequest, JSONRPCResponse, JSONRPCError, JSONRPCMessage, JSONRPCNotification, InitializeResult, } from "./types.js";
|
|
9
|
+
export { JSON_RPC_ERROR_CODES, ToolError } from "./types.js";
|
package/dist/index.js
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
export { createServer } from "./server.js";
|
|
3
3
|
// Schema
|
|
4
4
|
export { defineSchema } from "./schema.js";
|
|
5
|
-
// Testing utilities
|
|
6
|
-
export { createTestPair } from "./testing.js";
|
|
7
5
|
// Content helpers
|
|
8
6
|
export { Image, Audio, File, toContentBlocks, fileTypeFromBuffer, } from "./content/index.js";
|
|
9
|
-
export { JSON_RPC_ERROR_CODES } from "./types.js";
|
|
7
|
+
export { JSON_RPC_ERROR_CODES, ToolError } from "./types.js";
|
package/dist/server.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import type { ServerOptions, ToolHandler, Transport, SDKTransport } from "./types.js";
|
|
1
|
+
import type { ServerOptions, ToolHandler, HandleResult, Transport, SDKTransport, JSONRPCNotification } from "./types.js";
|
|
2
2
|
import type { TypedSchema } from "./schema.js";
|
|
3
3
|
export interface Server {
|
|
4
4
|
tool<T>(name: string, description: string, inputSchema: TypedSchema<T>, handler: ToolHandler<T>): Server;
|
|
5
|
+
onNotification(listener: (notification: JSONRPCNotification) => void): () => void;
|
|
5
6
|
removeTool(name: string): boolean;
|
|
6
7
|
notifyToolsChanged(): Promise<void>;
|
|
8
|
+
handleMessage(method: string, params?: Record<string, unknown>): Promise<HandleResult>;
|
|
7
9
|
listen(): Promise<void>;
|
|
8
10
|
connect(transport: Transport): Promise<void>;
|
|
9
11
|
connectSDK(transport: SDKTransport): Promise<void>;
|
package/dist/server.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import * as readline from "readline";
|
|
2
|
-
import { JSON_RPC_ERROR_CODES } from "./types.js";
|
|
2
|
+
import { JSON_RPC_ERROR_CODES, ToolError } from "./types.js";
|
|
3
3
|
import { parseMessage, formatSuccessResponse, formatErrorResponse, } from "./jsonrpc.js";
|
|
4
4
|
import { toContentBlocks } from "./content/convert.js";
|
|
5
5
|
const PROTOCOL_VERSION = "2025-11-25";
|
|
6
6
|
export function createServer(options) {
|
|
7
7
|
const tools = new Map();
|
|
8
|
+
const notificationListeners = new Set();
|
|
8
9
|
let initialized = false;
|
|
9
|
-
|
|
10
|
-
let activeSDKTransport = null;
|
|
11
|
-
const handleRequest = async (method, params) => {
|
|
10
|
+
const handleMessage = async (method, params) => {
|
|
12
11
|
// Allow ping and initialize before initialization
|
|
13
12
|
if (method === "ping") {
|
|
14
13
|
return { result: {} };
|
|
@@ -77,10 +76,20 @@ export function createServer(options) {
|
|
|
77
76
|
}
|
|
78
77
|
try {
|
|
79
78
|
const handlerResult = await tool.handler(toolArgs);
|
|
80
|
-
const result =
|
|
79
|
+
const result = isCallToolResult(handlerResult)
|
|
80
|
+
? handlerResult
|
|
81
|
+
: { content: toContentBlocks(handlerResult) };
|
|
81
82
|
return { result };
|
|
82
83
|
}
|
|
83
84
|
catch (err) {
|
|
85
|
+
if (err instanceof ToolError) {
|
|
86
|
+
return {
|
|
87
|
+
error: {
|
|
88
|
+
code: err.code,
|
|
89
|
+
message: err.message,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
84
93
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
85
94
|
const result = {
|
|
86
95
|
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
@@ -103,7 +112,7 @@ export function createServer(options) {
|
|
|
103
112
|
return;
|
|
104
113
|
}
|
|
105
114
|
const { request, isNotification } = parsed;
|
|
106
|
-
const { result, error } = await
|
|
115
|
+
const { result, error } = await server.handleMessage(request.method, request.params);
|
|
107
116
|
if (isNotification) {
|
|
108
117
|
return;
|
|
109
118
|
}
|
|
@@ -115,16 +124,13 @@ export function createServer(options) {
|
|
|
115
124
|
write(formatSuccessResponse(requestWithId.id, result) + "\n");
|
|
116
125
|
}
|
|
117
126
|
};
|
|
118
|
-
const
|
|
127
|
+
const broadcastNotification = (method) => {
|
|
119
128
|
const notification = {
|
|
120
129
|
jsonrpc: "2.0",
|
|
121
130
|
method,
|
|
122
131
|
};
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
else if (activeTransport) {
|
|
127
|
-
activeTransport.writable.write(JSON.stringify(notification) + "\n");
|
|
132
|
+
for (const listener of notificationListeners) {
|
|
133
|
+
listener(notification);
|
|
128
134
|
}
|
|
129
135
|
};
|
|
130
136
|
const server = {
|
|
@@ -137,14 +143,21 @@ export function createServer(options) {
|
|
|
137
143
|
});
|
|
138
144
|
return server;
|
|
139
145
|
},
|
|
146
|
+
onNotification(listener) {
|
|
147
|
+
notificationListeners.add(listener);
|
|
148
|
+
return () => {
|
|
149
|
+
notificationListeners.delete(listener);
|
|
150
|
+
};
|
|
151
|
+
},
|
|
140
152
|
removeTool(name) {
|
|
141
153
|
return tools.delete(name);
|
|
142
154
|
},
|
|
143
155
|
async notifyToolsChanged() {
|
|
144
156
|
if (initialized) {
|
|
145
|
-
|
|
157
|
+
broadcastNotification("notifications/tools/list_changed");
|
|
146
158
|
}
|
|
147
159
|
},
|
|
160
|
+
handleMessage,
|
|
148
161
|
async listen() {
|
|
149
162
|
return server.connect({
|
|
150
163
|
readable: process.stdin,
|
|
@@ -152,9 +165,10 @@ export function createServer(options) {
|
|
|
152
165
|
});
|
|
153
166
|
},
|
|
154
167
|
async connect(transport) {
|
|
155
|
-
activeTransport = transport;
|
|
156
|
-
activeSDKTransport = null;
|
|
157
168
|
return new Promise((resolve) => {
|
|
169
|
+
const unsubscribe = server.onNotification((notification) => {
|
|
170
|
+
transport.writable.write(`${JSON.stringify(notification)}\n`);
|
|
171
|
+
});
|
|
158
172
|
const rl = readline.createInterface({
|
|
159
173
|
input: transport.readable,
|
|
160
174
|
crlfDelay: Infinity,
|
|
@@ -163,15 +177,16 @@ export function createServer(options) {
|
|
|
163
177
|
processLine(line, (data) => transport.writable.write(data));
|
|
164
178
|
});
|
|
165
179
|
rl.on("close", () => {
|
|
166
|
-
|
|
180
|
+
unsubscribe();
|
|
167
181
|
resolve();
|
|
168
182
|
});
|
|
169
183
|
});
|
|
170
184
|
},
|
|
171
185
|
async connectSDK(transport) {
|
|
172
|
-
activeSDKTransport = transport;
|
|
173
|
-
activeTransport = null;
|
|
174
186
|
return new Promise((resolve) => {
|
|
187
|
+
const unsubscribe = server.onNotification((notification) => {
|
|
188
|
+
void transport.send(notification);
|
|
189
|
+
});
|
|
175
190
|
transport.onmessage = async (message) => {
|
|
176
191
|
// Ignore responses (we only handle requests/notifications)
|
|
177
192
|
if (!("method" in message)) {
|
|
@@ -179,11 +194,11 @@ export function createServer(options) {
|
|
|
179
194
|
}
|
|
180
195
|
// Handle notifications (no id) - don't respond
|
|
181
196
|
if (!("id" in message) || message.id === undefined) {
|
|
182
|
-
await
|
|
197
|
+
await server.handleMessage(message.method, message.params);
|
|
183
198
|
return;
|
|
184
199
|
}
|
|
185
200
|
const request = message;
|
|
186
|
-
const { result, error } = await
|
|
201
|
+
const { result, error } = await server.handleMessage(request.method, request.params);
|
|
187
202
|
if (error) {
|
|
188
203
|
const response = {
|
|
189
204
|
jsonrpc: "2.0",
|
|
@@ -202,7 +217,7 @@ export function createServer(options) {
|
|
|
202
217
|
}
|
|
203
218
|
};
|
|
204
219
|
transport.onclose = () => {
|
|
205
|
-
|
|
220
|
+
unsubscribe();
|
|
206
221
|
resolve();
|
|
207
222
|
};
|
|
208
223
|
transport.start();
|
|
@@ -211,3 +226,9 @@ export function createServer(options) {
|
|
|
211
226
|
};
|
|
212
227
|
return server;
|
|
213
228
|
}
|
|
229
|
+
function isCallToolResult(value) {
|
|
230
|
+
if (typeof value !== "object" || value === null || !("content" in value)) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
return Array.isArray(value.content);
|
|
234
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -22,6 +22,10 @@ export declare const JSON_RPC_ERROR_CODES: {
|
|
|
22
22
|
readonly INVALID_PARAMS: -32602;
|
|
23
23
|
readonly INTERNAL_ERROR: -32603;
|
|
24
24
|
};
|
|
25
|
+
export declare class ToolError extends Error {
|
|
26
|
+
readonly code: number;
|
|
27
|
+
constructor(code: number, message: string);
|
|
28
|
+
}
|
|
25
29
|
export interface ToolsCapability {
|
|
26
30
|
listChanged?: boolean;
|
|
27
31
|
}
|
|
@@ -44,6 +48,13 @@ export interface CallToolResult {
|
|
|
44
48
|
content: ContentItem[];
|
|
45
49
|
isError?: boolean;
|
|
46
50
|
}
|
|
51
|
+
export interface HandleResult {
|
|
52
|
+
result?: unknown;
|
|
53
|
+
error?: {
|
|
54
|
+
code: number;
|
|
55
|
+
message: string;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
47
58
|
export type ContentItem = {
|
|
48
59
|
type: "text";
|
|
49
60
|
text: string;
|
|
@@ -80,11 +91,8 @@ export interface ServerOptions {
|
|
|
80
91
|
name: string;
|
|
81
92
|
version: string;
|
|
82
93
|
}
|
|
83
|
-
import type {
|
|
84
|
-
|
|
85
|
-
import type { File } from "./content/file.js";
|
|
86
|
-
export type ToolReturn = string | Image | Audio | File | ContentItem | Array<string | Image | Audio | File | ContentItem>;
|
|
87
|
-
export type ToolHandler<T = Record<string, unknown>> = (args: T) => Promise<ToolReturn> | ToolReturn;
|
|
94
|
+
import type { ToolReturn } from "./content/index.js";
|
|
95
|
+
export type ToolHandler<T = Record<string, unknown>> = (args: T) => Promise<ToolReturn | CallToolResult> | ToolReturn | CallToolResult;
|
|
88
96
|
export interface ToolDefinition<T = Record<string, unknown>> {
|
|
89
97
|
name: string;
|
|
90
98
|
description: string;
|
package/dist/types.js
CHANGED
|
@@ -4,5 +4,13 @@ export const JSON_RPC_ERROR_CODES = {
|
|
|
4
4
|
INVALID_REQUEST: -32600,
|
|
5
5
|
METHOD_NOT_FOUND: -32601,
|
|
6
6
|
INVALID_PARAMS: -32602,
|
|
7
|
-
INTERNAL_ERROR: -32603
|
|
7
|
+
INTERNAL_ERROR: -32603
|
|
8
8
|
};
|
|
9
|
+
export class ToolError extends Error {
|
|
10
|
+
code;
|
|
11
|
+
constructor(code, message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.code = code;
|
|
14
|
+
this.name = "ToolError";
|
|
15
|
+
}
|
|
16
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tiny-stdio-mcp-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Minimal MCP server over stdio with typed tools and rich content helpers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -9,6 +9,14 @@
|
|
|
9
9
|
".": {
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
11
11
|
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./jsonrpc": {
|
|
14
|
+
"types": "./dist/jsonrpc.d.ts",
|
|
15
|
+
"import": "./dist/jsonrpc.js"
|
|
16
|
+
},
|
|
17
|
+
"./testing": {
|
|
18
|
+
"types": "./dist/testing.d.ts",
|
|
19
|
+
"import": "./dist/testing.js"
|
|
12
20
|
}
|
|
13
21
|
},
|
|
14
22
|
"scripts": {
|
|
@@ -16,15 +24,16 @@
|
|
|
16
24
|
"prepublishOnly": "tsc"
|
|
17
25
|
},
|
|
18
26
|
"files": [
|
|
19
|
-
"dist"
|
|
27
|
+
"dist",
|
|
28
|
+
"!dist/testing.*"
|
|
20
29
|
],
|
|
21
30
|
"engines": {
|
|
22
|
-
"node": ">=
|
|
31
|
+
"node": ">=20"
|
|
23
32
|
},
|
|
24
33
|
"repository": {
|
|
25
34
|
"type": "git",
|
|
26
35
|
"url": "https://github.com/poe-platform/poe-code.git",
|
|
27
|
-
"directory": "packages/tiny-mcp-server"
|
|
36
|
+
"directory": "packages/tiny-stdio-mcp-server"
|
|
28
37
|
},
|
|
29
38
|
"license": "MIT",
|
|
30
39
|
"keywords": [
|
package/dist/testing.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
-
import type { Server } from "./server.js";
|
|
3
|
-
export interface TestPair {
|
|
4
|
-
client: Client;
|
|
5
|
-
cleanup: () => Promise<void>;
|
|
6
|
-
}
|
|
7
|
-
export declare function createTestPair(server: Server): Promise<TestPair>;
|
package/dist/testing.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
-
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
3
|
-
export async function createTestPair(server) {
|
|
4
|
-
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
|
|
5
|
-
const client = new Client({
|
|
6
|
-
name: "test-client",
|
|
7
|
-
version: "1.0.0",
|
|
8
|
-
});
|
|
9
|
-
// Start server connection (runs in background)
|
|
10
|
-
const serverPromise = server.connectSDK(serverTransport);
|
|
11
|
-
// Connect client
|
|
12
|
-
await client.connect(clientTransport);
|
|
13
|
-
const cleanup = async () => {
|
|
14
|
-
await client.close();
|
|
15
|
-
await clientTransport.close();
|
|
16
|
-
await serverTransport.close();
|
|
17
|
-
await serverPromise;
|
|
18
|
-
};
|
|
19
|
-
return { client, cleanup };
|
|
20
|
-
}
|