@j0hanz/cortex-mcp 1.2.1 → 1.4.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 (95) hide show
  1. package/README.md +2 -2
  2. package/dist/engine/config.d.ts +3 -2
  3. package/dist/engine/config.js +15 -4
  4. package/dist/engine/context.d.ts +4 -5
  5. package/dist/engine/context.js +3 -1
  6. package/dist/engine/events.d.ts +8 -10
  7. package/dist/engine/events.js +6 -4
  8. package/dist/engine/reasoner.d.ts +7 -2
  9. package/dist/engine/reasoner.js +82 -39
  10. package/dist/engine/session-store.d.ts +14 -3
  11. package/dist/engine/session-store.js +134 -80
  12. package/dist/index.d.ts +0 -1
  13. package/dist/index.js +14 -8
  14. package/dist/lib/errors.d.ts +0 -1
  15. package/dist/lib/errors.js +16 -8
  16. package/dist/lib/formatting.d.ts +0 -1
  17. package/dist/lib/formatting.js +32 -25
  18. package/dist/lib/prompt-contracts.d.ts +14 -0
  19. package/dist/lib/prompt-contracts.js +124 -0
  20. package/dist/lib/text.d.ts +0 -1
  21. package/dist/lib/text.js +23 -18
  22. package/dist/lib/tool-contracts.d.ts +15 -0
  23. package/dist/lib/tool-contracts.js +92 -0
  24. package/dist/lib/tool-response.d.ts +0 -1
  25. package/dist/lib/tool-response.js +9 -7
  26. package/dist/lib/types.d.ts +23 -13
  27. package/dist/lib/types.js +0 -1
  28. package/dist/lib/validators.d.ts +0 -1
  29. package/dist/lib/validators.js +12 -11
  30. package/dist/prompts/index.d.ts +0 -1
  31. package/dist/prompts/index.js +98 -112
  32. package/dist/resources/index.d.ts +0 -1
  33. package/dist/resources/index.js +92 -68
  34. package/dist/resources/instructions.d.ts +1 -0
  35. package/dist/resources/instructions.js +95 -0
  36. package/dist/resources/tool-catalog.d.ts +1 -0
  37. package/dist/resources/tool-catalog.js +20 -0
  38. package/dist/resources/tool-info.d.ts +2 -0
  39. package/dist/resources/tool-info.js +35 -0
  40. package/dist/resources/workflows.d.ts +1 -0
  41. package/dist/resources/workflows.js +60 -0
  42. package/dist/schemas/inputs.d.ts +7 -2
  43. package/dist/schemas/inputs.js +62 -48
  44. package/dist/schemas/outputs.d.ts +3 -1
  45. package/dist/schemas/outputs.js +50 -25
  46. package/dist/server.d.ts +0 -1
  47. package/dist/server.js +71 -56
  48. package/dist/tools/index.d.ts +0 -4
  49. package/dist/tools/index.js +4 -5
  50. package/dist/tools/reasoning-think.d.ts +0 -1
  51. package/dist/tools/reasoning-think.js +273 -109
  52. package/package.json +11 -9
  53. package/dist/engine/config.d.ts.map +0 -1
  54. package/dist/engine/config.js.map +0 -1
  55. package/dist/engine/context.d.ts.map +0 -1
  56. package/dist/engine/context.js.map +0 -1
  57. package/dist/engine/events.d.ts.map +0 -1
  58. package/dist/engine/events.js.map +0 -1
  59. package/dist/engine/reasoner.d.ts.map +0 -1
  60. package/dist/engine/reasoner.js.map +0 -1
  61. package/dist/engine/session-store.d.ts.map +0 -1
  62. package/dist/engine/session-store.js.map +0 -1
  63. package/dist/index.d.ts.map +0 -1
  64. package/dist/index.js.map +0 -1
  65. package/dist/instructions.md +0 -147
  66. package/dist/lib/errors.d.ts.map +0 -1
  67. package/dist/lib/errors.js.map +0 -1
  68. package/dist/lib/formatting.d.ts.map +0 -1
  69. package/dist/lib/formatting.js.map +0 -1
  70. package/dist/lib/instructions.d.ts +0 -5
  71. package/dist/lib/instructions.d.ts.map +0 -1
  72. package/dist/lib/instructions.js +0 -14
  73. package/dist/lib/instructions.js.map +0 -1
  74. package/dist/lib/text.d.ts.map +0 -1
  75. package/dist/lib/text.js.map +0 -1
  76. package/dist/lib/tool-response.d.ts.map +0 -1
  77. package/dist/lib/tool-response.js.map +0 -1
  78. package/dist/lib/types.d.ts.map +0 -1
  79. package/dist/lib/types.js.map +0 -1
  80. package/dist/lib/validators.d.ts.map +0 -1
  81. package/dist/lib/validators.js.map +0 -1
  82. package/dist/prompts/index.d.ts.map +0 -1
  83. package/dist/prompts/index.js.map +0 -1
  84. package/dist/resources/index.d.ts.map +0 -1
  85. package/dist/resources/index.js.map +0 -1
  86. package/dist/schemas/inputs.d.ts.map +0 -1
  87. package/dist/schemas/inputs.js.map +0 -1
  88. package/dist/schemas/outputs.d.ts.map +0 -1
  89. package/dist/schemas/outputs.js.map +0 -1
  90. package/dist/server.d.ts.map +0 -1
  91. package/dist/server.js.map +0 -1
  92. package/dist/tools/index.d.ts.map +0 -1
  93. package/dist/tools/index.js.map +0 -1
  94. package/dist/tools/reasoning-think.d.ts.map +0 -1
  95. package/dist/tools/reasoning-think.js.map +0 -1
