@j0hanz/cortex-mcp 1.4.0 → 1.6.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 (43) hide show
  1. package/dist/engine/config.d.ts +4 -4
  2. package/dist/engine/config.js +6 -4
  3. package/dist/engine/context.d.ts +0 -1
  4. package/dist/engine/context.js +0 -3
  5. package/dist/engine/events.d.ts +1 -1
  6. package/dist/engine/events.js +2 -3
  7. package/dist/engine/heuristics.d.ts +4 -0
  8. package/dist/engine/heuristics.js +65 -0
  9. package/dist/engine/reasoner.d.ts +1 -1
  10. package/dist/engine/reasoner.js +22 -98
  11. package/dist/engine/session-store.d.ts +7 -1
  12. package/dist/engine/session-store.js +68 -27
  13. package/dist/lib/concurrency.d.ts +5 -0
  14. package/dist/lib/concurrency.js +17 -0
  15. package/dist/lib/errors.d.ts +23 -7
  16. package/dist/lib/errors.js +47 -5
  17. package/dist/lib/formatting.d.ts +13 -0
  18. package/dist/lib/formatting.js +18 -0
  19. package/dist/lib/prompt-contracts.d.ts +1 -9
  20. package/dist/lib/prompt-contracts.js +33 -122
  21. package/dist/lib/tool-contracts.d.ts +1 -1
  22. package/dist/lib/tool-contracts.js +85 -90
  23. package/dist/lib/tool-response.d.ts +4 -0
  24. package/dist/lib/tool-response.js +3 -0
  25. package/dist/lib/types.d.ts +17 -0
  26. package/dist/lib/types.js +8 -1
  27. package/dist/lib/validators.d.ts +12 -1
  28. package/dist/lib/validators.js +48 -7
  29. package/dist/prompts/index.js +136 -47
  30. package/dist/prompts/templates.d.ts +2 -0
  31. package/dist/prompts/templates.js +227 -0
  32. package/dist/resources/index.js +34 -60
  33. package/dist/resources/instructions.js +55 -64
  34. package/dist/resources/tool-catalog.js +10 -9
  35. package/dist/resources/tool-info.js +1 -1
  36. package/dist/resources/workflows.js +43 -42
  37. package/dist/schemas/inputs.d.ts +0 -1
  38. package/dist/schemas/inputs.js +14 -31
  39. package/dist/schemas/outputs.d.ts +29 -25
  40. package/dist/schemas/outputs.js +18 -22
  41. package/dist/server.js +11 -3
  42. package/dist/tools/reasoning-think.js +142 -158
  43. package/package.json +1 -1
@@ -4,7 +4,51 @@ const INSPECT_OPTIONS = {
4
4
  breakLength: 120,
5
5
  };
6
6
  const UNKNOWN_ERROR_MESSAGE = 'Unknown error';
