Skip to content

Commit ec7faa0

Browse files
committed
Convert MCP server to be a stdio server for demo simplicity
1 parent 38f2097 commit ec7faa0

File tree

4 files changed

+78
-173
lines changed

4 files changed

+78
-173
lines changed

package-lock.json

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
"description": "",
1515
"dependencies": {
1616
"@modelcontextprotocol/sdk": "^1.17.5",
17-
"express": "^5.1.0",
1817
"node-fetch": "^3.3.2"
1918
},
2019
"devDependencies": {

server.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,20 @@
22
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
33
"name": "io.github.dockersamples/mcp-docker-release-information",
44
"description": "MCP server providing Docker Desktop release notes and security information.",
5+
"status": "active",
6+
"repository": {
7+
"source": "github",
8+
"url": "https://github.com/dockersamples/mcp-docker-release-information"
9+
},
10+
"version": "1.0.0",
511
"packages": [
612
{
713
"registry_type": "oci",
814
"identifier": "dockersamples/mcp-docker-release-information",
9-
"version": "1.0.0"
15+
"version": "1.0.0",
16+
"transport": {
17+
"type": "stdio"
18+
}
1019
}
1120
]
1221
}

src/index.js

Lines changed: 68 additions & 170 deletions
Original file line numberDiff line numberDiff line change
@@ -1,180 +1,78 @@
1-
import express from "express";
21
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
32
import { z } from "zod";
43
import { parseReleases, parseSecurityAnnouncements } from "./util.js";
54
import 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

Comments
 (0)