@j0hanz/cortex-mcp 1.3.0 → 1.5.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 (100) hide show
  1. package/dist/engine/config.d.ts +5 -5
  2. package/dist/engine/config.js +14 -5
  3. package/dist/engine/context.d.ts +0 -2
  4. package/dist/engine/context.js +0 -4
  5. package/dist/engine/events.d.ts +1 -2
  6. package/dist/engine/events.js +2 -4
  7. package/dist/engine/heuristics.d.ts +4 -0
  8. package/dist/engine/heuristics.js +65 -0
  9. package/dist/engine/reasoner.d.ts +7 -2
  10. package/dist/engine/reasoner.js +41 -103
  11. package/dist/engine/session-store.d.ts +6 -2
  12. package/dist/engine/session-store.js +78 -27
  13. package/dist/index.d.ts +0 -1
  14. package/dist/index.js +0 -1
  15. package/dist/lib/concurrency.d.ts +5 -0
  16. package/dist/lib/concurrency.js +17 -0
  17. package/dist/lib/errors.d.ts +23 -8
  18. package/dist/lib/errors.js +47 -6
  19. package/dist/lib/formatting.d.ts +13 -1
  20. package/dist/lib/formatting.js +18 -1
  21. package/dist/lib/prompt-contracts.d.ts +6 -0
  22. package/dist/lib/prompt-contracts.js +35 -0
  23. package/dist/lib/text.d.ts +0 -1
  24. package/dist/lib/text.js +0 -1
  25. package/dist/lib/tool-contracts.d.ts +15 -0
  26. package/dist/lib/tool-contracts.js +93 -0
  27. package/dist/lib/tool-response.d.ts +4 -1
  28. package/dist/lib/tool-response.js +3 -1
  29. package/dist/lib/types.d.ts +18 -1
  30. package/dist/lib/types.js +8 -2
  31. package/dist/lib/validators.d.ts +7 -2
  32. package/dist/lib/validators.js +30 -8
  33. package/dist/prompts/index.d.ts +0 -1
  34. package/dist/prompts/index.js +171 -74
  35. package/dist/prompts/templates.d.ts +2 -0
  36. package/dist/prompts/templates.js +106 -0
  37. package/dist/resources/index.d.ts +0 -1
  38. package/dist/resources/index.js +40 -63
  39. package/dist/resources/instructions.d.ts +1 -0
  40. package/dist/resources/instructions.js +103 -0
  41. package/dist/resources/tool-catalog.d.ts +1 -0
  42. package/dist/resources/tool-catalog.js +20 -0
  43. package/dist/resources/tool-info.d.ts +2 -0
  44. package/dist/resources/tool-info.js +35 -0
  45. package/dist/resources/workflows.d.ts +1 -0
  46. package/dist/resources/workflows.js +61 -0
  47. package/dist/schemas/inputs.d.ts +7 -2
  48. package/dist/schemas/inputs.js +39 -4
  49. package/dist/schemas/outputs.d.ts +32 -26
  50. package/dist/schemas/outputs.js +17 -14
  51. package/dist/server.d.ts +0 -1
  52. package/dist/server.js +1 -4
  53. package/dist/tools/index.d.ts +0 -1
  54. package/dist/tools/index.js +0 -1
  55. package/dist/tools/reasoning-think.d.ts +0 -1
  56. package/dist/tools/reasoning-think.js +211 -118
  57. package/package.json +9 -10
  58. package/dist/engine/config.d.ts.map +0 -1
  59. package/dist/engine/config.js.map +0 -1
  60. package/dist/engine/context.d.ts.map +0 -1
  61. package/dist/engine/context.js.map +0 -1
  62. package/dist/engine/events.d.ts.map +0 -1
  63. package/dist/engine/events.js.map +0 -1
  64. package/dist/engine/reasoner.d.ts.map +0 -1
  65. package/dist/engine/reasoner.js.map +0 -1
  66. package/dist/engine/session-store.d.ts.map +0 -1
  67. package/dist/engine/session-store.js.map +0 -1
  68. package/dist/index.d.ts.map +0 -1
  69. package/dist/index.js.map +0 -1
  70. package/dist/instructions.md +0 -148
  71. package/dist/lib/errors.d.ts.map +0 -1
  72. package/dist/lib/errors.js.map +0 -1
  73. package/dist/lib/formatting.d.ts.map +0 -1
  74. package/dist/lib/formatting.js.map +0 -1
  75. package/dist/lib/instructions.d.ts +0 -5
  76. package/dist/lib/instructions.d.ts.map +0 -1
  77. package/dist/lib/instructions.js +0 -30
  78. package/dist/lib/instructions.js.map +0 -1
  79. package/dist/lib/text.d.ts.map +0 -1
  80. package/dist/lib/text.js.map +0 -1
  81. package/dist/lib/tool-response.d.ts.map +0 -1
  82. package/dist/lib/tool-response.js.map +0 -1
  83. package/dist/lib/types.d.ts.map +0 -1
  84. package/dist/lib/types.js.map +0 -1
  85. package/dist/lib/validators.d.ts.map +0 -1
  86. package/dist/lib/validators.js.map +0 -1
  87. package/dist/prompts/index.d.ts.map +0 -1
  88. package/dist/prompts/index.js.map +0 -1
  89. package/dist/resources/index.d.ts.map +0 -1
  90. package/dist/resources/index.js.map +0 -1
  91. package/dist/schemas/inputs.d.ts.map +0 -1
  92. package/dist/schemas/inputs.js.map +0 -1
  93. package/dist/schemas/outputs.d.ts.map +0 -1
  94. package/dist/schemas/outputs.js.map +0 -1
  95. package/dist/server.d.ts.map +0 -1
  96. package/dist/server.js.map +0 -1
  97. package/dist/tools/index.d.ts.map +0 -1
  98. package/dist/tools/index.js.map +0 -1
  99. package/dist/tools/reasoning-think.d.ts.map +0 -1
  100. package/dist/tools/reasoning-think.js.map +0 -1
