cognitive-modules-cli 2.2.1 → 2.2.7
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/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +35 -29
- package/dist/cli.js +519 -23
- package/dist/commands/add.d.ts +33 -14
- package/dist/commands/add.js +383 -16
- package/dist/commands/compose.js +60 -23
- package/dist/commands/index.d.ts +4 -0
- package/dist/commands/index.js +4 -0
- package/dist/commands/init.js +23 -1
- package/dist/commands/migrate.d.ts +30 -0
- package/dist/commands/migrate.js +650 -0
- package/dist/commands/pipe.d.ts +1 -0
- package/dist/commands/pipe.js +31 -11
- package/dist/commands/remove.js +33 -2
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.js +61 -28
- package/dist/commands/search.d.ts +28 -0
- package/dist/commands/search.js +143 -0
- package/dist/commands/test.d.ts +65 -0
- package/dist/commands/test.js +454 -0
- package/dist/commands/update.d.ts +1 -0
- package/dist/commands/update.js +106 -14
- package/dist/commands/validate.d.ts +36 -0
- package/dist/commands/validate.js +97 -0
- package/dist/errors/index.d.ts +225 -0
- package/dist/errors/index.js +420 -0
- package/dist/mcp/server.js +84 -79
- package/dist/modules/composition.js +97 -32
- package/dist/modules/loader.js +4 -2
- package/dist/modules/runner.d.ts +72 -5
- package/dist/modules/runner.js +306 -59
- package/dist/modules/subagent.d.ts +6 -1
- package/dist/modules/subagent.js +18 -13
- package/dist/modules/validator.js +14 -6
- package/dist/providers/anthropic.d.ts +15 -0
- package/dist/providers/anthropic.js +147 -5
- package/dist/providers/base.d.ts +11 -0
- package/dist/providers/base.js +18 -0
- package/dist/providers/gemini.d.ts +15 -0
- package/dist/providers/gemini.js +122 -5
- package/dist/providers/ollama.d.ts +15 -0
- package/dist/providers/ollama.js +111 -3
- package/dist/providers/openai.d.ts +11 -0
- package/dist/providers/openai.js +133 -0
- package/dist/registry/client.d.ts +212 -0
- package/dist/registry/client.js +359 -0
- package/dist/registry/index.d.ts +4 -0
- package/dist/registry/index.js +4 -0
- package/dist/registry/tar.d.ts +8 -0
- package/dist/registry/tar.js +353 -0
- package/dist/server/http.js +301 -45
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +1 -0
- package/dist/server/sse.d.ts +13 -0
- package/dist/server/sse.js +22 -0
- package/dist/types.d.ts +32 -1
- package/dist/types.js +4 -1
- package/dist/version.d.ts +1 -0
- package/dist/version.js +4 -0
- package/package.json +31 -7
- package/dist/modules/composition.test.d.ts +0 -11
- package/dist/modules/composition.test.js +0 -450
- package/dist/modules/policy.test.d.ts +0 -10
- package/dist/modules/policy.test.js +0 -369
- package/src/cli.ts +0 -471
- package/src/commands/add.ts +0 -315
- package/src/commands/compose.ts +0 -185
- package/src/commands/index.ts +0 -13
- package/src/commands/init.ts +0 -94
- package/src/commands/list.ts +0 -33
- package/src/commands/pipe.ts +0 -76
- package/src/commands/remove.ts +0 -57
- package/src/commands/run.ts +0 -80
- package/src/commands/update.ts +0 -130
- package/src/commands/versions.ts +0 -79
- package/src/index.ts +0 -90
- package/src/mcp/index.ts +0 -5
- package/src/mcp/server.ts +0 -403
- package/src/modules/composition.test.ts +0 -558
- package/src/modules/composition.ts +0 -1674
- package/src/modules/index.ts +0 -9
- package/src/modules/loader.ts +0 -508
- package/src/modules/policy.test.ts +0 -455
- package/src/modules/runner.ts +0 -1983
- package/src/modules/subagent.ts +0 -277
- package/src/modules/validator.ts +0 -700
- package/src/providers/anthropic.ts +0 -89
- package/src/providers/base.ts +0 -29
- package/src/providers/deepseek.ts +0 -83
- package/src/providers/gemini.ts +0 -117
- package/src/providers/index.ts +0 -78
- package/src/providers/minimax.ts +0 -81
- package/src/providers/moonshot.ts +0 -82
- package/src/providers/ollama.ts +0 -83
- package/src/providers/openai.ts +0 -84
- package/src/providers/qwen.ts +0 -82
- package/src/server/http.ts +0 -316
- package/src/server/index.ts +0 -6
- package/src/types.ts +0 -599
- package/tsconfig.json +0 -17
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cognitive Modules - Unified Error Handling
|
|
3
|
+
*
|
|
4
|
+
* Provides consistent error structures across HTTP, MCP, and CLI layers.
|
|
5
|
+
* Based on ERROR-CODES.md specification.
|
|
6
|
+
*/
|
|
7
|
+
// =============================================================================
|
|
8
|
+
// Error Codes
|
|
9
|
+
// =============================================================================
|
|
10
|
+
/**
|
|
11
|
+
* Standard error codes per ERROR-CODES.md specification.
|
|
12
|
+
*
|
|
13
|
+
* Format: E{category}{sequence}
|
|
14
|
+
* - Category 1: Input errors
|
|
15
|
+
* - Category 2: Processing errors
|
|
16
|
+
* - Category 3: Output errors
|
|
17
|
+
* - Category 4: Runtime errors
|
|
18
|
+
* - Category 5-9: Module-specific (reserved)
|
|
19
|
+
*/
|
|
20
|
+
export const ErrorCodes = {
|
|
21
|
+
// E1xxx: Input Errors
|
|
22
|
+
PARSE_ERROR: 'E1000',
|
|
23
|
+
INVALID_INPUT: 'E1001',
|
|
24
|
+
MISSING_REQUIRED_FIELD: 'E1002',
|
|
25
|
+
TYPE_MISMATCH: 'E1003',
|
|
26
|
+
UNSUPPORTED_VALUE: 'E1004',
|
|
27
|
+
INPUT_TOO_LARGE: 'E1005',
|
|
28
|
+
INVALID_REFERENCE: 'E1006',
|
|
29
|
+
// E2xxx: Processing Errors
|
|
30
|
+
LOW_CONFIDENCE: 'E2001',
|
|
31
|
+
TIMEOUT: 'E2002',
|
|
32
|
+
TOKEN_LIMIT: 'E2003',
|
|
33
|
+
NO_ACTION_POSSIBLE: 'E2004',
|
|
34
|
+
SEMANTIC_CONFLICT: 'E2005',
|
|
35
|
+
AMBIGUOUS_INPUT: 'E2006',
|
|
36
|
+
INSUFFICIENT_CONTEXT: 'E2007',
|
|
37
|
+
// E3xxx: Output Errors
|
|
38
|
+
OUTPUT_SCHEMA_VIOLATION: 'E3001',
|
|
39
|
+
PARTIAL_RESULT: 'E3002',
|
|
40
|
+
MISSING_RATIONALE: 'E3003',
|
|
41
|
+
OVERFLOW_LIMIT: 'E3004',
|
|
42
|
+
INVALID_ENUM: 'E3005',
|
|
43
|
+
CONSTRAINT_VIOLATION: 'E3006',
|
|
44
|
+
// E4xxx: Runtime Errors
|
|
45
|
+
INTERNAL_ERROR: 'E4000',
|
|
46
|
+
PROVIDER_UNAVAILABLE: 'E4001',
|
|
47
|
+
RATE_LIMITED: 'E4002',
|
|
48
|
+
CONTEXT_OVERFLOW: 'E4003',
|
|
49
|
+
CIRCULAR_DEPENDENCY: 'E4004',
|
|
50
|
+
MAX_DEPTH_EXCEEDED: 'E4005',
|
|
51
|
+
MODULE_NOT_FOUND: 'E4006',
|
|
52
|
+
PERMISSION_DENIED: 'E4007',
|
|
53
|
+
ENDPOINT_NOT_FOUND: 'E4008',
|
|
54
|
+
RESOURCE_NOT_FOUND: 'E4009',
|
|
55
|
+
};
|
|
56
|
+
// =============================================================================
|
|
57
|
+
// Legacy Code Mapping
|
|
58
|
+
// =============================================================================
|
|
59
|
+
const LEGACY_CODE_MAP = {
|
|
60
|
+
// Input errors
|
|
61
|
+
'PARSE_ERROR': ErrorCodes.PARSE_ERROR,
|
|
62
|
+
'INVALID_INPUT': ErrorCodes.INVALID_INPUT,
|
|
63
|
+
'MISSING_REQUIRED_FIELD': ErrorCodes.MISSING_REQUIRED_FIELD,
|
|
64
|
+
'TYPE_MISMATCH': ErrorCodes.TYPE_MISMATCH,
|
|
65
|
+
'UNSUPPORTED_VALUE': ErrorCodes.UNSUPPORTED_VALUE,
|
|
66
|
+
'UNSUPPORTED_LANGUAGE': ErrorCodes.UNSUPPORTED_VALUE,
|
|
67
|
+
'INPUT_TOO_LARGE': ErrorCodes.INPUT_TOO_LARGE,
|
|
68
|
+
'INVALID_REFERENCE': ErrorCodes.INVALID_REFERENCE,
|
|
69
|
+
// Processing errors
|
|
70
|
+
'LOW_CONFIDENCE': ErrorCodes.LOW_CONFIDENCE,
|
|
71
|
+
'TIMEOUT': ErrorCodes.TIMEOUT,
|
|
72
|
+
'TOKEN_LIMIT': ErrorCodes.TOKEN_LIMIT,
|
|
73
|
+
'NO_ACTION_POSSIBLE': ErrorCodes.NO_ACTION_POSSIBLE,
|
|
74
|
+
'NO_SIMPLIFICATION_POSSIBLE': ErrorCodes.NO_ACTION_POSSIBLE,
|
|
75
|
+
'SEMANTIC_CONFLICT': ErrorCodes.SEMANTIC_CONFLICT,
|
|
76
|
+
'BEHAVIOR_CHANGE_REQUIRED': ErrorCodes.SEMANTIC_CONFLICT,
|
|
77
|
+
'AMBIGUOUS_INPUT': ErrorCodes.AMBIGUOUS_INPUT,
|
|
78
|
+
'INSUFFICIENT_CONTEXT': ErrorCodes.INSUFFICIENT_CONTEXT,
|
|
79
|
+
// Output errors
|
|
80
|
+
'SCHEMA_VALIDATION_FAILED': ErrorCodes.OUTPUT_SCHEMA_VIOLATION,
|
|
81
|
+
'OUTPUT_SCHEMA_VIOLATION': ErrorCodes.OUTPUT_SCHEMA_VIOLATION,
|
|
82
|
+
'PARTIAL_RESULT': ErrorCodes.PARTIAL_RESULT,
|
|
83
|
+
// Runtime errors
|
|
84
|
+
'INTERNAL_ERROR': ErrorCodes.INTERNAL_ERROR,
|
|
85
|
+
'PROVIDER_UNAVAILABLE': ErrorCodes.PROVIDER_UNAVAILABLE,
|
|
86
|
+
'RATE_LIMITED': ErrorCodes.RATE_LIMITED,
|
|
87
|
+
'MODULE_NOT_FOUND': ErrorCodes.MODULE_NOT_FOUND,
|
|
88
|
+
'PERMISSION_DENIED': ErrorCodes.PERMISSION_DENIED,
|
|
89
|
+
'ENDPOINT_NOT_FOUND': ErrorCodes.ENDPOINT_NOT_FOUND,
|
|
90
|
+
'RESOURCE_NOT_FOUND': ErrorCodes.RESOURCE_NOT_FOUND,
|
|
91
|
+
'NOT_FOUND': ErrorCodes.RESOURCE_NOT_FOUND,
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Normalize error code to E-format.
|
|
95
|
+
* Accepts both legacy string codes and E-format codes.
|
|
96
|
+
*/
|
|
97
|
+
export function normalizeErrorCode(code) {
|
|
98
|
+
// Already E-format
|
|
99
|
+
if (/^E\d{4}$/.test(code)) {
|
|
100
|
+
return code;
|
|
101
|
+
}
|
|
102
|
+
// Legacy format
|
|
103
|
+
return LEGACY_CODE_MAP[code] || ErrorCodes.INTERNAL_ERROR;
|
|
104
|
+
}
|
|
105
|
+
// =============================================================================
|
|
106
|
+
// Error Envelope Factory
|
|
107
|
+
// =============================================================================
|
|
108
|
+
/**
|
|
109
|
+
* Default recoverability by error category.
|
|
110
|
+
*/
|
|
111
|
+
function getDefaultRecoverable(code) {
|
|
112
|
+
const category = code.charAt(1);
|
|
113
|
+
switch (category) {
|
|
114
|
+
case '1': return true; // Input errors are usually recoverable
|
|
115
|
+
case '2': return true; // Processing errors may be recoverable
|
|
116
|
+
case '3': return false; // Output errors are not recoverable
|
|
117
|
+
case '4': {
|
|
118
|
+
// Runtime errors: some are recoverable
|
|
119
|
+
const recoverable4xxx = [
|
|
120
|
+
ErrorCodes.PROVIDER_UNAVAILABLE,
|
|
121
|
+
ErrorCodes.RATE_LIMITED,
|
|
122
|
+
ErrorCodes.MODULE_NOT_FOUND,
|
|
123
|
+
];
|
|
124
|
+
return recoverable4xxx.includes(code);
|
|
125
|
+
}
|
|
126
|
+
default: return false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Create a standardized error envelope.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* // Simple error
|
|
134
|
+
* makeErrorEnvelope({
|
|
135
|
+
* code: ErrorCodes.MODULE_NOT_FOUND,
|
|
136
|
+
* message: "Module 'code-reviewer' not found",
|
|
137
|
+
* suggestion: "Use 'cog list' to see available modules"
|
|
138
|
+
* });
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* // Error with retry info
|
|
142
|
+
* makeErrorEnvelope({
|
|
143
|
+
* code: ErrorCodes.RATE_LIMITED,
|
|
144
|
+
* message: "Rate limit exceeded",
|
|
145
|
+
* retry_after_ms: 60000
|
|
146
|
+
* });
|
|
147
|
+
*/
|
|
148
|
+
export function makeErrorEnvelope(options) {
|
|
149
|
+
const code = normalizeErrorCode(options.code);
|
|
150
|
+
const recoverable = options.recoverable ?? getDefaultRecoverable(code);
|
|
151
|
+
const error = {
|
|
152
|
+
code,
|
|
153
|
+
message: options.message,
|
|
154
|
+
recoverable,
|
|
155
|
+
};
|
|
156
|
+
if (options.suggestion) {
|
|
157
|
+
error.suggestion = options.suggestion;
|
|
158
|
+
}
|
|
159
|
+
if (options.retry_after_ms !== undefined) {
|
|
160
|
+
error.retry_after_ms = options.retry_after_ms;
|
|
161
|
+
}
|
|
162
|
+
if (options.details) {
|
|
163
|
+
error.details = options.details;
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
ok: false,
|
|
167
|
+
version: options.version || '2.2',
|
|
168
|
+
meta: {
|
|
169
|
+
confidence: options.confidence ?? 0.0,
|
|
170
|
+
risk: options.risk ?? 'high',
|
|
171
|
+
explain: (options.explain || options.message).slice(0, 280),
|
|
172
|
+
trace_id: options.trace_id,
|
|
173
|
+
},
|
|
174
|
+
error,
|
|
175
|
+
partial_data: options.partial_data,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
// =============================================================================
|
|
179
|
+
// Layer-Specific Helpers
|
|
180
|
+
// =============================================================================
|
|
181
|
+
export function attachContext(envelope, context) {
|
|
182
|
+
if (!context)
|
|
183
|
+
return envelope;
|
|
184
|
+
const { module, provider } = context;
|
|
185
|
+
if (!module && !provider)
|
|
186
|
+
return envelope;
|
|
187
|
+
return {
|
|
188
|
+
...envelope,
|
|
189
|
+
...(module ? { module } : {}),
|
|
190
|
+
...(provider ? { provider } : {}),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Map a CEP error code to an HTTP status code.
|
|
195
|
+
*
|
|
196
|
+
* This is used to keep HTTP behavior consistent with the error model while
|
|
197
|
+
* allowing callers to attach context without rebuilding envelopes.
|
|
198
|
+
*/
|
|
199
|
+
export function httpStatusForErrorCode(code) {
|
|
200
|
+
const normalized = normalizeErrorCode(code);
|
|
201
|
+
const category = normalized.charAt(1);
|
|
202
|
+
switch (category) {
|
|
203
|
+
case '1': {
|
|
204
|
+
// Input errors -> Bad Request (with specific overrides)
|
|
205
|
+
if (normalized === ErrorCodes.INPUT_TOO_LARGE)
|
|
206
|
+
return 413;
|
|
207
|
+
return 400;
|
|
208
|
+
}
|
|
209
|
+
case '2': return 422; // Processing errors -> Unprocessable Entity
|
|
210
|
+
case '3': return 500; // Output errors -> Internal Server Error
|
|
211
|
+
case '4': {
|
|
212
|
+
// Runtime errors -> map to appropriate HTTP status
|
|
213
|
+
if (normalized === ErrorCodes.MODULE_NOT_FOUND ||
|
|
214
|
+
normalized === ErrorCodes.ENDPOINT_NOT_FOUND ||
|
|
215
|
+
normalized === ErrorCodes.RESOURCE_NOT_FOUND) {
|
|
216
|
+
return 404;
|
|
217
|
+
}
|
|
218
|
+
if (normalized === ErrorCodes.PERMISSION_DENIED)
|
|
219
|
+
return 403;
|
|
220
|
+
if (normalized === ErrorCodes.RATE_LIMITED)
|
|
221
|
+
return 429;
|
|
222
|
+
return 500;
|
|
223
|
+
}
|
|
224
|
+
default: return 500;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Create error envelope for HTTP API responses.
|
|
229
|
+
*
|
|
230
|
+
* @returns Tuple of [statusCode, body]
|
|
231
|
+
*/
|
|
232
|
+
export function makeHttpError(options) {
|
|
233
|
+
const envelope = attachContext(makeErrorEnvelope(options), options);
|
|
234
|
+
const statusCode = httpStatusForErrorCode(String(options.code));
|
|
235
|
+
return [statusCode, envelope];
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Create error envelope for MCP tool responses.
|
|
239
|
+
*/
|
|
240
|
+
export function makeMcpError(options) {
|
|
241
|
+
const envelope = attachContext(makeErrorEnvelope(options), {
|
|
242
|
+
module: options.module ?? 'unknown',
|
|
243
|
+
provider: options.provider ?? 'unknown',
|
|
244
|
+
});
|
|
245
|
+
return {
|
|
246
|
+
content: [
|
|
247
|
+
{
|
|
248
|
+
type: 'text',
|
|
249
|
+
text: JSON.stringify(envelope, null, 2),
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Convert error envelope to CLI-friendly error message.
|
|
256
|
+
*/
|
|
257
|
+
export function toCliError(envelope) {
|
|
258
|
+
const { error } = envelope;
|
|
259
|
+
let message = `Error [${error.code}]: ${error.message}`;
|
|
260
|
+
if (error.suggestion) {
|
|
261
|
+
message += `\n Suggestion: ${error.suggestion}`;
|
|
262
|
+
}
|
|
263
|
+
if (error.retry_after_ms) {
|
|
264
|
+
const seconds = Math.ceil(error.retry_after_ms / 1000);
|
|
265
|
+
message += `\n Retry after: ${seconds}s`;
|
|
266
|
+
}
|
|
267
|
+
return message;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Convert CLI CommandResult-style error to standard envelope.
|
|
271
|
+
* Used for backward compatibility during migration.
|
|
272
|
+
*/
|
|
273
|
+
export function fromCliError(errorMessage, code = ErrorCodes.INTERNAL_ERROR) {
|
|
274
|
+
return makeErrorEnvelope({
|
|
275
|
+
code,
|
|
276
|
+
message: errorMessage,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
// =============================================================================
|
|
280
|
+
// Error Type Guards
|
|
281
|
+
// =============================================================================
|
|
282
|
+
/**
|
|
283
|
+
* Check if an error envelope indicates a recoverable error.
|
|
284
|
+
*/
|
|
285
|
+
export function isRecoverable(envelope) {
|
|
286
|
+
return envelope.error.recoverable === true;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Check if an error envelope has partial data.
|
|
290
|
+
*/
|
|
291
|
+
export function hasPartialData(envelope) {
|
|
292
|
+
return envelope.partial_data !== undefined;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Check if an error envelope suggests retrying.
|
|
296
|
+
*/
|
|
297
|
+
export function shouldRetry(envelope) {
|
|
298
|
+
const retryableCodes = [
|
|
299
|
+
ErrorCodes.RATE_LIMITED,
|
|
300
|
+
ErrorCodes.PROVIDER_UNAVAILABLE,
|
|
301
|
+
ErrorCodes.TIMEOUT,
|
|
302
|
+
];
|
|
303
|
+
return (envelope.error.recoverable === true &&
|
|
304
|
+
(envelope.error.retry_after_ms !== undefined ||
|
|
305
|
+
retryableCodes.includes(envelope.error.code)));
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Create a standardized success envelope.
|
|
309
|
+
* Use this for consistent success responses across HTTP and MCP layers.
|
|
310
|
+
*/
|
|
311
|
+
export function makeSuccessEnvelope(options) {
|
|
312
|
+
const explain = (options.explain || 'Operation completed successfully').slice(0, 280);
|
|
313
|
+
// Envelope schema requires data to be an object with at least `rationale`.
|
|
314
|
+
// For non-module operations (list/info), we still emit a conforming envelope by
|
|
315
|
+
// injecting a minimal rationale if missing, or wrapping non-objects.
|
|
316
|
+
const normalizedData = (() => {
|
|
317
|
+
const d = options.data;
|
|
318
|
+
const isPlainObject = typeof d === 'object' && d !== null && !Array.isArray(d);
|
|
319
|
+
if (!isPlainObject) {
|
|
320
|
+
return { result: d, rationale: explain };
|
|
321
|
+
}
|
|
322
|
+
const obj = d;
|
|
323
|
+
if (typeof obj.rationale === 'string')
|
|
324
|
+
return options.data;
|
|
325
|
+
return { ...obj, rationale: explain };
|
|
326
|
+
})();
|
|
327
|
+
return {
|
|
328
|
+
ok: true,
|
|
329
|
+
version: options.version || '2.2',
|
|
330
|
+
meta: {
|
|
331
|
+
confidence: options.confidence ?? 1.0,
|
|
332
|
+
risk: options.risk ?? 'none',
|
|
333
|
+
explain,
|
|
334
|
+
trace_id: options.trace_id,
|
|
335
|
+
},
|
|
336
|
+
data: normalizedData,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Create success envelope for MCP tool responses.
|
|
341
|
+
*/
|
|
342
|
+
export function makeMcpSuccess(options) {
|
|
343
|
+
const envelope = makeSuccessEnvelope(options);
|
|
344
|
+
return {
|
|
345
|
+
content: [
|
|
346
|
+
{
|
|
347
|
+
type: 'text',
|
|
348
|
+
text: JSON.stringify(envelope, null, 2),
|
|
349
|
+
},
|
|
350
|
+
],
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
// =============================================================================
|
|
354
|
+
// Common Error Factories
|
|
355
|
+
// =============================================================================
|
|
356
|
+
/**
|
|
357
|
+
* Create MODULE_NOT_FOUND error.
|
|
358
|
+
*/
|
|
359
|
+
export function moduleNotFoundError(moduleName, options) {
|
|
360
|
+
return makeErrorEnvelope({
|
|
361
|
+
code: ErrorCodes.MODULE_NOT_FOUND,
|
|
362
|
+
message: `Module '${moduleName}' not found`,
|
|
363
|
+
suggestion: options?.suggestion || "Use 'cog list' to see available modules, or 'cog search' to find modules in registry",
|
|
364
|
+
trace_id: options?.trace_id,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Create PARSE_ERROR error.
|
|
369
|
+
*/
|
|
370
|
+
export function parseError(details, options) {
|
|
371
|
+
return makeErrorEnvelope({
|
|
372
|
+
code: ErrorCodes.PARSE_ERROR,
|
|
373
|
+
message: details ? `JSON parsing failed: ${details}` : 'Invalid JSON body',
|
|
374
|
+
recoverable: false,
|
|
375
|
+
trace_id: options?.trace_id,
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Create RATE_LIMITED error.
|
|
380
|
+
*/
|
|
381
|
+
export function rateLimitedError(retryAfterMs, provider) {
|
|
382
|
+
return makeErrorEnvelope({
|
|
383
|
+
code: ErrorCodes.RATE_LIMITED,
|
|
384
|
+
message: `Rate limit exceeded${provider ? ` for provider '${provider}'` : ''}`,
|
|
385
|
+
retry_after_ms: retryAfterMs,
|
|
386
|
+
suggestion: `Wait ${Math.ceil(retryAfterMs / 1000)} seconds before retrying`,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Create INTERNAL_ERROR error.
|
|
391
|
+
*/
|
|
392
|
+
export function internalError(message, options) {
|
|
393
|
+
return makeErrorEnvelope({
|
|
394
|
+
code: ErrorCodes.INTERNAL_ERROR,
|
|
395
|
+
message,
|
|
396
|
+
recoverable: false,
|
|
397
|
+
details: options?.details,
|
|
398
|
+
trace_id: options?.trace_id,
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Create MISSING_REQUIRED_FIELD error.
|
|
403
|
+
*/
|
|
404
|
+
export function missingFieldError(fieldName, options) {
|
|
405
|
+
return makeErrorEnvelope({
|
|
406
|
+
code: ErrorCodes.MISSING_REQUIRED_FIELD,
|
|
407
|
+
message: `Missing required field: ${fieldName}`,
|
|
408
|
+
suggestion: options?.suggestion || `Provide the '${fieldName}' field in your request`,
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Create PERMISSION_DENIED error.
|
|
413
|
+
*/
|
|
414
|
+
export function permissionDeniedError(reason) {
|
|
415
|
+
return makeErrorEnvelope({
|
|
416
|
+
code: ErrorCodes.PERMISSION_DENIED,
|
|
417
|
+
message: reason,
|
|
418
|
+
recoverable: false,
|
|
419
|
+
});
|
|
420
|
+
}
|
package/dist/mcp/server.js
CHANGED
|
@@ -12,12 +12,14 @@ import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSche
|
|
|
12
12
|
import { findModule, listModules, getDefaultSearchPaths } from '../modules/loader.js';
|
|
13
13
|
import { runModule } from '../modules/runner.js';
|
|
14
14
|
import { getProvider } from '../providers/index.js';
|
|
15
|
+
import { VERSION } from '../version.js';
|
|
16
|
+
import { ErrorCodes, attachContext, makeErrorEnvelope, makeMcpError, makeMcpSuccess } from '../errors/index.js';
|
|
15
17
|
// =============================================================================
|
|
16
18
|
// Server Setup
|
|
17
19
|
// =============================================================================
|
|
18
20
|
const server = new Server({
|
|
19
21
|
name: 'cognitive-modules',
|
|
20
|
-
version:
|
|
22
|
+
version: VERSION,
|
|
21
23
|
}, {
|
|
22
24
|
capabilities: {
|
|
23
25
|
tools: {},
|
|
@@ -35,7 +37,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
35
37
|
tools: [
|
|
36
38
|
{
|
|
37
39
|
name: 'cognitive_run',
|
|
38
|
-
description: 'Run a Cognitive Module to get structured AI analysis results',
|
|
40
|
+
description: 'Run a Cognitive Module to get structured AI analysis results (Cognitive Protocol v2.2)',
|
|
39
41
|
inputSchema: {
|
|
40
42
|
type: 'object',
|
|
41
43
|
properties: {
|
|
@@ -61,7 +63,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
61
63
|
},
|
|
62
64
|
{
|
|
63
65
|
name: 'cognitive_list',
|
|
64
|
-
description: 'List all installed Cognitive Modules',
|
|
66
|
+
description: 'List all installed Cognitive Modules (Cognitive Protocol v2.2)',
|
|
65
67
|
inputSchema: {
|
|
66
68
|
type: 'object',
|
|
67
69
|
properties: {},
|
|
@@ -69,7 +71,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
69
71
|
},
|
|
70
72
|
{
|
|
71
73
|
name: 'cognitive_info',
|
|
72
|
-
description: 'Get detailed information about a Cognitive Module',
|
|
74
|
+
description: 'Get detailed information about a Cognitive Module (Cognitive Protocol v2.2)',
|
|
73
75
|
inputSchema: {
|
|
74
76
|
type: 'object',
|
|
75
77
|
properties: {
|
|
@@ -84,113 +86,105 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
84
86
|
],
|
|
85
87
|
};
|
|
86
88
|
});
|
|
89
|
+
// Error handling now uses unified errors module (../errors/index.js)
|
|
87
90
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
88
91
|
const { name, arguments: args } = request.params;
|
|
92
|
+
const runContext = name === 'cognitive_run'
|
|
93
|
+
? { module: args.module, provider: args.provider ?? 'unknown' }
|
|
94
|
+
: undefined;
|
|
89
95
|
try {
|
|
90
96
|
switch (name) {
|
|
91
97
|
case 'cognitive_run': {
|
|
92
98
|
const { module: moduleName, args: inputArgs, provider: providerName, model } = args;
|
|
99
|
+
const providerLabel = providerName ?? 'unknown';
|
|
93
100
|
// Find module
|
|
94
101
|
const moduleData = await findModule(moduleName, searchPaths);
|
|
95
102
|
if (!moduleData) {
|
|
96
|
-
return {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
};
|
|
103
|
+
return makeMcpError({
|
|
104
|
+
code: ErrorCodes.MODULE_NOT_FOUND,
|
|
105
|
+
message: `Module '${moduleName}' not found`,
|
|
106
|
+
suggestion: 'Use cognitive_list to see available modules',
|
|
107
|
+
module: moduleName,
|
|
108
|
+
provider: providerLabel,
|
|
109
|
+
});
|
|
104
110
|
}
|
|
105
111
|
// Create provider
|
|
106
112
|
const provider = getProvider(providerName, model);
|
|
107
|
-
|
|
113
|
+
const resolvedProvider = provider?.name ?? providerLabel;
|
|
114
|
+
// Run module - result is already v2.2 envelope
|
|
108
115
|
const result = await runModule(moduleData, provider, {
|
|
109
|
-
|
|
116
|
+
args: inputArgs,
|
|
110
117
|
useV22: true,
|
|
111
118
|
});
|
|
119
|
+
const contextual = attachContext(result, {
|
|
120
|
+
module: moduleName,
|
|
121
|
+
provider: resolvedProvider,
|
|
122
|
+
});
|
|
112
123
|
return {
|
|
113
124
|
content: [
|
|
114
125
|
{
|
|
115
126
|
type: 'text',
|
|
116
|
-
text: JSON.stringify(
|
|
127
|
+
text: JSON.stringify(contextual, null, 2),
|
|
117
128
|
},
|
|
118
129
|
],
|
|
119
130
|
};
|
|
120
131
|
}
|
|
121
132
|
case 'cognitive_list': {
|
|
122
133
|
const modules = await listModules(searchPaths);
|
|
123
|
-
return {
|
|
124
|
-
|
|
125
|
-
{
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
},
|
|
137
|
-
],
|
|
138
|
-
};
|
|
134
|
+
return makeMcpSuccess({
|
|
135
|
+
data: {
|
|
136
|
+
modules: modules.map((m) => ({
|
|
137
|
+
name: m.name,
|
|
138
|
+
location: m.location,
|
|
139
|
+
format: m.format,
|
|
140
|
+
tier: m.tier,
|
|
141
|
+
responsibility: m.responsibility,
|
|
142
|
+
})),
|
|
143
|
+
count: modules.length,
|
|
144
|
+
},
|
|
145
|
+
explain: `Found ${modules.length} installed modules`,
|
|
146
|
+
});
|
|
139
147
|
}
|
|
140
148
|
case 'cognitive_info': {
|
|
141
149
|
const { module: moduleName } = args;
|
|
142
150
|
const moduleData = await findModule(moduleName, searchPaths);
|
|
143
151
|
if (!moduleData) {
|
|
144
|
-
return {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
],
|
|
151
|
-
};
|
|
152
|
+
return makeMcpError({
|
|
153
|
+
code: ErrorCodes.MODULE_NOT_FOUND,
|
|
154
|
+
message: `Module '${moduleName}' not found`,
|
|
155
|
+
suggestion: 'Use cognitive_list to see available modules',
|
|
156
|
+
module: moduleName,
|
|
157
|
+
});
|
|
152
158
|
}
|
|
153
|
-
return {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
outputSchema: moduleData.outputSchema,
|
|
166
|
-
}, null, 2),
|
|
167
|
-
},
|
|
168
|
-
],
|
|
169
|
-
};
|
|
159
|
+
return makeMcpSuccess({
|
|
160
|
+
data: {
|
|
161
|
+
name: moduleData.name,
|
|
162
|
+
version: moduleData.version,
|
|
163
|
+
responsibility: moduleData.responsibility,
|
|
164
|
+
tier: moduleData.tier,
|
|
165
|
+
format: moduleData.format,
|
|
166
|
+
inputSchema: moduleData.inputSchema,
|
|
167
|
+
outputSchema: moduleData.outputSchema,
|
|
168
|
+
},
|
|
169
|
+
explain: `Module '${moduleName}' info retrieved`,
|
|
170
|
+
});
|
|
170
171
|
}
|
|
171
172
|
default:
|
|
172
|
-
return {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
},
|
|
178
|
-
],
|
|
179
|
-
};
|
|
173
|
+
return makeMcpError({
|
|
174
|
+
code: ErrorCodes.INVALID_REFERENCE,
|
|
175
|
+
message: `Unknown tool: ${name}`,
|
|
176
|
+
suggestion: 'Use cognitive_run, cognitive_list, or cognitive_info',
|
|
177
|
+
});
|
|
180
178
|
}
|
|
181
179
|
}
|
|
182
180
|
catch (error) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}),
|
|
191
|
-
},
|
|
192
|
-
],
|
|
193
|
-
};
|
|
181
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
182
|
+
return makeMcpError({
|
|
183
|
+
code: ErrorCodes.INTERNAL_ERROR,
|
|
184
|
+
message: errorMessage,
|
|
185
|
+
recoverable: false,
|
|
186
|
+
...(runContext ?? {}),
|
|
187
|
+
});
|
|
194
188
|
}
|
|
195
189
|
});
|
|
196
190
|
// =============================================================================
|
|
@@ -234,12 +228,17 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
|
234
228
|
const moduleName = match[1];
|
|
235
229
|
const moduleData = await findModule(moduleName, searchPaths);
|
|
236
230
|
if (!moduleData) {
|
|
231
|
+
const envelope = attachContext(makeErrorEnvelope({
|
|
232
|
+
code: ErrorCodes.MODULE_NOT_FOUND,
|
|
233
|
+
message: `Module '${moduleName}' not found`,
|
|
234
|
+
recoverable: true,
|
|
235
|
+
}), { module: moduleName });
|
|
237
236
|
return {
|
|
238
237
|
contents: [
|
|
239
238
|
{
|
|
240
239
|
uri,
|
|
241
|
-
mimeType: '
|
|
242
|
-
text:
|
|
240
|
+
mimeType: 'application/json',
|
|
241
|
+
text: JSON.stringify(envelope, null, 2),
|
|
243
242
|
},
|
|
244
243
|
],
|
|
245
244
|
};
|
|
@@ -254,12 +253,18 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
|
254
253
|
],
|
|
255
254
|
};
|
|
256
255
|
}
|
|
256
|
+
// Return structured error for unknown resource
|
|
257
|
+
const envelope = makeErrorEnvelope({
|
|
258
|
+
code: ErrorCodes.RESOURCE_NOT_FOUND,
|
|
259
|
+
message: `Unknown resource: ${uri}`,
|
|
260
|
+
recoverable: true,
|
|
261
|
+
});
|
|
257
262
|
return {
|
|
258
263
|
contents: [
|
|
259
264
|
{
|
|
260
265
|
uri,
|
|
261
|
-
mimeType: '
|
|
262
|
-
text:
|
|
266
|
+
mimeType: 'application/json',
|
|
267
|
+
text: JSON.stringify(envelope, null, 2),
|
|
263
268
|
},
|
|
264
269
|
],
|
|
265
270
|
};
|