| 
1 | 1 | #!/usr/bin/env -S node --experimental-strip-types  | 
2 | 2 | 
 
  | 
3 |  | -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";  | 
4 |  | -import { authenticate } from "../authentication.ts";  | 
5 |  | -import { AppStateManager } from "../appState.ts";  | 
6 |  | -import { DashboardApi } from "../DashboardApi.ts";  | 
7 |  | -import {  | 
8 |  | -  operationId as GetUserInfoOperationId,  | 
9 |  | -  registerGetUserInfo,  | 
10 |  | -} from "../tools/registerGetUserInfo.ts";  | 
11 |  | -import {  | 
12 |  | -  operationId as GetApplicationsOperationId,  | 
13 |  | -  registerGetApplications,  | 
14 |  | -} from "../tools/registerGetApplications.ts";  | 
15 | 3 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";  | 
16 |  | -import { registerOpenApiTools } from "../tools/registerOpenApi.ts";  | 
17 |  | -import { CONFIG } from "../config.ts";  | 
18 |  | -import {  | 
19 |  | -  ABTestingSpec,  | 
20 |  | -  AnalyticsSpec,  | 
21 |  | -  CollectionsSpec,  | 
22 |  | -  IngestionSpec,  | 
23 |  | -  MonitoringSpec,  | 
24 |  | -  QuerySuggestionsSpec,  | 
25 |  | -  RecommendSpec,  | 
26 |  | -  SearchSpec,  | 
27 |  | -  UsageSpec,  | 
28 |  | -} from "../openApi.ts";  | 
29 |  | -import { type CliFilteringOptions, getToolFilter, isToolAllowed } from "../toolFilters.ts";  | 
30 |  | -import {  | 
31 |  | -  operationId as SetAttributesForFacetingOperationId,  | 
32 |  | -  registerSetAttributesForFaceting,  | 
33 |  | -} from "../tools/registerSetAttributesForFaceting.ts";  | 
34 |  | -import {  | 
35 |  | -  registerSetCustomRanking,  | 
36 |  | -  operationId as SetCustomRankingOperationId,  | 
37 |  | -} from "../tools/registerSetCustomRanking.ts";  | 
 | 4 | +import { type CliFilteringOptions } from "../toolFilters.ts";  | 
 | 5 | +import { initMCPServer } from "../server/init-server.js";  | 
38 | 6 | 
 
  | 
39 | 7 | export type StartServerOptions = CliFilteringOptions;  | 
40 | 8 | 
 
  | 
