From 71e8f972b791cbea5b04c66acfa2120e485fea8b Mon Sep 17 00:00:00 2001 From: Jose Erick Carreon Date: Fri, 12 Jun 2026 11:58:25 -0600 Subject: [PATCH 1/6] feat: Added new options --- src/index.ts | 12 ++++++++++-- src/modules/server.ts | 9 +++++++-- src/tools/searchVitePressDocs.ts | 6 +++--- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/index.ts b/src/index.ts index 40cf9ba..75711ee 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,11 +7,13 @@ import { runServer, restartServer } from "./modules/server"; export type MCPPluginOptions = { port: number; specPath?: string; // OpenAPI specification path + toolSearchName?: string; // Name of the search tool + toolSearchDescription?: string; // Description of the search tool }; let serverBootFlg = false; -export function MCPPlugin(inlineOptions?: Partial): Plugin { +function MCPPlugin(inlineOptions?: Partial): Plugin { return { name: "vitepress-plugin-mcp", enforce: "post", @@ -62,7 +64,7 @@ export function MCPPlugin(inlineOptions?: Partial): Plugin { return; } - runServer(inlineOptions?.port); + runServer(inlineOptions?.port, false, inlineOptions?.toolSearchName, inlineOptions?.toolSearchDescription); }, 1500); }, configurePreviewServer(server) { @@ -77,3 +79,9 @@ export function MCPPlugin(inlineOptions?: Partial): Plugin { }, } satisfies Plugin; } + +export { + MCPPlugin, + runServer, + restartServer, +} diff --git a/src/modules/server.ts b/src/modules/server.ts index 5ab520d..328798c 100644 --- a/src/modules/server.ts +++ b/src/modules/server.ts @@ -24,7 +24,12 @@ let app = express(); let appServer: Server; -export function runServer(port = 3000, buildMode = false) { +let toolSearchName: string | undefined; +let toolSearchDescription: string | undefined; + +export function runServer(port = 3000, buildMode = false, _toolSearchName?: string, _toolSearchDescription?: string) { + toolSearchName = _toolSearchName; + toolSearchDescription = _toolSearchDescription; console.log( styleText( "blue", @@ -41,7 +46,7 @@ export function runServer(port = 3000, buildMode = false) { version: "1.0.0", }); - toolSearchVitePressDocs(mcpServer, buildMode); + toolSearchVitePressDocs(mcpServer, buildMode, toolSearchName, toolSearchDescription); promptBasic(mcpServer); // NOTE:Handle POST requests for client-to-server communication diff --git a/src/tools/searchVitePressDocs.ts b/src/tools/searchVitePressDocs.ts index 75f50d6..36e1747 100644 --- a/src/tools/searchVitePressDocs.ts +++ b/src/tools/searchVitePressDocs.ts @@ -2,10 +2,10 @@ import { z } from "zod"; import { searchByMiniSearch } from "../modules/search/miniSearch"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -export function toolSearchVitePressDocs(mcp: McpServer, buildMode = false) { +export function toolSearchVitePressDocs(mcp: McpServer, buildMode = false, toolSearchName?: string, toolSearchDescription?: string) { mcp.tool( - "search_vitepress_docs", - "Search VitePress Documents For This Product. Extract up to five keywords each English and native language, and define all of them as single words. e.g. Vitepress, API, Specification,Extensions etc.", + toolSearchName || "search_vitepress_docs", + toolSearchDescription || "Search VitePress Documents For This Product. Extract up to five keywords each English and native language, and define all of them as single words. e.g. Vitepress, API, Specification,Extensions etc.", { keywords: z.array(z.string()) }, async ({ keywords }) => { console.log("START: search-docs"); From 02ee4217068ce0a5163bbbeeb77f3bbcf743f8bc Mon Sep 17 00:00:00 2001 From: Jose Erick Carreon Date: Fri, 12 Jun 2026 15:29:46 -0600 Subject: [PATCH 2/6] fix: MCP-SDK only allows one active connection per server --- src/modules/server.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/modules/server.ts b/src/modules/server.ts index 328798c..f1072aa 100644 --- a/src/modules/server.ts +++ b/src/modules/server.ts @@ -220,6 +220,13 @@ const initializeStreamableTransport = async () => { */ const initializeMCPServer = async (transport: StreamableHTTPServerTransport | SSEServerTransport, res: express.Response) => { try { + // Close any existing transport before connecting a new one + // (MCP SDK only allows one active connection per server) + try { + await mcpServer.close(); + } catch { + // Server may not be connected yet; that's fine + } // Connect to the MCP server await mcpServer.connect(transport); } catch (error) { From 9c5a713bda6199b3a9923fa353b5a782c45f2d26 Mon Sep 17 00:00:00 2001 From: Jose Erick Carreon Date: Fri, 12 Jun 2026 16:28:06 -0600 Subject: [PATCH 3/6] feat: Added get request for /mpc --- src/modules/server.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/server.ts b/src/modules/server.ts index f1072aa..b758224 100644 --- a/src/modules/server.ts +++ b/src/modules/server.ts @@ -49,6 +49,11 @@ export function runServer(port = 3000, buildMode = false, _toolSearchName?: stri toolSearchVitePressDocs(mcpServer, buildMode, toolSearchName, toolSearchDescription); promptBasic(mcpServer); + app.get("/mcp", (req, res) => { + console.log("GET request received at /mcp - this endpoint is reserved for Agent communication. Responding with 403 Forbidden."); + res.status(403).send("Use Agent can access this endpoint."); + }); + // NOTE:Handle POST requests for client-to-server communication app.post("/mcp", async (req, res) => { console.log("POST request received at /mcp"); From 8d9258acd942dd383825c6c86594dea114903e10 Mon Sep 17 00:00:00 2001 From: Jose Erick Carreon Date: Fri, 12 Jun 2026 17:42:29 -0600 Subject: [PATCH 4/6] fix: Bug finish session and loop transport see --- src/modules/server.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/modules/server.ts b/src/modules/server.ts index b758224..57946a0 100644 --- a/src/modules/server.ts +++ b/src/modules/server.ts @@ -49,9 +49,9 @@ export function runServer(port = 3000, buildMode = false, _toolSearchName?: stri toolSearchVitePressDocs(mcpServer, buildMode, toolSearchName, toolSearchDescription); promptBasic(mcpServer); - app.get("/mcp", (req, res) => { - console.log("GET request received at /mcp - this endpoint is reserved for Agent communication. Responding with 403 Forbidden."); - res.status(403).send("Use Agent can access this endpoint."); + app.get("/mcp", async (req, res) => { + console.log("GET request received at /mcp"); + await callStreamableServer(req, res); }); // NOTE:Handle POST requests for client-to-server communication @@ -60,10 +60,11 @@ export function runServer(port = 3000, buildMode = false, _toolSearchName?: stri await callStreamableServer(req, res); }); - // // Handle GET requests for Streamable HTTP sessions - // app.get("/mcp", async (req, res) => { - // await callStreamableServer(req, res); - // }); + // Handle DELETE requests for session termination (StreamableHTTP) + app.delete("/mcp", async (req, res) => { + console.log("DELETE request received at /mcp"); + await callStreamableServer(req, res); + }); // Handle GET requests for server-to-client notifications via SSE app.get("/mcp/__sse", handleSSESessionRequest); @@ -116,7 +117,7 @@ const callStreamableServer = async (req: express.Request, res: express.Response) if (sessionId && transports.streamable[sessionId]) { // Reuse existing transport transport = transports.streamable[sessionId]; - } else if (!sessionId && isInitializeRequest(req.body)) { + } else if (!sessionId && req.body && isInitializeRequest(req.body)) { console.log("Initializing new transport"); transport = await initializeStreamableTransport(); @@ -259,7 +260,7 @@ async function initializeServers() { } } for (const sessionId in transports.sse) { - const transport = transports.streamable[sessionId]; + const transport = transports.sse[sessionId]; if (transport) { await transport.close(); console.log(`Transport closed for session ID: ${sessionId}`); From 9a32dcac101567efb633ecdbe33c26b7c457af4f Mon Sep 17 00:00:00 2001 From: Jose Erick Carreon Date: Fri, 12 Jun 2026 18:08:11 -0600 Subject: [PATCH 5/6] feat: Added duality between browser and session --- src/modules/server.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/modules/server.ts b/src/modules/server.ts index 57946a0..3256f65 100644 --- a/src/modules/server.ts +++ b/src/modules/server.ts @@ -49,9 +49,14 @@ export function runServer(port = 3000, buildMode = false, _toolSearchName?: stri toolSearchVitePressDocs(mcpServer, buildMode, toolSearchName, toolSearchDescription); promptBasic(mcpServer); + // GET /mcp: SSE streaming (with session ID) or server metadata (without session ID) app.get("/mcp", async (req, res) => { - console.log("GET request received at /mcp"); - await callStreamableServer(req, res); + const sessionId = req.headers["mcp-session-id"] as string | undefined; + if (!sessionId || !transports.streamable[sessionId]) { + res.status(403).send("Use Agent can access this endpoint."); + } else { + await callStreamableServer(req, res); + } }); // NOTE:Handle POST requests for client-to-server communication From 6f32f4b8d16fd8debaf928d87373706ea6f25be5 Mon Sep 17 00:00:00 2001 From: Jose Erick Carreon Date: Fri, 12 Jun 2026 20:01:56 -0600 Subject: [PATCH 6/6] feat: Added options text default --- src/index.ts | 5 ++- src/modules/server.ts | 59 +++++++++++++++++++++++--------- src/tools/searchVitePressDocs.ts | 6 ++-- 3 files changed, 50 insertions(+), 20 deletions(-) diff --git a/src/index.ts b/src/index.ts index 75711ee..530f41a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,8 @@ import { runServer, restartServer } from "./modules/server"; export type MCPPluginOptions = { port: number; specPath?: string; // OpenAPI specification path + serverName?: string; // MCP server name for discovery + serverVersion?: string; // MCP server version for discovery toolSearchName?: string; // Name of the search tool toolSearchDescription?: string; // Description of the search tool }; @@ -64,7 +66,8 @@ function MCPPlugin(inlineOptions?: Partial): Plugin { return; } - runServer(inlineOptions?.port, false, inlineOptions?.toolSearchName, inlineOptions?.toolSearchDescription); + const { port, specPath, ...serverOpts } = inlineOptions ?? {}; + runServer(port, false, serverOpts); }, 1500); }, configurePreviewServer(server) { diff --git a/src/modules/server.ts b/src/modules/server.ts index 3256f65..23a32ca 100644 --- a/src/modules/server.ts +++ b/src/modules/server.ts @@ -15,21 +15,36 @@ const transports = { streamable: {} as Record, sse: {} as Record, }; + +export interface MCPServerOptions { + /** Server metadata shown in GET /mcp discovery response */ + serverName: string; + serverVersion: string; + /** Search tool configuration */ + toolSearchName: string; + toolSearchDescription: string; +} + +let serverOptions: MCPServerOptions = { + serverName: "VitePress-server", + serverVersion: "1.0.0", + toolSearchName: "search_vitepress_docs", + toolSearchDescription: + "Search VitePress Documents For This Product. Extract up to five keywords each English and native language, and define all of them as single words. e.g. Vitepress, API, Specification,Extensions etc.", +}; + let mcpServer = new McpServer({ - name: "VitePress-server", - version: "1.0.0", + name: serverOptions.serverName, + version: serverOptions.serverVersion, }); let app = express(); let appServer: Server; -let toolSearchName: string | undefined; -let toolSearchDescription: string | undefined; +export function runServer(port = 3000, buildMode = false, options: Partial = {}) { + serverOptions = Object.assign(serverOptions, options); -export function runServer(port = 3000, buildMode = false, _toolSearchName?: string, _toolSearchDescription?: string) { - toolSearchName = _toolSearchName; - toolSearchDescription = _toolSearchDescription; console.log( styleText( "blue", @@ -42,20 +57,33 @@ export function runServer(port = 3000, buildMode = false, _toolSearchName?: stri app.use(express.json()); mcpServer = new McpServer({ - name: "VitePress-server", - version: "1.0.0", + name: serverOptions.serverName, + version: serverOptions.serverVersion, }); - toolSearchVitePressDocs(mcpServer, buildMode, toolSearchName, toolSearchDescription); + toolSearchVitePressDocs(mcpServer, buildMode, serverOptions.toolSearchName, serverOptions.toolSearchDescription); promptBasic(mcpServer); - // GET /mcp: SSE streaming (with session ID) or server metadata (without session ID) + // GET /mcp: SSE streaming (with session ID) or server discovery (without session ID) app.get("/mcp", async (req, res) => { const sessionId = req.headers["mcp-session-id"] as string | undefined; - if (!sessionId || !transports.streamable[sessionId]) { - res.status(403).send("Use Agent can access this endpoint."); - } else { + if (sessionId && transports.streamable[sessionId]) { + // Valid session: delegate to transport for SSE streaming await callStreamableServer(req, res); + } else { + // No session: return server metadata JSON (discovery / health check) + res.json({ + server: { + name: serverOptions.serverName, + version: serverOptions.serverVersion, + transport: "http", + }, + capabilities: { + tools: serverOptions.toolSearchName + ? { name: serverOptions.toolSearchName, description: serverOptions.toolSearchDescription } + : {}, + }, + }); } }); @@ -96,8 +124,7 @@ export function runServer(port = 3000, buildMode = false, _toolSearchName?: stri if ((errorMessage ?? "").includes("address already in use")) { console.error("Error starting server:", error?.message); console.warn("Port", port, "is already in use. Retrying with next port..."); - port++; - runServer(port); + runServer(port + 1, buildMode, serverOptions); return; } console.log(styleText("whiteBright", `VitePress Plugin MCP`)); diff --git a/src/tools/searchVitePressDocs.ts b/src/tools/searchVitePressDocs.ts index 36e1747..df6a2b5 100644 --- a/src/tools/searchVitePressDocs.ts +++ b/src/tools/searchVitePressDocs.ts @@ -2,10 +2,10 @@ import { z } from "zod"; import { searchByMiniSearch } from "../modules/search/miniSearch"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -export function toolSearchVitePressDocs(mcp: McpServer, buildMode = false, toolSearchName?: string, toolSearchDescription?: string) { +export function toolSearchVitePressDocs(mcp: McpServer, buildMode = false, toolSearchName: string, toolSearchDescription: string) { mcp.tool( - toolSearchName || "search_vitepress_docs", - toolSearchDescription || "Search VitePress Documents For This Product. Extract up to five keywords each English and native language, and define all of them as single words. e.g. Vitepress, API, Specification,Extensions etc.", + toolSearchName, + toolSearchDescription, { keywords: z.array(z.string()) }, async ({ keywords }) => { console.log("START: search-docs");