@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.
- package/dist/engine/config.d.ts +5 -5
- package/dist/engine/config.js +14 -5
- package/dist/engine/context.d.ts +0 -2
- package/dist/engine/context.js +0 -4
- package/dist/engine/events.d.ts +1 -2
- package/dist/engine/events.js +2 -4
- package/dist/engine/heuristics.d.ts +4 -0
- package/dist/engine/heuristics.js +65 -0
- package/dist/engine/reasoner.d.ts +7 -2
- package/dist/engine/reasoner.js +41 -103
- package/dist/engine/session-store.d.ts +6 -2
- package/dist/engine/session-store.js +78 -27
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/lib/concurrency.d.ts +5 -0
- package/dist/lib/concurrency.js +17 -0
- package/dist/lib/errors.d.ts +23 -8
- package/dist/lib/errors.js +47 -6
- package/dist/lib/formatting.d.ts +13 -1
- package/dist/lib/formatting.js +18 -1
- package/dist/lib/prompt-contracts.d.ts +6 -0
- package/dist/lib/prompt-contracts.js +35 -0
- package/dist/lib/text.d.ts +0 -1
- package/dist/lib/text.js +0 -1
- package/dist/lib/tool-contracts.d.ts +15 -0
- package/dist/lib/tool-contracts.js +93 -0
- package/dist/lib/tool-response.d.ts +4 -1
- package/dist/lib/tool-response.js +3 -1
- package/dist/lib/types.d.ts +18 -1
- package/dist/lib/types.js +8 -2
- package/dist/lib/validators.d.ts +7 -2
- package/dist/lib/validators.js +30 -8
- package/dist/prompts/index.d.ts +0 -1
- package/dist/prompts/index.js +171 -74
- package/dist/prompts/templates.d.ts +2 -0
- package/dist/prompts/templates.js +106 -0
- package/dist/resources/index.d.ts +0 -1
- package/dist/resources/index.js +40 -63
- package/dist/resources/instructions.d.ts +1 -0
- package/dist/resources/instructions.js +103 -0
- package/dist/resources/tool-catalog.d.ts +1 -0
- package/dist/resources/tool-catalog.js +20 -0
- package/dist/resources/tool-info.d.ts +2 -0
- package/dist/resources/tool-info.js +35 -0
- package/dist/resources/workflows.d.ts +1 -0
- package/dist/resources/workflows.js +61 -0
- package/dist/schemas/inputs.d.ts +7 -2
- package/dist/schemas/inputs.js +39 -4
- package/dist/schemas/outputs.d.ts +32 -26
- package/dist/schemas/outputs.js +17 -14
- package/dist/server.d.ts +0 -1
- package/dist/server.js +1 -4
- package/dist/tools/index.d.ts +0 -1
- package/dist/tools/index.js +0 -1
- package/dist/tools/reasoning-think.d.ts +0 -1
- package/dist/tools/reasoning-think.js +211 -118
- package/package.json +9 -10
- package/dist/engine/config.d.ts.map +0 -1
- package/dist/engine/config.js.map +0 -1
- package/dist/engine/context.d.ts.map +0 -1
- package/dist/engine/context.js.map +0 -1
- package/dist/engine/events.d.ts.map +0 -1
- package/dist/engine/events.js.map +0 -1
- package/dist/engine/reasoner.d.ts.map +0 -1
- package/dist/engine/reasoner.js.map +0 -1
- package/dist/engine/session-store.d.ts.map +0 -1
- package/dist/engine/session-store.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/instructions.md +0 -148
- package/dist/lib/errors.d.ts.map +0 -1
- package/dist/lib/errors.js.map +0 -1
- package/dist/lib/formatting.d.ts.map +0 -1
- package/dist/lib/formatting.js.map +0 -1
- package/dist/lib/instructions.d.ts +0 -5
- package/dist/lib/instructions.d.ts.map +0 -1
- package/dist/lib/instructions.js +0 -30
- package/dist/lib/instructions.js.map +0 -1
- package/dist/lib/text.d.ts.map +0 -1
- package/dist/lib/text.js.map +0 -1
- package/dist/lib/tool-response.d.ts.map +0 -1
- package/dist/lib/tool-response.js.map +0 -1
- package/dist/lib/types.d.ts.map +0 -1
- package/dist/lib/types.js.map +0 -1
- package/dist/lib/validators.d.ts.map +0 -1
- package/dist/lib/validators.js.map +0 -1
- package/dist/prompts/index.d.ts.map +0 -1
- package/dist/prompts/index.js.map +0 -1
- package/dist/resources/index.d.ts.map +0 -1
- package/dist/resources/index.js.map +0 -1
- package/dist/schemas/inputs.d.ts.map +0 -1
- package/dist/schemas/inputs.js.map +0 -1
- package/dist/schemas/outputs.d.ts.map +0 -1
- package/dist/schemas/outputs.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.js.map +0 -1
- package/dist/tools/reasoning-think.d.ts.map +0 -1
- package/dist/tools/reasoning-think.js.map +0 -1
package/dist/prompts/index.js
CHANGED
|
@@ -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 {
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
...
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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 =
|
|
200
|
+
const text = buildRetryReasoningPrompt({
|
|
201
|
+
query,
|
|
202
|
+
level,
|
|
203
|
+
...(targetThoughts !== undefined ? { targetThoughts } : {}),
|
|
204
|
+
});
|
|
113
205
|
return createTextPrompt(text);
|
|
114
206
|
});
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
|
148
|
-
|
|
149
|
-
|
|
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,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
|
+
}
|
package/dist/resources/index.js
CHANGED
|
@@ -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 {
|
|
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
|
|
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
|
|
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
|
-
|
|
79
|
-
|
|
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 =
|
|
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
|
|
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;
|