@@ -1,52 +1,29 @@
1
1
  import { z } from 'zod';
2
2
  import { completable } from '@modelcontextprotocol/sdk/server/completable.js';
3
3
  import { sessionStore } from '../engine/reasoner.js';
4
- import { loadInstructions } from '../lib/instructions.js';
4
+ import { getPromptContracts, } from '../lib/prompt-contracts.js';
5
+ import { withIconMeta } from '../lib/tool-response.js';
6
+ import { REASONING_LEVELS } from '../lib/types.js';
7
+ import { collectPrefixMatches } from '../lib/validators.js';
8
+ import { buildServerInstructions } from '../resources/instructions.js';
9
+ import { getTemplate } from './templates.js';
5
10
  const COMPLETION_LIMIT = 20;
6
- const LEVEL_VALUES = ['basic', 'normal', 'high'];
7
- const LEVEL_ENUM_SCHEMA = z.enum(LEVEL_VALUES);
8
- const LEVEL_TITLES = {
9
- basic: 'Basic',
10
- normal: 'Normal',
11
- high: 'High',
12
- };
11
+ const LEVEL_ENUM_SCHEMA = z.enum(REASONING_LEVELS);
13
12
  const REASONING_TOOL_NAME = 'reasoning_think';
14
- const THOUGHT_PARAMETER_GUIDANCE = 'Provide your full reasoning in the "thought" parameter for each step.';
15
- function levelTitle(level) {
16
- return LEVEL_TITLES[level];
17
- }
18
- function formatTargetThoughts(targetThoughts) {
19
- if (targetThoughts === undefined) {
20
- return '';
21
- }
22
- return `, targetThoughts=${String(targetThoughts)}`;
23
- }
13
+ const THOUGHT_PARAMETER_GUIDANCE = 'Provide full reasoning in "thought" for every step.';
24
14
  function completeSessionId(value) {
25
- const results = [];
26
- for (const sessionId of sessionStore.listSessionIds()) {
27
- if (!sessionId.startsWith(value)) {
28
- continue;
29
- }
30
- results.push(sessionId);
31
- if (results.length >= COMPLETION_LIMIT) {
32
- break;
33
- }
34
- }
35
- return results;
15
+ return collectPrefixMatches(sessionStore.listSessionIds(), value, COMPLETION_LIMIT);
36
16
  }