@@ -0,0 +1,20 @@
1
+ import { buildCoreContextPack } from './tool-info.js';
2
+ const CATALOG_GUIDE = `# Tool Catalog Details
3
+ ## Optional Parameters
4
+ - \`observation\`: What facts are known at this step? Use with \`hypothesis\` and \`evaluation\` as an alternative to \`thought\`.
5
+ - \`hypothesis\`: What is the proposed idea or next logical leap?
6
+ - \`evaluation\`: Critique the hypothesis. Are there flaws?
7
+ - \`step_summary\`: A 1-sentence summary of the conclusion reached in this step. Accumulates in the \`summary\` field for contextual guidance.
8
+ - \`is_conclusion\`: Set to true to end the session early with a final answer.
9
+ - \`rollback_to_step\`: Roll back to a thought index (0-based). All thoughts after this index are discarded.
10
+
11
+ ## Cross-Tool Data Flow
12
+ \`\`\`
13
+ reasoning_think -> result.sessionId -> reasoning_think.sessionId
14
+ reasoning_think -> result.sessionId -> reasoning://sessions/{sessionId}
15
+ reasoning_think -> result.sessionId -> file:///cortex/sessions/{sessionId}/trace.md
16
+ \`\`\`
17
+ `;
18
+ export function buildToolCatalog() {
19
+ return `${buildCoreContextPack()}\n\n${CATALOG_GUIDE}`;
20
+ }
@@ -0,0 +1,2 @@
1
+ export declare function buildCoreContextPack(): string;
2
+ export declare function getSharedConstraints(): string[];
@@ -0,0 +1,35 @@
1
+ import { getToolContracts } from '../lib/tool-contracts.js';
2
+ function toEntry(contract) {
3
+ return {
4
+ name: contract.name,
5
+ model: contract.model,
6
+ timeout: contract.timeoutMs > 0
7
+ ? `${Math.round(contract.timeoutMs / 1_000)}s`
8
+ : 'N/A',
9
+ maxOutputTokens: contract.maxOutputTokens > 0 ? String(contract.maxOutputTokens) : 'N/A',
10
+ purpose: contract.purpose,
11
+ };
12
+ }
13
+ const ENTRIES = Object.fromEntries(getToolContracts().map((contract) => [contract.name, toEntry(contract)]));
14
+ export function buildCoreContextPack() {
15
+ const names = Object.keys(ENTRIES).sort((a, b) => a.localeCompare(b));
16
+ const rows = names.map((name) => {
17
+ const e = ENTRIES[name];
18
+ return e
19
+ ? `| \`${e.name}\` | ${e.model} | ${e.timeout} | ${e.maxOutputTokens} | ${e.purpose} |`
20
+ : '';
21
+ });
22
+ return `# Core Context Pack\n\n| Tool | Model | Timeout | Max Output Tokens | Purpose |\n|------|-------|---------|-------------------|---------|\n${rows.join('\n')}`;
23
+ }
24
+ export function getSharedConstraints() {
25
+ return [
26
+ 'Sessions are in-memory — all data is lost on process restart.',
27
+ 'Session TTL: 30 minutes from last update. Expired sessions cannot be recovered.',
28
+ 'Maximum query length: 10,000 characters.',
29
+ 'Token budget enforcement is approximate (character-based proxy, not true tokenization).',
30
+ 'stdio transport only — no HTTP endpoint available.',
31
+ 'Every thought in the trace contains LLM-authored reasoning content provided via the `thought` parameter.',
32
+ "targetThoughts must be an integer within the level's min/max range.",
33
+ 'Session store limits are configurable via CORTEX_SESSION_TTL_MS, CORTEX_MAX_SESSIONS, and CORTEX_MAX_TOTAL_TOKENS.',
34
+ ];
35
+ }
@@ -0,0 +1 @@
1
+ export declare function buildWorkflowGuide(): string;
@@ -0,0 +1,60 @@
1
+ import { getToolContracts } from '../lib/tool-contracts.js';
2
+ import { getSharedConstraints } from './tool-info.js';
3
+ function buildToolReference() {
4
+ return getToolContracts()
5
+ .map((c) => `### \`${c.name}\`\n- Purpose: ${c.purpose}\n- Output: \`${c.outputShape}\``)
6
+ .join('\n\n');
7
+ }
8
+ export function buildWorkflowGuide() {
9
+ return `# THE "GOLDEN PATH" WORKFLOWS (CRITICAL)
10
+
11
+ ### WORKFLOW A: Sequential Reasoning (Most Common)
12
+
13
+ 1. Call \`reasoning_think\` with \`{ query: "...", level: "basic", thought: "Your detailed reasoning for step 1..." }\`.
14
+ 2. Read the response — note the \`sessionId\` and \`remainingThoughts\` fields.
15
+ 3. **You MUST continue**: Call again with \`{ sessionId: "<from response>", level: "<same level>", thought: "Your next reasoning step..." }\`.
16
+ 4. Repeat step 3 until the response shows \`status: "completed"\` or \`remainingThoughts: 0\`.
17
+ NOTE: The \`summary\` field contains the exact continuation call you should make next.
18
+
19
+ ### WORKFLOW B: Multi-Turn Reasoning (Session Continuation)
20
+
21
+ 1. Call \`reasoning_think\` with \`{ query: "initial question", level: "normal", thought: "Your first reasoning step..." }\` — note the returned \`sessionId\`.
22
+ 2. Call \`reasoning_think\` with \`{ sessionId: "<id>", level: "normal", thought: "Your next reasoning step..." }\` (optional: add \`query\` for follow-up context).
23
+ 3. Repeat until \`status: "completed"\` or \`remainingThoughts: 0\`, then read \`reasoning://sessions/{sessionId}\` for the full chain.
24
+ NOTE: The \`level\` parameter is optional when continuing; if provided and mismatched, the session level is used.
25
+
26
+ ### WORKFLOW C: Controlled Depth Reasoning
27
+
28
+ 1. Call \`reasoning_think\` with \`{ query: "...", level: "normal", targetThoughts: 8, thought: "Your reasoning..." }\` to set the session's planned step count.
29
+ 2. Repeat calls with the returned \`sessionId\` and your next \`thought\` until \`result.totalThoughts\` is reached.
30
+ NOTE: \`targetThoughts\` must fall within the level range (basic: 3–5, normal: 6–10, high: 15–25). Out-of-range values return \`E_INVALID_THOUGHT_COUNT\`.
31
+
32
+ ### WORKFLOW D: Async Task Execution
33
+
34
+ 1. Call \`reasoning_think\` as a task (send \`tools/call\` with \`task\` field) for long-running \`high\`-level reasoning.
35
+ 2. Poll \`tasks/get\` until status is \`completed\` or \`failed\`.
36
+ 3. Retrieve the result via \`tasks/result\`.
37
+ 4. Use \`tasks/cancel\` to abort if needed.
38
+
39
+ ### WORKFLOW E: Batched Run-To-Completion
40
+
41
+ 1. Start a new session with explicit \`targetThoughts\` and \`runMode: "run_to_completion"\`.
42
+ 2. Provide one \`thought\` plus additional \`thoughts[]\` entries to cover the planned step count.
43
+ 3. The server consumes thought inputs in order until completion, token budget exhaustion, or cancellation.
44
+
45
+ ### WORKFLOW F: Structured Reasoning (Observation/Hypothesis/Evaluation)
46
+
47
+ 1. Call \`reasoning_think\` with \`{ query: "...", level: "normal", observation: "facts...", hypothesis: "idea...", evaluation: "critique..." }\`.
48
+ 2. The server formats these into a structured thought and stores it in the session trace.
49
+ 3. Continue with \`sessionId\` using either \`thought\` or structured fields for subsequent steps.
50
+ 4. Use \`is_conclusion: true\` to end early, or \`rollback_to_step\` to discard and redo from a specific step.
51
+
52
+ ## Shared Constraints
53
+ ${getSharedConstraints()
54
+ .map((c) => `- ${c}`)
55
+ .join('\n')}
56
+
57
+ ## Tool Reference
58
+ ${buildToolReference()}
59
+ `;
60
+ }
@@ -12,8 +12,13 @@ export declare const ReasoningThinkInputSchema: z.ZodObject<{
12
12
  step: "step";
13
13
  run_to_completion: "run_to_completion";
14
14
  }>>;
