@mseep/mcp-agent-social 1.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 +154 -0
- package/bin/mcp-agent-social +30 -0
- package/dist/api-client.d.ts +31 -0
- package/dist/api-client.d.ts.map +1 -0
- package/dist/api-client.js +212 -0
- package/dist/api-client.js.map +1 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +79 -0
- package/dist/config.js.map +1 -0
- package/dist/hooks/index.d.ts +38 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +253 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/types.d.ts +35 -0
- package/dist/hooks/types.d.ts.map +1 -0
- package/dist/hooks/types.js +4 -0
- package/dist/hooks/types.js.map +1 -0
- package/dist/http-server.d.ts +38 -0
- package/dist/http-server.d.ts.map +1 -0
- package/dist/http-server.js +210 -0
- package/dist/http-server.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +186 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +44 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +281 -0
- package/dist/logger.js.map +1 -0
- package/dist/metrics.d.ts +47 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +178 -0
- package/dist/metrics.js.map +1 -0
- package/dist/middleware/error-handler.d.ts +74 -0
- package/dist/middleware/error-handler.d.ts.map +1 -0
- package/dist/middleware/error-handler.js +218 -0
- package/dist/middleware/error-handler.js.map +1 -0
- package/dist/middleware/index.d.ts +55 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +91 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/timeout.d.ts +52 -0
- package/dist/middleware/timeout.d.ts.map +1 -0
- package/dist/middleware/timeout.js +189 -0
- package/dist/middleware/timeout.js.map +1 -0
- package/dist/middleware/validator.d.ts +25 -0
- package/dist/middleware/validator.d.ts.map +1 -0
- package/dist/middleware/validator.js +186 -0
- package/dist/middleware/validator.js.map +1 -0
- package/dist/prompts/analyze.d.ts +46 -0
- package/dist/prompts/analyze.d.ts.map +1 -0
- package/dist/prompts/analyze.js +351 -0
- package/dist/prompts/analyze.js.map +1 -0
- package/dist/prompts/generate.d.ts +48 -0
- package/dist/prompts/generate.d.ts.map +1 -0
- package/dist/prompts/generate.js +177 -0
- package/dist/prompts/generate.js.map +1 -0
- package/dist/prompts/index.d.ts +23 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +69 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/summarize.d.ts +32 -0
- package/dist/prompts/summarize.d.ts.map +1 -0
- package/dist/prompts/summarize.js +182 -0
- package/dist/prompts/summarize.js.map +1 -0
- package/dist/prompts/types.d.ts +34 -0
- package/dist/prompts/types.d.ts.map +1 -0
- package/dist/prompts/types.js +24 -0
- package/dist/prompts/types.js.map +1 -0
- package/dist/resources/agents.d.ts +17 -0
- package/dist/resources/agents.d.ts.map +1 -0
- package/dist/resources/agents.js +139 -0
- package/dist/resources/agents.js.map +1 -0
- package/dist/resources/feed.d.ts +19 -0
- package/dist/resources/feed.d.ts.map +1 -0
- package/dist/resources/feed.js +138 -0
- package/dist/resources/feed.js.map +1 -0
- package/dist/resources/index.d.ts +19 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +146 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/posts.d.ts +17 -0
- package/dist/resources/posts.d.ts.map +1 -0
- package/dist/resources/posts.js +151 -0
- package/dist/resources/posts.js.map +1 -0
- package/dist/resources/types.d.ts +91 -0
- package/dist/resources/types.d.ts.map +1 -0
- package/dist/resources/types.js +12 -0
- package/dist/resources/types.js.map +1 -0
- package/dist/roots/index.d.ts +43 -0
- package/dist/roots/index.d.ts.map +1 -0
- package/dist/roots/index.js +131 -0
- package/dist/roots/index.js.map +1 -0
- package/dist/roots/types.d.ts +31 -0
- package/dist/roots/types.d.ts.map +1 -0
- package/dist/roots/types.js +4 -0
- package/dist/roots/types.js.map +1 -0
- package/dist/session-manager.d.ts +50 -0
- package/dist/session-manager.d.ts.map +1 -0
- package/dist/session-manager.js +127 -0
- package/dist/session-manager.js.map +1 -0
- package/dist/tools/create-post.d.ts +45 -0
- package/dist/tools/create-post.d.ts.map +1 -0
- package/dist/tools/create-post.js +119 -0
- package/dist/tools/create-post.js.map +1 -0
- package/dist/tools/index.d.ts +13 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +44 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/login.d.ts +35 -0
- package/dist/tools/login.d.ts.map +1 -0
- package/dist/tools/login.js +132 -0
- package/dist/tools/login.js.map +1 -0
- package/dist/tools/read-posts.d.ts +48 -0
- package/dist/tools/read-posts.d.ts.map +1 -0
- package/dist/tools/read-posts.js +93 -0
- package/dist/tools/read-posts.js.map +1 -0
- package/dist/types.d.ts +88 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/json.d.ts +13 -0
- package/dist/utils/json.d.ts.map +1 -0
- package/dist/utils/json.js +48 -0
- package/dist/utils/json.js.map +1 -0
- package/dist/validation.d.ts +58 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +223 -0
- package/dist/validation.js.map +1 -0
- package/package.json +70 -0
- package/src/api-client.ts +292 -0
- package/src/config.ts +92 -0
- package/src/hooks/index.ts +304 -0
- package/src/hooks/types.ts +44 -0
- package/src/http-server.ts +243 -0
- package/src/index.ts +213 -0
- package/src/logger.ts +326 -0
- package/src/metrics.ts +235 -0
- package/src/middleware/error-handler.ts +252 -0
- package/src/middleware/index.ts +112 -0
- package/src/middleware/timeout.ts +216 -0
- package/src/middleware/validator.ts +216 -0
- package/src/prompts/analyze.ts +404 -0
- package/src/prompts/generate.ts +217 -0
- package/src/prompts/index.ts +121 -0
- package/src/prompts/summarize.ts +217 -0
- package/src/prompts/types.ts +44 -0
- package/src/resources/agents.ts +165 -0
- package/src/resources/feed.ts +169 -0
- package/src/resources/index.ts +210 -0
- package/src/resources/posts.ts +179 -0
- package/src/resources/types.ts +104 -0
- package/src/roots/index.ts +166 -0
- package/src/roots/types.ts +36 -0
- package/src/session-manager.ts +149 -0
- package/src/tools/create-post.ts +154 -0
- package/src/tools/index.ts +70 -0
- package/src/tools/login.ts +169 -0
- package/src/tools/read-posts.ts +120 -0
- package/src/types.ts +107 -0
- package/src/utils/json.ts +46 -0
- package/src/validation.ts +322 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
// ABOUTME: Timeout management for MCP requests
|
|
2
|
+
// ABOUTME: Prevents hanging requests and provides configurable timeout handling
|
|
3
|
+
|
|
4
|
+
import { logger } from '../logger.js';
|
|
5
|
+
import { McpTimeoutError } from './error-handler.js';
|
|
6
|
+
|
|
7
|
+
// Simple async lock implementation for thread safety
|
|
8
|
+
class AsyncLock {
|
|
9
|
+
private queue: Array<() => void> = [];
|
|
10
|
+
private locked = false;
|
|
11
|
+
|
|
12
|
+
async acquire(): Promise<() => void> {
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
const unlock = () => {
|
|
15
|
+
this.locked = false;
|
|
16
|
+
const next = this.queue.shift();
|
|
17
|
+
if (next) {
|
|
18
|
+
this.locked = true;
|
|
19
|
+
next();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
if (this.locked) {
|
|
24
|
+
this.queue.push(() => resolve(unlock));
|
|
25
|
+
} else {
|
|
26
|
+
this.locked = true;
|
|
27
|
+
resolve(unlock);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface TimeoutConfig {
|
|
34
|
+
defaultTimeout: number;
|
|
35
|
+
methodTimeouts: Record<string, number>;
|
|
36
|
+
maxTimeout: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class TimeoutManager {
|
|
40
|
+
private readonly config: TimeoutConfig;
|
|
41
|
+
private timeoutCount = 0;
|
|
42
|
+
private activeTimeouts = new Set<NodeJS.Timeout>();
|
|
43
|
+
private readonly timeoutLock = new AsyncLock();
|
|
44
|
+
|
|
45
|
+
constructor(config?: Partial<TimeoutConfig>) {
|
|
46
|
+
this.config = {
|
|
47
|
+
defaultTimeout: 30000, // 30 seconds
|
|
48
|
+
maxTimeout: 120000, // 2 minutes
|
|
49
|
+
methodTimeouts: {
|
|
50
|
+
'tools/call': 60000, // Tool calls can take longer
|
|
51
|
+
'sampling/create': 90000, // LLM requests need more time
|
|
52
|
+
'resources/read': 10000, // Resource reads should be fast
|
|
53
|
+
'resources/list': 5000,
|
|
54
|
+
'tools/list': 5000,
|
|
55
|
+
'prompts/list': 5000,
|
|
56
|
+
'prompts/get': 10000,
|
|
57
|
+
'roots/list': 5000,
|
|
58
|
+
...config?.methodTimeouts,
|
|
59
|
+
},
|
|
60
|
+
...config,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
logger.info('Timeout manager initialized', {
|
|
64
|
+
defaultTimeout: this.config.defaultTimeout,
|
|
65
|
+
maxTimeout: this.config.maxTimeout,
|
|
66
|
+
methodTimeouts: Object.keys(this.config.methodTimeouts).length,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Create a timeout promise for a specific method
|
|
72
|
+
*/
|
|
73
|
+
createTimeout(method: string): Promise<never> {
|
|
74
|
+
const timeoutMs = this.getTimeoutForMethod(method);
|
|
75
|
+
|
|
76
|
+
return new Promise((_, reject) => {
|
|
77
|
+
const timeoutId = setTimeout(async () => {
|
|
78
|
+
// Use lock for thread-safe updates
|
|
79
|
+
const unlock = await this.timeoutLock.acquire();
|
|
80
|
+
try {
|
|
81
|
+
this.timeoutCount++;
|
|
82
|
+
this.activeTimeouts.delete(timeoutId);
|
|
83
|
+
} finally {
|
|
84
|
+
unlock();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
logger.warn('Request timed out', {
|
|
88
|
+
method,
|
|
89
|
+
timeout: timeoutMs,
|
|
90
|
+
totalTimeouts: this.timeoutCount,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const error = new McpTimeoutError(`Request timed out after ${timeoutMs}ms`, timeoutMs);
|
|
94
|
+
reject(error);
|
|
95
|
+
}, timeoutMs);
|
|
96
|
+
|
|
97
|
+
// Thread-safe addition to active timeouts
|
|
98
|
+
this.activeTimeouts.add(timeoutId);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Create a timeout promise that can be cleared
|
|
104
|
+
*/
|
|
105
|
+
createClearableTimeout(method: string): {
|
|
106
|
+
promise: Promise<never>;
|
|
107
|
+
clear: () => void;
|
|
108
|
+
} {
|
|
109
|
+
const timeoutMs = this.getTimeoutForMethod(method);
|
|
110
|
+
let timeoutId: NodeJS.Timeout;
|
|
111
|
+
|
|
112
|
+
const promise = new Promise<never>((_, reject) => {
|
|
113
|
+
timeoutId = setTimeout(async () => {
|
|
114
|
+
// Use lock for thread-safe updates
|
|
115
|
+
const unlock = await this.timeoutLock.acquire();
|
|
116
|
+
try {
|
|
117
|
+
this.timeoutCount++;
|
|
118
|
+
this.activeTimeouts.delete(timeoutId);
|
|
119
|
+
} finally {
|
|
120
|
+
unlock();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
logger.warn('Request timed out', {
|
|
124
|
+
method,
|
|
125
|
+
timeout: timeoutMs,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const error = new McpTimeoutError(`Request timed out after ${timeoutMs}ms`, timeoutMs);
|
|
129
|
+
reject(error);
|
|
130
|
+
}, timeoutMs);
|
|
131
|
+
|
|
132
|
+
this.activeTimeouts.add(timeoutId);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const clear = async () => {
|
|
136
|
+
if (timeoutId) {
|
|
137
|
+
clearTimeout(timeoutId);
|
|
138
|
+
// Use lock for thread-safe removal
|
|
139
|
+
const unlock = await this.timeoutLock.acquire();
|
|
140
|
+
try {
|
|
141
|
+
this.activeTimeouts.delete(timeoutId);
|
|
142
|
+
} finally {
|
|
143
|
+
unlock();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
return { promise, clear };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Wrap a promise with timeout
|
|
153
|
+
*/
|
|
154
|
+
async withTimeout<T>(promise: Promise<T>, method: string): Promise<T> {
|
|
155
|
+
const { promise: timeoutPromise, clear } = this.createClearableTimeout(method);
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
const result = await Promise.race([promise, timeoutPromise]);
|
|
159
|
+
clear();
|
|
160
|
+
return result;
|
|
161
|
+
} catch (error) {
|
|
162
|
+
clear();
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get timeout duration for a specific method
|
|
169
|
+
*/
|
|
170
|
+
private getTimeoutForMethod(method: string): number {
|
|
171
|
+
const methodTimeout = this.config.methodTimeouts[method];
|
|
172
|
+
if (methodTimeout) {
|
|
173
|
+
return Math.min(methodTimeout, this.config.maxTimeout);
|
|
174
|
+
}
|
|
175
|
+
return this.config.defaultTimeout;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Clear all active timeouts
|
|
180
|
+
*/
|
|
181
|
+
async clearAllTimeouts(): Promise<void> {
|
|
182
|
+
const unlock = await this.timeoutLock.acquire();
|
|
183
|
+
try {
|
|
184
|
+
for (const timeoutId of this.activeTimeouts) {
|
|
185
|
+
clearTimeout(timeoutId);
|
|
186
|
+
}
|
|
187
|
+
this.activeTimeouts.clear();
|
|
188
|
+
} finally {
|
|
189
|
+
unlock();
|
|
190
|
+
}
|
|
191
|
+
logger.info('Cleared all active timeouts');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Get timeout statistics
|
|
196
|
+
*/
|
|
197
|
+
getStats() {
|
|
198
|
+
return {
|
|
199
|
+
totalTimeouts: this.timeoutCount,
|
|
200
|
+
activeTimeouts: this.activeTimeouts.size,
|
|
201
|
+
config: {
|
|
202
|
+
defaultTimeout: this.config.defaultTimeout,
|
|
203
|
+
maxTimeout: this.config.maxTimeout,
|
|
204
|
+
methodTimeoutCount: Object.keys(this.config.methodTimeouts).length,
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Update timeout configuration
|
|
211
|
+
*/
|
|
212
|
+
updateConfig(newConfig: Partial<TimeoutConfig>): void {
|
|
213
|
+
Object.assign(this.config, newConfig);
|
|
214
|
+
logger.info('Timeout configuration updated', { config: this.config });
|
|
215
|
+
}
|
|
216
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
// ABOUTME: Protocol-level request and response validation
|
|
2
|
+
// ABOUTME: Ensures MCP protocol compliance and data integrity
|
|
3
|
+
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { logger } from '../logger.js';
|
|
6
|
+
|
|
7
|
+
// Base MCP request schema
|
|
8
|
+
const McpRequestSchema = z.object({
|
|
9
|
+
jsonrpc: z.literal('2.0'),
|
|
10
|
+
id: z.union([z.string(), z.number(), z.null()]),
|
|
11
|
+
method: z.string(),
|
|
12
|
+
params: z.record(z.any()).optional(),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// Base MCP response schema
|
|
16
|
+
const McpResponseSchema = z.object({
|
|
17
|
+
jsonrpc: z.literal('2.0'),
|
|
18
|
+
id: z.union([z.string(), z.number(), z.null()]),
|
|
19
|
+
result: z.any().optional(),
|
|
20
|
+
error: z
|
|
21
|
+
.object({
|
|
22
|
+
code: z.number(),
|
|
23
|
+
message: z.string(),
|
|
24
|
+
data: z.any().optional(),
|
|
25
|
+
})
|
|
26
|
+
.optional(),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Method-specific validation schemas (only validate params, base structure already validated)
|
|
30
|
+
const MethodSchemas = {
|
|
31
|
+
'tools/list': {
|
|
32
|
+
request: z.object({
|
|
33
|
+
params: z.object({}).optional(),
|
|
34
|
+
}),
|
|
35
|
+
response: z.object({
|
|
36
|
+
tools: z.array(
|
|
37
|
+
z.object({
|
|
38
|
+
name: z.string(),
|
|
39
|
+
description: z.string(),
|
|
40
|
+
inputSchema: z.record(z.any()),
|
|
41
|
+
}),
|
|
42
|
+
),
|
|
43
|
+
}),
|
|
44
|
+
},
|
|
45
|
+
'tools/call': {
|
|
46
|
+
request: z.object({
|
|
47
|
+
params: z.object({
|
|
48
|
+
name: z.string(),
|
|
49
|
+
arguments: z.record(z.any()).optional(),
|
|
50
|
+
}),
|
|
51
|
+
}),
|
|
52
|
+
response: z.object({
|
|
53
|
+
content: z.array(
|
|
54
|
+
z.object({
|
|
55
|
+
type: z.string(),
|
|
56
|
+
text: z.string().optional(),
|
|
57
|
+
data: z.any().optional(),
|
|
58
|
+
}),
|
|
59
|
+
),
|
|
60
|
+
}),
|
|
61
|
+
},
|
|
62
|
+
'resources/list': {
|
|
63
|
+
request: z.object({
|
|
64
|
+
params: z
|
|
65
|
+
.object({
|
|
66
|
+
cursor: z.string().optional(),
|
|
67
|
+
})
|
|
68
|
+
.optional(),
|
|
69
|
+
}),
|
|
70
|
+
response: z.object({
|
|
71
|
+
resources: z.array(
|
|
72
|
+
z.object({
|
|
73
|
+
uri: z.string(),
|
|
74
|
+
name: z.string(),
|
|
75
|
+
description: z.string().optional(),
|
|
76
|
+
mimeType: z.string().optional(),
|
|
77
|
+
}),
|
|
78
|
+
),
|
|
79
|
+
}),
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export class RequestValidator {
|
|
84
|
+
private validationCount = 0;
|
|
85
|
+
private validationErrors = 0;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Validate an MCP request
|
|
89
|
+
*/
|
|
90
|
+
async validateRequest(request: any): Promise<void> {
|
|
91
|
+
this.validationCount++;
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
// Validate base MCP structure
|
|
95
|
+
McpRequestSchema.parse(request);
|
|
96
|
+
|
|
97
|
+
// Then validate method-specific params only (avoiding redundant validation)
|
|
98
|
+
const methodSchema = MethodSchemas[request.method as keyof typeof MethodSchemas];
|
|
99
|
+
if (methodSchema?.request) {
|
|
100
|
+
// Extract just the params and validate them separately
|
|
101
|
+
const paramsToValidate = { params: request.params };
|
|
102
|
+
methodSchema.request.parse(paramsToValidate);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Additional custom validations
|
|
106
|
+
await this.performCustomValidations(request);
|
|
107
|
+
|
|
108
|
+
logger.debug('Request validation passed', {
|
|
109
|
+
method: request.method,
|
|
110
|
+
id: request.id,
|
|
111
|
+
});
|
|
112
|
+
} catch (error) {
|
|
113
|
+
this.validationErrors++;
|
|
114
|
+
|
|
115
|
+
if (error instanceof z.ZodError) {
|
|
116
|
+
const validationError = new Error(
|
|
117
|
+
`Request validation failed: ${error.errors.map((e) => e.message).join(', ')}`,
|
|
118
|
+
);
|
|
119
|
+
(validationError as any).code = -32602; // Invalid params
|
|
120
|
+
(validationError as any).data = error.errors;
|
|
121
|
+
throw validationError;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Validate an MCP response
|
|
130
|
+
*/
|
|
131
|
+
async validateResponse(response: any, method: string): Promise<void> {
|
|
132
|
+
try {
|
|
133
|
+
// Validate base response structure
|
|
134
|
+
McpResponseSchema.parse(response);
|
|
135
|
+
|
|
136
|
+
// Validate method-specific response if available
|
|
137
|
+
const methodSchema = MethodSchemas[method as keyof typeof MethodSchemas];
|
|
138
|
+
if (methodSchema?.response && response.result) {
|
|
139
|
+
methodSchema.response.parse(response.result);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
logger.debug('Response validation passed', { method });
|
|
143
|
+
} catch (error) {
|
|
144
|
+
if (error instanceof z.ZodError) {
|
|
145
|
+
const validationError = new Error(
|
|
146
|
+
`Response validation failed: ${error.errors.map((e) => e.message).join(', ')}`,
|
|
147
|
+
);
|
|
148
|
+
(validationError as any).code = -32603; // Internal error
|
|
149
|
+
(validationError as any).data = error.errors;
|
|
150
|
+
throw validationError;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Perform additional custom validations
|
|
159
|
+
*/
|
|
160
|
+
private async performCustomValidations(request: any): Promise<void> {
|
|
161
|
+
// Validate content length limits
|
|
162
|
+
if (request.params) {
|
|
163
|
+
const stringContent = JSON.stringify(request.params);
|
|
164
|
+
if (stringContent.length > 100000) {
|
|
165
|
+
// 100KB limit
|
|
166
|
+
throw new Error('Request payload exceeds maximum size limit');
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Validate method exists
|
|
171
|
+
const allowedMethods = [
|
|
172
|
+
'tools/list',
|
|
173
|
+
'tools/call',
|
|
174
|
+
'resources/list',
|
|
175
|
+
'resources/read',
|
|
176
|
+
'prompts/list',
|
|
177
|
+
'prompts/get',
|
|
178
|
+
'sampling/create',
|
|
179
|
+
'roots/list',
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
if (!allowedMethods.includes(request.method)) {
|
|
183
|
+
const error = new Error(`Method not found: ${request.method}`);
|
|
184
|
+
(error as any).code = -32601;
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Validate specific parameter constraints
|
|
189
|
+
if (request.method === 'sampling/create' && request.params?.messages) {
|
|
190
|
+
const messageCount = request.params.messages.length;
|
|
191
|
+
if (messageCount > 50) {
|
|
192
|
+
throw new Error('Too many messages in sampling request (max 50)');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
for (const message of request.params.messages) {
|
|
196
|
+
if (message.content.length > 10000) {
|
|
197
|
+
throw new Error('Message content exceeds maximum length (10000 characters)');
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Get validation statistics
|
|
205
|
+
*/
|
|
206
|
+
getStats() {
|
|
207
|
+
return {
|
|
208
|
+
totalValidations: this.validationCount,
|
|
209
|
+
validationErrors: this.validationErrors,
|
|
210
|
+
successRate:
|
|
211
|
+
this.validationCount > 0
|
|
212
|
+
? (this.validationCount - this.validationErrors) / this.validationCount
|
|
213
|
+
: 1,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|