@karashiiro/mcp 0.1.0 → 0.3.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 CHANGED
@@ -1,3 +1,227 @@
1
- # mcp
1
+ # @karashiiro/mcp
2
2
 
3
- (WIP) MCP utilities in TypeScript
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
+ #### Session-Aware Factories
100
+
101
+ In stateful mode, the factory function receives the session ID as a parameter, enabling session-specific initialization:
102
+
103
+ ```ts
104
+ const handle = await serveHttp(
105
+ (sessionId) => {
106
+ console.log(`Creating server for session: ${sessionId}`);
107
+ return createServer();
108
+ },
109
+ {
110
+ port: 8080,
111
+ sessions: {},
112
+ },
113
+ );
114
+ ```
115
+
116
+ #### Async Factories
117
+
118
+ The factory function can be async, which is useful for initialization that requires async operations:
119
+
120
+ ```ts
121
+ const handle = await serveHttp(
122
+ async (sessionId) => {
123
+ // Perform async initialization (e.g., connect to database, load config)
124
+ await initializeResources(sessionId);
125
+ return createServer();
126
+ },
127
+ {
128
+ port: 8080,
129
+ sessions: {},
130
+ },
131
+ );
132
+ ```
133
+
134
+ #### Custom Session IDs
135
+
136
+ ```ts
137
+ const handle = await serveHttp(createServer, {
138
+ port: 8080,
139
+ sessions: {
140
+ sessionIdGenerator: () => `session-${Date.now()}`,
141
+ },
142
+ });
143
+ ```
144
+
145
+ #### Legacy SSE Support
146
+
147
+ For backwards compatibility with older MCP clients that use SSE transport:
148
+
149
+ ```ts
150
+ const handle = await serveHttp(createServer, {
151
+ port: 8080,
152
+ sessions: {
153
+ legacySse: {
154
+ sseEndpoint: "/sse", // default: "/sse"
155
+ messagesEndpoint: "/messages", // default: "/messages"
156
+ },
157
+ },
158
+ });
159
+ ```
160
+
161
+ ## Entry Points
162
+
163
+ This package provides multiple entry points for optimal bundle size:
164
+
165
+ | Entry Point | Description | Requires Hono |
166
+ |-------------|-------------|---------------|
167
+ | `@karashiiro/mcp` | Everything (re-exports all) | Yes |
168
+ | `@karashiiro/mcp/stdio` | Stdio transport only | No |
169
+ | `@karashiiro/mcp/http` | HTTP transport only | Yes |
170
+
171
+ If you only need stdio transport, import from `@karashiiro/mcp/stdio` to avoid bundling Hono.
172
+
173
+ ## API Reference
174
+
175
+ ### Factory Types
176
+
177
+ The library provides two factory types for type-safe server creation:
178
+
179
+ ```ts
180
+ // For stateless mode (serveStdio and serveHttp without sessions)
181
+ type StatelessServerFactory = () => McpServer | Promise<McpServer>;
182
+
183
+ // For stateful mode (serveHttp with sessions)
184
+ type StatefulServerFactory = (sessionId: string) => McpServer | Promise<McpServer>;
185
+ ```
186
+
187
+ Both factory types support async initialization by returning a `Promise<McpServer>`.
188
+
189
+ ### `serveStdio(serverFactory)`
190
+
191
+ Serves an MCP server over stdin/stdout.
192
+
193
+ - `serverFactory: StatelessServerFactory` - Factory function that creates a single server instance
194
+ - Returns: `Promise<ServerHandle>`
195
+
196
+ ### `serveHttp(serverFactory, options?)` (stateless)
197
+
198
+ Serves an MCP server over HTTP in stateless mode.
199
+
200
+ - `serverFactory: StatelessServerFactory` - Factory function called once, creates a shared server
201
+ - `options.port` - Port to listen on (default: `8080`)
202
+ - `options.host` - Host to bind to (default: `"127.0.0.1"`)
203
+ - `options.endpoint` - MCP endpoint path (default: `"/mcp"`)
204
+ - Returns: `Promise<ServerHandle>`
205
+
206
+ ### `serveHttp(serverFactory, options)` (stateful)
207
+
208
+ Serves an MCP server over HTTP in stateful mode with per-client sessions.
209
+
210
+ - `serverFactory: StatefulServerFactory` - Factory function called per session with the session ID
211
+ - `options.port` - Port to listen on (default: `8080`)
212
+ - `options.host` - Host to bind to (default: `"127.0.0.1"`)
213
+ - `options.endpoint` - MCP endpoint path (default: `"/mcp"`)
214
+ - `options.sessions` - **Required** to enable stateful mode
215
+ - `options.sessions.sessionIdGenerator` - Custom session ID generator function
216
+ - `options.sessions.legacySse` - Enable legacy SSE transport endpoints
217
+ - Returns: `Promise<ServerHandle>`
218
+
219
+ ### `ServerHandle`
220
+
221
+ Handle for controlling the server lifecycle.
222
+
223
+ - `close(): Promise<void>` - Gracefully shut down the server
224
+
225
+ ## License
226
+
227
+ UNLICENSED
package/dist/http.d.ts ADDED
@@ -0,0 +1,73 @@
1
+ import type { ServerHandle, StatelessServerFactory, StatefulServerFactory } from "./types.js";
2
+ export type { ServerHandle, StatelessServerFactory, StatefulServerFactory, } from "./types.js";
3
+ export type { ServerFactory } 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
+ /**
24
+ * Base HTTP server options without session configuration.
25
+ */
26
+ export interface HttpServerOptionsBase {
27
+ port: number;
28
+ host: string;
29
+ endpoint: string;
30
+ }
31
+ /**
32
+ * HTTP server options for stateless mode (no sessions).
33
+ */
34
+ export interface HttpServerStatelessOptions extends HttpServerOptionsBase {
35
+ sessions?: undefined;
36
+ }
37
+ /**
38
+ * HTTP server options for stateful mode (with sessions).
39
+ */
40
+ export interface HttpServerStatefulOptions extends HttpServerOptionsBase {
41
+ sessions: HttpServerSessionOptions;
42
+ }
43
+ /**
44
+ * Combined HTTP server options type.
45
+ */
46
+ export type HttpServerOptions = HttpServerStatelessOptions | HttpServerStatefulOptions;
47
+ /**
48
+ * Serve an MCP server over HTTP in stateless mode.
49
+ *
50
+ * In stateless mode, the factory is called once and all clients share the same server instance.
51
+ * The factory receives no parameters.
52
+ *
53
+ * @param serverFactory - Factory function that creates a single shared McpServer instance.
54
+ * @param options - Server configuration options (without sessions).
55
+ * @returns A handle to control the server lifecycle.
56
+ */
57
+ export declare function serveHttp(serverFactory: StatelessServerFactory, options?: Partial<Omit<HttpServerOptionsBase, "sessions">> & {
58
+ sessions?: undefined;
59
+ }): Promise<ServerHandle>;
60
+ /**
61
+ * Serve an MCP server over HTTP in stateful mode with per-client sessions.
62
+ *
63
+ * In stateful mode, the factory is called once per client session, receiving the session ID.
64
+ * This allows each client to have isolated state.
65
+ *
66
+ * @param serverFactory - Factory function that creates McpServer instances per session.
67
+ * @param options - Server configuration options with sessions enabled.
68
+ * @returns A handle to control the server lifecycle.
69
+ */
70
+ export declare function serveHttp(serverFactory: StatefulServerFactory, options: Partial<HttpServerOptionsBase> & {
71
+ sessions: HttpServerSessionOptions;
72
+ }): Promise<ServerHandle>;
73
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,YAAY,EACZ,sBAAsB,EACtB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAEpB,YAAY,EACV,YAAY,EACZ,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,YAAY,CAAC;AAGpB,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;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;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,0BAA2B,SAAQ,qBAAqB;IACvE,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,yBAA0B,SAAQ,qBAAqB;IACtE,QAAQ,EAAE,wBAAwB,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GACzB,0BAA0B,GAC1B,yBAAyB,CAAC;AAqD9B;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CACvB,aAAa,EAAE,sBAAsB,EACrC,OAAO,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,qBAAqB,EAAE,UAAU,CAAC,CAAC,GAAG;IAC3D,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB,GACA,OAAO,CAAC,YAAY,CAAC,CAAC;AAEzB;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CACvB,aAAa,EAAE,qBAAqB,EACpC,OAAO,EAAE,OAAO,CAAC,qBAAqB,CAAC,GAAG;IACxC,QAAQ,EAAE,wBAAwB,CAAC;CACpC,GACA,OAAO,CAAC,YAAY,CAAC,CAAC"}
package/dist/http.js ADDED
@@ -0,0 +1,230 @@
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
+ // Implementation signature (accepts both)
32
+ export async function serveHttp(serverFactory, options = {}) {
33
+ const mergedOptions = {
34
+ ...defaultOptions,
35
+ ...options,
36
+ };
37
+ if (mergedOptions.sessions) {
38
+ return serveHttpStateful(serverFactory, mergedOptions);
39
+ }
40
+ else {
41
+ return serveHttpStateless(serverFactory, mergedOptions);
42
+ }
43
+ }
44
+ /**
45
+ * Stateless mode: single server instance, single transport, no session tracking.
46
+ */
47
+ async function serveHttpStateless(serverFactory, options) {
48
+ // Call factory ONCE to get the single server instance (no sessionId in stateless mode)
49
+ const server = await serverFactory();
50
+ // Create the transport (no session ID generator = stateless)
51
+ const transport = new WebStandardStreamableHTTPServerTransport({
52
+ sessionIdGenerator: undefined,
53
+ });
54
+ // Create the Hono app
55
+ const app = new Hono();
56
+ addCors(app);
57
+ // MCP endpoint
58
+ app.all(options.endpoint, (c) => transport.handleRequest(c.req.raw));
59
+ await server.connect(transport);
60
+ const httpServer = serve({
61
+ fetch: app.fetch,
62
+ port: options.port,
63
+ hostname: options.host,
64
+ });
65
+ return createHandle(httpServer);
66
+ }
67
+ /**
68
+ * Stateful mode: per-session servers, transports, and event stores.
69
+ */
70
+ function serveHttpStateful(serverFactory, options) {
71
+ const sessions = new Map();
72
+ const sessionIdGenerator = options.sessions.sessionIdGenerator ?? uuidv4;
73
+ // Legacy SSE options
74
+ const legacySse = options.sessions?.legacySse;
75
+ const sseEndpoint = legacySse?.sseEndpoint ?? "/sse";
76
+ const messagesEndpoint = legacySse?.messagesEndpoint ?? "/messages";
77
+ const app = new Hono();
78
+ addCors(app);
79
+ // Main MCP endpoint (Streamable HTTP)
80
+ app.all(options.endpoint, async (c) => {
81
+ const sessionId = c.req.header("mcp-session-id");
82
+ // Clone the request so we can read the body without consuming it
83
+ const rawRequest = c.req.raw;
84
+ const bodyText = await rawRequest.text();
85
+ let body = null;
86
+ try {
87
+ body = bodyText ? JSON.parse(bodyText) : null;
88
+ }
89
+ catch {
90
+ // Invalid JSON - body stays null
91
+ }
92
+ // Helper to recreate request with body (since we consumed it)
93
+ const recreateRequest = () => new Request(rawRequest.url, {
94
+ method: rawRequest.method,
95
+ headers: rawRequest.headers,
96
+ body: bodyText || undefined,
97
+ });
98
+ // New session (initialize request without session ID)
99
+ if (!sessionId && body && isInitializeRequest(body)) {
100
+ // Generate session ID upfront so we can pass it to the factory
101
+ const sid = sessionIdGenerator();
102
+ // Create server with session ID (supports async factories)
103
+ const server = await serverFactory(sid);
104
+ const eventStore = new InMemoryEventStore();
105
+ const transport = new WebStandardStreamableHTTPServerTransport({
106
+ // Use our pre-generated session ID
107
+ sessionIdGenerator: () => sid,
108
+ eventStore,
109
+ onsessioninitialized: () => {
110
+ // Server already created above, just store the session state
111
+ const session = {
112
+ type: "streamable-http",
113
+ transport,
114
+ server,
115
+ eventStore,
116
+ };
117
+ sessions.set(sid, session);
118
+ },
119
+ });
120
+ // Connect server to transport before handling the request
121
+ await server.connect(transport);
122
+ transport.onclose = () => {
123
+ sessions.delete(sid);
124
+ };
125
+ return transport.handleRequest(recreateRequest());
126
+ }
127
+ // Existing session
128
+ if (sessionId) {
129
+ const session = sessions.get(sessionId);
130
+ if (!session) {
131
+ return c.text("Session not found", 404);
132
+ }
133
+ // Validate transport type matches endpoint
134
+ if (session.type !== "streamable-http") {
135
+ return c.json({
136
+ jsonrpc: "2.0",
137
+ error: {
138
+ code: -32000,
139
+ message: "Bad Request: Session exists but uses a different transport protocol",
140
+ },
141
+ id: null,
142
+ }, 400);
143
+ }
144
+ return session.transport.handleRequest(recreateRequest());
145
+ }
146
+ // Invalid request (no session ID, not an initialize request)
147
+ return c.text("Bad request - missing session ID", 400);
148
+ });
149
+ // Legacy SSE endpoints (only if enabled)
150
+ if (legacySse) {
151
+ // GET /sse - Establish SSE stream
152
+ app.get(sseEndpoint, async (c) => {
153
+ const { outgoing } = c.env;
154
+ // Create SSE transport with messages endpoint
155
+ const transport = new SSEServerTransport(messagesEndpoint, outgoing);
156
+ // Create server with session ID (supports async factories)
157
+ const server = await serverFactory(transport.sessionId);
158
+ // Store session
159
+ const session = {
160
+ type: "sse",
161
+ transport,
162
+ server,
163
+ };
164
+ sessions.set(transport.sessionId, session);
165
+ // Cleanup on close
166
+ transport.onclose = () => {
167
+ sessions.delete(transport.sessionId);
168
+ };
169
+ // Connect and start
170
+ await server.connect(transport);
171
+ // Signal to Hono that we've handled the response directly
172
+ return RESPONSE_ALREADY_SENT;
173
+ });
174
+ // POST /messages - Handle messages for SSE sessions
175
+ app.post(messagesEndpoint, async (c) => {
176
+ const sessionId = c.req.query("sessionId");
177
+ if (!sessionId) {
178
+ return c.text("Missing sessionId query parameter", 400);
179
+ }
180
+ const session = sessions.get(sessionId);
181
+ if (!session) {
182
+ return c.text("Session not found", 404);
183
+ }
184
+ // Validate transport type matches endpoint
185
+ if (session.type !== "sse") {
186
+ return c.json({
187
+ jsonrpc: "2.0",
188
+ error: {
189
+ code: -32000,
190
+ message: "Bad Request: Session exists but uses a different transport protocol",
191
+ },
192
+ id: null,
193
+ }, 400);
194
+ }
195
+ const { incoming, outgoing } = c.env;
196
+ // Parse body for SSEServerTransport
197
+ const bodyText = await c.req.text();
198
+ let parsedBody = null;
199
+ try {
200
+ parsedBody = bodyText ? JSON.parse(bodyText) : null;
201
+ }
202
+ catch {
203
+ // Let handlePostMessage handle the error
204
+ }
205
+ await session.transport.handlePostMessage(incoming, outgoing, parsedBody);
206
+ return RESPONSE_ALREADY_SENT;
207
+ });
208
+ }
209
+ const httpServer = serve({
210
+ fetch: app.fetch,
211
+ port: options.port,
212
+ hostname: options.host,
213
+ });
214
+ return createHandle(httpServer);
215
+ }
216
+ function addCors(app) {
217
+ // Enable CORS for all origins
218
+ app.use("*", cors({
219
+ origin: "*",
220
+ allowMethods: ["GET", "POST", "DELETE", "OPTIONS"],
221
+ allowHeaders: [
222
+ "Content-Type",
223
+ "mcp-session-id",
224
+ "Last-Event-ID",
225
+ "mcp-protocol-version",
226
+ ],
227
+ exposeHeaders: ["mcp-session-id", "mcp-protocol-version"],
228
+ }));
229
+ }
230
+ //# sourceMappingURL=http.js.map
@@ -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
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- export interface HttpServerSessionOptions {
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
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAQzE,MAAM,WAAW,wBAAwB;IACvC,kBAAkB,CAAC,EAAE,MAAM,MAAM,CAAC;CACnC;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;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,gEAAgE;IAChE,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAiCD;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAC7B,aAAa,EAAE,MAAM,SAAS,EAC9B,OAAO,GAAE,OAAO,CAAC,iBAAiB,CAAM,GACvC,OAAO,CAAC,gBAAgB,CAAC,CAW3B"}
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
- import { serve } from "@hono/node-server";
2
- import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
3
- import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
4
- import { Hono } from "hono";
5
- import { cors } from "hono/cors";
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":"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"}
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"}
@@ -0,0 +1,13 @@
1
+ import type { ServerHandle, StatelessServerFactory } from "./types.js";
2
+ export type { ServerHandle, StatelessServerFactory } from "./types.js";
3
+ export type { ServerFactory } 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
+ * Called once with no parameters (stdio is always single-session).
9
+ * Can return a Promise for async initialization.
10
+ * @returns A handle to control the server lifecycle.
11
+ */
12
+ export declare function serveStdio(serverFactory: StatelessServerFactory): Promise<ServerHandle>;
13
+ //# sourceMappingURL=stdio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEvE,YAAY,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAGvE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAC9B,aAAa,EAAE,sBAAsB,GACpC,OAAO,CAAC,YAAY,CAAC,CA4BvB"}
package/dist/stdio.js ADDED
@@ -0,0 +1,33 @@
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
+ * Called once with no parameters (stdio is always single-session).
7
+ * Can return a Promise for async initialization.
8
+ * @returns A handle to control the server lifecycle.
9
+ */
10
+ export async function serveStdio(serverFactory) {
11
+ const server = await serverFactory();
12
+ const transport = new StdioServerTransport();
13
+ // Set up the closed promise before connecting
14
+ let resolveClose;
15
+ const closedPromise = new Promise((resolve) => {
16
+ resolveClose = resolve;
17
+ });
18
+ transport.onclose = () => {
19
+ resolveClose();
20
+ };
21
+ // connect() automatically calls transport.start() for stdio
22
+ await server.connect(transport);
23
+ let closePromise;
24
+ return {
25
+ close: () => {
26
+ if (!closePromise) {
27
+ closePromise = transport.close().then(() => closedPromise);
28
+ }
29
+ return closePromise;
30
+ },
31
+ };
32
+ }
33
+ //# sourceMappingURL=stdio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAQjF;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,aAAqC,EACd;IACvB,MAAM,MAAM,GAAG,MAAM,aAAa,EAAE,CAAC;IAErC,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"}
@@ -0,0 +1,24 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ /**
3
+ * Handle returned by serve functions for controlling the server lifecycle.
4
+ */
5
+ export interface ServerHandle {
6
+ /** Close the server and stop accepting new connections. */
7
+ close: () => Promise<void>;
8
+ }
9
+ /**
10
+ * Factory function for stateless mode (no session ID).
11
+ * Used by serveStdio and serveHttp without sessions.
12
+ */
13
+ export type StatelessServerFactory = () => McpServer | Promise<McpServer>;
14
+ /**
15
+ * Factory function for stateful mode (receives session ID).
16
+ * Used by serveHttp with sessions enabled.
17
+ */
18
+ export type StatefulServerFactory = (sessionId: string) => McpServer | Promise<McpServer>;
19
+ /**
20
+ * Union type for all server factory signatures.
21
+ * @deprecated Prefer using StatelessServerFactory or StatefulServerFactory directly.
22
+ */
23
+ export type ServerFactory = (sessionId?: string) => McpServer | Promise<McpServer>;
24
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,2DAA2D;IAC3D,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;AAE1E;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAClC,SAAS,EAAE,MAAM,KACd,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;AAEpC;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,CAC1B,SAAS,CAAC,EAAE,MAAM,KACf,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -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.1.0",
3
+ "version": "0.3.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
- "dependencies": {
34
+ "peerDependencies": {
25
35
  "@hono/node-server": "^1.19.8",
26
36
  "@modelcontextprotocol/sdk": "^1.25.2",
27
- "hono": "^4.11.4",
28
- "uuid": "^13.0.0",
29
- "zod": "^4.3.5"
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
  }