Skip to content

Commit ebb2f68

Browse files
committed
add support for SSE
1 parent 2dae17d commit ebb2f68

File tree

4 files changed

+107
-34
lines changed

4 files changed

+107
-34
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ The Elasticsearch MCP Server supports configuration options to connect to your E
7272
|---------------------|-------------|----------|
7373
| `SSE_ADDR` | Enable SSE and set the ADDR | No |
7474
| `ES_URL` | Your Elasticsearch instance URL | Yes |
75-
| `ES_URL` | Your Elasticsearch instance URL | Yes |
7675
| `ES_API_KEY` | Elasticsearch API key for authentication | No |
7776
| `ES_USERNAME` | Elasticsearch username for basic authentication | No |
7877
| `ES_PASSWORD` | Elasticsearch password for basic authentication | No |

index.ts

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
*/
77

88
import express, { Request, Response } from "express";
9+
import morgan from "morgan";
910
import { z } from "zod";
1011
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
1112
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
12-
import { Client, estypes, ClientOptions } from "@elastic/elasticsearch";
13+
import { Client, ClientOptions, estypes } from "@elastic/elasticsearch";
1314
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
1415
import fs from "fs";
1516

@@ -73,13 +74,13 @@ const ConfigSchema = z
7374
message:
7475
"Either ES_API_KEY or both ES_USERNAME and ES_PASSWORD must be provided, or no auth for local development",
7576
path: ["username", "password"],
76-
}
77+
},
7778
);
7879

7980
type ElasticsearchConfig = z.infer<typeof ConfigSchema>;
8081

8182
export async function createElasticsearchMcpServer(
82-
config: ElasticsearchConfig
83+
config: ElasticsearchConfig,
8384
) {
8485
const validatedConfig = ConfigSchema.parse(config);
8586
const { url, apiKey, username, password, caCert } = validatedConfig;
@@ -104,7 +105,7 @@ export async function createElasticsearchMcpServer(
104105
console.error(
105106
`Failed to read certificate file: ${
106107
error instanceof Error ? error.message : String(error)
107-
}`
108+
}`,
108109
);
109110
}
110111
}
@@ -148,7 +149,7 @@ export async function createElasticsearchMcpServer(
148149
console.error(
149150
`Failed to list indices: ${
150151
error instanceof Error ? error.message : String(error)
151-
}`
152+
}`,
152153
);
153154
return {
154155
content: [
@@ -161,7 +162,7 @@ export async function createElasticsearchMcpServer(
161162
],
162163
};
163164
}
164-
}
165+
},
165166
);
166167

