@j0hanz/superfetch 1.0.2 → 1.0.5
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 +345 -57
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +6 -10
- package/dist/config/index.js.map +1 -1
- package/dist/config/types.d.ts +256 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/errors/app-error.d.ts +6 -20
- package/dist/errors/app-error.d.ts.map +1 -1
- package/dist/errors/app-error.js +7 -18
- package/dist/errors/app-error.js.map +1 -1
- package/dist/index.js +75 -62
- package/dist/index.js.map +1 -1
- package/dist/middleware/error-handler.d.ts +1 -5
- package/dist/middleware/error-handler.d.ts.map +1 -1
- package/dist/middleware/error-handler.js +4 -12
- package/dist/middleware/error-handler.js.map +1 -1
- package/dist/middleware/rate-limiter.d.ts +2 -20
- package/dist/middleware/rate-limiter.d.ts.map +1 -1
- package/dist/middleware/rate-limiter.js +22 -47
- package/dist/middleware/rate-limiter.js.map +1 -1
- package/dist/prompts/index.d.ts +0 -3
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +2 -10
- package/dist/prompts/index.js.map +1 -1
- package/dist/resources/cached-content.d.ts +5 -0
- package/dist/resources/cached-content.d.ts.map +1 -0
- package/dist/resources/cached-content.js +93 -0
- package/dist/resources/cached-content.js.map +1 -0
- package/dist/resources/index.d.ts +0 -3
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/index.js +40 -5
- package/dist/resources/index.js.map +1 -1
- package/dist/server.d.ts +0 -4
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +11 -6
- package/dist/server.js.map +1 -1
- package/dist/services/cache.d.ts +20 -6
- package/dist/services/cache.d.ts.map +1 -1
- package/dist/services/cache.js +128 -20
- package/dist/services/cache.js.map +1 -1
- package/dist/services/card-extractor.d.ts +10 -0
- package/dist/services/card-extractor.d.ts.map +1 -0
- package/dist/services/card-extractor.js +194 -0
- package/dist/services/card-extractor.js.map +1 -0
- package/dist/services/extractor.d.ts +12 -19
- package/dist/services/extractor.d.ts.map +1 -1
- package/dist/services/extractor.js +60 -46
- package/dist/services/extractor.js.map +1 -1
- package/dist/services/fetcher.d.ts +13 -11
- package/dist/services/fetcher.d.ts.map +1 -1
- package/dist/services/fetcher.js +143 -54
- package/dist/services/fetcher.js.map +1 -1
- package/dist/services/logger.d.ts.map +1 -1
- package/dist/services/logger.js +4 -6
- package/dist/services/logger.js.map +1 -1
- package/dist/services/parser.d.ts +1 -6
- package/dist/services/parser.d.ts.map +1 -1
- package/dist/services/parser.js +57 -27
- package/dist/services/parser.js.map +1 -1
- package/dist/tools/handlers/fetch-links.tool.d.ts +6 -18
- package/dist/tools/handlers/fetch-links.tool.d.ts.map +1 -1
- package/dist/tools/handlers/fetch-links.tool.js +104 -79
- package/dist/tools/handlers/fetch-links.tool.js.map +1 -1
- package/dist/tools/handlers/fetch-markdown.tool.d.ts +6 -10
- package/dist/tools/handlers/fetch-markdown.tool.d.ts.map +1 -1
- package/dist/tools/handlers/fetch-markdown.tool.js +83 -84
- package/dist/tools/handlers/fetch-markdown.tool.js.map +1 -1
- package/dist/tools/handlers/fetch-url.tool.d.ts +6 -12
- package/dist/tools/handlers/fetch-url.tool.d.ts.map +1 -1
- package/dist/tools/handlers/fetch-url.tool.js +51 -93
- package/dist/tools/handlers/fetch-url.tool.js.map +1 -1
- package/dist/tools/handlers/fetch-urls.tool.d.ts +12 -0
- package/dist/tools/handlers/fetch-urls.tool.d.ts.map +1 -0
- package/dist/tools/handlers/fetch-urls.tool.js +184 -0
- package/dist/tools/handlers/fetch-urls.tool.js.map +1 -0
- package/dist/tools/index.d.ts +0 -4
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +145 -15
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/utils/common.d.ts +8 -0
- package/dist/tools/utils/common.d.ts.map +1 -0
- package/dist/tools/utils/common.js +35 -0
- package/dist/tools/utils/common.js.map +1 -0
- package/dist/tools/utils/fetch-pipeline.d.ts +3 -0
- package/dist/tools/utils/fetch-pipeline.d.ts.map +1 -0
- package/dist/tools/utils/fetch-pipeline.js +78 -0
- package/dist/tools/utils/fetch-pipeline.js.map +1 -0
- package/dist/tools/utils/index.d.ts +4 -0
- package/dist/tools/utils/index.d.ts.map +1 -0
- package/dist/tools/utils/index.js +3 -0
- package/dist/tools/utils/index.js.map +1 -0
- package/dist/tools/utils/response-builder.d.ts +3 -0
- package/dist/tools/utils/response-builder.d.ts.map +1 -0
- package/dist/tools/utils/response-builder.js +24 -0
- package/dist/tools/utils/response-builder.js.map +1 -0
- package/dist/transformers/jsonl.transformer.d.ts +1 -1
- package/dist/transformers/jsonl.transformer.d.ts.map +1 -1
- package/dist/transformers/jsonl.transformer.js +2 -1
- package/dist/transformers/jsonl.transformer.js.map +1 -1
- package/dist/transformers/markdown.transformer.d.ts +1 -1
- package/dist/transformers/markdown.transformer.d.ts.map +1 -1
- package/dist/transformers/markdown.transformer.js +99 -5
- package/dist/transformers/markdown.transformer.js.map +1 -1
- package/dist/types/content.types.d.ts +11 -11
- package/dist/types/content.types.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -2
- package/dist/types/index.js.map +1 -1
- package/dist/types/schemas.d.ts +39 -12
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/utils/concurrency.d.ts +6 -0
- package/dist/utils/concurrency.d.ts.map +1 -0
- package/dist/utils/concurrency.js +38 -0
- package/dist/utils/concurrency.js.map +1 -0
- package/dist/utils/content-cleaner.d.ts +32 -0
- package/dist/utils/content-cleaner.d.ts.map +1 -0
- package/dist/utils/content-cleaner.js +238 -0
- package/dist/utils/content-cleaner.js.map +1 -0
- package/dist/utils/language-detector.d.ts +5 -0
- package/dist/utils/language-detector.d.ts.map +1 -0
- package/dist/utils/language-detector.js +50 -0
- package/dist/utils/language-detector.js.map +1 -0
- package/dist/utils/sanitizer.d.ts +0 -10
- package/dist/utils/sanitizer.d.ts.map +1 -1
- package/dist/utils/sanitizer.js +4 -12
- package/dist/utils/sanitizer.js.map +1 -1
- package/dist/utils/tool-error-handler.d.ts +1 -15
- package/dist/utils/tool-error-handler.d.ts.map +1 -1
- package/dist/utils/tool-error-handler.js +34 -6
- package/dist/utils/tool-error-handler.js.map +1 -1
- package/dist/utils/url-validator.d.ts +0 -8
- package/dist/utils/url-validator.d.ts.map +1 -1
- package/dist/utils/url-validator.js +17 -31
- package/dist/utils/url-validator.js.map +1 -1
- package/package.json +81 -79
package/dist/index.js
CHANGED
|
@@ -1,50 +1,55 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import express from 'express';
|
|
2
|
+
import express, {} from 'express';
|
|
3
3
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
4
4
|
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
|
|
5
5
|
import { config } from './config/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import { destroyAgents } from './services/fetcher.js';
|
|
7
|
+
import { logError, logInfo } from './services/logger.js';
|
|
7
8
|
import { errorHandler } from './middleware/error-handler.js';
|
|
8
9
|
import { rateLimiter } from './middleware/rate-limiter.js';
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
//
|
|
10
|
+
import { createMcpServer } from './server.js';
|
|
11
|
+
let isShuttingDown = false;
|
|
12
|
+
// Ref for shutdown handler to be assigned later
|
|
13
|
+
const shutdownHandlerRef = {};
|
|
12
14
|
process.on('uncaughtException', (error) => {
|
|
13
15
|
logError('Uncaught exception', error);
|
|
14
16
|
process.stderr.write(`Uncaught exception: ${error.message}\n`);
|
|
15
|
-
|
|
17
|
+
if (!isShuttingDown && !isStdioMode && shutdownHandlerRef.current) {
|
|
18
|
+
isShuttingDown = true;
|
|
19
|
+
// Attempt graceful cleanup before exit
|
|
20
|
+
process.stderr.write('Attempting graceful shutdown...\n');
|
|
21
|
+
void shutdownHandlerRef.current('UNCAUGHT_EXCEPTION');
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
16
26
|
});
|
|
17
27
|
process.on('unhandledRejection', (reason) => {
|
|
18
28
|
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
19
29
|
logError('Unhandled rejection', error);
|
|
20
30
|
process.stderr.write(`Unhandled rejection: ${error.message}\n`);
|
|
21
31
|
});
|
|
22
|
-
// Check if running in stdio mode
|
|
23
32
|
const isStdioMode = process.argv.includes('--stdio');
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
const ALLOWED_ORIGINS = process.env.ALLOWED_ORIGINS
|
|
34
|
+
? process.env.ALLOWED_ORIGINS.split(',').map((o) => o.trim())
|
|
35
|
+
: [];
|
|
36
|
+
const ALLOW_ALL_ORIGINS = process.env.CORS_ALLOW_ALL === 'true';
|
|
37
|
+
function getSessionId(req) {
|
|
38
|
+
const header = req.headers['mcp-session-id'];
|
|
39
|
+
return Array.isArray(header) ? header[0] : header;
|
|
40
|
+
}
|
|
31
41
|
const asyncHandler = (fn) => {
|
|
32
42
|
return (req, res, next) => {
|
|
33
43
|
Promise.resolve(fn(req, res, next)).catch(next);
|
|
34
44
|
};
|
|
35
45
|
};
|
|
36
46
|
if (isStdioMode) {
|
|
37
|
-
// Run in stdio mode for direct integration
|
|
38
47
|
const { startStdioServer } = await import('./server.js');
|
|
39
48
|
await startStdioServer();
|
|
40
49
|
}
|
|
41
50
|
else {
|
|
42
|
-
// Run HTTP server mode
|
|
43
51
|
const app = express();
|
|
44
|
-
// Middleware
|
|
45
|
-
// Limit request body size to prevent DoS
|
|
46
52
|
app.use(express.json({ limit: '1mb' }));
|
|
47
|
-
// Handle JSON parsing errors
|
|
48
53
|
app.use((err, _req, res, next) => {
|
|
49
54
|
if (err instanceof SyntaxError && 'body' in err) {
|
|
50
55
|
res.status(400).json({
|
|
@@ -59,30 +64,30 @@ else {
|
|
|
59
64
|
}
|
|
60
65
|
next(err);
|
|
61
66
|
});
|
|
62
|
-
// Rate limiting for HTTP mode
|
|
63
67
|
app.use(rateLimiter.middleware());
|
|
64
|
-
// CORS headers for MCP clients
|
|
65
68
|
app.use((req, res, next) => {
|
|
66
|
-
const origin = req.headers
|
|
67
|
-
// Validate origin format if provided
|
|
69
|
+
const { origin } = req.headers;
|
|
68
70
|
if (origin) {
|
|
69
71
|
try {
|
|
70
72
|
new URL(origin);
|
|
71
73
|
}
|
|
72
74
|
catch {
|
|
73
|
-
// Invalid origin format - skip CORS headers
|
|
74
75
|
next();
|
|
75
76
|
return;
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
|
-
// Allow if no origin (same-origin/non-browser), no allowlist configured, or origin in allowlist
|
|
79
79
|
if (!origin ||
|
|
80
|
-
|
|
81
|
-
ALLOWED_ORIGINS.includes(origin)) {
|
|
80
|
+
ALLOW_ALL_ORIGINS ||
|
|
81
|
+
(ALLOWED_ORIGINS.length > 0 && ALLOWED_ORIGINS.includes(origin))) {
|
|
82
82
|
res.header('Access-Control-Allow-Origin', origin ?? '*');
|
|
83
83
|
res.header('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
|
|
84
84
|
res.header('Access-Control-Allow-Headers', 'Content-Type, mcp-session-id');
|
|
85
|
-
res.header('Access-Control-Max-Age', '86400');
|
|
85
|
+
res.header('Access-Control-Max-Age', '86400');
|
|
86
|
+
}
|
|
87
|
+
else if (ALLOWED_ORIGINS.length > 0) {
|
|
88
|
+
// Origin not in allowlist
|
|
89
|
+
next();
|
|
90
|
+
return;
|
|
86
91
|
}
|
|
87
92
|
if (req.method === 'OPTIONS') {
|
|
88
93
|
res.sendStatus(200);
|
|
@@ -90,7 +95,6 @@ else {
|
|
|
90
95
|
}
|
|
91
96
|
next();
|
|
92
97
|
});
|
|
93
|
-
// Health check endpoint
|
|
94
98
|
app.get('/health', (_req, res) => {
|
|
95
99
|
res.json({
|
|
96
100
|
status: 'healthy',
|
|
@@ -100,25 +104,35 @@ else {
|
|
|
100
104
|
});
|
|
101
105
|
});
|
|
102
106
|
const sessions = new Map();
|
|
103
|
-
// Session TTL: 30 minutes (sessions without activity will be cleaned up)
|
|
104
107
|
const SESSION_TTL_MS = 30 * 60 * 1000;
|
|
105
|
-
|
|
106
|
-
|
|
108
|
+
let cleanupTimeout = null;
|
|
109
|
+
async function cleanupStaleSessions() {
|
|
107
110
|
const now = Date.now();
|
|
111
|
+
const closePromises = [];
|
|
108
112
|
for (const [sessionId, entry] of sessions) {
|
|
109
113
|
if (now - entry.createdAt > SESSION_TTL_MS) {
|
|
110
114
|
logInfo('Cleaning up stale session', { sessionId });
|
|
111
|
-
|
|
115
|
+
closePromises.push(entry.transport.close());
|
|
112
116
|
sessions.delete(sessionId);
|
|
113
117
|
}
|
|
114
118
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
119
|
+
await Promise.allSettled(closePromises);
|
|
120
|
+
}
|
|
121
|
+
function scheduleCleanup() {
|
|
122
|
+
if (cleanupTimeout)
|
|
123
|
+
return;
|
|
124
|
+
cleanupTimeout = setTimeout(() => {
|
|
125
|
+
void cleanupStaleSessions().then(() => {
|
|
126
|
+
cleanupTimeout = null;
|
|
127
|
+
if (sessions.size > 0)
|
|
128
|
+
scheduleCleanup();
|
|
129
|
+
});
|
|
130
|
+
}, 5 * 60 * 1000);
|
|
131
|
+
cleanupTimeout.unref();
|
|
132
|
+
}
|
|
118
133
|
app.post('/mcp', asyncHandler(async (req, res) => {
|
|
119
|
-
const sessionId = req
|
|
134
|
+
const sessionId = getSessionId(req);
|
|
120
135
|
let transport;
|
|
121
|
-
// Debug logging
|
|
122
136
|
const body = req.body;
|
|
123
137
|
logInfo('[MCP POST]', {
|
|
124
138
|
method: body?.method,
|
|
@@ -129,17 +143,16 @@ else {
|
|
|
129
143
|
});
|
|
130
144
|
const existingSession = sessionId ? sessions.get(sessionId) : undefined;
|
|
131
145
|
if (existingSession) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
existingSession.createdAt = Date.now(); // Refresh TTL on activity
|
|
146
|
+
({ transport } = existingSession);
|
|
147
|
+
existingSession.createdAt = Date.now();
|
|
135
148
|
}
|
|
136
149
|
else if (!sessionId && isInitializeRequest(req.body)) {
|
|
137
|
-
// New session initialization
|
|
138
150
|
transport = new StreamableHTTPServerTransport({
|
|
139
151
|
sessionIdGenerator: () => crypto.randomUUID(),
|
|
140
152
|
onsessioninitialized: (id) => {
|
|
141
153
|
sessions.set(id, { transport, createdAt: Date.now() });
|
|
142
154
|
logInfo('Session initialized', { sessionId: id });
|
|
155
|
+
scheduleCleanup();
|
|
143
156
|
},
|
|
144
157
|
onsessionclosed: (id) => {
|
|
145
158
|
sessions.delete(id);
|
|
@@ -155,7 +168,6 @@ else {
|
|
|
155
168
|
await mcpServer.connect(transport);
|
|
156
169
|
}
|
|
157
170
|
else {
|
|
158
|
-
// Invalid request - no session and not an initialize request
|
|
159
171
|
res.status(400).json({
|
|
160
172
|
jsonrpc: '2.0',
|
|
161
173
|
error: {
|
|
@@ -168,9 +180,8 @@ else {
|
|
|
168
180
|
}
|
|
169
181
|
await transport.handleRequest(req, res, req.body);
|
|
170
182
|
}));
|
|
171
|
-
// GET endpoint for SSE stream (for server-initiated messages)
|
|
172
183
|
app.get('/mcp', asyncHandler(async (req, res) => {
|
|
173
|
-
const sessionId = req
|
|
184
|
+
const sessionId = getSessionId(req);
|
|
174
185
|
if (!sessionId) {
|
|
175
186
|
res.status(400).json({ error: 'Missing mcp-session-id header' });
|
|
176
187
|
return;
|
|
@@ -180,14 +191,11 @@ else {
|
|
|
180
191
|
res.status(404).json({ error: 'Session not found' });
|
|
181
192
|
return;
|
|
182
193
|
}
|
|
183
|
-
// Refresh TTL on activity
|
|
184
194
|
session.createdAt = Date.now();
|
|
185
|
-
// Handle SSE stream for server-initiated messages
|
|
186
195
|
await session.transport.handleRequest(req, res);
|
|
187
196
|
}));
|
|
188
|
-
// DELETE endpoint for session cleanup
|
|
189
197
|
app.delete('/mcp', asyncHandler(async (req, res) => {
|
|
190
|
-
const sessionId = req
|
|
198
|
+
const sessionId = getSessionId(req);
|
|
191
199
|
const session = sessionId ? sessions.get(sessionId) : undefined;
|
|
192
200
|
if (session) {
|
|
193
201
|
await session.transport.handleRequest(req, res);
|
|
@@ -196,9 +204,7 @@ else {
|
|
|
196
204
|
res.status(204).end();
|
|
197
205
|
}
|
|
198
206
|
}));
|
|
199
|
-
// Error handling middleware (must be last)
|
|
200
207
|
app.use(errorHandler);
|
|
201
|
-
// Start server
|
|
202
208
|
const server = app
|
|
203
209
|
.listen(config.server.port, config.server.host, () => {
|
|
204
210
|
logInfo(`superFetch MCP server started`, {
|
|
@@ -214,31 +220,38 @@ else {
|
|
|
214
220
|
logError('Failed to start server', err);
|
|
215
221
|
process.exit(1);
|
|
216
222
|
});
|
|
217
|
-
|
|
218
|
-
|
|
223
|
+
const shutdownFn = async (signal) => {
|
|
224
|
+
if (isShuttingDown)
|
|
225
|
+
return;
|
|
226
|
+
isShuttingDown = true;
|
|
219
227
|
process.stdout.write(`\n${signal} received, shutting down gracefully...\n`);
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
// Destroy rate limiter to stop cleanup interval
|
|
228
|
+
if (cleanupTimeout)
|
|
229
|
+
clearTimeout(cleanupTimeout);
|
|
223
230
|
rateLimiter.destroy();
|
|
224
|
-
// Close all
|
|
231
|
+
// Close all sessions gracefully
|
|
232
|
+
const closePromises = [];
|
|
225
233
|
for (const session of sessions.values()) {
|
|
226
|
-
|
|
234
|
+
closePromises.push(session.transport.close());
|
|
227
235
|
}
|
|
236
|
+
await Promise.allSettled(closePromises);
|
|
228
237
|
sessions.clear();
|
|
229
|
-
// Destroy HTTP agents to close all connections
|
|
230
238
|
destroyAgents();
|
|
231
239
|
server.close(() => {
|
|
232
240
|
logInfo('HTTP server closed');
|
|
233
241
|
process.exit(0);
|
|
234
242
|
});
|
|
235
|
-
// Force exit after timeout
|
|
236
243
|
setTimeout(() => {
|
|
237
244
|
logError('Forced shutdown after timeout');
|
|
238
245
|
process.exit(1);
|
|
239
246
|
}, 10000).unref();
|
|
240
247
|
};
|
|
241
|
-
|
|
242
|
-
|
|
248
|
+
// Assign shutdown function for signal handlers and uncaughtException handler
|
|
249
|
+
shutdownHandlerRef.current = shutdownFn;
|
|
250
|
+
process.on('SIGINT', () => {
|
|
251
|
+
void shutdownFn('SIGINT');
|
|
252
|
+
});
|
|
253
|
+
process.on('SIGTERM', () => {
|
|
254
|
+
void shutdownFn('SIGTERM');
|
|
255
|
+
});
|
|
243
256
|
}
|
|
244
257
|
//# 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,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import type { Request, Response
|
|
2
|
-
/**
|
|
3
|
-
* Error handling middleware for Express
|
|
4
|
-
* Note: Express error handlers require 4 parameters
|
|
5
|
-
*/
|
|
1
|
+
import type { NextFunction, Request, Response } from 'express';
|
|
6
2
|
export declare function errorHandler(err: Error, _req: Request, res: Response, _next: NextFunction): void;
|
|
7
3
|
//# sourceMappingURL=error-handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../src/middleware/error-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../src/middleware/error-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAQ/D,wBAAgB,YAAY,CAC1B,GAAG,EAAE,KAAK,EACV,IAAI,EAAE,OAAO,EACb,GAAG,EAAE,QAAQ,EAEb,KAAK,EAAE,YAAY,GAClB,IAAI,CA8BN"}
|
|
@@ -1,22 +1,16 @@
|
|
|
1
|
-
import { logError } from '../services/logger.js';
|
|
2
1
|
import { AppError, RateLimitError, ValidationError } from '../errors/index.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export function errorHandler(err, _req, res, _next) {
|
|
8
|
-
// Determine error properties
|
|
2
|
+
import { logError } from '../services/logger.js';
|
|
3
|
+
export function errorHandler(err, _req, res,
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- Required by Express error handler signature
|
|
5
|
+
_next) {
|
|
9
6
|
const isAppError = err instanceof AppError;
|
|
10
7
|
const statusCode = isAppError ? err.statusCode : 500;
|
|
11
8
|
const code = isAppError ? err.code : 'INTERNAL_ERROR';
|
|
12
9
|
const message = isAppError && err.isOperational ? err.message : 'Internal Server Error';
|
|
13
|
-
// Log error (full details for non-operational errors)
|
|
14
10
|
logError(`HTTP ${statusCode}: ${err.message}`, err);
|
|
15
|
-
// Add retry-after header for rate limit errors
|
|
16
11
|
if (err instanceof RateLimitError) {
|
|
17
12
|
res.set('Retry-After', String(err.retryAfter));
|
|
18
13
|
}
|
|
19
|
-
// Build error response
|
|
20
14
|
const response = {
|
|
21
15
|
error: {
|
|
22
16
|
message,
|
|
@@ -24,11 +18,9 @@ export function errorHandler(err, _req, res, _next) {
|
|
|
24
18
|
statusCode,
|
|
25
19
|
},
|
|
26
20
|
};
|
|
27
|
-
// Add validation details if present
|
|
28
21
|
if (err instanceof ValidationError && err.details) {
|
|
29
22
|
response.error.details = err.details;
|
|
30
23
|
}
|
|
31
|
-
// Add stack trace in development
|
|
32
24
|
if (process.env.NODE_ENV === 'development') {
|
|
33
25
|
response.error.stack = err.stack;
|
|
34
26
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../src/middleware/error-handler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../src/middleware/error-handler.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE/E,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,MAAM,UAAU,YAAY,CAC1B,GAAU,EACV,IAAa,EACb,GAAa;AACb,4GAA4G;AAC5G,KAAmB;IAEnB,MAAM,UAAU,GAAG,GAAG,YAAY,QAAQ,CAAC;IAC3C,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC;IACrD,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC;IACtD,MAAM,OAAO,GACX,UAAU,IAAI,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,uBAAuB,CAAC;IAE1E,QAAQ,CAAC,QAAQ,UAAU,KAAK,GAAG,CAAC,OAAO,EAAE,EAAE,GAAG,CAAC,CAAC;IAEpD,IAAI,GAAG,YAAY,cAAc,EAAE,CAAC;QAClC,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,MAAM,QAAQ,GAAkB;QAC9B,KAAK,EAAE;YACL,OAAO;YACP,IAAI;YACJ,UAAU;SACX;KACF,CAAC;IAEF,IAAI,GAAG,YAAY,eAAe,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;QAClD,QAAQ,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;IACvC,CAAC;IAED,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,EAAE,CAAC;QAC3C,QAAQ,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;IACnC,CAAC;IAED,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACxC,CAAC"}
|
|
@@ -1,32 +1,14 @@
|
|
|
1
|
-
import type { Request, Response
|
|
2
|
-
|
|
3
|
-
maxRequests: number;
|
|
4
|
-
windowMs: number;
|
|
5
|
-
cleanupIntervalMs: number;
|
|
6
|
-
}
|
|
1
|
+
import type { NextFunction, Request, Response } from 'express';
|
|
2
|
+
import type { RateLimiterOptions } from '../config/types.js';
|
|
7
3
|
declare class RateLimiter {
|
|
8
4
|
private readonly store;
|
|
9
5
|
private readonly maxRequests;
|
|
10
6
|
private readonly windowMs;
|
|
11
7
|
private cleanupInterval;
|
|
12
8
|
constructor(options?: Partial<RateLimiterOptions>);
|
|
13
|
-
/**
|
|
14
|
-
* Destroys the rate limiter and cleans up resources
|
|
15
|
-
*/
|
|
16
9
|
destroy(): void;
|
|
17
|
-
/**
|
|
18
|
-
* Rate limiting middleware
|
|
19
|
-
*/
|
|
20
10
|
middleware(): (req: Request, res: Response, next: NextFunction) => void;
|
|
21
|
-
/**
|
|
22
|
-
* Get key for request (IP address)
|
|
23
|
-
* Handles proxy configurations and provides fallback
|
|
24
|
-
* Sanitizes IP to prevent cache key injection
|
|
25
|
-
*/
|
|
26
11
|
private getKey;
|
|
27
|
-
/**
|
|
28
|
-
* Clean up expired entries
|
|
29
|
-
*/
|
|
30
12
|
private cleanup;
|
|
31
13
|
}
|
|
32
14
|
export declare const rateLimiter: RateLimiter;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE/D,OAAO,KAAK,EAAkB,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAa7E,cAAM,WAAW;IACf,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqC;IAC3D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,eAAe,CAA+C;gBAE1D,OAAO,GAAE,OAAO,CAAC,kBAAkB,CAAM;IAmBrD,OAAO,IAAI,IAAI;IAQf,UAAU,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI;IAqCvE,OAAO,CAAC,MAAM;IAiBd,OAAO,CAAC,OAAO;CAUhB;AAID,eAAO,MAAM,WAAW,aAGtB,CAAC"}
|
|
@@ -3,11 +3,10 @@ const DEFAULT_OPTIONS = {
|
|
|
3
3
|
windowMs: 60000,
|
|
4
4
|
cleanupIntervalMs: 60000,
|
|
5
5
|
};
|
|
6
|
-
// Validation bounds for options
|
|
7
6
|
const MIN_MAX_REQUESTS = 1;
|
|
8
7
|
const MAX_MAX_REQUESTS = 10000;
|
|
9
|
-
const MIN_WINDOW_MS = 1000;
|
|
10
|
-
const MAX_WINDOW_MS = 3600000;
|
|
8
|
+
const MIN_WINDOW_MS = 1000;
|
|
9
|
+
const MAX_WINDOW_MS = 3600000;
|
|
11
10
|
class RateLimiter {
|
|
12
11
|
store = new Map();
|
|
13
12
|
maxRequests;
|
|
@@ -15,18 +14,14 @@ class RateLimiter {
|
|
|
15
14
|
cleanupInterval = null;
|
|
16
15
|
constructor(options = {}) {
|
|
17
16
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
18
|
-
// Validate and clamp options within bounds
|
|
19
17
|
this.maxRequests = Math.min(Math.max(opts.maxRequests, MIN_MAX_REQUESTS), MAX_MAX_REQUESTS);
|
|
20
18
|
this.windowMs = Math.min(Math.max(opts.windowMs, MIN_WINDOW_MS), MAX_WINDOW_MS);
|
|
21
19
|
const cleanupInterval = Math.max(opts.cleanupIntervalMs, MIN_WINDOW_MS);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
this.cleanupInterval = setInterval(() => {
|
|
21
|
+
this.cleanup();
|
|
22
|
+
}, cleanupInterval);
|
|
25
23
|
this.cleanupInterval.unref();
|
|
26
24
|
}
|
|
27
|
-
/**
|
|
28
|
-
* Destroys the rate limiter and cleans up resources
|
|
29
|
-
*/
|
|
30
25
|
destroy() {
|
|
31
26
|
if (this.cleanupInterval) {
|
|
32
27
|
clearInterval(this.cleanupInterval);
|
|
@@ -34,23 +29,21 @@ class RateLimiter {
|
|
|
34
29
|
}
|
|
35
30
|
this.store.clear();
|
|
36
31
|
}
|
|
37
|
-
/**
|
|
38
|
-
* Rate limiting middleware
|
|
39
|
-
*/
|
|
40
32
|
middleware() {
|
|
41
33
|
return (req, res, next) => {
|
|
42
34
|
const key = this.getKey(req);
|
|
43
35
|
const now = Date.now();
|
|
44
|
-
// Get or create entry
|
|
45
36
|
let entry = this.store.get(key);
|
|
46
|
-
// Reset if window has passed
|
|
47
37
|
if (!entry || now > entry.resetTime) {
|
|
48
|
-
entry = {
|
|
38
|
+
entry = {
|
|
39
|
+
count: 0,
|
|
40
|
+
resetTime: now + this.windowMs,
|
|
41
|
+
lastAccessed: now,
|
|
42
|
+
};
|
|
49
43
|
this.store.set(key, entry);
|
|
50
44
|
}
|
|
51
|
-
// Increment count
|
|
52
45
|
entry.count++;
|
|
53
|
-
|
|
46
|
+
entry.lastAccessed = now;
|
|
54
47
|
if (entry.count > this.maxRequests) {
|
|
55
48
|
const retryAfter = Math.ceil((entry.resetTime - now) / 1000);
|
|
56
49
|
res.set('Retry-After', String(retryAfter));
|
|
@@ -60,51 +53,33 @@ class RateLimiter {
|
|
|
60
53
|
});
|
|
61
54
|
return;
|
|
62
55
|
}
|
|
63
|
-
// Add rate limit headers
|
|
64
56
|
res.set('X-RateLimit-Limit', String(this.maxRequests));
|
|
65
57
|
res.set('X-RateLimit-Remaining', String(this.maxRequests - entry.count));
|
|
66
58
|
res.set('X-RateLimit-Reset', String(Math.ceil(entry.resetTime / 1000)));
|
|
67
59
|
next();
|
|
68
60
|
};
|
|
69
61
|
}
|
|
70
|
-
/**
|
|
71
|
-
* Get key for request (IP address)
|
|
72
|
-
* Handles proxy configurations and provides fallback
|
|
73
|
-
* Sanitizes IP to prevent cache key injection
|
|
74
|
-
*/
|
|
75
62
|
getKey(req) {
|
|
76
|
-
|
|
77
|
-
// Priority: X-Real-IP > first X-Forwarded-For > req.ip > socket
|
|
63
|
+
const fallback = req.ip ?? req.socket.remoteAddress ?? 'unknown';
|
|
78
64
|
const realIp = req.headers['x-real-ip'];
|
|
65
|
+
const forwardedFor = req.headers['x-forwarded-for'];
|
|
66
|
+
let ip = fallback;
|
|
79
67
|
if (typeof realIp === 'string' && realIp) {
|
|
80
68
|
ip = realIp;
|
|
81
69
|
}
|
|
82
|
-
else {
|
|
83
|
-
const
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
if (firstIp) {
|
|
87
|
-
ip = firstIp;
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
ip = req.ip ?? req.socket.remoteAddress ?? 'unknown';
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
ip = req.ip ?? req.socket.remoteAddress ?? 'unknown';
|
|
95
|
-
}
|
|
70
|
+
else if (typeof forwardedFor === 'string') {
|
|
71
|
+
const firstIp = forwardedFor.split(',')[0]?.trim();
|
|
72
|
+
if (firstIp)
|
|
73
|
+
ip = firstIp;
|
|
96
74
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return ip.replace(/[^a-fA-F0-9.:]/g, '').substring(0, 45) || 'unknown';
|
|
75
|
+
const sanitized = ip.replace(/[^a-fA-F0-9.:]/g, '').substring(0, 45);
|
|
76
|
+
return sanitized.length > 0 ? sanitized : 'unknown';
|
|
100
77
|
}
|
|
101
|
-
/**
|
|
102
|
-
* Clean up expired entries
|
|
103
|
-
*/
|
|
104
78
|
cleanup() {
|
|
105
79
|
const now = Date.now();
|
|
80
|
+
const MAX_IDLE_TIME = 3600000; // 1 hour
|
|
106
81
|
for (const [key, entry] of this.store) {
|
|
107
|
-
if (entry.resetTime < now) {
|
|
82
|
+
if (entry.resetTime < now || now - entry.lastAccessed > MAX_IDLE_TIME) {
|
|
108
83
|
this.store.delete(key);
|
|
109
84
|
}
|
|
110
85
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/middleware/rate-limiter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/middleware/rate-limiter.ts"],"names":[],"mappings":"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"}
|
package/dist/prompts/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAsFvD"}
|
package/dist/prompts/index.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
/**
|
|
3
|
-
* Registers all prompts with the MCP server using the modern McpServer API
|
|
4
|
-
*/
|
|
5
2
|
export function registerPrompts(server) {
|
|
6
3
|
// Register analyze-web-content prompt
|
|
7
4
|
server.registerPrompt('analyze-web-content', {
|
|
@@ -9,10 +6,7 @@ export function registerPrompts(server) {
|
|
|
9
6
|
description: 'Analyze fetched web content with optional focus area',
|
|
10
7
|
argsSchema: {
|
|
11
8
|
url: z.string().min(1).describe('URL of the content to analyze'),
|
|
12
|
-
focus: z
|
|
13
|
-
.string()
|
|
14
|
-
.optional()
|
|
15
|
-
.describe('Specific aspect to focus on (e.g., "technical details", "pricing")'),
|
|
9
|
+
focus: z.string().optional().describe('Specific aspect to focus on'),
|
|
16
10
|
},
|
|
17
11
|
}, ({ url, focus }) => {
|
|
18
12
|
const focusText = focus ? ` with focus on ${focus}` : '';
|
|
@@ -62,9 +56,7 @@ export function registerPrompts(server) {
|
|
|
62
56
|
description: 'Extract specific structured data from a web page',
|
|
63
57
|
argsSchema: {
|
|
64
58
|
url: z.string().min(1).describe('URL of the page to extract data from'),
|
|
65
|
-
dataType: z
|
|
66
|
-
.string()
|
|
67
|
-
.describe('Type of data to extract (e.g., "contact info", "product details", "article metadata")'),
|
|
59
|
+
dataType: z.string().describe('Type of data to extract'),
|
|
68
60
|
},
|
|
69
61
|
}, ({ url, dataType }) => ({
|
|
70
62
|
messages: [
|