@j0hanz/superfetch 1.1.9 → 1.2.1
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/README.md +179 -469
- package/dist/config/constants.d.ts +19 -0
- package/dist/config/constants.d.ts.map +1 -0
- package/dist/config/constants.js +24 -0
- package/dist/config/constants.js.map +1 -0
- package/dist/config/formatting.d.ts +0 -2
- package/dist/config/formatting.d.ts.map +1 -1
- package/dist/config/formatting.js +1 -3
- package/dist/config/formatting.js.map +1 -1
- package/dist/config/index.d.ts +9 -3
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +19 -16
- package/dist/config/index.js.map +1 -1
- package/dist/config/types/content.d.ts +1 -20
- package/dist/config/types/content.d.ts.map +1 -1
- package/dist/config/types/content.js +0 -1
- package/dist/config/types/runtime.d.ts +7 -5
- package/dist/config/types/runtime.d.ts.map +1 -1
- package/dist/config/types/runtime.js +0 -1
- package/dist/config/types/tools.d.ts +5 -50
- package/dist/config/types/tools.d.ts.map +1 -1
- package/dist/config/types/tools.js +0 -1
- package/dist/errors/app-error.d.ts +0 -1
- package/dist/errors/app-error.js +0 -1
- package/dist/http/auth.d.ts +0 -1
- package/dist/http/auth.d.ts.map +1 -1
- package/dist/http/auth.js +17 -13
- package/dist/http/auth.js.map +1 -1
- package/dist/http/cors.d.ts +0 -1
- package/dist/http/cors.js +4 -1
- package/dist/http/cors.js.map +1 -1
- package/dist/http/download-routes.d.ts +14 -0
- package/dist/http/download-routes.d.ts.map +1 -0
- package/dist/http/download-routes.js +131 -0
- package/dist/http/download-routes.js.map +1 -0
- package/dist/http/mcp-routes.d.ts +1 -2
- package/dist/http/mcp-routes.d.ts.map +1 -1
- package/dist/http/mcp-routes.js +1 -2
- package/dist/http/mcp-routes.js.map +1 -1
- package/dist/http/mcp-session-helpers.d.ts +13 -0
- package/dist/http/mcp-session-helpers.d.ts.map +1 -0
- package/dist/http/mcp-session-helpers.js +64 -0
- package/dist/http/mcp-session-helpers.js.map +1 -0
- package/dist/http/mcp-session.d.ts +1 -3
- package/dist/http/mcp-session.d.ts.map +1 -1
- package/dist/http/mcp-session.js +7 -71
- package/dist/http/mcp-session.js.map +1 -1
- package/dist/http/mcp-validation.d.ts +1 -2
- package/dist/http/mcp-validation.d.ts.map +1 -1
- package/dist/http/mcp-validation.js +6 -27
- package/dist/http/mcp-validation.js.map +1 -1
- package/dist/http/rate-limit.d.ts +1 -2
- package/dist/http/rate-limit.d.ts.map +1 -1
- package/dist/http/rate-limit.js +0 -1
- package/dist/http/rate-limit.js.map +1 -1
- package/dist/http/server-middleware.d.ts +9 -0
- package/dist/http/server-middleware.d.ts.map +1 -0
- package/dist/http/server-middleware.js +111 -0
- package/dist/http/server-middleware.js.map +1 -0
- package/dist/http/server.d.ts +0 -1
- package/dist/http/server.d.ts.map +1 -1
- package/dist/http/server.js +20 -99
- package/dist/http/server.js.map +1 -1
- package/dist/http/session-cleanup.d.ts +2 -0
- package/dist/http/session-cleanup.d.ts.map +1 -0
- package/dist/http/session-cleanup.js +37 -0
- package/dist/http/session-cleanup.js.map +1 -0
- package/dist/http/sessions.d.ts +1 -2
- package/dist/http/sessions.d.ts.map +1 -1
- package/dist/http/sessions.js +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +13 -6
- package/dist/index.js.map +1 -1
- package/dist/middleware/error-handler.d.ts +0 -1
- package/dist/middleware/error-handler.js +0 -1
- package/dist/resources/cached-content.d.ts +0 -1
- package/dist/resources/cached-content.d.ts.map +1 -1
- package/dist/resources/cached-content.js +76 -12
- package/dist/resources/cached-content.js.map +1 -1
- package/dist/resources/index.d.ts +0 -1
- package/dist/resources/index.js +0 -1
- package/dist/server.d.ts +0 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +8 -3
- package/dist/server.js.map +1 -1
- package/dist/services/cache.d.ts +7 -4
- package/dist/services/cache.d.ts.map +1 -1
- package/dist/services/cache.js +86 -26
- package/dist/services/cache.js.map +1 -1
- package/dist/services/context.d.ts +2 -2
- package/dist/services/context.d.ts.map +1 -1
- package/dist/services/context.js +0 -1
- package/dist/services/extractor.d.ts +1 -2
- package/dist/services/extractor.d.ts.map +1 -1
- package/dist/services/extractor.js +45 -18
- package/dist/services/extractor.js.map +1 -1
- package/dist/services/fetcher/agents.d.ts +0 -1
- package/dist/services/fetcher/agents.d.ts.map +1 -1
- package/dist/services/fetcher/agents.js +3 -7
- package/dist/services/fetcher/agents.js.map +1 -1
- package/dist/services/fetcher/errors.d.ts +0 -1
- package/dist/services/fetcher/errors.js +0 -1
- package/dist/services/fetcher/headers.d.ts.map +1 -1
- package/dist/services/fetcher/headers.js +2 -24
- package/dist/services/fetcher/headers.js.map +1 -1
- package/dist/services/fetcher/interceptors.d.ts +2 -2
- package/dist/services/fetcher/interceptors.d.ts.map +1 -1
- package/dist/services/fetcher/interceptors.js +30 -21
- package/dist/services/fetcher/interceptors.js.map +1 -1
- package/dist/services/fetcher/redirects.d.ts +0 -2
- package/dist/services/fetcher/redirects.d.ts.map +1 -1
- package/dist/services/fetcher/redirects.js +20 -18
- package/dist/services/fetcher/redirects.js.map +1 -1
- package/dist/services/fetcher/response.d.ts +0 -1
- package/dist/services/fetcher/response.js +4 -5
- package/dist/services/fetcher/retry-policy.d.ts +1 -28
- package/dist/services/fetcher/retry-policy.d.ts.map +1 -1
- package/dist/services/fetcher/retry-policy.js +119 -126
- package/dist/services/fetcher/retry-policy.js.map +1 -1
- package/dist/services/fetcher.d.ts +1 -2
- package/dist/services/fetcher.d.ts.map +1 -1
- package/dist/services/fetcher.js +18 -13
- package/dist/services/fetcher.js.map +1 -1
- package/dist/services/logger.d.ts +1 -2
- package/dist/services/logger.d.ts.map +1 -1
- package/dist/services/logger.js +0 -1
- package/dist/services/parser.d.ts +1 -3
- package/dist/services/parser.d.ts.map +1 -1
- package/dist/services/parser.js +5 -39
- package/dist/services/parser.js.map +1 -1
- package/dist/tools/handlers/fetch-links/link-extractor.d.ts.map +1 -1
- package/dist/tools/handlers/fetch-links/link-extractor.js +15 -19
- package/dist/tools/handlers/fetch-links/link-extractor.js.map +1 -1
- package/dist/tools/handlers/fetch-links.tool.d.ts.map +1 -1
- package/dist/tools/handlers/fetch-links.tool.js +0 -2
- package/dist/tools/handlers/fetch-links.tool.js.map +1 -1
- package/dist/tools/handlers/fetch-markdown.tool.d.ts +1 -2
- package/dist/tools/handlers/fetch-markdown.tool.d.ts.map +1 -1
- package/dist/tools/handlers/fetch-markdown.tool.js +50 -20
- package/dist/tools/handlers/fetch-markdown.tool.js.map +1 -1
- package/dist/tools/handlers/fetch-single.shared.d.ts +14 -3
- package/dist/tools/handlers/fetch-single.shared.d.ts.map +1 -1
- package/dist/tools/handlers/fetch-single.shared.js +66 -3
- package/dist/tools/handlers/fetch-single.shared.js.map +1 -1
- package/dist/tools/handlers/fetch-url.tool.d.ts +1 -2
- package/dist/tools/handlers/fetch-url.tool.d.ts.map +1 -1
- package/dist/tools/handlers/fetch-url.tool.js +39 -17
- package/dist/tools/handlers/fetch-url.tool.js.map +1 -1
- package/dist/tools/handlers/fetch-urls/validation.d.ts +0 -1
- package/dist/tools/handlers/fetch-urls/validation.d.ts.map +1 -1
- package/dist/tools/handlers/fetch-urls/validation.js +1 -1
- package/dist/tools/handlers/fetch-urls/validation.js.map +1 -1
- package/dist/tools/index.d.ts +0 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -20
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/schemas.d.ts +57 -250
- package/dist/tools/schemas.d.ts.map +1 -1
- package/dist/tools/schemas.js +38 -198
- package/dist/tools/schemas.js.map +1 -1
- package/dist/tools/utils/cache-vary.d.ts +0 -2
- package/dist/tools/utils/cache-vary.d.ts.map +1 -1
- package/dist/tools/utils/cache-vary.js +8 -40
- package/dist/tools/utils/cache-vary.js.map +1 -1
- package/dist/tools/utils/common.d.ts +2 -4
- package/dist/tools/utils/common.d.ts.map +1 -1
- package/dist/tools/utils/common.js +6 -7
- package/dist/tools/utils/common.js.map +1 -1
- package/dist/tools/utils/content-transform.d.ts +1 -3
- package/dist/tools/utils/content-transform.d.ts.map +1 -1
- package/dist/tools/utils/content-transform.js +65 -14
- package/dist/tools/utils/content-transform.js.map +1 -1
- package/dist/tools/utils/fetch-pipeline.d.ts +1 -2
- package/dist/tools/utils/fetch-pipeline.d.ts.map +1 -1
- package/dist/tools/utils/fetch-pipeline.js +25 -21
- package/dist/tools/utils/fetch-pipeline.js.map +1 -1
- package/dist/tools/utils/inline-content.d.ts +3 -3
- package/dist/tools/utils/inline-content.d.ts.map +1 -1
- package/dist/tools/utils/inline-content.js +0 -1
- package/dist/transformers/jsonl.transformer.d.ts +1 -2
- package/dist/transformers/jsonl.transformer.d.ts.map +1 -1
- package/dist/transformers/jsonl.transformer.js +0 -1
- package/dist/transformers/jsonl.transformer.js.map +1 -1
- package/dist/transformers/markdown.transformer.d.ts +1 -2
- package/dist/transformers/markdown.transformer.d.ts.map +1 -1
- package/dist/transformers/markdown.transformer.js +11 -7
- package/dist/transformers/markdown.transformer.js.map +1 -1
- package/dist/utils/code-language.d.ts +2 -0
- package/dist/utils/code-language.d.ts.map +1 -0
- package/dist/utils/code-language.js +56 -0
- package/dist/utils/code-language.js.map +1 -0
- package/dist/utils/content-cleaner.d.ts +0 -2
- package/dist/utils/content-cleaner.d.ts.map +1 -1
- package/dist/utils/content-cleaner.js +0 -4
- package/dist/utils/content-cleaner.js.map +1 -1
- package/dist/utils/crypto.d.ts +2 -0
- package/dist/utils/crypto.d.ts.map +1 -0
- package/dist/utils/crypto.js +32 -0
- package/dist/utils/crypto.js.map +1 -0
- package/dist/utils/download-url.d.ts +8 -0
- package/dist/utils/download-url.d.ts.map +1 -0
- package/dist/utils/download-url.js +27 -0
- package/dist/utils/download-url.js.map +1 -0
- package/dist/utils/error-utils.d.ts +3 -0
- package/dist/utils/error-utils.d.ts.map +1 -0
- package/dist/utils/error-utils.js +12 -0
- package/dist/utils/error-utils.js.map +1 -0
- package/dist/utils/filename-generator.d.ts +1 -0
- package/dist/utils/filename-generator.d.ts.map +1 -0
- package/dist/utils/filename-generator.js +59 -0
- package/dist/utils/filename-generator.js.map +1 -0
- package/dist/utils/header-normalizer.d.ts +7 -4
- package/dist/utils/header-normalizer.d.ts.map +1 -1
- package/dist/utils/header-normalizer.js +23 -17
- package/dist/utils/header-normalizer.js.map +1 -1
- package/dist/utils/html-truncator.d.ts +0 -1
- package/dist/utils/html-truncator.js +0 -1
- package/dist/utils/sanitizer.d.ts +0 -1
- package/dist/utils/sanitizer.js +0 -1
- package/dist/utils/tool-error-handler.d.ts +1 -3
- package/dist/utils/tool-error-handler.d.ts.map +1 -1
- package/dist/utils/tool-error-handler.js +11 -6
- package/dist/utils/tool-error-handler.js.map +1 -1
- package/dist/utils/url-sanitizer.d.ts +2 -0
- package/dist/utils/url-sanitizer.d.ts.map +1 -0
- package/dist/utils/url-sanitizer.js +12 -0
- package/dist/utils/url-sanitizer.js.map +1 -0
- package/dist/utils/url-validator.d.ts +1 -3
- package/dist/utils/url-validator.d.ts.map +1 -1
- package/dist/utils/url-validator.js +89 -53
- package/dist/utils/url-validator.js.map +1 -1
- package/package.json +7 -9
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { config } from '../config/index.js';
|
|
3
|
+
import { requestContext } from '../services/context.js';
|
|
4
|
+
import { getSessionId } from './sessions.js';
|
|
5
|
+
const LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '::1']);
|
|
6
|
+
function normalizeHost(value) {
|
|
7
|
+
const trimmed = value.trim().toLowerCase();
|
|
8
|
+
if (!trimmed)
|
|
9
|
+
return null;
|
|
10
|
+
const first = trimmed.split(',')[0]?.trim();
|
|
11
|
+
if (!first)
|
|
12
|
+
return null;
|
|
13
|
+
if (first.startsWith('[')) {
|
|
14
|
+
const end = first.indexOf(']');
|
|
15
|
+
if (end === -1)
|
|
16
|
+
return null;
|
|
17
|
+
return first.slice(1, end);
|
|
18
|
+
}
|
|
19
|
+
const colonIndex = first.indexOf(':');
|
|
20
|
+
if (colonIndex !== -1) {
|
|
21
|
+
return first.slice(0, colonIndex);
|
|
22
|
+
}
|
|
23
|
+
return first;
|
|
24
|
+
}
|
|
25
|
+
function buildAllowedHosts() {
|
|
26
|
+
const allowedHosts = new Set();
|
|
27
|
+
const raw = process.env.ALLOWED_HOSTS ?? '';
|
|
28
|
+
for (const entry of raw.split(',')) {
|
|
29
|
+
const normalized = normalizeHost(entry);
|
|
30
|
+
if (normalized) {
|
|
31
|
+
allowedHosts.add(normalized);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
for (const host of LOOPBACK_HOSTS) {
|
|
35
|
+
allowedHosts.add(host);
|
|
36
|
+
}
|
|
37
|
+
const configuredHost = normalizeHost(config.server.host);
|
|
38
|
+
if (configuredHost &&
|
|
39
|
+
configuredHost !== '0.0.0.0' &&
|
|
40
|
+
configuredHost !== '::') {
|
|
41
|
+
allowedHosts.add(configuredHost);
|
|
42
|
+
}
|
|
43
|
+
return allowedHosts;
|
|
44
|
+
}
|
|
45
|
+
function createHostValidationMiddleware() {
|
|
46
|
+
const allowedHosts = buildAllowedHosts();
|
|
47
|
+
return (req, res, next) => {
|
|
48
|
+
const hostHeader = typeof req.headers.host === 'string' ? req.headers.host : '';
|
|
49
|
+
const normalized = normalizeHost(hostHeader);
|
|
50
|
+
if (!normalized || !allowedHosts.has(normalized)) {
|
|
51
|
+
res.status(403).json({
|
|
52
|
+
error: 'Host not allowed',
|
|
53
|
+
code: 'HOST_NOT_ALLOWED',
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
next();
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export function buildCorsOptions() {
|
|
61
|
+
const allowedOrigins = process.env.ALLOWED_ORIGINS
|
|
62
|
+
? process.env.ALLOWED_ORIGINS.split(',').map((o) => o.trim())
|
|
63
|
+
: [];
|
|
64
|
+
const allowAllOrigins = process.env.CORS_ALLOW_ALL === 'true';
|
|
65
|
+
return { allowedOrigins, allowAllOrigins };
|
|
66
|
+
}
|
|
67
|
+
export function createJsonParseErrorHandler() {
|
|
68
|
+
return (err, _req, res, next) => {
|
|
69
|
+
if (err instanceof SyntaxError && 'body' in err) {
|
|
70
|
+
res.status(400).json({
|
|
71
|
+
jsonrpc: '2.0',
|
|
72
|
+
error: {
|
|
73
|
+
code: -32700,
|
|
74
|
+
message: 'Parse error: Invalid JSON',
|
|
75
|
+
},
|
|
76
|
+
id: null,
|
|
77
|
+
});
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
next();
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export function createContextMiddleware() {
|
|
84
|
+
return (req, _res, next) => {
|
|
85
|
+
const requestId = randomUUID();
|
|
86
|
+
const sessionId = getSessionId(req);
|
|
87
|
+
requestContext.run({ requestId, sessionId }, () => {
|
|
88
|
+
next();
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
export function registerHealthRoute(app) {
|
|
93
|
+
app.get('/health', (_req, res) => {
|
|
94
|
+
res.json({
|
|
95
|
+
status: 'healthy',
|
|
96
|
+
name: config.server.name,
|
|
97
|
+
version: config.server.version,
|
|
98
|
+
uptime: process.uptime(),
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
export function attachBaseMiddleware(app, jsonParser, rateLimitMiddleware, authMiddleware, corsMiddleware) {
|
|
103
|
+
app.use(createHostValidationMiddleware());
|
|
104
|
+
app.use(jsonParser);
|
|
105
|
+
app.use(createContextMiddleware());
|
|
106
|
+
app.use(createJsonParseErrorHandler());
|
|
107
|
+
app.use(corsMiddleware);
|
|
108
|
+
app.use('/mcp', rateLimitMiddleware);
|
|
109
|
+
app.use(authMiddleware);
|
|
110
|
+
registerHealthRoute(app);
|
|
111
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-middleware.js","sourceRoot":"","sources":["../../src/http/server-middleware.ts"],"names":[],"mappings":"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"}
|
package/dist/http/server.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/http/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/http/server.ts"],"names":[],"mappings":"AAsHA,wBAAsB,eAAe,IAAI,OAAO,CAAC;IAC/C,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7C,CAAC,CAwCD"}
|
package/dist/http/server.js
CHANGED
|
@@ -1,50 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { config } from '../config/index.js';
|
|
4
|
-
import { requestContext } from '../services/context.js';
|
|
1
|
+
import { styleText } from 'node:util';
|
|
2
|
+
import { config, enableHttpMode } from '../config/index.js';
|
|
5
3
|
import { destroyAgents } from '../services/fetcher.js';
|
|
6
4
|
import { logError, logInfo, logWarn } from '../services/logger.js';
|
|
7
5
|
import { errorHandler } from '../middleware/error-handler.js';
|
|
6
|
+
import { getErrorMessage } from '../utils/error-utils.js';
|
|
8
7
|
import { createAuthMiddleware } from './auth.js';
|
|
9
8
|
import { createCorsMiddleware } from './cors.js';
|
|
10
|
-
import {
|
|
9
|
+
import { registerDownloadRoutes } from './download-routes.js';
|
|
10
|
+
import { registerMcpRoutes } from './mcp-routes.js';
|
|
11
11
|
import { createRateLimitMiddleware } from './rate-limit.js';
|
|
12
|
-
import {
|
|
12
|
+
import { attachBaseMiddleware, buildCorsOptions } from './server-middleware.js';
|
|
13
|
+
import { startSessionCleanupLoop } from './session-cleanup.js';
|
|
14
|
+
import { createSessionStore } from './sessions.js';
|
|
13
15
|
function isLoopbackHost(host) {
|
|
14
16
|
return host === '127.0.0.1' || host === '::1' || host === 'localhost';
|
|
15
17
|
}
|
|
16
|
-
function buildCorsOptions() {
|
|
17
|
-
const allowedOrigins = process.env.ALLOWED_ORIGINS
|
|
18
|
-
? process.env.ALLOWED_ORIGINS.split(',').map((o) => o.trim())
|
|
19
|
-
: [];
|
|
20
|
-
const allowAllOrigins = process.env.CORS_ALLOW_ALL === 'true';
|
|
21
|
-
return { allowedOrigins, allowAllOrigins };
|
|
22
|
-
}
|
|
23
|
-
function createJsonParseErrorHandler() {
|
|
24
|
-
return (err, _req, res, next) => {
|
|
25
|
-
if (err instanceof SyntaxError && 'body' in err) {
|
|
26
|
-
res.status(400).json({
|
|
27
|
-
jsonrpc: '2.0',
|
|
28
|
-
error: {
|
|
29
|
-
code: -32700,
|
|
30
|
-
message: 'Parse error: Invalid JSON',
|
|
31
|
-
},
|
|
32
|
-
id: null,
|
|
33
|
-
});
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
next();
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
function createContextMiddleware() {
|
|
40
|
-
return (req, _res, next) => {
|
|
41
|
-
const requestId = randomUUID();
|
|
42
|
-
const sessionId = getSessionId(req);
|
|
43
|
-
requestContext.run({ requestId, sessionId }, () => {
|
|
44
|
-
next();
|
|
45
|
-
});
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
18
|
function assertHttpConfiguration() {
|
|
49
19
|
if (!config.security.allowRemote && !isLoopbackHost(config.server.host)) {
|
|
50
20
|
logError('Refusing to bind to non-loopback host without ALLOW_REMOTE=true', { host: config.server.host });
|
|
@@ -55,59 +25,6 @@ function assertHttpConfiguration() {
|
|
|
55
25
|
process.exit(1);
|
|
56
26
|
}
|
|
57
27
|
}
|
|
58
|
-
function registerHealthRoute(app) {
|
|
59
|
-
app.get('/health', (_req, res) => {
|
|
60
|
-
res.json({
|
|
61
|
-
status: 'healthy',
|
|
62
|
-
name: config.server.name,
|
|
63
|
-
version: config.server.version,
|
|
64
|
-
uptime: process.uptime(),
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
function attachBaseMiddleware(app, jsonParser, rateLimitMiddleware, authMiddleware, corsOptions) {
|
|
69
|
-
app.use(jsonParser);
|
|
70
|
-
app.use(createContextMiddleware());
|
|
71
|
-
app.use(createJsonParseErrorHandler());
|
|
72
|
-
app.use(createCorsMiddleware(corsOptions));
|
|
73
|
-
app.use('/mcp', rateLimitMiddleware);
|
|
74
|
-
app.use(authMiddleware);
|
|
75
|
-
registerHealthRoute(app);
|
|
76
|
-
}
|
|
77
|
-
function startSessionCleanupLoop(store, sessionTtlMs) {
|
|
78
|
-
const controller = new AbortController();
|
|
79
|
-
void runSessionCleanupLoop(store, sessionTtlMs, controller.signal).catch(handleSessionCleanupError);
|
|
80
|
-
return controller;
|
|
81
|
-
}
|
|
82
|
-
async function runSessionCleanupLoop(store, sessionTtlMs, signal) {
|
|
83
|
-
const intervalMs = getCleanupIntervalMs(sessionTtlMs);
|
|
84
|
-
for await (const _ of setIntervalPromise(intervalMs, undefined, {
|
|
85
|
-
signal,
|
|
86
|
-
ref: false,
|
|
87
|
-
})) {
|
|
88
|
-
handleSessionEvictions(store);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
function getCleanupIntervalMs(sessionTtlMs) {
|
|
92
|
-
return Math.min(Math.max(Math.floor(sessionTtlMs / 2), 10000), 60000);
|
|
93
|
-
}
|
|
94
|
-
function isAbortError(error) {
|
|
95
|
-
return error instanceof Error && error.name === 'AbortError';
|
|
96
|
-
}
|
|
97
|
-
function handleSessionEvictions(store) {
|
|
98
|
-
const evicted = evictExpiredSessions(store);
|
|
99
|
-
if (evicted > 0) {
|
|
100
|
-
logInfo('Expired sessions evicted', { evicted });
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
function handleSessionCleanupError(error) {
|
|
104
|
-
if (isAbortError(error)) {
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
logWarn('Session cleanup loop failed', {
|
|
108
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
28
|
function startListening(app) {
|
|
112
29
|
return app
|
|
113
30
|
.listen(config.server.port, config.server.host, () => {
|
|
@@ -115,10 +32,10 @@ function startListening(app) {
|
|
|
115
32
|
host: config.server.host,
|
|
116
33
|
port: config.server.port,
|
|
117
34
|
});
|
|
118
|
-
process.stdout.write(
|
|
119
|
-
process.stdout.write(` Health check: http://${config.server.host}:${config.server.port}/health\n`);
|
|
120
|
-
process.stdout.write(` MCP endpoint: http://${config.server.host}:${config.server.port}/mcp\n`);
|
|
121
|
-
process.stdout.write(`\
|
|
35
|
+
process.stdout.write(`${styleText('green', '✓')} superFetch MCP server running at ${styleText('cyan', `http://${config.server.host}:${config.server.port}`)}\n`);
|
|
36
|
+
process.stdout.write(` Health check: ${styleText('dim', `http://${config.server.host}:${config.server.port}/health`)}\n`);
|
|
37
|
+
process.stdout.write(` MCP endpoint: ${styleText('dim', `http://${config.server.host}:${config.server.port}/mcp`)}\n`);
|
|
38
|
+
process.stdout.write(`\n${styleText('dim', 'Run with --stdio flag for direct stdio integration')}\n`);
|
|
122
39
|
})
|
|
123
40
|
.on('error', (err) => {
|
|
124
41
|
logError('Failed to start server', err);
|
|
@@ -127,13 +44,13 @@ function startListening(app) {
|
|
|
127
44
|
}
|
|
128
45
|
function createShutdownHandler(server, sessionStore, sessionCleanupController, stopRateLimitCleanup) {
|
|
129
46
|
return async (signal) => {
|
|
130
|
-
process.stdout.write(`\n${signal} received, shutting down gracefully...\n`);
|
|
47
|
+
process.stdout.write(`\n${styleText('yellow', signal)} received, shutting down gracefully...\n`);
|
|
131
48
|
stopRateLimitCleanup();
|
|
132
49
|
sessionCleanupController.abort();
|
|
133
50
|
const sessions = sessionStore.clear();
|
|
134
51
|
await Promise.allSettled(sessions.map((session) => session.transport.close().catch((error) => {
|
|
135
52
|
logWarn('Failed to close session during shutdown', {
|
|
136
|
-
error: error
|
|
53
|
+
error: getErrorMessage(error),
|
|
137
54
|
});
|
|
138
55
|
})));
|
|
139
56
|
destroyAgents();
|
|
@@ -156,11 +73,12 @@ function registerSignalHandlers(shutdown) {
|
|
|
156
73
|
});
|
|
157
74
|
}
|
|
158
75
|
export async function startHttpServer() {
|
|
76
|
+
enableHttpMode();
|
|
159
77
|
const { app, jsonParser } = await createExpressApp();
|
|
160
78
|
const corsOptions = buildCorsOptions();
|
|
161
79
|
const { middleware: rateLimitMiddleware, stop: stopRateLimitCleanup } = createRateLimitMiddleware(config.rateLimit);
|
|
162
80
|
const authMiddleware = createAuthMiddleware(config.security.apiKey ?? '');
|
|
163
|
-
attachBaseMiddleware(app, jsonParser, rateLimitMiddleware, authMiddleware, corsOptions);
|
|
81
|
+
attachBaseMiddleware(app, jsonParser, rateLimitMiddleware, authMiddleware, createCorsMiddleware(corsOptions));
|
|
164
82
|
assertHttpConfiguration();
|
|
165
83
|
const sessionStore = createSessionStore(config.server.sessionTtlMs);
|
|
166
84
|
const sessionCleanupController = startSessionCleanupLoop(sessionStore, config.server.sessionTtlMs);
|
|
@@ -168,6 +86,7 @@ export async function startHttpServer() {
|
|
|
168
86
|
sessionStore,
|
|
169
87
|
maxSessions: config.server.maxSessions,
|
|
170
88
|
});
|
|
89
|
+
registerDownloadRoutes(app);
|
|
171
90
|
app.use(errorHandler);
|
|
172
91
|
const server = startListening(app);
|
|
173
92
|
const shutdown = createShutdownHandler(server, sessionStore, sessionCleanupController, stopRateLimitCleanup);
|
|
@@ -177,7 +96,9 @@ export async function startHttpServer() {
|
|
|
177
96
|
async function createExpressApp() {
|
|
178
97
|
const { default: express } = await import('express');
|
|
179
98
|
const app = express();
|
|
99
|
+
if (config.server.trustProxy) {
|
|
100
|
+
app.set('trust proxy', true);
|
|
101
|
+
}
|
|
180
102
|
const jsonParser = express.json({ limit: '1mb' });
|
|
181
103
|
return { app, jsonParser };
|
|
182
104
|
}
|
|
183
|
-
//# sourceMappingURL=server.js.map
|
package/dist/http/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/http/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/http/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAItC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE5D,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEnE,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAE9D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD,SAAS,cAAc,CAAC,IAAY;IAClC,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,WAAW,CAAC;AACxE,CAAC;AAED,SAAS,uBAAuB;IAC9B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QACxE,QAAQ,CACN,iEAAiE,EACjE,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAC7B,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC5B,QAAQ,CAAC,sDAAsD,CAAC,CAAC;QACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,GAAY;IAClC,OAAO,GAAG;SACP,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;QACnD,OAAO,CAAC,+BAA+B,EAAE;YACvC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI;YACxB,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI;SACzB,CAAC,CAAC;QAEH,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,qCAAqC,SAAS,CAAC,MAAM,EAAE,UAAU,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,IAAI,CAC3I,CAAC;QACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,mBAAmB,SAAS,CAAC,KAAK,EAAE,UAAU,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC,IAAI,CACrG,CAAC;QACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,mBAAmB,SAAS,CAAC,KAAK,EAAE,UAAU,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAClG,CAAC;QACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,KAAK,SAAS,CAAC,KAAK,EAAE,oDAAoD,CAAC,IAAI,CAChF,CAAC;IACJ,CAAC,CAAC;SACD,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QACnB,QAAQ,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAAC;QACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACP,CAAC;AAED,SAAS,qBAAqB,CAC5B,MAAqC,EACrC,YAAmD,EACnD,wBAAyC,EACzC,oBAAgC;IAEhC,OAAO,KAAK,EAAE,MAAc,EAAiB,EAAE;QAC7C,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,KAAK,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,0CAA0C,CAC3E,CAAC;QAEF,oBAAoB,EAAE,CAAC;QACvB,wBAAwB,CAAC,KAAK,EAAE,CAAC;QAEjC,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,EAAE,CAAC;QACtC,MAAM,OAAO,CAAC,UAAU,CACtB,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CACvB,OAAO,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;YACjD,OAAO,CAAC,yCAAyC,EAAE;gBACjD,KAAK,EAAE,eAAe,CAAC,KAAK,CAAC;aAC9B,CAAC,CAAC;QACL,CAAC,CAAC,CACH,CACF,CAAC;QAEF,aAAa,EAAE,CAAC;QAEhB,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;YAChB,OAAO,CAAC,oBAAoB,CAAC,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;QAEH,UAAU,CAAC,GAAG,EAAE;YACd,QAAQ,CAAC,+BAA+B,CAAC,CAAC;YAC1C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,EAAE,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,sBAAsB,CAC7B,QAA2C;IAE3C,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;QACxB,KAAK,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;QACzB,KAAK,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe;IAGnC,cAAc,EAAE,CAAC;IAEjB,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,MAAM,gBAAgB,EAAE,CAAC;IACrD,MAAM,WAAW,GAAG,gBAAgB,EAAE,CAAC;IACvC,MAAM,EAAE,UAAU,EAAE,mBAAmB,EAAE,IAAI,EAAE,oBAAoB,EAAE,GACnE,yBAAyB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC9C,MAAM,cAAc,GAAG,oBAAoB,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IAE1E,oBAAoB,CAClB,GAAG,EACH,UAAU,EACV,mBAAmB,EACnB,cAAc,EACd,oBAAoB,CAAC,WAAW,CAAC,CAClC,CAAC;IACF,uBAAuB,EAAE,CAAC;IAE1B,MAAM,YAAY,GAAG,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IACpE,MAAM,wBAAwB,GAAG,uBAAuB,CACtD,YAAY,EACZ,MAAM,CAAC,MAAM,CAAC,YAAY,CAC3B,CAAC;IAEF,iBAAiB,CAAC,GAAG,EAAE;QACrB,YAAY;QACZ,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,WAAW;KACvC,CAAC,CAAC;IACH,sBAAsB,CAAC,GAAG,CAAC,CAAC;IAC5B,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAEtB,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,QAAQ,GAAG,qBAAqB,CACpC,MAAM,EACN,YAAY,EACZ,wBAAwB,EACxB,oBAAoB,CACrB,CAAC;IACF,sBAAsB,CAAC,QAAQ,CAAC,CAAC;IACjC,OAAO,EAAE,QAAQ,EAAE,CAAC;AACtB,CAAC;AAED,KAAK,UAAU,gBAAgB;IAI7B,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;IACrD,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IACtB,IAAI,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QAC7B,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;IAC/B,CAAC;IACD,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAClD,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-cleanup.d.ts","sourceRoot":"","sources":["../../src/http/session-cleanup.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,YAAY,EACnB,YAAY,EAAE,MAAM,GACnB,eAAe,CAMjB"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { setInterval as setIntervalPromise } from 'node:timers/promises';
|
|
2
|
+
import { logInfo, logWarn } from '../services/logger.js';
|
|
3
|
+
import { evictExpiredSessions } from './mcp-routes.js';
|
|
4
|
+
export function startSessionCleanupLoop(store, sessionTtlMs) {
|
|
5
|
+
const controller = new AbortController();
|
|
6
|
+
void runSessionCleanupLoop(store, sessionTtlMs, controller.signal).catch(handleSessionCleanupError);
|
|
7
|
+
return controller;
|
|
8
|
+
}
|
|
9
|
+
async function runSessionCleanupLoop(store, sessionTtlMs, signal) {
|
|
10
|
+
const intervalMs = getCleanupIntervalMs(sessionTtlMs);
|
|
11
|
+
for await (const _ of setIntervalPromise(intervalMs, undefined, {
|
|
12
|
+
signal,
|
|
13
|
+
ref: false,
|
|
14
|
+
})) {
|
|
15
|
+
handleSessionEvictions(store);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function getCleanupIntervalMs(sessionTtlMs) {
|
|
19
|
+
return Math.min(Math.max(Math.floor(sessionTtlMs / 2), 10000), 60000);
|
|
20
|
+
}
|
|
21
|
+
function isAbortError(error) {
|
|
22
|
+
return error instanceof Error && error.name === 'AbortError';
|
|
23
|
+
}
|
|
24
|
+
function handleSessionEvictions(store) {
|
|
25
|
+
const evicted = evictExpiredSessions(store);
|
|
26
|
+
if (evicted > 0) {
|
|
27
|
+
logInfo('Expired sessions evicted', { evicted });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function handleSessionCleanupError(error) {
|
|
31
|
+
if (isAbortError(error)) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
logWarn('Session cleanup loop failed', {
|
|
35
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-cleanup.js","sourceRoot":"","sources":["../../src/http/session-cleanup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAEzE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAGvD,MAAM,UAAU,uBAAuB,CACrC,KAAmB,EACnB,YAAoB;IAEpB,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,KAAK,qBAAqB,CAAC,KAAK,EAAE,YAAY,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,KAAK,CACtE,yBAAyB,CAC1B,CAAC;IACF,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,KAAK,UAAU,qBAAqB,CAClC,KAAmB,EACnB,YAAoB,EACpB,MAAmB;IAEnB,MAAM,UAAU,GAAG,oBAAoB,CAAC,YAAY,CAAC,CAAC;IACtD,IAAI,KAAK,EAAE,MAAM,CAAC,IAAI,kBAAkB,CAAC,UAAU,EAAE,SAAS,EAAE;QAC9D,MAAM;QACN,GAAG,EAAE,KAAK;KACX,CAAC,EAAE,CAAC;QACH,sBAAsB,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB,CAAC,YAAoB;IAChD,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;AACxE,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,OAAO,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC;AAC/D,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAmB;IACjD,MAAM,OAAO,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAC5C,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,OAAO,CAAC,0BAA0B,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC;AAED,SAAS,yBAAyB,CAAC,KAAc;IAC/C,IAAI,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO;IACT,CAAC;IACD,OAAO,CAAC,6BAA6B,EAAE;QACrC,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;KAChE,CAAC,CAAC;AACL,CAAC"}
|
package/dist/http/sessions.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Request } from 'express';
|
|
2
|
-
import type { SessionEntry } from '../config/types.js';
|
|
2
|
+
import type { SessionEntry } from '../config/types/runtime.js';
|
|
3
3
|
export interface SessionStore {
|
|
4
4
|
get: (sessionId: string) => SessionEntry | undefined;
|
|
5
5
|
touch: (sessionId: string) => void;
|
|
@@ -12,4 +12,3 @@ export interface SessionStore {
|
|
|
12
12
|
}
|
|
13
13
|
export declare function getSessionId(req: Request): string | undefined;
|
|
14
14
|
export declare function createSessionStore(sessionTtlMs: number): SessionStore;
|
|
15
|
-
//# sourceMappingURL=sessions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../src/http/sessions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../src/http/sessions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE/D,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,YAAY,GAAG,SAAS,CAAC;IACrD,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,GAAG,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IACtD,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,YAAY,GAAG,SAAS,CAAC;IACxD,IAAI,EAAE,MAAM,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,YAAY,EAAE,CAAC;IAC5B,YAAY,EAAE,MAAM,YAAY,EAAE,CAAC;IACnC,WAAW,EAAE,MAAM,YAAY,GAAG,SAAS,CAAC;CAC7C;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAG7D;AAED,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,YAAY,CAiBrE"}
|
package/dist/http/sessions.js
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -35,11 +35,18 @@ process.on('unhandledRejection', (reason) => {
|
|
|
35
35
|
logError('Unhandled rejection', error);
|
|
36
36
|
process.stderr.write(`Unhandled rejection: ${error.message}\n`);
|
|
37
37
|
});
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
try {
|
|
39
|
+
if (isStdioMode) {
|
|
40
|
+
await startStdioServer();
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
const { shutdown } = await startHttpServer();
|
|
44
|
+
shutdownHandlerRef.current = shutdown;
|
|
45
|
+
}
|
|
40
46
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
47
|
+
catch (error) {
|
|
48
|
+
logError('Failed to start server', error instanceof Error ? error : undefined);
|
|
49
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
50
|
+
process.stderr.write(`Failed to start server: ${message}\n`);
|
|
51
|
+
process.exit(1);
|
|
44
52
|
}
|
|
45
|
-
//# 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":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE;QACP,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;KAC3C;CACF,CAAC,CAAC;AACH,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC;AACjC,IAAI,cAAc,GAAG,KAAK,CAAC;AAE3B,MAAM,kBAAkB,GAAoD,EAAE,CAAC;AAE/E,SAAS,qBAAqB;IAC5B,OAAO,CAAC,cAAc,IAAI,CAAC,WAAW,IAAI,OAAO,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;AAChF,CAAC;AAED,SAAS,eAAe,CAAC,MAAc;IACrC,IAAI,CAAC,kBAAkB,CAAC,OAAO;QAAE,OAAO;IACxC,cAAc,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;IAC1D,KAAK,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;AAC1C,CAAC;AAED,OAAO,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,KAAK,EAAE,EAAE;IACxC,QAAQ,CAAC,oBAAoB,EAAE,KAAK,CAAC,CAAC;IACtC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;IAE/D,IAAI,qBAAqB,EAAE,EAAE,CAAC;QAC5B,eAAe,CAAC,oBAAoB,CAAC,CAAC;QACtC,OAAO;IACT,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,MAAM,EAAE,EAAE;IAC1C,MAAM,KAAK,GAAG,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3E,QAAQ,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;IACvC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAwB,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;AAClE,CAAC,CAAC,CAAC;AAEH,IAAI,WAAW,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE;QACP,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;KAC3C;CACF,CAAC,CAAC;AACH,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC;AACjC,IAAI,cAAc,GAAG,KAAK,CAAC;AAE3B,MAAM,kBAAkB,GAAoD,EAAE,CAAC;AAE/E,SAAS,qBAAqB;IAC5B,OAAO,CAAC,cAAc,IAAI,CAAC,WAAW,IAAI,OAAO,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;AAChF,CAAC;AAED,SAAS,eAAe,CAAC,MAAc;IACrC,IAAI,CAAC,kBAAkB,CAAC,OAAO;QAAE,OAAO;IACxC,cAAc,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;IAC1D,KAAK,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;AAC1C,CAAC;AAED,OAAO,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,KAAK,EAAE,EAAE;IACxC,QAAQ,CAAC,oBAAoB,EAAE,KAAK,CAAC,CAAC;IACtC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;IAE/D,IAAI,qBAAqB,EAAE,EAAE,CAAC;QAC5B,eAAe,CAAC,oBAAoB,CAAC,CAAC;QACtC,OAAO;IACT,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,MAAM,EAAE,EAAE;IAC1C,MAAM,KAAK,GAAG,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3E,QAAQ,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;IACvC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAwB,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;AAClE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC;IACH,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,gBAAgB,EAAE,CAAC;IAC3B,CAAC;SAAM,CAAC;QACN,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,eAAe,EAAE,CAAC;QAC7C,kBAAkB,CAAC,OAAO,GAAG,QAAQ,CAAC;IACxC,CAAC;AACH,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,QAAQ,CACN,wBAAwB,EACxB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAC3C,CAAC;IACF,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2BAA2B,OAAO,IAAI,CAAC,CAAC;IAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cached-content.d.ts","sourceRoot":"","sources":["../../src/resources/cached-content.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"cached-content.d.ts","sourceRoot":"","sources":["../../src/resources/cached-content.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA2EzE,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAIrE"}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
2
3
|
import * as cache from '../services/cache.js';
|
|
3
4
|
import { logWarn } from '../services/logger.js';
|
|
5
|
+
import { getErrorMessage } from '../utils/error-utils.js';
|
|
6
|
+
const VALID_NAMESPACES = new Set(['url', 'markdown', 'links']);
|
|
7
|
+
const HASH_PATTERN = /^[a-f0-9.]+$/i;
|
|
4
8
|
function buildResourceEntry(namespace, urlHash) {
|
|
5
9
|
return {
|
|
6
10
|
name: `${namespace}:${urlHash}`,
|
|
7
11
|
uri: `superfetch://cache/${namespace}/${urlHash}`,
|
|
8
12
|
description: `Cached content entry for ${namespace}`,
|
|
9
|
-
mimeType:
|
|
13
|
+
mimeType: resolveCacheMimeType(namespace),
|
|
10
14
|
};
|
|
11
15
|
}
|
|
12
16
|
function listCachedResources() {
|
|
@@ -42,7 +46,7 @@ function notifyResourceUpdate(server, uri) {
|
|
|
42
46
|
void server.server.sendResourceUpdated({ uri }).catch((error) => {
|
|
43
47
|
logWarn('Failed to send resource update notification', {
|
|
44
48
|
uri,
|
|
45
|
-
error: error
|
|
49
|
+
error: getErrorMessage(error),
|
|
46
50
|
});
|
|
47
51
|
});
|
|
48
52
|
}
|
|
@@ -52,24 +56,45 @@ export function registerCachedContentResource(server) {
|
|
|
52
56
|
registerCacheUpdateSubscription(server);
|
|
53
57
|
}
|
|
54
58
|
function resolveCacheParams(params) {
|
|
55
|
-
const namespace = params.namespace;
|
|
56
|
-
const urlHash = params.urlHash;
|
|
59
|
+
const namespace = resolveStringParam(params.namespace);
|
|
60
|
+
const urlHash = resolveStringParam(params.urlHash);
|
|
57
61
|
if (!namespace || !urlHash) {
|
|
58
|
-
throw new
|
|
62
|
+
throw new McpError(ErrorCode.InvalidParams, 'Both namespace and urlHash parameters are required');
|
|
63
|
+
}
|
|
64
|
+
if (!isValidNamespace(namespace) || !isValidHash(urlHash)) {
|
|
65
|
+
throw new McpError(ErrorCode.InvalidParams, 'Invalid cache resource parameters');
|
|
59
66
|
}
|
|
60
67
|
return { namespace, urlHash };
|
|
61
68
|
}
|
|
62
|
-
function buildCachedContentResponse(uri, cacheKey) {
|
|
69
|
+
function buildCachedContentResponse(uri, cacheKey, namespace) {
|
|
63
70
|
const cached = cache.get(cacheKey);
|
|
64
71
|
if (!cached) {
|
|
65
|
-
throw new
|
|
72
|
+
throw new McpError(ErrorCode.InvalidParams, `Content not found in cache for key: ${cacheKey}. Use superfetch://stats to see available cache entries.`);
|
|
73
|
+
}
|
|
74
|
+
if (namespace !== 'url' && namespace !== 'markdown') {
|
|
75
|
+
return {
|
|
76
|
+
contents: [
|
|
77
|
+
{
|
|
78
|
+
uri: uri.href,
|
|
79
|
+
mimeType: resolveCacheMimeType(namespace),
|
|
80
|
+
text: cached.content,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const payload = parseCachedPayload(cached.content);
|
|
86
|
+
const resolvedContent = payload
|
|
87
|
+
? resolvePayloadContent(payload, namespace)
|
|
88
|
+
: null;
|
|
89
|
+
if (!resolvedContent) {
|
|
90
|
+
throw new McpError(ErrorCode.InternalError, `Cached content is missing for namespace ${namespace}`);
|
|
66
91
|
}
|
|
67
92
|
return {
|
|
68
93
|
contents: [
|
|
69
94
|
{
|
|
70
95
|
uri: uri.href,
|
|
71
|
-
mimeType:
|
|
72
|
-
text:
|
|
96
|
+
mimeType: resolveCacheMimeType(namespace),
|
|
97
|
+
text: resolvedContent,
|
|
73
98
|
},
|
|
74
99
|
],
|
|
75
100
|
};
|
|
@@ -80,11 +105,11 @@ function registerCacheContentResource(server) {
|
|
|
80
105
|
}), {
|
|
81
106
|
title: 'Cached Content',
|
|
82
107
|
description: 'Access previously fetched web content from cache. Namespace: url, links, markdown. UrlHash: SHA-256 hash of the URL.',
|
|
83
|
-
mimeType: '
|
|
108
|
+
mimeType: 'text/plain',
|
|
84
109
|
}, (uri, params) => {
|
|
85
110
|
const { namespace, urlHash } = resolveCacheParams(params);
|
|
86
111
|
const cacheKey = `${namespace}:${urlHash}`;
|
|
87
|
-
return buildCachedContentResponse(uri, cacheKey);
|
|
112
|
+
return buildCachedContentResponse(uri, cacheKey, namespace);
|
|
88
113
|
});
|
|
89
114
|
}
|
|
90
115
|
function registerCacheListResource(server) {
|
|
@@ -119,4 +144,43 @@ function registerCacheUpdateSubscription(server) {
|
|
|
119
144
|
unsubscribe();
|
|
120
145
|
};
|
|
121
146
|
}
|
|
122
|
-
|
|
147
|
+
function resolveCacheMimeType(namespace) {
|
|
148
|
+
if (namespace === 'markdown')
|
|
149
|
+
return 'text/markdown';
|
|
150
|
+
if (namespace === 'url')
|
|
151
|
+
return 'application/jsonl';
|
|
152
|
+
return 'application/json';
|
|
153
|
+
}
|
|
154
|
+
function isValidNamespace(namespace) {
|
|
155
|
+
return VALID_NAMESPACES.has(namespace);
|
|
156
|
+
}
|
|
157
|
+
function isValidHash(hash) {
|
|
158
|
+
return HASH_PATTERN.test(hash) && hash.length >= 8 && hash.length <= 64;
|
|
159
|
+
}
|
|
160
|
+
function resolveStringParam(value) {
|
|
161
|
+
return typeof value === 'string' ? value : null;
|
|
162
|
+
}
|
|
163
|
+
function parseCachedPayload(raw) {
|
|
164
|
+
try {
|
|
165
|
+
const parsed = JSON.parse(raw);
|
|
166
|
+
if (parsed && typeof parsed === 'object') {
|
|
167
|
+
return parsed;
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function resolvePayloadContent(payload, namespace) {
|
|
176
|
+
if (namespace === 'markdown') {
|
|
177
|
+
if (typeof payload.markdown === 'string') {
|
|
178
|
+
return payload.markdown;
|
|
179
|
+
}
|
|
180
|
+
if (typeof payload.content === 'string') {
|
|
181
|
+
return payload.content;
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
return typeof payload.content === 'string' ? payload.content : null;
|
|
186
|
+
}
|