1
- import express from "express" ;
2
1
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js" ;
3
2
import { z } from "zod" ;
4
3
import { parseReleases , parseSecurityAnnouncements } from "./util.js" ;
5
4
import fetch from "node-fetch" ;
6
- import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse .js" ;
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio .js" ;
7
6
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
+ } ) ;
44
11
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" ) ,
60
25
} ,
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" ) ,
76
57
} ,
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
+ ] ,
105
73
} ;
74
+ } ,
75
+ ) ;
106
76
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