7
- function isRecord(value) {
7
+ export class ReasoningError extends Error {
8
+ code;
9
+ constructor(code, message) {
10
+ super(message);
11
+ this.code = code;
12
+ this.name = 'ReasoningError';
13
+ }
14
+ }
15
+ export class SessionNotFoundError extends ReasoningError {
16
+ constructor(sessionId) {
17
+ super('E_SESSION_NOT_FOUND', `Session not found or expired: ${sessionId}`);
18
+ this.name = 'SessionNotFoundError';
19
+ }
20
+ }
21
+ export class InvalidThoughtCountError extends ReasoningError {
22
+ constructor(message) {
23
+ super('E_INVALID_THOUGHT_COUNT', message);
24
+ this.name = 'InvalidThoughtCountError';
25
+ }
26
+ }
27
+ export class InsufficientThoughtsError extends ReasoningError {
28
+ constructor(message) {
29
+ super('E_INSUFFICIENT_THOUGHTS', message);
30
+ this.name = 'InsufficientThoughtsError';
31
+ }
32
+ }
33
+ export class InvalidRunModeArgsError extends ReasoningError {
34
+ constructor(message) {
35
+ super('E_INVALID_RUN_MODE_ARGS', message);
36
+ this.name = 'InvalidRunModeArgsError';
37
+ }
38
+ }
39
+ export class ReasoningAbortedError extends ReasoningError {
40
+ constructor(message = 'Reasoning aborted') {
41
+ super('E_ABORTED', message);
42
+ this.name = 'ReasoningAbortedError';
43
+ }
44
+ }
45
+ export class ServerBusyError extends ReasoningError {
46
+ constructor(message = 'Server busy — too many concurrent reasoning tasks. Retry shortly.') {
47
+ super('E_SERVER_BUSY', message);
48
+ this.name = 'ServerBusyError';
49
+ }
50
+ }
51
+ export function isObjectRecord(value) {
8
52
  return typeof value === 'object' && value !== null;
9
53
  }
10
54
  function stringifyUnknown(value) {
@@ -17,7 +61,7 @@ function stringifyUnknown(value) {
17
61
  return inspect(value, INSPECT_OPTIONS);
18
62
  }
19
63
  function getMessageFromErrorLike(value) {
20
- if (!isRecord(value)) {
64
+ if (!isObjectRecord(value)) {
21
65
  return undefined;
22
66
  }
23
67
  return typeof value.message === 'string' ? value.message : undefined;
@@ -39,11 +83,9 @@ export function getErrorMessage(error) {
39
83
  return stringifyUnknown(error);
40
84
  }
41
85
  export function createErrorResponse(code, message) {
42
- const structured = { ok: false, error: { code, message } };
43
- const text = JSON.stringify(structured);
86
+ const text = JSON.stringify({ ok: false, error: { code, message } });
44
87
  return {
45
88
  content: [{ type: 'text', text }],
46
- structuredContent: structured,
47
89
  isError: true,
48
90
  };
49
91
  }
@@ -30,3 +30,16 @@ export declare function formatThoughtsToMarkdown(session: Readonly<Session>, ran
30
30
  start: number;
31
31
  end: number;
32
32
  }): string;
33
+ /**
34
+ * Format a standardized progress message string.
35
+ *
36
+ * Rules:
37
+ * - Start/Mid: <tool>: <context> [metadata]
38
+ * - End: <tool>: <context> [metadata] • <outcome>
39
+ */
40
+ export declare function formatProgressMessage(args: {
41
+ toolName: string;
42
+ context: string;
43
+ metadata?: string;
44
+ outcome?: string;
45
+ }): string;
@@ -21,6 +21,9 @@ export function extractPinnedSections(thoughts) {
21
21
  const byTitle = new Map();
22
22
  for (const thought of thoughts) {
23
23
  const { content } = thought;
24
+ if (!content.includes(PIN_START)) {
25
+ continue;
26
+ }
24
27
  let searchFrom = 0;
25
28
  while (searchFrom < content.length) {
26
29
  const startIdx = content.indexOf(PIN_START, searchFrom);
@@ -120,3 +123,18 @@ export function formatThoughtsToMarkdown(session, range) {
120
123
  }
121
124
  return sections.join(TRACE_SEPARATOR);
122
125
  }
126
+ /**
127
+ * Format a standardized progress message string.
128
+ *
129
+ * Rules:
130
+ * - Start/Mid: <tool>: <context> [metadata]
131
+ * - End: <tool>: <context> [metadata] • <outcome>
132
+ */
133
+ export function formatProgressMessage(args) {
134
+ const { toolName, context, metadata, outcome } = args;
135
+ const metaPart = metadata ? ` ${metadata}` : '';
136
+ if (outcome) {
137
+ return `${toolName}: ${context}${metaPart} • ${outcome}`;
138
+ }
139
+ return `${toolName}: ${context}${metaPart}`;
140
+ }
@@ -1,14 +1,6 @@
1
- import { z } from 'zod';
2
- export interface PromptArg {
3
- name: string;
4
- type: z.ZodType;
5
- description: string;
6
- required: boolean;
7
- }
8
1
  export interface PromptContract {
9
2
  name: string;
10
3
  title: string;
11
4
  description: string;
12
- args: PromptArg[];
13
5
  }
14
- export declare function getPromptContracts(): PromptContract[];
6
+ export declare function getPromptContracts(): readonly PromptContract[];
@@ -1,124 +1,35 @@
1
- import { z } from 'zod';
1
+ const PROMPT_CONTRACTS = [
2
+ {
3
+ name: 'get-help',
4
+ title: 'Get Help',
5
+ description: 'Return the server usage instructions.',
6
+ },
7
+ {
8
+ name: 'reasoning.basic',
9
+ title: 'Reasoning Basic',
10
+ description: 'Prepare a basic-depth reasoning request (3–5 thoughts).',
11
+ },
12
+ {
13
+ name: 'reasoning.normal',
14
+ title: 'Reasoning Normal',
15
+ description: 'Prepare a normal-depth reasoning request (6–10 thoughts).',
16
+ },
17
+ {
18
+ name: 'reasoning.high',
19
+ title: 'Reasoning High',
20
+ description: 'Prepare a high-depth reasoning request (15–25 thoughts).',
21
+ },
22
+ {
23
+ name: 'reasoning.continue',
24
+ title: 'Continue Reasoning',
25
+ description: 'Continue an existing reasoning session (follow-up query optional).',
26
+ },
27
+ {
28
+ name: 'reasoning.retry',
29
+ title: 'Retry Reasoning',
30
+ description: 'Retry a failed reasoning task with modified parameters.',
31
+ },
32
+ ];
2
33
  export function getPromptContracts() {
3
- return [
4
- {
5
- name: 'get-help',
6
- title: 'Get Help',
7
- description: 'Return the server usage instructions.',
8
- args: [],
9
- },
10
- {
11
- name: 'reasoning.basic',
12
- title: 'Reasoning Basic',
13
- description: 'Prepare a basic-depth reasoning request (3–5 thoughts).',
14
- args: [
15
- {
16
- name: 'query',
17
- type: z.string().min(1).max(10000),
18
- description: 'The question or problem to reason about',
19
- required: true,
20
- },
21
- {
22
- name: 'targetThoughts',
23
- type: z.number().int().min(1).max(25),
24
- description: 'Optional exact step count within the selected level range (max 25)',
25
- required: false,
26
- },
27
- ],
28
- },
29
- {
30
- name: 'reasoning.normal',
31
- title: 'Reasoning Normal',
32
- description: 'Prepare a normal-depth reasoning request (6–10 thoughts).',
33
- args: [
34
- {
35
- name: 'query',
36
- type: z.string().min(1).max(10000),
37
- description: 'The question or problem to reason about',
38
- required: true,
39
- },
40
- {
41
- name: 'targetThoughts',
42
- type: z.number().int().min(1).max(25),
43
- description: 'Optional exact step count within the selected level range (max 25)',
44
- required: false,
45
- },
46
- ],
47
- },
48
- {
49
- name: 'reasoning.high',
50
- title: 'Reasoning High',
51
- description: 'Prepare a high-depth reasoning request (15–25 thoughts).',
52
- args: [
53
- {
54
- name: 'query',
55
- type: z.string().min(1).max(10000),
56
- description: 'The question or problem to reason about',
57
- required: true,
58
- },
59
- {
60
- name: 'targetThoughts',
61
- type: z.number().int().min(1).max(25),
62
- description: 'Optional exact step count within the selected level range (max 25)',
63
- required: false,
64
- },
65
- ],
66
- },
67
- {
68
- name: 'reasoning.continue',
69
- title: 'Continue Reasoning',
70
- description: 'Continue an existing reasoning session (follow-up query optional).',
71
- args: [
72
- {
73
- name: 'sessionId',
74
- type: z.string().min(1).max(128),
75
- description: 'Existing session ID to continue',
76
- required: true,
77
- },
78
- {
79
- name: 'query',
80
- type: z.string().min(1).max(10000),
81
- description: 'Follow-up query for the existing session',
82
- required: false,
83
- },
84
- {
85
- name: 'level',
86
- type: z.enum(['basic', 'normal', 'high']),
87
- description: 'Optional in the tool; session level is used if provided',
88
- required: false,
89
- },
90
- {
91
- name: 'targetThoughts',
92
- type: z.number().int().min(1).max(25),
93
- description: 'Optional exact step count within the selected level range (max 25)',
94
- required: false,
95
- },
96
- ],
97
- },
98
- {
99
- name: 'reasoning.retry',
100
- title: 'Retry Reasoning',
101
- description: 'Retry a failed reasoning task with modified parameters.',
102
- args: [
103
- {
104
- name: 'query',
105
- type: z.string().min(1).max(10000),
106
- description: 'The original or modified query',
107
- required: true,
108
- },
109
- {
110
- name: 'level',
111
- type: z.enum(['basic', 'normal', 'high']),
112
- description: 'The reasoning level to use',
113
- required: true,
114
- },
115
- {
116
- name: 'targetThoughts',
117
- type: z.number().int().min(1).max(25),
118
- description: 'Optional exact step count',
119
- required: false,
120
- },
121
- ],
122
- },
123
- ];
34
+ return PROMPT_CONTRACTS;
124
35
  }
@@ -12,4 +12,4 @@ export interface ToolContract {
12
12
  }[];
13
13
  outputShape: string;
14
14
  }
15
- export declare function getToolContracts(): ToolContract[];
15
+ export declare function getToolContracts(): readonly ToolContract[];
@@ -1,92 +1,87 @@
1
+ const TOOL_CONTRACTS = [
2
+ {
3
+ name: 'reasoning_think',
4
+ purpose: 'Structured multi-step reasoning tool. Decomposes analysis into sequential thought steps stored in a persistent session trace.',
5
+ model: 'none (engine)',
6
+ timeoutMs: 0,
7
+ maxOutputTokens: 0,
8
+ params: [
9
+ {
10
+ name: 'query',
11
+ type: 'string',
12
+ required: false,
13
+ constraints: '1-10,000 chars',
14
+ },
15
+ {
16
+ name: 'level',
17
+ type: 'string',
18
+ required: false,
19
+ constraints: 'basic | normal | high',
20
+ },
21
+ {
22
+ name: 'runMode',
23
+ type: 'string',
24
+ required: false,
25
+ constraints: 'step | run_to_completion',
26
+ },
27
+ {
28
+ name: 'thought',
29
+ type: 'string | string[]',
30
+ required: false,
31
+ constraints: '1-100,000 chars',
32
+ },
33
+ {
34
+ name: 'targetThoughts',
35
+ type: 'number',
36
+ required: false,
37
+ constraints: '1-25',
38
+ },
39
+ {
40
+ name: 'sessionId',
41
+ type: 'string',
42
+ required: false,
43
+ constraints: '1-128 chars',
44
+ },
45
+ {
46
+ name: 'observation',
47
+ type: 'string',
48
+ required: false,
49
+ constraints: 'optional',
50
+ },
51
+ {
52
+ name: 'hypothesis',
53
+ type: 'string',
54
+ required: false,
55
+ constraints: 'optional',
56
+ },
57
+ {
58
+ name: 'evaluation',
59
+ type: 'string',
60
+ required: false,
61
+ constraints: 'optional',
62
+ },
63
+ {
64
+ name: 'step_summary',
65
+ type: 'string',
66
+ required: false,
67
+ constraints: 'optional',
68
+ },
69
+ {
70
+ name: 'is_conclusion',
71
+ type: 'boolean',
72
+ required: false,
73
+ constraints: 'optional',
74
+ },
75
+ {
76
+ name: 'rollback_to_step',
77
+ type: 'number',
78
+ required: false,
79
+ constraints: 'optional',
80
+ },
81
+ ],
82
+ outputShape: '{ok, result: {sessionId, level, status, thoughts[], generatedThoughts, requestedThoughts, totalThoughts, remainingThoughts, tokenBudget, tokensUsed, ttlMs, expiresAt, createdAt, updatedAt, summary}}',
83
+ },
84
+ ];
1
85
  export function getToolContracts() {
2
- return [
3
- {
4
- name: 'reasoning_think',
5
- purpose: 'Structured multi-step reasoning tool. Decomposes analysis into sequential thought steps stored in a persistent session trace.',
6
- model: 'none (engine)',
7
- timeoutMs: 0,
8
- maxOutputTokens: 0,
9
- params: [
10
- {
11
- name: 'query',
12
- type: 'string',
13
- required: false,
14
- constraints: '1-10,000 chars',
15
- },
16
- {
17
- name: 'level',
18
- type: 'string',
19
- required: false,
20
- constraints: 'basic | normal | high',
21
- },
22
- {
23
- name: 'runMode',
24
- type: 'string',
25
- required: false,
26
- constraints: 'step | run_to_completion',
27
- },
28
- {
29
- name: 'thought',
30
- type: 'string | string[]',
31
- required: false,
32
- constraints: '1-100,000 chars',
33
- },
34
- {
35
- name: 'thoughts',
36
- type: 'string[]',
37
- required: false,
38
- constraints: 'optional',
39
- },
40
- {
41
- name: 'targetThoughts',
42
- type: 'number',
43
- required: false,
44
- constraints: '1-25',
45
- },
46
- {
47
- name: 'sessionId',
48
- type: 'string',
49
- required: false,
50
- constraints: '1-128 chars',
51
- },
52
- {
53
- name: 'observation',
54
- type: 'string',
55
- required: false,
56
- constraints: 'optional',
57
- },
58
- {
59
- name: 'hypothesis',
60
- type: 'string',
61
- required: false,
62
- constraints: 'optional',
63
- },
64
- {
65
- name: 'evaluation',
66
- type: 'string',
67
- required: false,
68
- constraints: 'optional',
69
- },
70
- {
71
- name: 'step_summary',
72
- type: 'string',
73
- required: false,
74
- constraints: 'optional',
75
- },
76
- {
77
- name: 'is_conclusion',
78
- type: 'boolean',
79
- required: false,
80
- constraints: 'optional',
81
- },
82
- {
83
- name: 'rollback_to_step',
84
- type: 'number',
85
- required: false,
86
- constraints: 'optional',
87
- },
88
- ],
89
- outputShape: '{ok, result: {sessionId, level, status, thoughts[], generatedThoughts, requestedThoughts, totalThoughts, remainingThoughts, tokenBudget, tokensUsed, ttlMs, expiresAt, createdAt, updatedAt, summary}}',
90
- },
91
- ];
86
+ return TOOL_CONTRACTS;
92
87
  }
@@ -1,4 +1,8 @@
1
1
  import type { ContentBlock, TextResourceContents } from '@modelcontextprotocol/sdk/types.js';
2
+ import type { IconMeta } from './types.js';
3
+ export declare function withIconMeta(iconMeta?: IconMeta): {
4
+ icons: IconMeta[];
5
+ } | undefined;
2
6
  export declare function createToolResponse<T extends object>(structured: T, embeddedResource?: TextResourceContents): {
3
7
  content: ContentBlock[];
4
8
  structuredContent: T;
@@ -1,3 +1,6 @@
1
+ export function withIconMeta(iconMeta) {
2
+ return iconMeta ? { icons: [iconMeta] } : undefined;
3
+ }
1
4
  function createStructuredTextBlock(structured) {
2
5
  return { type: 'text', text: JSON.stringify(structured) };
3
6
  }
@@ -1,6 +1,23 @@
1
1
  export type ReasoningLevel = 'basic' | 'normal' | 'high';
2
+ export declare const REASONING_LEVELS: readonly ["basic", "normal", "high"];
3
+ /** Shared level bounds — single source of truth for min/maxThoughts per level. */
4
+ export declare const LEVEL_BOUNDS: {
5
+ readonly basic: {
6
+ readonly minThoughts: 3;
7
+ readonly maxThoughts: 5;
8
+ };
9
+ readonly normal: {
10
+ readonly minThoughts: 6;
11
+ readonly maxThoughts: 10;
12
+ };
13
+ readonly high: {
14
+ readonly minThoughts: 15;
15
+ readonly maxThoughts: 25;
16
+ };
17
+ };
2
18
  export type ReasoningRunMode = 'step' | 'run_to_completion';
3
19
  export type SessionStatus = 'active' | 'completed' | 'cancelled';
20
+ export declare const SESSION_STATUSES: readonly ["active", "completed", "cancelled"];
4
21
  interface Timestamped {
5
22
  readonly createdAt: number;
6
23
  readonly updatedAt: number;
package/dist/lib/types.js CHANGED
@@ -1 +1,8 @@
1
- export {};
1
+ export const REASONING_LEVELS = ['basic', 'normal', 'high'];
2
+ /** Shared level bounds — single source of truth for min/maxThoughts per level. */
3
+ export const LEVEL_BOUNDS = {
4
+ basic: { minThoughts: 3, maxThoughts: 5 },
5
+ normal: { minThoughts: 6, maxThoughts: 10 },
6
+ high: { minThoughts: 15, maxThoughts: 25 },
7
+ };
8
+ export const SESSION_STATUSES = ['active', 'completed', 'cancelled'];
@@ -1,6 +1,17 @@
1
- import type { ReasoningLevel } from './types.js';
1
+ import { type ReasoningLevel } from './types.js';
2
2
  export declare function getThoughtBounds(level: ReasoningLevel): {
3
3
  minThoughts: number;
4
4
  maxThoughts: number;
5
5
  };
6
6
  export declare function getTargetThoughtsError(level: ReasoningLevel, targetThoughts: number): string | undefined;
7
+ /**
8
+ * Parse a positive integer from an environment variable, returning `fallback`
9
+ * if the variable is absent or invalid. Values below `minimum` also fall back.
10
+ */
11
+ export declare function parsePositiveIntEnv(name: string, fallback: number, minimum?: number): number;
12
+ /**
13
+ * Parse a boolean from an environment variable, returning `fallback` when absent
14
+ * or when the value is not a recognized boolean literal.
15
+ */
16
+ export declare function parseBooleanEnv(name: string, fallback: boolean): boolean;
17
+ export declare function collectPrefixMatches(candidates: readonly string[], value: string, limit: number): string[];
@@ -1,14 +1,9 @@
1
- /** Level-specific thought count boundaries for validation. */
2
- const THOUGHT_BOUNDS = Object.freeze({
3
- basic: Object.freeze({ minThoughts: 3, maxThoughts: 5 }),
4
- normal: Object.freeze({ minThoughts: 6, maxThoughts: 10 }),
5
- high: Object.freeze({ minThoughts: 15, maxThoughts: 25 }),
6
- });
1
+ import { LEVEL_BOUNDS } from './types.js';
7
2
  function isOutOfBounds(value, bounds) {
8
3
  return value < bounds.minThoughts || value > bounds.maxThoughts;
9
4
  }
10
5
  export function getThoughtBounds(level) {
11
- return THOUGHT_BOUNDS[level];
6
+ return LEVEL_BOUNDS[level];
12
7
  }
13
8
  export function getTargetThoughtsError(level, targetThoughts) {
14
9
  if (!Number.isInteger(targetThoughts)) {
@@ -20,3 +15,49 @@ export function getTargetThoughtsError(level, targetThoughts) {
20
15
  }
21
16
  return undefined;
22
17
  }
18
+ /**
19
+ * Parse a positive integer from an environment variable, returning `fallback`
20
+ * if the variable is absent or invalid. Values below `minimum` also fall back.
21
+ */
22
+ export function parsePositiveIntEnv(name, fallback, minimum = 1) {
23
+ const raw = process.env[name];
24
+ if (raw === undefined) {
25
+ return fallback;
26
+ }
27
+ const parsed = Number.parseInt(raw, 10);
28
+ if (!Number.isInteger(parsed) || parsed < minimum) {
29
+ return fallback;
30
+ }
31
+ return parsed;
32
+ }
33
+ /**
34
+ * Parse a boolean from an environment variable, returning `fallback` when absent
35
+ * or when the value is not a recognized boolean literal.
36
+ */
37
+ export function parseBooleanEnv(name, fallback) {
38
+ const raw = process.env[name];
39
+ if (raw === undefined) {
40
+ return fallback;
41
+ }
42
+ const normalized = raw.trim().toLowerCase();
43
+ if (['1', 'true', 'yes', 'on'].includes(normalized)) {
44
+ return true;
45
+ }
46
+ if (['0', 'false', 'no', 'off'].includes(normalized)) {
47
+ return false;
48
+ }
49
+ return fallback;
50
+ }
51
+ export function collectPrefixMatches(candidates, value, limit) {
52
+ const results = [];
53
+ for (const candidate of candidates) {
54
+ if (!candidate.startsWith(value)) {
55
+ continue;
56
+ }
57
+ results.push(candidate);
58
+ if (results.length >= limit) {
59
+ break;
60
+ }
61
+ }
62
+ return results;
63
+ }