37
17
  function completeLevel(value) {
38
18
  const normalized = value.toLowerCase();
39
19
  const results = [];
40
- for (const level of LEVEL_VALUES) {
20
+ for (const level of REASONING_LEVELS) {
41
21
  if (level.startsWith(normalized)) {
42
22
  results.push(level);
43
23
  }
44
24
  }
45
25
  return results;
46
26
  }
47
- function withIconMeta(iconMeta) {
48
- return iconMeta ? { icons: [iconMeta] } : undefined;
49
- }
50
27
  function createTextPrompt(text) {
51
28
  return {
52
29
  messages: [
@@ -60,38 +37,149 @@ function createTextPrompt(text) {
60
37
  ],
61
38
  };
62
39
  }
63
- function registerLevelPrompt(server, level, iconMeta) {
64
- server.registerPrompt(`reasoning.${level}`, {
65
- title: `Reasoning ${levelTitle(level)}`,
66
- description: `Prepare a ${level}-depth reasoning request.`,
67
- ...(withIconMeta(iconMeta) ?? {}),
68
- argsSchema: {
69
- query: z
70
- .string()
71
- .min(1)
72
- .max(10000)
73
- .describe('The question or problem to reason about'),
74
- targetThoughts: z
75
- .number()
76
- .int()
77
- .min(1)
78
- .max(25)
79
- .optional()
80
- .describe('Optional exact step count within the selected level range (max 25)'),
81
- },
82
- }, ({ query, targetThoughts }) => {
83
- // Create user message
84
- const text = `Initiate a ${level}-depth reasoning session for the query: ${JSON.stringify(query)}. Use the "${REASONING_TOOL_NAME}" tool${formatTargetThoughts(targetThoughts)}. ${THOUGHT_PARAMETER_GUIDANCE} This is stored verbatim in the session trace. Repeat calls with the returned sessionId until totalThoughts is reached.`;
85
- return createTextPrompt(text);
40
+ function buildPromptText(args) {
41
+ const { context, task, constraints, output } = args;
42
+ return [
43
+ '# Context',
44
+ ...context,
45
+ '',
46
+ '# Task',
47
+ ...task,
48
+ '',
49
+ '# Constraints',
50
+ ...constraints.map((line) => `- ${line}`),
51
+ '',
52
+ '# Output',
53
+ ...output,
54
+ ].join('\n');
55
+ }
56
+ function buildStartReasoningPrompt(args) {
57
+ const { level, query, targetThoughts } = args;
58
+ const base = buildPromptText({
59
+ context: [
60
+ `Query: ${JSON.stringify(query)}`,
61
+ `Requested level: ${level}`,
62
+ `Target thoughts: ${targetThoughts === undefined
63
+ ? 'use level default'
64
+ : String(targetThoughts)}`,
65
+ ],
66
+ task: [
67
+ `Start a new reasoning session using "${REASONING_TOOL_NAME}".`,
68
+ 'Create the first step with a complete, concrete reasoning thought.',
69
+ ],
70
+ constraints: [
71
+ THOUGHT_PARAMETER_GUIDANCE,
72
+ 'Preserve sessionId from the response for continuation calls.',
73
+ 'Continue until status is completed or remainingThoughts is 0.',
74
+ ],
75
+ output: [
76
+ 'Return the first tool call payload only.',
77
+ 'Fields: query, level, thought, and optional targetThoughts.',
78
+ ],
79
+ });
80
+ return `${base}\n\n---\n\n${getTemplate(level)}`;
81
+ }
82
+ function buildRetryReasoningPrompt(args) {
83
+ const { query, level, targetThoughts } = args;
84
+ const base = buildPromptText({
85
+ context: [
86
+ `Retry query: ${JSON.stringify(query)}`,
87
+ `Retry level: ${level}`,
88
+ `Target thoughts: ${targetThoughts === undefined
89
+ ? 'unchanged / default'
90
+ : String(targetThoughts)}`,
91
+ ],
92
+ task: [
93
+ `Retry by calling "${REASONING_TOOL_NAME}" with an improved first thought.`,
94
+ ],
95
+ constraints: [
96
+ THOUGHT_PARAMETER_GUIDANCE,
97
+ 'Use a direct and specific thought with no filler language.',
98
+ ],
99
+ output: [
100
+ 'Return one tool call payload only.',
101
+ 'Fields: query, level, thought, and optional targetThoughts.',
102
+ ],
103
+ });
104
+ return `${base}\n\n---\n\n${getTemplate(level)}`;
105
+ }
106
+ function buildContinueReasoningPrompt(args) {
107
+ const { sessionId, query, level, targetThoughts } = args;
108
+ return buildPromptText({
109
+ context: [
110
+ `Session: ${JSON.stringify(sessionId)}`,
111
+ query === undefined
112
+ ? 'Follow-up query: none provided'
113
+ : `Follow-up query: ${JSON.stringify(query)}`,
114
+ level === undefined
115
+ ? 'Level: keep session level'
116
+ : `Level override: ${level}`,
117
+ `Target thoughts: ${targetThoughts === undefined
118
+ ? 'unchanged / default'
119
+ : String(targetThoughts)}`,
120
+ ],
121
+ task: [
122
+ `Continue the existing session using "${REASONING_TOOL_NAME}".`,
123
+ 'Generate the next reasoning step only.',
124
+ ],
125
+ constraints: [
126
+ THOUGHT_PARAMETER_GUIDANCE,
127
+ 'Keep the same sessionId in the call payload.',
128
+ 'Prefer concise, concrete reasoning over meta commentary.',
129
+ ],
130
+ output: [
131
+ 'Return one continuation tool call payload only.',
132
+ 'Fields: sessionId, thought, and optional query/level/targetThoughts.',
133
+ ],
86
134
  });
87
135
  }
88
136
  export function registerAllPrompts(server, iconMeta) {
89
- registerLevelPrompt(server, 'basic', iconMeta);
90
- registerLevelPrompt(server, 'normal', iconMeta);
91
- registerLevelPrompt(server, 'high', iconMeta);
92
- server.registerPrompt('reasoning.retry', {
93
- title: 'Retry Reasoning',
94
- description: 'Retry a failed reasoning task with modified parameters.',
137
+ const contracts = getPromptContracts();
138
+ const instructions = buildServerInstructions();
139
+ const contractByName = new Map(contracts.map((contract) => [contract.name, contract]));
140
+ const getRequiredContract = (name) => {
141
+ const contract = contractByName.get(name);
142
+ if (!contract) {
143
+ throw new Error(`Missing mandatory prompt contract for '${name}'. Check src/lib/prompt-contracts.ts.`);
144
+ }
145
+ return contract;
146
+ };
147
+ // Register Level Prompts (reasoning.basic, .normal, .high)
148
+ for (const level of REASONING_LEVELS) {
149
+ const name = `reasoning.${level}`;
150
+ const contract = getRequiredContract(name);
151
+ server.registerPrompt(name, {
152
+ title: contract.title,
153
+ description: contract.description,
154
+ ...(withIconMeta(iconMeta) ?? {}),
155
+ argsSchema: {
156
+ query: z
157
+ .string()
158
+ .min(1)
159
+ .max(10000)
160
+ .describe('The question or problem to reason about'),
161
+ targetThoughts: z
162
+ .number()
163
+ .int()
164
+ .min(1)
165
+ .max(25)
166
+ .optional()
167
+ .describe('Optional exact step count within the selected level range (max 25)'),
168
+ },
169
+ }, ({ query, targetThoughts }) => {
170
+ const text = buildStartReasoningPrompt({
171
+ level,
172
+ query,
173
+ ...(targetThoughts !== undefined ? { targetThoughts } : {}),
174
+ });
175
+ return createTextPrompt(text);
176
+ });
177
+ }
178
+ // Register reasoning.retry
179
+ const retryContract = getRequiredContract('reasoning.retry');
180
+ server.registerPrompt(retryContract.name, {
181
+ title: retryContract.title,
182
+ description: retryContract.description,
95
183
  ...(withIconMeta(iconMeta) ?? {}),
96
184
  argsSchema: {
97
185
  query: z
@@ -109,18 +197,25 @@ export function registerAllPrompts(server, iconMeta) {
109
197
  .describe('Optional exact step count'),
110
198
  },
111
199
  }, ({ query, level, targetThoughts }) => {
112
- const text = `Retry the reasoning session for query: ${JSON.stringify(query)}. Use the "${REASONING_TOOL_NAME}" tool with level="${level}"${formatTargetThoughts(targetThoughts)}. ${THOUGHT_PARAMETER_GUIDANCE}`;
200
+ const text = buildRetryReasoningPrompt({
201
+ query,
202
+ level,
203
+ ...(targetThoughts !== undefined ? { targetThoughts } : {}),
204
+ });
113
205
  return createTextPrompt(text);
114
206
  });
115
- const instructions = loadInstructions();
116
- server.registerPrompt('get-help', {
117
- title: 'Get Help',
118
- description: 'Return the server usage instructions.',
207
+ // Register get-help
208
+ const helpContract = getRequiredContract('get-help');
209
+ server.registerPrompt(helpContract.name, {
210
+ title: helpContract.title,
211
+ description: helpContract.description,
119
212
  ...(withIconMeta(iconMeta) ?? {}),
120
213
  }, () => createTextPrompt(instructions));
121
- server.registerPrompt('reasoning.continue', {
122
- title: 'Continue Reasoning',
123
- description: 'Continue an existing reasoning session with a follow-up query.',
214
+ // Register reasoning.continue
215
+ const continueContract = getRequiredContract('reasoning.continue');
216
+ server.registerPrompt(continueContract.name, {
217
+ title: continueContract.title,
218
+ description: continueContract.description,
124
219
  ...(withIconMeta(iconMeta) ?? {}),
125
220
  argsSchema: {
126
221
  sessionId: completable(z
@@ -144,10 +239,12 @@ export function registerAllPrompts(server, iconMeta) {
144
239
  .describe('Optional exact step count within the selected level range (max 25)'),
145
240
  },
146
241
  }, ({ sessionId, query, level, targetThoughts }) => {
147
- const followUpText = query === undefined ? '' : ` with follow-up: ${JSON.stringify(query)}`;
148
- const levelText = level === undefined ? '' : ` with level="${level}"`;
149
- const text = `Continue reasoning session ${JSON.stringify(sessionId)}${followUpText}. Use "${REASONING_TOOL_NAME}"${levelText}${formatTargetThoughts(targetThoughts)}. ${THOUGHT_PARAMETER_GUIDANCE}`;
242
+ const text = buildContinueReasoningPrompt({
243
+ sessionId,
244
+ ...(query !== undefined ? { query } : {}),
245
+ ...(level !== undefined ? { level } : {}),
246
+ ...(targetThoughts !== undefined ? { targetThoughts } : {}),
247
+ });
150
248
  return createTextPrompt(text);
151
249
  });
152
250
  }
153
- //# sourceMappingURL=index.js.map
@@ -0,0 +1,2 @@
1
+ import type { ReasoningLevel } from '../lib/types.js';
2
+ export declare function getTemplate(level: ReasoningLevel): string;
@@ -0,0 +1,106 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Guided few-shot examples for each reasoning level.
3
+ // Each template shows correct `thought` depth and step count so the LLM
4
+ // calibrates its output to the selected level requirements.
5
+ // ---------------------------------------------------------------------------
6
+ const BASIC_TEMPLATE = `## Guided Example (basic — 3 thoughts)
7
+
8
+ **Query:** "Is Set or Array better for deduplicating a list of strings in JavaScript?"
9
+
10
+ **Thought 1 of 3:**
11
+ > A \`Set\` enforces uniqueness automatically; \`[...new Set(arr)]\` is a single-step O(n) operation. An Array-based approach (\`filter\` + \`indexOf\`) is O(n²) and adds unnecessary complexity.
12
+
13
+ **Thought 2 of 3:**
14
+ > Edge cases: both preserve insertion order in modern JS engines, so ordering is not a differentiator. \`Set\` converts all values to a common reference internally, which handles string equality correctly.
15
+
16
+ **Thought 3 of 3 — conclusion:**
17
+ > Use \`Set\`. It is faster (O(n) vs O(n²)), shorter to write, and immediately communicates the intent of deduplication. \`[...new Set(list)]\` is the idiomatic solution.
18
+
19
+ ---
20
+ **System Directive:** Follow the pattern above. Each \`thought\` must contain self-contained, concrete analysis — no filler language or meta-commentary. Use 3 to 5 thoughts total.`;
21
+ const NORMAL_TEMPLATE = `## Guided Example (normal — 7 thoughts)
22
+
23
+ **Query:** "How do I safely handle concurrent writes to a shared in-memory counter in a Node.js service?"
24
+
25
+ **Thought 1 of 7:**
26
+ > Node.js is single-threaded: synchronous code is never interrupted mid-execution, so a plain \`counter++\` within one event-loop tick is effectively atomic from JavaScript's perspective.
27
+
28
+ **Thought 2 of 7:**
29
+ > The risk arises in *async* code. A read-modify-write spanning an \`await\` boundary is NOT atomic. Two concurrent callers can both read the same value before either writes, causing a lost update.
30
+
31
+ **Thought 3 of 7:**
32
+ > Example race: \`const v = await db.get('c'); await db.set('c', v + 1);\` — if two requests interleave at the \`await\` points, both read \`v=5\` and both write \`6\`, losing one increment.
33
+
34
+ **Thought 4 of 7:**
35
+ > Solution A — atomic DB operation: \`UPDATE counter SET n = n + 1 RETURNING n\` (SQL) or Redis \`INCR\`. The DB engine serialises the read-modify-write internally with no async gap.
36
+
37
+ **Thought 5 of 7:**
38
+ > Solution B — async mutex: use a library-level lock (e.g. \`async-mutex\`) to serialise access. Works for in-process state but does not scale across multiple processes or restarts.
39
+
40
+ **Thought 6 of 7:**
41
+ > Solution C — synchronous in-memory only: keep the counter as a plain variable, increment with \`counter++\` (no \`await\` in the read-modify-write path). Valid only for single-process, ephemeral state.
42
+
43
+ **Thought 7 of 7 — conclusion:**
44
+ > Prefer Solution A (atomic DB op) for correctness across restarts and multi-process deployments. Use Solution C only for in-process, non-persisted counters where an \`await\` never touches the variable. Avoid async read-modify-write without a mutex.
45
+
46
+ ---
47
+ **System Directive:** Follow the pattern above. Each \`thought\` must be concrete and progress the analysis. Use 6 to 10 thoughts total; avoid restating earlier thoughts.`;
48
+ const HIGH_TEMPLATE = `## Guided Example (high — 15 thoughts)
49
+
50
+ **Query:** "Our Node.js API latency jumped from p50=20ms to p50=800ms after a dependency upgrade. How do I diagnose and fix this?"
51
+
52
+ **Thought 1 of 15:**
53
+ > Establish the change boundary: run \`git log --oneline\` to find the upgrade commit. Use \`git bisect\` between the last known-good tag and HEAD to confirm the exact commit that caused the regression.
54
+
55
+ **Thought 2 of 15:**
56
+ > Collect baseline metrics before touching anything: event-loop lag (\`perf_hooks.monitorEventLoopDelay\`), GC pause times (\`--expose-gc\` + \`PerformanceObserver\`), and per-route timings. This separates compute regressions from I/O regressions.
57
+
58
+ **Thought 3 of 15:**
59
+ > If event-loop lag is high (>50ms per tick), the cause is synchronous blocking inserted into the hot path — JSON serialisation of large objects, synchronous file I/O, regex backtracking, or CPU-heavy validation.
60
+
61
+ **Thought 4 of 15:**
62
+ > If event-loop lag is low but p50 is high, the bottleneck is I/O wait: slow DB queries, connection-pool exhaustion, DNS resolution delays, or increased network RTT to the upgraded service.
63
+
64
+ **Thought 5 of 15:**
65
+ > Read the dependency's changelog between the old and new version. Look for: new middleware injected at startup, serialisation format changes, default timeout changes, or connection-pool default reductions.
66
+
67
+ **Thought 6 of 15:**
68
+ > Profile with \`clinic.js flame\` (or \`node --prof\` + \`node --prof-process\`) under representative load. The flame graph will pinpoint whether wall-clock time is in JS compute vs. idle I/O await.
69
+
70
+ **Thought 7 of 15:**
71
+ > Write a minimal reproduction that calls *only* the upgraded package's API with representative input. Benchmark it against the pinned old version in isolation to confirm the package itself is the source.
72
+
73
+ **Thought 8 of 15:**
74
+ > Common 40× regression patterns: (a) added synchronous schema validation on every request, (b) HTTP/1.1 → HTTP/2 frame parsing overhead, (c) new middleware that buffers the full request body before routing.
75
+
76
+ **Thought 9 of 15:**
77
+ > Check connection-pool configuration: if the upgrade changed default pool size or idle timeout, requests may queue waiting for connections. Inspect \`pool.min\`, \`pool.max\`, and \`acquireTimeoutMillis\` in the new version's defaults.
78
+
79
+ **Thought 10 of 15:**
80
+ > Check middleware registration order: some packages inject global middleware at \`require\`-time. A slow middleware (e.g., large-payload body parser) before fast routes affects all endpoints even if the route itself is unchanged.
81
+
82
+ **Thought 11 of 15:**
83
+ > Immediate mitigation: pin the dependency to the last known-good version (\`npm install dep@x.y.z\`) and deploy to restore SLA while the full investigation continues. Add a TODO linking to the issue tracker.
84
+
85
+ **Thought 12 of 15:**
86
+ > If the regression is a bug in the dependency, open an issue with the minimal reproduction from Thought 7. Check if a patch release or a configuration flag exists to disable the slow behaviour.
87
+
88
+ **Thought 13 of 15:**
89
+ > If the slow path is unavoidable, mitigation options: (a) cache the expensive result at the request or process level, (b) offload CPU work to a \`worker_threads\` worker, (c) evaluate an alternative package.
90
+
91
+ **Thought 14 of 15:**
92
+ > After applying the fix, run the same load test that revealed the regression. Confirm p50 and p99 return to baseline and do not diverge under sustained load. Check that GC pressure did not increase.
93
+
94
+ **Thought 15 of 15 — conclusion:**
95
+ > Diagnosis path: git bisect → event-loop lag check → clinic.js flame graph → isolated package benchmark → changelog review → pool/middleware audit. Mitigation: pin version immediately. Fix: configure, cache, or replace. Prevention: add a latency benchmark target to CI.
96
+
97
+ ---
98
+ **System Directive:** Follow the pattern above. Each \`thought\` must be specific, advancing the investigation — no summaries of prior steps, no filler. Use 15 to 25 thoughts total; scale depth to complexity.`;
99
+ const TEMPLATES = {
100
+ basic: BASIC_TEMPLATE,
101
+ normal: NORMAL_TEMPLATE,
102
+ high: HIGH_TEMPLATE,
103
+ };
104
+ export function getTemplate(level) {
105
+ return TEMPLATES[level];
106
+ }
@@ -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 registerAllResources(server: McpServer, iconMeta?: IconMeta): void;
4
- //# sourceMappingURL=index.d.ts.map
@@ -2,7 +2,11 @@ import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { McpError } from '@modelcontextprotocol/sdk/types.js';
3
3
  import { sessionStore } from '../engine/reasoner.js';
4
4
  import { formatThoughtsToMarkdown } from '../lib/formatting.js';
5
- import { loadInstructions } from '../lib/instructions.js';
5
+ import { withIconMeta } from '../lib/tool-response.js';
6
+ import { collectPrefixMatches } from '../lib/validators.js';
7
+ import { buildServerInstructions } from './instructions.js';
8
+ import { buildToolCatalog } from './tool-catalog.js';
9
+ import { buildWorkflowGuide } from './workflows.js';
6
10
  const SESSIONS_RESOURCE_URI = 'reasoning://sessions';
7
11
  const SESSION_RESOURCE_PREFIX = `${SESSIONS_RESOURCE_URI}/`;
8
12
  const TRACE_RESOURCE_PREFIX = 'file:///cortex/sessions/';
@@ -22,7 +26,7 @@ function resolveSession(sessionId, uri) {
22
26
  }
23
27
  return session;
24
28
  }
25
- function buildSessionSummaryFromSummary(session) {
29
+ function buildSessionSummary(session) {
26
30
  const ttlMs = sessionStore.getTtlMs();
27
31
  const expiresAt = sessionStore.getExpiresAt(session.id) ?? session.updatedAt + ttlMs;
28
32
  return {
@@ -31,7 +35,6 @@ function buildSessionSummaryFromSummary(session) {
31
35
  status: session.status,
32
36
  generatedThoughts: session.generatedThoughts,
33
37
  remainingThoughts: Math.max(0, session.totalThoughts - session.generatedThoughts),
34
- plannedThoughts: session.totalThoughts,
35
38
  totalThoughts: session.totalThoughts,
36
39
  tokenBudget: session.tokenBudget,
37
40
  tokensUsed: session.tokensUsed,
@@ -40,13 +43,6 @@ function buildSessionSummaryFromSummary(session) {
40
43
  expiresAt,
41
44
  };
42
45
  }
43
- function buildSessionSummary(sessionId) {
44
- const session = sessionStore.getSummary(sessionId);
45
- if (!session) {
46
- throw new McpError(-32002, `Resource not found: ${SESSION_RESOURCE_PREFIX}${sessionId}`);
47
- }
48
- return buildSessionSummaryFromSummary(session);
49
- }
50
46
  const THOUGHT_NAME_PATTERN = /^Thought-(\d+)(?:-Revised)?$/;
51
47
  function parseThoughtName(thoughtName, session) {
52
48
  const match = THOUGHT_NAME_PATTERN.exec(thoughtName);
@@ -64,61 +60,18 @@ function parseThoughtName(thoughtName, session) {
64
60
  };
65
61
  }
66
62
  function serializeJson(data) {
67
- return JSON.stringify(data, null, 2);
68
- }
69
- function withIconMeta(iconMeta) {
70
- return iconMeta ? { icons: [iconMeta] } : undefined;
63
+ return JSON.stringify(data);
71
64
  }
72
65
  function shortSessionId(sessionId) {
73
66
  return sessionId.slice(0, 8);
74
67
  }
75
- const COMPLETION_CACHE_TTL_MS = 1000;
76
- const COMPLETION_CACHE_MAX_ENTRIES = 512;
77
68
  const MAX_COMPLETION_RESULTS = 20;
78
- const completionCache = new Map();
79
- let lastCompletionCachePruneAt = 0;
69
+ function completeSessionIds(value) {
70
+ return collectPrefixMatches(sessionStore.listSessionIds(), value, MAX_COMPLETION_RESULTS);
71
+ }
80
72
  function toIsoTimestamp(unixMs) {
81
73
  return new Date(unixMs).toISOString();
82
74
  }
83
- function pruneCompletionCacheIfNeeded(now) {
84
- if (now - lastCompletionCachePruneAt < COMPLETION_CACHE_TTL_MS) {
85
- return;
86
- }
87
- lastCompletionCachePruneAt = now;
88
- for (const [cacheKey, entry] of completionCache.entries()) {
89
- if (now - entry.timestamp >= COMPLETION_CACHE_TTL_MS) {
90
- completionCache.delete(cacheKey);
91
- }
92
- }
93
- while (completionCache.size > COMPLETION_CACHE_MAX_ENTRIES) {
94
- const oldestKey = completionCache.keys().next().value;
95
- if (typeof oldestKey !== 'string') {
96
- break;
97
- }
98
- completionCache.delete(oldestKey);
99
- }
100
- }
101
- function completeSessionIds(value) {
102
- const now = Date.now();
103
- pruneCompletionCacheIfNeeded(now);
104
- const cacheKey = `sessionId:${value}`;
105
- const cached = completionCache.get(cacheKey);
106
- if (cached && now - cached.timestamp < COMPLETION_CACHE_TTL_MS) {
107
- return cached.results;
108
- }
109
- const results = [];
110
- for (const sessionId of sessionStore.listSessionIds()) {
111
- if (!sessionId.startsWith(value)) {
112
- continue;
113
- }
114
- results.push(sessionId);
115
- if (results.length >= MAX_COMPLETION_RESULTS) {
116
- break;
117
- }
118
- }
119
- completionCache.set(cacheKey, { results, timestamp: now });
120
- return results;
121
- }
122
75
  function completeThoughtNames(value, sessionId) {
123
76
  const session = sessionStore.get(sessionId);
124
77
  if (!session) {
@@ -143,7 +96,9 @@ function completeThoughtNames(value, sessionId) {
143
96
  return results;
144
97
  }
145
98
  export function registerAllResources(server, iconMeta) {
146
- const instructions = loadInstructions();
99
+ const instructions = buildServerInstructions();
100
+ const toolCatalog = buildToolCatalog();
101
+ const workflows = buildWorkflowGuide();
147
102
  server.registerResource('server-instructions', 'internal://instructions', {
148
103
  title: 'Server Instructions',
149
104
  description: 'Usage instructions for the MCP server.',
@@ -155,6 +110,26 @@ export function registerAllResources(server, iconMeta) {
155
110
  { uri: uri.href, mimeType: 'text/markdown', text: instructions },
156
111
  ],
157
112
  }));
113
+ server.registerResource('tool-catalog', 'internal://tool-catalog', {
114
+ title: 'Tool Catalog',
115
+ description: 'Tool reference: models, params, outputs, data flow.',
116
+ mimeType: 'text/markdown',
117
+ annotations: { audience: ['assistant'], priority: 0.7 },
118
+ ...(withIconMeta(iconMeta) ?? {}),
119
+ }, (uri) => ({
120
+ contents: [
121
+ { uri: uri.href, mimeType: 'text/markdown', text: toolCatalog },
122
+ ],
123
+ }));
124
+ server.registerResource('workflows', 'internal://workflows', {
125
+ title: 'Workflows',
126
+ description: 'Recommended workflows and tool sequences.',
127
+ mimeType: 'text/markdown',
128
+ annotations: { audience: ['assistant'], priority: 0.7 },
129
+ ...(withIconMeta(iconMeta) ?? {}),
130
+ }, (uri) => ({
131
+ contents: [{ uri: uri.href, mimeType: 'text/markdown', text: workflows }],
132
+ }));
158
133
  server.registerResource('reasoning.sessions', SESSIONS_RESOURCE_URI, {
159
134
  title: 'Reasoning Sessions',
160
135
  description: 'List of active reasoning sessions with summaries. Updated in real-time as sessions progress.',
@@ -166,9 +141,7 @@ export function registerAllResources(server, iconMeta) {
166
141
  ...(withIconMeta(iconMeta) ?? {}),
167
142
  }, () => {
168
143
  const ttlMs = sessionStore.getTtlMs();
169
- const sessions = sessionStore
170
- .listSummaries()
171
- .map((session) => buildSessionSummaryFromSummary(session));
144
+ const sessions = sessionStore.listSummaries().map(buildSessionSummary);
172
145
  return {
173
146
  contents: [
174
147
  {
@@ -287,11 +260,13 @@ export function registerAllResources(server, iconMeta) {
287
260
  title: 'Reasoning Session Detail',
288
261
  description: 'Detailed view of a single reasoning session, including all thoughts and metadata.',
289
262
  mimeType: 'application/json',
263
+ annotations: { audience: ['assistant', 'user'], priority: 0.8 },
290
264
  ...(withIconMeta(iconMeta) ?? {}),
291
265
  }, (uri, variables) => {
292
266
  const sessionId = extractStringVariable(variables, 'sessionId', uri);
293
267
  const session = resolveSession(sessionId, uri);
294
- const summary = buildSessionSummary(sessionId);
268
+ const generatedThoughts = session.thoughts.length;
269
+ const summary = buildSessionSummary({ ...session, generatedThoughts });
295
270
  return {
296
271
  contents: [
297
272
  {
@@ -303,6 +278,9 @@ export function registerAllResources(server, iconMeta) {
303
278
  index: thought.index,
304
279
  content: thought.content,
305
280
  revision: thought.revision,
281
+ ...(thought.stepSummary !== undefined
282
+ ? { stepSummary: thought.stepSummary }
283
+ : {}),
306
284
  })),
307
285
  }),
308
286
  },
@@ -310,4 +288,3 @@ export function registerAllResources(server, iconMeta) {
310
288
  };
311
289
  });
312
290
  }
313
- //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ export declare function buildServerInstructions(): string;