@productbrain/mcp 0.0.1-beta.8702 → 0.0.1-beta.8719
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/{chunk-FZWP45AU.js → chunk-2UJDIWEX.js} +180 -5
- package/dist/chunk-2UJDIWEX.js.map +1 -0
- package/dist/{chunk-RPKS4G3K.js → chunk-ZOYUYCKV.js} +109 -222
- package/dist/chunk-ZOYUYCKV.js.map +1 -0
- package/dist/cli/index.js +1 -1
- package/dist/http.js +4 -2
- package/dist/http.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/{setup-YCYGYJNK.js → setup-SF4Z3IBZ.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-FZWP45AU.js.map +0 -1
- package/dist/chunk-RPKS4G3K.js.map +0 -1
- /package/dist/{setup-YCYGYJNK.js.map → setup-SF4Z3IBZ.js.map} +0 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
KernelCallError,
|
|
3
|
+
PASS_SESSION_ID,
|
|
3
4
|
cacheScope,
|
|
5
|
+
classifyError,
|
|
4
6
|
closeAgentSession,
|
|
5
7
|
formatGatewaySeamSummary,
|
|
6
8
|
getAgentSessionId,
|
|
@@ -15,10 +17,13 @@ import {
|
|
|
15
17
|
kernelCallEnvelope,
|
|
16
18
|
kernelMutation,
|
|
17
19
|
kernelQuery,
|
|
20
|
+
orientationRefusal,
|
|
18
21
|
recordSessionActivity,
|
|
19
22
|
requireActiveSession,
|
|
20
23
|
requireVendorTriageAccess,
|
|
21
24
|
requireWriteAccess,
|
|
25
|
+
retryIsFutile,
|
|
26
|
+
runWithSeatedSession,
|
|
22
27
|
runWithToolContext,
|
|
23
28
|
setSessionOriented,
|
|
24
29
|
startAgentSession,
|
|
@@ -37,223 +42,16 @@ import {
|
|
|
37
42
|
trackQualityCheck,
|
|
38
43
|
trackQualityVerdict,
|
|
39
44
|
trackSessionCaptureRate,
|
|
40
|
-
trackZeroCaptureAuditFired
|
|
41
|
-
|
|
45
|
+
trackZeroCaptureAuditFired,
|
|
46
|
+
validateCallerSession
|
|
47
|
+
} from "./chunk-2UJDIWEX.js";
|
|
42
48
|
|
|
43
49
|
// src/server.ts
|
|
44
50
|
import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
45
|
-
|
|
46
|
-
// src/lib/createEntryPreviewResult.ts
|
|
47
|
-
async function resolvePreviewPreflight(result2, links) {
|
|
48
|
-
if (!links?.length) return result2.preflight;
|
|
49
|
-
try {
|
|
50
|
-
return await kernelQuery("chain.preflightEntry", {
|
|
51
|
-
collectionSlug: result2.collection,
|
|
52
|
-
canonicalKey: result2.preflight.kind ?? void 0,
|
|
53
|
-
links: links.map((link) => ({ entryId: link.to, type: link.type }))
|
|
54
|
-
});
|
|
55
|
-
} catch {
|
|
56
|
-
return {
|
|
57
|
-
...result2.preflight,
|
|
58
|
-
checks: [
|
|
59
|
-
...result2.preflight.checks,
|
|
60
|
-
{
|
|
61
|
-
id: "link-recheck",
|
|
62
|
-
status: "not_checked",
|
|
63
|
-
blocking: false,
|
|
64
|
-
message: "Selected links could not be rechecked now; target existence and relation policy are checked at capture."
|
|
65
|
-
}
|
|
66
|
-
]
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
function formatPreflightCheck(check) {
|
|
71
|
-
if (check.status === "missing" && check.blocking) return `Blocked: ${check.message}`;
|
|
72
|
-
if (check.status === "incomplete") return `Unverified: ${check.message}`;
|
|
73
|
-
if (check.status === "not_checked") return `Checked later: ${check.message}`;
|
|
74
|
-
return check.message;
|
|
75
|
-
}
|
|
76
|
-
function renderEntryPreflight(preflight) {
|
|
77
|
-
return [
|
|
78
|
-
...preflight.checks.map(formatPreflightCheck),
|
|
79
|
-
...preflight.requiredFields.length ? [`Required information: ${preflight.requiredFields.join(", ")}.`] : [],
|
|
80
|
-
preflight.notice
|
|
81
|
-
];
|
|
82
|
-
}
|
|
83
|
-
function isPreviewCreateEntryResult(result2) {
|
|
84
|
-
return "preview" in result2 && result2.preview === true;
|
|
85
|
-
}
|
|
86
|
-
function resolveEffectiveCollection(result2, preview, fallbackCollection) {
|
|
87
|
-
return preview && result2 && isPreviewCreateEntryResult(result2) ? result2.collection ?? fallbackCollection : fallbackCollection;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// src/tools/entries.ts
|
|
91
|
-
import { z as z11 } from "zod/v3";
|
|
51
|
+
import { z as z34 } from "zod/v3";
|
|
92
52
|
|
|
93
53
|
// src/envelope.ts
|
|
94
54
|
import { z } from "zod/v3";
|
|
95
|
-
|
|
96
|
-
// src/errors.ts
|
|
97
|
-
var MCP_CALL_ERROR_RE = /^MCP call "([^"]+)" failed \(\d+\): (.+)$/s;
|
|
98
|
-
function asKernelCallError(err) {
|
|
99
|
-
if (!(err instanceof Error)) return void 0;
|
|
100
|
-
const e = err;
|
|
101
|
-
if (typeof e.code !== "string") return void 0;
|
|
102
|
-
const match = MCP_CALL_ERROR_RE.exec(e.message);
|
|
103
|
-
if (!match) return void 0;
|
|
104
|
-
return {
|
|
105
|
-
code: e.code,
|
|
106
|
-
fn: match[1],
|
|
107
|
-
cleanMessage: match[2],
|
|
108
|
-
// NOTE: match[2] not match[1] — group 1 is now fn scope (DEC-798)
|
|
109
|
-
missingRequiredFields: Array.isArray(e.missingRequiredFields) ? e.missingRequiredFields : void 0,
|
|
110
|
-
fieldErrors: Array.isArray(e.fieldErrors) ? e.fieldErrors : void 0
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
var GateError = class extends Error {
|
|
114
|
-
constructor(message) {
|
|
115
|
-
super(message);
|
|
116
|
-
this.name = "GateError";
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
var BackendError = class extends Error {
|
|
120
|
-
constructor(message) {
|
|
121
|
-
super(message);
|
|
122
|
-
this.name = "BackendError";
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
var ValidationError = class extends Error {
|
|
126
|
-
constructor(message) {
|
|
127
|
-
super(message);
|
|
128
|
-
this.name = "ValidationError";
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
var GATE_PATTERNS = [
|
|
132
|
-
{
|
|
133
|
-
pattern: /session required|no active.*session|call.*session.*start/i,
|
|
134
|
-
code: "SESSION_REQUIRED",
|
|
135
|
-
recovery: "Start an agent session first.",
|
|
136
|
-
action: { tool: "session", description: "Start session", parameters: { action: "start" } }
|
|
137
|
-
},
|
|
138
|
-
{
|
|
139
|
-
pattern: /session.*closed/i,
|
|
140
|
-
code: "SESSION_CLOSED",
|
|
141
|
-
recovery: "Start a new session.",
|
|
142
|
-
action: { tool: "session", description: "Start new session", parameters: { action: "start" } }
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
pattern: /orientation required|call.*orient/i,
|
|
146
|
-
code: "ORIENTATION_REQUIRED",
|
|
147
|
-
recovery: "Run orient with a task, then retry.",
|
|
148
|
-
action: { tool: "orient", description: "Orient with a task", parameters: { action: "task" } }
|
|
149
|
-
},
|
|
150
|
-
{
|
|
151
|
-
pattern: /read.?only.*scope/i,
|
|
152
|
-
code: "READONLY_SCOPE",
|
|
153
|
-
recovery: "This API key cannot write. Use a readwrite key.",
|
|
154
|
-
action: { tool: "workspace", description: "Check key scope", parameters: { action: "whoami" } }
|
|
155
|
-
}
|
|
156
|
-
];
|
|
157
|
-
function orientationRefusal(err) {
|
|
158
|
-
const gate = GATE_PATTERNS.find((g) => g.code === "ORIENTATION_REQUIRED");
|
|
159
|
-
const mcpErr = asKernelCallError(err);
|
|
160
|
-
const message = mcpErr?.cleanMessage ?? (err instanceof Error ? err.message : String(err));
|
|
161
|
-
if (!gate || !gate.pattern.test(message)) return null;
|
|
162
|
-
return { code: gate.code, message, recovery: gate.recovery, availableActions: [gate.action] };
|
|
163
|
-
}
|
|
164
|
-
function classifyError(err) {
|
|
165
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
166
|
-
if (err instanceof GateError) {
|
|
167
|
-
return {
|
|
168
|
-
code: "PERMISSION_DENIED",
|
|
169
|
-
message,
|
|
170
|
-
recovery: "Check permissions or use a readwrite API key.",
|
|
171
|
-
availableActions: [{ tool: "workspace", description: "Check key scope", parameters: { action: "whoami" } }]
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
if (err instanceof BackendError) {
|
|
175
|
-
return { code: "BACKEND_ERROR", message, recovery: "Retry the operation or check backend health." };
|
|
176
|
-
}
|
|
177
|
-
if (err instanceof ValidationError) {
|
|
178
|
-
return { code: "VALIDATION_ERROR", message };
|
|
179
|
-
}
|
|
180
|
-
const mcpErr = asKernelCallError(err);
|
|
181
|
-
if (mcpErr) {
|
|
182
|
-
const { code: convexCode, fn, cleanMessage, missingRequiredFields, fieldErrors } = mcpErr;
|
|
183
|
-
if (convexCode === "VALIDATION_FAILED") {
|
|
184
|
-
const hasDiagnostics = missingRequiredFields?.length || fieldErrors?.length;
|
|
185
|
-
return {
|
|
186
|
-
code: "VALIDATION_ERROR",
|
|
187
|
-
fn,
|
|
188
|
-
message: cleanMessage,
|
|
189
|
-
...hasDiagnostics ? {
|
|
190
|
-
diagnostics: {
|
|
191
|
-
...missingRequiredFields?.length ? { missingRequiredFields } : {},
|
|
192
|
-
...fieldErrors?.length ? { fieldErrors } : {}
|
|
193
|
-
}
|
|
194
|
-
} : {}
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
if (convexCode === "NOT_FOUND") {
|
|
198
|
-
return {
|
|
199
|
-
code: "NOT_FOUND",
|
|
200
|
-
fn,
|
|
201
|
-
message: cleanMessage,
|
|
202
|
-
recovery: "Use entries action=search to find the correct ID.",
|
|
203
|
-
availableActions: [{ tool: "entries", description: "Search entries", parameters: { action: "search" } }]
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
if (convexCode?.startsWith("AUTHZ_")) {
|
|
207
|
-
return {
|
|
208
|
-
code: "PERMISSION_DENIED",
|
|
209
|
-
fn,
|
|
210
|
-
message: cleanMessage,
|
|
211
|
-
recovery: "Check permissions or use a readwrite API key.",
|
|
212
|
-
availableActions: [{ tool: "workspace", description: "Check key scope", parameters: { action: "whoami" } }]
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
if (convexCode === "WORKSPACE_SLUG_TAKEN" || convexCode === "PERSON_ALREADY_EXISTS") {
|
|
216
|
-
return {
|
|
217
|
-
code: "DUPLICATE",
|
|
218
|
-
fn,
|
|
219
|
-
message: cleanMessage,
|
|
220
|
-
recovery: "Use entries action=get to inspect the existing entry.",
|
|
221
|
-
availableActions: [{ tool: "entries", description: "Get entry", parameters: { action: "get" } }]
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
if (convexCode === "rate-limited") {
|
|
225
|
-
return {
|
|
226
|
-
code: "rate-limited",
|
|
227
|
-
fn,
|
|
228
|
-
message: cleanMessage,
|
|
229
|
-
recovery: "Rate limit hit for this API key. Wait the number of seconds in the message (Retry-After), then retry the same call unchanged \u2014 it is safe to retry."
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
return { code: convexCode, fn, message: cleanMessage };
|
|
233
|
-
}
|
|
234
|
-
for (const { pattern, code, recovery, action } of GATE_PATTERNS) {
|
|
235
|
-
if (pattern.test(message)) {
|
|
236
|
-
return { code, message, recovery, availableActions: [action] };
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
if (err?.name === "GatewayTimeoutError") {
|
|
240
|
-
const mayHaveLanded = err.mayHaveLanded === true;
|
|
241
|
-
return {
|
|
242
|
-
code: "GATEWAY_TIMEOUT",
|
|
243
|
-
message,
|
|
244
|
-
recovery: mayHaveLanded ? "The server was reachable and may have completed this write \u2014 verify before retrying, since a blind retry can duplicate it." : "The server was reachable but did not answer in time. Nothing was written \u2014 safe to retry."
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
if (/network error|fetch failed|ECONNREFUSED|ETIMEDOUT/i.test(message)) {
|
|
248
|
-
return { code: "BACKEND_UNAVAILABLE", message, recovery: "Retry in a few seconds." };
|
|
249
|
-
}
|
|
250
|
-
return { code: "INTERNAL_ERROR", message: message || "An unexpected error occurred." };
|
|
251
|
-
}
|
|
252
|
-
function retryIsFutile(code) {
|
|
253
|
-
return code === "VALIDATION_ERROR";
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// src/envelope.ts
|
|
257
55
|
function success(summary, data, next) {
|
|
258
56
|
return {
|
|
259
57
|
ok: true,
|
|
@@ -376,6 +174,53 @@ function orientationRefusalResult(err, lines) {
|
|
|
376
174
|
}
|
|
377
175
|
var ORIENT_REQUIRED_NOTE = "workflow checkpoints and organisation-structure writes (membership, ownership, parent) still require an oriented session.";
|
|
378
176
|
|
|
177
|
+
// src/lib/createEntryPreviewResult.ts
|
|
178
|
+
async function resolvePreviewPreflight(result2, links) {
|
|
179
|
+
if (!links?.length) return result2.preflight;
|
|
180
|
+
try {
|
|
181
|
+
return await kernelQuery("chain.preflightEntry", {
|
|
182
|
+
collectionSlug: result2.collection,
|
|
183
|
+
canonicalKey: result2.preflight.kind ?? void 0,
|
|
184
|
+
links: links.map((link) => ({ entryId: link.to, type: link.type }))
|
|
185
|
+
});
|
|
186
|
+
} catch {
|
|
187
|
+
return {
|
|
188
|
+
...result2.preflight,
|
|
189
|
+
checks: [
|
|
190
|
+
...result2.preflight.checks,
|
|
191
|
+
{
|
|
192
|
+
id: "link-recheck",
|
|
193
|
+
status: "not_checked",
|
|
194
|
+
blocking: false,
|
|
195
|
+
message: "Selected links could not be rechecked now; target existence and relation policy are checked at capture."
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function formatPreflightCheck(check) {
|
|
202
|
+
if (check.status === "missing" && check.blocking) return `Blocked: ${check.message}`;
|
|
203
|
+
if (check.status === "incomplete") return `Unverified: ${check.message}`;
|
|
204
|
+
if (check.status === "not_checked") return `Checked later: ${check.message}`;
|
|
205
|
+
return check.message;
|
|
206
|
+
}
|
|
207
|
+
function renderEntryPreflight(preflight) {
|
|
208
|
+
return [
|
|
209
|
+
...preflight.checks.map(formatPreflightCheck),
|
|
210
|
+
...preflight.requiredFields.length ? [`Required information: ${preflight.requiredFields.join(", ")}.`] : [],
|
|
211
|
+
preflight.notice
|
|
212
|
+
];
|
|
213
|
+
}
|
|
214
|
+
function isPreviewCreateEntryResult(result2) {
|
|
215
|
+
return "preview" in result2 && result2.preview === true;
|
|
216
|
+
}
|
|
217
|
+
function resolveEffectiveCollection(result2, preview, fallbackCollection) {
|
|
218
|
+
return preview && result2 && isPreviewCreateEntryResult(result2) ? result2.collection ?? fallbackCollection : fallbackCollection;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// src/tools/entries.ts
|
|
222
|
+
import { z as z11 } from "zod/v3";
|
|
223
|
+
|
|
379
224
|
// src/tools/knowledge-helpers.ts
|
|
380
225
|
import { z as z2 } from "zod/v3";
|
|
381
226
|
|
|
@@ -11738,14 +11583,16 @@ var sessionSchema = z24.object({
|
|
|
11738
11583
|
),
|
|
11739
11584
|
// DEC-1715 (WP-753 O1a)
|
|
11740
11585
|
role: z24.string().max(200).optional().describe(
|
|
11741
|
-
"For 'start': role to act as this session \u2014 only needed if you hold more than one; the error lists your roles if omitted."
|
|
11586
|
+
"For 'start': role to act as this session, by the '<PREFIX>-<n>' id a start reply prints beside the role name \u2014 only needed if you hold more than one; the error lists your roles if omitted."
|
|
11742
11587
|
)
|
|
11743
11588
|
});
|
|
11744
11589
|
var sessionStartVariant = z24.object({
|
|
11745
11590
|
action: z24.literal("start"),
|
|
11746
11591
|
// DEC-1715 (WP-753 O1a): the role to act as this session — only needed when the person holds
|
|
11747
11592
|
// more than one role; the server refuses and lists the choices when it's required and omitted.
|
|
11748
|
-
role: z24.string().max(200).optional().describe(
|
|
11593
|
+
role: z24.string().max(200).optional().describe(
|
|
11594
|
+
"Role to act as this session, by the '<PREFIX>-<n>' id a start reply prints beside the role name \u2014 only needed if you hold more than one; the error lists your roles if omitted."
|
|
11595
|
+
)
|
|
11749
11596
|
});
|
|
11750
11597
|
var sessionCloseVariant = z24.object({ action: z24.literal("close") });
|
|
11751
11598
|
var sessionStatusVariant = z24.object({ action: z24.literal("status") });
|
|
@@ -11810,10 +11657,10 @@ async function handleStart2(data) {
|
|
|
11810
11657
|
const result2 = await startAgentSession(data?.role);
|
|
11811
11658
|
const lines = [];
|
|
11812
11659
|
lines.push(
|
|
11813
|
-
`Session ${result2.sessionId} active. Initiated by ${result2.initiatedBy}. Workspace ${result2.workspaceName}. Scope: ${result2.toolsScope}. Orient with a task before substantive work: it is advice for captures, relations, accepts, ticks and questions; workflow checkpoints and organisation-structure writes still require it.`
|
|
11660
|
+
`Session ${result2.sessionId} active. Pass sessionId="${result2.sessionId}" on every write: the connection does not remember your session between calls. Initiated by ${result2.initiatedBy}. Workspace ${result2.workspaceName}. Scope: ${result2.toolsScope}. Orient with a task before substantive work: it is advice for captures, relations, accepts, ticks and questions; workflow checkpoints and organisation-structure writes still require it.`
|
|
11814
11661
|
);
|
|
11815
11662
|
if (result2.roleEntryId) {
|
|
11816
|
-
lines.push(`Acting as role: ${result2.roleName ?? result2.roleEntryId}.`);
|
|
11663
|
+
lines.push(`Acting as role: ${result2.roleName ?? result2.roleEntryId}${result2.roleRef ? ` (${result2.roleRef})` : ""}.`);
|
|
11817
11664
|
}
|
|
11818
11665
|
lines.push(FEEDBACK_SUBMIT_HINT);
|
|
11819
11666
|
const questionNotice = result2.notices?.find((n) => n.source === QUESTION_ACK_GATE_SOURCE_ID);
|
|
@@ -11829,7 +11676,7 @@ async function handleStart2(data) {
|
|
|
11829
11676
|
openQuestionsTruncated: questionNotice?.payload?.truncated === true,
|
|
11830
11677
|
surface: "mcp"
|
|
11831
11678
|
}));
|
|
11832
|
-
const summary = `Session ${result2.sessionId} active. Workspace ${result2.workspaceName}. Orient with a task first; workflow checkpoints and organisation-structure writes still require it.`;
|
|
11679
|
+
const summary = `Session ${result2.sessionId} active. Workspace ${result2.workspaceName}. Pass sessionId="${result2.sessionId}" on every write. Orient with a task first; workflow checkpoints and organisation-structure writes still require it.`;
|
|
11833
11680
|
return {
|
|
11834
11681
|
content: [{ type: "text", text: lines.join("\n") }],
|
|
11835
11682
|
structuredContent: success(summary, {
|
|
@@ -11839,6 +11686,8 @@ async function handleStart2(data) {
|
|
|
11839
11686
|
toolsScope: result2.toolsScope,
|
|
11840
11687
|
roleEntryId: result2.roleEntryId,
|
|
11841
11688
|
roleName: result2.roleName,
|
|
11689
|
+
roleRef: result2.roleRef,
|
|
11690
|
+
// TASK-583
|
|
11842
11691
|
feedbackQueueNew: result2.feedbackQueueNew,
|
|
11843
11692
|
feedbackOldestNewAt: result2.feedbackOldestNewAt,
|
|
11844
11693
|
notices: result2.notices ?? [],
|
|
@@ -11852,17 +11701,18 @@ async function handleStart2(data) {
|
|
|
11852
11701
|
// the guided-setup entrypoint this session just opened write access for.
|
|
11853
11702
|
// FEAT-1409 spine core: notice next-actions are appended through this SAME existing
|
|
11854
11703
|
// `next` field (HARD CONSTRAINT 2) — never a parallel action list.
|
|
11855
|
-
|
|
11704
|
+
// TASK-583 (DEC-1907): every next action carries the sessionId, since the next call may arrive on a new connection.
|
|
11705
|
+
}, [{ tool: "orient", description: "Orient session", parameters: { action: "start" } }, ...noticesToNextActions(result2.notices)].map((next) => ({ ...next, parameters: { ...next.parameters, sessionId: result2.sessionId } })))
|
|
11856
11706
|
};
|
|
11857
11707
|
}
|
|
11858
11708
|
async function handleClose() {
|
|
11859
11709
|
const sessionId = getAgentSessionId();
|
|
11860
11710
|
if (!sessionId) {
|
|
11861
11711
|
return failureResult(
|
|
11862
|
-
|
|
11712
|
+
`No active agent session to close. Pass the sessionId to close as \`sessionId\`. ${PASS_SESSION_ID}`,
|
|
11863
11713
|
"SESSION_REQUIRED",
|
|
11864
11714
|
"No active agent session to close.",
|
|
11865
|
-
|
|
11715
|
+
PASS_SESSION_ID,
|
|
11866
11716
|
[{ tool: "session", description: "Start a session", parameters: { action: "start" } }]
|
|
11867
11717
|
);
|
|
11868
11718
|
}
|
|
@@ -11953,10 +11803,10 @@ async function handleStatus() {
|
|
|
11953
11803
|
const sessionId = getAgentSessionId();
|
|
11954
11804
|
if (!sessionId) {
|
|
11955
11805
|
return failureResult(
|
|
11956
|
-
|
|
11806
|
+
`No active agent session on this connection. Pass your sessionId as \`sessionId\` to read it. ${PASS_SESSION_ID}`,
|
|
11957
11807
|
"SESSION_REQUIRED",
|
|
11958
11808
|
"No active agent session.",
|
|
11959
|
-
|
|
11809
|
+
PASS_SESSION_ID,
|
|
11960
11810
|
[{ tool: "session", description: "Start a session", parameters: { action: "start" } }]
|
|
11961
11811
|
);
|
|
11962
11812
|
}
|
|
@@ -11977,6 +11827,7 @@ async function handleStatus() {
|
|
|
11977
11827
|
const modified = session.entriesModified?.length ?? 0;
|
|
11978
11828
|
const lines = [
|
|
11979
11829
|
`Session ${sessionId} ${session.status}.`,
|
|
11830
|
+
`Pass sessionId="${sessionId}" on every write: the connection does not remember your session between calls.`,
|
|
11980
11831
|
`Initiated by ${session.initiatedBy}.`,
|
|
11981
11832
|
`Scope: ${session.toolsScope}.`,
|
|
11982
11833
|
`Oriented: ${oriented ? "yes" : "no"}.`,
|
|
@@ -14216,6 +14067,7 @@ var INSTRUCTIONS = [
|
|
|
14216
14067
|
"## Writing",
|
|
14217
14068
|
"",
|
|
14218
14069
|
"Write actions (capture, entries update and commit, relations create, batch-create, dismiss and delete) need an active session and a readwrite key.",
|
|
14070
|
+
"Pass the `sessionId` that `session action=start` returned on every write: the connection does not remember it.",
|
|
14219
14071
|
"Use `capture` to create an entry and read its reply: an entry that came back accepted needs no `entries action=commit`.",
|
|
14220
14072
|
"Link with `relations action=suggest`, then `relations action=create`.",
|
|
14221
14073
|
"Finish with `session action=wrapup-review`, then `session action=close`.",
|
|
@@ -14225,11 +14077,46 @@ var INSTRUCTIONS = [
|
|
|
14225
14077
|
"",
|
|
14226
14078
|
"Every tool and action: `productbrain://agent-cheatsheet`."
|
|
14227
14079
|
].join("\n");
|
|
14080
|
+
var sessionIdArg = z34.string().max(200).optional().describe(
|
|
14081
|
+
"The sessionId `session action=start` returned. Pass it on every write: the connection does not remember your session between calls."
|
|
14082
|
+
);
|
|
14083
|
+
function acceptSessionIdOnEveryTool(server) {
|
|
14084
|
+
const register = server.registerTool.bind(server);
|
|
14085
|
+
server.registerTool = (name, config, cb) => register(
|
|
14086
|
+
name,
|
|
14087
|
+
{ ...config, inputSchema: config.inputSchema.extend({ sessionId: sessionIdArg }) },
|
|
14088
|
+
async ({ sessionId, ...args }, extra) => {
|
|
14089
|
+
if (typeof sessionId === "string" && sessionId.length > 0 && !(name === "session" && args.action === "start")) {
|
|
14090
|
+
const seated = await validateOrRefuse(sessionId);
|
|
14091
|
+
if ("refused" in seated) return seated.refused;
|
|
14092
|
+
return runWithSeatedSession(seated.lc, () => cb(args, extra));
|
|
14093
|
+
}
|
|
14094
|
+
return cb(args, extra);
|
|
14095
|
+
}
|
|
14096
|
+
);
|
|
14097
|
+
}
|
|
14098
|
+
async function validateOrRefuse(sessionId) {
|
|
14099
|
+
try {
|
|
14100
|
+
return { lc: await validateCallerSession(sessionId) };
|
|
14101
|
+
} catch (err) {
|
|
14102
|
+
const { code, message, recovery } = classifyError(err);
|
|
14103
|
+
const reason = message.split("\n")[0].replace(/\.?\s*$/, ".");
|
|
14104
|
+
const result2 = err instanceof KernelCallError && err.status >= 400 && err.status < 500 && err.status !== 429 ? failureResult(
|
|
14105
|
+
`[SESSION_INVALID] The sessionId you passed (${sessionId}) was refused: ${reason} ${PASS_SESSION_ID}`,
|
|
14106
|
+
"SESSION_INVALID",
|
|
14107
|
+
`sessionId refused: ${reason}`,
|
|
14108
|
+
PASS_SESSION_ID,
|
|
14109
|
+
[{ tool: "session", description: "Start a session", parameters: { action: "start" } }]
|
|
14110
|
+
) : failureResult(`[${code}] ${message}`, code, message, recovery);
|
|
14111
|
+
return { refused: { ...result2, isError: true } };
|
|
14112
|
+
}
|
|
14113
|
+
}
|
|
14228
14114
|
function createProductBrainServer() {
|
|
14229
14115
|
const server = new McpServer2(
|
|
14230
14116
|
{ name: "Product Brain", version: SERVER_VERSION },
|
|
14231
14117
|
{ capabilities: { logging: {} }, instructions: INSTRUCTIONS }
|
|
14232
14118
|
);
|
|
14119
|
+
acceptSessionIdOnEveryTool(server);
|
|
14233
14120
|
registerSessionTools(server);
|
|
14234
14121
|
registerEntriesTools(server);
|
|
14235
14122
|
registerRelationsTools(server);
|
|
@@ -14280,4 +14167,4 @@ export {
|
|
|
14280
14167
|
createProductBrainServer,
|
|
14281
14168
|
initFeatureFlags
|
|
14282
14169
|
};
|
|
14283
|
-
//# sourceMappingURL=chunk-
|
|
14170
|
+
//# sourceMappingURL=chunk-ZOYUYCKV.js.map
|