@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
|
@@ -925,6 +925,167 @@ function recordGatewayCall(params) {
|
|
|
925
925
|
// src/client.ts
|
|
926
926
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
927
927
|
|
|
928
|
+
// src/errors.ts
|
|
929
|
+
var MCP_CALL_ERROR_RE = /^MCP call "([^"]+)" failed \(\d+\): (.+)$/s;
|
|
930
|
+
function asKernelCallError(err) {
|
|
931
|
+
if (!(err instanceof Error)) return void 0;
|
|
932
|
+
const e = err;
|
|
933
|
+
if (typeof e.code !== "string") return void 0;
|
|
934
|
+
const match = MCP_CALL_ERROR_RE.exec(e.message);
|
|
935
|
+
if (!match) return void 0;
|
|
936
|
+
return {
|
|
937
|
+
code: e.code,
|
|
938
|
+
fn: match[1],
|
|
939
|
+
cleanMessage: match[2],
|
|
940
|
+
// NOTE: match[2] not match[1] — group 1 is now fn scope (DEC-798)
|
|
941
|
+
missingRequiredFields: Array.isArray(e.missingRequiredFields) ? e.missingRequiredFields : void 0,
|
|
942
|
+
fieldErrors: Array.isArray(e.fieldErrors) ? e.fieldErrors : void 0
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
var GateError = class extends Error {
|
|
946
|
+
constructor(message) {
|
|
947
|
+
super(message);
|
|
948
|
+
this.name = "GateError";
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
var BackendError = class extends Error {
|
|
952
|
+
constructor(message) {
|
|
953
|
+
super(message);
|
|
954
|
+
this.name = "BackendError";
|
|
955
|
+
}
|
|
956
|
+
};
|
|
957
|
+
var ValidationError = class extends Error {
|
|
958
|
+
constructor(message) {
|
|
959
|
+
super(message);
|
|
960
|
+
this.name = "ValidationError";
|
|
961
|
+
}
|
|
962
|
+
};
|
|
963
|
+
var PASS_SESSION_ID = "Call `session action=start`, then pass the sessionId it returns as `sessionId` on every write: the connection does not remember your session between calls.";
|
|
964
|
+
var GATE_PATTERNS = [
|
|
965
|
+
{
|
|
966
|
+
pattern: /session required|no active.*session|call.*session.*start/i,
|
|
967
|
+
code: "SESSION_REQUIRED",
|
|
968
|
+
recovery: PASS_SESSION_ID,
|
|
969
|
+
action: { tool: "session", description: "Start session", parameters: { action: "start" } }
|
|
970
|
+
},
|
|
971
|
+
{
|
|
972
|
+
pattern: /session.*closed/i,
|
|
973
|
+
code: "SESSION_CLOSED",
|
|
974
|
+
recovery: "Start a new session.",
|
|
975
|
+
action: { tool: "session", description: "Start new session", parameters: { action: "start" } }
|
|
976
|
+
},
|
|
977
|
+
{
|
|
978
|
+
pattern: /orientation required|call.*orient/i,
|
|
979
|
+
code: "ORIENTATION_REQUIRED",
|
|
980
|
+
recovery: "Run orient with a task, then retry.",
|
|
981
|
+
action: { tool: "orient", description: "Orient with a task", parameters: { action: "task" } }
|
|
982
|
+
},
|
|
983
|
+
{
|
|
984
|
+
pattern: /read.?only.*scope/i,
|
|
985
|
+
code: "READONLY_SCOPE",
|
|
986
|
+
recovery: "This API key cannot write. Use a readwrite key.",
|
|
987
|
+
action: { tool: "workspace", description: "Check key scope", parameters: { action: "whoami" } }
|
|
988
|
+
}
|
|
989
|
+
];
|
|
990
|
+
function orientationRefusal(err) {
|
|
991
|
+
const gate = GATE_PATTERNS.find((g) => g.code === "ORIENTATION_REQUIRED");
|
|
992
|
+
const mcpErr = asKernelCallError(err);
|
|
993
|
+
const message = mcpErr?.cleanMessage ?? (err instanceof Error ? err.message : String(err));
|
|
994
|
+
if (!gate || !gate.pattern.test(message)) return null;
|
|
995
|
+
return { code: gate.code, message, recovery: gate.recovery, availableActions: [gate.action] };
|
|
996
|
+
}
|
|
997
|
+
function classifyError(err) {
|
|
998
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
999
|
+
if (err instanceof GateError) {
|
|
1000
|
+
return {
|
|
1001
|
+
code: "PERMISSION_DENIED",
|
|
1002
|
+
message,
|
|
1003
|
+
recovery: "Check permissions or use a readwrite API key.",
|
|
1004
|
+
availableActions: [{ tool: "workspace", description: "Check key scope", parameters: { action: "whoami" } }]
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
1007
|
+
if (err instanceof BackendError) {
|
|
1008
|
+
return { code: "BACKEND_ERROR", message, recovery: "Retry the operation or check backend health." };
|
|
1009
|
+
}
|
|
1010
|
+
if (err instanceof ValidationError) {
|
|
1011
|
+
return { code: "VALIDATION_ERROR", message };
|
|
1012
|
+
}
|
|
1013
|
+
const mcpErr = asKernelCallError(err);
|
|
1014
|
+
if (mcpErr) {
|
|
1015
|
+
const { code: convexCode, fn, cleanMessage, missingRequiredFields, fieldErrors } = mcpErr;
|
|
1016
|
+
if (convexCode === "VALIDATION_FAILED") {
|
|
1017
|
+
const hasDiagnostics = missingRequiredFields?.length || fieldErrors?.length;
|
|
1018
|
+
return {
|
|
1019
|
+
code: "VALIDATION_ERROR",
|
|
1020
|
+
fn,
|
|
1021
|
+
message: cleanMessage,
|
|
1022
|
+
...hasDiagnostics ? {
|
|
1023
|
+
diagnostics: {
|
|
1024
|
+
...missingRequiredFields?.length ? { missingRequiredFields } : {},
|
|
1025
|
+
...fieldErrors?.length ? { fieldErrors } : {}
|
|
1026
|
+
}
|
|
1027
|
+
} : {}
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
if (convexCode === "NOT_FOUND") {
|
|
1031
|
+
return {
|
|
1032
|
+
code: "NOT_FOUND",
|
|
1033
|
+
fn,
|
|
1034
|
+
message: cleanMessage,
|
|
1035
|
+
recovery: "Use entries action=search to find the correct ID.",
|
|
1036
|
+
availableActions: [{ tool: "entries", description: "Search entries", parameters: { action: "search" } }]
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
if (convexCode?.startsWith("AUTHZ_")) {
|
|
1040
|
+
return {
|
|
1041
|
+
code: "PERMISSION_DENIED",
|
|
1042
|
+
fn,
|
|
1043
|
+
message: cleanMessage,
|
|
1044
|
+
recovery: "Check permissions or use a readwrite API key.",
|
|
1045
|
+
availableActions: [{ tool: "workspace", description: "Check key scope", parameters: { action: "whoami" } }]
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
if (convexCode === "WORKSPACE_SLUG_TAKEN" || convexCode === "PERSON_ALREADY_EXISTS") {
|
|
1049
|
+
return {
|
|
1050
|
+
code: "DUPLICATE",
|
|
1051
|
+
fn,
|
|
1052
|
+
message: cleanMessage,
|
|
1053
|
+
recovery: "Use entries action=get to inspect the existing entry.",
|
|
1054
|
+
availableActions: [{ tool: "entries", description: "Get entry", parameters: { action: "get" } }]
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
if (convexCode === "rate-limited") {
|
|
1058
|
+
return {
|
|
1059
|
+
code: "rate-limited",
|
|
1060
|
+
fn,
|
|
1061
|
+
message: cleanMessage,
|
|
1062
|
+
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."
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
return { code: convexCode, fn, message: cleanMessage };
|
|
1066
|
+
}
|
|
1067
|
+
for (const { pattern, code, recovery, action } of GATE_PATTERNS) {
|
|
1068
|
+
if (pattern.test(message)) {
|
|
1069
|
+
return { code, message, recovery, availableActions: [action] };
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
if (err?.name === "GatewayTimeoutError") {
|
|
1073
|
+
const mayHaveLanded = err.mayHaveLanded === true;
|
|
1074
|
+
return {
|
|
1075
|
+
code: "GATEWAY_TIMEOUT",
|
|
1076
|
+
message,
|
|
1077
|
+
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."
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
if (/network error|fetch failed|ECONNREFUSED|ETIMEDOUT/i.test(message)) {
|
|
1081
|
+
return { code: "BACKEND_UNAVAILABLE", message, recovery: "Retry in a few seconds." };
|
|
1082
|
+
}
|
|
1083
|
+
return { code: "INTERNAL_ERROR", message: message || "An unexpected error occurred." };
|
|
1084
|
+
}
|
|
1085
|
+
function retryIsFutile(code) {
|
|
1086
|
+
return code === "VALIDATION_ERROR";
|
|
1087
|
+
}
|
|
1088
|
+
|
|
928
1089
|
// src/lib/conversation.ts
|
|
929
1090
|
import { createHash as createHash2 } from "crypto";
|
|
930
1091
|
var MAX_RAW_LEN = 128;
|
|
@@ -990,7 +1151,7 @@ async function probeDeploymentCandidates(candidates, apiKey) {
|
|
|
990
1151
|
var _conversationId;
|
|
991
1152
|
function getConversationId() {
|
|
992
1153
|
const mcpSid = getRequestMcpSessionId();
|
|
993
|
-
if (mcpSid) return `mcp:${mcpSid}`;
|
|
1154
|
+
if (mcpSid) return sessionLifecycle().conversationId ?? `mcp:${mcpSid}`;
|
|
994
1155
|
if (getRequestApiKey()) return null;
|
|
995
1156
|
if (_conversationId === void 0) _conversationId = resolveConversationId();
|
|
996
1157
|
return _conversationId;
|
|
@@ -1076,6 +1237,8 @@ function getActiveApiKey() {
|
|
|
1076
1237
|
var _sessionLifecycleByStream = /* @__PURE__ */ new Map();
|
|
1077
1238
|
var MAX_SESSION_STREAMS = 500;
|
|
1078
1239
|
function sessionLifecycle() {
|
|
1240
|
+
const seated = seatedSessionStore.getStore();
|
|
1241
|
+
if (seated) return seated;
|
|
1079
1242
|
const mcpSid = getRequestMcpSessionId();
|
|
1080
1243
|
if (!mcpSid) return state();
|
|
1081
1244
|
const key = `${cacheScope()}:${mcpSid}`;
|
|
@@ -1102,6 +1265,13 @@ function setSessionOriented(value) {
|
|
|
1102
1265
|
function getApiKeyScope() {
|
|
1103
1266
|
return state().apiKeyScope;
|
|
1104
1267
|
}
|
|
1268
|
+
var seatedSessionStore = new AsyncLocalStorage2();
|
|
1269
|
+
async function validateCallerSession(sessionId) {
|
|
1270
|
+
const session = await kernelCall("agent.validateSession", { sessionId, workspaceId: await getWorkspaceId(), requireActive: true });
|
|
1271
|
+
return { agentSessionId: session._id, sessionOriented: session.oriented, sessionClosed: false, conversationId: session.conversationId ?? null };
|
|
1272
|
+
}
|
|
1273
|
+
var runWithSeatedSession = (lc, fn) => seatedSessionStore.run(lc, fn);
|
|
1274
|
+
var forgetStreamLifecycle = (keyHash, mcpSessionId) => _sessionLifecycleByStream.delete(`${keyHash}:${mcpSessionId}`);
|
|
1105
1275
|
async function startAgentSession(role) {
|
|
1106
1276
|
const workspaceId = await getWorkspaceId();
|
|
1107
1277
|
const s = state();
|
|
@@ -1419,9 +1589,7 @@ function requireWriteAccess() {
|
|
|
1419
1589
|
const lc = sessionLifecycle();
|
|
1420
1590
|
const s = state();
|
|
1421
1591
|
if (!lc.agentSessionId) {
|
|
1422
|
-
throw new Error(
|
|
1423
|
-
"Agent session required for write operations. Call `session action=start` first."
|
|
1424
|
-
);
|
|
1592
|
+
throw new Error(`Agent session required for write operations. ${PASS_SESSION_ID}`);
|
|
1425
1593
|
}
|
|
1426
1594
|
if (lc.sessionClosed) {
|
|
1427
1595
|
throw new Error(
|
|
@@ -1518,6 +1686,10 @@ export {
|
|
|
1518
1686
|
MCP_NPX_PACKAGE,
|
|
1519
1687
|
resolveClient,
|
|
1520
1688
|
writeClientConfig,
|
|
1689
|
+
PASS_SESSION_ID,
|
|
1690
|
+
orientationRefusal,
|
|
1691
|
+
classifyError,
|
|
1692
|
+
retryIsFutile,
|
|
1521
1693
|
warnOnProdFallthrough,
|
|
1522
1694
|
getConversationId,
|
|
1523
1695
|
runWithToolContext,
|
|
@@ -1527,6 +1699,9 @@ export {
|
|
|
1527
1699
|
isSessionOriented,
|
|
1528
1700
|
setSessionOriented,
|
|
1529
1701
|
getApiKeyScope,
|
|
1702
|
+
validateCallerSession,
|
|
1703
|
+
runWithSeatedSession,
|
|
1704
|
+
forgetStreamLifecycle,
|
|
1530
1705
|
startAgentSession,
|
|
1531
1706
|
closeAgentSession,
|
|
1532
1707
|
orphanAgentSession,
|
|
@@ -1544,4 +1719,4 @@ export {
|
|
|
1544
1719
|
requireVendorTriageAccess,
|
|
1545
1720
|
recoverSessionState
|
|
1546
1721
|
};
|
|
1547
|
-
//# sourceMappingURL=chunk-
|
|
1722
|
+
//# sourceMappingURL=chunk-2UJDIWEX.js.map
|