@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.
Files changed (165) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +154 -0
  3. package/bin/mcp-agent-social +30 -0
  4. package/dist/api-client.d.ts +31 -0
  5. package/dist/api-client.d.ts.map +1 -0
  6. package/dist/api-client.js +212 -0
  7. package/dist/api-client.js.map +1 -0
  8. package/dist/config.d.ts +19 -0
  9. package/dist/config.d.ts.map +1 -0
  10. package/dist/config.js +79 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/hooks/index.d.ts +38 -0
  13. package/dist/hooks/index.d.ts.map +1 -0
  14. package/dist/hooks/index.js +253 -0
  15. package/dist/hooks/index.js.map +1 -0
  16. package/dist/hooks/types.d.ts +35 -0
  17. package/dist/hooks/types.d.ts.map +1 -0
  18. package/dist/hooks/types.js +4 -0
  19. package/dist/hooks/types.js.map +1 -0
  20. package/dist/http-server.d.ts +38 -0
  21. package/dist/http-server.d.ts.map +1 -0
  22. package/dist/http-server.js +210 -0
  23. package/dist/http-server.js.map +1 -0
  24. package/dist/index.d.ts +2 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +186 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/logger.d.ts +44 -0
  29. package/dist/logger.d.ts.map +1 -0
  30. package/dist/logger.js +281 -0
  31. package/dist/logger.js.map +1 -0
  32. package/dist/metrics.d.ts +47 -0
  33. package/dist/metrics.d.ts.map +1 -0
  34. package/dist/metrics.js +178 -0
  35. package/dist/metrics.js.map +1 -0
  36. package/dist/middleware/error-handler.d.ts +74 -0
  37. package/dist/middleware/error-handler.d.ts.map +1 -0
  38. package/dist/middleware/error-handler.js +218 -0
  39. package/dist/middleware/error-handler.js.map +1 -0
  40. package/dist/middleware/index.d.ts +55 -0
  41. package/dist/middleware/index.d.ts.map +1 -0
  42. package/dist/middleware/index.js +91 -0
  43. package/dist/middleware/index.js.map +1 -0
  44. package/dist/middleware/timeout.d.ts +52 -0
  45. package/dist/middleware/timeout.d.ts.map +1 -0
  46. package/dist/middleware/timeout.js +189 -0
  47. package/dist/middleware/timeout.js.map +1 -0
  48. package/dist/middleware/validator.d.ts +25 -0
  49. package/dist/middleware/validator.d.ts.map +1 -0
  50. package/dist/middleware/validator.js +186 -0
  51. package/dist/middleware/validator.js.map +1 -0
  52. package/dist/prompts/analyze.d.ts +46 -0
  53. package/dist/prompts/analyze.d.ts.map +1 -0
  54. package/dist/prompts/analyze.js +351 -0
  55. package/dist/prompts/analyze.js.map +1 -0
  56. package/dist/prompts/generate.d.ts +48 -0
  57. package/dist/prompts/generate.d.ts.map +1 -0
  58. package/dist/prompts/generate.js +177 -0
  59. package/dist/prompts/generate.js.map +1 -0
  60. package/dist/prompts/index.d.ts +23 -0
  61. package/dist/prompts/index.d.ts.map +1 -0
  62. package/dist/prompts/index.js +69 -0
  63. package/dist/prompts/index.js.map +1 -0
  64. package/dist/prompts/summarize.d.ts +32 -0
  65. package/dist/prompts/summarize.d.ts.map +1 -0
  66. package/dist/prompts/summarize.js +182 -0
  67. package/dist/prompts/summarize.js.map +1 -0
  68. package/dist/prompts/types.d.ts +34 -0
  69. package/dist/prompts/types.d.ts.map +1 -0
  70. package/dist/prompts/types.js +24 -0
  71. package/dist/prompts/types.js.map +1 -0
  72. package/dist/resources/agents.d.ts +17 -0
  73. package/dist/resources/agents.d.ts.map +1 -0
  74. package/dist/resources/agents.js +139 -0
  75. package/dist/resources/agents.js.map +1 -0
  76. package/dist/resources/feed.d.ts +19 -0
  77. package/dist/resources/feed.d.ts.map +1 -0
  78. package/dist/resources/feed.js +138 -0
  79. package/dist/resources/feed.js.map +1 -0
  80. package/dist/resources/index.d.ts +19 -0
  81. package/dist/resources/index.d.ts.map +1 -0
  82. package/dist/resources/index.js +146 -0
  83. package/dist/resources/index.js.map +1 -0
  84. package/dist/resources/posts.d.ts +17 -0
  85. package/dist/resources/posts.d.ts.map +1 -0
  86. package/dist/resources/posts.js +151 -0
  87. package/dist/resources/posts.js.map +1 -0
  88. package/dist/resources/types.d.ts +91 -0
  89. package/dist/resources/types.d.ts.map +1 -0
  90. package/dist/resources/types.js +12 -0
  91. package/dist/resources/types.js.map +1 -0
  92. package/dist/roots/index.d.ts +43 -0
  93. package/dist/roots/index.d.ts.map +1 -0
  94. package/dist/roots/index.js +131 -0
  95. package/dist/roots/index.js.map +1 -0
  96. package/dist/roots/types.d.ts +31 -0
  97. package/dist/roots/types.d.ts.map +1 -0
  98. package/dist/roots/types.js +4 -0
  99. package/dist/roots/types.js.map +1 -0
  100. package/dist/session-manager.d.ts +50 -0
  101. package/dist/session-manager.d.ts.map +1 -0
  102. package/dist/session-manager.js +127 -0
  103. package/dist/session-manager.js.map +1 -0
  104. package/dist/tools/create-post.d.ts +45 -0
  105. package/dist/tools/create-post.d.ts.map +1 -0
  106. package/dist/tools/create-post.js +119 -0
  107. package/dist/tools/create-post.js.map +1 -0
  108. package/dist/tools/index.d.ts +13 -0
  109. package/dist/tools/index.d.ts.map +1 -0
  110. package/dist/tools/index.js +44 -0
  111. package/dist/tools/index.js.map +1 -0
  112. package/dist/tools/login.d.ts +35 -0
  113. package/dist/tools/login.d.ts.map +1 -0
  114. package/dist/tools/login.js +132 -0
  115. package/dist/tools/login.js.map +1 -0
  116. package/dist/tools/read-posts.d.ts +48 -0
  117. package/dist/tools/read-posts.d.ts.map +1 -0
  118. package/dist/tools/read-posts.js +93 -0
  119. package/dist/tools/read-posts.js.map +1 -0
  120. package/dist/types.d.ts +88 -0
  121. package/dist/types.d.ts.map +1 -0
  122. package/dist/types.js +4 -0
  123. package/dist/types.js.map +1 -0
  124. package/dist/utils/json.d.ts +13 -0
  125. package/dist/utils/json.d.ts.map +1 -0
  126. package/dist/utils/json.js +48 -0
  127. package/dist/utils/json.js.map +1 -0
  128. package/dist/validation.d.ts +58 -0
  129. package/dist/validation.d.ts.map +1 -0
  130. package/dist/validation.js +223 -0
  131. package/dist/validation.js.map +1 -0
  132. package/package.json +70 -0
  133. package/src/api-client.ts +292 -0
  134. package/src/config.ts +92 -0
  135. package/src/hooks/index.ts +304 -0
  136. package/src/hooks/types.ts +44 -0
  137. package/src/http-server.ts +243 -0
  138. package/src/index.ts +213 -0
  139. package/src/logger.ts +326 -0
  140. package/src/metrics.ts +235 -0
  141. package/src/middleware/error-handler.ts +252 -0
  142. package/src/middleware/index.ts +112 -0
  143. package/src/middleware/timeout.ts +216 -0
  144. package/src/middleware/validator.ts +216 -0
  145. package/src/prompts/analyze.ts +404 -0
  146. package/src/prompts/generate.ts +217 -0
  147. package/src/prompts/index.ts +121 -0
  148. package/src/prompts/summarize.ts +217 -0
  149. package/src/prompts/types.ts +44 -0
  150. package/src/resources/agents.ts +165 -0
  151. package/src/resources/feed.ts +169 -0
  152. package/src/resources/index.ts +210 -0
  153. package/src/resources/posts.ts +179 -0
  154. package/src/resources/types.ts +104 -0
  155. package/src/roots/index.ts +166 -0
  156. package/src/roots/types.ts +36 -0
  157. package/src/session-manager.ts +149 -0
  158. package/src/tools/create-post.ts +154 -0
  159. package/src/tools/index.ts +70 -0
  160. package/src/tools/login.ts +169 -0
  161. package/src/tools/read-posts.ts +120 -0
  162. package/src/types.ts +107 -0
  163. package/src/utils/json.ts +46 -0
  164. package/src/validation.ts +322 -0
  165. 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
+ }