15
- thought: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
15
+ thought: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
16
16
  thoughts: z.ZodOptional<z.ZodArray<z.ZodString>>;
17
+ is_conclusion: z.ZodOptional<z.ZodBoolean>;
18
+ rollback_to_step: z.ZodOptional<z.ZodNumber>;
19
+ step_summary: z.ZodOptional<z.ZodString>;
20
+ observation: z.ZodOptional<z.ZodString>;
21
+ hypothesis: z.ZodOptional<z.ZodString>;
22
+ evaluation: z.ZodOptional<z.ZodString>;
17
23
  }, z.core.$strict>;
18
24
  export type ReasoningThinkInput = z.infer<typeof ReasoningThinkInputSchema>;
19
- //# sourceMappingURL=inputs.d.ts.map
@@ -1,21 +1,29 @@
1
1
  import { z } from 'zod';
2
+ import { getLevelDescriptionString } from '../engine/config.js';
2
3
  import { getTargetThoughtsError } from '../lib/validators.js';
4
+ const RUN_MODE_VALUES = ['step', 'run_to_completion'];
5
+ const DEFAULT_RUN_MODE = 'step';
6
+ const LEVEL_VALUES = ['basic', 'normal', 'high'];
7
+ const QUERY_TEXT_SCHEMA = z.string().min(1).max(10000);
8
+ const LEVEL_SCHEMA = z.enum(LEVEL_VALUES);
9
+ const THOUGHT_TEXT_SCHEMA = z.string().min(1).max(100000);
10
+ const THOUGHT_BATCH_SCHEMA = z.array(THOUGHT_TEXT_SCHEMA).min(1).max(25);
11
+ function addCustomIssue(ctx, path, message) {
12
+ ctx.addIssue({
13
+ code: 'custom',
14
+ message,
15
+ path,
16
+ });
17
+ }
3
18
  export const ReasoningThinkInputSchema = z
