@@ -11,9 +11,10 @@ This document provides Node/TypeScript-specific best practices and examples for
1111### Key Imports
1212``` typescript
1313import { McpServer } from " @modelcontextprotocol/sdk/server/mcp.js" ;
14+ import { StreamableHTTPServerTransport } from " @modelcontextprotocol/sdk/server/streamableHttp.js" ;
1415import { StdioServerTransport } from " @modelcontextprotocol/sdk/server/stdio.js" ;
16+ import express from " express" ;
1517import { z } from " zod" ;
16- import axios , { AxiosError } from " axios" ;
1718```
1819
1920### Server Initialization
@@ -26,9 +27,22 @@ const server = new McpServer({
2627
2728### Tool Registration Pattern
2829``` typescript
29- server .registerTool (" tool_name" , {... config }, async (params ) => {
30- // Implementation
31- });
30+ server .registerTool (
31+ " tool_name" ,
32+ {
33+ title: " Tool Display Name" ,
34+ description: " What the tool does" ,
35+ inputSchema: { param: z .string () },
36+ outputSchema: { result: z .string () }
37+ },
38+ async ({ param }) => {
39+ const output = { result: ` Processed: ${param } ` };
40+ return {
41+ content: [{ type: " text" , text: JSON .stringify (output ) }],
42+ structuredContent: output // Modern pattern for structured data
43+ };
44+ }
45+ );
3246```
3347
3448---
@@ -41,6 +55,11 @@ The official MCP TypeScript SDK provides:
4155- Zod schema integration for runtime input validation
4256- Type-safe tool handler implementations
4357
58+ ** IMPORTANT - Use Modern APIs Only:**
59+ - ** DO use** : ` server.registerTool() ` , ` server.registerResource() ` , ` server.registerPrompt() `
60+ - ** DO NOT use** : Old deprecated APIs such as ` server.tool() ` , ` server.setRequestHandler(ListToolsRequestSchema, ...) ` , or manual handler registration
61+ - The ` register* ` methods provide better type safety, automatic schema handling, and are the recommended approach
62+
4463See the MCP SDK documentation in the references for complete details.
4564
4665## Server Naming Convention
@@ -204,55 +223,43 @@ Error Handling:
204223 };
205224 }
206225
207- // Format response based on requested format
208- let result: string ;
226+ // Prepare structured output
227+ const output = {
228+ total ,
229+ count: users .length ,
230+ offset: params .offset ,
231+ users: users .map ((user : any ) => ({
232+ id: user .id ,
233+ name: user .name ,
234+ email: user .email ,
235+ ... (user .team ? { team: user .team } : {}),
236+ active: user .active ?? true
237+ })),
238+ has_more: total > params .offset + users .length ,
239+ ... (total > params .offset + users .length ? {
240+ next_offset: params .offset + users .length
241+ } : {})
242+ };
209243
244+ // Format text representation based on requested format
245+ let textContent: string ;
210246 if (params .response_format === ResponseFormat .MARKDOWN ) {
211- // Human-readable markdown format
212- const lines: string [] = [` # User Search Results: '${params .query }' ` , " " ];
213- lines .push (` Found ${total } users (showing ${users .length }) ` );
214- lines .push (" " );
215-
247+ const lines = [` # User Search Results: '${params .query }' ` , " " ,
248+ ` Found ${total } users (showing ${users .length }) ` , " " ];
216249 for (const user of users ) {
217250 lines .push (` ## ${user .name } (${user .id }) ` );
218251 lines .push (` - **Email**: ${user .email } ` );
219- if (user .team ) {
220- lines .push (` - **Team**: ${user .team } ` );
221- }
252+ if (user .team ) lines .push (` - **Team**: ${user .team } ` );
222253 lines .push (" " );
223254 }
224-
225- result = lines .join (" \n " );
226-
255+ textContent = lines .join (" \n " );
227256 } else {
228- // Machine-readable JSON format
229- const response: any = {
230- total ,
231- count: users .length ,
232- offset: params .offset ,
233- users: users .map ((user : any ) => ({
234- id: user .id ,
235- name: user .name ,
236- email: user .email ,
237- ... (user .team ? { team: user .team } : {}),
238- active: user .active ?? true
239- }))
240- };
241-
242- // Add pagination info if there are more results
243- if (total > params .offset + users .length ) {
244- response .has_more = true ;
245- response .next_offset = params .offset + users .length ;
246- }
247-
248- result = JSON .stringify (response , null , 2 );
257+ textContent = JSON .stringify (output , null , 2 );
249258 }
250259
251260 return {
252- content: [{
253- type: " text" ,
254- text: result
255- }]
261+ content: [{ type: " text" , text: textContent }],
262+ structuredContent: output // Modern pattern for structured data
256263 };
257264 } catch (error ) {
258265 return {
@@ -695,27 +702,57 @@ server.registerTool(
695702);
696703
697704// Main function
698- async function main() {
699- // Verify environment variables if needed
705+ // For stdio (local):
706+ async function runStdio() {
700707 if (! process .env .EXAMPLE_API_KEY ) {
701708 console .error (" ERROR: EXAMPLE_API_KEY environment variable is required" );
702709 process .exit (1 );
703710 }
704711
705- // Create transport
706712 const transport = new StdioServerTransport ();
707-
708- // Connect server to transport
709713 await server .connect (transport );
714+ console .error (" MCP server running via stdio" );
715+ }
710716
711- console .error (" Example MCP server running via stdio" );
717+ // For streamable HTTP (remote):
718+ async function runHTTP() {
719+ if (! process .env .EXAMPLE_API_KEY ) {
720+ console .error (" ERROR: EXAMPLE_API_KEY environment variable is required" );
721+ process .exit (1 );
722+ }
723+
724+ const app = express ();
725+ app .use (express .json ());
726+
727+ app .post (' /mcp' , async (req , res ) => {
728+ const transport = new StreamableHTTPServerTransport ({
729+ sessionIdGenerator: undefined ,
730+ enableJsonResponse: true
731+ });
732+ res .on (' close' , () => transport .close ());
733+ await server .connect (transport );
734+ await transport .handleRequest (req , res , req .body );
735+ });
736+
737+ const port = parseInt (process .env .PORT || ' 3000' );
738+ app .listen (port , () => {
739+ console .error (` MCP server running on http://localhost:${port }/mcp ` );
740+ });
712741}
713742
714- // Run the server
715- main ().catch ((error ) => {
716- console .error (" Server error:" , error );
717- process .exit (1 );
718- });
743+ // Choose transport based on environment
744+ const transport = process .env .TRANSPORT || ' stdio' ;
745+ if (transport === ' http' ) {
746+ runHTTP ().catch (error => {
747+ console .error (" Server error:" , error );
748+ process .exit (1 );
749+ });
750+ } else {
751+ runStdio ().catch (error => {
752+ console .error (" Server error:" , error );
753+ process .exit (1 );
754+ });
755+ }
719756```
720757
721758---
@@ -777,30 +814,47 @@ server.registerResourceList(async () => {
777814- ** Resources** : When data is relatively static or template-based
778815- ** Tools** : When operations have side effects or complex workflows
779816
780- ### Multiple Transport Options
817+ ### Transport Options
818+
819+ The TypeScript SDK supports two main transport mechanisms:
781820
782- The TypeScript SDK supports different transport mechanisms:
821+ #### Streamable HTTP (Recommended for Remote Servers)
783822
784823``` typescript
785- import { StdioServerTransport } from " @modelcontextprotocol/sdk/server/stdio.js" ;
786- import { SSEServerTransport } from " @modelcontextprotocol/sdk/server/sse.js" ;
824+ import { StreamableHTTPServerTransport } from " @modelcontextprotocol/sdk/server/streamableHttp.js" ;
825+ import express from " express" ;
826+
827+ const app = express ();
828+ app .use (express .json ());
829+
830+ app .post (' /mcp' , async (req , res ) => {
831+ // Create new transport for each request (stateless, prevents request ID collisions)
832+ const transport = new StreamableHTTPServerTransport ({
833+ sessionIdGenerator: undefined ,
834+ enableJsonResponse: true
835+ });
787836
788- // Stdio transport (default - for CLI tools)
789- const stdioTransport = new StdioServerTransport ();
790- await server .connect (stdioTransport );
837+ res .on (' close' , () => transport .close ());
838+
839+ await server .connect (transport );
840+ await transport .handleRequest (req , res , req .body );
841+ });
791842
792- // SSE transport (for real-time web updates)
793- const sseTransport = new SSEServerTransport (" /message" , response );
794- await server .connect (sseTransport );
843+ app .listen (3000 );
844+ ```
845+
846+ #### stdio (For Local Integrations)
847+
848+ ``` typescript
849+ import { StdioServerTransport } from " @modelcontextprotocol/sdk/server/stdio.js" ;
795850
796- // HTTP transport (for web services)
797- // Configure based on your HTTP framework integration
851+ const transport = new StdioServerTransport ();
852+ await server . connect ( transport );
798853```
799854
800- ** Transport selection guide:**
801- - ** Stdio** : Command-line tools, subprocess integration, local development
802- - ** HTTP** : Web services, remote access, multiple simultaneous clients
803- - ** SSE** : Real-time updates, server-push notifications, web dashboards
855+ ** Transport selection:**
856+ - ** Streamable HTTP** : Web services, remote access, multiple clients
857+ - ** stdio** : Command-line tools, local development, subprocess integration
804858
805859### Notification Support
806860
@@ -889,7 +943,7 @@ Before finalizing your Node/TypeScript MCP server implementation, ensure:
889943
890944### Advanced Features (where applicable)
891945- [ ] Resources registered for appropriate data endpoints
892- - [ ] Appropriate transport configured (stdio, HTTP, SSE )
946+ - [ ] Appropriate transport configured (stdio or streamable HTTP )
893947- [ ] Notifications implemented for dynamic server capabilities
894948- [ ] Type-safe with SDK interfaces
895949
0 commit comments