@karashiiro/mcp 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +167 -2
- package/dist/http.d.ts +40 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +237 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +2 -28
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -151
- package/dist/index.js.map +1 -1
- package/dist/stdio.d.ts +11 -0
- package/dist/stdio.d.ts.map +1 -0
- package/dist/stdio.js +31 -0
- package/dist/stdio.js.map +1 -0
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +29 -8
package/README.md
CHANGED
|
@@ -1,3 +1,168 @@
|
|
|
1
|
-
# mcp
|
|
1
|
+
# @karashiiro/mcp
|
|
2
2
|
|
|
3
|
-
(
|
|
3
|
+
Lightweight utilities for serving [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers in TypeScript.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @karashiiro/mcp @modelcontextprotocol/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### For HTTP transport
|
|
12
|
+
|
|
13
|
+
If you plan to use HTTP transport, you'll also need Hono:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install hono @hono/node-server
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### Stdio Transport
|
|
22
|
+
|
|
23
|
+
The simplest way to serve an MCP server. Great for CLI tools and local integrations.
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
27
|
+
import { serveStdio } from "@karashiiro/mcp/stdio";
|
|
28
|
+
|
|
29
|
+
function createServer() {
|
|
30
|
+
const server = new McpServer({
|
|
31
|
+
name: "my-server",
|
|
32
|
+
version: "1.0.0",
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
server.registerTool(
|
|
36
|
+
"hello",
|
|
37
|
+
{ description: "Says hello" },
|
|
38
|
+
async () => ({
|
|
39
|
+
content: [{ type: "text", text: "Hello from MCP!" }],
|
|
40
|
+
}),
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
return server;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
await serveStdio(createServer);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### HTTP Transport
|
|
50
|
+
|
|
51
|
+
Serve your MCP server over HTTP using the Streamable HTTP transport.
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
55
|
+
import { serveHttp } from "@karashiiro/mcp/http";
|
|
56
|
+
|
|
57
|
+
function createServer() {
|
|
58
|
+
const server = new McpServer({
|
|
59
|
+
name: "my-server",
|
|
60
|
+
version: "1.0.0",
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
server.registerTool(
|
|
64
|
+
"hello",
|
|
65
|
+
{ description: "Says hello" },
|
|
66
|
+
async () => ({
|
|
67
|
+
content: [{ type: "text", text: "Hello from MCP!" }],
|
|
68
|
+
}),
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
return server;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const handle = await serveHttp(createServer, {
|
|
75
|
+
port: 8080,
|
|
76
|
+
host: "127.0.0.1",
|
|
77
|
+
endpoint: "/mcp",
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Later, to shut down:
|
|
81
|
+
await handle.close();
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
#### Stateless vs Stateful Mode
|
|
85
|
+
|
|
86
|
+
By default, `serveHttp` runs in **stateless mode** where all clients share a single server instance.
|
|
87
|
+
|
|
88
|
+
For **stateful mode** with per-client sessions, provide the `sessions` option:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
const handle = await serveHttp(createServer, {
|
|
92
|
+
port: 8080,
|
|
93
|
+
sessions: {}, // Enable stateful mode
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
In stateful mode, `createServer` is called once per client session, allowing each client to have isolated state.
|
|
98
|
+
|
|
99
|
+
#### Custom Session IDs
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
const handle = await serveHttp(createServer, {
|
|
103
|
+
port: 8080,
|
|
104
|
+
sessions: {
|
|
105
|
+
sessionIdGenerator: () => `session-${Date.now()}`,
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
#### Legacy SSE Support
|
|
111
|
+
|
|
112
|
+
For backwards compatibility with older MCP clients that use SSE transport:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
const handle = await serveHttp(createServer, {
|
|
116
|
+
port: 8080,
|
|
117
|
+
sessions: {
|
|
118
|
+
legacySse: {
|
|
119
|
+
sseEndpoint: "/sse", // default: "/sse"
|
|
120
|
+
messagesEndpoint: "/messages", // default: "/messages"
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Entry Points
|
|
127
|
+
|
|
128
|
+
This package provides multiple entry points for optimal bundle size:
|
|
129
|
+
|
|
130
|
+
| Entry Point | Description | Requires Hono |
|
|
131
|
+
|-------------|-------------|---------------|
|
|
132
|
+
| `@karashiiro/mcp` | Everything (re-exports all) | Yes |
|
|
133
|
+
| `@karashiiro/mcp/stdio` | Stdio transport only | No |
|
|
134
|
+
| `@karashiiro/mcp/http` | HTTP transport only | Yes |
|
|
135
|
+
|
|
136
|
+
If you only need stdio transport, import from `@karashiiro/mcp/stdio` to avoid bundling Hono.
|
|
137
|
+
|
|
138
|
+
## API Reference
|
|
139
|
+
|
|
140
|
+
### `serveStdio(serverFactory)`
|
|
141
|
+
|
|
142
|
+
Serves an MCP server over stdin/stdout.
|
|
143
|
+
|
|
144
|
+
- `serverFactory: () => McpServer` - Factory function that creates the server instance
|
|
145
|
+
- Returns: `Promise<ServerHandle>`
|
|
146
|
+
|
|
147
|
+
### `serveHttp(serverFactory, options?)`
|
|
148
|
+
|
|
149
|
+
Serves an MCP server over HTTP.
|
|
150
|
+
|
|
151
|
+
- `serverFactory: () => McpServer` - Factory function that creates server instances
|
|
152
|
+
- `options.port` - Port to listen on (default: `8080`)
|
|
153
|
+
- `options.host` - Host to bind to (default: `"127.0.0.1"`)
|
|
154
|
+
- `options.endpoint` - MCP endpoint path (default: `"/mcp"`)
|
|
155
|
+
- `options.sessions` - Enable stateful mode with per-client sessions
|
|
156
|
+
- `options.sessions.sessionIdGenerator` - Custom session ID generator function
|
|
157
|
+
- `options.sessions.legacySse` - Enable legacy SSE transport endpoints
|
|
158
|
+
- Returns: `Promise<ServerHandle>`
|
|
159
|
+
|
|
160
|
+
### `ServerHandle`
|
|
161
|
+
|
|
162
|
+
Handle for controlling the server lifecycle.
|
|
163
|
+
|
|
164
|
+
- `close(): Promise<void>` - Gracefully shut down the server
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
UNLICENSED
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { ServerHandle } from "./types.js";
|
|
3
|
+
export type { ServerHandle } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Options for legacy SSE transport compatibility.
|
|
6
|
+
* @deprecated SSE transport is deprecated in favor of Streamable HTTP.
|
|
7
|
+
*/
|
|
8
|
+
export interface LegacySseOptions {
|
|
9
|
+
/** Endpoint for SSE stream. Defaults to "/sse". */
|
|
10
|
+
sseEndpoint?: string;
|
|
11
|
+
/** Endpoint for messages. Defaults to "/messages". */
|
|
12
|
+
messagesEndpoint?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface HttpServerSessionOptions {
|
|
15
|
+
sessionIdGenerator?: () => string;
|
|
16
|
+
/**
|
|
17
|
+
* Enable legacy SSE transport compatibility.
|
|
18
|
+
* When provided, adds /sse and /messages endpoints for older clients.
|
|
19
|
+
* @deprecated SSE transport is deprecated in favor of Streamable HTTP.
|
|
20
|
+
*/
|
|
21
|
+
legacySse?: LegacySseOptions;
|
|
22
|
+
}
|
|
23
|
+
export interface HttpServerOptions {
|
|
24
|
+
port: number;
|
|
25
|
+
host: string;
|
|
26
|
+
endpoint: string;
|
|
27
|
+
sessions?: HttpServerSessionOptions | undefined;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Serve an MCP server over HTTP.
|
|
31
|
+
*
|
|
32
|
+
* @param serverFactory - Factory function that creates McpServer instances.
|
|
33
|
+
* In stateless mode, called once. In stateful mode, called per session.
|
|
34
|
+
* @param options - Server configuration options.
|
|
35
|
+
* If `sessions` is provided, runs in stateful mode with per-session servers.
|
|
36
|
+
* If `sessions` is undefined, runs in stateless mode with a single server.
|
|
37
|
+
* @returns A handle to control the server lifecycle.
|
|
38
|
+
*/
|
|
39
|
+
export declare function serveHttp(serverFactory: () => McpServer, options?: Partial<HttpServerOptions>): Promise<ServerHandle>;
|
|
40
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAQzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,wBAAwB;IACvC,kBAAkB,CAAC,EAAE,MAAM,MAAM,CAAC;IAClC;;;;OAIG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,wBAAwB,GAAG,SAAS,CAAC;CACjD;AAqDD;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAC7B,aAAa,EAAE,MAAM,SAAS,EAC9B,OAAO,GAAE,OAAO,CAAC,iBAAiB,CAAM,GACvC,OAAO,CAAC,YAAY,CAAC,CAWvB"}
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { serve } from "@hono/node-server";
|
|
2
|
+
import { RESPONSE_ALREADY_SENT } from "@hono/node-server/utils/response";
|
|
3
|
+
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
4
|
+
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
5
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
+
import { Hono } from "hono";
|
|
7
|
+
import { cors } from "hono/cors";
|
|
8
|
+
import { v4 as uuidv4 } from "uuid";
|
|
9
|
+
import { InMemoryEventStore } from "./event-store.js";
|
|
10
|
+
const defaultOptions = Object.freeze({
|
|
11
|
+
port: 8080,
|
|
12
|
+
host: "127.0.0.1",
|
|
13
|
+
endpoint: "/mcp",
|
|
14
|
+
sessions: undefined,
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* Helper to create a closeable handle from a node server.
|
|
18
|
+
*/
|
|
19
|
+
function createHandle(server) {
|
|
20
|
+
return {
|
|
21
|
+
close: () => new Promise((resolve, reject) => {
|
|
22
|
+
server.close((err) => {
|
|
23
|
+
if (err)
|
|
24
|
+
reject(err);
|
|
25
|
+
else
|
|
26
|
+
resolve();
|
|
27
|
+
});
|
|
28
|
+
}),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Serve an MCP server over HTTP.
|
|
33
|
+
*
|
|
34
|
+
* @param serverFactory - Factory function that creates McpServer instances.
|
|
35
|
+
* In stateless mode, called once. In stateful mode, called per session.
|
|
36
|
+
* @param options - Server configuration options.
|
|
37
|
+
* If `sessions` is provided, runs in stateful mode with per-session servers.
|
|
38
|
+
* If `sessions` is undefined, runs in stateless mode with a single server.
|
|
39
|
+
* @returns A handle to control the server lifecycle.
|
|
40
|
+
*/
|
|
41
|
+
export async function serveHttp(serverFactory, options = {}) {
|
|
42
|
+
const mergedOptions = {
|
|
43
|
+
...defaultOptions,
|
|
44
|
+
...options,
|
|
45
|
+
};
|
|
46
|
+
if (mergedOptions.sessions) {
|
|
47
|
+
return serveHttpStateful(serverFactory, mergedOptions);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
return serveHttpStateless(serverFactory, mergedOptions);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Stateless mode: single server instance, single transport, no session tracking.
|
|
55
|
+
*/
|
|
56
|
+
async function serveHttpStateless(serverFactory, options) {
|
|
57
|
+
// Call factory ONCE to get the single server instance
|
|
58
|
+
const server = serverFactory();
|
|
59
|
+
// Create the transport (no session ID generator = stateless)
|
|
60
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
61
|
+
sessionIdGenerator: undefined,
|
|
62
|
+
});
|
|
63
|
+
// Create the Hono app
|
|
64
|
+
const app = new Hono();
|
|
65
|
+
addCors(app);
|
|
66
|
+
// MCP endpoint
|
|
67
|
+
app.all(options.endpoint, (c) => transport.handleRequest(c.req.raw));
|
|
68
|
+
await server.connect(transport);
|
|
69
|
+
const httpServer = serve({
|
|
70
|
+
fetch: app.fetch,
|
|
71
|
+
port: options.port,
|
|
72
|
+
hostname: options.host,
|
|
73
|
+
});
|
|
74
|
+
return createHandle(httpServer);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Stateful mode: per-session servers, transports, and event stores.
|
|
78
|
+
*/
|
|
79
|
+
function serveHttpStateful(serverFactory, options) {
|
|
80
|
+
const sessions = new Map();
|
|
81
|
+
const sessionIdGenerator = options.sessions?.sessionIdGenerator ?? uuidv4;
|
|
82
|
+
// Legacy SSE options
|
|
83
|
+
const legacySse = options.sessions?.legacySse;
|
|
84
|
+
const sseEndpoint = legacySse?.sseEndpoint ?? "/sse";
|
|
85
|
+
const messagesEndpoint = legacySse?.messagesEndpoint ?? "/messages";
|
|
86
|
+
const app = new Hono();
|
|
87
|
+
addCors(app);
|
|
88
|
+
// Main MCP endpoint (Streamable HTTP)
|
|
89
|
+
app.all(options.endpoint, async (c) => {
|
|
90
|
+
const sessionId = c.req.header("mcp-session-id");
|
|
91
|
+
// Clone the request so we can read the body without consuming it
|
|
92
|
+
const rawRequest = c.req.raw;
|
|
93
|
+
const bodyText = await rawRequest.text();
|
|
94
|
+
let body = null;
|
|
95
|
+
try {
|
|
96
|
+
body = bodyText ? JSON.parse(bodyText) : null;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Invalid JSON - body stays null
|
|
100
|
+
}
|
|
101
|
+
// Helper to recreate request with body (since we consumed it)
|
|
102
|
+
const recreateRequest = () => new Request(rawRequest.url, {
|
|
103
|
+
method: rawRequest.method,
|
|
104
|
+
headers: rawRequest.headers,
|
|
105
|
+
body: bodyText || undefined,
|
|
106
|
+
});
|
|
107
|
+
// New session (initialize request without session ID)
|
|
108
|
+
if (!sessionId && body && isInitializeRequest(body)) {
|
|
109
|
+
const eventStore = new InMemoryEventStore();
|
|
110
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
111
|
+
sessionIdGenerator,
|
|
112
|
+
eventStore,
|
|
113
|
+
onsessioninitialized: (sid) => {
|
|
114
|
+
// Factory called per session!
|
|
115
|
+
const server = serverFactory();
|
|
116
|
+
const session = {
|
|
117
|
+
type: "streamable-http",
|
|
118
|
+
transport,
|
|
119
|
+
server,
|
|
120
|
+
eventStore,
|
|
121
|
+
};
|
|
122
|
+
sessions.set(sid, session);
|
|
123
|
+
server.connect(transport);
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
transport.onclose = () => {
|
|
127
|
+
if (transport.sessionId) {
|
|
128
|
+
sessions.delete(transport.sessionId);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
return transport.handleRequest(recreateRequest());
|
|
132
|
+
}
|
|
133
|
+
// Existing session
|
|
134
|
+
if (sessionId) {
|
|
135
|
+
const session = sessions.get(sessionId);
|
|
136
|
+
if (!session) {
|
|
137
|
+
return c.text("Session not found", 404);
|
|
138
|
+
}
|
|
139
|
+
// Validate transport type matches endpoint
|
|
140
|
+
if (session.type !== "streamable-http") {
|
|
141
|
+
return c.json({
|
|
142
|
+
jsonrpc: "2.0",
|
|
143
|
+
error: {
|
|
144
|
+
code: -32000,
|
|
145
|
+
message: "Bad Request: Session exists but uses a different transport protocol",
|
|
146
|
+
},
|
|
147
|
+
id: null,
|
|
148
|
+
}, 400);
|
|
149
|
+
}
|
|
150
|
+
return session.transport.handleRequest(recreateRequest());
|
|
151
|
+
}
|
|
152
|
+
// Invalid request (no session ID, not an initialize request)
|
|
153
|
+
return c.text("Bad request - missing session ID", 400);
|
|
154
|
+
});
|
|
155
|
+
// Legacy SSE endpoints (only if enabled)
|
|
156
|
+
if (legacySse) {
|
|
157
|
+
// GET /sse - Establish SSE stream
|
|
158
|
+
app.get(sseEndpoint, async (c) => {
|
|
159
|
+
const { outgoing } = c.env;
|
|
160
|
+
// Create SSE transport with messages endpoint
|
|
161
|
+
const transport = new SSEServerTransport(messagesEndpoint, outgoing);
|
|
162
|
+
// Create and connect server
|
|
163
|
+
const server = serverFactory();
|
|
164
|
+
// Store session
|
|
165
|
+
const session = {
|
|
166
|
+
type: "sse",
|
|
167
|
+
transport,
|
|
168
|
+
server,
|
|
169
|
+
};
|
|
170
|
+
sessions.set(transport.sessionId, session);
|
|
171
|
+
// Cleanup on close
|
|
172
|
+
transport.onclose = () => {
|
|
173
|
+
sessions.delete(transport.sessionId);
|
|
174
|
+
};
|
|
175
|
+
// Connect and start
|
|
176
|
+
await server.connect(transport);
|
|
177
|
+
// Signal to Hono that we've handled the response directly
|
|
178
|
+
return RESPONSE_ALREADY_SENT;
|
|
179
|
+
});
|
|
180
|
+
// POST /messages - Handle messages for SSE sessions
|
|
181
|
+
app.post(messagesEndpoint, async (c) => {
|
|
182
|
+
const sessionId = c.req.query("sessionId");
|
|
183
|
+
if (!sessionId) {
|
|
184
|
+
return c.text("Missing sessionId query parameter", 400);
|
|
185
|
+
}
|
|
186
|
+
const session = sessions.get(sessionId);
|
|
187
|
+
if (!session) {
|
|
188
|
+
return c.text("Session not found", 404);
|
|
189
|
+
}
|
|
190
|
+
// Validate transport type matches endpoint
|
|
191
|
+
if (session.type !== "sse") {
|
|
192
|
+
return c.json({
|
|
193
|
+
jsonrpc: "2.0",
|
|
194
|
+
error: {
|
|
195
|
+
code: -32000,
|
|
196
|
+
message: "Bad Request: Session exists but uses a different transport protocol",
|
|
197
|
+
},
|
|
198
|
+
id: null,
|
|
199
|
+
}, 400);
|
|
200
|
+
}
|
|
201
|
+
const { incoming, outgoing } = c.env;
|
|
202
|
+
// Parse body for SSEServerTransport
|
|
203
|
+
const bodyText = await c.req.text();
|
|
204
|
+
let parsedBody = null;
|
|
205
|
+
try {
|
|
206
|
+
parsedBody = bodyText ? JSON.parse(bodyText) : null;
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
// Let handlePostMessage handle the error
|
|
210
|
+
}
|
|
211
|
+
await session.transport.handlePostMessage(incoming, outgoing, parsedBody);
|
|
212
|
+
return RESPONSE_ALREADY_SENT;
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
const httpServer = serve({
|
|
216
|
+
fetch: app.fetch,
|
|
217
|
+
port: options.port,
|
|
218
|
+
hostname: options.host,
|
|
219
|
+
});
|
|
220
|
+
return createHandle(httpServer);
|
|
221
|
+
}
|
|
222
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
223
|
+
function addCors(app) {
|
|
224
|
+
// Enable CORS for all origins
|
|
225
|
+
app.use("*", cors({
|
|
226
|
+
origin: "*",
|
|
227
|
+
allowMethods: ["GET", "POST", "DELETE", "OPTIONS"],
|
|
228
|
+
allowHeaders: [
|
|
229
|
+
"Content-Type",
|
|
230
|
+
"mcp-session-id",
|
|
231
|
+
"Last-Event-ID",
|
|
232
|
+
"mcp-protocol-version",
|
|
233
|
+
],
|
|
234
|
+
exposeHeaders: ["mcp-session-id", "mcp-protocol-version"],
|
|
235
|
+
}));
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,29 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
sessionIdGenerator?: () => string;
|
|
4
|
-
}
|
|
5
|
-
export interface HttpServerOptions {
|
|
6
|
-
port: number;
|
|
7
|
-
host: string;
|
|
8
|
-
endpoint: string;
|
|
9
|
-
sessions?: HttpServerSessionOptions | undefined;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Handle returned by serveHttp for controlling the server lifecycle.
|
|
13
|
-
*/
|
|
14
|
-
export interface HttpServerHandle {
|
|
15
|
-
/** Close the HTTP server and stop accepting new connections. */
|
|
16
|
-
close: () => Promise<void>;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Serve an MCP server over HTTP.
|
|
20
|
-
*
|
|
21
|
-
* @param serverFactory - Factory function that creates McpServer instances.
|
|
22
|
-
* In stateless mode, called once. In stateful mode, called per session.
|
|
23
|
-
* @param options - Server configuration options.
|
|
24
|
-
* If `sessions` is provided, runs in stateful mode with per-session servers.
|
|
25
|
-
* If `sessions` is undefined, runs in stateless mode with a single server.
|
|
26
|
-
* @returns A handle to control the server lifecycle.
|
|
27
|
-
*/
|
|
28
|
-
export declare function serveHttp(serverFactory: () => McpServer, options?: Partial<HttpServerOptions>): Promise<HttpServerHandle>;
|
|
1
|
+
export * from "./http.js";
|
|
2
|
+
export * from "./stdio.js";
|
|
29
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,152 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import { v4 as uuidv4 } from "uuid";
|
|
7
|
-
import { InMemoryEventStore } from "./event-store.js";
|
|
8
|
-
const defaultOptions = Object.freeze({
|
|
9
|
-
port: 8080,
|
|
10
|
-
host: "127.0.0.1",
|
|
11
|
-
endpoint: "/mcp",
|
|
12
|
-
sessions: undefined,
|
|
13
|
-
});
|
|
14
|
-
/**
|
|
15
|
-
* Helper to create a closeable handle from a node server.
|
|
16
|
-
*/
|
|
17
|
-
function createHandle(server) {
|
|
18
|
-
return {
|
|
19
|
-
close: () => new Promise((resolve, reject) => {
|
|
20
|
-
server.close((err) => {
|
|
21
|
-
if (err)
|
|
22
|
-
reject(err);
|
|
23
|
-
else
|
|
24
|
-
resolve();
|
|
25
|
-
});
|
|
26
|
-
}),
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Serve an MCP server over HTTP.
|
|
31
|
-
*
|
|
32
|
-
* @param serverFactory - Factory function that creates McpServer instances.
|
|
33
|
-
* In stateless mode, called once. In stateful mode, called per session.
|
|
34
|
-
* @param options - Server configuration options.
|
|
35
|
-
* If `sessions` is provided, runs in stateful mode with per-session servers.
|
|
36
|
-
* If `sessions` is undefined, runs in stateless mode with a single server.
|
|
37
|
-
* @returns A handle to control the server lifecycle.
|
|
38
|
-
*/
|
|
39
|
-
export async function serveHttp(serverFactory, options = {}) {
|
|
40
|
-
const mergedOptions = {
|
|
41
|
-
...defaultOptions,
|
|
42
|
-
...options,
|
|
43
|
-
};
|
|
44
|
-
if (mergedOptions.sessions) {
|
|
45
|
-
return serveHttpStateful(serverFactory, mergedOptions);
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
return serveHttpStateless(serverFactory, mergedOptions);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Stateless mode: single server instance, single transport, no session tracking.
|
|
53
|
-
*/
|
|
54
|
-
async function serveHttpStateless(serverFactory, options) {
|
|
55
|
-
// Call factory ONCE to get the single server instance
|
|
56
|
-
const server = serverFactory();
|
|
57
|
-
// Create the transport (no session ID generator = stateless)
|
|
58
|
-
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
59
|
-
sessionIdGenerator: undefined,
|
|
60
|
-
});
|
|
61
|
-
// Create the Hono app
|
|
62
|
-
const app = new Hono();
|
|
63
|
-
addCors(app);
|
|
64
|
-
// MCP endpoint
|
|
65
|
-
app.all(options.endpoint, (c) => transport.handleRequest(c.req.raw));
|
|
66
|
-
await server.connect(transport);
|
|
67
|
-
const httpServer = serve({
|
|
68
|
-
fetch: app.fetch,
|
|
69
|
-
port: options.port,
|
|
70
|
-
hostname: options.host,
|
|
71
|
-
});
|
|
72
|
-
return createHandle(httpServer);
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Stateful mode: per-session servers, transports, and event stores.
|
|
76
|
-
*/
|
|
77
|
-
function serveHttpStateful(serverFactory, options) {
|
|
78
|
-
const sessions = new Map();
|
|
79
|
-
const sessionIdGenerator = options.sessions?.sessionIdGenerator ?? uuidv4;
|
|
80
|
-
const app = new Hono();
|
|
81
|
-
addCors(app);
|
|
82
|
-
app.all(options.endpoint, async (c) => {
|
|
83
|
-
const sessionId = c.req.header("mcp-session-id");
|
|
84
|
-
// Clone the request so we can read the body without consuming it
|
|
85
|
-
const rawRequest = c.req.raw;
|
|
86
|
-
const bodyText = await rawRequest.text();
|
|
87
|
-
let body = null;
|
|
88
|
-
try {
|
|
89
|
-
body = bodyText ? JSON.parse(bodyText) : null;
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
// Invalid JSON - body stays null
|
|
93
|
-
}
|
|
94
|
-
// Helper to recreate request with body (since we consumed it)
|
|
95
|
-
const recreateRequest = () => new Request(rawRequest.url, {
|
|
96
|
-
method: rawRequest.method,
|
|
97
|
-
headers: rawRequest.headers,
|
|
98
|
-
body: bodyText || undefined,
|
|
99
|
-
});
|
|
100
|
-
// New session (initialize request without session ID)
|
|
101
|
-
if (!sessionId && body && isInitializeRequest(body)) {
|
|
102
|
-
const eventStore = new InMemoryEventStore();
|
|
103
|
-
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
104
|
-
sessionIdGenerator,
|
|
105
|
-
eventStore,
|
|
106
|
-
onsessioninitialized: (sid) => {
|
|
107
|
-
// Factory called per session!
|
|
108
|
-
const server = serverFactory();
|
|
109
|
-
sessions.set(sid, { transport, server, eventStore });
|
|
110
|
-
server.connect(transport);
|
|
111
|
-
},
|
|
112
|
-
});
|
|
113
|
-
transport.onclose = () => {
|
|
114
|
-
if (transport.sessionId) {
|
|
115
|
-
sessions.delete(transport.sessionId);
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
return transport.handleRequest(recreateRequest());
|
|
119
|
-
}
|
|
120
|
-
// Existing session
|
|
121
|
-
if (sessionId) {
|
|
122
|
-
const session = sessions.get(sessionId);
|
|
123
|
-
if (!session) {
|
|
124
|
-
return c.text("Session not found", 404);
|
|
125
|
-
}
|
|
126
|
-
return session.transport.handleRequest(recreateRequest());
|
|
127
|
-
}
|
|
128
|
-
// Invalid request (no session ID, not an initialize request)
|
|
129
|
-
return c.text("Bad request - missing session ID", 400);
|
|
130
|
-
});
|
|
131
|
-
const httpServer = serve({
|
|
132
|
-
fetch: app.fetch,
|
|
133
|
-
port: options.port,
|
|
134
|
-
hostname: options.host,
|
|
135
|
-
});
|
|
136
|
-
return createHandle(httpServer);
|
|
137
|
-
}
|
|
138
|
-
function addCors(app) {
|
|
139
|
-
// Enable CORS for all origins
|
|
140
|
-
app.use("*", cors({
|
|
141
|
-
origin: "*",
|
|
142
|
-
allowMethods: ["GET", "POST", "DELETE", "OPTIONS"],
|
|
143
|
-
allowHeaders: [
|
|
144
|
-
"Content-Type",
|
|
145
|
-
"mcp-session-id",
|
|
146
|
-
"Last-Event-ID",
|
|
147
|
-
"mcp-protocol-version",
|
|
148
|
-
],
|
|
149
|
-
exposeHeaders: ["mcp-session-id", "mcp-protocol-version"],
|
|
150
|
-
}));
|
|
151
|
-
}
|
|
1
|
+
// Re-export everything from both modules for convenience.
|
|
2
|
+
// Note: Importing from this entry point will load Hono dependencies.
|
|
3
|
+
// For stdio-only usage without Hono, import from "@karashiiro/mcp/stdio" instead.
|
|
4
|
+
export * from "./http.js";
|
|
5
|
+
export * from "./stdio.js";
|
|
152
6
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,qEAAqE;AACrE,kFAAkF;AAElF,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC"}
|
package/dist/stdio.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { ServerHandle } from "./types.js";
|
|
3
|
+
export type { ServerHandle } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Serve an MCP server over stdio (stdin/stdout).
|
|
6
|
+
*
|
|
7
|
+
* @param serverFactory - Factory function that creates an McpServer instance.
|
|
8
|
+
* @returns A handle to control the server lifecycle.
|
|
9
|
+
*/
|
|
10
|
+
export declare function serveStdio(serverFactory: () => McpServer): Promise<ServerHandle>;
|
|
11
|
+
//# sourceMappingURL=stdio.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,aAAa,EAAE,MAAM,SAAS,GAC7B,OAAO,CAAC,YAAY,CAAC,CA4BvB"}
|
package/dist/stdio.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2
|
+
/**
|
|
3
|
+
* Serve an MCP server over stdio (stdin/stdout).
|
|
4
|
+
*
|
|
5
|
+
* @param serverFactory - Factory function that creates an McpServer instance.
|
|
6
|
+
* @returns A handle to control the server lifecycle.
|
|
7
|
+
*/
|
|
8
|
+
export async function serveStdio(serverFactory) {
|
|
9
|
+
const server = serverFactory();
|
|
10
|
+
const transport = new StdioServerTransport();
|
|
11
|
+
// Set up the closed promise before connecting
|
|
12
|
+
let resolveClose;
|
|
13
|
+
const closedPromise = new Promise((resolve) => {
|
|
14
|
+
resolveClose = resolve;
|
|
15
|
+
});
|
|
16
|
+
transport.onclose = () => {
|
|
17
|
+
resolveClose();
|
|
18
|
+
};
|
|
19
|
+
// connect() automatically calls transport.start() for stdio
|
|
20
|
+
await server.connect(transport);
|
|
21
|
+
let closePromise;
|
|
22
|
+
return {
|
|
23
|
+
close: () => {
|
|
24
|
+
if (!closePromise) {
|
|
25
|
+
closePromise = transport.close().then(() => closedPromise);
|
|
26
|
+
}
|
|
27
|
+
return closePromise;
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=stdio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio.js","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAKjF;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,aAA8B,EACP;IACvB,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAE/B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAE7C,8CAA8C;IAC9C,IAAI,YAAwB,CAAC;IAC7B,MAAM,aAAa,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QACnD,YAAY,GAAG,OAAO,CAAC;IAAA,CACxB,CAAC,CAAC;IAEH,SAAS,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC;QACxB,YAAY,EAAE,CAAC;IAAA,CAChB,CAAC;IAEF,4DAA4D;IAC5D,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,IAAI,YAAuC,CAAC;IAE5C,OAAO;QACL,KAAK,EAAE,GAAG,EAAE,CAAC;YACX,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,YAAY,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,CAAC;YAC7D,CAAC;YACD,OAAO,YAAY,CAAC;QAAA,CACrB;KACF,CAAC;AAAA,CACH"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,2DAA2D;IAC3D,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@karashiiro/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./dist/index.js",
|
|
9
|
+
"./stdio": "./dist/stdio.js",
|
|
10
|
+
"./http": "./dist/http.js"
|
|
11
|
+
},
|
|
7
12
|
"files": [
|
|
8
13
|
"dist"
|
|
9
14
|
],
|
|
@@ -12,26 +17,42 @@
|
|
|
12
17
|
"license": "UNLICENSED",
|
|
13
18
|
"devDependencies": {
|
|
14
19
|
"@eslint/js": "^9.39.2",
|
|
20
|
+
"@hono/node-server": "^1.19.8",
|
|
21
|
+
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
15
22
|
"@typescript/native-preview": "7.0.0-dev.20260113.1",
|
|
16
23
|
"eslint": "^9.39.2",
|
|
24
|
+
"eventsource": "^4.1.0",
|
|
17
25
|
"get-port": "^7.1.0",
|
|
18
26
|
"globals": "^17.0.0",
|
|
27
|
+
"hono": "^4.11.4",
|
|
19
28
|
"jiti": "^2.6.1",
|
|
20
29
|
"prettier": "^3.7.4",
|
|
21
30
|
"typescript-eslint": "^8.53.0",
|
|
22
|
-
"vitest": "^4.0.17"
|
|
31
|
+
"vitest": "^4.0.17",
|
|
32
|
+
"zod": "^4.3.5"
|
|
23
33
|
},
|
|
24
|
-
"
|
|
34
|
+
"peerDependencies": {
|
|
25
35
|
"@hono/node-server": "^1.19.8",
|
|
26
36
|
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
27
|
-
"hono": "^4.11.4"
|
|
28
|
-
|
|
29
|
-
|
|
37
|
+
"hono": "^4.11.4"
|
|
38
|
+
},
|
|
39
|
+
"peerDependenciesMeta": {
|
|
40
|
+
"@hono/node-server": {
|
|
41
|
+
"optional": true
|
|
42
|
+
},
|
|
43
|
+
"hono": {
|
|
44
|
+
"optional": true
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"uuid": "^13.0.0"
|
|
30
49
|
},
|
|
31
50
|
"scripts": {
|
|
32
51
|
"build": "tsgo -p tsconfig.dist.json",
|
|
33
|
-
"test": "vitest",
|
|
52
|
+
"test": "vitest --run",
|
|
34
53
|
"lint": "eslint . --ext .ts",
|
|
35
|
-
"format": "prettier --write ."
|
|
54
|
+
"format": "prettier --write .",
|
|
55
|
+
"typecheck": "tsgo --noEmit",
|
|
56
|
+
"check": "pnpm typecheck && pnpm lint && pnpm test"
|
|
36
57
|
}
|
|
37
58
|
}
|