4
19
  .strictObject({
5
- query: z
6
- .string()
7
- .min(1)
8
- .max(10000)
9
- .optional()
10
- .describe('The question or problem to reason about'),
11
- level: z
12
- .enum(['basic', 'normal', 'high'])
13
- .optional()
14
- .describe('Reasoning depth level (required for new sessions, optional for continuing)'),
20
+ query: QUERY_TEXT_SCHEMA.optional().describe('The question or problem to reason about'),
21
+ level: LEVEL_SCHEMA.optional().describe(`Reasoning depth level (required for new sessions, optional for continuing). ${getLevelDescriptionString()}.`),
15
22
  targetThoughts: z
16
23
  .number()
17
24
  .int()
18
25
  .min(1)
26
+ .max(25)
19
27
  .optional()
20
28
  .describe('Optional explicit thought count. Must fit the level range: basic 3–5, normal 6–10, high 15–25.'),
21
29
  sessionId: z
@@ -25,73 +33,79 @@ export const ReasoningThinkInputSchema = z
25
33
  .optional()
26
34
  .describe('Session ID to continue. The session level is used when continuing; provided level is optional.'),
27
35
  runMode: z
28
- .enum(['step', 'run_to_completion'])
36
+ .enum(RUN_MODE_VALUES)
29
37
  .optional()
30
38
  .describe('Execution mode (default: "step"). "step" appends a single thought per call. "run_to_completion" consumes all supplied thought inputs in one request.'),
31
39
  thought: z
32
- .union([
33
- z.string().min(1).max(100000),
34
- z.array(z.string().min(1).max(100000)).min(1).max(25),
35
- ])
40
+ .union([THOUGHT_TEXT_SCHEMA, THOUGHT_BATCH_SCHEMA])
41
+ .optional()
36
42
  .describe('Your full reasoning content for this step. ' +
37
43
  'The server stores this text verbatim as the thought in the session trace. ' +
38
44
  'Write your complete analysis, observations, and conclusions here — this is what appears in trace.md. ' +
39
45
  'Can be a single string or an array of strings (for batch execution).'),
40
46
  thoughts: z
41
- .array(z.string().min(1).max(100000))
47
+ .array(THOUGHT_TEXT_SCHEMA)
42
48
  .max(25)
43
49
  .optional()
44
50
  .describe('(Deprecated) Optional additional thought inputs. Use "thought" as an array instead.'),
51
+ is_conclusion: z
52
+ .boolean()
53
+ .optional()
54
+ .describe('Set to true if you have arrived at the final answer and wish to end the reasoning session early.'),
55
+ rollback_to_step: z
56
+ .number()
57
+ .int()
58
+ .min(0)
59
+ .optional()
60
+ .describe('Set to a thought index (0-based) to rollback to. All thoughts after this index will be discarded.'),
61
+ step_summary: z
62
+ .string()
63
+ .optional()
64
+ .describe('A 1-sentence summary of the conclusion reached in this specific step.'),
65
+ observation: z
66
+ .string()
67
+ .optional()
68
+ .describe('What facts are known at this step?'),
69
+ hypothesis: z
70
+ .string()
71
+ .optional()
72
+ .describe('What is the proposed idea or next logical leap?'),
73
+ evaluation: z
74
+ .string()
75
+ .optional()
76
+ .describe('Critique the hypothesis. Are there flaws?'),
45
77
  })
