@novorb/mcp-core 2.1.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/LICENSE +21 -0
- package/README.md +179 -0
- package/dist/http-client.d.ts +69 -0
- package/dist/http-client.d.ts.map +1 -0
- package/dist/http-client.js +228 -0
- package/dist/http-client.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +80 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +236 -0
- package/dist/logger.js.map +1 -0
- package/dist/pagination.d.ts +45 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +46 -0
- package/dist/pagination.js.map +1 -0
- package/dist/retry.d.ts +51 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +94 -0
- package/dist/retry.js.map +1 -0
- package/dist/server.d.ts +80 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +73 -0
- package/dist/server.js.map +1 -0
- package/dist/transport.d.ts +102 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +213 -0
- package/dist/transport.js.map +1 -0
- package/package.json +86 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,8EAA8E;AAC9E,oEAAoE;AACpE,8EAA8E;AAE9E,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,iDAAiD,CAAC;AAU9E;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,4EAA4E;IAC5E,QAAQ,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAA4B;IAChF,iEAAiE;IACjE,eAAe,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAA4B;IACxF,kEAAkE;IAClE,YAAY,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAA4B;IACtF,sDAAsD;IACtD,WAAW,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAA4B;CACpG,CAAC;AAEX,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,OAAe;IAC3D,OAAO,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @novorb/mcp-core — Transport
|
|
3
|
+
*
|
|
4
|
+
* Unified transport for MCP servers. Selects between stdio and native Streamable HTTP
|
|
5
|
+
* based on the MCP_TRANSPORT environment variable.
|
|
6
|
+
*
|
|
7
|
+
* HTTP mode is stateless (sessionIdGenerator: undefined) — compatible with MCPize Cloud Run,
|
|
8
|
+
* Apify Standby, and any other scale-to-zero host. One transport instance is created per
|
|
9
|
+
* request; the server instance is resolved once and reconnected per request (SDK 1.29+).
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* import { startMcpServer } from '@novorb/mcp-core/transport';
|
|
13
|
+
*
|
|
14
|
+
* // Factory (recommended for HTTP — ensures clean state per request):
|
|
15
|
+
* await startMcpServer(() => createServer(services));
|
|
16
|
+
*
|
|
17
|
+
* // Server instance (fine for stdio; also works for HTTP in SDK 1.29+):
|
|
18
|
+
* await startMcpServer(server);
|
|
19
|
+
*
|
|
20
|
+
* Environment variables:
|
|
21
|
+
* MCP_TRANSPORT — 'stdio' (default) | 'http'
|
|
22
|
+
* PORT — listen port for HTTP transport (default: 8080)
|
|
23
|
+
* MCP_HTTP_HOST — bind address for HTTP (default: '0.0.0.0' for container hosts; set
|
|
24
|
+
* '127.0.0.1' for local-only)
|
|
25
|
+
* MCP_API_KEY — Bearer token for HTTP auth. REQUIRED in HTTP mode unless MCP_AUTH_DISABLED.
|
|
26
|
+
* MCP_AUTH_DISABLED — set 'true' to run HTTP WITHOUT auth (only when a gateway/platform fronts it)
|
|
27
|
+
* MCP_MAX_BODY_BYTES — max accepted POST body size (default: 4 MiB)
|
|
28
|
+
* MCP_ALLOWED_HOSTS — comma-separated Host allowlist; setting it enables DNS-rebinding protection
|
|
29
|
+
* MCP_ALLOWED_ORIGINS — comma-separated Origin allowlist (also enables DNS-rebinding protection)
|
|
30
|
+
*/
|
|
31
|
+
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
32
|
+
/** Accepts both the high-level McpServer and the low-level Server */
|
|
33
|
+
type AnyMcpServer = {
|
|
34
|
+
connect(transport: unknown): Promise<void>;
|
|
35
|
+
close?(): Promise<void>;
|
|
36
|
+
};
|
|
37
|
+
/** A server instance or a factory that creates one per request */
|
|
38
|
+
type ServerOrFactory = AnyMcpServer | (() => AnyMcpServer);
|
|
39
|
+
export type TransportMode = 'stdio' | 'http';
|
|
40
|
+
export interface TransportOptions {
|
|
41
|
+
/** Transport mode. Defaults to MCP_TRANSPORT env var, falling back to 'stdio'. */
|
|
42
|
+
mode?: TransportMode;
|
|
43
|
+
/** HTTP listen port. Defaults to PORT env var, falling back to 8080. */
|
|
44
|
+
port?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Optional Bearer token for HTTP auth. When set, all /mcp requests must include
|
|
47
|
+
* `Authorization: Bearer <apiKey>`. Defaults to MCP_API_KEY env var.
|
|
48
|
+
* Set to null to EXPLICITLY disable auth (equivalent to MCP_AUTH_DISABLED=true).
|
|
49
|
+
*/
|
|
50
|
+
apiKey?: string | null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Constant-time comparison of the Authorization header against `Bearer <apiKey>`.
|
|
54
|
+
* Both sides are SHA-256-hashed first so `timingSafeEqual` never throws on a length mismatch and the
|
|
55
|
+
* token length is not itself a timing oracle.
|
|
56
|
+
*/
|
|
57
|
+
export declare function bearerMatches(authHeader: string, apiKey: string): boolean;
|
|
58
|
+
export interface HttpAuthResolution {
|
|
59
|
+
/** Whether the Bearer check is enforced on /mcp requests. */
|
|
60
|
+
authEnabled: boolean;
|
|
61
|
+
/** The resolved key ('' when auth is explicitly disabled). */
|
|
62
|
+
apiKey: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Resolve HTTP auth — FAIL CLOSED. `apiKey: null` (programmatic) or MCP_AUTH_DISABLED=true are the
|
|
66
|
+
* only ways to run open; a missing/empty MCP_API_KEY is treated as "not configured" (not a valid
|
|
67
|
+
* empty key) and THROWS, so a config typo can't silently downgrade the endpoint to no-auth.
|
|
68
|
+
*/
|
|
69
|
+
export declare function resolveHttpAuth(options: Pick<TransportOptions, 'apiKey'>, env?: NodeJS.ProcessEnv): HttpAuthResolution;
|
|
70
|
+
/** Resolved configuration for the HTTP request handler — everything `startHttp` derives once at boot. */
|
|
71
|
+
export interface RequestHandlerConfig {
|
|
72
|
+
/** Returns a server instance to connect for a request (fresh per request in the stateless model). */
|
|
73
|
+
getServer: () => AnyMcpServer;
|
|
74
|
+
/** Whether the Bearer check is enforced on /mcp requests. */
|
|
75
|
+
authEnabled: boolean;
|
|
76
|
+
/** The resolved Bearer key ('' when auth is disabled). */
|
|
77
|
+
apiKey: string;
|
|
78
|
+
/** Max accepted POST body size in bytes (→ 413 when exceeded). */
|
|
79
|
+
maxBodyBytes: number;
|
|
80
|
+
/** Whether to enable the SDK's DNS-rebinding (Host/Origin) protection. */
|
|
81
|
+
enableDnsRebindingProtection: boolean;
|
|
82
|
+
/** Host allowlist (only consulted when DNS-rebinding protection is enabled). */
|
|
83
|
+
allowedHosts: string[];
|
|
84
|
+
/** Origin allowlist (only consulted when DNS-rebinding protection is enabled). */
|
|
85
|
+
allowedOrigins: string[];
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Build the `(req, res)` handler for the HTTP transport. Extracted from `startHttp` so the routing,
|
|
89
|
+
* auth, content-type/size guards, and error handling are unit-testable with mock req/res — no socket
|
|
90
|
+
* binding required.
|
|
91
|
+
*/
|
|
92
|
+
export declare function createRequestHandler(cfg: RequestHandlerConfig): (req: IncomingMessage, res: ServerResponse) => Promise<void>;
|
|
93
|
+
/**
|
|
94
|
+
* Start an MCP server with the configured transport.
|
|
95
|
+
*
|
|
96
|
+
* For stdio: connects once and keeps the process alive.
|
|
97
|
+
* For http: starts a stateless HTTP server. GET /health is always available.
|
|
98
|
+
* Pass a factory function to get a fresh server instance per request.
|
|
99
|
+
*/
|
|
100
|
+
export declare function startMcpServer(serverOrFactory: ServerOrFactory, options?: TransportOptions): Promise<void>;
|
|
101
|
+
export {};
|
|
102
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAIH,OAAO,EAAgB,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAG1E,qEAAqE;AACrE,KAAK,YAAY,GAAG;IAClB,OAAO,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB,CAAC;AAEF,kEAAkE;AAClE,KAAK,eAAe,GAAG,YAAY,GAAG,CAAC,MAAM,YAAY,CAAC,CAAC;AAE3D,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,CAAC;AAE7C,MAAM,WAAW,gBAAgB;IAC/B,kFAAkF;IAClF,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AA8BD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAIzE;AAED,MAAM,WAAW,kBAAkB;IACjC,6DAA6D;IAC7D,WAAW,EAAE,OAAO,CAAC;IACrB,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,EACzC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,kBAAkB,CAYpB;AAED,yGAAyG;AACzG,MAAM,WAAW,oBAAoB;IACnC,qGAAqG;IACrG,SAAS,EAAE,MAAM,YAAY,CAAC;IAC9B,6DAA6D;IAC7D,WAAW,EAAE,OAAO,CAAC;IACrB,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,4BAA4B,EAAE,OAAO,CAAC;IACtC,gFAAgF;IAChF,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,kFAAkF;IAClF,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,oBAAoB,GACxB,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CA0E9D;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,eAAe,EAAE,eAAe,EAChC,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAQf"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @novorb/mcp-core — Transport
|
|
3
|
+
*
|
|
4
|
+
* Unified transport for MCP servers. Selects between stdio and native Streamable HTTP
|
|
5
|
+
* based on the MCP_TRANSPORT environment variable.
|
|
6
|
+
*
|
|
7
|
+
* HTTP mode is stateless (sessionIdGenerator: undefined) — compatible with MCPize Cloud Run,
|
|
8
|
+
* Apify Standby, and any other scale-to-zero host. One transport instance is created per
|
|
9
|
+
* request; the server instance is resolved once and reconnected per request (SDK 1.29+).
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* import { startMcpServer } from '@novorb/mcp-core/transport';
|
|
13
|
+
*
|
|
14
|
+
* // Factory (recommended for HTTP — ensures clean state per request):
|
|
15
|
+
* await startMcpServer(() => createServer(services));
|
|
16
|
+
*
|
|
17
|
+
* // Server instance (fine for stdio; also works for HTTP in SDK 1.29+):
|
|
18
|
+
* await startMcpServer(server);
|
|
19
|
+
*
|
|
20
|
+
* Environment variables:
|
|
21
|
+
* MCP_TRANSPORT — 'stdio' (default) | 'http'
|
|
22
|
+
* PORT — listen port for HTTP transport (default: 8080)
|
|
23
|
+
* MCP_HTTP_HOST — bind address for HTTP (default: '0.0.0.0' for container hosts; set
|
|
24
|
+
* '127.0.0.1' for local-only)
|
|
25
|
+
* MCP_API_KEY — Bearer token for HTTP auth. REQUIRED in HTTP mode unless MCP_AUTH_DISABLED.
|
|
26
|
+
* MCP_AUTH_DISABLED — set 'true' to run HTTP WITHOUT auth (only when a gateway/platform fronts it)
|
|
27
|
+
* MCP_MAX_BODY_BYTES — max accepted POST body size (default: 4 MiB)
|
|
28
|
+
* MCP_ALLOWED_HOSTS — comma-separated Host allowlist; setting it enables DNS-rebinding protection
|
|
29
|
+
* MCP_ALLOWED_ORIGINS — comma-separated Origin allowlist (also enables DNS-rebinding protection)
|
|
30
|
+
*/
|
|
31
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
32
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
33
|
+
import { createServer } from 'node:http';
|
|
34
|
+
import { createHash, timingSafeEqual } from 'node:crypto';
|
|
35
|
+
function resolve(s) {
|
|
36
|
+
return typeof s === 'function' ? s() : s;
|
|
37
|
+
}
|
|
38
|
+
/** Marker error so the request handler can answer 413 rather than 500. */
|
|
39
|
+
class BodyTooLargeError extends Error {
|
|
40
|
+
constructor() { super('Request body too large'); this.name = 'BodyTooLargeError'; }
|
|
41
|
+
}
|
|
42
|
+
/** Read the request body, rejecting (and destroying the socket) once it exceeds `maxBytes`. */
|
|
43
|
+
function readBody(req, maxBytes) {
|
|
44
|
+
return new Promise((res, rej) => {
|
|
45
|
+
const chunks = [];
|
|
46
|
+
let size = 0;
|
|
47
|
+
req.on('data', (c) => {
|
|
48
|
+
size += c.length;
|
|
49
|
+
if (size > maxBytes) {
|
|
50
|
+
rej(new BodyTooLargeError());
|
|
51
|
+
req.destroy();
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
chunks.push(c);
|
|
55
|
+
});
|
|
56
|
+
req.on('end', () => res(Buffer.concat(chunks).toString('utf8')));
|
|
57
|
+
req.on('error', rej);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Constant-time comparison of the Authorization header against `Bearer <apiKey>`.
|
|
62
|
+
* Both sides are SHA-256-hashed first so `timingSafeEqual` never throws on a length mismatch and the
|
|
63
|
+
* token length is not itself a timing oracle.
|
|
64
|
+
*/
|
|
65
|
+
export function bearerMatches(authHeader, apiKey) {
|
|
66
|
+
const a = createHash('sha256').update(authHeader).digest();
|
|
67
|
+
const b = createHash('sha256').update(`Bearer ${apiKey}`).digest();
|
|
68
|
+
return timingSafeEqual(a, b);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Resolve HTTP auth — FAIL CLOSED. `apiKey: null` (programmatic) or MCP_AUTH_DISABLED=true are the
|
|
72
|
+
* only ways to run open; a missing/empty MCP_API_KEY is treated as "not configured" (not a valid
|
|
73
|
+
* empty key) and THROWS, so a config typo can't silently downgrade the endpoint to no-auth.
|
|
74
|
+
*/
|
|
75
|
+
export function resolveHttpAuth(options, env = process.env) {
|
|
76
|
+
const explicitlyDisabled = options.apiKey === null || env.MCP_AUTH_DISABLED === 'true';
|
|
77
|
+
const apiKey = (options.apiKey !== undefined && options.apiKey !== null)
|
|
78
|
+
? options.apiKey
|
|
79
|
+
: (env.MCP_API_KEY || '');
|
|
80
|
+
if (!explicitlyDisabled && apiKey.length === 0) {
|
|
81
|
+
throw new Error('[mcp-core] HTTP transport refused to start without auth: set MCP_API_KEY to a non-empty token, ' +
|
|
82
|
+
'or set MCP_AUTH_DISABLED=true to run open (only when a gateway/platform fronts authentication).');
|
|
83
|
+
}
|
|
84
|
+
return { authEnabled: apiKey.length > 0, apiKey };
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Build the `(req, res)` handler for the HTTP transport. Extracted from `startHttp` so the routing,
|
|
88
|
+
* auth, content-type/size guards, and error handling are unit-testable with mock req/res — no socket
|
|
89
|
+
* binding required.
|
|
90
|
+
*/
|
|
91
|
+
export function createRequestHandler(cfg) {
|
|
92
|
+
const { getServer, authEnabled, apiKey, maxBodyBytes, enableDnsRebindingProtection, allowedHosts, allowedOrigins, } = cfg;
|
|
93
|
+
return async (req, res) => {
|
|
94
|
+
try {
|
|
95
|
+
// Health probe — no auth required
|
|
96
|
+
if (req.method === 'GET' && req.url === '/health') {
|
|
97
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
98
|
+
res.end(JSON.stringify({ status: 'healthy' }));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// Bearer token auth (constant-time)
|
|
102
|
+
if (authEnabled) {
|
|
103
|
+
const auth = req.headers['authorization'] ?? '';
|
|
104
|
+
if (!bearerMatches(auth, apiKey)) {
|
|
105
|
+
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
106
|
+
res.end(JSON.stringify({ error: 'Unauthorized' }));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// MCP endpoint — stateless: new server + transport per request
|
|
111
|
+
if (req.url === '/mcp' || req.url?.startsWith('/mcp?')) {
|
|
112
|
+
let body;
|
|
113
|
+
if (req.method === 'POST') {
|
|
114
|
+
// Require JSON content type (the body is parsed below).
|
|
115
|
+
const contentType = (req.headers['content-type'] ?? '').toLowerCase();
|
|
116
|
+
if (!contentType.includes('application/json')) {
|
|
117
|
+
res.writeHead(415, { 'Content-Type': 'application/json' });
|
|
118
|
+
res.end(JSON.stringify({ error: 'Unsupported Media Type: Content-Type must be application/json' }));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
let raw;
|
|
122
|
+
try {
|
|
123
|
+
raw = await readBody(req, maxBodyBytes);
|
|
124
|
+
}
|
|
125
|
+
catch (e) {
|
|
126
|
+
if (e instanceof BodyTooLargeError) {
|
|
127
|
+
res.writeHead(413, { 'Content-Type': 'application/json' });
|
|
128
|
+
res.end(JSON.stringify({ error: 'Payload Too Large' }));
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
throw e;
|
|
132
|
+
}
|
|
133
|
+
if (raw.trim()) {
|
|
134
|
+
try {
|
|
135
|
+
body = JSON.parse(raw);
|
|
136
|
+
}
|
|
137
|
+
catch { /* let transport surface the error */ }
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const server = getServer();
|
|
141
|
+
const transport = new StreamableHTTPServerTransport({
|
|
142
|
+
sessionIdGenerator: undefined, // stateless — no Mcp-Session-Id
|
|
143
|
+
enableJsonResponse: true,
|
|
144
|
+
...(enableDnsRebindingProtection ? { enableDnsRebindingProtection: true, allowedHosts, allowedOrigins } : {}),
|
|
145
|
+
});
|
|
146
|
+
await server.connect(transport);
|
|
147
|
+
await transport.handleRequest(req, res, body);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
151
|
+
res.end(JSON.stringify({ error: 'Not found' }));
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
155
|
+
process.stderr.write(`[mcp-core] request error: ${msg}\n`);
|
|
156
|
+
if (!res.headersSent) {
|
|
157
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
158
|
+
res.end(JSON.stringify({ error: 'Internal server error' }));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Start an MCP server with the configured transport.
|
|
165
|
+
*
|
|
166
|
+
* For stdio: connects once and keeps the process alive.
|
|
167
|
+
* For http: starts a stateless HTTP server. GET /health is always available.
|
|
168
|
+
* Pass a factory function to get a fresh server instance per request.
|
|
169
|
+
*/
|
|
170
|
+
export async function startMcpServer(serverOrFactory, options = {}) {
|
|
171
|
+
const mode = options.mode ?? process.env.MCP_TRANSPORT ?? 'stdio';
|
|
172
|
+
if (mode === 'stdio') {
|
|
173
|
+
const server = resolve(serverOrFactory);
|
|
174
|
+
await server.connect(new StdioServerTransport());
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
await startHttp(serverOrFactory, options);
|
|
178
|
+
}
|
|
179
|
+
async function startHttp(serverOrFactory, options) {
|
|
180
|
+
const port = options.port ?? parseInt(process.env.PORT ?? '8080', 10);
|
|
181
|
+
const host = process.env.MCP_HTTP_HOST || '0.0.0.0';
|
|
182
|
+
const maxBodyBytes = Number(process.env.MCP_MAX_BODY_BYTES) || 4 * 1024 * 1024;
|
|
183
|
+
// Auth resolution: FAIL CLOSED (throws when http + no key + not explicitly disabled).
|
|
184
|
+
const { authEnabled, apiKey } = resolveHttpAuth(options);
|
|
185
|
+
if (!authEnabled) {
|
|
186
|
+
process.stderr.write('[mcp-core] WARNING: HTTP transport running WITHOUT auth (MCP_AUTH_DISABLED). ' +
|
|
187
|
+
'Ensure a gateway/platform fronts authentication.\n');
|
|
188
|
+
}
|
|
189
|
+
// --- DNS-rebinding / Host+Origin protection (opt-in via allowlist envs) --------------------------
|
|
190
|
+
const allowedHosts = (process.env.MCP_ALLOWED_HOSTS || '').split(',').map(s => s.trim()).filter(Boolean);
|
|
191
|
+
const allowedOrigins = (process.env.MCP_ALLOWED_ORIGINS || '').split(',').map(s => s.trim()).filter(Boolean);
|
|
192
|
+
const enableDnsRebindingProtection = allowedHosts.length > 0 || allowedOrigins.length > 0;
|
|
193
|
+
// Wrap a plain instance so getServer() always returns a callable factory
|
|
194
|
+
const getServer = typeof serverOrFactory === 'function' ? serverOrFactory : () => serverOrFactory;
|
|
195
|
+
const httpServer = createServer(createRequestHandler({
|
|
196
|
+
getServer, authEnabled, apiKey, maxBodyBytes,
|
|
197
|
+
enableDnsRebindingProtection, allowedHosts, allowedOrigins,
|
|
198
|
+
}));
|
|
199
|
+
await new Promise((ok, fail) => {
|
|
200
|
+
httpServer.listen(port, host, ok);
|
|
201
|
+
httpServer.once('error', fail);
|
|
202
|
+
});
|
|
203
|
+
process.stderr.write(`[mcp-core] HTTP server listening on http://${host}:${port}/mcp\n`);
|
|
204
|
+
// Block until signal
|
|
205
|
+
await new Promise(done => {
|
|
206
|
+
const shutdown = () => {
|
|
207
|
+
httpServer.close(() => done());
|
|
208
|
+
};
|
|
209
|
+
process.once('SIGTERM', shutdown);
|
|
210
|
+
process.once('SIGINT', shutdown);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@novorb/mcp-core",
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": "Shared utilities for MCP servers — logger, HTTP client, retry, pagination, transport",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./dist/index.js",
|
|
10
|
+
"./logger": "./dist/logger.js",
|
|
11
|
+
"./retry": "./dist/retry.js",
|
|
12
|
+
"./pagination": "./dist/pagination.js",
|
|
13
|
+
"./http-client": "./dist/http-client.js",
|
|
14
|
+
"./transport": "./dist/transport.js",
|
|
15
|
+
"./server": "./dist/server.js"
|
|
16
|
+
},
|
|
17
|
+
"typesVersions": {
|
|
18
|
+
"*": {
|
|
19
|
+
"logger": [
|
|
20
|
+
"dist/logger.d.ts"
|
|
21
|
+
],
|
|
22
|
+
"retry": [
|
|
23
|
+
"dist/retry.d.ts"
|
|
24
|
+
],
|
|
25
|
+
"pagination": [
|
|
26
|
+
"dist/pagination.d.ts"
|
|
27
|
+
],
|
|
28
|
+
"http-client": [
|
|
29
|
+
"dist/http-client.d.ts"
|
|
30
|
+
],
|
|
31
|
+
"transport": [
|
|
32
|
+
"dist/transport.d.ts"
|
|
33
|
+
],
|
|
34
|
+
"server": [
|
|
35
|
+
"dist/server.d.ts"
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"mcp",
|
|
41
|
+
"utilities",
|
|
42
|
+
"logger",
|
|
43
|
+
"http-client",
|
|
44
|
+
"retry",
|
|
45
|
+
"pagination"
|
|
46
|
+
],
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"license": "MIT",
|
|
51
|
+
"repository": {
|
|
52
|
+
"type": "git",
|
|
53
|
+
"url": "git+https://github.com/Novorb/mcp-core.git"
|
|
54
|
+
},
|
|
55
|
+
"homepage": "https://github.com/Novorb/mcp-core#readme",
|
|
56
|
+
"bugs": {
|
|
57
|
+
"url": "https://github.com/Novorb/mcp-core/issues"
|
|
58
|
+
},
|
|
59
|
+
"author": "novorb",
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
62
|
+
"axios": "^1.16.0",
|
|
63
|
+
"axios-retry": "^4.5.0"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@jest/globals": "^29.7.0",
|
|
67
|
+
"@types/jest": "^29.5.14",
|
|
68
|
+
"@types/node": "^20.19.33",
|
|
69
|
+
"jest": "^29.7.0",
|
|
70
|
+
"ts-jest": "^29.4.6",
|
|
71
|
+
"typescript": "^5.9.3"
|
|
72
|
+
},
|
|
73
|
+
"engines": {
|
|
74
|
+
"node": ">=18.0.0"
|
|
75
|
+
},
|
|
76
|
+
"files": [
|
|
77
|
+
"dist"
|
|
78
|
+
],
|
|
79
|
+
"scripts": {
|
|
80
|
+
"build": "tsc",
|
|
81
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
|
|
82
|
+
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch",
|
|
83
|
+
"test:coverage": "NODE_OPTIONS=--experimental-vm-modules jest --coverage",
|
|
84
|
+
"clean": "rm -rf dist"
|
|
85
|
+
}
|
|
86
|
+
}
|