claude-code-rust 0.14.2 → 0.14.4
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/README.md +2 -3
- package/agent-sdk/dist/bridge/account_metadata.js +6 -2
- package/agent-sdk/dist/bridge/agents.js +9 -3
- package/agent-sdk/dist/bridge/available_commands.js +13 -3
- package/agent-sdk/dist/bridge/command_lifecycle.js +79 -4
- package/agent-sdk/dist/bridge/command_scheduler.js +7 -2
- package/agent-sdk/dist/bridge/command_session_control.js +5 -1
- package/agent-sdk/dist/bridge/command_session_data.js +225 -10
- package/agent-sdk/dist/bridge/commands.js +48 -11
- package/agent-sdk/dist/bridge/error_classification.js +5 -1
- package/agent-sdk/dist/bridge/events.js +45 -7
- package/agent-sdk/dist/bridge/history.js +33 -10
- package/agent-sdk/dist/bridge/logger.js +19 -3
- package/agent-sdk/dist/bridge/mcp.js +7 -2
- package/agent-sdk/dist/bridge/mcp_auth_adapter.js +4 -2
- package/agent-sdk/dist/bridge/mcp_metadata.js +113 -39
- package/agent-sdk/dist/bridge/mcp_monitor.js +6 -1
- package/agent-sdk/dist/bridge/message_handlers.js +336 -74
- package/agent-sdk/dist/bridge/model_metadata.js +19 -6
- package/agent-sdk/dist/bridge/permissions.js +32 -8
- package/agent-sdk/dist/bridge/session_lifecycle.js +224 -45
- package/agent-sdk/dist/bridge/state_parsing.js +23 -9
- package/agent-sdk/dist/bridge/tasks.js +62 -22
- package/agent-sdk/dist/bridge/tool_calls.js +89 -31
- package/agent-sdk/dist/bridge/tooling.js +430 -82
- package/agent-sdk/dist/bridge/user_interaction.js +45 -13
- package/agent-sdk/dist/bridge.js +200 -104
- package/package.json +8 -8
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { looksLikeAuthRequired } from "./auth.js";
|
|
2
2
|
import { writeEvent } from "./events.js";
|
|
3
3
|
import { emitSessionUpdate } from "./events.js";
|
|
4
|
-
import { parseFastModeDisabledReason, parseFastModeState } from "./state_parsing.js";
|
|
4
|
+
import { parseFastModeDisabledReason, parseFastModeState, } from "./state_parsing.js";
|
|
5
5
|
export function emitAuthRequired(session, detail) {
|
|
6
|
+
if (session.deferConnect) {
|
|
7
|
+
session.deferredAuthRequired = detail ? { detail } : {};
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
6
10
|
if (session.authHintSent) {
|
|
7
11
|
return;
|
|
8
12
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { listSessions } from "@anthropic-ai/claude-agent-sdk";
|
|
1
|
+
import { listSessions, } from "@anthropic-ai/claude-agent-sdk";
|
|
2
2
|
import { writeSync } from "node:fs";
|
|
3
3
|
import { buildModeState } from "./commands.js";
|
|
4
4
|
import { mapSdkSessions } from "./history.js";
|
|
5
5
|
import { bridgeLogger, LOG_TARGETS, logBridgeEventSent } from "./logger.js";
|
|
6
|
-
import { detachSessionForClose, resolveCurrentModel, trackSessionCloseTask, } from "./session_lifecycle.js";
|
|
6
|
+
import { detachSessionForClose, resolveCurrentModel, sessionById, trackSessionCloseTask, } from "./session_lifecycle.js";
|
|
7
7
|
const SESSION_LIST_LIMIT = 50;
|
|
8
8
|
let sessionListingDir;
|
|
9
9
|
function writeProtocolEventToStdout(line) {
|
|
@@ -37,6 +37,20 @@ export function currentSessionListOptions() {
|
|
|
37
37
|
return buildSessionListOptions(sessionListingDir);
|
|
38
38
|
}
|
|
39
39
|
export function writeEvent(event, requestId) {
|
|
40
|
+
if ("session_id" in event &&
|
|
41
|
+
event.event !== "connected" &&
|
|
42
|
+
event.event !== "session_replaced") {
|
|
43
|
+
const session = sessionById(event.session_id);
|
|
44
|
+
if (session?.deferConnect) {
|
|
45
|
+
const events = session.deferredBridgeEvents ?? [];
|
|
46
|
+
events.push({ event, ...(requestId ? { requestId } : {}) });
|
|
47
|
+
session.deferredBridgeEvents = events;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
writeEventNow(event, requestId);
|
|
52
|
+
}
|
|
53
|
+
function writeEventNow(event, requestId) {
|
|
40
54
|
const envelope = {
|
|
41
55
|
...(requestId ? { request_id: requestId } : {}),
|
|
42
56
|
...event,
|
|
@@ -51,6 +65,13 @@ export function failConnection(message, requestId) {
|
|
|
51
65
|
export function slashError(sessionId, message, requestId) {
|
|
52
66
|
writeEvent({ event: "slash_error", session_id: sessionId, message }, requestId);
|
|
53
67
|
}
|
|
68
|
+
export function emitSessionResumeFailed(sessionId, operationId, message) {
|
|
69
|
+
writeEventNow({
|
|
70
|
+
event: "session_resume_failed",
|
|
71
|
+
session_id: sessionId,
|
|
72
|
+
message,
|
|
73
|
+
}, operationId);
|
|
74
|
+
}
|
|
54
75
|
export function emitRuntimeReloadCompleted(sessionId, requestId) {
|
|
55
76
|
writeEvent({ event: "runtime_reload_completed", session_id: sessionId }, requestId);
|
|
56
77
|
}
|
|
@@ -146,8 +167,12 @@ export function buildConnectBridgeEvent(session, eventName) {
|
|
|
146
167
|
...(session.fastModeDisabledReason
|
|
147
168
|
? { fast_mode_disabled_reason: session.fastModeDisabledReason }
|
|
148
169
|
: {}),
|
|
149
|
-
...(historyUpdates && historyUpdates.length > 0
|
|
150
|
-
|
|
170
|
+
...(historyUpdates && historyUpdates.length > 0
|
|
171
|
+
? { history_updates: historyUpdates }
|
|
172
|
+
: {}),
|
|
173
|
+
...(session.restoredInput !== undefined
|
|
174
|
+
? { restored_input: session.restoredInput }
|
|
175
|
+
: {}),
|
|
151
176
|
}
|
|
152
177
|
: {
|
|
153
178
|
event: "connected",
|
|
@@ -160,14 +185,20 @@ export function buildConnectBridgeEvent(session, eventName) {
|
|
|
160
185
|
...(session.fastModeDisabledReason
|
|
161
186
|
? { fast_mode_disabled_reason: session.fastModeDisabledReason }
|
|
162
187
|
: {}),
|
|
163
|
-
...(historyUpdates && historyUpdates.length > 0
|
|
188
|
+
...(historyUpdates && historyUpdates.length > 0
|
|
189
|
+
? { history_updates: historyUpdates }
|
|
190
|
+
: {}),
|
|
164
191
|
};
|
|
165
192
|
}
|
|
166
193
|
function logConnectEventEmission(session, eventName, requestId) {
|
|
167
194
|
bridgeLogger.info({
|
|
168
195
|
target: LOG_TARGETS.APP_SESSION,
|
|
169
|
-
eventName: eventName === "session_replaced"
|
|
170
|
-
|
|
196
|
+
eventName: eventName === "session_replaced"
|
|
197
|
+
? "session_replaced_emitted"
|
|
198
|
+
: "session_connected_emitted",
|
|
199
|
+
message: eventName === "session_replaced"
|
|
200
|
+
? "session replaced event emitted"
|
|
201
|
+
: "session connected event emitted",
|
|
171
202
|
outcome: "success",
|
|
172
203
|
...(requestId ? { requestId } : {}),
|
|
173
204
|
sessionId: session.sessionId,
|
|
@@ -198,6 +229,13 @@ export function emitConnectEvent(session) {
|
|
|
198
229
|
session.connectRequestId = undefined;
|
|
199
230
|
session.connected = true;
|
|
200
231
|
session.authHintSent = false;
|
|
232
|
+
const deferredBridgeEvents = session.deferredBridgeEvents;
|
|
233
|
+
session.deferredBridgeEvents = undefined;
|
|
234
|
+
if (deferredBridgeEvents) {
|
|
235
|
+
for (const deferred of deferredBridgeEvents) {
|
|
236
|
+
writeEventNow({ ...deferred.event, session_id: session.sessionId }, deferred.requestId);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
201
239
|
session.resumeUpdates = undefined;
|
|
202
240
|
session.restoredInput = undefined;
|
|
203
241
|
session.sessionsToCloseAfterConnect = undefined;
|
|
@@ -124,7 +124,9 @@ function pushResumeTaskSystemUpdate(updates, tasksById, taskToolUseIds, msg) {
|
|
|
124
124
|
const patch = asRecordOrNull(msg.patch) ?? undefined;
|
|
125
125
|
const status = normalizeLifecycleTaskStatus(msg.status) ??
|
|
126
126
|
normalizeLifecycleTaskStatus(patch?.status) ??
|
|
127
|
-
(subtype === "task_started" || subtype === "task_progress"
|
|
127
|
+
(subtype === "task_started" || subtype === "task_progress"
|
|
128
|
+
? "in_progress"
|
|
129
|
+
: undefined);
|
|
128
130
|
const description = nonEmptyTrimmed(patch?.description) ??
|
|
129
131
|
nonEmptyTrimmed(msg.description) ??
|
|
130
132
|
nonEmptyTrimmed(msg.summary);
|
|
@@ -141,14 +143,24 @@ function pushResumeTaskSystemUpdate(updates, tasksById, taskToolUseIds, msg) {
|
|
|
141
143
|
const task = {
|
|
142
144
|
task_id: taskId,
|
|
143
145
|
subject,
|
|
144
|
-
...(description !== undefined
|
|
145
|
-
|
|
146
|
+
...(description !== undefined
|
|
147
|
+
? { description }
|
|
148
|
+
: existing?.description !== undefined
|
|
149
|
+
? { description: existing.description }
|
|
150
|
+
: {}),
|
|
151
|
+
...(activeForm !== undefined
|
|
152
|
+
? { active_form: activeForm }
|
|
153
|
+
: existing?.active_form !== undefined
|
|
154
|
+
? { active_form: existing.active_form }
|
|
155
|
+
: {}),
|
|
146
156
|
status: status ?? existing?.status ?? "pending",
|
|
147
157
|
...(existing?.owner !== undefined ? { owner: existing.owner } : {}),
|
|
148
158
|
blocks: existing ? [...existing.blocks] : [],
|
|
149
159
|
blocked_by: existing ? [...existing.blocked_by] : [],
|
|
150
160
|
...(metadata !== undefined ? { metadata } : {}),
|
|
151
|
-
...(sourceToolCallId !== undefined
|
|
161
|
+
...(sourceToolCallId !== undefined
|
|
162
|
+
? { source_tool_call_id: sourceToolCallId }
|
|
163
|
+
: {}),
|
|
152
164
|
};
|
|
153
165
|
tasksById.set(taskId, task);
|
|
154
166
|
updates.push({
|
|
@@ -203,7 +215,8 @@ function pushResumeToolResult(updates, toolCalls, hiddenToolUseIds, block, nonEx
|
|
|
203
215
|
return;
|
|
204
216
|
}
|
|
205
217
|
const blockType = typeof block.type === "string" ? block.type : "";
|
|
206
|
-
if (isToolSearchToolResultType(blockType) ||
|
|
218
|
+
if (isToolSearchToolResultType(blockType) ||
|
|
219
|
+
hiddenToolUseIds.has(toolUseId)) {
|
|
207
220
|
hiddenToolUseIds.add(toolUseId);
|
|
208
221
|
return;
|
|
209
222
|
}
|
|
@@ -246,9 +259,15 @@ export function mapSdkSessionInfo(info) {
|
|
|
246
259
|
last_modified_ms: info.lastModified,
|
|
247
260
|
file_size_bytes: info.fileSize ?? 0,
|
|
248
261
|
...(nonEmptyTrimmed(info.cwd) ? { cwd: info.cwd?.trim() } : {}),
|
|
249
|
-
...(nonEmptyTrimmed(info.gitBranch)
|
|
250
|
-
|
|
251
|
-
|
|
262
|
+
...(nonEmptyTrimmed(info.gitBranch)
|
|
263
|
+
? { git_branch: info.gitBranch?.trim() }
|
|
264
|
+
: {}),
|
|
265
|
+
...(nonEmptyTrimmed(info.customTitle)
|
|
266
|
+
? { custom_title: info.customTitle?.trim() }
|
|
267
|
+
: {}),
|
|
268
|
+
...(nonEmptyTrimmed(info.firstPrompt)
|
|
269
|
+
? { first_prompt: info.firstPrompt?.trim() }
|
|
270
|
+
: {}),
|
|
252
271
|
};
|
|
253
272
|
}
|
|
254
273
|
export function mapSdkSessions(infos, limit = 50) {
|
|
@@ -286,9 +305,13 @@ export function mapSessionMessagesToUpdates(messages) {
|
|
|
286
305
|
continue;
|
|
287
306
|
}
|
|
288
307
|
for (const message of candidates) {
|
|
289
|
-
const sourceMessageUuid = typeof message.uuid === "string"
|
|
308
|
+
const sourceMessageUuid = typeof message.uuid === "string"
|
|
309
|
+
? message.uuid
|
|
310
|
+
: entrySourceMessageUuid;
|
|
290
311
|
const roleCandidate = message.role;
|
|
291
|
-
const role = roleCandidate === "assistant" || roleCandidate === "user"
|
|
312
|
+
const role = roleCandidate === "assistant" || roleCandidate === "user"
|
|
313
|
+
? roleCandidate
|
|
314
|
+
: fallbackRole;
|
|
292
315
|
const parentToolUseId = typeof entry.parent_tool_use_id === "string"
|
|
293
316
|
? entry.parent_tool_use_id
|
|
294
317
|
: typeof message.parent_tool_use_id === "string"
|
|
@@ -45,7 +45,9 @@ function writeDiagnostic(level, event) {
|
|
|
45
45
|
...(event.terminalId ? { terminal_id: event.terminalId } : {}),
|
|
46
46
|
...(event.errorKind ? { error_kind: event.errorKind } : {}),
|
|
47
47
|
...(event.errorCode ? { error_code: event.errorCode } : {}),
|
|
48
|
-
...(event.durationMs !== undefined
|
|
48
|
+
...(event.durationMs !== undefined
|
|
49
|
+
? { duration_ms: event.durationMs }
|
|
50
|
+
: {}),
|
|
49
51
|
...(event.count !== undefined ? { count: event.count } : {}),
|
|
50
52
|
...(event.sizeBytes !== undefined ? { size_bytes: event.sizeBytes } : {}),
|
|
51
53
|
};
|
|
@@ -61,6 +63,7 @@ function previewText(value, limit) {
|
|
|
61
63
|
function commandSessionId(command) {
|
|
62
64
|
switch (command.command) {
|
|
63
65
|
case "resume_session":
|
|
66
|
+
case "resume_session_at":
|
|
64
67
|
case "prompt":
|
|
65
68
|
case "cancel_turn":
|
|
66
69
|
case "set_model":
|
|
@@ -73,6 +76,7 @@ function commandSessionId(command) {
|
|
|
73
76
|
case "elicitation_response":
|
|
74
77
|
case "get_status_snapshot":
|
|
75
78
|
case "get_context_usage":
|
|
79
|
+
case "get_usage":
|
|
76
80
|
case "get_rewind_targets":
|
|
77
81
|
case "rewind":
|
|
78
82
|
case "reload_plugins":
|
|
@@ -100,6 +104,7 @@ function commandToolCallId(command) {
|
|
|
100
104
|
case "initialize":
|
|
101
105
|
case "create_session":
|
|
102
106
|
case "resume_session":
|
|
107
|
+
case "resume_session_at":
|
|
103
108
|
case "prompt":
|
|
104
109
|
case "cancel_turn":
|
|
105
110
|
case "set_model":
|
|
@@ -111,6 +116,7 @@ function commandToolCallId(command) {
|
|
|
111
116
|
case "elicitation_response":
|
|
112
117
|
case "get_status_snapshot":
|
|
113
118
|
case "get_context_usage":
|
|
119
|
+
case "get_usage":
|
|
114
120
|
case "get_rewind_targets":
|
|
115
121
|
case "rewind":
|
|
116
122
|
case "reload_plugins":
|
|
@@ -134,6 +140,7 @@ function eventToolCallId(event) {
|
|
|
134
140
|
case "auth_required":
|
|
135
141
|
case "connection_failed":
|
|
136
142
|
case "session_update":
|
|
143
|
+
case "user_dialog_request":
|
|
137
144
|
case "elicitation_request":
|
|
138
145
|
case "elicitation_complete":
|
|
139
146
|
case "mcp_auth_redirect":
|
|
@@ -142,6 +149,7 @@ function eventToolCallId(event) {
|
|
|
142
149
|
case "turn_complete":
|
|
143
150
|
case "turn_error":
|
|
144
151
|
case "slash_error":
|
|
152
|
+
case "session_resume_failed":
|
|
145
153
|
case "runtime_reload_completed":
|
|
146
154
|
case "runtime_reload_failed":
|
|
147
155
|
case "session_replaced":
|
|
@@ -149,6 +157,7 @@ function eventToolCallId(event) {
|
|
|
149
157
|
case "sessions_listed":
|
|
150
158
|
case "status_snapshot":
|
|
151
159
|
case "context_usage":
|
|
160
|
+
case "usage_snapshot":
|
|
152
161
|
case "rewind_targets":
|
|
153
162
|
case "rewind_result":
|
|
154
163
|
case "mcp_snapshot":
|
|
@@ -160,6 +169,7 @@ function protocolCommandLevel(command) {
|
|
|
160
169
|
case "initialize":
|
|
161
170
|
case "create_session":
|
|
162
171
|
case "resume_session":
|
|
172
|
+
case "resume_session_at":
|
|
163
173
|
case "new_session":
|
|
164
174
|
case "shutdown":
|
|
165
175
|
return "info";
|
|
@@ -179,6 +189,7 @@ function protocolEventLevel(event) {
|
|
|
179
189
|
case "mcp_operation_error":
|
|
180
190
|
case "turn_error":
|
|
181
191
|
case "slash_error":
|
|
192
|
+
case "session_resume_failed":
|
|
182
193
|
case "runtime_reload_failed":
|
|
183
194
|
return "warn";
|
|
184
195
|
case "session_update":
|
|
@@ -193,6 +204,7 @@ function protocolEventLevel(event) {
|
|
|
193
204
|
case "sessions_listed":
|
|
194
205
|
case "status_snapshot":
|
|
195
206
|
case "context_usage":
|
|
207
|
+
case "usage_snapshot":
|
|
196
208
|
case "rewind_targets":
|
|
197
209
|
case "rewind_result":
|
|
198
210
|
case "runtime_reload_completed":
|
|
@@ -248,8 +260,12 @@ export function logBridgeCommandReceived(command, requestId) {
|
|
|
248
260
|
message: "bridge command received",
|
|
249
261
|
outcome: "success",
|
|
250
262
|
...(requestId ? { requestId } : {}),
|
|
251
|
-
...(commandSessionId(command)
|
|
252
|
-
|
|
263
|
+
...(commandSessionId(command)
|
|
264
|
+
? { sessionId: commandSessionId(command) }
|
|
265
|
+
: {}),
|
|
266
|
+
...(commandToolCallId(command)
|
|
267
|
+
? { toolCallId: commandToolCallId(command) }
|
|
268
|
+
: {}),
|
|
253
269
|
fields: { bridge_command: command.command },
|
|
254
270
|
});
|
|
255
271
|
}
|
|
@@ -40,7 +40,9 @@ function mapMcpSetServersResult(result) {
|
|
|
40
40
|
const removed = Array.isArray(record.removed)
|
|
41
41
|
? record.removed.filter((value) => typeof value === "string")
|
|
42
42
|
: [];
|
|
43
|
-
const errors = record.errors &&
|
|
43
|
+
const errors = record.errors &&
|
|
44
|
+
typeof record.errors === "object" &&
|
|
45
|
+
!Array.isArray(record.errors)
|
|
44
46
|
? Object.fromEntries(Object.entries(record.errors).filter((entry) => typeof entry[1] === "string"))
|
|
45
47
|
: {};
|
|
46
48
|
return { added, removed, errors };
|
|
@@ -293,7 +295,10 @@ export async function handleMcpAuthenticateCommand(session, command, requestId)
|
|
|
293
295
|
try {
|
|
294
296
|
const redirect = await authenticateMcpServer(session.query, command.server_name);
|
|
295
297
|
if (redirect) {
|
|
296
|
-
logMcpSuccess("mcp_auth_redirect_emitted", "MCP auth redirect emitted", command.session_id, requestId, {
|
|
298
|
+
logMcpSuccess("mcp_auth_redirect_emitted", "MCP auth redirect emitted", command.session_id, requestId, {
|
|
299
|
+
server_name: command.server_name,
|
|
300
|
+
requires_user_action: redirect.requires_user_action,
|
|
301
|
+
});
|
|
297
302
|
writeEvent({
|
|
298
303
|
event: "mcp_auth_redirect",
|
|
299
304
|
session_id: command.session_id,
|
|
@@ -25,7 +25,8 @@ export function detectMcpAuthCapabilities(query) {
|
|
|
25
25
|
return {
|
|
26
26
|
authenticate: runtimeMethod(query, MCP_AUTH_METHODS.authenticate) !== undefined,
|
|
27
27
|
clear_auth: runtimeMethod(query, MCP_AUTH_METHODS.clear_auth) !== undefined,
|
|
28
|
-
submit_oauth_callback_url: runtimeMethod(query, MCP_AUTH_METHODS.submit_oauth_callback_url) !==
|
|
28
|
+
submit_oauth_callback_url: runtimeMethod(query, MCP_AUTH_METHODS.submit_oauth_callback_url) !==
|
|
29
|
+
undefined,
|
|
29
30
|
};
|
|
30
31
|
}
|
|
31
32
|
function parseMcpAuthRedirect(serverName, value) {
|
|
@@ -43,7 +44,8 @@ function parseMcpAuthRedirect(serverName, value) {
|
|
|
43
44
|
throw new Error("installed SDK returned an invalid mcpAuthenticate authUrl");
|
|
44
45
|
}
|
|
45
46
|
const requiresUserAction = Reflect.get(value, "requiresUserAction");
|
|
46
|
-
if (requiresUserAction !== undefined &&
|
|
47
|
+
if (requiresUserAction !== undefined &&
|
|
48
|
+
typeof requiresUserAction !== "boolean") {
|
|
47
49
|
throw new Error("installed SDK returned an invalid mcpAuthenticate requiresUserAction");
|
|
48
50
|
}
|
|
49
51
|
return {
|
|
@@ -20,7 +20,8 @@ function optionalStringArray(record, key, context) {
|
|
|
20
20
|
if (value === undefined) {
|
|
21
21
|
return undefined;
|
|
22
22
|
}
|
|
23
|
-
if (!Array.isArray(value) ||
|
|
23
|
+
if (!Array.isArray(value) ||
|
|
24
|
+
!value.every((entry) => typeof entry === "string")) {
|
|
24
25
|
throw new Error(`${context}.${key} must be an array of strings`);
|
|
25
26
|
}
|
|
26
27
|
return value;
|
|
@@ -43,7 +44,10 @@ function optionalTimeout(record, context) {
|
|
|
43
44
|
if (value === undefined) {
|
|
44
45
|
return undefined;
|
|
45
46
|
}
|
|
46
|
-
if (typeof value !== "number" ||
|
|
47
|
+
if (typeof value !== "number" ||
|
|
48
|
+
!Number.isFinite(value) ||
|
|
49
|
+
!Number.isInteger(value) ||
|
|
50
|
+
value < 1000) {
|
|
47
51
|
throw new Error(`${context}.timeout must be an integer >= 1000`);
|
|
48
52
|
}
|
|
49
53
|
return value;
|
|
@@ -53,7 +57,10 @@ function optionalRequestTimeoutMs(record, context) {
|
|
|
53
57
|
if (value === undefined) {
|
|
54
58
|
return undefined;
|
|
55
59
|
}
|
|
56
|
-
if (typeof value !== "number" ||
|
|
60
|
+
if (typeof value !== "number" ||
|
|
61
|
+
!Number.isFinite(value) ||
|
|
62
|
+
!Number.isInteger(value) ||
|
|
63
|
+
value < 1000) {
|
|
57
64
|
throw new Error(`${context}.request_timeout_ms must be an integer >= 1000`);
|
|
58
65
|
}
|
|
59
66
|
return value;
|
|
@@ -85,14 +92,17 @@ function optionalToolPolicies(record, context) {
|
|
|
85
92
|
const policy = { name };
|
|
86
93
|
const permissionPolicy = item.permission_policy;
|
|
87
94
|
if (permissionPolicy !== undefined) {
|
|
88
|
-
if (typeof permissionPolicy !== "string" ||
|
|
95
|
+
if (typeof permissionPolicy !== "string" ||
|
|
96
|
+
!TOOL_PERMISSION_POLICIES.has(permissionPolicy)) {
|
|
89
97
|
throw new Error(`${context}.tools[${index}].permission_policy must be one of always_allow, always_ask, always_deny`);
|
|
90
98
|
}
|
|
91
|
-
policy.permission_policy =
|
|
99
|
+
policy.permission_policy =
|
|
100
|
+
permissionPolicy;
|
|
92
101
|
}
|
|
93
102
|
const orgMaxPermission = item.org_max_permission;
|
|
94
103
|
if (orgMaxPermission !== undefined) {
|
|
95
|
-
if (typeof orgMaxPermission !== "string" ||
|
|
104
|
+
if (typeof orgMaxPermission !== "string" ||
|
|
105
|
+
!ORG_MAX_PERMISSIONS.has(orgMaxPermission)) {
|
|
96
106
|
throw new Error(`${context}.tools[${index}].org_max_permission must be one of allow, ask, blocked`);
|
|
97
107
|
}
|
|
98
108
|
policy.org_max_permission = orgMaxPermission;
|
|
@@ -122,10 +132,16 @@ export function parseMcpServerConfig(value, context) {
|
|
|
122
132
|
return {
|
|
123
133
|
type,
|
|
124
134
|
command,
|
|
125
|
-
...(optionalStringArray(record, "args", context)
|
|
126
|
-
|
|
135
|
+
...(optionalStringArray(record, "args", context)
|
|
136
|
+
? { args: optionalStringArray(record, "args", context) }
|
|
137
|
+
: {}),
|
|
138
|
+
...(optionalStringMap(record, "env", context)
|
|
139
|
+
? { env: optionalStringMap(record, "env", context) }
|
|
140
|
+
: {}),
|
|
127
141
|
...(timeout === undefined ? {} : { timeout }),
|
|
128
|
-
...(requestTimeoutMs === undefined
|
|
142
|
+
...(requestTimeoutMs === undefined
|
|
143
|
+
? {}
|
|
144
|
+
: { request_timeout_ms: requestTimeoutMs }),
|
|
129
145
|
...(alwaysLoad === undefined ? {} : { always_load: alwaysLoad }),
|
|
130
146
|
};
|
|
131
147
|
}
|
|
@@ -139,10 +155,14 @@ export function parseMcpServerConfig(value, context) {
|
|
|
139
155
|
return {
|
|
140
156
|
type,
|
|
141
157
|
url,
|
|
142
|
-
...(optionalStringMap(record, "headers", context)
|
|
158
|
+
...(optionalStringMap(record, "headers", context)
|
|
159
|
+
? { headers: optionalStringMap(record, "headers", context) }
|
|
160
|
+
: {}),
|
|
143
161
|
...(tools === undefined ? {} : { tools }),
|
|
144
162
|
...(timeout === undefined ? {} : { timeout }),
|
|
145
|
-
...(requestTimeoutMs === undefined
|
|
163
|
+
...(requestTimeoutMs === undefined
|
|
164
|
+
? {}
|
|
165
|
+
: { request_timeout_ms: requestTimeoutMs }),
|
|
146
166
|
...(alwaysLoad === undefined ? {} : { always_load: alwaysLoad }),
|
|
147
167
|
};
|
|
148
168
|
}
|
|
@@ -152,17 +172,26 @@ export function parseMcpServerConfig(value, context) {
|
|
|
152
172
|
}
|
|
153
173
|
export function parseMcpServersRecord(value, context) {
|
|
154
174
|
const record = asRecord(value, context);
|
|
155
|
-
return Object.fromEntries(Object.entries(record).map(([key, entry]) => [
|
|
175
|
+
return Object.fromEntries(Object.entries(record).map(([key, entry]) => [
|
|
176
|
+
key,
|
|
177
|
+
parseMcpServerConfig(entry, `${context}.${key}`),
|
|
178
|
+
]));
|
|
156
179
|
}
|
|
157
180
|
function toSdkToolPolicies(tools) {
|
|
158
181
|
return tools?.map((tool) => ({
|
|
159
182
|
name: tool.name,
|
|
160
|
-
...(tool.permission_policy === undefined
|
|
161
|
-
|
|
183
|
+
...(tool.permission_policy === undefined
|
|
184
|
+
? {}
|
|
185
|
+
: { permission_policy: tool.permission_policy }),
|
|
186
|
+
...(tool.org_max_permission === undefined
|
|
187
|
+
? {}
|
|
188
|
+
: { org_max_permission: tool.org_max_permission }),
|
|
162
189
|
}));
|
|
163
190
|
}
|
|
164
191
|
function sdkRequestTimeoutConfig(config) {
|
|
165
|
-
return config.request_timeout_ms === undefined
|
|
192
|
+
return config.request_timeout_ms === undefined
|
|
193
|
+
? {}
|
|
194
|
+
: { requestTimeoutMs: config.request_timeout_ms };
|
|
166
195
|
}
|
|
167
196
|
export function bridgeMcpConfigToSdk(config) {
|
|
168
197
|
switch (config.type) {
|
|
@@ -174,7 +203,9 @@ export function bridgeMcpConfigToSdk(config) {
|
|
|
174
203
|
...(config.env ? { env: config.env } : {}),
|
|
175
204
|
...(config.timeout === undefined ? {} : { timeout: config.timeout }),
|
|
176
205
|
...sdkRequestTimeoutConfig(config),
|
|
177
|
-
...(config.always_load === undefined
|
|
206
|
+
...(config.always_load === undefined
|
|
207
|
+
? {}
|
|
208
|
+
: { alwaysLoad: config.always_load }),
|
|
178
209
|
};
|
|
179
210
|
case "sse":
|
|
180
211
|
return {
|
|
@@ -184,7 +215,9 @@ export function bridgeMcpConfigToSdk(config) {
|
|
|
184
215
|
...(config.tools ? { tools: toSdkToolPolicies(config.tools) } : {}),
|
|
185
216
|
...(config.timeout === undefined ? {} : { timeout: config.timeout }),
|
|
186
217
|
...sdkRequestTimeoutConfig(config),
|
|
187
|
-
...(config.always_load === undefined
|
|
218
|
+
...(config.always_load === undefined
|
|
219
|
+
? {}
|
|
220
|
+
: { alwaysLoad: config.always_load }),
|
|
188
221
|
};
|
|
189
222
|
case "http":
|
|
190
223
|
return {
|
|
@@ -194,12 +227,17 @@ export function bridgeMcpConfigToSdk(config) {
|
|
|
194
227
|
...(config.tools ? { tools: toSdkToolPolicies(config.tools) } : {}),
|
|
195
228
|
...(config.timeout === undefined ? {} : { timeout: config.timeout }),
|
|
196
229
|
...sdkRequestTimeoutConfig(config),
|
|
197
|
-
...(config.always_load === undefined
|
|
230
|
+
...(config.always_load === undefined
|
|
231
|
+
? {}
|
|
232
|
+
: { alwaysLoad: config.always_load }),
|
|
198
233
|
};
|
|
199
234
|
}
|
|
200
235
|
}
|
|
201
236
|
export function bridgeMcpServersToSdk(servers) {
|
|
202
|
-
return Object.fromEntries(Object.entries(servers).map(([name, config]) => [
|
|
237
|
+
return Object.fromEntries(Object.entries(servers).map(([name, config]) => [
|
|
238
|
+
name,
|
|
239
|
+
bridgeMcpConfigToSdk(config),
|
|
240
|
+
]));
|
|
203
241
|
}
|
|
204
242
|
function mapSdkToolPolicies(tools) {
|
|
205
243
|
if (!Array.isArray(tools)) {
|
|
@@ -216,16 +254,20 @@ function mapSdkToolPolicies(tools) {
|
|
|
216
254
|
}
|
|
217
255
|
const policy = { name: raw.name };
|
|
218
256
|
if (raw.permission_policy !== undefined) {
|
|
219
|
-
if (typeof raw.permission_policy !== "string" ||
|
|
257
|
+
if (typeof raw.permission_policy !== "string" ||
|
|
258
|
+
!TOOL_PERMISSION_POLICIES.has(raw.permission_policy)) {
|
|
220
259
|
return null;
|
|
221
260
|
}
|
|
222
|
-
policy.permission_policy =
|
|
261
|
+
policy.permission_policy =
|
|
262
|
+
raw.permission_policy;
|
|
223
263
|
}
|
|
224
264
|
if (raw.org_max_permission !== undefined) {
|
|
225
|
-
if (typeof raw.org_max_permission !== "string" ||
|
|
265
|
+
if (typeof raw.org_max_permission !== "string" ||
|
|
266
|
+
!ORG_MAX_PERMISSIONS.has(raw.org_max_permission)) {
|
|
226
267
|
return null;
|
|
227
268
|
}
|
|
228
|
-
policy.org_max_permission =
|
|
269
|
+
policy.org_max_permission =
|
|
270
|
+
raw.org_max_permission;
|
|
229
271
|
}
|
|
230
272
|
return policy;
|
|
231
273
|
})
|
|
@@ -244,7 +286,9 @@ export function mapMcpServerStatus(status) {
|
|
|
244
286
|
}
|
|
245
287
|
: {}),
|
|
246
288
|
...(status.error ? { error: status.error } : {}),
|
|
247
|
-
...(status.config
|
|
289
|
+
...(status.config
|
|
290
|
+
? { config: mapMcpServerStatusConfig(status.config) }
|
|
291
|
+
: {}),
|
|
248
292
|
...(status.scope ? { scope: status.scope } : {}),
|
|
249
293
|
tools: Array.isArray(status.tools)
|
|
250
294
|
? status.tools.map((tool) => ({
|
|
@@ -275,7 +319,9 @@ function sdkRequestTimeoutMs(config) {
|
|
|
275
319
|
}
|
|
276
320
|
const raw = config;
|
|
277
321
|
const value = raw.requestTimeoutMs ?? raw.request_timeout_ms;
|
|
278
|
-
return typeof value === "number" &&
|
|
322
|
+
return typeof value === "number" &&
|
|
323
|
+
Number.isFinite(value) &&
|
|
324
|
+
Number.isInteger(value)
|
|
279
325
|
? value
|
|
280
326
|
: undefined;
|
|
281
327
|
}
|
|
@@ -285,11 +331,17 @@ export function mapMcpServerStatusConfig(config) {
|
|
|
285
331
|
return {
|
|
286
332
|
type: "stdio",
|
|
287
333
|
command: config.command,
|
|
288
|
-
...(Array.isArray(config.args) && config.args.length > 0
|
|
334
|
+
...(Array.isArray(config.args) && config.args.length > 0
|
|
335
|
+
? { args: config.args }
|
|
336
|
+
: {}),
|
|
289
337
|
...(config.env ? { env: config.env } : {}),
|
|
290
338
|
...(config.timeout === undefined ? {} : { timeout: config.timeout }),
|
|
291
|
-
...(sdkRequestTimeoutMs(config) === undefined
|
|
292
|
-
|
|
339
|
+
...(sdkRequestTimeoutMs(config) === undefined
|
|
340
|
+
? {}
|
|
341
|
+
: { request_timeout_ms: sdkRequestTimeoutMs(config) }),
|
|
342
|
+
...(config.alwaysLoad === undefined
|
|
343
|
+
? {}
|
|
344
|
+
: { always_load: config.alwaysLoad }),
|
|
293
345
|
};
|
|
294
346
|
case "sse": {
|
|
295
347
|
const tools = mapSdkToolPolicies(config.tools);
|
|
@@ -299,8 +351,12 @@ export function mapMcpServerStatusConfig(config) {
|
|
|
299
351
|
...(config.headers ? { headers: config.headers } : {}),
|
|
300
352
|
...(tools === undefined ? {} : { tools }),
|
|
301
353
|
...(config.timeout === undefined ? {} : { timeout: config.timeout }),
|
|
302
|
-
...(sdkRequestTimeoutMs(config) === undefined
|
|
303
|
-
|
|
354
|
+
...(sdkRequestTimeoutMs(config) === undefined
|
|
355
|
+
? {}
|
|
356
|
+
: { request_timeout_ms: sdkRequestTimeoutMs(config) }),
|
|
357
|
+
...(config.alwaysLoad === undefined
|
|
358
|
+
? {}
|
|
359
|
+
: { always_load: config.alwaysLoad }),
|
|
304
360
|
};
|
|
305
361
|
}
|
|
306
362
|
case "http": {
|
|
@@ -311,8 +367,12 @@ export function mapMcpServerStatusConfig(config) {
|
|
|
311
367
|
...(config.headers ? { headers: config.headers } : {}),
|
|
312
368
|
...(tools === undefined ? {} : { tools }),
|
|
313
369
|
...(config.timeout === undefined ? {} : { timeout: config.timeout }),
|
|
314
|
-
...(sdkRequestTimeoutMs(config) === undefined
|
|
315
|
-
|
|
370
|
+
...(sdkRequestTimeoutMs(config) === undefined
|
|
371
|
+
? {}
|
|
372
|
+
: { request_timeout_ms: sdkRequestTimeoutMs(config) }),
|
|
373
|
+
...(config.alwaysLoad === undefined
|
|
374
|
+
? {}
|
|
375
|
+
: { always_load: config.alwaysLoad }),
|
|
316
376
|
};
|
|
317
377
|
}
|
|
318
378
|
case "sdk":
|
|
@@ -353,8 +413,12 @@ function mcpStatusConfigDiagnostics(config) {
|
|
|
353
413
|
return {
|
|
354
414
|
config_type: "stdio",
|
|
355
415
|
...(config.timeout === undefined ? {} : { timeout_ms: config.timeout }),
|
|
356
|
-
...(config.request_timeout_ms === undefined
|
|
357
|
-
|
|
416
|
+
...(config.request_timeout_ms === undefined
|
|
417
|
+
? {}
|
|
418
|
+
: { request_timeout_ms: config.request_timeout_ms }),
|
|
419
|
+
...(config.always_load === undefined
|
|
420
|
+
? {}
|
|
421
|
+
: { always_load: config.always_load }),
|
|
358
422
|
configured_tool_policy_count: 0,
|
|
359
423
|
};
|
|
360
424
|
case "sse":
|
|
@@ -362,8 +426,12 @@ function mcpStatusConfigDiagnostics(config) {
|
|
|
362
426
|
return {
|
|
363
427
|
config_type: config.type,
|
|
364
428
|
...(config.timeout === undefined ? {} : { timeout_ms: config.timeout }),
|
|
365
|
-
...(config.request_timeout_ms === undefined
|
|
366
|
-
|
|
429
|
+
...(config.request_timeout_ms === undefined
|
|
430
|
+
? {}
|
|
431
|
+
: { request_timeout_ms: config.request_timeout_ms }),
|
|
432
|
+
...(config.always_load === undefined
|
|
433
|
+
? {}
|
|
434
|
+
: { always_load: config.always_load }),
|
|
367
435
|
configured_tool_policy_count: config.tools?.length ?? 0,
|
|
368
436
|
};
|
|
369
437
|
case "sdk":
|
|
@@ -392,9 +460,15 @@ export function summarizeMcpServersForDiagnostics(servers) {
|
|
|
392
460
|
status: server.status,
|
|
393
461
|
config_type: config.config_type,
|
|
394
462
|
...(server.scope ? { scope: server.scope } : {}),
|
|
395
|
-
...(config.timeout_ms === undefined
|
|
396
|
-
|
|
397
|
-
|
|
463
|
+
...(config.timeout_ms === undefined
|
|
464
|
+
? {}
|
|
465
|
+
: { timeout_ms: config.timeout_ms }),
|
|
466
|
+
...(config.request_timeout_ms === undefined
|
|
467
|
+
? {}
|
|
468
|
+
: { request_timeout_ms: config.request_timeout_ms }),
|
|
469
|
+
...(config.always_load === undefined
|
|
470
|
+
? {}
|
|
471
|
+
: { always_load: config.always_load }),
|
|
398
472
|
tool_count: server.tools.length,
|
|
399
473
|
configured_tool_policy_count: config.configured_tool_policy_count,
|
|
400
474
|
has_error: typeof server.error === "string" && server.error.length > 0,
|
|
@@ -21,7 +21,12 @@ export async function runMcpAuthMonitor({ signal, poll, maxAttempts = DEFAULT_MA
|
|
|
21
21
|
}
|
|
22
22
|
lastError = errorMessage(error);
|
|
23
23
|
if (attempt === maxAttempts) {
|
|
24
|
-
return {
|
|
24
|
+
return {
|
|
25
|
+
outcome: "exhausted",
|
|
26
|
+
attempts: attempt,
|
|
27
|
+
reason: "error",
|
|
28
|
+
lastError,
|
|
29
|
+
};
|
|
25
30
|
}
|
|
26
31
|
nextDelayMs = Math.min(nextDelayMs * 2, maxDelayMs);
|
|
27
32
|
continue;
|