46
78
  .superRefine((data, ctx) => {
47
- const runMode = data.runMode ?? 'step';
79
+ const runMode = data.runMode ?? DEFAULT_RUN_MODE;
48
80
  if (data.sessionId === undefined && data.query === undefined) {
49
- ctx.addIssue({
50
- code: 'custom',
51
- message: 'query is required when sessionId is not provided',
52
- path: ['query'],
53
- });
81
+ addCustomIssue(ctx, ['query'], 'query is required when sessionId is not provided');
54
82
  }
55
83
  if (data.sessionId === undefined && data.level === undefined) {
56
- ctx.addIssue({
57
- code: 'custom',
58
- message: 'level is required when sessionId is not provided',
59
- path: ['level'],
60
- });
84
+ addCustomIssue(ctx, ['level'], 'level is required when sessionId is not provided');
61
85
  }
62
86
  if (runMode === 'run_to_completion' &&
63
87
  data.sessionId === undefined &&
64
88
  data.targetThoughts === undefined) {
65
- ctx.addIssue({
66
- code: 'custom',
67
- message: 'targetThoughts is required for run_to_completion when sessionId is not provided',
68
- path: ['targetThoughts'],
69
- });
89
+ addCustomIssue(ctx, ['targetThoughts'], 'targetThoughts is required for run_to_completion when sessionId is not provided');
70
90
  }
71
91
  if (runMode === 'step' && Array.isArray(data.thought)) {
72
- ctx.addIssue({
73
- code: 'custom',
74
- message: 'thought must be a string when runMode is "step"',
75
- path: ['thought'],
76
- });
92
+ addCustomIssue(ctx, ['thought'], 'thought must be a string when runMode is "step"');
77
93
  }
78
94
  if (runMode === 'step' && data.thoughts !== undefined) {
79
- ctx.addIssue({
80
- code: 'custom',
81
- message: 'thoughts is only allowed when runMode is "run_to_completion"',
82
- path: ['thoughts'],
83
- });
95
+ addCustomIssue(ctx, ['thoughts'], 'thoughts is only allowed when runMode is "run_to_completion"');
96
+ }
97
+ const hasThought = data.thought !== undefined;
98
+ const hasStructured = data.observation !== undefined &&
99
+ data.hypothesis !== undefined &&
100
+ data.evaluation !== undefined;
101
+ if (!hasThought && !hasStructured && data.rollback_to_step === undefined) {
102
+ addCustomIssue(ctx, ['thought'], 'Either "thought" or structured fields ("observation", "hypothesis", "evaluation") are required, unless rolling back.');
84
103
  }
85
104
  if (data.targetThoughts === undefined || data.level === undefined) {
86
105
  return;
87
106
  }
88
107
  const error = getTargetThoughtsError(data.level, data.targetThoughts);
89
108
  if (error) {
90
- ctx.addIssue({
91
- code: 'custom',
92
- message: error,
93
- path: ['targetThoughts'],
94
- });
109
+ addCustomIssue(ctx, ['targetThoughts'], error);
95
110
  }
96
111
  });
97
- //# sourceMappingURL=inputs.js.map
@@ -25,6 +25,7 @@ declare const ReasoningThinkSuccessSchema: z.ZodObject<{
25
25
  index: z.ZodNumber;
26
26
  content: z.ZodString;
27
27
  revision: z.ZodNumber;
28
+ stepSummary: z.ZodOptional<z.ZodString>;
28
29
  }, z.core.$strict>>;
29
30
  generatedThoughts: z.ZodNumber;
30
31
  requestedThoughts: z.ZodNumber;
@@ -57,6 +58,7 @@ export declare const ReasoningThinkResultSchema: z.ZodDiscriminatedUnion<[z.ZodO
57
58
  index: z.ZodNumber;
58
59
  content: z.ZodString;
59
60
  revision: z.ZodNumber;
61
+ stepSummary: z.ZodOptional<z.ZodString>;
60
62
  }, z.core.$strict>>;
61
63
  generatedThoughts: z.ZodNumber;