167168
// Tool 2: Get mappings for an index
@@ -189,19 +190,21 @@ export async function createElasticsearchMcpServer(
189190
},
190191
{
191192
type: "text" as const,
192-
text: `Mappings for index ${index}: ${JSON.stringify(
193-
mappingResponse[index]?.mappings || {},
194-
null,
195-
2
196-
)}`,
193+
text: `Mappings for index ${index}: ${
194+
JSON.stringify(
195+
mappingResponse[index]?.mappings || {},
196+
null,
197+
2,
198+
)
199+
}`,
197200
},
198201
],
199202
};
200203
} catch (error) {
201204
console.error(
202205
`Failed to get mappings: ${
203206
error instanceof Error ? error.message : String(error)
204-
}`
207+
}`,
205208
);
206209
return {
207210
content: [
@@ -214,7 +217,7 @@ export async function createElasticsearchMcpServer(
214217
],
215218
};
216219
}
217-
}
220+
},
218221
);
219222

220223
// Tool 3: Search an index with simplified parameters
@@ -241,10 +244,10 @@ export async function createElasticsearchMcpServer(
241244
},
242245
{
243246
message: "queryBody must be a valid Elasticsearch query DSL object",
244-
}
247+
},
245248
)
246249
.describe(
247-
"Complete Elasticsearch query DSL object that can include query, size, from, sort, etc."
250+
"Complete Elasticsearch query DSL object that can include query, size, from, sort, etc.",
248251
),
249252
},
250253
async ({ index, queryBody }) => {
@@ -265,9 +268,11 @@ export async function createElasticsearchMcpServer(
265268
if (indexMappings.properties) {
266269
const textFields: Record<string, estypes.SearchHighlightField> = {};
267270

268-
for (const [fieldName, fieldData] of Object.entries(
269-
indexMappings.properties
270-
)) {
271+
for (
272+
const [fieldName, fieldData] of Object.entries(
273+
indexMappings.properties,
274+
)
275+
) {
271276
if (fieldData.type === "text" || "dense_vector" in fieldData) {
272277
textFields[fieldName] = {};
273278
}
@@ -293,9 +298,11 @@ export async function createElasticsearchMcpServer(
293298

294299
for (const [field, highlights] of Object.entries(highlightedFields)) {
295300
if (highlights && highlights.length > 0) {
296-
content += `${field} (highlighted): ${highlights.join(
297-
" ... "
298-
)}\n`;
301+
content += `${field} (highlighted): ${
302+
highlights.join(
303+
" ... ",
304+
)
305+
}\n`;
299306
}
300307
}
301308

@@ -327,7 +334,7 @@ export async function createElasticsearchMcpServer(
327334
console.error(
328335
`Search failed: ${
329336
error instanceof Error ? error.message : String(error)
330-
}`
337+
}`,
331338
);
332339
return {
333340
content: [
@@ -340,7 +347,7 @@ export async function createElasticsearchMcpServer(
340347
],
341348
};
342349
}
343-
}
350+
},
344351
);
345352

