1- import express from "express" ;
21import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js" ;
32import { z } from "zod" ;
43import { parseReleases , parseSecurityAnnouncements } from "./util.js" ;
54import fetch from "node-fetch" ;
6- import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse .js" ;
5+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio .js" ;
76
8- function getServer ( ) {
9- const server = new McpServer ( {
10- name : "demo-server" ,
11- version : "1.0.0" ,
12- } ) ;
13-
14- server . registerTool (
15- "get-desktop-releases" ,
16- {
17- title : "Get Docker Desktop Release Notes" ,
18- description : "Get information about the latest Docker Desktop releases" ,
19- inputSchema : {
20- limit : z
21- . number ( )
22- . min ( 1 )
23- . max ( 10 )
24- . default ( 6 )
25- . optional ( )
26- . describe ( "Number of releases to return" ) ,
27- } ,
28- } ,
29- async ( { limit = 6 } ) => {
30- const data = (
31- await fetch ( "https://docs.docker.com/desktop/release-notes/index.md" )
32- ) . text ( ) ;
33-
34- return {
35- content : [
36- {
37- type : "text" ,
38- text : parseReleases ( await data , limit ) ,
39- } ,
40- ] ,
41- } ;
42- } ,
43- ) ;
7+ const server = new McpServer ( {
8+ name : "demo-server" ,
9+ version : "1.0.0" ,
10+ } ) ;
4411
45- server . registerTool (
46- "get-security-details" ,
47- {
48- title : "Get Docker Desktop Security Details" ,
49- description :
50- "Get information about the latest Docker Desktop security updates" ,
51- inputSchema : {
52- limit : z
53- . number ( )
54- . min ( 1 )
55- . max ( 10 )
56- . default ( 6 )
57- . optional ( )
58- . describe ( "Number of security updates to return" ) ,
59- } ,
12+ server . registerTool (
13+ "get-desktop-releases" ,
14+ {
15+ title : "Get Docker Desktop Release Notes" ,
16+ description : "Get information about the latest Docker Desktop releases" ,
17+ inputSchema : {
18+ limit : z
19+ . number ( )
20+ . min ( 1 )
21+ . max ( 10 )
22+ . default ( 6 )
23+ . optional ( )
24+ . describe ( "Number of releases to return" ) ,
6025 } ,
61- async ( { limit = 6 } ) => {
62- const data = (
63- await fetch (
64- "https://docs.docker.com/security/security-announcements/index.md" ,
65- )
66- ) . text ( ) ;
67-
68- return {
69- content : [
70- {
71- type : "text" ,
72- text : parseSecurityAnnouncements ( await data , limit ) ,
73- } ,
74- ] ,
75- } ;
26+ } ,
27+ async ( { limit = 6 } ) => {
28+ const data = (
29+ await fetch ( "https://docs.docker.com/desktop/release-notes/index.md" )
30+ ) . text ( ) ;
31+
32+ return {
33+ content : [
34+ {
35+ type : "text" ,
36+ text : parseReleases ( await data , limit ) ,
37+ } ,
38+ ] ,
39+ } ;
40+ } ,
41+ ) ;
42+
43+ server . registerTool (
44+ "get-security-details" ,
45+ {
46+ title : "Get Docker Desktop Security Details" ,
47+ description :
48+ "Get information about the latest Docker Desktop security updates" ,
49+ inputSchema : {
50+ limit : z
51+ . number ( )
52+ . min ( 1 )
53+ . max ( 10 )
54+ . default ( 6 )
55+ . optional ( )
56+ . describe ( "Number of security updates to return" ) ,
7657 } ,
77- ) ;
78-
79- return server ;
80- }
81-
82- const app = express ( ) ;
83- app . use ( express . json ( ) ) ;
84-
85- // Store transports by session ID
86- const transports = { } ;
87-
88- // SSE endpoint for establishing the stream
89- app . get ( "/mcp" , async ( req , res ) => {
90- console . log ( "Received GET request to /sse (establishing SSE stream)" ) ;
91-
92- try {
93- // Create a new SSE transport for the client
94- // The endpoint for POST messages is '/messages'
95- const transport = new SSEServerTransport ( "/messages" , res ) ;
96-
97- // Store the transport by session ID
98- const sessionId = transport . sessionId ;
99- transports [ sessionId ] = transport ;
100-
101- // Set up onclose handler to clean up transport when closed
102- transport . onclose = ( ) => {
103- console . log ( `SSE transport closed for session ${ sessionId } ` ) ;
104- delete transports [ sessionId ] ;
58+ } ,
59+ async ( { limit = 6 } ) => {
60+ const data = (
61+ await fetch (
62+ "https://docs.docker.com/security/security-announcements/index.md" ,
63+ )
64+ ) . text ( ) ;
65+
66+ return {
67+ content : [
68+ {
69+ type : "text" ,
70+ text : parseSecurityAnnouncements ( await data , limit ) ,
71+ } ,
72+ ] ,
10573 } ;
74+ } ,
75+ ) ;
10676
107- // Connect the transport to the MCP server
108- const server = getServer ( ) ;
109- await server . connect ( transport ) ;
110-
111- console . log ( `Established SSE stream with session ID: ${ sessionId } ` ) ;
112- } catch ( error ) {
113- console . error ( "Error establishing SSE stream:" , error ) ;
114- if ( ! res . headersSent ) {
115- res . status ( 500 ) . send ( "Error establishing SSE stream" ) ;
116- }
117- }
118- } ) ;
119-
120- // Messages endpoint for receiving client JSON-RPC requests
121- app . post ( "/messages" , async ( req , res ) => {
122- console . log ( "Received POST request to /messages" ) ;
123-
124- // Extract session ID from URL query parameter
125- // In the SSE protocol, this is added by the client based on the endpoint event
126- const sessionId = req . query . sessionId ;
127-
128- if ( ! sessionId ) {
129- console . error ( "No session ID provided in request URL" ) ;
130- res . status ( 400 ) . send ( "Missing sessionId parameter" ) ;
131- return ;
132- }
133-
134- const transport = transports [ sessionId ] ;
135- if ( ! transport ) {
136- console . error ( `No active transport found for session ID: ${ sessionId } ` ) ;
137- res . status ( 404 ) . send ( "Session not found" ) ;
138- return ;
139- }
140-
141- try {
142- // Handle the POST message with the transport
143- await transport . handlePostMessage ( req , res , req . body ) ;
144- } catch ( error ) {
145- console . error ( "Error handling request:" , error ) ;
146- if ( ! res . headersSent ) {
147- res . status ( 500 ) . send ( "Error handling request" ) ;
148- }
149- }
150- } ) ;
151-
152- // Start the server
153- const PORT = 3000 ;
154- app . listen ( PORT , ( error ) => {
155- if ( error ) {
156- console . error ( "Failed to start server:" , error ) ;
157- process . exit ( 1 ) ;
158- }
159- console . log (
160- `Simple SSE Server (deprecated protocol version 2024-11-05) listening on port ${ PORT } ` ,
161- ) ;
162- } ) ;
163-
164- // Handle server shutdown
165- process . on ( "SIGINT" , async ( ) => {
166- console . log ( "Shutting down server..." ) ;
167-
168- // Close all active transports to properly clean up resources
169- for ( const sessionId in transports ) {
170- try {
171- console . log ( `Closing transport for session ${ sessionId } ` ) ;
172- await transports [ sessionId ] . close ( ) ;
173- delete transports [ sessionId ] ;
174- } catch ( error ) {
175- console . error ( `Error closing transport for session ${ sessionId } :` , error ) ;
176- }
177- }
178- console . log ( "Server shutdown complete" ) ;
179- process . exit ( 0 ) ;
180- } ) ;
77+ const transport = new StdioServerTransport ( ) ;
78+ await server . connect ( transport ) ;
0 commit comments