62
64
  requestedThoughts: z.ZodNumber;
@@ -99,6 +101,7 @@ export declare const ReasoningThinkToolOutputSchema: z.ZodObject<{
99
101
  index: z.ZodNumber;
100
102
  content: z.ZodString;
101
103
  revision: z.ZodNumber;
104
+ stepSummary: z.ZodOptional<z.ZodString>;
102
105
  }, z.core.$strict>>;
103
106
  generatedThoughts: z.ZodNumber;
104
107
  requestedThoughts: z.ZodNumber;
@@ -119,4 +122,3 @@ export declare const ReasoningThinkToolOutputSchema: z.ZodObject<{
119
122
  }, z.core.$strict>;
120
123
  export type ReasoningThinkSuccess = z.infer<typeof ReasoningThinkSuccessSchema>;
121
124
  export {};
122
- //# sourceMappingURL=outputs.d.ts.map
@@ -1,25 +1,33 @@
1
1
  import { z } from 'zod';
2
+ const ErrorInfoSchema = z.strictObject({
3
+ code: z.string(),
4
+ message: z.string(),
5
+ });
6
+ const MISSING_RESULT_PATH = ['result'];
7
+ const MISSING_ERROR_PATH = ['error'];
8
+ const LEVEL_VALUES = ['basic', 'normal', 'high'];
9
+ const STATUS_VALUES = ['active', 'completed', 'cancelled'];
10
+ const ThoughtSchema = z.strictObject({
11
+ index: z.number(),
12
+ content: z.string(),
13
+ revision: z.number(),
14
+ stepSummary: z
15
+ .string()
16
+ .optional()
17
+ .describe('A 1-sentence summary of the conclusion reached in this step, if provided.'),
18
+ });
2
19
  export const DefaultOutputSchema = z.strictObject({
3
20
  ok: z.boolean(),
4
21
  result: z.unknown().optional(),
5
- error: z
6
- .strictObject({
7
- code: z.string(),
8
- message: z.string(),
9
- })
10
- .optional(),
22
+ error: ErrorInfoSchema.optional(),
11
23
  });
