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.
Files changed (101) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +35 -29
  4. package/dist/cli.js +519 -23
  5. package/dist/commands/add.d.ts +33 -14
  6. package/dist/commands/add.js +383 -16
  7. package/dist/commands/compose.js +60 -23
  8. package/dist/commands/index.d.ts +4 -0
  9. package/dist/commands/index.js +4 -0
  10. package/dist/commands/init.js +23 -1
  11. package/dist/commands/migrate.d.ts +30 -0
  12. package/dist/commands/migrate.js +650 -0
  13. package/dist/commands/pipe.d.ts +1 -0
  14. package/dist/commands/pipe.js +31 -11
  15. package/dist/commands/remove.js +33 -2
  16. package/dist/commands/run.d.ts +2 -0
  17. package/dist/commands/run.js +61 -28
  18. package/dist/commands/search.d.ts +28 -0
  19. package/dist/commands/search.js +143 -0
  20. package/dist/commands/test.d.ts +65 -0
  21. package/dist/commands/test.js +454 -0
  22. package/dist/commands/update.d.ts +1 -0
  23. package/dist/commands/update.js +106 -14
  24. package/dist/commands/validate.d.ts +36 -0
  25. package/dist/commands/validate.js +97 -0
  26. package/dist/errors/index.d.ts +225 -0
  27. package/dist/errors/index.js +420 -0
  28. package/dist/mcp/server.js +84 -79
  29. package/dist/modules/composition.js +97 -32
  30. package/dist/modules/loader.js +4 -2
  31. package/dist/modules/runner.d.ts +72 -5
  32. package/dist/modules/runner.js +306 -59
  33. package/dist/modules/subagent.d.ts +6 -1
  34. package/dist/modules/subagent.js +18 -13
  35. package/dist/modules/validator.js +14 -6
  36. package/dist/providers/anthropic.d.ts +15 -0
  37. package/dist/providers/anthropic.js +147 -5
  38. package/dist/providers/base.d.ts +11 -0
  39. package/dist/providers/base.js +18 -0
  40. package/dist/providers/gemini.d.ts +15 -0
  41. package/dist/providers/gemini.js +122 -5
  42. package/dist/providers/ollama.d.ts +15 -0
  43. package/dist/providers/ollama.js +111 -3
  44. package/dist/providers/openai.d.ts +11 -0
  45. package/dist/providers/openai.js +133 -0
  46. package/dist/registry/client.d.ts +212 -0
  47. package/dist/registry/client.js +359 -0
  48. package/dist/registry/index.d.ts +4 -0
  49. package/dist/registry/index.js +4 -0
  50. package/dist/registry/tar.d.ts +8 -0
  51. package/dist/registry/tar.js +353 -0
  52. package/dist/server/http.js +301 -45
  53. package/dist/server/index.d.ts +2 -0
  54. package/dist/server/index.js +1 -0
  55. package/dist/server/sse.d.ts +13 -0
  56. package/dist/server/sse.js +22 -0
  57. package/dist/types.d.ts +32 -1
  58. package/dist/types.js +4 -1
  59. package/dist/version.d.ts +1 -0
  60. package/dist/version.js +4 -0
  61. package/package.json +31 -7
  62. package/dist/modules/composition.test.d.ts +0 -11
  63. package/dist/modules/composition.test.js +0 -450
  64. package/dist/modules/policy.test.d.ts +0 -10
  65. package/dist/modules/policy.test.js +0 -369
  66. package/src/cli.ts +0 -471
  67. package/src/commands/add.ts +0 -315
  68. package/src/commands/compose.ts +0 -185
  69. package/src/commands/index.ts +0 -13
  70. package/src/commands/init.ts +0 -94
  71. package/src/commands/list.ts +0 -33
  72. package/src/commands/pipe.ts +0 -76
  73. package/src/commands/remove.ts +0 -57
  74. package/src/commands/run.ts +0 -80
  75. package/src/commands/update.ts +0 -130
  76. package/src/commands/versions.ts +0 -79
  77. package/src/index.ts +0 -90
  78. package/src/mcp/index.ts +0 -5
  79. package/src/mcp/server.ts +0 -403
  80. package/src/modules/composition.test.ts +0 -558
  81. package/src/modules/composition.ts +0 -1674
  82. package/src/modules/index.ts +0 -9
  83. package/src/modules/loader.ts +0 -508
  84. package/src/modules/policy.test.ts +0 -455
  85. package/src/modules/runner.ts +0 -1983
  86. package/src/modules/subagent.ts +0 -277
  87. package/src/modules/validator.ts +0 -700
  88. package/src/providers/anthropic.ts +0 -89
  89. package/src/providers/base.ts +0 -29
  90. package/src/providers/deepseek.ts +0 -83
  91. package/src/providers/gemini.ts +0 -117
  92. package/src/providers/index.ts +0 -78
  93. package/src/providers/minimax.ts +0 -81
  94. package/src/providers/moonshot.ts +0 -82
  95. package/src/providers/ollama.ts +0 -83
  96. package/src/providers/openai.ts +0 -84
  97. package/src/providers/qwen.ts +0 -82
  98. package/src/server/http.ts +0 -316
  99. package/src/server/index.ts +0 -6
  100. package/src/types.ts +0 -599
  101. 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
+ }
@@ -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: '1.3.0',
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
- content: [
98
- {
99
- type: 'text',
100
- text: JSON.stringify({ ok: false, error: `Module '${moduleName}' not found` }),
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
- // Run module
113
+ const resolvedProvider = provider?.name ?? providerLabel;
114
+ // Run module - result is already v2.2 envelope
108
115
  const result = await runModule(moduleData, provider, {
109
- input: { query: inputArgs, code: inputArgs },
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(result, null, 2),
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
- content: [
125
- {
126
- type: 'text',
127
- text: JSON.stringify({
128
- modules: modules.map((m) => ({
129
- name: m.name,
130
- location: m.location,
131
- format: m.format,
132
- tier: m.tier,
133
- })),
134
- count: modules.length,
135
- }, null, 2),
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
- content: [
146
- {
147
- type: 'text',
148
- text: JSON.stringify({ ok: false, error: `Module '${moduleName}' not found` }),
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
- content: [
155
- {
156
- type: 'text',
157
- text: JSON.stringify({
158
- ok: true,
159
- name: moduleData.name,
160
- version: moduleData.version,
161
- responsibility: moduleData.responsibility,
162
- tier: moduleData.tier,
163
- format: moduleData.format,
164
- inputSchema: moduleData.inputSchema,
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
- content: [
174
- {
175
- type: 'text',
176
- text: JSON.stringify({ ok: false, error: `Unknown tool: ${name}` }),
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
- return {
184
- content: [
185
- {
186
- type: 'text',
187
- text: JSON.stringify({
188
- ok: false,
189
- error: error instanceof Error ? error.message : String(error),
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: 'text/plain',
242
- text: `Module '${moduleName}' not found`,
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: 'text/plain',
262
- text: `Unknown resource: ${uri}`,
266
+ mimeType: 'application/json',
267
+ text: JSON.stringify(envelope, null, 2),
263
268
  },
264
269
  ],
265
270
  };