@qverisai/mcp 0.7.4 → 0.8.0
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/CHANGELOG.md +127 -0
- package/LICENSE +1 -1
- package/README.md +149 -103
- package/dist/api/client.d.ts +9 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +110 -68
- package/dist/api/client.js.map +1 -1
- package/dist/http.d.ts +78 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +472 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +15 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +80 -34
- package/dist/index.js.map +1 -1
- package/dist/retry.d.ts +42 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +69 -0
- package/dist/retry.js.map +1 -0
- package/dist/server-card.d.ts +85 -0
- package/dist/server-card.d.ts.map +1 -0
- package/dist/server-card.js +85 -0
- package/dist/server-card.js.map +1 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +59 -58
package/dist/http.js
ADDED
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streamable HTTP transport for the Qveris MCP server.
|
|
3
|
+
*
|
|
4
|
+
* Adds a remote transport mode (MCP Streamable HTTP) alongside the default
|
|
5
|
+
* stdio transport, so the same server can run locally over stdio or be deployed
|
|
6
|
+
* behind HTTP for Claude Desktop Custom Connectors and other remote MCP clients.
|
|
7
|
+
*
|
|
8
|
+
* The transport is stateful: each client session gets its own
|
|
9
|
+
* {@link StreamableHTTPServerTransport} (and its own Qveris {@link Server}),
|
|
10
|
+
* keyed by the `Mcp-Session-Id` header the SDK assigns on `initialize`.
|
|
11
|
+
*
|
|
12
|
+
* Inbound auth is an optional shared bearer token (`QVERIS_MCP_HTTP_AUTH_TOKEN`).
|
|
13
|
+
* When binding a non-loopback host the server refuses to start unless a token is
|
|
14
|
+
* set (or auth is explicitly delegated to an external layer). OAuth 2.1 and the
|
|
15
|
+
* `.well-known` Server Card are tracked as follow-ups to issue #107.
|
|
16
|
+
*
|
|
17
|
+
* @module @qverisai/mcp/http
|
|
18
|
+
*/
|
|
19
|
+
import { createHash, randomUUID, timingSafeEqual } from 'node:crypto';
|
|
20
|
+
import { createServer, } from 'node:http';
|
|
21
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
22
|
+
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
|
|
23
|
+
import { buildCatalog, buildServerCard, CATALOG_PATH, SERVER_CARD_MEDIA_TYPE, } from './server-card.js';
|
|
24
|
+
const DEFAULT_PORT = 3000;
|
|
25
|
+
const DEFAULT_HOST = '127.0.0.1';
|
|
26
|
+
const DEFAULT_PATH = '/mcp';
|
|
27
|
+
const DEFAULT_MAX_BODY_BYTES = 4 * 1024 * 1024; // 4 MiB
|
|
28
|
+
const DEFAULT_SESSION_TIMEOUT_MS = 5 * 60 * 1000; // 5 min
|
|
29
|
+
const SWEEP_INTERVAL_MS = 60 * 1000; // idle-session sweep cadence
|
|
30
|
+
const MAX_PORT = 65535;
|
|
31
|
+
class BodyTooLargeError extends Error {
|
|
32
|
+
}
|
|
33
|
+
class JsonParseError extends Error {
|
|
34
|
+
}
|
|
35
|
+
function parseList(value) {
|
|
36
|
+
if (!value)
|
|
37
|
+
return [];
|
|
38
|
+
return value
|
|
39
|
+
.split(',')
|
|
40
|
+
.map((item) => item.trim())
|
|
41
|
+
.filter((item) => item.length > 0);
|
|
42
|
+
}
|
|
43
|
+
function parseBool(value, fallback) {
|
|
44
|
+
if (value === undefined)
|
|
45
|
+
return fallback;
|
|
46
|
+
const normalized = value.trim().toLowerCase();
|
|
47
|
+
if (['1', 'true', 'yes', 'on'].includes(normalized))
|
|
48
|
+
return true;
|
|
49
|
+
if (['0', 'false', 'no', 'off'].includes(normalized))
|
|
50
|
+
return false;
|
|
51
|
+
return fallback;
|
|
52
|
+
}
|
|
53
|
+
function parsePositiveInt(value, fallback) {
|
|
54
|
+
if (value === undefined)
|
|
55
|
+
return fallback;
|
|
56
|
+
const parsed = Number(value);
|
|
57
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
58
|
+
}
|
|
59
|
+
function readFlag(argv, name) {
|
|
60
|
+
// Supports `--name value` and `--name=value`.
|
|
61
|
+
for (let i = 0; i < argv.length; i++) {
|
|
62
|
+
const arg = argv[i];
|
|
63
|
+
if (arg === name) {
|
|
64
|
+
const next = argv[i + 1];
|
|
65
|
+
return next && !next.startsWith('--') ? next : '';
|
|
66
|
+
}
|
|
67
|
+
if (arg.startsWith(`${name}=`)) {
|
|
68
|
+
return arg.slice(name.length + 1);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
function hasFlag(argv, name) {
|
|
74
|
+
return argv.some((arg) => arg === name || arg.startsWith(`${name}=`));
|
|
75
|
+
}
|
|
76
|
+
// Matches the whole IPv4 loopback range 127.0.0.0/8 (and only that) — a precise
|
|
77
|
+
// address check, not a `startsWith('127.')` prefix that would also accept a
|
|
78
|
+
// hostname like `127.example.com`.
|
|
79
|
+
const IPV4_LOOPBACK = /^127(?:\.(?:0|[1-9]\d?|1\d\d|2[0-4]\d|25[0-5])){3}$/;
|
|
80
|
+
function isLoopbackHost(host) {
|
|
81
|
+
const h = host.trim().toLowerCase().replace(/^\[|\]$/g, ''); // strip IPv6 brackets
|
|
82
|
+
return h === 'localhost' || h === '::1' || IPV4_LOOPBACK.test(h);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Resolve the transport configuration from environment and CLI flags.
|
|
86
|
+
*
|
|
87
|
+
* HTTP mode is selected when `--http` is passed, `QVERIS_MCP_TRANSPORT=http`,
|
|
88
|
+
* or an HTTP port/host is explicitly set; otherwise the transport stays stdio
|
|
89
|
+
* so existing local configs are unaffected.
|
|
90
|
+
*/
|
|
91
|
+
export function resolveTransportConfig(env, argv = []) {
|
|
92
|
+
const portFlag = readFlag(argv, '--port');
|
|
93
|
+
const hostFlag = readFlag(argv, '--host');
|
|
94
|
+
const pathFlag = readFlag(argv, '--path');
|
|
95
|
+
const envPort = env.QVERIS_MCP_HTTP_PORT;
|
|
96
|
+
const envHost = env.QVERIS_MCP_HTTP_HOST;
|
|
97
|
+
const envTransport = env.QVERIS_MCP_TRANSPORT?.trim().toLowerCase();
|
|
98
|
+
const httpRequested = hasFlag(argv, '--http') ||
|
|
99
|
+
envTransport === 'http' ||
|
|
100
|
+
portFlag !== undefined ||
|
|
101
|
+
envPort !== undefined ||
|
|
102
|
+
hostFlag !== undefined ||
|
|
103
|
+
envHost !== undefined;
|
|
104
|
+
const mode = envTransport === 'stdio' ? 'stdio' : httpRequested ? 'http' : 'stdio';
|
|
105
|
+
const rawPort = portFlag || envPort;
|
|
106
|
+
const parsedPort = Number(rawPort);
|
|
107
|
+
const port = rawPort !== undefined &&
|
|
108
|
+
rawPort.trim() !== '' && // e.g. a bare `--port` -> '' -> Number('') === 0; fall back instead
|
|
109
|
+
Number.isInteger(parsedPort) &&
|
|
110
|
+
parsedPort >= 0 &&
|
|
111
|
+
parsedPort <= MAX_PORT
|
|
112
|
+
? parsedPort
|
|
113
|
+
: DEFAULT_PORT;
|
|
114
|
+
return {
|
|
115
|
+
mode,
|
|
116
|
+
host: hostFlag || envHost || DEFAULT_HOST,
|
|
117
|
+
port,
|
|
118
|
+
path: pathFlag || env.QVERIS_MCP_HTTP_PATH || DEFAULT_PATH,
|
|
119
|
+
allowedHosts: parseList(env.QVERIS_MCP_ALLOWED_HOSTS),
|
|
120
|
+
allowedOrigins: parseList(env.QVERIS_MCP_ALLOWED_ORIGINS),
|
|
121
|
+
enableDnsRebindingProtection: parseBool(env.QVERIS_MCP_DNS_REBINDING_PROTECTION, true),
|
|
122
|
+
enableJsonResponse: parseBool(env.QVERIS_MCP_HTTP_JSON, false),
|
|
123
|
+
authToken: env.QVERIS_MCP_HTTP_AUTH_TOKEN?.trim() || undefined,
|
|
124
|
+
allowUnauthenticated: parseBool(env.QVERIS_MCP_HTTP_ALLOW_UNAUTHENTICATED, false),
|
|
125
|
+
maxBodyBytes: parsePositiveInt(env.QVERIS_MCP_MAX_BODY_BYTES, DEFAULT_MAX_BODY_BYTES),
|
|
126
|
+
sessionTimeoutMs: parsePositiveInt(env.QVERIS_MCP_SESSION_TIMEOUT_MS, DEFAULT_SESSION_TIMEOUT_MS),
|
|
127
|
+
publicUrl: env.QVERIS_MCP_PUBLIC_URL?.trim().replace(/\/+$/, '') || undefined,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/** Absolute origin for discovery URLs: the configured public origin, or derived
|
|
131
|
+
* from the request (honoring an `X-Forwarded-Proto` from a TLS-terminating proxy). */
|
|
132
|
+
function requestOrigin(req, publicUrl) {
|
|
133
|
+
if (publicUrl)
|
|
134
|
+
return publicUrl;
|
|
135
|
+
const forwarded = req.headers['x-forwarded-proto'];
|
|
136
|
+
const proto = (typeof forwarded === 'string' ? forwarded.split(',')[0].trim() : undefined) || 'http';
|
|
137
|
+
const host = req.headers.host ?? 'localhost';
|
|
138
|
+
return `${proto}://${host}`;
|
|
139
|
+
}
|
|
140
|
+
/** Read and JSON-parse a request body with a hard size cap. */
|
|
141
|
+
async function readJsonBody(req, maxBytes) {
|
|
142
|
+
const chunks = [];
|
|
143
|
+
let total = 0;
|
|
144
|
+
for await (const chunk of req) {
|
|
145
|
+
const buf = chunk;
|
|
146
|
+
total += buf.length;
|
|
147
|
+
if (total > maxBytes) {
|
|
148
|
+
throw new BodyTooLargeError(`Request body exceeds ${maxBytes} bytes`);
|
|
149
|
+
}
|
|
150
|
+
chunks.push(buf);
|
|
151
|
+
}
|
|
152
|
+
if (chunks.length === 0)
|
|
153
|
+
return undefined;
|
|
154
|
+
const raw = Buffer.concat(chunks).toString('utf8');
|
|
155
|
+
if (raw.trim().length === 0)
|
|
156
|
+
return undefined;
|
|
157
|
+
try {
|
|
158
|
+
return JSON.parse(raw);
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
throw new JsonParseError(err instanceof Error ? err.message : 'Invalid JSON');
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function writeJson(res, status, body, headers = {}) {
|
|
165
|
+
const payload = JSON.stringify(body);
|
|
166
|
+
res.writeHead(status, { 'Content-Type': 'application/json', ...headers });
|
|
167
|
+
res.end(payload);
|
|
168
|
+
}
|
|
169
|
+
function jsonRpcError(id, code, message) {
|
|
170
|
+
return { jsonrpc: '2.0', error: { code, message }, id: id ?? null };
|
|
171
|
+
}
|
|
172
|
+
/** Constant-time bearer check against the configured token. */
|
|
173
|
+
function bearerMatches(authHeader, token) {
|
|
174
|
+
if (!authHeader)
|
|
175
|
+
return false;
|
|
176
|
+
const prefix = 'bearer ';
|
|
177
|
+
if (!authHeader.toLowerCase().startsWith(prefix))
|
|
178
|
+
return false;
|
|
179
|
+
const provided = authHeader.slice(prefix.length).trim();
|
|
180
|
+
// Compare fixed-length SHA-256 digests so the comparison is constant-time
|
|
181
|
+
// *and* doesn't leak the expected token's length via an early length check.
|
|
182
|
+
const providedHash = createHash('sha256').update(provided).digest();
|
|
183
|
+
const expectedHash = createHash('sha256').update(token).digest();
|
|
184
|
+
return timingSafeEqual(providedHash, expectedHash);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Start the Streamable HTTP server.
|
|
188
|
+
*
|
|
189
|
+
* @param config - Resolved HTTP transport settings.
|
|
190
|
+
* @param makeServer - Factory that builds a fresh Qveris {@link Server} for a
|
|
191
|
+
* new client session. Called once per `initialize`.
|
|
192
|
+
* @param cardInfo - Metadata for the discovery documents (Server Card + Catalog).
|
|
193
|
+
* When omitted those endpoints are not served.
|
|
194
|
+
* @param logger - Where to write startup/lifecycle lines (defaults to stderr,
|
|
195
|
+
* keeping stdout clean).
|
|
196
|
+
* @throws if binding a non-loopback host without an auth token and without an
|
|
197
|
+
* explicit opt-out (fail-closed).
|
|
198
|
+
*/
|
|
199
|
+
export async function startHttpServer(config, makeServer, cardInfo, logger = (message) => process.stderr.write(message)) {
|
|
200
|
+
const nonLoopback = !isLoopbackHost(config.host);
|
|
201
|
+
if (nonLoopback && !config.authToken && !config.allowUnauthenticated) {
|
|
202
|
+
throw new Error(`Refusing to start: HTTP transport is binding a non-loopback host (${config.host}) with no authentication. ` +
|
|
203
|
+
`Set QVERIS_MCP_HTTP_AUTH_TOKEN to require a bearer token, or set ` +
|
|
204
|
+
`QVERIS_MCP_HTTP_ALLOW_UNAUTHENTICATED=true if an external layer (proxy/gateway) authenticates requests.`);
|
|
205
|
+
}
|
|
206
|
+
if (nonLoopback && !config.authToken) {
|
|
207
|
+
logger('[qveris] WARNING: HTTP transport exposed on a non-loopback host without an auth token. ' +
|
|
208
|
+
'Ensure an external auth layer protects the endpoint.\n');
|
|
209
|
+
}
|
|
210
|
+
// The reserved public paths are matched before the transport path; a custom
|
|
211
|
+
// QVERIS_MCP_HTTP_PATH set to one of them would shadow the MCP endpoint, so
|
|
212
|
+
// fail fast rather than start a silently-broken server (consistent with the
|
|
213
|
+
// fail-closed auth check above). The card path (config.path + '/server-card')
|
|
214
|
+
// can't equal config.path, so it isn't a collision candidate.
|
|
215
|
+
const reservedPaths = cardInfo ? ['/health', CATALOG_PATH] : ['/health'];
|
|
216
|
+
if (reservedPaths.includes(config.path)) {
|
|
217
|
+
throw new Error(`Refusing to start: QVERIS_MCP_HTTP_PATH="${config.path}" collides with a reserved ` +
|
|
218
|
+
`endpoint (${reservedPaths.join(', ')}) and would shadow the MCP transport; choose a different path.`);
|
|
219
|
+
}
|
|
220
|
+
const transports = new Map();
|
|
221
|
+
const lastSeen = new Map();
|
|
222
|
+
const inflight = new Map(); // requests currently being handled per session
|
|
223
|
+
let boundPort = config.port;
|
|
224
|
+
// In-flight guards keep the idle sweep from evicting a session that has an
|
|
225
|
+
// active request or a live (long-lived) SSE stream. A session is only
|
|
226
|
+
// eligible for idle eviction once it has zero in-flight requests.
|
|
227
|
+
const retain = (sessionId) => {
|
|
228
|
+
inflight.set(sessionId, (inflight.get(sessionId) ?? 0) + 1);
|
|
229
|
+
};
|
|
230
|
+
const release = (sessionId) => {
|
|
231
|
+
const remaining = (inflight.get(sessionId) ?? 0) - 1;
|
|
232
|
+
if (remaining > 0)
|
|
233
|
+
inflight.set(sessionId, remaining);
|
|
234
|
+
else
|
|
235
|
+
inflight.delete(sessionId);
|
|
236
|
+
};
|
|
237
|
+
// The SDK matches the Host header (which includes the port) against
|
|
238
|
+
// allowedHosts. Build the effective list once the real port is known so DNS
|
|
239
|
+
// rebinding protection works even when config.port is 0 (ephemeral).
|
|
240
|
+
const effectiveAllowedHosts = () => {
|
|
241
|
+
const base = [
|
|
242
|
+
`${config.host}:${boundPort}`,
|
|
243
|
+
`localhost:${boundPort}`,
|
|
244
|
+
`127.0.0.1:${boundPort}`,
|
|
245
|
+
];
|
|
246
|
+
return [...new Set([...base, ...config.allowedHosts])];
|
|
247
|
+
};
|
|
248
|
+
const evict = (sessionId) => {
|
|
249
|
+
lastSeen.delete(sessionId);
|
|
250
|
+
inflight.delete(sessionId);
|
|
251
|
+
if (transports.get(sessionId))
|
|
252
|
+
transports.delete(sessionId);
|
|
253
|
+
};
|
|
254
|
+
const createTransport = async () => {
|
|
255
|
+
const transport = new StreamableHTTPServerTransport({
|
|
256
|
+
sessionIdGenerator: () => randomUUID(),
|
|
257
|
+
enableJsonResponse: config.enableJsonResponse,
|
|
258
|
+
enableDnsRebindingProtection: config.enableDnsRebindingProtection,
|
|
259
|
+
allowedHosts: effectiveAllowedHosts(),
|
|
260
|
+
allowedOrigins: config.allowedOrigins,
|
|
261
|
+
onsessioninitialized: (sessionId) => {
|
|
262
|
+
transports.set(sessionId, transport);
|
|
263
|
+
lastSeen.set(sessionId, Date.now());
|
|
264
|
+
logger(`[qveris] MCP session initialized: ${sessionId}\n`);
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
transport.onclose = () => {
|
|
268
|
+
const sessionId = transport.sessionId;
|
|
269
|
+
if (sessionId && transports.get(sessionId) === transport) {
|
|
270
|
+
evict(sessionId);
|
|
271
|
+
logger(`[qveris] MCP session closed: ${sessionId}\n`);
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
const server = makeServer(randomUUID());
|
|
275
|
+
await server.connect(transport);
|
|
276
|
+
return transport;
|
|
277
|
+
};
|
|
278
|
+
const handler = async (req, res) => {
|
|
279
|
+
try {
|
|
280
|
+
const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
|
|
281
|
+
// Lightweight, unauthenticated health check for load balancers.
|
|
282
|
+
if (req.method === 'GET' && url.pathname === '/health') {
|
|
283
|
+
writeJson(res, 200, { status: 'ok', transport: 'streamable-http' });
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
// Public, unauthenticated discovery documents (Server Card + Catalog).
|
|
287
|
+
// Like robots.txt / OAuth metadata, these are meant to be crawled without
|
|
288
|
+
// credentials, so they are handled before the auth check.
|
|
289
|
+
const cardPath = `${config.path}/server-card`;
|
|
290
|
+
if (cardInfo && (url.pathname === cardPath || url.pathname === CATALOG_PATH)) {
|
|
291
|
+
const cors = {
|
|
292
|
+
'Access-Control-Allow-Origin': '*',
|
|
293
|
+
'Access-Control-Allow-Methods': 'GET, OPTIONS',
|
|
294
|
+
'Access-Control-Allow-Headers': 'Accept',
|
|
295
|
+
};
|
|
296
|
+
if (req.method === 'OPTIONS') {
|
|
297
|
+
req.resume();
|
|
298
|
+
res.writeHead(204, { ...cors, 'Access-Control-Max-Age': '86400' });
|
|
299
|
+
res.end();
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
if (req.method === 'GET') {
|
|
303
|
+
const origin = requestOrigin(req, config.publicUrl);
|
|
304
|
+
// With a configured public origin the response is host-independent and
|
|
305
|
+
// freely cacheable. When the origin is derived from the request Host,
|
|
306
|
+
// don't let a shared cache serve it cross-host (cache-poisoning): mark
|
|
307
|
+
// it no-store and Vary on the headers it depends on.
|
|
308
|
+
const cache = config.publicUrl
|
|
309
|
+
? { 'Cache-Control': 'public, max-age=3600' }
|
|
310
|
+
: { 'Cache-Control': 'no-store', Vary: 'Host, X-Forwarded-Proto' };
|
|
311
|
+
if (url.pathname === cardPath) {
|
|
312
|
+
const card = buildServerCard(cardInfo, `${origin}${config.path}`);
|
|
313
|
+
writeJson(res, 200, card, { ...cors, ...cache, 'Content-Type': SERVER_CARD_MEDIA_TYPE });
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
const catalog = buildCatalog(cardInfo, `${origin}${cardPath}`);
|
|
317
|
+
writeJson(res, 200, catalog, { ...cors, ...cache });
|
|
318
|
+
}
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
req.resume();
|
|
322
|
+
res.writeHead(405, { Allow: 'GET, OPTIONS' });
|
|
323
|
+
res.end();
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
if (url.pathname !== config.path) {
|
|
327
|
+
req.resume(); // drain the body so the connection can be reused/closed
|
|
328
|
+
writeJson(res, 404, jsonRpcError(null, -32601, `Not found: ${url.pathname}`));
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
// Inbound auth (when a token is configured) on the MCP endpoint.
|
|
332
|
+
if (config.authToken && !bearerMatches(req.headers['authorization'], config.authToken)) {
|
|
333
|
+
req.resume(); // drain the (unread) body before short-circuiting
|
|
334
|
+
writeJson(res, 401, jsonRpcError(null, -32001, 'Unauthorized'), {
|
|
335
|
+
'WWW-Authenticate': 'Bearer',
|
|
336
|
+
});
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
340
|
+
const existing = typeof sessionId === 'string' ? transports.get(sessionId) : undefined;
|
|
341
|
+
if (typeof sessionId === 'string' && existing)
|
|
342
|
+
lastSeen.set(sessionId, Date.now());
|
|
343
|
+
if (req.method === 'POST') {
|
|
344
|
+
let body;
|
|
345
|
+
try {
|
|
346
|
+
body = await readJsonBody(req, config.maxBodyBytes);
|
|
347
|
+
}
|
|
348
|
+
catch (err) {
|
|
349
|
+
if (err instanceof BodyTooLargeError) {
|
|
350
|
+
// Don't drain the rest of an oversized upload — terminate the
|
|
351
|
+
// connection so an attacker can't keep streaming a huge body.
|
|
352
|
+
writeJson(res, 413, jsonRpcError(null, -32600, 'Request body too large'), {
|
|
353
|
+
Connection: 'close',
|
|
354
|
+
});
|
|
355
|
+
req.destroy();
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (err instanceof JsonParseError) {
|
|
359
|
+
writeJson(res, 400, jsonRpcError(null, -32700, 'Parse error'));
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
throw err;
|
|
363
|
+
}
|
|
364
|
+
let transport = existing;
|
|
365
|
+
if (!transport) {
|
|
366
|
+
if (sessionId !== undefined) {
|
|
367
|
+
writeJson(res, 404, jsonRpcError(null, -32001, 'Session not found'));
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
if (!isInitializeRequest(body)) {
|
|
371
|
+
writeJson(res, 400, jsonRpcError(null, -32000, 'Missing session ID: first request must be initialize'));
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
transport = await createTransport();
|
|
375
|
+
}
|
|
376
|
+
// Guard only requests against an existing session (a known id); a fresh
|
|
377
|
+
// initialize gets its id mid-handle and can't be swept in that window.
|
|
378
|
+
if (existing && typeof sessionId === 'string') {
|
|
379
|
+
retain(sessionId);
|
|
380
|
+
try {
|
|
381
|
+
await transport.handleRequest(req, res, body);
|
|
382
|
+
}
|
|
383
|
+
finally {
|
|
384
|
+
release(sessionId);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
await transport.handleRequest(req, res, body);
|
|
389
|
+
}
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
// GET (SSE stream) and DELETE (session teardown) require a live session.
|
|
393
|
+
if (req.method === 'GET' || req.method === 'DELETE') {
|
|
394
|
+
if (!existing || typeof sessionId !== 'string') {
|
|
395
|
+
writeJson(res, 404, jsonRpcError(null, -32001, 'Invalid or missing session ID'));
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
// A GET holds the SSE stream open for the session's lifetime; retaining
|
|
399
|
+
// it keeps the idle sweep from tearing down an active channel.
|
|
400
|
+
retain(sessionId);
|
|
401
|
+
try {
|
|
402
|
+
await existing.handleRequest(req, res);
|
|
403
|
+
}
|
|
404
|
+
finally {
|
|
405
|
+
release(sessionId);
|
|
406
|
+
}
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
req.resume();
|
|
410
|
+
res.writeHead(405, { Allow: 'GET, POST, DELETE' });
|
|
411
|
+
res.end();
|
|
412
|
+
}
|
|
413
|
+
catch (error) {
|
|
414
|
+
logger(`[qveris] HTTP handler error: ${error instanceof Error ? error.message : error}\n`);
|
|
415
|
+
if (!res.headersSent) {
|
|
416
|
+
writeJson(res, 500, jsonRpcError(null, -32603, 'Internal server error'));
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
const httpServer = createServer((req, res) => {
|
|
421
|
+
void handler(req, res);
|
|
422
|
+
});
|
|
423
|
+
await new Promise((resolve, reject) => {
|
|
424
|
+
httpServer.once('error', reject);
|
|
425
|
+
httpServer.listen(config.port, config.host, () => {
|
|
426
|
+
httpServer.removeListener('error', reject);
|
|
427
|
+
const address = httpServer.address();
|
|
428
|
+
if (address)
|
|
429
|
+
boundPort = address.port;
|
|
430
|
+
resolve();
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
// Keep a persistent error handler so a post-startup server error (e.g. EMFILE)
|
|
434
|
+
// is logged instead of crashing the process as an unhandled 'error' event.
|
|
435
|
+
httpServer.on('error', (err) => {
|
|
436
|
+
logger(`[qveris] HTTP server error: ${err.message}\n`);
|
|
437
|
+
});
|
|
438
|
+
// Evict idle sessions so abandoned connections don't leak transports/servers.
|
|
439
|
+
// Sessions with an in-flight request or an open SSE stream are skipped so an
|
|
440
|
+
// active call is never torn down mid-flight.
|
|
441
|
+
const sweep = setInterval(() => {
|
|
442
|
+
const now = Date.now();
|
|
443
|
+
for (const [sessionId, seen] of lastSeen) {
|
|
444
|
+
if (now - seen > config.sessionTimeoutMs && !inflight.get(sessionId)) {
|
|
445
|
+
const transport = transports.get(sessionId);
|
|
446
|
+
evict(sessionId);
|
|
447
|
+
if (transport)
|
|
448
|
+
void transport.close().catch(() => undefined);
|
|
449
|
+
logger(`[qveris] MCP session evicted (idle): ${sessionId}\n`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}, SWEEP_INTERVAL_MS);
|
|
453
|
+
sweep.unref();
|
|
454
|
+
logger(`[qveris] Qveris MCP Server started (streamable-http) on http://${config.host}:${boundPort}${config.path}\n`);
|
|
455
|
+
const close = async () => {
|
|
456
|
+
clearInterval(sweep);
|
|
457
|
+
for (const transport of transports.values()) {
|
|
458
|
+
await transport.close().catch(() => undefined);
|
|
459
|
+
}
|
|
460
|
+
transports.clear();
|
|
461
|
+
lastSeen.clear();
|
|
462
|
+
inflight.clear();
|
|
463
|
+
await new Promise((resolve) => {
|
|
464
|
+
httpServer.close(() => resolve());
|
|
465
|
+
// Don't wait out idle keep-alive sockets (clients pool connections and
|
|
466
|
+
// hold them open for seconds); force them closed so shutdown is prompt.
|
|
467
|
+
httpServer.closeAllConnections?.();
|
|
468
|
+
});
|
|
469
|
+
};
|
|
470
|
+
return { httpServer, port: boundPort, close };
|
|
471
|
+
}
|
|
472
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* }
|
|
24
24
|
* ```
|
|
25
25
|
*/
|
|
26
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
26
27
|
import { type CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
27
28
|
import { QverisClient } from './api/client.js';
|
|
28
29
|
/**
|
|
@@ -228,12 +229,23 @@ export declare const DEPRECATED_ALIASES: Record<string, string>;
|
|
|
228
229
|
/**
|
|
229
230
|
* Route one MCP tool call to the matching Qveris operation.
|
|
230
231
|
*/
|
|
231
|
-
export declare function executeQverisMcpTool(client: QverisClient, defaultSessionId: string, rawName: string, args: unknown, warn?: (message: string) => void): Promise<CallToolResult>;
|
|
232
|
+
export declare function executeQverisMcpTool(client: QverisClient | undefined, defaultSessionId: string, rawName: string, args: unknown, warn?: (message: string) => void): Promise<CallToolResult>;
|
|
233
|
+
/**
|
|
234
|
+
* Build a fully-wired Qveris MCP {@link Server} instance.
|
|
235
|
+
*
|
|
236
|
+
* Registers the list-tools and call-tool handlers against the given client and
|
|
237
|
+
* default session id. Kept separate from transport startup so both the stdio
|
|
238
|
+
* and Streamable HTTP paths (and tests) can construct a server the same way; in
|
|
239
|
+
* HTTP mode one server is created per client session.
|
|
240
|
+
*/
|
|
241
|
+
export declare function createQverisServer(client: QverisClient | undefined, defaultSessionId: string): Server;
|
|
232
242
|
/**
|
|
233
243
|
* Main entry point for the Qveris MCP Server.
|
|
234
244
|
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
245
|
+
* Registers the discover/inspect/call handlers and starts listening. The
|
|
246
|
+
* transport is selected by env/CLI (see {@link resolveTransportConfig}): stdio
|
|
247
|
+
* by default (unchanged for existing Claude Desktop / Cursor configs), or
|
|
248
|
+
* Streamable HTTP for remote deployment.
|
|
237
249
|
*/
|
|
238
250
|
export declare function main(): Promise<void>;
|
|
239
251
|
export declare function isEntrypoint(argvEntry: string | undefined, moduleUrl?: string): boolean;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAEnE,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,oCAAoC,CAAC;AAO5C,OAAO,EAAuB,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAyDpE;;;;;GAKG;AACH,wBAAgB,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA0DjC;AAGD,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAIrD,CAAC;AA2CF;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,YAAY,GAAG,SAAS,EAChC,gBAAgB,EAAE,MAAM,EACxB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,OAAO,EACb,IAAI,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAiD,GAC3E,OAAO,CAAC,cAAc,CAAC,CAmNzB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,YAAY,GAAG,SAAS,EAChC,gBAAgB,EAAE,MAAM,GACvB,MAAM,CA2BR;AAED;;;;;;;GAOG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAsC1C;AAcD,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,SAAkB,GAAG,OAAO,CAYhG"}
|