12
24
  const ReasoningThinkSuccessSchema = z.strictObject({
13
25
  ok: z.literal(true),
14
26
  result: z.strictObject({
15
27
  sessionId: z.string(),
16
- level: z.enum(['basic', 'normal', 'high']),
17
- status: z.enum(['active', 'completed', 'cancelled']),
18
- thoughts: z.array(z.strictObject({
19
- index: z.number(),
20
- content: z.string(),
21
- revision: z.number(),
22
- })),
28
+ level: z.enum(LEVEL_VALUES),
29
+ status: z.enum(STATUS_VALUES),
30
+ thoughts: z.array(ThoughtSchema),
23
31
  generatedThoughts: z.number(),
24
32
  requestedThoughts: z.number(),
25
33
  totalThoughts: z.number(),
@@ -43,27 +51,44 @@ const ReasoningThinkSuccessSchema = z.strictObject({
43
51
  });
44
52
  const ReasoningThinkErrorSchema = z.strictObject({
45
53
  ok: z.literal(false),
46
- error: z.strictObject({
47
- code: z.string(),
48
- message: z.string(),
49
- }),
54
+ error: ErrorInfoSchema,
50
55
  });
51
56
  export const ReasoningThinkResultSchema = z.discriminatedUnion('ok', [
52
57
  ReasoningThinkSuccessSchema,
53
58
  ReasoningThinkErrorSchema,
54
59
  ]);
60
+ function getMissingFieldIssue(data) {
61
+ if (data.ok && data.result === undefined) {
62
+ return {
63
+ message: 'result is required when ok is true',
64
+ path: MISSING_RESULT_PATH,
65
+ };
66
+ }
67
+ if (!data.ok && data.error === undefined) {
68
+ return {
69
+ message: 'error is required when ok is false',
70
+ path: MISSING_ERROR_PATH,
71
+ };
72
+ }
73
+ return undefined;
74
+ }
55
75
  /**
56
76
  * Tool-facing output schema kept as a strict object so SDK tooling
57
77
  * can advertise outputSchema via tools/list.
58
78
  */
59
- export const ReasoningThinkToolOutputSchema = z.strictObject({
79
+ export const ReasoningThinkToolOutputSchema = z
80
+ .strictObject({
60
81
  ok: z.boolean(),
61
82
  result: ReasoningThinkSuccessSchema.shape.result.optional(),
62
- error: z
63
- .strictObject({
64
- code: z.string(),
65
- message: z.string(),
66
- })
67
- .optional(),
83
+ error: ErrorInfoSchema.optional(),
84
+ })
85
+ .superRefine((data, ctx) => {
86
+ const missingFieldIssue = getMissingFieldIssue(data);
87
+ if (missingFieldIssue) {
88
+ ctx.addIssue({
89
+ code: 'custom',
90
+ message: missingFieldIssue.message,
91
+ path: missingFieldIssue.path,
92
+ });
93
+ }
68
94
  });
69
- //# sourceMappingURL=outputs.js.map
package/dist/server.d.ts CHANGED
@@ -1,3 +1,2 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  export declare function createServer(): McpServer;
3
- //# sourceMappingURL=server.d.ts.map
package/dist/server.js CHANGED
@@ -4,34 +4,41 @@ import { InMemoryTaskStore } from '@modelcontextprotocol/sdk/experimental/tasks'
4
4
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
5
  import { engineEvents } from './engine/events.js';
6
6
  import { getErrorMessage } from './lib/errors.js';
7
- import { loadInstructions } from './lib/instructions.js';
8
7
  import { registerAllTools } from './tools/index.js';
9
8
  import { registerAllPrompts } from './prompts/index.js';
10
9
  import { registerAllResources } from './resources/index.js';
10
+ import { buildServerInstructions } from './resources/instructions.js';
11
11
  const ICON_MIME = 'image/svg+xml';
12
+ const SERVER_NAME = 'cortex-mcp';
13
+ const SERVER_TITLE = 'Cortex MCP';
14
+ const SERVER_WEBSITE_URL = 'https://github.com/j0hanz/cortex-mcp';
15
+ const RESOURCE_LIST_CHANGED_METHOD = 'resources/list_changed';
16
+ const RESOURCE_UPDATED_METHOD = 'resources/updated';
17
+ const SERVER_DESCRIPTION = 'Multi-level reasoning MCP server with configurable depth levels.';
18
+ const ICON_URL_CANDIDATES = [
19
+ new URL('../assets/logo.svg', import.meta.url),
20
+ new URL('./assets/logo.svg', import.meta.url),
21
+ ];
22
+ let cachedLocalIconData;
23
+ let cachedVersion;
12
24
  function getLocalIconData() {
13
- const candidates = [
14
- new URL('../assets/logo.svg', import.meta.url),
15
- new URL('./assets/logo.svg', import.meta.url),
16
- ];
17
- for (const candidate of candidates) {
25
+ if (cachedLocalIconData !== undefined) {
26
+ return cachedLocalIconData ?? undefined;
27
+ }
28
+ for (const candidate of ICON_URL_CANDIDATES) {
18
29
  try {
19
30
  const data = readFileSync(candidate);
20
- return `data:${ICON_MIME};base64,${data.toString('base64')}`;
31
+ cachedLocalIconData = `data:${ICON_MIME};base64,${data.toString('base64')}`;
32
+ return cachedLocalIconData;
21
33
  }
22
34
  catch {
23
35
  continue;
24
36
  }
25
37
  }
38
+ cachedLocalIconData = null;
26
39
  return undefined;
27
40
  }
28
- function loadVersion() {
29
- const packageJsonPath = findPackageJSON(import.meta.url);
30
- if (!packageJsonPath) {
31
- throw new Error('Unable to locate package.json for cortex-mcp.');
32
- }
33
- const packageJson = readFileSync(packageJsonPath, 'utf8');
34
- const parsed = JSON.parse(packageJson);
41
+ function getPackageVersion(parsed) {
35
42
  if (typeof parsed !== 'object' ||
36
43
  parsed === null ||
37
44
  !('version' in parsed) ||
@@ -40,41 +47,57 @@ function loadVersion() {
40
47
  }
41
48
  return parsed.version;
42
49
  }
50
+ function loadVersion() {
51
+ if (cachedVersion) {
52
+ return cachedVersion;
53
+ }
54
+ const packageJsonPath = findPackageJSON(import.meta.url);
55
+ if (!packageJsonPath) {
56
+ throw new Error('Unable to locate package.json for cortex-mcp.');
57
+ }
58
+ const packageJson = readFileSync(packageJsonPath, 'utf8');
59
+ cachedVersion = getPackageVersion(JSON.parse(packageJson));
60
+ return cachedVersion;
61
+ }
62
+ function getServerIcons(iconMeta) {
63
+ if (!iconMeta) {
64
+ return undefined;
65
+ }
66
+ return [
67
+ {
68
+ src: iconMeta.src,
69
+ mimeType: iconMeta.mimeType,
70
+ },
71
+ ];
72
+ }
43
73
  function attachEngineEventHandlers(server) {
74
+ const logNotificationFailure = (method, error, data) => {
75
+ void server
76
+ .sendLoggingMessage({
77
+ level: 'debug',
78
+ logger: 'cortex-mcp.server',
79
+ data: {
80
+ event: 'notification_failed',
81
+ method,
82
+ ...(data ?? {}),
83
+ error: getErrorMessage(error),
84
+ },
85
+ })
86
+ .catch(() => {
87
+ // Never fail on logging errors.
88
+ });
89
+ };
44
90
  const onResourcesChanged = () => {
45
91
  void server.server.sendResourceListChanged().catch((err) => {
46
- void server
47
- .sendLoggingMessage({
48
- level: 'debug',
49
- logger: 'cortex-mcp.server',
50
- data: {
51
- event: 'notification_failed',
52
- method: 'resources/list_changed',
53
- error: getErrorMessage(err),
54
- },
55
- })
56
- .catch(() => {
57
- // Never fail on logging errors.
58
- });
92
+ logNotificationFailure(RESOURCE_LIST_CHANGED_METHOD, err);
59
93
  });
60
94
  };
61
95
  const onResourceUpdated = (data) => {
62
96
  void server.server
63
97
  .sendResourceUpdated({ uri: data.uri })
64
98
  .catch((err) => {
65
- void server
66
- .sendLoggingMessage({
67
- level: 'debug',
68
- logger: 'cortex-mcp.server',
69
- data: {
70
- event: 'notification_failed',
71
- method: 'resources/updated',
72
- uri: data.uri,
73
- error: getErrorMessage(err),
74
- },
75
- })
76
- .catch(() => {
77
- // Never fail on logging errors.
99
+ logNotificationFailure(RESOURCE_UPDATED_METHOD, err, {
100
+ uri: data.uri,
78
101
  });
79
102
  });
80
103
  };
@@ -124,34 +147,27 @@ function installCloseCleanup(server, cleanup) {
124
147
  };
125
148
  }
126
149
  export function createServer() {
127
- const instructions = loadInstructions();
150
+ const instructions = buildServerInstructions();
128
151
  const version = loadVersion();
129
152
  const taskStore = new InMemoryTaskStore();
130
153
  const localIcon = getLocalIconData();
131
154
  const iconMeta = localIcon
132
155
  ? { src: localIcon, mimeType: ICON_MIME }
133
156
  : undefined;
157
+ const icons = getServerIcons(iconMeta);
134
158
  const server = new McpServer({
135
- name: 'cortex-mcp',
136
- title: 'Cortex MCP',
137
- description: 'Multi-level reasoning MCP server with configurable depth levels.',
138
- websiteUrl: 'https://github.com/j0hanz/cortex-mcp',
159
+ name: SERVER_NAME,
160
+ title: SERVER_TITLE,
161
+ description: SERVER_DESCRIPTION,
162
+ websiteUrl: SERVER_WEBSITE_URL,
139
163
  version,
140
- ...(iconMeta
141
- ? {
142
- icons: [
143
- {
144
- src: iconMeta.src,
145
- mimeType: iconMeta.mimeType,
146
- },
147
- ],
148
- }
149
- : {}),
164
+ ...(icons ? { icons } : {}),
150
165
  }, {
151
166
  capabilities: {
152
167
  tools: {},
153
168
  logging: {},
154
169
  prompts: {},
170
+ completions: {},
155
171
  resources: { subscribe: true, listChanged: true },
156
172
  tasks: {
157
173
  list: {},
@@ -173,4 +189,3 @@ export function createServer() {
173
189
  installCloseCleanup(server, detachEngineHandlers);
174
190
  return server;
175
191
  }
176
- //# sourceMappingURL=server.js.map
@@ -1,7 +1,3 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import type { IconMeta } from '../lib/types.js';
3
- /**
4
- * Registers all tools with the MCP server.
5
- */
6
3
  export declare function registerAllTools(server: McpServer, iconMeta?: IconMeta): void;
7
- //# sourceMappingURL=index.d.ts.map
@@ -1,8 +1,7 @@
1
1
  import { registerReasoningThinkTool } from './reasoning-think.js';
2
- /**
3
- * Registers all tools with the MCP server.
4
- */
2
+ const TOOL_REGISTRARS = [registerReasoningThinkTool];
5
3
  export function registerAllTools(server, iconMeta) {
6
- registerReasoningThinkTool(server, iconMeta);
4
+ for (const registerTool of TOOL_REGISTRARS) {
5
+ registerTool(server, iconMeta);
6
+ }
7
7
  }
8
- //# sourceMappingURL=index.js.map
@@ -1,4 +1,3 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import type { IconMeta } from '../lib/types.js';
3
3
  export declare function registerReasoningThinkTool(server: McpServer, iconMeta?: IconMeta): void;
4
- //# sourceMappingURL=reasoning-think.d.ts.map