346353
// Tool 4: Get shard information
@@ -391,7 +398,7 @@ export async function createElasticsearchMcpServer(
391398
console.error(
392399
`Failed to get shard information: ${
393400
error instanceof Error ? error.message : String(error)
394-
}`
401+
}`,
395402
);
396403
return {
397404
content: [
@@ -404,7 +411,7 @@ export async function createElasticsearchMcpServer(
404411
],
405412
};
406413
}
407-
}
414+
},
408415
);
409416

410417
return server;
@@ -422,10 +429,13 @@ const config: ElasticsearchConfig = {
422429
async function create_sse_server(server: any) {
423430
const app = express();
424431

425-
const transports: {[sessionId: string]: SSEServerTransport} = {};
432+
// Use morgan to log every request
433+
app.use(morgan("combined"));
434+
435+
const transports: { [sessionId: string]: SSEServerTransport } = {};
426436

427437
app.get("/sse", async (_: Request, res: Response) => {
428-
const transport = new SSEServerTransport('/messages', res);
438+
const transport = new SSEServerTransport("/messages", res);
429439
transports[transport.sessionId] = transport;
430440
res.on("close", () => {
431441
delete transports[transport.sessionId];
@@ -439,12 +449,20 @@ async function create_sse_server(server: any) {
439449
if (transport) {
440450
await transport.handlePostMessage(req, res);
441451
} else {
442-
res.status(400).send('No transport found for sessionId');
452+
res.status(400).send("No transport found for sessionId");
443453
}
444454
});
445455

446-
console.info("SSE server started on: ", process.env.SSE_ADDR);
447-
app.listen(process.env.SSE_ADDR);
456+
const sseAddr = process.env.SSE_ADDR || "127.0.0.1:3000";
457+
const [host, port] = sseAddr.split(":");
458+
if (!port) {
459+
console.error("Invalid SSE_ADDR format. Expected 'host:port'.");
460+
process.exit(1);
461+
}
462+
463+
app.listen(Number(port), host, () => {
464+
console.info(`SSE server started on: ${host}:${port}`);
465+
});
448466
}
449467

450468
async function main() {
@@ -467,7 +485,7 @@ async function main() {
467485
main().catch((error) => {
468486
console.error(
469487
"Server error:",
470-
error instanceof Error ? error.message : String(error)
488+
error instanceof Error ? error.message : String(error),
471489
);
472490
process.exit(1);
473491
});

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@
4040
"@elastic/elasticsearch": "^8.17.1",
4141
"@modelcontextprotocol/sdk": "1.9.0",
4242
"@types/express": "^5.0.1",
43-
"express": "^5.1.0"
43+
"@types/morgan": "^1.9.9",
44+
"express": "^5.1.0",
45+
"morgan": "^1.10.0"
4446
},
4547
"engines": {
4648
"node": ">=18"

yarn.lock

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,13 @@
127127
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690"
128128
integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==
129129

130+
"@types/morgan@^1.9.9":
131+
version "1.9.9"
132+
resolved "https://registry.yarnpkg.com/@types/morgan/-/morgan-1.9.9.tgz#d60dec3979e16c203a000159daa07d3fb7270d7f"
133+
integrity sha512-iRYSDKVaC6FkGSpEVVIvrRGw0DfJMiQzIn3qr2G5B3C//AWkulhXgaBd7tS9/J79GWSYMTHGs7PfI5b3Y8m+RQ==
134+
dependencies:
135+
"@types/node" "*"
136+
130137
"@types/node@*", "@types/[email protected]":
131138
version "22.14.1"
132139
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.14.1.tgz#53b54585cec81c21eee3697521e31312d6ca1e6f"
@@ -235,6 +242,13 @@ balanced-match@^1.0.0:
235242
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
236243
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
237244

245+
basic-auth@~2.0.1:
246+
version "2.0.1"
247+
resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a"
248+
integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==
249+
dependencies:
250+
safe-buffer "5.1.2"
251+
238252
body-parser@^2.2.0:
239253
version "2.2.0"
240254
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-2.2.0.tgz#f7a9656de305249a715b549b7b8fd1ab9dfddcfa"
@@ -409,6 +423,13 @@ cross-spawn@^7.0.3:
409423
shebang-command "^2.0.0"
410424
which "^2.0.1"
411425

426+
427+
version "2.6.9"
428+
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
429+
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
430+
dependencies:
431+
ms "2.0.0"
432+
412433
debug@^3.1.0:
413434
version "3.2.7"
414435
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
@@ -428,7 +449,7 @@ debuglog@^1.0.1:
428449
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
429450
integrity sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==
430451

431-
[email protected], depd@^2.0.0:
452+
[email protected], depd@^2.0.0, depd@~2.0.0:
432453
version "2.0.0"
433454
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
434455
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
@@ -893,6 +914,22 @@ mkdirp@^0.5.1:
893914
dependencies:
894915
minimist "^1.2.6"
895916

917+
morgan@^1.10.0:
918+
version "1.10.0"
919+
resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7"
920+
integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==
921+
dependencies:
922+
basic-auth "~2.0.1"
923+
debug "2.6.9"
924+
depd "~2.0.0"
925+
on-finished "~2.3.0"
926+
on-headers "~1.0.2"
927+
928+
929+
version "2.0.0"
930+
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
931+
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
932+
896933
ms@^2.1.1, ms@^2.1.3:
897934
version "2.1.3"
898935
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
@@ -955,6 +992,18 @@ on-finished@^2.4.1:
955992
dependencies:
956993
ee-first "1.1.1"
957994

995+
on-finished@~2.3.0:
996+
version "2.3.0"
997+
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
998+
integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==
999+
dependencies:
1000+
ee-first "1.1.1"
1001+
1002+
on-headers@~1.0.2:
1003+
version "1.0.2"
1004+
resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
1005+
integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
1006+
9581007
once@^1.3.0, once@^1.3.1, once@^1.4.0:
9591008
version "1.4.0"
9601009
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@@ -1141,6 +1190,11 @@ run-parallel@^1.1.9:
11411190
dependencies:
11421191
queue-microtask "^1.2.2"
11431192

1193+
1194+
version "5.1.2"
1195+
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
1196+
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
1197+
11441198
11451199
version "5.2.1"
11461200
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"

0 commit comments

Comments
 (0)