@tmcp/transport-http 0.1.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # @tmcp/transport-http
2
+
3
+ ## 0.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 41fb096: fix: use new sessions management
8
+ - Updated dependencies [41fb096]
9
+ - tmcp@1.4.0
package/README.md ADDED
@@ -0,0 +1,283 @@
1
+ # @tmcp/transport-http
2
+
3
+ An HTTP transport implementation for TMCP (TypeScript Model Context Protocol) servers. This package provides HTTP Streaming based communication for MCP servers over HTTP, enabling web-based clients to interact with your MCP server through standard HTTP requests.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @tmcp/transport-http tmcp
9
+ # or
10
+ pnpm add @tmcp/transport-http tmcp
11
+ # or
12
+ yarn add @tmcp/transport-http tmcp
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ### Basic Setup
18
+
19
+ ```javascript
20
+ import { McpServer } from 'tmcp';
21
+ import { HttpTransport } from '@tmcp/transport-http';
22
+
23
+ // Create your MCP server
24
+ const server = new McpServer(
25
+ {
26
+ name: 'my-http-server',
27
+ version: '1.0.0',
28
+ description: 'My HTTP MCP server',
29
+ },
30
+ {
31
+ adapter: new YourSchemaAdapter(),
32
+ capabilities: {
33
+ tools: { listChanged: true },
34
+ prompts: { listChanged: true },
35
+ resources: { listChanged: true },
36
+ },
37
+ },
38
+ );
39
+
40
+ // Add your tools, prompts, and resources
41
+ server.tool(
42
+ {
43
+ name: 'example_tool',
44
+ description: 'An example tool',
45
+ },
46
+ async () => {
47
+ return {
48
+ content: [{ type: 'text', text: 'Hello from HTTP!' }],
49
+ };
50
+ },
51
+ );
52
+
53
+ // Create the HTTP transport
54
+ const transport = new HttpTransport(server);
55
+
56
+ // Use with your preferred HTTP server
57
+ // Example with Node.js built-in server:
58
+ import { createServer } from 'http';
59
+
60
+ const httpServer = createServer(async (req, res) => {
61
+ if (req.method === 'POST' && req.url === '/mcp') {
62
+ try {
63
+ let body = '';
64
+ req.on('data', (chunk) => (body += chunk));
65
+ req.on('end', async () => {
66
+ const request = new Request('http://localhost/mcp', {
67
+ method: 'POST',
68
+ headers: req.headers,
69
+ body: body,
70
+ });
71
+
72
+ const response = await transport.respond(request);
73
+
74
+ // Copy response to Node.js response
75
+ response.headers.forEach((value, key) => {
76
+ res.setHeader(key, value);
77
+ });
78
+ res.statusCode = response.status;
79
+
80
+ const reader = response.body.getReader();
81
+ const pump = async () => {
82
+ const { done, value } = await reader.read();
83
+ if (done) {
84
+ res.end();
85
+ return;
86
+ }
87
+ res.write(value);
88
+ pump();
89
+ };
90
+ pump();
91
+ });
92
+ } catch (error) {
93
+ res.statusCode = 500;
94
+ res.end('Internal Server Error');
95
+ }
96
+ } else {
97
+ res.statusCode = 404;
98
+ res.end('Not Found');
99
+ }
100
+ });
101
+
102
+ httpServer.listen(3000, () => {
103
+ console.log('MCP HTTP server listening on port 3000');
104
+ });
105
+ ```
106
+
107
+ ### With Custom Session Management
108
+
109
+ ```javascript
110
+ const transport = new HttpTransport(server, {
111
+ getSessionId: () => {
112
+ // Custom session ID generation
113
+ return `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
114
+ },
115
+ });
116
+ ```
117
+
118
+ ## Features
119
+
120
+ - **🌐 HTTP/SSE Communication**: Uses Server-Sent Events for real-time bidirectional communication
121
+ - **🔄 Session Management**: Maintains client sessions with automatic session ID generation
122
+ - **📡 Streaming Responses**: Supports streaming responses through SSE
123
+ - **🔧 Framework Agnostic**: Works with any HTTP server framework (Fastify, Bun, Deno, etc.)
124
+ - **⚡ Real-time Updates**: Server can push notifications and updates to connected clients
125
+ - **🛡️ Error Handling**: Graceful error handling for malformed requests
126
+
127
+ ## API
128
+
129
+ ### `HttpTransport`
130
+
131
+ #### Constructor
132
+
133
+ ```typescript
134
+ new HttpTransport(server: McpServer, options?: HttpTransportOptions)
135
+ ```
136
+
137
+ Creates a new HTTP transport instance.
138
+
139
+ **Parameters:**
140
+
141
+ - `server` - A TMCP server instance to handle incoming requests
142
+ - `options` - Optional configuration for the transport
143
+
144
+ **Options:**
145
+
146
+ ```typescript
147
+ interface HttpTransportOptions {
148
+ getSessionId: () => string; // Custom session ID generator
149
+ }
150
+ ```
151
+
152
+ #### Methods
153
+
154
+ ##### `respond(request: Request): Promise<Response>`
155
+
156
+ Processes an HTTP request and returns a Response with Server-Sent Events.
157
+
158
+ **Parameters:**
159
+
160
+ - `request` - A Web API Request object containing the JSON-RPC message
161
+
162
+ **Returns:**
163
+
164
+ - A Response object with SSE stream for ongoing communication
165
+
166
+ ## Protocol Details
167
+
168
+ ### Request Format
169
+
170
+ Clients send JSON-RPC messages via HTTP POST requests:
171
+
172
+ ```http
173
+ POST /mcp HTTP/1.1
174
+ Content-Type: application/json
175
+ mcp-session-id: optional-session-id
176
+
177
+ {
178
+ "jsonrpc": "2.0",
179
+ "id": 1,
180
+ "method": "tools/list",
181
+ "params": {}
182
+ }
183
+ ```
184
+
185
+ ### Response Format
186
+
187
+ The server responds with Server-Sent Events:
188
+
189
+ ```http
190
+ HTTP/1.1 200 OK
191
+ Content-Type: text/event-stream
192
+ Cache-Control: no-cache
193
+ Connection: keep-alive
194
+ mcp-session-id: generated-or-provided-session-id
195
+
196
+ data: {"jsonrpc":"2.0","id":1,"result":{"tools":[...]}}
197
+
198
+ ```
199
+
200
+ ### Session Management
201
+
202
+ - **Session ID Header**: `mcp-session-id`
203
+ - **Automatic Generation**: If no session ID is provided, one is generated automatically
204
+ - **Session Persistence**: Sessions persist across multiple requests until the client disconnects
205
+ - **Server Notifications**: Server can send notifications to all active sessions
206
+
207
+ ## Framework Examples
208
+
209
+ ### Bun
210
+
211
+ ```javascript
212
+ import { McpServer } from 'tmcp';
213
+ import { HttpTransport } from '@tmcp/transport-http';
214
+
215
+ const server = new McpServer(/* ... */);
216
+ const transport = new HttpTransport(server);
217
+
218
+ Bun.serve({
219
+ port: 3000,
220
+ async fetch(req) {
221
+ if (req.method === 'POST' && new URL(req.url).pathname === '/mcp') {
222
+ return await transport.respond(req);
223
+ }
224
+ return new Response('Not Found', { status: 404 });
225
+ },
226
+ });
227
+ ```
228
+
229
+ ### Deno
230
+
231
+ ```javascript
232
+ import { McpServer } from 'tmcp';
233
+ import { HttpTransport } from '@tmcp/transport-http';
234
+
235
+ const server = new McpServer(/* ... */);
236
+ const transport = new HttpTransport(server);
237
+
238
+ Deno.serve({ port: 3000 }, async (req) => {
239
+ if (req.method === 'POST' && new URL(req.url).pathname === '/mcp') {
240
+ return await transport.respond(req);
241
+ }
242
+ return new Response('Not Found', { status: 404 });
243
+ });
244
+ ```
245
+
246
+ ## Error Handling
247
+
248
+ The transport includes comprehensive error handling:
249
+
250
+ - **Malformed JSON**: Invalid JSON requests return appropriate error responses
251
+ - **Session Management**: Automatic cleanup of disconnected sessions
252
+ - **Server Errors**: Server processing errors are propagated to clients
253
+
254
+ ## Development
255
+
256
+ ```bash
257
+ # Install dependencies
258
+ pnpm install
259
+
260
+ # Generate TypeScript declarations
261
+ pnpm generate:types
262
+
263
+ # Lint the code
264
+ pnpm lint
265
+ ```
266
+
267
+ ## Requirements
268
+
269
+ - Node.js 16+ (for native ES modules and Web API support)
270
+ - A TMCP server instance
271
+ - An HTTP server framework or runtime
272
+ - A schema adapter (Zod, Valibot, etc.)
273
+
274
+ ## Related Packages
275
+
276
+ - [`tmcp`](../tmcp) - Core TMCP server implementation
277
+ - [`@tmcp/transport-stdio`](../transport-stdio) - Standard I/O transport
278
+ - [`@tmcp/adapter-zod`](../adapter-zod) - Zod schema adapter
279
+ - [`@tmcp/adapter-valibot`](../adapter-valibot) - Valibot schema adapter
280
+
281
+ ## License
282
+
283
+ MIT
package/jsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "esnext",
4
+ "module": "nodenext",
5
+ "moduleResolution": "nodenext",
6
+ "allowJs": true,
7
+ "checkJs": true,
8
+ "declaration": true,
9
+ "emitDeclarationOnly": false,
10
+ "strict": true,
11
+ "noEmit": true,
12
+ "skipLibCheck": true
13
+ },
14
+ "include": ["src/**/*"],
15
+ "exclude": ["node_modules"]
16
+ }
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@tmcp/transport-http",
3
+ "version": "0.1.1",
4
+ "description": "Transport for TMCP using HTTP",
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "types": "src/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./src/index.d.ts",
11
+ "default": "./src/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "peerDependencies": {
16
+ "tmcp": "^1.5.0"
17
+ },
18
+ "keywords": [
19
+ "tmcp",
20
+ "http",
21
+ "transport"
22
+ ],
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/paoloricciuti/tmcp.git",
26
+ "directory": "packages/transport-http"
27
+ },
28
+ "devDependencies": {
29
+ "publint": "^0.3.12",
30
+ "tmcp": "^1.5.0"
31
+ },
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "scripts": {
36
+ "generate:types": "tsc ./src/*.js --declaration --emitDeclarationOnly --target esnext --allowJs --moduleresolution nodenext --module nodenext && publint",
37
+ "prepublish": "pnpm generate:types"
38
+ }
39
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @import { McpServer, ClientCapabilities } from "tmcp";
3
+ */
4
+ /**
5
+ * @typedef {{
6
+ * getSessionId: () => string
7
+ * }} HttpTransportOptions
8
+ */
9
+ export class HttpTransport {
10
+ /**
11
+ *
12
+ * @param {McpServer<any>} server
13
+ * @param {HttpTransportOptions} [options]
14
+ */
15
+ constructor(server: McpServer<any>, options?: HttpTransportOptions);
16
+ /**
17
+ *
18
+ * @param {Request} request
19
+ * @returns
20
+ */
21
+ respond(request: Request): Promise<Response>;
22
+ #private;
23
+ }
24
+ export type HttpTransportOptions = {
25
+ getSessionId: () => string;
26
+ };
27
+ import type { McpServer } from "tmcp";
package/src/index.js ADDED
@@ -0,0 +1,102 @@
1
+ /**
2
+ * @import { McpServer, ClientCapabilities } from "tmcp";
3
+ */
4
+
5
+ /**
6
+ * @typedef {{
7
+ * getSessionId: () => string
8
+ * }} HttpTransportOptions
9
+ */
10
+
11
+ export class HttpTransport {
12
+ /**
13
+ * @type {McpServer<any>}
14
+ */
15
+ #server;
16
+
17
+ /**
18
+ * @type {HttpTransportOptions}
19
+ */
20
+ #options;
21
+
22
+ /**
23
+ * @type {Map<string, ReadableStreamDefaultController>}
24
+ */
25
+ #session = new Map();
26
+ /**
27
+ * @type {Map<string, ReadableStream>}
28
+ */
29
+ #streams = new Map();
30
+
31
+ /**
32
+ *
33
+ * @param {McpServer<any>} server
34
+ * @param {HttpTransportOptions} [options]
35
+ */
36
+ constructor(server, options) {
37
+ this.#server = server;
38
+ this.#options = options || {
39
+ getSessionId: () => crypto.randomUUID(),
40
+ };
41
+ this.#server.on('send', ({ request, context: { sessions } }) => {
42
+ for (let [session_id, controller] of this.#session.entries()) {
43
+ if (sessions === undefined || sessions.includes(session_id)) {
44
+ controller.enqueue(
45
+ 'data: ' + JSON.stringify(request) + '\n\n',
46
+ );
47
+ }
48
+ }
49
+ });
50
+ }
51
+
52
+ /**
53
+ *
54
+ * @param {Request} request
55
+ * @returns
56
+ */
57
+ async respond(request) {
58
+ const sessions = this.#session;
59
+ const streams = this.#streams;
60
+
61
+ const body = await request.json();
62
+ const session_id =
63
+ request.headers.get('mcp-session-id') ||
64
+ this.#options.getSessionId();
65
+ const controller = sessions.get(session_id);
66
+ const response = await this.#server.receive(body, session_id);
67
+
68
+ if (controller) {
69
+ controller.enqueue('data: ' + JSON.stringify(response) + '\n\n');
70
+ return new Response(null, {
71
+ status: 204,
72
+ headers: {
73
+ 'Content-Type': 'text/event-stream',
74
+ 'mcp-session-id': session_id,
75
+ },
76
+ });
77
+ }
78
+
79
+ const stream = new ReadableStream({
80
+ start(controller) {
81
+ sessions.set(session_id, controller);
82
+ controller.enqueue(
83
+ 'data: ' + JSON.stringify(response) + '\n\n',
84
+ );
85
+ },
86
+ cancel() {
87
+ sessions.delete(session_id);
88
+ },
89
+ });
90
+
91
+ streams.set(session_id, stream);
92
+ return new Response(stream, {
93
+ headers: {
94
+ 'Content-Type': 'text/event-stream',
95
+ 'Cache-Control': 'no-cache',
96
+ Connection: 'keep-alive',
97
+ 'mcp-session-id': session_id,
98
+ },
99
+ status: 200,
100
+ });
101
+ }
102
+ }