@omophub/omophub-mcp 1.5.1 → 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +119 -3
- package/dist/index.js.map +1 -1
- package/dist/transports/http.d.ts +31 -1
- package/dist/transports/http.d.ts.map +1 -1
- package/dist/transports/http.js +317 -37
- package/dist/transports/http.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -11,4 +11,12 @@ export declare function resolveHealthPort(cliPort?: number): number | undefined;
|
|
|
11
11
|
export declare function resolveTransport(cliTransport?: TransportType): TransportType;
|
|
12
12
|
export declare function resolvePort(cliPort?: number): number;
|
|
13
13
|
export declare function main(): Promise<void>;
|
|
14
|
+
/** Normalizes any thrown/rejected value to a loggable string, without ever
|
|
15
|
+
* throwing itself — this runs on the fatal shutdown path, so a value that
|
|
16
|
+
* can't be coerced (a null-prototype object, or one whose `toString` /
|
|
17
|
+
* `Symbol.toPrimitive` throws) must not take out the diagnostic. Also handles
|
|
18
|
+
* a non-Error throw (`throw 'boom'`) and `throw null` (where `.stack` would
|
|
19
|
+
* throw). */
|
|
20
|
+
export declare function formatError(value: unknown): string;
|
|
21
|
+
export declare function installProcessSafetyNets(): void;
|
|
14
22
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAaA,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,CAAC;AAI7C,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAiCA;AAED,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAYtE;AAED,wBAAgB,gBAAgB,CAAC,YAAY,CAAC,EAAE,aAAa,GAAG,aAAa,CAS5E;AAED,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAYpD;AAED,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAwC1C;AAaD;;;;;cAKc;AACd,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAYlD;AAyFD,wBAAgB,wBAAwB,IAAI,IAAI,CAQ/C"}
|
package/dist/index.js
CHANGED
|
@@ -88,13 +88,16 @@ export async function main() {
|
|
|
88
88
|
if (transportType === 'http') {
|
|
89
89
|
const port = resolvePort(args.port);
|
|
90
90
|
logger.info('Starting OMOPHub MCP server (http transport)');
|
|
91
|
-
|
|
91
|
+
// Kept so the fatal path can stop accepting new work before exiting.
|
|
92
|
+
runningServer = await startHttpTransport(createServer, defaultClient, port);
|
|
92
93
|
}
|
|
93
94
|
else {
|
|
94
95
|
const server = createServer(defaultClient);
|
|
95
96
|
const transport = new StdioServerTransport();
|
|
96
97
|
logger.info('Starting OMOPHub MCP server (stdio transport)');
|
|
97
98
|
await server.connect(transport);
|
|
99
|
+
// Kept so the fatal path can stop processing stdin before exiting.
|
|
100
|
+
stdioTransport = transport;
|
|
98
101
|
const healthPort = resolveHealthPort(args.healthPort);
|
|
99
102
|
if (healthPort !== undefined) {
|
|
100
103
|
startHealthServer(healthPort);
|
|
@@ -112,10 +115,123 @@ function isRunDirectly() {
|
|
|
112
115
|
}
|
|
113
116
|
}
|
|
114
117
|
const isDirectRun = isRunDirectly();
|
|
118
|
+
/** Normalizes any thrown/rejected value to a loggable string, without ever
|
|
119
|
+
* throwing itself — this runs on the fatal shutdown path, so a value that
|
|
120
|
+
* can't be coerced (a null-prototype object, or one whose `toString` /
|
|
121
|
+
* `Symbol.toPrimitive` throws) must not take out the diagnostic. Also handles
|
|
122
|
+
* a non-Error throw (`throw 'boom'`) and `throw null` (where `.stack` would
|
|
123
|
+
* throw). */
|
|
124
|
+
export function formatError(value) {
|
|
125
|
+
try {
|
|
126
|
+
// String(...) the selected property too: an Error can carry a non-string
|
|
127
|
+
// `stack`/`message` (reassigned, or a getter), and returning that non-string
|
|
128
|
+
// would only defer the failure to the caller's JSON.stringify (which throws
|
|
129
|
+
// on e.g. a BigInt or circular value). Coercing here keeps the fallback
|
|
130
|
+
// effective for those malformed Errors as well.
|
|
131
|
+
if (value instanceof Error)
|
|
132
|
+
return String(value.stack ?? value.message);
|
|
133
|
+
return String(value);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return '[unrepresentable error value]';
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
let shuttingDown = false;
|
|
140
|
+
let pendingFatalWrites = 0;
|
|
141
|
+
let exited = false;
|
|
142
|
+
let runningServer;
|
|
143
|
+
// In stdio mode there is no HTTP server; keep the transport so the fatal path
|
|
144
|
+
// can stop reading/processing stdin messages during the shutdown window.
|
|
145
|
+
let stdioTransport;
|
|
146
|
+
function exitNow() {
|
|
147
|
+
if (exited)
|
|
148
|
+
return;
|
|
149
|
+
exited = true;
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
/** Exit once shutdown has started AND every queued fatal write has flushed, so
|
|
153
|
+
* a diagnostic from a second simultaneous fatal event isn't truncated by the
|
|
154
|
+
* first write's completion triggering the exit. */
|
|
155
|
+
function exitWhenDrained() {
|
|
156
|
+
if (shuttingDown && pendingFatalWrites === 0)
|
|
157
|
+
exitNow();
|
|
158
|
+
}
|
|
159
|
+
/** Writes one fatal diagnostic line to stderr (an async pipe under Docker),
|
|
160
|
+
* matching the logger's format, and tracks it as pending so the process
|
|
161
|
+
* doesn't exit before it drains to the OS. */
|
|
162
|
+
function writeFatalLine(message, value) {
|
|
163
|
+
const line = `[${new Date().toISOString()}] ERROR ${message} ${JSON.stringify({
|
|
164
|
+
error: formatError(value),
|
|
165
|
+
})}\n`;
|
|
166
|
+
pendingFatalWrites++;
|
|
167
|
+
const done = () => {
|
|
168
|
+
pendingFatalWrites--;
|
|
169
|
+
exitWhenDrained();
|
|
170
|
+
};
|
|
171
|
+
try {
|
|
172
|
+
process.stderr.write(line, done);
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
done();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Logs a fatal condition and terminates for a clean, supervised restart.
|
|
180
|
+
*
|
|
181
|
+
* We do NOT resume. Once an exception or rejection reaches the process top,
|
|
182
|
+
* we know nothing about what state was left behind — half-applied work, an
|
|
183
|
+
* un-released handle, a transport mid-write — so continuing is undefined
|
|
184
|
+
* behaviour (and Node's own default for both signals is to crash). Aborted
|
|
185
|
+
* client requests, the one routine case, are already caught at their boundary
|
|
186
|
+
* in the HTTP transport and never reach here, so anything that does is a
|
|
187
|
+
* genuine unknown.
|
|
188
|
+
*
|
|
189
|
+
* Shutdown (once, on the first fatal event): stop the HTTP server accepting
|
|
190
|
+
* new connections AND drop existing ones — otherwise a client on a kept-alive
|
|
191
|
+
* connection could still be served in a state we've declared unsafe. Then emit
|
|
192
|
+
* the diagnostic; the process exits once every fatal write has flushed to
|
|
193
|
+
* stderr (so simultaneous fatal events aren't lost), or after a 1s fallback if
|
|
194
|
+
* a write callback never fires.
|
|
195
|
+
*
|
|
196
|
+
* NOTE: this assumes the container runs under a restart policy that restarts
|
|
197
|
+
* on a non-zero exit — Docker's `always` or `unless-stopped` both work; they
|
|
198
|
+
* differ only in how they treat a manual `docker stop` across a daemon
|
|
199
|
+
* restart, which is irrelevant here. Without any restart policy, a fatal
|
|
200
|
+
* condition now stops the service instead of limping on.
|
|
201
|
+
*/
|
|
202
|
+
function fatalExit(message, value) {
|
|
203
|
+
process.exitCode = 1;
|
|
204
|
+
const first = !shuttingDown;
|
|
205
|
+
shuttingDown = true;
|
|
206
|
+
if (first) {
|
|
207
|
+
try {
|
|
208
|
+
// http mode: refuse new connections and drop in-flight/kept-alive ones.
|
|
209
|
+
runningServer?.close();
|
|
210
|
+
runningServer?.closeAllConnections();
|
|
211
|
+
// stdio mode: stop reading/processing further messages from stdin.
|
|
212
|
+
void stdioTransport?.close();
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// already closed / never started — nothing to do
|
|
216
|
+
}
|
|
217
|
+
setTimeout(exitNow, 1000); // hard fallback if a write never drains
|
|
218
|
+
}
|
|
219
|
+
// Always record the diagnostic — including a second simultaneous fatal
|
|
220
|
+
// event; the exit waits until all such writes have flushed.
|
|
221
|
+
writeFatalLine(message, value);
|
|
222
|
+
}
|
|
223
|
+
export function installProcessSafetyNets() {
|
|
224
|
+
process.on('unhandledRejection', (reason) => {
|
|
225
|
+
fatalExit('Unhandled promise rejection — exiting for a clean restart', reason);
|
|
226
|
+
});
|
|
227
|
+
process.on('uncaughtException', (error) => {
|
|
228
|
+
fatalExit('Uncaught exception — exiting for a clean restart', error);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
115
231
|
if (isDirectRun) {
|
|
232
|
+
installProcessSafetyNets();
|
|
116
233
|
main().catch((error) => {
|
|
117
|
-
|
|
118
|
-
process.exit(1);
|
|
234
|
+
fatalExit('Fatal error during startup', error);
|
|
119
235
|
});
|
|
120
236
|
}
|
|
121
237
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,39 @@
|
|
|
1
|
-
import { type Server } from 'node:http';
|
|
1
|
+
import { type Server, type ServerResponse } from 'node:http';
|
|
2
2
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
3
4
|
import type { OmopHubClient } from '../client/api.js';
|
|
5
|
+
interface Session {
|
|
6
|
+
transport: StreamableHTTPServerTransport;
|
|
7
|
+
lastSeenAt: number;
|
|
8
|
+
openStreams: number;
|
|
9
|
+
}
|
|
10
|
+
/** Reads a positive-integer tuning value from the environment. Uses `Number`
|
|
11
|
+
* (not `parseInt`) so a malformed value like "100abc" or "1e3x" is rejected
|
|
12
|
+
* whole rather than silently coerced to a partial number; on rejection it
|
|
13
|
+
* warns once and uses the documented fallback. Exported for testing. */
|
|
14
|
+
export declare function envPositiveInt(name: string, fallback: number): number;
|
|
15
|
+
/** Marks a long-lived response (GET SSE stream, or streaming POST) as open for
|
|
16
|
+
* the session and refreshes the idle clock when it closes. While open, the
|
|
17
|
+
* session is exempt from the idle sweep even if no new requests arrive. */
|
|
18
|
+
export declare function trackStream(session: Session, res: ServerResponse): void;
|
|
19
|
+
/** Closes sessions idle past the timeout. transport.close() fires onclose,
|
|
20
|
+
* which removes the map entry and shuts down the attached McpServer.
|
|
21
|
+
* Sessions with an open stream are actively connected and never reaped. */
|
|
22
|
+
export declare function sweepIdleSessions(now?: number): number;
|
|
23
|
+
/**
|
|
24
|
+
* Picks the least-recently-seen session that has NO open stream — the same
|
|
25
|
+
* "an open stream means actively connected" rule the idle sweep uses, so the
|
|
26
|
+
* capacity backstop never disconnects a working SSE/streaming client. Returns
|
|
27
|
+
* undefined when every session is streaming. Exported for unit testing.
|
|
28
|
+
*/
|
|
29
|
+
export declare function pickEvictableSessionId(entries: Iterable<[string, {
|
|
30
|
+
lastSeenAt: number;
|
|
31
|
+
openStreams: number;
|
|
32
|
+
}]>): string | undefined;
|
|
4
33
|
/**
|
|
5
34
|
* Starts an HTTP server with per-session MCP transports on /
|
|
6
35
|
* and a health endpoint on /health.
|
|
7
36
|
*/
|
|
8
37
|
export declare function startHttpTransport(serverFactory: (client: OmopHubClient) => McpServer, defaultClient: OmopHubClient, port: number): Promise<Server>;
|
|
38
|
+
export {};
|
|
9
39
|
//# sourceMappingURL=http.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/transports/http.ts"],"names":[],"mappings":"AACA,OAAO,EAAsC,KAAK,MAAM,
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/transports/http.ts"],"names":[],"mappings":"AACA,OAAO,EAAsC,KAAK,MAAM,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AACjG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAEnG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAiBtD,UAAU,OAAO;IACf,SAAS,EAAE,6BAA6B,CAAC;IACzC,UAAU,EAAE,MAAM,CAAC;IAInB,WAAW,EAAE,MAAM,CAAC;CACrB;AAiCD;;;yEAGyE;AACzE,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUrE;AAUD;;4EAE4E;AAC5E,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,IAAI,CAYvE;AAED;;4EAE4E;AAC5E,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAmB,GAAG,MAAM,CAalE;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,QAAQ,CAAC,CAAC,MAAM,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,GACvE,MAAM,GAAG,SAAS,CAWpB;AAgCD;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,aAAa,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,SAAS,EACnD,aAAa,EAAE,aAAa,EAC5B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC,CA8TjB"}
|
package/dist/transports/http.js
CHANGED
|
@@ -15,9 +15,134 @@ function setCorsHeaders(res) {
|
|
|
15
15
|
res.setHeader(key, value);
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
-
// Active sessions — each client gets its own transport + server
|
|
18
|
+
// Active sessions — each client gets its own transport + server.
|
|
19
|
+
//
|
|
20
|
+
// Cleanup is driven by idle timeout, not by socket lifecycle. The SDK only
|
|
21
|
+
// fires transport.onclose from close(), which it calls solely from the DELETE
|
|
22
|
+
// handler, so any client that vanishes without a clean shutdown (network drop,
|
|
23
|
+
// sleep, crash) would otherwise leak its transport AND its McpServer forever.
|
|
24
|
+
//
|
|
25
|
+
// A session is reaped only when it has NO open stream AND has not been seen
|
|
26
|
+
// for the idle timeout. We deliberately do NOT close sessions the moment a
|
|
27
|
+
// socket drops: Streamable HTTP clients legitimately disconnect and reconnect
|
|
28
|
+
// their SSE stream, and a client can hold an SSE stream open for hours while
|
|
29
|
+
// only receiving server pushes, so socket- or last-request-based reaping alone
|
|
30
|
+
// would disconnect working clients.
|
|
19
31
|
const sessions = new Map();
|
|
32
|
+
// Inits that passed the admission cap but haven't been inserted into `sessions`
|
|
33
|
+
// yet (initialization is async). Counted against the cap so a burst of
|
|
34
|
+
// concurrent initializes can't all pass the pre-check and overshoot it.
|
|
35
|
+
let pendingAdmissions = 0;
|
|
20
36
|
const MAX_BODY_SIZE = 1_048_576; // 1 MB
|
|
37
|
+
const SESSION_IDLE_TIMEOUT_MS = 30 * 60 * 1000; // 30 min without a request
|
|
38
|
+
const SESSION_SWEEP_INTERVAL_MS = 60 * 1000;
|
|
39
|
+
// Backstops, overridable via env for ops tuning (and tests). Read lazily so a
|
|
40
|
+
// value can change without reloading the module.
|
|
41
|
+
const DEFAULT_MAX_SESSIONS = 1000; // hard cap on concurrent sessions
|
|
42
|
+
const DEFAULT_REQUEST_BODY_TIMEOUT_MS = 30_000; // max time to receive a POST body
|
|
43
|
+
const warnedInvalidEnv = new Set();
|
|
44
|
+
/** Reads a positive-integer tuning value from the environment. Uses `Number`
|
|
45
|
+
* (not `parseInt`) so a malformed value like "100abc" or "1e3x" is rejected
|
|
46
|
+
* whole rather than silently coerced to a partial number; on rejection it
|
|
47
|
+
* warns once and uses the documented fallback. Exported for testing. */
|
|
48
|
+
export function envPositiveInt(name, fallback) {
|
|
49
|
+
const raw = process.env[name];
|
|
50
|
+
if (raw === undefined || raw === '')
|
|
51
|
+
return fallback;
|
|
52
|
+
const n = Number(raw);
|
|
53
|
+
if (Number.isSafeInteger(n) && n > 0)
|
|
54
|
+
return n;
|
|
55
|
+
if (!warnedInvalidEnv.has(name)) {
|
|
56
|
+
warnedInvalidEnv.add(name);
|
|
57
|
+
logger.warn(`Ignoring invalid ${name}="${raw}"; using default ${fallback}`);
|
|
58
|
+
}
|
|
59
|
+
return fallback;
|
|
60
|
+
}
|
|
61
|
+
const getMaxSessions = () => envPositiveInt('OMOPHUB_MAX_SESSIONS', DEFAULT_MAX_SESSIONS);
|
|
62
|
+
const getRequestBodyTimeoutMs = () => envPositiveInt('OMOPHUB_REQUEST_BODY_TIMEOUT_MS', DEFAULT_REQUEST_BODY_TIMEOUT_MS);
|
|
63
|
+
function touchSession(sessionId) {
|
|
64
|
+
const session = sessions.get(sessionId);
|
|
65
|
+
if (session)
|
|
66
|
+
session.lastSeenAt = Date.now();
|
|
67
|
+
}
|
|
68
|
+
/** Marks a long-lived response (GET SSE stream, or streaming POST) as open for
|
|
69
|
+
* the session and refreshes the idle clock when it closes. While open, the
|
|
70
|
+
* session is exempt from the idle sweep even if no new requests arrive. */
|
|
71
|
+
export function trackStream(session, res) {
|
|
72
|
+
// If the response already finished (e.g. the client aborted before we got
|
|
73
|
+
// here), its 'close' event has already fired and will never fire again —
|
|
74
|
+
// attaching a listener would leave openStreams stuck at 1 and pin the
|
|
75
|
+
// session (and its McpServer) forever, since the idle sweep skips it.
|
|
76
|
+
if (res.destroyed || res.writableEnded)
|
|
77
|
+
return;
|
|
78
|
+
session.openStreams++;
|
|
79
|
+
res.once('close', () => {
|
|
80
|
+
session.openStreams = Math.max(0, session.openStreams - 1);
|
|
81
|
+
// Reset the idle clock on disconnect so a reconnect window is preserved.
|
|
82
|
+
session.lastSeenAt = Date.now();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/** Closes sessions idle past the timeout. transport.close() fires onclose,
|
|
86
|
+
* which removes the map entry and shuts down the attached McpServer.
|
|
87
|
+
* Sessions with an open stream are actively connected and never reaped. */
|
|
88
|
+
export function sweepIdleSessions(now = Date.now()) {
|
|
89
|
+
let closed = 0;
|
|
90
|
+
for (const [sessionId, session] of sessions) {
|
|
91
|
+
if (session.openStreams > 0)
|
|
92
|
+
continue;
|
|
93
|
+
if (now - session.lastSeenAt <= SESSION_IDLE_TIMEOUT_MS)
|
|
94
|
+
continue;
|
|
95
|
+
logger.info('Closing idle MCP session', { sessionId });
|
|
96
|
+
void Promise.resolve(session.transport.close()).catch((error) => {
|
|
97
|
+
logger.error('Failed to close idle session', { error: String(error), sessionId });
|
|
98
|
+
sessions.delete(sessionId); // never let a failed close pin the entry
|
|
99
|
+
});
|
|
100
|
+
closed++;
|
|
101
|
+
}
|
|
102
|
+
return closed;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Picks the least-recently-seen session that has NO open stream — the same
|
|
106
|
+
* "an open stream means actively connected" rule the idle sweep uses, so the
|
|
107
|
+
* capacity backstop never disconnects a working SSE/streaming client. Returns
|
|
108
|
+
* undefined when every session is streaming. Exported for unit testing.
|
|
109
|
+
*/
|
|
110
|
+
export function pickEvictableSessionId(entries) {
|
|
111
|
+
let oldestId;
|
|
112
|
+
let oldestSeenAt = Number.POSITIVE_INFINITY;
|
|
113
|
+
for (const [sessionId, session] of entries) {
|
|
114
|
+
if (session.openStreams > 0)
|
|
115
|
+
continue;
|
|
116
|
+
if (session.lastSeenAt < oldestSeenAt) {
|
|
117
|
+
oldestSeenAt = session.lastSeenAt;
|
|
118
|
+
oldestId = sessionId;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return oldestId;
|
|
122
|
+
}
|
|
123
|
+
/** Tries to free a slot by evicting the oldest non-streaming session. Never
|
|
124
|
+
* closes a session with a live stream. If none can be evicted (all streaming)
|
|
125
|
+
* the map size is unchanged and the caller rejects the new session, keeping
|
|
126
|
+
* the cap hard so held-open streams can't drive unbounded admission. */
|
|
127
|
+
function evictOldestSession() {
|
|
128
|
+
const oldestId = pickEvictableSessionId(sessions);
|
|
129
|
+
if (!oldestId) {
|
|
130
|
+
// Every session is streaming — nothing safe to evict. The caller enforces
|
|
131
|
+
// the hard cap by rejecting the new session; we just report the state.
|
|
132
|
+
logger.warn('Session limit reached and all sessions are streaming; cannot evict', {
|
|
133
|
+
maxSessions: getMaxSessions(),
|
|
134
|
+
});
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const session = sessions.get(oldestId);
|
|
138
|
+
logger.warn('Session limit reached, evicting oldest idle session', {
|
|
139
|
+
sessionId: oldestId,
|
|
140
|
+
maxSessions: getMaxSessions(),
|
|
141
|
+
});
|
|
142
|
+
void Promise.resolve(session?.transport.close()).catch(() => {
|
|
143
|
+
sessions.delete(oldestId);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
21
146
|
const JSON_RPC_INTERNAL_ERROR = JSON.stringify({
|
|
22
147
|
jsonrpc: '2.0',
|
|
23
148
|
error: { code: -32603, message: 'Internal server error' },
|
|
@@ -28,7 +153,30 @@ const JSON_RPC_INTERNAL_ERROR = JSON.stringify({
|
|
|
28
153
|
* and a health endpoint on /health.
|
|
29
154
|
*/
|
|
30
155
|
export async function startHttpTransport(serverFactory, defaultClient, port) {
|
|
31
|
-
const httpServer = createServer(
|
|
156
|
+
const httpServer = createServer((req, res) => {
|
|
157
|
+
// The handler is async; an unhandled rejection here (e.g. the request
|
|
158
|
+
// stream aborting mid-body with ECONNRESET) would terminate the process.
|
|
159
|
+
void handleRequest(req, res).catch((error) => {
|
|
160
|
+
// A client that aborted mid-request is routine, not an error worth alarming on.
|
|
161
|
+
const aborted = error?.code === 'ECONNRESET' ||
|
|
162
|
+
req.destroyed ||
|
|
163
|
+
res.destroyed;
|
|
164
|
+
if (aborted) {
|
|
165
|
+
logger.debug('Client aborted request', { error: String(error) });
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
logger.error('Unhandled request error', { error: String(error) });
|
|
169
|
+
}
|
|
170
|
+
if (!res.headersSent && !res.writableEnded) {
|
|
171
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
172
|
+
res.end(JSON_RPC_INTERNAL_ERROR);
|
|
173
|
+
}
|
|
174
|
+
else if (!res.writableEnded) {
|
|
175
|
+
res.end();
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
async function handleRequest(req, res) {
|
|
32
180
|
setCorsHeaders(res);
|
|
33
181
|
if (req.method === 'OPTIONS') {
|
|
34
182
|
res.writeHead(204);
|
|
@@ -44,11 +192,16 @@ export async function startHttpTransport(serverFactory, defaultClient, port) {
|
|
|
44
192
|
// GET (SSE stream) and DELETE (session close) carry no body — route directly
|
|
45
193
|
if (req.method === 'GET' || req.method === 'DELETE') {
|
|
46
194
|
if (sessionId && sessions.has(sessionId)) {
|
|
47
|
-
const
|
|
48
|
-
if (!
|
|
195
|
+
const session = sessions.get(sessionId);
|
|
196
|
+
if (!session)
|
|
49
197
|
return;
|
|
198
|
+
touchSession(sessionId);
|
|
199
|
+
// A GET opens the long-lived SSE stream; track it so the idle sweep
|
|
200
|
+
// won't reap a client that's connected but quietly receiving pushes.
|
|
201
|
+
if (req.method === 'GET')
|
|
202
|
+
trackStream(session, res);
|
|
50
203
|
try {
|
|
51
|
-
await transport.handleRequest(req, res);
|
|
204
|
+
await session.transport.handleRequest(req, res);
|
|
52
205
|
}
|
|
53
206
|
catch (error) {
|
|
54
207
|
logger.error('MCP transport error', { error: String(error), sessionId });
|
|
@@ -68,26 +221,72 @@ export async function startHttpTransport(serverFactory, defaultClient, port) {
|
|
|
68
221
|
}
|
|
69
222
|
return;
|
|
70
223
|
}
|
|
71
|
-
// POST
|
|
224
|
+
// POST. If it targets a known session, mark it active BEFORE reading the
|
|
225
|
+
// (possibly slow or trickled) body — otherwise a concurrent sweep could
|
|
226
|
+
// reap the session mid-upload. trackStream keeps it alive for the whole
|
|
227
|
+
// request/response and refreshes the idle clock when the response closes.
|
|
228
|
+
const postSession = sessionId ? sessions.get(sessionId) : undefined;
|
|
229
|
+
if (postSession && sessionId) {
|
|
230
|
+
touchSession(sessionId);
|
|
231
|
+
trackStream(postSession, res);
|
|
232
|
+
}
|
|
233
|
+
// POST — read body with a size limit AND a total-time limit. The time
|
|
234
|
+
// limit bounds trickled/stalled uploads: without it a client could hold
|
|
235
|
+
// a session slot (and up to MAX_BODY_SIZE of buffer) open indefinitely.
|
|
72
236
|
const chunks = [];
|
|
73
237
|
let totalBytes = 0;
|
|
74
238
|
let aborted = false;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
239
|
+
let bodyTimedOut = false;
|
|
240
|
+
const bodyTimer = setTimeout(() => {
|
|
241
|
+
bodyTimedOut = true;
|
|
242
|
+
// req and res share a socket, so destroying req first would reset the
|
|
243
|
+
// connection and the client would never see the 408. Write the response
|
|
244
|
+
// and tear down the request only AFTER it has flushed. Destroying req is
|
|
245
|
+
// what makes the for-await below throw (caught as a timeout, not an error).
|
|
246
|
+
if (!res.headersSent) {
|
|
247
|
+
res.writeHead(408, { 'Content-Type': 'application/json' });
|
|
81
248
|
res.end(JSON.stringify({
|
|
82
249
|
jsonrpc: '2.0',
|
|
83
|
-
error: { code: -32000, message: '
|
|
250
|
+
error: { code: -32000, message: 'Request body timeout' },
|
|
84
251
|
id: null,
|
|
85
|
-
}));
|
|
86
|
-
|
|
87
|
-
|
|
252
|
+
}), () => req.destroy());
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
req.destroy();
|
|
256
|
+
}
|
|
257
|
+
}, getRequestBodyTimeoutMs());
|
|
258
|
+
try {
|
|
259
|
+
for await (const chunk of req) {
|
|
260
|
+
const buf = typeof chunk === 'string' ? Buffer.from(chunk) : chunk;
|
|
261
|
+
totalBytes += buf.length;
|
|
262
|
+
if (totalBytes > MAX_BODY_SIZE) {
|
|
263
|
+
// Same socket-sharing concern as the timeout path: req and res share
|
|
264
|
+
// a socket, so destroying req before the response flushes would reset
|
|
265
|
+
// the connection and the client would never see the 413. Write the
|
|
266
|
+
// response, then tear down the request from the flush callback.
|
|
267
|
+
res.writeHead(413, { 'Content-Type': 'application/json' });
|
|
268
|
+
res.end(JSON.stringify({
|
|
269
|
+
jsonrpc: '2.0',
|
|
270
|
+
error: { code: -32000, message: 'Payload too large' },
|
|
271
|
+
id: null,
|
|
272
|
+
}), () => req.destroy());
|
|
273
|
+
aborted = true;
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
chunks.push(buf);
|
|
88
277
|
}
|
|
89
|
-
chunks.push(buf);
|
|
90
278
|
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
// A genuine mid-body abort (ECONNRESET) is not ours to handle here —
|
|
281
|
+
// let it propagate to the request wrapper. A timeout-induced destroy is.
|
|
282
|
+
if (!bodyTimedOut)
|
|
283
|
+
throw error;
|
|
284
|
+
}
|
|
285
|
+
finally {
|
|
286
|
+
clearTimeout(bodyTimer);
|
|
287
|
+
}
|
|
288
|
+
if (bodyTimedOut)
|
|
289
|
+
return; // the 408 was already written in the timeout callback
|
|
91
290
|
if (aborted)
|
|
92
291
|
return;
|
|
93
292
|
const body = Buffer.concat(chunks).toString();
|
|
@@ -104,13 +303,23 @@ export async function startHttpTransport(serverFactory, defaultClient, port) {
|
|
|
104
303
|
}));
|
|
105
304
|
return;
|
|
106
305
|
}
|
|
107
|
-
// Existing session
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
306
|
+
// Existing session — marked active before the body was read. Re-validate:
|
|
307
|
+
// the session may have been closed (DELETE, or its transport erroring)
|
|
308
|
+
// while we awaited the body, in which case dispatching to it is wrong.
|
|
309
|
+
if (postSession) {
|
|
310
|
+
if (!sessionId || sessions.get(sessionId) !== postSession) {
|
|
311
|
+
if (!res.headersSent) {
|
|
312
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
313
|
+
res.end(JSON.stringify({
|
|
314
|
+
jsonrpc: '2.0',
|
|
315
|
+
error: { code: -32001, message: 'Session no longer exists' },
|
|
316
|
+
id: null,
|
|
317
|
+
}));
|
|
318
|
+
}
|
|
111
319
|
return;
|
|
320
|
+
}
|
|
112
321
|
try {
|
|
113
|
-
await transport.handleRequest(req, res, parsedBody);
|
|
322
|
+
await postSession.transport.handleRequest(req, res, parsedBody);
|
|
114
323
|
}
|
|
115
324
|
catch (error) {
|
|
116
325
|
logger.error('MCP transport error', { error: String(error), sessionId });
|
|
@@ -123,22 +332,68 @@ export async function startHttpTransport(serverFactory, defaultClient, port) {
|
|
|
123
332
|
}
|
|
124
333
|
// New session (initialize request)
|
|
125
334
|
if (!sessionId && isInitializeRequest(parsedBody)) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (
|
|
135
|
-
|
|
136
|
-
|
|
335
|
+
// Hard admission cap. The effective count includes in-flight inits
|
|
336
|
+
// (pendingAdmissions) that haven't been inserted yet, so concurrent
|
|
337
|
+
// initializes can't all pass this pre-check and overshoot the cap. Try
|
|
338
|
+
// to free a slot; if still full (every session is streaming and none
|
|
339
|
+
// can be safely evicted), reject rather than admit — otherwise a client
|
|
340
|
+
// holding many long-lived streams could drive unbounded growth.
|
|
341
|
+
if (sessions.size + pendingAdmissions >= getMaxSessions()) {
|
|
342
|
+
evictOldestSession();
|
|
343
|
+
if (sessions.size + pendingAdmissions >= getMaxSessions()) {
|
|
344
|
+
logger.warn('Rejecting new session — server at capacity', {
|
|
345
|
+
activeSessions: sessions.size,
|
|
346
|
+
pendingAdmissions,
|
|
347
|
+
maxSessions: getMaxSessions(),
|
|
348
|
+
});
|
|
349
|
+
res.writeHead(503, { 'Content-Type': 'application/json', 'Retry-After': '5' });
|
|
350
|
+
res.end(JSON.stringify({
|
|
351
|
+
jsonrpc: '2.0',
|
|
352
|
+
error: { code: -32000, message: 'Server at capacity, retry later' },
|
|
353
|
+
id: null,
|
|
354
|
+
}));
|
|
355
|
+
return;
|
|
137
356
|
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
await
|
|
357
|
+
}
|
|
358
|
+
// Reserve a slot for the async init. Incremented synchronously here,
|
|
359
|
+
// before the first await, so a concurrent init sees it in its own
|
|
360
|
+
// pre-check above. The reservation is released the moment the session
|
|
361
|
+
// is inserted into `sessions` (it now counts toward `sessions.size`);
|
|
362
|
+
// otherwise it would be double-counted for the whole lifetime of a
|
|
363
|
+
// streaming init, whose `handleRequest` doesn't return until the stream
|
|
364
|
+
// closes. If the init never inserts a session (failure), the `finally`
|
|
365
|
+
// releases it instead.
|
|
366
|
+
pendingAdmissions++;
|
|
367
|
+
let admitted = false;
|
|
141
368
|
try {
|
|
369
|
+
const transport = new StreamableHTTPServerTransport({
|
|
370
|
+
sessionIdGenerator: () => randomUUID(),
|
|
371
|
+
onsessioninitialized: (id) => {
|
|
372
|
+
const session = { transport, lastSeenAt: Date.now(), openStreams: 0 };
|
|
373
|
+
sessions.set(id, session);
|
|
374
|
+
if (!admitted) {
|
|
375
|
+
admitted = true;
|
|
376
|
+
pendingAdmissions--; // reservation fulfilled — now counted in sessions.size
|
|
377
|
+
}
|
|
378
|
+
// Track the initialize response (this `res`) so a slow or streaming
|
|
379
|
+
// init can't be reaped while it's still in flight.
|
|
380
|
+
trackStream(session, res);
|
|
381
|
+
logger.info('MCP session created', { sessionId: id, activeSessions: sessions.size });
|
|
382
|
+
},
|
|
383
|
+
});
|
|
384
|
+
// Assigned before connect() — the SDK chains rather than replaces this,
|
|
385
|
+
// so both this cleanup and the McpServer shutdown run on close.
|
|
386
|
+
transport.onclose = () => {
|
|
387
|
+
if (transport.sessionId) {
|
|
388
|
+
sessions.delete(transport.sessionId);
|
|
389
|
+
logger.info('MCP session closed', {
|
|
390
|
+
sessionId: transport.sessionId,
|
|
391
|
+
activeSessions: sessions.size,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
const server = serverFactory(defaultClient);
|
|
396
|
+
await server.connect(transport);
|
|
142
397
|
await transport.handleRequest(req, res, parsedBody);
|
|
143
398
|
}
|
|
144
399
|
catch (error) {
|
|
@@ -148,6 +403,13 @@ export async function startHttpTransport(serverFactory, defaultClient, port) {
|
|
|
148
403
|
res.end(JSON_RPC_INTERNAL_ERROR);
|
|
149
404
|
}
|
|
150
405
|
}
|
|
406
|
+
finally {
|
|
407
|
+
// Only release here if the init never inserted a session (it failed
|
|
408
|
+
// before onsessioninitialized fired); otherwise it was already
|
|
409
|
+
// released at insertion.
|
|
410
|
+
if (!admitted)
|
|
411
|
+
pendingAdmissions--;
|
|
412
|
+
}
|
|
151
413
|
return;
|
|
152
414
|
}
|
|
153
415
|
// Invalid request — no session and not an initialize
|
|
@@ -161,10 +423,28 @@ export async function startHttpTransport(serverFactory, defaultClient, port) {
|
|
|
161
423
|
}
|
|
162
424
|
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
163
425
|
res.end(JSON.stringify({ error: 'Not found' }));
|
|
164
|
-
}
|
|
426
|
+
}
|
|
165
427
|
httpServer.on('error', (err) => {
|
|
166
428
|
logger.error('HTTP server error', { error: String(err), port });
|
|
167
429
|
});
|
|
430
|
+
// unref'd so the sweep timer never keeps the process alive on its own
|
|
431
|
+
const sweepTimer = setInterval(() => {
|
|
432
|
+
sweepIdleSessions();
|
|
433
|
+
}, SESSION_SWEEP_INTERVAL_MS);
|
|
434
|
+
sweepTimer.unref();
|
|
435
|
+
httpServer.on('close', () => {
|
|
436
|
+
clearInterval(sweepTimer);
|
|
437
|
+
// Close active sessions on shutdown so their transports and McpServer
|
|
438
|
+
// instances don't leak if the server is closed without a process exit
|
|
439
|
+
// (e.g. a graceful restart within the same process, or tests). Snapshot
|
|
440
|
+
// first — transport.close() fires onclose, which deletes from `sessions`.
|
|
441
|
+
for (const session of [...sessions.values()]) {
|
|
442
|
+
void Promise.resolve(session.transport.close()).catch(() => {
|
|
443
|
+
// best-effort cleanup during shutdown
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
sessions.clear();
|
|
447
|
+
});
|
|
168
448
|
await new Promise((resolve) => {
|
|
169
449
|
httpServer.listen(port, () => {
|
|
170
450
|
logger.info(`OMOPHub MCP server listening on http://localhost:${String(port)}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/transports/http.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/transports/http.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omophub/omophub-mcp",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.3",
|
|
4
4
|
"mcpName": "io.github.OMOPHub/omophub-mcp",
|
|
5
5
|
"description": "MCP server for OHDSI OMOP standardized medical vocabularies - search, lookup, map, and navigate concepts via AI agents",
|
|
6
6
|
"type": "module",
|
|
@@ -64,9 +64,9 @@
|
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@biomejs/biome": "^2.4.4",
|
|
66
66
|
"@types/node": "^25.0.3",
|
|
67
|
-
"@vitest/coverage-v8": "^
|
|
67
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
68
68
|
"tsx": "^4.19.0",
|
|
69
69
|
"typescript": "^5.9.3",
|
|
70
|
-
"vitest": "^
|
|
70
|
+
"vitest": "^4.1.8"
|
|
71
71
|
}
|
|
72
72
|
}
|