From fd45a832fd4ba1af64e34d1e7829f5d9895a610e Mon Sep 17 00:00:00 2001 From: Thomas Raffray Date: Thu, 17 Apr 2025 10:02:39 +0200 Subject: [PATCH 01/13] feat(sse): add sse server --- package.json | 4 + src/app.ts | 10 +++ src/commands/start-sse-server.ts | 136 +++++++++++++++++++++++++++++ src/server/init-server.ts | 141 +++++++++++++++++++++++++++++++ src/server/types.ts | 4 + 5 files changed, 295 insertions(+) create mode 100644 src/commands/start-sse-server.ts create mode 100644 src/server/init-server.ts create mode 100644 src/server/types.ts diff --git a/package.json b/package.json index 17d345c..7b8ea94 100644 --- a/package.json +++ b/package.json @@ -20,12 +20,16 @@ "ajv": "^8.17.1", "algoliasearch": "^5.23.4", "commander": "^13.1.0", + "cors": "^2.8.5", + "express": "^5.1.0", "open": "^10.1.0", "zod": "^3.24.4" }, "devDependencies": { "@eslint/js": "^9.24.0", "@modelcontextprotocol/inspector": "^0.9.0", + "@types/cors": "^2.8.17", + "@types/express": "^5.0.1", "@types/node": "^22.14.0", "@vitest/coverage-v8": "^3.1.1", "bun": "^1.2.9", diff --git a/src/app.ts b/src/app.ts index 02394d4..6f1cbc1 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,4 +1,5 @@ import { Command } from "commander"; +import type { StartServerOptions } from "./server/types.ts"; import { type ListToolsOptions } from "./commands/list-tools.ts"; import { ZodError } from "zod"; @@ -102,6 +103,15 @@ program } }); +program + .command("start-sse-server") + .description("Starts the remote-ready Algolia MCP server") + .option(...ALLOW_TOOLS_OPTIONS_TUPLE) + .action(async (opts: StartServerOptions) => { + const { startSseServer } = await import("./commands/start-sse-server.ts"); + await startSseServer(opts); + }); + program .command("authenticate") .description("Authenticate with Algolia") diff --git a/src/commands/start-sse-server.ts b/src/commands/start-sse-server.ts new file mode 100644 index 0000000..8244463 --- /dev/null +++ b/src/commands/start-sse-server.ts @@ -0,0 +1,136 @@ +import express, { Request, Response } from "express"; +import cors from "cors"; +import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; +import type { StartServerOptions } from "../server/types.ts"; +import { initMCPServer } from "../server/init-server.ts"; + +export async function startSseServer(opts: StartServerOptions) { + try { + const app = express(); + app.use( + cors({ + origin: process.env.ALLOWED_ORIGINS?.split(",") || "*", + methods: ["GET", "POST"], + allowedHeaders: ["Content-Type", "Authorization"], + }), + ); + + // Store active connections + const connections = new Map(); + + // Health check endpoint + app.get("/health", (req, res) => { + res.status(200).json({ + status: "ok", + version: "1.0.0", + uptime: process.uptime(), + timestamp: new Date().toISOString(), + connections: connections.size, + }); + }); + + // SSE connection establishment endpoint + app.get("/sse", async (req, res) => { + // Instantiate SSE transport object + const transport = new SSEServerTransport("/messages", res); + // Get sessionId + const sessionId = transport.sessionId; + console.log(`[${new Date().toISOString()}] New SSE connection established: ${sessionId}`); + + // Register connection + connections.set(sessionId, transport); + + // Connection interruption handling + req.on("close", () => { + console.log(`[${new Date().toISOString()}] SSE connection closed: ${sessionId}`); + connections.delete(sessionId); + }); + + // Connect the transport object to the MCP server + const mcpServer = await initMCPServer(opts); + await mcpServer.connect(transport); + console.log(`[${new Date().toISOString()}] MCP server connection successful: ${sessionId}`); + }); + + // Endpoint for receiving client messages + app.post("/messages", async (req: Request, res: Response) => { + try { + console.log(`[${new Date().toISOString()}] Received client message:`, req.query); + const sessionId = req.query.sessionId as string; + + // Find the corresponding SSE connection and process the message + if (connections.size > 0) { + const transport: SSEServerTransport = connections.get(sessionId) as SSEServerTransport; + // Use transport to process messages + if (transport) { + await transport.handlePostMessage(req, res); + } else { + throw new Error("No active SSE connection"); + } + } else { + throw new Error("No active SSE connection"); + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (error: any) { + console.error(`[${new Date().toISOString()}] Failed to process client message:`, error); + res.status(500).json({ error: "Failed to process message", message: error.message }); + } + }); + + // Graceful shutdown of all connections + async function closeAllConnections() { + console.log( + `[${new Date().toISOString()}] Closing all connections (${connections.size} active)`, + ); + for (const [id, transport] of connections.entries()) { + try { + // Send shutdown event + transport.res.write( + 'event: server_shutdown\ndata: {"reason": "Server is shutting down"}\n\n', + ); + transport.res.end(); + console.log(`[${new Date().toISOString()}] Connection closed: ${id}`); + } catch (error) { + console.error(`[${new Date().toISOString()}] Failed to close connection: ${id}`, error); + } + } + connections.clear(); + } + + // Error handling + app.use((err: Error, _: Request, res: Response) => { + console.error(`[${new Date().toISOString()}] Unhandled exception:`, err); + res.status(500).json({ error: "Server internal error" }); + }); + + // Graceful shutdown + process.on("SIGTERM", async () => { + console.log(`[${new Date().toISOString()}] Received SIGTERM signal, preparing to close`); + await closeAllConnections(); + server.close(() => { + console.log(`[${new Date().toISOString()}] Server closed`); + process.exit(0); + }); + }); + + process.on("SIGINT", async () => { + console.log(`[${new Date().toISOString()}] Received SIGINT signal, preparing to close`); + await closeAllConnections(); + process.exit(0); + }); + + // Start server + const port = 4243; + const server = app.listen(port, () => { + console.log( + `[${new Date().toISOString()}] Algolia MCP SSE server started, address: http://localhost:${port}`, + ); + console.log(`- SSE connection endpoint: http://localhost:${port}/sse`); + console.log(`- Message processing endpoint: http://localhost:${port}/messages`); + console.log(`- Health check endpoint: http://localhost:${port}/health`); + }); + } catch (err) { + console.error("Error starting server:", err); + process.exit(1); + } +} diff --git a/src/server/init-server.ts b/src/server/init-server.ts new file mode 100644 index 0000000..0758efd --- /dev/null +++ b/src/server/init-server.ts @@ -0,0 +1,141 @@ +import type { StartServerOptions } from "./types.ts"; +import { AppStateManager } from "../appState.ts"; +import { authenticate } from "../authentication.ts"; +import { DashboardApi } from "../DashboardApi.ts"; +import { CONFIG } from "../config.ts"; +import { getToolFilter, isToolAllowed } from "../toolFilters.ts"; +import { operationId as GetUserInfoOperationId, registerGetUserInfo } from "../tools/registerGetUserInfo.ts"; +import { + operationId as GetApplicationsOperationId, + registerGetApplications +} from "../tools/registerGetApplications.ts"; +import { registerOpenApiTools } from "../tools/registerOpenApi.ts"; +import { + ABTestingSpec, + AnalyticsSpec, + IngestionSpec, + MonitoringSpec, + RecommendSpec, + SearchSpec, + UsageSpec +} from "../openApi.ts"; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; + +export async function initMCPServer(opts: StartServerOptions): Promise { + try { + const appState = await AppStateManager.load(); + + if (!appState.get("accessToken")) { + const token = await authenticate(); + + await appState.update({ + accessToken: token.access_token, + refreshToken: token.refresh_token, + }); + } + + const dashboardApi = new DashboardApi({ + baseUrl: CONFIG.dashboardApiBaseUrl, + appState, + }); + + const server = new McpServer({ + name: "algolia", + version: "1.0.0", + capabilities: { + resources: {}, + tools: {}, + }, + }); + + const toolFilter = getToolFilter(opts); + + // Dashboard API Tools + if (isToolAllowed(GetUserInfoOperationId, toolFilter)) { + registerGetUserInfo(server, dashboardApi); + } + + if (isToolAllowed(GetApplicationsOperationId, toolFilter)) { + registerGetApplications(server, dashboardApi); + } + + // Search API Tools + registerOpenApiTools({ + server, + dashboardApi, + openApiSpec: SearchSpec, + toolFilter, + }); + + // Analytics API Tools + registerOpenApiTools({ + server, + dashboardApi, + openApiSpec: AnalyticsSpec, + toolFilter, + }); + + // Recommend API Tools + registerOpenApiTools({ + server, + dashboardApi, + openApiSpec: RecommendSpec, + toolFilter, + }); + + // AB Testing + registerOpenApiTools({ + server, + dashboardApi, + openApiSpec: ABTestingSpec, + toolFilter, + }); + + // Monitoring API Tools + registerOpenApiTools({ + server, + dashboardApi, + openApiSpec: MonitoringSpec, + toolFilter, + }); + + // Usage + registerOpenApiTools({ + server, + dashboardApi, + openApiSpec: UsageSpec, + toolFilter, + }); + + // Ingestion API Tools + registerOpenApiTools({ + server, + dashboardApi, + openApiSpec: IngestionSpec, + toolFilter, + requestMiddlewares: [ + // Dirty fix for Claud hallucinating regions + async ({ request, params }) => { + const application = await dashboardApi.getApplication(params.applicationId); + const region = application.data.attributes.log_region === "de" ? "eu" : "us"; + + const url = new URL(request.url); + const regionFromUrl = url.hostname.match(/data\.(.+)\.algolia.com/)?.[0]; + + if (regionFromUrl !== region) { + console.error("Had to adjust region from", regionFromUrl, "to", region); + url.hostname = `data.${region}.algolia.com`; + return new Request(url, request.clone()); + } + + return request; + }, + ], + }); + + return server; + } catch (err) { + console.error("Error starting server:", err); + process.exit(1); + } +} \ No newline at end of file diff --git a/src/server/types.ts b/src/server/types.ts new file mode 100644 index 0000000..7d83a9a --- /dev/null +++ b/src/server/types.ts @@ -0,0 +1,4 @@ +import type { CliFilteringOptions } from "../toolFilters.ts"; + + +export type StartServerOptions = CliFilteringOptions; \ No newline at end of file From 4a3f846dd87c9ccf05f7f0a7eb58735cf066227e Mon Sep 17 00:00:00 2001 From: Thomas Raffray Date: Thu, 17 Apr 2025 10:41:09 +0200 Subject: [PATCH 02/13] chore: put back the bin in dist to avoid it being pushed to git --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7b8ea94..ae3efee 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "type-check": "tsc --noEmit", "lint": "eslint --ext .ts src", "test": "vitest", - "build": "bun build ./src/app.ts --compile", + "build": "bun build ./src/app.ts --compile --outfile dist/app", "debug": "mcp-inspector npm start" }, "type": "module", From b5d0d967e7273fa4a8224d82a9bc55cfcd9d608a Mon Sep 17 00:00:00 2001 From: Thomas Raffray Date: Fri, 18 Apr 2025 09:33:38 +0200 Subject: [PATCH 03/13] chore: package lock --- package-lock.json | 111 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/package-lock.json b/package-lock.json index 70d9966..78f30ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,12 +13,16 @@ "ajv": "^8.17.1", "algoliasearch": "^5.23.4", "commander": "^13.1.0", + "cors": "^2.8.5", + "express": "^5.1.0", "open": "^10.1.0", "zod": "^3.24.4" }, "devDependencies": { "@eslint/js": "^9.24.0", "@modelcontextprotocol/inspector": "^0.9.0", + "@types/cors": "^2.8.17", + "@types/express": "^5.0.1", "@types/node": "^22.14.0", "@vitest/coverage-v8": "^3.1.1", "bun": "^1.2.9", @@ -3114,6 +3118,27 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", @@ -3121,6 +3146,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", @@ -3128,6 +3163,38 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/express": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.1.tgz", + "integrity": "sha512-UZUw8vjpWFXuDnjFTh7/5c2TWDlQqeXHi6hcN7F2XSVT5P+WmUnnbFS3KA6Jnc6IsEqI2qCVu2bK0R0J4A8ZQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", + "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -3135,6 +3202,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.14.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz", @@ -3145,6 +3219,43 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/qs": { + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, "node_modules/@types/statuses": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.5.tgz", From ae917cb5399d0830d821bd6a25ad8a9fe5e46f21 Mon Sep 17 00:00:00 2001 From: Thomas Raffray Date: Fri, 18 Apr 2025 09:33:47 +0200 Subject: [PATCH 04/13] chore: add collections in common file --- src/server/init-server.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/server/init-server.ts b/src/server/init-server.ts index 0758efd..e21add4 100644 --- a/src/server/init-server.ts +++ b/src/server/init-server.ts @@ -12,7 +12,7 @@ import { import { registerOpenApiTools } from "../tools/registerOpenApi.ts"; import { ABTestingSpec, - AnalyticsSpec, + AnalyticsSpec, CollectionsSpec, IngestionSpec, MonitoringSpec, RecommendSpec, @@ -133,6 +133,14 @@ export async function initMCPServer(opts: StartServerOptions): Promise Date: Fri, 18 Apr 2025 09:35:33 +0200 Subject: [PATCH 05/13] chore: lint --- src/commands/start-sse-server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/start-sse-server.ts b/src/commands/start-sse-server.ts index 8244463..625d54c 100644 --- a/src/commands/start-sse-server.ts +++ b/src/commands/start-sse-server.ts @@ -1,4 +1,4 @@ -import express, { Request, Response } from "express"; +import express, { type Request, type Response } from "express"; import cors from "cors"; import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; import type { StartServerOptions } from "../server/types.ts"; From a7e49c880c79f476bb208a2bde5fc0d5228aac43 Mon Sep 17 00:00:00 2001 From: Thomas Raffray Date: Fri, 18 Apr 2025 12:03:35 +0200 Subject: [PATCH 06/13] chore: match new readme --- README.md | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 70bb011..39ed0f9 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,23 @@ _Coming soon._ > [!TIP] > You can refer to the [official documentation](https://modelcontextprotocol.io/quickstart/user) for Claude Desktop. +#### SSE Server specific configuration +To run the SSE version, as Claude Desktop doesn't natively support it yet, you'll have to use a gateway: +```json +{ + "mcpServers": { + "algolia-mcp": { + "command": "/npx", + "args": [ + "-y", + "mcp-remote", + "http://localhost:4243/sse" + ] + } + } +} +``` + ### CLI Options #### Available Commands @@ -134,14 +151,15 @@ _Coming soon._ Usage: algolia-mcp [options] [command] Options: - -h, --help display help for command + -h, --help Display help for command Commands: - start-server [options] Starts the Algolia MCP server - authenticate Authenticate with Algolia - logout Remove all stored credentials - list-tools List all available tools - help [command] display help for command + start-server [options] Starts the Algolia MCP server + start-sse-server [options] Starts the Algolia MCP SSE server + authenticate Authenticate with Algolia + logout Remove all stored credentials + list-tools List all available tools + help [command] Display help for command ``` #### Server Options From 4d94288d109af3daa8707acc35b80ae2002cb491 Mon Sep 17 00:00:00 2001 From: Thomas Raffray Date: Tue, 22 Apr 2025 17:35:50 +0200 Subject: [PATCH 07/13] chore: merge --- src/commands/start-sse-server.ts | 5 ++- src/server/init-server.ts | 64 +++++++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/src/commands/start-sse-server.ts b/src/commands/start-sse-server.ts index 625d54c..ef3df98 100644 --- a/src/commands/start-sse-server.ts +++ b/src/commands/start-sse-server.ts @@ -3,6 +3,7 @@ import cors from "cors"; import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; import type { StartServerOptions } from "../server/types.ts"; import { initMCPServer } from "../server/init-server.ts"; +import { CONFIG } from "../config.ts"; export async function startSseServer(opts: StartServerOptions) { try { @@ -19,10 +20,10 @@ export async function startSseServer(opts: StartServerOptions) { const connections = new Map(); // Health check endpoint - app.get("/health", (req, res) => { + app.get("/health", (_, res) => { res.status(200).json({ status: "ok", - version: "1.0.0", + version: CONFIG.version, uptime: process.uptime(), timestamp: new Date().toISOString(), connections: connections.size, diff --git a/src/server/init-server.ts b/src/server/init-server.ts index e21add4..cf4e1a8 100644 --- a/src/server/init-server.ts +++ b/src/server/init-server.ts @@ -4,22 +4,35 @@ import { authenticate } from "../authentication.ts"; import { DashboardApi } from "../DashboardApi.ts"; import { CONFIG } from "../config.ts"; import { getToolFilter, isToolAllowed } from "../toolFilters.ts"; -import { operationId as GetUserInfoOperationId, registerGetUserInfo } from "../tools/registerGetUserInfo.ts"; +import { + operationId as GetUserInfoOperationId, + registerGetUserInfo, +} from "../tools/registerGetUserInfo.ts"; import { operationId as GetApplicationsOperationId, - registerGetApplications + registerGetApplications, } from "../tools/registerGetApplications.ts"; import { registerOpenApiTools } from "../tools/registerOpenApi.ts"; import { ABTestingSpec, - AnalyticsSpec, CollectionsSpec, + AnalyticsSpec, + CollectionsSpec, IngestionSpec, MonitoringSpec, + QuerySuggestionsSpec, RecommendSpec, SearchSpec, - UsageSpec + UsageSpec, } from "../openApi.ts"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { + operationId as SetAttributesForFacetingOperationId, + registerSetAttributesForFaceting, +} from "../tools/registerSetAttributesForFaceting.ts"; +import { + operationId as SetCustomRankingOperationId, + registerSetCustomRanking, +} from "../tools/registerSetCustomRanking.ts"; export async function initMCPServer(opts: StartServerOptions): Promise { try { @@ -41,7 +54,7 @@ export async function initMCPServer(opts: StartServerOptions): Promise { + const url = new URL(request.url); + const nameParams = url.searchParams.get("name"); + + if (!nameParams) { + return new Request(url, request.clone()); + } + + const nameValues = nameParams.split(","); + + url.searchParams.delete("name"); + + nameValues.forEach((value) => { + url.searchParams.append("name", value); + }); + + return new Request(url, request.clone()); + }, + ], }); // Ingestion API Tools @@ -141,9 +176,26 @@ export async function initMCPServer(opts: StartServerOptions): Promise Date: Mon, 5 May 2025 12:22:54 +0200 Subject: [PATCH 08/13] fix: use a flag for transport choice --- src/app.ts | 30 +++++++++++++++++++----------- src/toolFilters.ts | 1 + 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/app.ts b/src/app.ts index 6f1cbc1..a47eb19 100644 --- a/src/app.ts +++ b/src/app.ts @@ -93,25 +93,33 @@ program return { applicationId, apiKey }; }, ) + + .option("--transport [stdio|sse]", "Transport type, either `stdio` (default) or `sse`", "stdio") .action(async (opts) => { try { - const { startServer } = await import("./commands/start-server.ts"); - await startServer(opts); + switch (opts.transport) { + case "stdio": { + console.info('Starting server with stdio transport'); + const { startServer } = await import("./commands/start-server.ts"); + await startServer(opts); + break; + } + case "sse": { + console.info('Starting server with SSE transport'); + const { startSseServer } = await import("./commands/start-sse-server.ts"); + await startSseServer(opts); + break; + } + default: + console.error(`Unknown transport type: ${opts.transport}\nAllowed values: stdio, sse`); + process.exit(1); + } } catch (error) { console.error(formatErrorForCli(error)); process.exit(1); } }); -program - .command("start-sse-server") - .description("Starts the remote-ready Algolia MCP server") - .option(...ALLOW_TOOLS_OPTIONS_TUPLE) - .action(async (opts: StartServerOptions) => { - const { startSseServer } = await import("./commands/start-sse-server.ts"); - await startSseServer(opts); - }); - program .command("authenticate") .description("Authenticate with Algolia") diff --git a/src/toolFilters.ts b/src/toolFilters.ts index 131e7c5..5100192 100644 --- a/src/toolFilters.ts +++ b/src/toolFilters.ts @@ -3,6 +3,7 @@ import z from "zod"; export const CliFilteringOptionsSchema = z.object({ allowTools: z.array(z.string()).optional(), denyTools: z.array(z.string()).optional(), + transport: z.literal('stdio').or(z.literal('sse')).default('stdio') }); export type CliFilteringOptions = z.infer; From dc8fe54aed10267d6bcad632ac50c12a73ef30cd Mon Sep 17 00:00:00 2001 From: Thomas Raffray Date: Mon, 5 May 2025 17:28:00 +0200 Subject: [PATCH 09/13] feat: add streamable transport --- README.md | 12 +- src/app.ts | 12 +- src/commands/start-http-server.ts | 256 ++++++++++++++++++++++++++++++ src/commands/start-sse-server.ts | 137 ---------------- src/toolFilters.ts | 2 +- 5 files changed, 272 insertions(+), 147 deletions(-) create mode 100644 src/commands/start-http-server.ts delete mode 100644 src/commands/start-sse-server.ts diff --git a/README.md b/README.md index 39ed0f9..bcd4b46 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,8 @@ _Coming soon._ ### Claude Desktop Setup +#### Local + 1. Open Claude Desktop settings 2. Add the following to your configuration: ```json @@ -126,8 +128,8 @@ _Coming soon._ > [!TIP] > You can refer to the [official documentation](https://modelcontextprotocol.io/quickstart/user) for Claude Desktop. -#### SSE Server specific configuration -To run the SSE version, as Claude Desktop doesn't natively support it yet, you'll have to use a gateway: +#### Remote +To run an HTTP server, as Claude Desktop doesn't natively support it yet, you'll have to use a gateway: ```json { "mcpServers": { @@ -136,12 +138,16 @@ To run the SSE version, as Claude Desktop doesn't natively support it yet, you'l "args": [ "-y", "mcp-remote", - "http://localhost:4243/sse" + "http://localhost:4243/mcp" ] } } } ``` +> [!INFO] +> Our HTTP server leverages the [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http). +> It is also backward compatible with the [SSE transport](https://modelcontextprotocol.io/docs/concepts/transports#server-sent-events-sse). + ### CLI Options diff --git a/src/app.ts b/src/app.ts index a47eb19..be9c66a 100644 --- a/src/app.ts +++ b/src/app.ts @@ -94,7 +94,7 @@ program }, ) - .option("--transport [stdio|sse]", "Transport type, either `stdio` (default) or `sse`", "stdio") + .option("--transport [stdio|http]", "Transport type, either `stdio` (default) or `http`", "stdio") .action(async (opts) => { try { switch (opts.transport) { @@ -104,14 +104,14 @@ program await startServer(opts); break; } - case "sse": { - console.info('Starting server with SSE transport'); - const { startSseServer } = await import("./commands/start-sse-server.ts"); - await startSseServer(opts); + case "http": { + console.info('Starting server with HTTP transport support'); + const { startHttpServer } = await import("./commands/start-http-server.ts"); + await startHttpServer(opts); break; } default: - console.error(`Unknown transport type: ${opts.transport}\nAllowed values: stdio, sse`); + console.error(`Unknown transport type: ${opts.transport}\nAllowed values: stdio, http`); process.exit(1); } } catch (error) { diff --git a/src/commands/start-http-server.ts b/src/commands/start-http-server.ts new file mode 100644 index 0000000..e756e7c --- /dev/null +++ b/src/commands/start-http-server.ts @@ -0,0 +1,256 @@ +import express, { type NextFunction, type Request, type Response } from "express"; +import cors from "cors"; +import { randomUUID } from "node:crypto"; +import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js"; +import { InMemoryEventStore } from "@modelcontextprotocol/sdk/examples/shared/inMemoryEventStore.js"; +import type { StartServerOptions } from "../server/types.ts"; +import { initMCPServer } from "../server/init-server.ts"; +import { CONFIG } from "../config.ts"; + +export async function startHttpServer(opts: StartServerOptions) { + try { + // Create Express application + const app = express(); + app.use(express.json()); + app.use( + cors({ + origin: process.env.ALLOWED_ORIGINS?.split(",") || "*", + methods: ["GET", "POST", "DELETE"], + allowedHeaders: ["Content-Type", "Authorization"], + }), + ); + + // Store transports by session ID + const transports: Map = new Map(); + + // Health check endpoint + app.get("/health", (_, res) => { + res.status(200).json({ + status: "ok", + version: CONFIG.version, + uptime: process.uptime(), + timestamp: new Date().toISOString(), + connections: transports.size, + }); + }); + + //============================================================================= + // STREAMABLE HTTP TRANSPORT (PROTOCOL VERSION 2025-03-26) + //============================================================================= + + // Handle all MCP Streamable HTTP requests (GET, POST, DELETE) on a single endpoint + app.all("/mcp", async (req: Request, res: Response) => { + console.log(`Received ${req.method} request to /mcp`); + + try { + // Check for existing session ID + const sessionId = req.headers["mcp-session-id"] as string | undefined; + let transport: StreamableHTTPServerTransport; + + if (sessionId != null && transports.has(sessionId)) { + // Check if the transport is of the correct type + const existingTransport = transports.get(sessionId); + if (existingTransport instanceof StreamableHTTPServerTransport) { + // Reuse existing transport + transport = existingTransport; + } else { + // Transport exists but is not a StreamableHTTPServerTransport (could be SSEServerTransport) + res.status(400).json({ + jsonrpc: "2.0", + error: { + code: -32000, + message: "Bad Request: Session exists but uses a different transport protocol", + }, + id: null, + }); + return; + } + } else if (sessionId == null && req.method === "POST" && isInitializeRequest(req.body)) { + const eventStore = new InMemoryEventStore(); + transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: () => randomUUID(), + eventStore, // Enable resumability + onsessioninitialized: (sessionId) => { + // Store the transport by session ID when session is initialized + console.log(`StreamableHTTP session initialized with ID: ${sessionId}`); + transports.set(sessionId, transport); + }, + }); + + // Set up onclose handler to clean up transport when closed + transport.onclose = () => { + if (transport.sessionId != null && transports.has(transport.sessionId)) { + console.log(`Transport closed for HTTP session ${transport.sessionId}, removing from transports map`); + transports.delete(transport.sessionId); + } + }; + + // Connect the transport to the MCP server + const server = await initMCPServer(opts); + await server.connect(transport); + } else { + // Invalid request - no session ID or not initialization request + res.status(400).json({ + jsonrpc: "2.0", + error: { + code: -32000, + message: "Bad Request: No valid session ID provided", + }, + id: null, + }); + return; + } + + // Handle the request with the transport + await transport.handleRequest(req, res, req.body); + } catch (error) { + console.error("Error handling MCP request:", error); + if (!res.headersSent) { + res.status(500).json({ + jsonrpc: "2.0", + error: { + code: -32603, + message: "Internal server error", + }, + id: null, + }); + } + } + }); + + //============================================================================= + // DEPRECATED HTTP+SSE TRANSPORT (PROTOCOL VERSION 2024-11-05) + //============================================================================= + + app.get("/sse", async (_: Request, res: Response) => { + console.log("Received GET request to /sse (deprecated SSE transport)"); + const transport = new SSEServerTransport("/messages", res); + transports.set(transport.sessionId, transport); + res.on("close", () => { + if (transport.sessionId != null && transports.has(transport.sessionId)) { + console.log(`Transport closed for SSE session ${transport.sessionId}, removing from transports map`); + transports.delete(transport.sessionId); + } + }); + const server = await initMCPServer(opts); + await server.connect(transport); + }); + + app.post("/messages", async (req: Request, res: Response) => { + const sessionId = req.query.sessionId as string; + let transport: SSEServerTransport; + const existingTransport = transports.get(sessionId); + if (existingTransport instanceof SSEServerTransport) { + // Reuse existing transport + transport = existingTransport; + } else { + // Transport exists but is not a SSEServerTransport (could be StreamableHTTPServerTransport) + res.status(400).json({ + jsonrpc: "2.0", + error: { + code: -32000, + message: "Bad Request: Session exists but uses a different transport protocol", + }, + id: null, + }); + return; + } + if (transport != null) { + await transport.handlePostMessage(req, res, req.body); + } else { + res.status(400).send({ + jsonrpc: "2.0", + error: { + code: -32000, + message: "Bad Request: No transport found for sessionId", + }, + id: null, + }); + } + }); + + //============================================================================= + // END OF SERVER SETUP + //============================================================================= + + // Error handling + app.use((err: Error, _: Request, res: Response, __: NextFunction) => { + console.error("Unhandled exception: ", err.stack); + res.status(500).json({ + jsonrpc: "2.0", + error: { + code: -32603, + message: "Internal Server Error", + }, + id: null, + }); + }); + + // Graceful shutdown of all connections + async function closeAllConnections() { + for (const [sessionId, transport] of transports.entries()) { + try { + console.log(`Closing transport for session ${sessionId}`); + await transport.send({ + jsonrpc: "2.0", + method: "notifications/shutdown", + }); + await transport.close(); + } catch (error) { + console.error(`Error closing transport for session ${sessionId}: `, error); + } + } + transports.clear(); + console.log("All connections closed"); + } + + // Graceful shutdown + process.on("SIGTERM", async () => { + console.log(`Graceful shutdown initiated at ${new Date().toISOString()}`); + await closeAllConnections(); + server.close(() => { + console.log(`Graceful shutdown complete at ${new Date().toISOString()}`); + process.exit(0); + }); + }); + + // Handle server shutdown + process.on("SIGINT", async () => { + console.log("Shutting down server..."); + await closeAllConnections(); + + process.exit(0); + }); + + // Start the server + const PORT = 4243; + const server = app.listen(PORT, () => { + console.log(`HTTP MCP server listening on port ${PORT}`); + console.log(` + ============================================== + SUPPORTED TRANSPORT OPTIONS: + + 1. Streamable Http(Protocol version: 2025-03-26) + Endpoint: /mcp + Methods: GET, POST, DELETE + Usage: + - Initialize with POST to /mcp + - Establish SSE stream with GET to /mcp + - Send requests with POST to /mcp + - Terminate session with DELETE to /mcp + + 2. Http + SSE (Protocol version: 2024-11-05) + Endpoints: /sse (GET) and /messages (POST) + Usage: + - Establish SSE stream with GET to /sse + - Send requests with POST to /messages?sessionId= + ============================================== + `); + }); + } catch (err) { + console.error("Error starting server:", err); + process.exit(1); + } +} diff --git a/src/commands/start-sse-server.ts b/src/commands/start-sse-server.ts deleted file mode 100644 index ef3df98..0000000 --- a/src/commands/start-sse-server.ts +++ /dev/null @@ -1,137 +0,0 @@ -import express, { type Request, type Response } from "express"; -import cors from "cors"; -import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; -import type { StartServerOptions } from "../server/types.ts"; -import { initMCPServer } from "../server/init-server.ts"; -import { CONFIG } from "../config.ts"; - -export async function startSseServer(opts: StartServerOptions) { - try { - const app = express(); - app.use( - cors({ - origin: process.env.ALLOWED_ORIGINS?.split(",") || "*", - methods: ["GET", "POST"], - allowedHeaders: ["Content-Type", "Authorization"], - }), - ); - - // Store active connections - const connections = new Map(); - - // Health check endpoint - app.get("/health", (_, res) => { - res.status(200).json({ - status: "ok", - version: CONFIG.version, - uptime: process.uptime(), - timestamp: new Date().toISOString(), - connections: connections.size, - }); - }); - - // SSE connection establishment endpoint - app.get("/sse", async (req, res) => { - // Instantiate SSE transport object - const transport = new SSEServerTransport("/messages", res); - // Get sessionId - const sessionId = transport.sessionId; - console.log(`[${new Date().toISOString()}] New SSE connection established: ${sessionId}`); - - // Register connection - connections.set(sessionId, transport); - - // Connection interruption handling - req.on("close", () => { - console.log(`[${new Date().toISOString()}] SSE connection closed: ${sessionId}`); - connections.delete(sessionId); - }); - - // Connect the transport object to the MCP server - const mcpServer = await initMCPServer(opts); - await mcpServer.connect(transport); - console.log(`[${new Date().toISOString()}] MCP server connection successful: ${sessionId}`); - }); - - // Endpoint for receiving client messages - app.post("/messages", async (req: Request, res: Response) => { - try { - console.log(`[${new Date().toISOString()}] Received client message:`, req.query); - const sessionId = req.query.sessionId as string; - - // Find the corresponding SSE connection and process the message - if (connections.size > 0) { - const transport: SSEServerTransport = connections.get(sessionId) as SSEServerTransport; - // Use transport to process messages - if (transport) { - await transport.handlePostMessage(req, res); - } else { - throw new Error("No active SSE connection"); - } - } else { - throw new Error("No active SSE connection"); - } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (error: any) { - console.error(`[${new Date().toISOString()}] Failed to process client message:`, error); - res.status(500).json({ error: "Failed to process message", message: error.message }); - } - }); - - // Graceful shutdown of all connections - async function closeAllConnections() { - console.log( - `[${new Date().toISOString()}] Closing all connections (${connections.size} active)`, - ); - for (const [id, transport] of connections.entries()) { - try { - // Send shutdown event - transport.res.write( - 'event: server_shutdown\ndata: {"reason": "Server is shutting down"}\n\n', - ); - transport.res.end(); - console.log(`[${new Date().toISOString()}] Connection closed: ${id}`); - } catch (error) { - console.error(`[${new Date().toISOString()}] Failed to close connection: ${id}`, error); - } - } - connections.clear(); - } - - // Error handling - app.use((err: Error, _: Request, res: Response) => { - console.error(`[${new Date().toISOString()}] Unhandled exception:`, err); - res.status(500).json({ error: "Server internal error" }); - }); - - // Graceful shutdown - process.on("SIGTERM", async () => { - console.log(`[${new Date().toISOString()}] Received SIGTERM signal, preparing to close`); - await closeAllConnections(); - server.close(() => { - console.log(`[${new Date().toISOString()}] Server closed`); - process.exit(0); - }); - }); - - process.on("SIGINT", async () => { - console.log(`[${new Date().toISOString()}] Received SIGINT signal, preparing to close`); - await closeAllConnections(); - process.exit(0); - }); - - // Start server - const port = 4243; - const server = app.listen(port, () => { - console.log( - `[${new Date().toISOString()}] Algolia MCP SSE server started, address: http://localhost:${port}`, - ); - console.log(`- SSE connection endpoint: http://localhost:${port}/sse`); - console.log(`- Message processing endpoint: http://localhost:${port}/messages`); - console.log(`- Health check endpoint: http://localhost:${port}/health`); - }); - } catch (err) { - console.error("Error starting server:", err); - process.exit(1); - } -} diff --git a/src/toolFilters.ts b/src/toolFilters.ts index 5100192..5cc5dd2 100644 --- a/src/toolFilters.ts +++ b/src/toolFilters.ts @@ -3,7 +3,7 @@ import z from "zod"; export const CliFilteringOptionsSchema = z.object({ allowTools: z.array(z.string()).optional(), denyTools: z.array(z.string()).optional(), - transport: z.literal('stdio').or(z.literal('sse')).default('stdio') + transport: z.literal('stdio').or(z.literal('http')).default('stdio') }); export type CliFilteringOptions = z.infer; From 194811a71a32bc0f629df0f3c928681830f872a8 Mon Sep 17 00:00:00 2001 From: Alexandre Collin Date: Thu, 22 May 2025 08:44:42 +0200 Subject: [PATCH 10/13] doc: how to start sse server --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bcd4b46..501b0cd 100644 --- a/README.md +++ b/README.md @@ -160,8 +160,7 @@ Options: -h, --help Display help for command Commands: - start-server [options] Starts the Algolia MCP server - start-sse-server [options] Starts the Algolia MCP SSE server + start-server [options] Starts the Algolia MCP server () authenticate Authenticate with Algolia logout Remove all stored credentials list-tools List all available tools @@ -178,7 +177,8 @@ Starts the Algolia MCP server Options: -t, --allow-tools Comma separated list of tool ids (default: getUserInfo,getApplications,...,listIndices) --credentials Application ID and associated API key to use. Optional: the MCP will authenticate you if unspecified, giving you access to all your applications. - -h, --help display help for command + --transport [stdio|http] Transport type (default:stdio) + -h, --help Display help for command ``` ## 🛠 Development From 9bea052828a19df09df02cb4a9e20f03b67a9eed Mon Sep 17 00:00:00 2001 From: Alexandre Collin Date: Thu, 22 May 2025 14:57:37 +0200 Subject: [PATCH 11/13] doc: how to run mcp in OpenAI Playground and n8n --- README.md | 26 ++++++++++++++++++++++++++ assets/openai_playgroud_add_mcp.png | Bin 0 -> 139234 bytes 2 files changed, 26 insertions(+) create mode 100644 assets/openai_playgroud_add_mcp.png diff --git a/README.md b/README.md index 501b0cd..73b2ba1 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,32 @@ To run an HTTP server, as Claude Desktop doesn't natively support it yet, you'll > Our HTTP server leverages the [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http). > It is also backward compatible with the [SSE transport](https://modelcontextprotocol.io/docs/concepts/transports#server-sent-events-sse). +### OpenAI Playground (SSE server) +![alt text](assets/openai_playgroud_add_mcp.png) + +Run the Algolia MCP server in SSE mode +1. Set up the project +2. Build the server +3. Authenticate with Algolia +4. Launch the server in SSE mode (default: on port 4243) + +```sh +cd dist +./algolia-mcp start-server --transport http +``` +5. Make the SSE server accessible from the internet using ngrok (installation guide) +```sh +ngrok http 4243 +``` + +Add the SSE server to the Playground +1. Go to https://platform.openai.com/playground +2. Select Tools > “MCP Server” +3. Add the `https://[random].ngrok-free.app` obtained after running ngrok +4. Select “None” for authentication + +### n8n or any other MCP client using an SSE server +Follow the same instructions as for the OpenAI Playground. ### CLI Options diff --git a/assets/openai_playgroud_add_mcp.png b/assets/openai_playgroud_add_mcp.png new file mode 100644 index 0000000000000000000000000000000000000000..c1b8cb8474d1205ad2f506b0cca4b3c45eff05c7 GIT binary patch literal 139234 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%0DUCV{vY8SM9@h_+L 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 Date: Fri, 23 May 2025 20:26:52 +0200 Subject: [PATCH 12/13] chore: merge --- src/app.ts | 51 ++----- src/commands/start-server.ts | 240 +++---------------------------- src/server/init-server.ts | 272 ++++++++++++++++++----------------- src/server/types.ts | 13 +- 4 files changed, 188 insertions(+), 388 deletions(-) diff --git a/src/app.ts b/src/app.ts index be9c66a..a1f2aba 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,7 +1,5 @@ import { Command } from "commander"; -import type { StartServerOptions } from "./server/types.ts"; import { type ListToolsOptions } from "./commands/list-tools.ts"; -import { ZodError } from "zod"; const program = new Command("algolia-mcp"); @@ -64,20 +62,6 @@ const ALLOW_TOOLS_OPTIONS_TUPLE = [ DEFAULT_ALLOW_TOOLS, ] as const; -function formatErrorForCli(error: unknown): string { - if (error instanceof ZodError) { - return [...error.errors.map((e) => `- ${e.path.join(".") || ""}: ${e.message}`)].join( - "\n", - ); - } - - if (error instanceof Error) { - return error.message; - } - - return "Unknown error"; -} - program .command("start-server", { isDefault: true }) .description("Starts the Algolia MCP server") @@ -96,27 +80,22 @@ program .option("--transport [stdio|http]", "Transport type, either `stdio` (default) or `http`", "stdio") .action(async (opts) => { - try { - switch (opts.transport) { - case "stdio": { - console.info('Starting server with stdio transport'); - const { startServer } = await import("./commands/start-server.ts"); - await startServer(opts); - break; - } - case "http": { - console.info('Starting server with HTTP transport support'); - const { startHttpServer } = await import("./commands/start-http-server.ts"); - await startHttpServer(opts); - break; - } - default: - console.error(`Unknown transport type: ${opts.transport}\nAllowed values: stdio, http`); - process.exit(1); + switch (opts.transport) { + case "stdio": { + console.info('Starting server with stdio transport'); + const { startServer } = await import("./commands/start-server.ts"); + await startServer(opts); + break; + } + case "http": { + console.info('Starting server with HTTP transport support'); + const { startHttpServer } = await import("./commands/start-http-server.ts"); + await startHttpServer(opts); + break; } - } catch (error) { - console.error(formatErrorForCli(error)); - process.exit(1); + default: + console.error(`Unknown transport type: ${opts.transport}\nAllowed values: stdio, http`); + process.exit(1); } }); diff --git a/src/commands/start-server.ts b/src/commands/start-server.ts index 5b9331d..4135edb 100644 --- a/src/commands/start-server.ts +++ b/src/commands/start-server.ts @@ -1,227 +1,31 @@ #!/usr/bin/env -S node --experimental-strip-types -import { authenticate } from "../authentication.ts"; -import { AppStateManager } from "../appState.ts"; -import { DashboardApi } from "../DashboardApi.ts"; -import { - operationId as GetUserInfoOperationId, - registerGetUserInfo, -} from "../tools/registerGetUserInfo.ts"; -import { - operationId as GetApplicationsOperationId, - registerGetApplications, -} from "../tools/registerGetApplications.ts"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import type { - ProcessCallbackArguments, - ProcessInputSchema, - RequestMiddleware, -} from "../tools/registerOpenApi.ts"; -import { registerOpenApiTools } from "../tools/registerOpenApi.ts"; -import { CONFIG } from "../config.ts"; -import { - ABTestingSpec, - AnalyticsSpec, - CollectionsSpec, - IngestionSpec, - MonitoringSpec, - QuerySuggestionsSpec, - RecommendSpec, - SearchSpec, - UsageSpec, -} from "../openApi.ts"; -import { CliFilteringOptionsSchema, getToolFilter, isToolAllowed } from "../toolFilters.ts"; -import { - operationId as SetAttributesForFacetingOperationId, - registerSetAttributesForFaceting, -} from "../tools/registerSetAttributesForFaceting.ts"; -import { - registerSetCustomRanking, - operationId as SetCustomRankingOperationId, -} from "../tools/registerSetCustomRanking.ts"; - -import { CustomMcpServer } from "../CustomMcpServer.ts"; -import { z } from "zod"; - -export const StartServerOptionsSchema = CliFilteringOptionsSchema.extend({ - credentials: z - .object({ - applicationId: z.string(), - apiKey: z.string(), - }) - .optional(), -}); - -type StartServerOptions = z.infer; - -function makeRegionRequestMiddleware(dashboardApi: DashboardApi): RequestMiddleware { - return async ({ request, params }) => { - const application = await dashboardApi.getApplication(params.applicationId); - const region = application.data.attributes.log_region === "de" ? "eu" : "us"; - - const url = new URL(request.url); - const regionFromUrl = url.hostname.match(/data\.(.+)\.algolia.com/)?.[0]; - - if (regionFromUrl !== region) { - console.error("Had to adjust region from", regionFromUrl, "to", region); - url.hostname = `data.${region}.algolia.com`; - return new Request(url, request.clone()); - } - - return request; - }; -} - -export async function startServer(options: StartServerOptions): Promise { - const { credentials, ...opts } = StartServerOptionsSchema.parse(options); - const toolFilter = getToolFilter(opts); - - const server = new CustomMcpServer({ - name: "algolia", - version: CONFIG.version, - capabilities: { - resources: {}, - tools: {}, - }, - }); - - const regionHotFixMiddlewares: RequestMiddleware[] = []; - let processCallbackArguments: ProcessCallbackArguments; - const processInputSchema: ProcessInputSchema = (inputSchema) => { - // If we got it from the options, we don't need it from the AI - if (credentials && inputSchema.properties?.applicationId) { - delete inputSchema.properties.applicationId; - - if (Array.isArray(inputSchema.required)) { - inputSchema.required = inputSchema.required.filter((item) => item !== "applicationId"); - } - } - - return inputSchema; - }; - - if (credentials) { - processCallbackArguments = async (params, securityKeys) => { - const result = { ...params }; - - if (securityKeys.has("applicationId")) { - result.applicationId = credentials.applicationId; - } - - if (securityKeys.has("apiKey")) { - result.apiKey = credentials.apiKey; - } - - return result; - }; - } else { - const appState = await AppStateManager.load(); - - if (!appState.get("accessToken")) { - const token = await authenticate(); - - await appState.update({ - accessToken: token.access_token, - refreshToken: token.refresh_token, - }); - } - - const dashboardApi = new DashboardApi({ baseUrl: CONFIG.dashboardApiBaseUrl, appState }); - - processCallbackArguments = async (params, securityKeys) => { - const result = { ...params }; - - if (securityKeys.has("apiKey")) { - result.apiKey = await dashboardApi.getApiKey(params.applicationId); - } - - return result; - }; - - regionHotFixMiddlewares.push(makeRegionRequestMiddleware(dashboardApi)); - - // Dashboard API Tools - if (isToolAllowed(GetUserInfoOperationId, toolFilter)) { - registerGetUserInfo(server, dashboardApi); - } - - if (isToolAllowed(GetApplicationsOperationId, toolFilter)) { - registerGetApplications(server, dashboardApi); - } - - // TODO: Make it available when with applicationId+apiKey mode too - if (isToolAllowed(SetAttributesForFacetingOperationId, toolFilter)) { - registerSetAttributesForFaceting(server, dashboardApi); - } - - if (isToolAllowed(SetCustomRankingOperationId, toolFilter)) { - registerSetCustomRanking(server, dashboardApi); - } +import { initMCPServer } from "../server/init-server.ts"; +import type { StartServerOptions } from "../server/types.ts"; +import { ZodError } from "zod"; + +function formatErrorForCli(error: unknown): string { + if (error instanceof ZodError) { + return [...error.errors.map((e) => `- ${e.path.join(".") || ""}: ${e.message}`)].join( + "\n", + ); } - for (const openApiSpec of [ - SearchSpec, - AnalyticsSpec, - RecommendSpec, - ABTestingSpec, - MonitoringSpec, - CollectionsSpec, - QuerySuggestionsSpec, - ]) { - registerOpenApiTools({ - server, - processInputSchema, - processCallbackArguments, - openApiSpec, - toolFilter, - }); + if (error instanceof Error) { + return error.message; } - // Usage - registerOpenApiTools({ - server, - processInputSchema, - processCallbackArguments, - openApiSpec: UsageSpec, - toolFilter, - requestMiddlewares: [ - // The Usage API expects `name` parameter as multiple values - // rather than comma-separated. - async ({ request }) => { - const url = new URL(request.url); - const nameParams = url.searchParams.get("name"); - - if (!nameParams) { - return new Request(url, request.clone()); - } - - const nameValues = nameParams.split(","); - - url.searchParams.delete("name"); - - nameValues.forEach((value) => { - url.searchParams.append("name", value); - }); - - return new Request(url, request.clone()); - }, - ], - }); - - // Ingestion API Tools - registerOpenApiTools({ - server, - processInputSchema, - processCallbackArguments, - openApiSpec: IngestionSpec, - toolFilter, - requestMiddlewares: [ - // Dirty fix for Claud hallucinating regions - ...regionHotFixMiddlewares, - ], - }); + return "Unknown error"; +} - const transport = new StdioServerTransport(); - await server.connect(transport); - return server; +export async function startServer(options: StartServerOptions) { + try { + const server = await initMCPServer(options); + const transport = new StdioServerTransport(); + await server.connect(transport); + } catch (error) { + console.error(formatErrorForCli(error)); + process.exit(1); + } } diff --git a/src/server/init-server.ts b/src/server/init-server.ts index cf4e1a8..d85e83a 100644 --- a/src/server/init-server.ts +++ b/src/server/init-server.ts @@ -1,4 +1,3 @@ -import type { StartServerOptions } from "./types.ts"; import { AppStateManager } from "../appState.ts"; import { authenticate } from "../authentication.ts"; import { DashboardApi } from "../DashboardApi.ts"; @@ -12,7 +11,11 @@ import { operationId as GetApplicationsOperationId, registerGetApplications, } from "../tools/registerGetApplications.ts"; -import { registerOpenApiTools } from "../tools/registerOpenApi.ts"; +import { + type ProcessCallbackArguments, type ProcessInputSchema, + registerOpenApiTools, + type RequestMiddleware +} from "../tools/registerOpenApi.ts"; import { ABTestingSpec, AnalyticsSpec, @@ -24,7 +27,6 @@ import { SearchSpec, UsageSpec, } from "../openApi.ts"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { operationId as SetAttributesForFacetingOperationId, registerSetAttributesForFaceting, @@ -33,9 +35,70 @@ import { operationId as SetCustomRankingOperationId, registerSetCustomRanking, } from "../tools/registerSetCustomRanking.ts"; +import { CustomMcpServer } from "../CustomMcpServer.ts"; +import { type StartServerOptions, StartServerOptionsSchema } from "./types.ts"; + +function makeRegionRequestMiddleware(dashboardApi: DashboardApi): RequestMiddleware { + return async ({ request, params }) => { + const application = await dashboardApi.getApplication(params.applicationId); + const region = application.data.attributes.log_region === "de" ? "eu" : "us"; + + const url = new URL(request.url); + const regionFromUrl = url.hostname.match(/data\.(.+)\.algolia.com/)?.[0]; + + if (regionFromUrl !== region) { + console.error("Had to adjust region from", regionFromUrl, "to", region); + url.hostname = `data.${region}.algolia.com`; + return new Request(url, request.clone()); + } + + return request; + }; +} + +export async function initMCPServer(options: StartServerOptions): Promise { + const { credentials, ...opts } = StartServerOptionsSchema.parse(options); + const toolFilter = getToolFilter(opts); + + const server = new CustomMcpServer({ + name: "algolia", + version: CONFIG.version, + capabilities: { + resources: {}, + tools: {}, + }, + }); + + const regionHotFixMiddlewares: RequestMiddleware[] = []; + let processCallbackArguments: ProcessCallbackArguments; + const processInputSchema: ProcessInputSchema = (inputSchema) => { + // If we got it from the options, we don't need it from the AI + if (credentials && inputSchema.properties?.applicationId) { + delete inputSchema.properties.applicationId; + + if (Array.isArray(inputSchema.required)) { + inputSchema.required = inputSchema.required.filter((item) => item !== "applicationId"); + } + } + + return inputSchema; + }; -export async function initMCPServer(opts: StartServerOptions): Promise { - try { + if (credentials) { + processCallbackArguments = async (params, securityKeys) => { + const result = { ...params }; + + if (securityKeys.has("applicationId")) { + result.applicationId = credentials.applicationId; + } + + if (securityKeys.has("apiKey")) { + result.apiKey = credentials.apiKey; + } + + return result; + }; + } else { const appState = await AppStateManager.load(); if (!appState.get("accessToken")) { @@ -47,21 +110,19 @@ export async function initMCPServer(opts: StartServerOptions): Promise { + const result = { ...params }; + + if (securityKeys.has("apiKey")) { + result.apiKey = await dashboardApi.getApiKey(params.applicationId); + } - const toolFilter = getToolFilter(opts); + return result; + }; + + regionHotFixMiddlewares.push(makeRegionRequestMiddleware(dashboardApi)); // Dashboard API Tools if (isToolAllowed(GetUserInfoOperationId, toolFilter)) { @@ -72,130 +133,77 @@ export async function initMCPServer(opts: StartServerOptions): Promise { - const url = new URL(request.url); - const nameParams = url.searchParams.get("name"); - - if (!nameParams) { - return new Request(url, request.clone()); - } - - const nameValues = nameParams.split(","); - - url.searchParams.delete("name"); - - nameValues.forEach((value) => { - url.searchParams.append("name", value); - }); - + // Usage + registerOpenApiTools({ + server, + processInputSchema, + processCallbackArguments, + openApiSpec: UsageSpec, + toolFilter, + requestMiddlewares: [ + // The Usage API expects `name` parameter as multiple values + // rather than comma-separated. + async ({ request }) => { + const url = new URL(request.url); + const nameParams = url.searchParams.get("name"); + + if (!nameParams) { return new Request(url, request.clone()); - }, - ], - }); + } - // Ingestion API Tools - registerOpenApiTools({ - server, - dashboardApi, - openApiSpec: IngestionSpec, - toolFilter, - requestMiddlewares: [ - // Dirty fix for Claud hallucinating regions - async ({ request, params }) => { - const application = await dashboardApi.getApplication(params.applicationId); - const region = application.data.attributes.log_region === "de" ? "eu" : "us"; - - const url = new URL(request.url); - const regionFromUrl = url.hostname.match(/data\.(.+)\.algolia.com/)?.[0]; - - if (regionFromUrl !== region) { - console.error("Had to adjust region from", regionFromUrl, "to", region); - url.hostname = `data.${region}.algolia.com`; - return new Request(url, request.clone()); - } - - return request; - }, - ], - }); + const nameValues = nameParams.split(","); - // Collections API Tools - registerOpenApiTools({ - server, - dashboardApi, - openApiSpec: CollectionsSpec, - toolFilter, - }); - - // Query Suggestions API Tools - registerOpenApiTools({ - server, - dashboardApi, - openApiSpec: QuerySuggestionsSpec, - toolFilter, - }); + url.searchParams.delete("name"); - // Custom settings Tools - if (isToolAllowed(SetAttributesForFacetingOperationId, toolFilter)) { - registerSetAttributesForFaceting(server, dashboardApi); - } + nameValues.forEach((value) => { + url.searchParams.append("name", value); + }); - if (isToolAllowed(SetCustomRankingOperationId, toolFilter)) { - registerSetCustomRanking(server, dashboardApi); - } - - return server; - } catch (err) { - console.error("Error starting server:", err); - process.exit(1); - } + return new Request(url, request.clone()); + }, + ], + }); + + // Ingestion API Tools + registerOpenApiTools({ + server, + processInputSchema, + processCallbackArguments, + openApiSpec: IngestionSpec, + toolFilter, + requestMiddlewares: [ + // Dirty fix for Claud hallucinating regions + ...regionHotFixMiddlewares, + ], + }); + + return server; } diff --git a/src/server/types.ts b/src/server/types.ts index 7d83a9a..2d75b38 100644 --- a/src/server/types.ts +++ b/src/server/types.ts @@ -1,4 +1,13 @@ -import type { CliFilteringOptions } from "../toolFilters.ts"; +import { z } from "zod"; +import { CliFilteringOptionsSchema } from "../toolFilters.ts"; +export const StartServerOptionsSchema = CliFilteringOptionsSchema.extend({ + credentials: z + .object({ + applicationId: z.string(), + apiKey: z.string(), + }) + .optional(), +}); -export type StartServerOptions = CliFilteringOptions; \ No newline at end of file +export type StartServerOptions = z.infer; From 38d76e68db603dc1102667a1c980e3656b03d069 Mon Sep 17 00:00:00 2001 From: Thomas Raffray Date: Fri, 23 May 2025 20:35:46 +0200 Subject: [PATCH 13/13] chore: stdio doesn't do well with logs on stdin --- src/app.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app.ts b/src/app.ts index a1f2aba..533b5fd 100644 --- a/src/app.ts +++ b/src/app.ts @@ -82,7 +82,6 @@ program .action(async (opts) => { switch (opts.transport) { case "stdio": { - console.info('Starting server with stdio transport'); const { startServer } = await import("./commands/start-server.ts"); await startServer(opts); break;