From 962a9c959ae86a1635afdd996150ce011c3a1805 Mon Sep 17 00:00:00 2001 From: ihrpr Date: Thu, 10 Apr 2025 13:01:09 +0100 Subject: [PATCH] clean up and docs fixes --- src/examples/README.md | 57 +++++++++++++++--- .../server/jsonResponseStreamableHttp.ts | 27 +++++++-- src/examples/server/simpleStreamableHttp.ts | 23 ++++++- src/server/.streamableHttp.test.ts.swp | Bin 16384 -> 0 bytes src/server/streamableHttp.test.ts | 11 ++-- 5 files changed, 99 insertions(+), 19 deletions(-) delete mode 100644 src/server/.streamableHttp.test.ts.swp diff --git a/src/examples/README.md b/src/examples/README.md index 937159c3..cc6af51c 100644 --- a/src/examples/README.md +++ b/src/examples/README.md @@ -20,15 +20,43 @@ The server will start on port 3000. You can test the initialization and tool cal ```bash # Initialize the server and get the session ID from headers -SESSION_ID=$(curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" \ - -d '{"jsonrpc":"2.0","method":"initialize","params":{"capabilities":{}},"id":"1"}' \ +SESSION_ID=$(curl -X POST \ + -H "Content-Type: application/json" \ + -H "Accept: application/json" \ + -H "Accept: text/event-stream" \ + -d '{ + "jsonrpc": "2.0", + "method": "initialize", + "params": { + "capabilities": {}, + "protocolVersion": "2025-03-26", + "clientInfo": { + "name": "test", + "version": "1.0.0" + } + }, + "id": "1" + }' \ -i http://localhost:3000/mcp 2>&1 | grep -i "mcp-session-id" | cut -d' ' -f2 | tr -d '\r') echo "Session ID: $SESSION_ID" # Call the greet tool using the saved session ID -curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" \ +curl -X POST \ + -H "Content-Type: application/json" \ + -H "Accept: application/json" \ + -H "Accept: text/event-stream" \ -H "mcp-session-id: $SESSION_ID" \ - -d '{"jsonrpc":"2.0","method":"mcp.call_tool","params":{"name":"greet","arguments":{"name":"World"}},"id":"2"}' \ + -d '{ + "jsonrpc": "2.0", + "method": "tools/call", + "params": { + "name": "greet", + "arguments": { + "name": "World" + } + }, + "id": "2" + }' \ http://localhost:3000/mcp ``` @@ -52,10 +80,25 @@ The server will start on port 3000. You can test the initialization and tool lis ```bash # First initialize the server and save the session ID to a variable -SESSION_ID=$(curl -X POST -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \ - -d '{"jsonrpc":"2.0","method":"initialize","params":{"capabilities":{}},"id":"1"}' \ +SESSION_ID=$(curl -X POST \ + -H "Content-Type: application/json" \ + -H "Accept: application/json" \ + -H "Accept: text/event-stream" \ + -d '{ + "jsonrpc": "2.0", + "method": "initialize", + "params": { + "capabilities": {}, + "protocolVersion": "2025-03-26", + "clientInfo": { + "name": "test", + "version": "1.0.0" + } + }, + "id": "1" + }' \ -i http://localhost:3000/mcp 2>&1 | grep -i "mcp-session-id" | cut -d' ' -f2 | tr -d '\r') -echo "Session ID: $SESSION_ID" +echo "Session ID: $SESSION_ID # Then list tools using the saved session ID curl -X POST -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \ diff --git a/src/examples/server/jsonResponseStreamableHttp.ts b/src/examples/server/jsonResponseStreamableHttp.ts index 9b053631..1d322112 100644 --- a/src/examples/server/jsonResponseStreamableHttp.ts +++ b/src/examples/server/jsonResponseStreamableHttp.ts @@ -149,10 +149,29 @@ function isInitializeRequest(body: unknown): boolean { // Start the server const PORT = 3000; app.listen(PORT, () => { - console.log(`MCP Streamable HTTP Server with JSON responses listening on port ${PORT}`); - console.log(`Server is running. Press Ctrl+C to stop.`); - console.log(`Initialize with: curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -d '{"jsonrpc":"2.0","method":"initialize","params":{"capabilities":{}},"id":"1"}' http://localhost:${PORT}/mcp`); - console.log(`Then call tool with: curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "mcp-session-id: YOUR_SESSION_ID" -d '{"jsonrpc":"2.0","method":"mcp.call_tool","params":{"name":"greet","arguments":{"name":"World"}},"id":"2"}' http://localhost:${PORT}/mcp`); + console.log(`MCP Streamable HTTP Server listening on port ${PORT}`); + console.log(`Initialize session with the command below id you are using curl for testing: + ----------------------------- + SESSION_ID=$(curl -X POST \ + -H "Content-Type: application/json" \ + -H "Accept: application/json" \ + -H "Accept: text/event-stream" \ + -d '{ + "jsonrpc": "2.0", + "method": "initialize", + "params": { + "capabilities": {}, + "protocolVersion": "2025-03-26", + "clientInfo": { + "name": "test", + "version": "1.0.0" + } + }, + "id": "1" + }' \ + -i http://localhost:3000/mcp 2>&1 | grep -i "mcp-session-id" | cut -d' ' -f2 | tr -d '\\r') + echo "Session ID: $SESSION_ID" + -----------------------------`); }); // Handle server shutdown diff --git a/src/examples/server/simpleStreamableHttp.ts b/src/examples/server/simpleStreamableHttp.ts index 74c3ef53..5b228cbd 100644 --- a/src/examples/server/simpleStreamableHttp.ts +++ b/src/examples/server/simpleStreamableHttp.ts @@ -186,7 +186,28 @@ function isInitializeRequest(body: unknown): boolean { const PORT = 3000; app.listen(PORT, () => { console.log(`MCP Streamable HTTP Server listening on port ${PORT}`); - console.log(`Test with: curl -X POST -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" -d '{"jsonrpc":"2.0","method":"initialize","params":{"capabilities":{}},"id":"1"}' http://localhost:${PORT}/mcp`); + console.log(`Initialize session with the command below id you are using curl for testing: + ----------------------------- + SESSION_ID=$(curl -X POST \ + -H "Content-Type: application/json" \ + -H "Accept: application/json" \ + -H "Accept: text/event-stream" \ + -d '{ + "jsonrpc": "2.0", + "method": "initialize", + "params": { + "capabilities": {}, + "protocolVersion": "2025-03-26", + "clientInfo": { + "name": "test", + "version": "1.0.0" + } + }, + "id": "1" + }' \ + -i http://localhost:3000/mcp 2>&1 | grep -i "mcp-session-id" | cut -d' ' -f2 | tr -d '\\r') + echo "Session ID: $SESSION_ID" + -----------------------------`); }); // Handle server shutdown diff --git a/src/server/.streamableHttp.test.ts.swp b/src/server/.streamableHttp.test.ts.swp deleted file mode 100644 index 4ab20fa3582429b76d0a1f7da19f2c2ec77faf7f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeI3%a0tz9mg9&c=~~Yxqw89;lK>c^z5u5h_SI@*T62);?+KgWXnOl-8DPC?x}8f z*X(*@1{5Xv0}{gl@sL6ULXH%H00B`BNF)l8LZlpkLoP^>B7q2r0(JrkIN$23>gj2E zy@nHzYU!gL_phGcs`}NV%S&VTOwW*;?Use-Qp>va52u!Ie4}n{{j+8Hj5*@W)F z1$2t@$mTTRHcv{^G%t+;MuD?cV5_xryfr35UsJi7T=uQSvyEuN83l|2MggOMQNSo* z6fg=H1&jh;0|j`r&3XX!I9Ju>KK1vU!r#;C3Dx^S!TzbT`|ACB1^Xw;9;x?>Dg&L* zf0aE}_TLul|50{*3UHGuu#W$+vL8|LPZaDQDf_CjU-=FBL&yKOvNL7>Nx}XvW!EPg zrwjH|%HC7)SI!$v|D>`9%Klct{-Lt3Df{F%N8^8>?B7=QUlr`?wfSQdFbWt2i~>dh zqkvJsC}0#Y3K#{90!D#S1su<^&WB~2*e~Pzzuy19y4|ww2eY6GKHX+ne+93AUjhzX zFaxdwTfnKUmh}esHFycU2p$AS!2xg^*bDZ6Yr(r)5FVTWKLpN zfBy}50vrJya0o1d1#l@ig)@TRgJ;2U@B=Ubt^nJ?M>vD{2lzX94Lk!L10lEp)W9cb z*AKuy!OP&s;78yP*a@}+(e_&b9+E!2&^Unys!4BC7HavNQ-;3cd9x3vNVNQ=CsJOh-kd{iF;C7EnqGgX&@?u6#?DWd3IP3+v5(4yD4bkP;`N!?+yaA`B^{)=ik$vSgM zwMK5)gVfVRhW#to(FV1gToqh`Y9#1k(6r%u=mW!ktX@xhx?+UL;bDhMqC}JEJPwM( zmeHt0J>z3zI=C83BLg`bq+%dVIjeMnQa0%X0rkG?^KMn8^F@YXF^-*;Uk=)RwBs}C z3GbnD*>vD;G;WJoZQvz3PLK@UL@`pae2dKcObLe#cm@X{os;TXUpX)FfA1tlnr}l- zk@E-3l+hSo6&@iSw`6)B@%m!Q7&?*EIef+`5aOk`9hYdeGOr@fI1IB(4^9)obKXOD zy%lls(G~OVZ9yO$c_Qaur^9J{yW@7NC6RPw>DpV`BV_(Ho4~RuM7Ze8gO>SK=|<8y z1YM>uPfGuj_cVoWCD##p<|FQuqK0u>iGUS=7xoqxr}tIWn94jO(q?px%nxsf zE-cK>r`JXo^ui%^iAxkK4O$c_P|FatLTD2?-;3#RXp#kPlT>~oJN0(UEc7yBkB*R) zt9|Ao?Xq&Mf2OkMJHh~^iYf2M41Z*yM7{$|H>A%Fvv7@xpPLkups#YVHlxrt4clQ_ z*5wT?wCD3&@N@+@mzHIr>!TaWZY352S&|!s!TNFYKNs@326Rh?Q;{ z!0(Xi$V()eOYL3}9kMC^LI$EfT2z^%8i{kN0AhR(YVD#9X|f{Mz4@`0=`x3%KQrr8-g~!Kl6>g-Gx>xpE4z3zel(Fn z?dpZznkAFgtc#oVD1q4jKaaii6!zR=|F6F4jup=I0z1aYr#3-9qjwx z1W$t=*bQ3XH1_u=!H3{=@C5i7SOwRE>wwt*KM4+kc`yS+Iqn4JF$x$3i~>dhqkvJs zC}0#Y3K#{xyb9o0hhU4SPj~cReQaF~nc5hOZFJ3MA^t~i*&#H(U5IZ&6WB58Q&qW@ z-YBKgRB+gpP_HZz>;&iK(SARngm)>CVbgCwIKxZ0*!YH>)T9cN+P>hybzP;uL0M+8 zx0j`eX(L|}&yR7;AFS(avx6o(WIg#+3LEedRkcoXn?7Pf(CnBdJAxm!(FO { it("should return JSON response for a single request", async () => { const requestMessage: JSONRPCMessage = { jsonrpc: "2.0", - method: "test", + method: "tools/list", params: {}, id: "test-req-id", }; @@ -1306,8 +1306,8 @@ describe("StreamableHTTPServerTransport", () => { it("should return JSON response for batch requests", async () => { const batchMessages: JSONRPCMessage[] = [ - { jsonrpc: "2.0", method: "test1", params: {}, id: "req1" }, - { jsonrpc: "2.0", method: "test2", params: {}, id: "req2" }, + { jsonrpc: "2.0", method: "tools/list", params: {}, id: "req1" }, + { jsonrpc: "2.0", method: "tools/call", params: {}, id: "req2" }, ]; const req = createMockRequest({ @@ -1334,9 +1334,6 @@ describe("StreamableHTTPServerTransport", () => { await jsonResponseTransport.handleRequest(req, mockResponse); - // Wait for all promises to resolve - give it enough time - await new Promise(resolve => setTimeout(resolve, 100)); - // Should respond with application/json header expect(mockResponse.writeHead).toHaveBeenCalledWith( 200, @@ -1350,7 +1347,7 @@ describe("StreamableHTTPServerTransport", () => { const responseJson = JSON.parse(mockResponse.end.mock.calls[0][0] as string); expect(Array.isArray(responseJson)).toBe(true); expect(responseJson).toHaveLength(2); - + // Check each response exists separately without assuming order expect(responseJson).toContainEqual(expect.objectContaining({ id: "req1", result: { value: "result-for-req1" } })); expect(responseJson).toContainEqual(expect.objectContaining({ id: "req2", result: { value: "result-for-req2" } }));