41 | 9 | export async function startServer(opts: StartServerOptions) {  | 
42 | 10 |   try {  | 
43 |  | -    const appState = await AppStateManager.load();  | 
44 |  | - | 
45 |  | -    if (!appState.get("accessToken")) {  | 
46 |  | -      const token = await authenticate();  | 
47 |  | - | 
48 |  | -      await appState.update({  | 
49 |  | -        accessToken: token.access_token,  | 
50 |  | -        refreshToken: token.refresh_token,  | 
51 |  | -      });  | 
52 |  | -    }  | 
53 |  | - | 
54 |  | -    const dashboardApi = new DashboardApi({  | 
55 |  | -      baseUrl: CONFIG.dashboardApiBaseUrl,  | 
56 |  | -      appState,  | 
57 |  | -    });  | 
58 |  | - | 
59 |  | -    const server = new McpServer({  | 
60 |  | -      name: "algolia",  | 
61 |  | -      version: CONFIG.version,  | 
62 |  | -      capabilities: {  | 
63 |  | -        resources: {},  | 
64 |  | -        tools: {},  | 
65 |  | -      },  | 
66 |  | -    });  | 
67 |  | - | 
68 |  | -    const toolFilter = getToolFilter(opts);  | 
69 |  | - | 
70 |  | -    // Dashboard API Tools  | 
71 |  | -    if (isToolAllowed(GetUserInfoOperationId, toolFilter)) {  | 
72 |  | -      registerGetUserInfo(server, dashboardApi);  | 
73 |  | -    }  | 
74 |  | - | 
75 |  | -    if (isToolAllowed(GetApplicationsOperationId, toolFilter)) {  | 
76 |  | -      registerGetApplications(server, dashboardApi);  | 
77 |  | -    }  | 
78 |  | - | 
79 |  | -    // Search API Tools  | 
80 |  | -    registerOpenApiTools({  | 
81 |  | -      server,  | 
82 |  | -      dashboardApi,  | 
83 |  | -      openApiSpec: SearchSpec,  | 
84 |  | -      toolFilter,  | 
85 |  | -    });  | 
86 |  | - | 
87 |  | -    // Analytics API Tools  | 
88 |  | -    registerOpenApiTools({  | 
89 |  | -      server,  | 
90 |  | -      dashboardApi,  | 
91 |  | -      openApiSpec: AnalyticsSpec,  | 
92 |  | -      toolFilter,  | 
93 |  | -    });  | 
94 |  | - | 
95 |  | -    // Recommend API Tools  | 
96 |  | -    registerOpenApiTools({  | 
97 |  | -      server,  | 
98 |  | -      dashboardApi,  | 
99 |  | -      openApiSpec: RecommendSpec,  | 
100 |  | -      toolFilter,  | 
101 |  | -    });  | 
102 |  | - | 
103 |  | -    // AB Testing  | 
104 |  | -    registerOpenApiTools({  | 
105 |  | -      server,  | 
106 |  | -      dashboardApi,  | 
107 |  | -      openApiSpec: ABTestingSpec,  | 
108 |  | -      toolFilter,  | 
109 |  | -    });  | 
110 |  | - | 
111 |  | -    // Monitoring API Tools  | 
112 |  | -    registerOpenApiTools({  | 
113 |  | -      server,  | 
114 |  | -      dashboardApi,  | 
115 |  | -      openApiSpec: MonitoringSpec,  | 
116 |  | -      toolFilter,  | 
117 |  | -    });  | 
118 |  | - | 
119 |  | -    // Usage  | 
120 |  | -    registerOpenApiTools({  | 
121 |  | -      server,  | 
122 |  | -      dashboardApi,  | 
123 |  | -      openApiSpec: UsageSpec,  | 
124 |  | -      toolFilter,  | 
125 |  | -      requestMiddlewares: [  | 
126 |  | -        // The Usage API expects `name` parameter as multiple values  | 
127 |  | -        // rather than comma-separated.  | 
128 |  | -        async ({ request }) => {  | 
129 |  | -          const url = new URL(request.url);  | 
130 |  | -          const nameParams = url.searchParams.get("name");  | 
131 |  | - | 
132 |  | -          if (!nameParams) {  | 
133 |  | -            return new Request(url, request.clone());  | 
134 |  | -          }  | 
135 |  | - | 
136 |  | -          const nameValues = nameParams.split(",");  | 
137 |  | - | 
138 |  | -          url.searchParams.delete("name");  | 
139 |  | - | 
140 |  | -          nameValues.forEach((value) => {  | 
141 |  | -            url.searchParams.append("name", value);  | 
142 |  | -          });  | 
143 |  | - | 
144 |  | -          return new Request(url, request.clone());  | 
145 |  | -        },  | 
146 |  | -      ],  | 
147 |  | -    });  | 
148 |  | - | 
149 |  | -    // Ingestion API Tools  | 
150 |  | -    registerOpenApiTools({  | 
151 |  | -      server,  | 
152 |  | -      dashboardApi,  | 
153 |  | -      openApiSpec: IngestionSpec,  | 
154 |  | -      toolFilter,  | 
155 |  | -      requestMiddlewares: [  | 
156 |  | -        // Dirty fix for Claud hallucinating regions  | 
157 |  | -        async ({ request, params }) => {  | 
158 |  | -          const application = await dashboardApi.getApplication(params.applicationId);  | 
159 |  | -          const region = application.data.attributes.log_region === "de" ? "eu" : "us";  | 
160 |  | - | 
161 |  | -          const url = new URL(request.url);  | 
162 |  | -          const regionFromUrl = url.hostname.match(/data\.(.+)\.algolia.com/)?.[0];  | 
163 |  | - | 
164 |  | -          if (regionFromUrl !== region) {  | 
165 |  | -            console.error("Had to adjust region from", regionFromUrl, "to", region);  | 
166 |  | -            url.hostname = `data.${region}.algolia.com`;  | 
167 |  | -            return new Request(url, request.clone());  | 
168 |  | -          }  | 
169 |  | - | 
170 |  | -          return request;  | 
171 |  | -        },  | 
172 |  | -      ],  | 
173 |  | -    });  | 
174 |  | - | 
175 |  | -    // Collections API Tools  | 
176 |  | -    registerOpenApiTools({  | 
177 |  | -      server,  | 
178 |  | -      dashboardApi,  | 
179 |  | -      openApiSpec: CollectionsSpec,  | 
180 |  | -      toolFilter,  | 
181 |  | -    });  | 
182 |  | - | 
183 |  | -    // Query Suggestions API Tools  | 
184 |  | -    registerOpenApiTools({  | 
185 |  | -      server,  | 
186 |  | -      dashboardApi,  | 
187 |  | -      openApiSpec: QuerySuggestionsSpec,  | 
188 |  | -      toolFilter,  | 
189 |  | -    });  | 
190 |  | - | 
191 |  | -    // Custom settings Tools  | 
192 |  | -    if (isToolAllowed(SetAttributesForFacetingOperationId, toolFilter)) {  | 
193 |  | -      registerSetAttributesForFaceting(server, dashboardApi);  | 
194 |  | -    }  | 
195 |  | - | 
196 |  | -    if (isToolAllowed(SetCustomRankingOperationId, toolFilter)) {  | 
197 |  | -      registerSetCustomRanking(server, dashboardApi);  | 
198 |  | -    }  | 
 | 11 | +    const server = await initMCPServer(opts);  | 
199 | 12 | 
 
  | 
200 | 13 |     const transport = new StdioServerTransport();  | 
201 | 14 |     await server.connect(transport);  | 
 | 
0 commit comments