@cnwenf/occ 2.1.340 → 2.1.342
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 +5 -5
- package/dist/cli.js +489 -95
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/@cnwenf/occ)
|
|
6
6
|
[](./LICENSE)
|
|
7
7
|
[](https://bun.sh/)
|
|
8
|
-
[](https://docs.anthropic.com/en/docs/claude-code)
|
|
9
9
|
|
|
10
10
|
[简体中文](./README.zh-CN.md) · **English**
|
|
11
11
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
## What is OCC
|
|
15
15
|
|
|
16
|
-
**Open C Code (OCC)** is an open-source coding agent. Its capabilities are aligned with [Claude Code](https://docs.anthropic.com/en/docs/claude-code) (currently tracking `2.1.
|
|
16
|
+
**Open C Code (OCC)** is an open-source coding agent. Its capabilities are aligned with [Claude Code](https://docs.anthropic.com/en/docs/claude-code) (currently tracking `2.1.276` — 2.1.276 fully aligned; 2.1.271–2.1.275 partial: the latest OCC-90 round landed the byte-verified 2.1.276 advisor proxy-400 fix — advisor-model resolution now gated on a first-party `ANTHROPIC_BASE_URL` (official module #489 `!Ia()||` gate), the growthbook experiment key corrected to `tengu_sage_compass2`, strict firstParty advisor enablement with the `CLAUDE_CODE_ENABLE_EXPERIMENTAL_ADVISOR_TOOL` env override and the `_CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL` arm — plus removal of the dead `src/services/api/src/` stub tree and a full 96-entry triage of 2.1.275, per `docs/upstream-version-gap-occ129.md`; prior rounds landed the 2.1.274 MCP auth-stub description secret-leak fix (byte-verified display-sanitizer family port) with NO-OP behavioral verification of the 2.1.274 bash special-variable and worktree nested-expansion hardening (OCC-127), the 2.1.273 gateway-hint request headers, the spinner doubled-ellipsis guard, the shell-mode `!` insert fix, and the subshell-hidden dangerous-`rm` bypass fix, per `docs/upstream-version-gap-occ127.md`; still not landed from the 2.1.270→2.1.272 delta: `/config` panel fullscreen mouse support (PORTABLE-LARGE) and the spinner status ladder (STAGED), plus the pre-existing sandbox per-command `allowed_domains` gap; prior alignment through 2.1.270 landed via OCC-122/OCC-123/OCC-84/OCC-124/OCC-85/OCC-125/OCC-126). The code is fully open, auditable, backdoor-free, and your data stays under your control.
|
|
17
17
|
|
|
18
18
|
If you worry that a closed-source CLI might hide backdoors, or that your code and credentials are uploaded to unauditable services, OCC is for you: all source is open and unobfuscated, the build is reproducible from source, and API credentials are sent only to endpoints you configure.
|
|
19
19
|
|
|
@@ -62,7 +62,7 @@ Requires a valid Anthropic API Key (or AWS Bedrock / Google Vertex / Azure Found
|
|
|
62
62
|
| **Auditability** | Line-by-line reviewable | No |
|
|
63
63
|
| **Telemetry** | Minimal (analytics stubbed) | Standard |
|
|
64
64
|
| **Data sovereignty** | Credentials stay on your machine; requests only to endpoints you configure | Anthropic endpoints |
|
|
65
|
-
| **Capability parity** | Tracks CC `2.1.
|
|
65
|
+
| **Capability parity** | Tracks CC `2.1.276` (2.1.276 fully aligned; 2.1.271–2.1.275 partial) | Reference implementation |
|
|
66
66
|
| **Providers** | Anthropic Direct, Bedrock, Vertex, Azure | Anthropic, Bedrock, Vertex |
|
|
67
67
|
| **Cost** | Free & open-source (MIT) | Subscription |
|
|
68
68
|
| **Build** | Reproducible from source | N/A |
|
|
@@ -143,7 +143,7 @@ Requires [Bun](https://bun.sh/) >= 1.3.11 (use `bun upgrade` — older Bun cause
|
|
|
143
143
|
|
|
144
144
|
```bash
|
|
145
145
|
bun install
|
|
146
|
-
bun run dev # run from source; version prints 2.1.
|
|
146
|
+
bun run dev # run from source; version prints 2.1.276 (dev polyfill; build overrides with pkg.version) when working
|
|
147
147
|
bun run build # output: dist/cli.js (~26 MB, single-file bundle)
|
|
148
148
|
bun test # test suite (Bun test runner)
|
|
149
149
|
bun run lint # Biome lint (formatter disabled to avoid large diffs)
|
|
@@ -157,7 +157,7 @@ For architecture, entry/bootstrap, tool system, UI layer, and module-status deta
|
|
|
157
157
|
|
|
158
158
|
## Status
|
|
159
159
|
|
|
160
|
-
- Tracks Claude Code **`2.1.
|
|
160
|
+
- Tracks Claude Code **`2.1.276`** (2.1.276 fully aligned; 2.1.271–2.1.275 partial — the latest OCC-90 round landed the byte-verified 2.1.276 advisor proxy-400 fix (first-party `ANTHROPIC_BASE_URL` gate on advisor resolution, `tengu_sage_compass2` growthbook key, strict firstParty enablement, `_CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL` arm) and removed the dead `src/services/api/src/` stub tree — see `docs/upstream-version-gap-occ129.md`; prior rounds landed the 2.1.274 MCP auth-stub description secret-leak fix with NO-OP verification of the bash special-variable and worktree nested-expansion hardening (OCC-127, `docs/upstream-version-gap-occ127.md`), the 2.1.273 gateway-hint request headers, the spinner doubled-ellipsis guard, the shell-mode `!` insert fix, and the subshell-hidden dangerous-`rm` bypass fix; still not landed from the 2.1.270→2.1.272 delta: `/config` fullscreen mouse support (PORTABLE-LARGE) and the spinner status ladder (STAGED), plus the pre-existing sandbox per-command `allowed_domains` gap; prior alignment through 2.1.270 landed via OCC-122/OCC-123/OCC-84, OCC-124 + OCC-85, OCC-125, and OCC-126 — see `docs/upstream-version-gap-occ124.md`, `docs/upstream-version-gap-occ125.md`, and `docs/upstream-version-gap-occ126.md`).
|
|
161
161
|
- Published to npm as [`@cnwenf/occ`](https://www.npmjs.com/package/@cnwenf/occ).
|
|
162
162
|
- Many modules are intentionally stubbed or feature-flagged off — see "Disabled / stubbed" above.
|
|
163
163
|
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
globalThis.MACRO={"VERSION":"2.1.
|
|
2
|
+
globalThis.MACRO={"VERSION":"2.1.342","BINARY_NAME":"occ","BUILD_TIME":"2026-09-18T19:34:48.042Z","FEEDBACK_CHANNEL":"","ISSUES_EXPLAINER":"","NATIVE_PACKAGE_URL":"","PACKAGE_URL":"@cnwenf/occ","VERSION_CHANGELOG":""};
|
|
3
3
|
// @bun
|
|
4
4
|
var __create = Object.create;
|
|
5
5
|
var __getProtoOf = Object.getPrototypeOf;
|
|
@@ -96737,6 +96737,9 @@ function getAPIProviderForStatsig() {
|
|
|
96737
96737
|
return getAPIProvider();
|
|
96738
96738
|
}
|
|
96739
96739
|
function isFirstPartyAnthropicBaseUrl() {
|
|
96740
|
+
if (isEnvTruthy(process.env._CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL)) {
|
|
96741
|
+
return true;
|
|
96742
|
+
}
|
|
96740
96743
|
const baseUrl = process.env.ANTHROPIC_BASE_URL;
|
|
96741
96744
|
if (!baseUrl) {
|
|
96742
96745
|
return true;
|
|
@@ -264309,15 +264312,21 @@ function isAdvisorBlock(param) {
|
|
|
264309
264312
|
return param.type === "advisor_tool_result" || param.type === "server_tool_use" && param.name === "advisor";
|
|
264310
264313
|
}
|
|
264311
264314
|
function getAdvisorConfig() {
|
|
264312
|
-
return getFeatureValue_CACHED_MAY_BE_STALE("
|
|
264315
|
+
return getFeatureValue_CACHED_MAY_BE_STALE("tengu_sage_compass2", {});
|
|
264313
264316
|
}
|
|
264314
264317
|
function isAdvisorEnabled() {
|
|
264315
264318
|
if (isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_ADVISOR_TOOL)) {
|
|
264316
264319
|
return false;
|
|
264317
264320
|
}
|
|
264318
|
-
if (
|
|
264321
|
+
if (getAPIProvider() !== "firstParty") {
|
|
264319
264322
|
return false;
|
|
264320
264323
|
}
|
|
264324
|
+
if (isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS)) {
|
|
264325
|
+
return false;
|
|
264326
|
+
}
|
|
264327
|
+
if (isEnvTruthy(process.env.CLAUDE_CODE_ENABLE_EXPERIMENTAL_ADVISOR_TOOL)) {
|
|
264328
|
+
return true;
|
|
264329
|
+
}
|
|
264321
264330
|
return getAdvisorConfig().enabled ?? false;
|
|
264322
264331
|
}
|
|
264323
264332
|
function canUserConfigureAdvisor() {
|
|
@@ -264366,8 +264375,8 @@ Give the advice serious weight. If you follow a step and it fails empirically, o
|
|
|
264366
264375
|
If you've already retrieved data pointing one way and the advisor points another: don't silently switch. Surface the conflict in one more advisor call -- "I found X, you suggest Y, which constraint breaks the tie?" The advisor saw your evidence but may have underweighted it; a reconcile call is cheaper than committing to the wrong branch.`;
|
|
264367
264376
|
var init_advisor = __esm(() => {
|
|
264368
264377
|
init_growthbook();
|
|
264369
|
-
init_betas2();
|
|
264370
264378
|
init_envUtils();
|
|
264379
|
+
init_providers();
|
|
264371
264380
|
init_settings2();
|
|
264372
264381
|
});
|
|
264373
264382
|
|
|
@@ -627164,6 +627173,52 @@ async function fetchAuthServerMetadata(serverName, serverUrl, configuredMetadata
|
|
|
627164
627173
|
...fetchFn && { fetchFn }
|
|
627165
627174
|
});
|
|
627166
627175
|
}
|
|
627176
|
+
function getOAuthCallbackSubmitter(serverName) {
|
|
627177
|
+
return oauthCallbackSubmitters.get(serverName);
|
|
627178
|
+
}
|
|
627179
|
+
function setActiveOAuthPromise(serverName, promise3) {
|
|
627180
|
+
activeOAuthFlows.set(serverName, promise3);
|
|
627181
|
+
promise3.finally(() => {
|
|
627182
|
+
if (activeOAuthFlows.get(serverName) === promise3) {
|
|
627183
|
+
activeOAuthFlows.delete(serverName);
|
|
627184
|
+
}
|
|
627185
|
+
}).catch(() => {});
|
|
627186
|
+
}
|
|
627187
|
+
function getActiveOAuthPromise(serverName) {
|
|
627188
|
+
return activeOAuthFlows.get(serverName);
|
|
627189
|
+
}
|
|
627190
|
+
function createManualCallbackSubmitter(serverName, oauthState, hooks) {
|
|
627191
|
+
return (callbackUrl) => {
|
|
627192
|
+
try {
|
|
627193
|
+
const parsed = new URL(callbackUrl);
|
|
627194
|
+
const code = parsed.searchParams.get("code");
|
|
627195
|
+
const state3 = parsed.searchParams.get("state");
|
|
627196
|
+
const error52 = parsed.searchParams.get("error");
|
|
627197
|
+
if (!code && !error52) {
|
|
627198
|
+
return false;
|
|
627199
|
+
}
|
|
627200
|
+
if (state3 !== oauthState) {
|
|
627201
|
+
logMCPDebug(serverName, "Ignoring manual callback URL whose state belongs to a different flow");
|
|
627202
|
+
return false;
|
|
627203
|
+
}
|
|
627204
|
+
if (error52) {
|
|
627205
|
+
const errorDescription = parsed.searchParams.get("error_description") || "";
|
|
627206
|
+
hooks.cleanup();
|
|
627207
|
+
hooks.rejectFlow(new Error(`OAuth error: ${error52} - ${errorDescription}`));
|
|
627208
|
+
return true;
|
|
627209
|
+
}
|
|
627210
|
+
if (!code) {
|
|
627211
|
+
return false;
|
|
627212
|
+
}
|
|
627213
|
+
logMCPDebug(serverName, "Received auth code via manual callback URL");
|
|
627214
|
+
hooks.cleanup();
|
|
627215
|
+
hooks.resolveCode(code);
|
|
627216
|
+
return true;
|
|
627217
|
+
} catch {
|
|
627218
|
+
return false;
|
|
627219
|
+
}
|
|
627220
|
+
};
|
|
627221
|
+
}
|
|
627167
627222
|
function getServerKey(serverName, serverConfig) {
|
|
627168
627223
|
const configJson = jsonStringify({
|
|
627169
627224
|
type: serverConfig.type,
|
|
@@ -627521,6 +627576,7 @@ async function performMCPOAuthFlow(serverName, serverConfig, onAuthorizationUrl,
|
|
|
627521
627576
|
let server = null;
|
|
627522
627577
|
let timeoutId = null;
|
|
627523
627578
|
let abortHandler = null;
|
|
627579
|
+
let manualSubmitter = null;
|
|
627524
627580
|
const cleanup2 = () => {
|
|
627525
627581
|
if (server) {
|
|
627526
627582
|
server.removeAllListeners();
|
|
@@ -627536,6 +627592,13 @@ async function performMCPOAuthFlow(serverName, serverConfig, onAuthorizationUrl,
|
|
|
627536
627592
|
abortSignal.removeEventListener("abort", abortHandler);
|
|
627537
627593
|
abortHandler = null;
|
|
627538
627594
|
}
|
|
627595
|
+
if (manualSubmitter && oauthCallbackSubmitters.get(serverName) === manualSubmitter) {
|
|
627596
|
+
oauthCallbackSubmitters.delete(serverName);
|
|
627597
|
+
}
|
|
627598
|
+
if (manualSubmitter && oauthFlowRecords.get(serverName)?.submitter === manualSubmitter) {
|
|
627599
|
+
oauthFlowRecords.delete(serverName);
|
|
627600
|
+
}
|
|
627601
|
+
manualSubmitter = null;
|
|
627539
627602
|
logMCPDebug(serverName, `MCP OAuth server cleaned up`);
|
|
627540
627603
|
};
|
|
627541
627604
|
const authorizationCode = await new Promise((resolve53, reject2) => {
|
|
@@ -627563,32 +627626,37 @@ async function performMCPOAuthFlow(serverName, serverConfig, onAuthorizationUrl,
|
|
|
627563
627626
|
}
|
|
627564
627627
|
abortSignal.addEventListener("abort", abortHandler);
|
|
627565
627628
|
}
|
|
627566
|
-
|
|
627567
|
-
|
|
627568
|
-
|
|
627569
|
-
|
|
627570
|
-
|
|
627571
|
-
|
|
627572
|
-
|
|
627573
|
-
|
|
627574
|
-
|
|
627575
|
-
|
|
627576
|
-
|
|
627577
|
-
|
|
627578
|
-
|
|
627579
|
-
|
|
627580
|
-
|
|
627581
|
-
|
|
627582
|
-
|
|
627583
|
-
|
|
627584
|
-
|
|
627585
|
-
|
|
627586
|
-
|
|
627587
|
-
|
|
627588
|
-
|
|
627589
|
-
|
|
627590
|
-
|
|
627591
|
-
}
|
|
627629
|
+
manualSubmitter = createManualCallbackSubmitter(serverName, oauthState, {
|
|
627630
|
+
cleanup: cleanup2,
|
|
627631
|
+
resolveCode: resolveOnce,
|
|
627632
|
+
rejectFlow: rejectOnce
|
|
627633
|
+
});
|
|
627634
|
+
const supersededFlow = oauthFlowRecords.get(serverName);
|
|
627635
|
+
if (supersededFlow) {
|
|
627636
|
+
logMCPDebug(serverName, `Cancelling superseded OAuth flow (epoch ${supersededFlow.epoch}) \u2014 a newer flow is taking over`);
|
|
627637
|
+
supersededFlow.cancel();
|
|
627638
|
+
}
|
|
627639
|
+
const flowRecord = {
|
|
627640
|
+
epoch: ++oauthFlowEpochCounter,
|
|
627641
|
+
submitter: manualSubmitter,
|
|
627642
|
+
cancel: () => {
|
|
627643
|
+
cleanup2();
|
|
627644
|
+
rejectOnce(new AuthenticationCancelledError);
|
|
627645
|
+
}
|
|
627646
|
+
};
|
|
627647
|
+
oauthFlowRecords.set(serverName, flowRecord);
|
|
627648
|
+
oauthCallbackSubmitters.set(serverName, manualSubmitter);
|
|
627649
|
+
try {
|
|
627650
|
+
options?.onWaitingForCallback?.(manualSubmitter, redirectUri, oauthState);
|
|
627651
|
+
} catch (callbackError) {
|
|
627652
|
+
if (oauthCallbackSubmitters.get(serverName) === manualSubmitter) {
|
|
627653
|
+
oauthCallbackSubmitters.delete(serverName);
|
|
627654
|
+
}
|
|
627655
|
+
if (oauthFlowRecords.get(serverName) === flowRecord) {
|
|
627656
|
+
oauthFlowRecords.delete(serverName);
|
|
627657
|
+
}
|
|
627658
|
+
manualSubmitter = null;
|
|
627659
|
+
throw callbackError;
|
|
627592
627660
|
}
|
|
627593
627661
|
server = createServer6((req, res) => {
|
|
627594
627662
|
const parsedUrl = parse14(req.url || "", true);
|
|
@@ -628428,7 +628496,7 @@ function getScopeFromMetadata(metadata) {
|
|
|
628428
628496
|
}
|
|
628429
628497
|
return;
|
|
628430
628498
|
}
|
|
628431
|
-
var import_xss2, AUTH_REQUEST_TIMEOUT_MS = 30000, MAX_LOCK_RETRIES = 5, SENSITIVE_OAUTH_PARAMS, NONSTANDARD_INVALID_GRANT_ALIASES, AuthenticationCancelledError;
|
|
628499
|
+
var import_xss2, AUTH_REQUEST_TIMEOUT_MS = 30000, MAX_LOCK_RETRIES = 5, SENSITIVE_OAUTH_PARAMS, NONSTANDARD_INVALID_GRANT_ALIASES, AuthenticationCancelledError, oauthCallbackSubmitters, activeOAuthFlows, oauthFlowRecords, oauthFlowEpochCounter = 0;
|
|
628432
628500
|
var init_auth11 = __esm(() => {
|
|
628433
628501
|
init_auth10();
|
|
628434
628502
|
init_errors12();
|
|
@@ -628468,6 +628536,9 @@ var init_auth11 = __esm(() => {
|
|
|
628468
628536
|
this.name = "AuthenticationCancelledError";
|
|
628469
628537
|
}
|
|
628470
628538
|
};
|
|
628539
|
+
oauthCallbackSubmitters = new Map;
|
|
628540
|
+
activeOAuthFlows = new Map;
|
|
628541
|
+
oauthFlowRecords = new Map;
|
|
628471
628542
|
});
|
|
628472
628543
|
|
|
628473
628544
|
// src/services/mcp/displaySanitize.ts
|
|
@@ -628532,20 +628603,205 @@ var init_displaySanitize = __esm(() => {
|
|
|
628532
628603
|
INVISIBLE_PATTERN = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}\p{Default_Ignorable_Code_Point}\u2800]|(?!\u0020)\p{Zs}/gu;
|
|
628533
628604
|
});
|
|
628534
628605
|
|
|
628606
|
+
// src/tools/McpAuthTool/mcpAuthStubShared.ts
|
|
628607
|
+
function isRemoteOAuthSession() {
|
|
628608
|
+
return env4.isSSH() || isEnvTruthy(process.env.CLAUDE_CODE_REMOTE);
|
|
628609
|
+
}
|
|
628610
|
+
function extractOAuthRedirectUri(authUrl) {
|
|
628611
|
+
try {
|
|
628612
|
+
const redirectUri = new URL(authUrl).searchParams.get("redirect_uri");
|
|
628613
|
+
if (redirectUri) {
|
|
628614
|
+
return redirectUri;
|
|
628615
|
+
}
|
|
628616
|
+
} catch {}
|
|
628617
|
+
return "http://localhost:<port>/callback";
|
|
628618
|
+
}
|
|
628619
|
+
var MCP_AUTH_TOOL_SUFFIX = "authenticate", MCP_COMPLETE_AUTH_TOOL_SUFFIX = "complete_authentication", TOOLS_BECOME_AVAILABLE_TEXT = "available automatically", TOOLS_NOW_AVAILABLE_SENTENCE = "The server's tools should now be available.";
|
|
628620
|
+
var init_mcpAuthStubShared = __esm(() => {
|
|
628621
|
+
init_env();
|
|
628622
|
+
init_envUtils();
|
|
628623
|
+
});
|
|
628624
|
+
|
|
628625
|
+
// src/tools/McpAuthTool/McpCompleteAuthTool.ts
|
|
628626
|
+
function buildMcpCompleteAuthToolDescription(serverName) {
|
|
628627
|
+
const authenticateToolName = buildMcpToolName(serverName, MCP_AUTH_TOOL_SUFFIX);
|
|
628628
|
+
return `Complete an in-progress OAuth flow for the "${sanitizeServerNameForDisplay(serverName)}" MCP server by submitting the callback URL. Call \`${authenticateToolName}\` first to start the flow and get the authorization URL. ` + "After the user authorizes in their browser, the browser is redirected to a `http://localhost:<port>/callback?code=...&state=...` URL \u2014 " + "on remote sessions that page fails to load, but the URL in the address bar is still valid. Pass that full URL here as `callback_url`.";
|
|
628629
|
+
}
|
|
628630
|
+
function createMcpCompleteAuthTool(serverName, config7, resolveUnexpanded = resolveUnexpandedMcpServers) {
|
|
628631
|
+
const description = buildMcpCompleteAuthToolDescription(serverName);
|
|
628632
|
+
return {
|
|
628633
|
+
name: buildMcpToolName(serverName, MCP_COMPLETE_AUTH_TOOL_SUFFIX),
|
|
628634
|
+
isMcp: true,
|
|
628635
|
+
mcpInfo: { serverName, toolName: MCP_COMPLETE_AUTH_TOOL_SUFFIX },
|
|
628636
|
+
isEnabled: () => true,
|
|
628637
|
+
isConcurrencySafe: () => false,
|
|
628638
|
+
isReadOnly: () => false,
|
|
628639
|
+
toAutoClassifierInput: () => serverName,
|
|
628640
|
+
userFacingName: () => `${serverName} - complete authentication (MCP)`,
|
|
628641
|
+
maxResultSizeChars: 1e4,
|
|
628642
|
+
renderToolUseMessage: () => `Complete authentication for ${serverName} MCP server`,
|
|
628643
|
+
async description() {
|
|
628644
|
+
return description;
|
|
628645
|
+
},
|
|
628646
|
+
async prompt() {
|
|
628647
|
+
return description;
|
|
628648
|
+
},
|
|
628649
|
+
get inputSchema() {
|
|
628650
|
+
return inputSchema45();
|
|
628651
|
+
},
|
|
628652
|
+
async checkPermissions(input2) {
|
|
628653
|
+
return { behavior: "allow", updatedInput: input2 };
|
|
628654
|
+
},
|
|
628655
|
+
async call(input2) {
|
|
628656
|
+
const { callback_url: callbackUrl } = input2;
|
|
628657
|
+
const displayName = sanitizeServerNameForDisplay(serverName);
|
|
628658
|
+
const authenticateToolName = buildMcpToolName(serverName, MCP_AUTH_TOOL_SUFFIX);
|
|
628659
|
+
const submitter = getOAuthCallbackSubmitter(serverName);
|
|
628660
|
+
if (!submitter) {
|
|
628661
|
+
return {
|
|
628662
|
+
data: {
|
|
628663
|
+
status: "error",
|
|
628664
|
+
message: `No OAuth flow is in progress for ${displayName}. Call \`${authenticateToolName}\` first, then retry with the callback URL.`
|
|
628665
|
+
}
|
|
628666
|
+
};
|
|
628667
|
+
}
|
|
628668
|
+
let hasCodeOrError = false;
|
|
628669
|
+
try {
|
|
628670
|
+
const parsed = new URL(callbackUrl);
|
|
628671
|
+
hasCodeOrError = parsed.searchParams.has("code") || parsed.searchParams.has("error");
|
|
628672
|
+
} catch {}
|
|
628673
|
+
if (!hasCodeOrError) {
|
|
628674
|
+
return {
|
|
628675
|
+
data: {
|
|
628676
|
+
status: "error",
|
|
628677
|
+
message: "Invalid callback URL: missing authorization code. Ask the user to paste the full redirect URL from their browser's address bar, including the `?code=...&state=...` query string."
|
|
628678
|
+
}
|
|
628679
|
+
};
|
|
628680
|
+
}
|
|
628681
|
+
if (!submitter(callbackUrl)) {
|
|
628682
|
+
return {
|
|
628683
|
+
data: {
|
|
628684
|
+
status: "error",
|
|
628685
|
+
message: `That callback URL belongs to a different sign-in attempt for ${displayName} (its state does not match the flow in progress), or carries no authorization code. The current flow is still waiting: ask the user for the URL from the page this sign-in opened, then retry.`
|
|
628686
|
+
}
|
|
628687
|
+
};
|
|
628688
|
+
}
|
|
628689
|
+
const activeFlow = getActiveOAuthPromise(serverName);
|
|
628690
|
+
if (!activeFlow) {
|
|
628691
|
+
return {
|
|
628692
|
+
data: {
|
|
628693
|
+
status: "error",
|
|
628694
|
+
message: `The callback URL was accepted by the in-progress OAuth flow for ${displayName}, but that flow was started outside this session's tool path (e.g. \`occ mcp login\` or a headless control channel), so its token exchange cannot be tracked here. The surface that started the flow will report completion \u2014 do not retry this tool.`
|
|
628695
|
+
}
|
|
628696
|
+
};
|
|
628697
|
+
}
|
|
628698
|
+
try {
|
|
628699
|
+
await activeFlow;
|
|
628700
|
+
return {
|
|
628701
|
+
data: {
|
|
628702
|
+
status: "success",
|
|
628703
|
+
message: `Authentication complete for ${displayName}. ${TOOLS_NOW_AVAILABLE_SENTENCE}`
|
|
628704
|
+
}
|
|
628705
|
+
};
|
|
628706
|
+
} catch (err2) {
|
|
628707
|
+
if (err2 instanceof AuthenticationCancelledError) {
|
|
628708
|
+
return {
|
|
628709
|
+
data: {
|
|
628710
|
+
status: "error",
|
|
628711
|
+
message: `The OAuth flow for ${displayName} was cancelled (a newer attempt may have superseded it). Call \`${authenticateToolName}\` again to restart.`
|
|
628712
|
+
}
|
|
628713
|
+
};
|
|
628714
|
+
}
|
|
628715
|
+
return {
|
|
628716
|
+
data: {
|
|
628717
|
+
status: "error",
|
|
628718
|
+
message: `Authentication failed for ${displayName}: ${sanitizeForDisplay(redactMcpErrorDetail(serverName, config7, errorMessage(err2), resolveUnexpanded))}`
|
|
628719
|
+
}
|
|
628720
|
+
};
|
|
628721
|
+
}
|
|
628722
|
+
},
|
|
628723
|
+
mapToolResultToToolResultBlockParam(data, toolUseID) {
|
|
628724
|
+
return {
|
|
628725
|
+
tool_use_id: toolUseID,
|
|
628726
|
+
type: "tool_result",
|
|
628727
|
+
content: data.message
|
|
628728
|
+
};
|
|
628729
|
+
}
|
|
628730
|
+
};
|
|
628731
|
+
}
|
|
628732
|
+
var inputSchema45;
|
|
628733
|
+
var init_McpCompleteAuthTool = __esm(() => {
|
|
628734
|
+
init_v4();
|
|
628735
|
+
init_auth11();
|
|
628736
|
+
init_displaySanitize();
|
|
628737
|
+
init_mcpStringUtils();
|
|
628738
|
+
init_redaction();
|
|
628739
|
+
init_utils9();
|
|
628740
|
+
init_errors();
|
|
628741
|
+
init_mcpAuthStubShared();
|
|
628742
|
+
inputSchema45 = lazySchema(() => exports_external.object({
|
|
628743
|
+
callback_url: exports_external.string().describe("The full callback URL from the browser address bar after authorizing, e.g. http://localhost:<port>/callback?code=...&state=...")
|
|
628744
|
+
}));
|
|
628745
|
+
});
|
|
628746
|
+
|
|
628535
628747
|
// src/tools/McpAuthTool/McpAuthTool.ts
|
|
628748
|
+
function normalizeHostnameForMatch(hostname4) {
|
|
628749
|
+
return hostname4.toLowerCase().replace(/\.$/, "");
|
|
628750
|
+
}
|
|
628751
|
+
function isAnthropicHostedMcpUrl(url3) {
|
|
628752
|
+
if (!url3) {
|
|
628753
|
+
return false;
|
|
628754
|
+
}
|
|
628755
|
+
try {
|
|
628756
|
+
const blocked = new Set(ANTHROPIC_HOSTED_OAUTH_BLOCKED_HOSTS.map(normalizeHostnameForMatch));
|
|
628757
|
+
return blocked.has(normalizeHostnameForMatch(new URL(url3).hostname));
|
|
628758
|
+
} catch {
|
|
628759
|
+
return false;
|
|
628760
|
+
}
|
|
628761
|
+
}
|
|
628762
|
+
function renderMcpRemoveCommand(serverName) {
|
|
628763
|
+
if (!COMMAND_ARG_SAFE_PATTERN.test(serverName)) {
|
|
628764
|
+
return null;
|
|
628765
|
+
}
|
|
628766
|
+
return `occ mcp remove ${serverName}`;
|
|
628767
|
+
}
|
|
628768
|
+
function buildAnthropicHostedMessage(serverName, scope) {
|
|
628769
|
+
const base2 = `"${sanitizeServerNameForDisplay(serverName)}" is Anthropic-hosted and doesn't support local OAuth. ` + "Connect it via Settings \u2192 Connectors on claude.ai (requires " + "`claude login`), then it'll be available here automatically.";
|
|
628770
|
+
const removeCommand = scope === "local" || scope === "project" || scope === "user" ? renderMcpRemoveCommand(serverName) : null;
|
|
628771
|
+
if (!removeCommand) {
|
|
628772
|
+
return base2;
|
|
628773
|
+
}
|
|
628774
|
+
const withRemove = `${base2} Remove the stale entry with: \`${removeCommand}\``;
|
|
628775
|
+
return [...withRemove].length <= 1024 ? withRemove : base2;
|
|
628776
|
+
}
|
|
628777
|
+
function classifyMcpServerAuthBlock(serverName, config7) {
|
|
628778
|
+
if (!isMcpServerAllowedByPolicy(serverName, config7)) {
|
|
628779
|
+
return "managed-policy";
|
|
628780
|
+
}
|
|
628781
|
+
if (config7.scope === "project" && getProjectMcpServerStatus(serverName) !== "approved") {
|
|
628782
|
+
return "project-approval";
|
|
628783
|
+
}
|
|
628784
|
+
return null;
|
|
628785
|
+
}
|
|
628786
|
+
function managedPolicyBlockMessage(serverName) {
|
|
628787
|
+
return `"${sanitizeServerNameForDisplay(serverName)}" is blocked by your organization's managed policy \u2014 it can't be authenticated or reconnected here`;
|
|
628788
|
+
}
|
|
628789
|
+
function projectApprovalBlockMessage(serverName) {
|
|
628790
|
+
return `"${sanitizeServerNameForDisplay(serverName)}" is a project-scope MCP server (.mcp.json) that is not approved for this project \u2014 approve it via /mcp first, then authenticate or reconnect it`;
|
|
628791
|
+
}
|
|
628536
628792
|
function buildMcpAuthToolDescription(serverName, config7, resolveUnexpanded = resolveUnexpandedMcpServers) {
|
|
628537
628793
|
const transport = config7.type ?? "stdio";
|
|
628538
628794
|
const displayOrigin = getMcpErrorEndpoint(serverName, config7, { detail: "origin" }, resolveUnexpanded);
|
|
628539
628795
|
const location = displayOrigin && displayOrigin !== transport ? `${transport} at ${sanitizeDisplayUrl(displayOrigin, 256)}` : transport;
|
|
628540
|
-
return `The "${sanitizeServerNameForDisplay(serverName)}" MCP server (${location}) is installed but requires authentication. ` + `Call this tool to start the OAuth flow \u2014 you'll receive an authorization URL to share with the user. ` + `Once the user completes authorization in their browser, the server's real tools will become
|
|
628796
|
+
return `The "${sanitizeServerNameForDisplay(serverName)}" MCP server (${location}) is installed but requires authentication. ` + `Call this tool to start the OAuth flow \u2014 you'll receive an authorization URL to share with the user. ` + `Once the user completes authorization in their browser, the server's real tools will become ${TOOLS_BECOME_AVAILABLE_TEXT}.`;
|
|
628541
628797
|
}
|
|
628542
628798
|
function createMcpAuthTool(serverName, config7, resolveUnexpanded = resolveUnexpandedMcpServers) {
|
|
628543
628799
|
const transport = config7.type ?? "stdio";
|
|
628544
628800
|
const description = buildMcpAuthToolDescription(serverName, config7, resolveUnexpanded);
|
|
628545
628801
|
return {
|
|
628546
|
-
name: buildMcpToolName(serverName,
|
|
628802
|
+
name: buildMcpToolName(serverName, MCP_AUTH_TOOL_SUFFIX),
|
|
628547
628803
|
isMcp: true,
|
|
628548
|
-
mcpInfo: { serverName, toolName:
|
|
628804
|
+
mcpInfo: { serverName, toolName: MCP_AUTH_TOOL_SUFFIX },
|
|
628549
628805
|
isEnabled: () => true,
|
|
628550
628806
|
isConcurrencySafe: () => false,
|
|
628551
628807
|
isReadOnly: () => false,
|
|
@@ -628560,17 +628816,43 @@ function createMcpAuthTool(serverName, config7, resolveUnexpanded = resolveUnexp
|
|
|
628560
628816
|
return description;
|
|
628561
628817
|
},
|
|
628562
628818
|
get inputSchema() {
|
|
628563
|
-
return
|
|
628819
|
+
return inputSchema46();
|
|
628564
628820
|
},
|
|
628565
628821
|
async checkPermissions(input2) {
|
|
628566
628822
|
return { behavior: "allow", updatedInput: input2 };
|
|
628567
628823
|
},
|
|
628568
628824
|
async call(_input, context7) {
|
|
628825
|
+
const displayName = sanitizeServerNameForDisplay(serverName);
|
|
628826
|
+
const policyBlock = classifyMcpServerAuthBlock(serverName, config7);
|
|
628827
|
+
if (policyBlock === "managed-policy") {
|
|
628828
|
+
return {
|
|
628829
|
+
data: {
|
|
628830
|
+
status: "error",
|
|
628831
|
+
message: `${managedPolicyBlockMessage(serverName)}. Only an organization admin can change this; do not retry or ask the user to enable it.`
|
|
628832
|
+
}
|
|
628833
|
+
};
|
|
628834
|
+
}
|
|
628835
|
+
if (isMcpServerDisabled(serverName)) {
|
|
628836
|
+
return {
|
|
628837
|
+
data: {
|
|
628838
|
+
status: "error",
|
|
628839
|
+
message: `MCP server ${displayName} is disabled. Ask the user to enable it in /mcp before authenticating.`
|
|
628840
|
+
}
|
|
628841
|
+
};
|
|
628842
|
+
}
|
|
628843
|
+
if (policyBlock === "project-approval") {
|
|
628844
|
+
return {
|
|
628845
|
+
data: {
|
|
628846
|
+
status: "error",
|
|
628847
|
+
message: `${projectApprovalBlockMessage(serverName)}. Ask the user to approve it; do not retry until they have.`
|
|
628848
|
+
}
|
|
628849
|
+
};
|
|
628850
|
+
}
|
|
628569
628851
|
if (config7.type === "claudeai-proxy") {
|
|
628570
628852
|
return {
|
|
628571
628853
|
data: {
|
|
628572
628854
|
status: "unsupported",
|
|
628573
|
-
message: `This is a claude.ai MCP connector. Ask the user to run /mcp and select "${
|
|
628855
|
+
message: `This is a claude.ai MCP connector. Ask the user to run /mcp and select "${displayName}" to authenticate.`
|
|
628574
628856
|
}
|
|
628575
628857
|
};
|
|
628576
628858
|
}
|
|
@@ -628578,7 +628860,15 @@ function createMcpAuthTool(serverName, config7, resolveUnexpanded = resolveUnexp
|
|
|
628578
628860
|
return {
|
|
628579
628861
|
data: {
|
|
628580
628862
|
status: "unsupported",
|
|
628581
|
-
message: `Server "${
|
|
628863
|
+
message: `Server "${displayName}" uses ${transport} transport which does not support OAuth from this tool. Ask the user to run /mcp and authenticate manually.`
|
|
628864
|
+
}
|
|
628865
|
+
};
|
|
628866
|
+
}
|
|
628867
|
+
if (isAnthropicHostedMcpUrl(config7.url)) {
|
|
628868
|
+
return {
|
|
628869
|
+
data: {
|
|
628870
|
+
status: "unsupported",
|
|
628871
|
+
message: sanitizeForDisplay(buildAnthropicHostedMessage(serverName, config7.scope), 1024, "none")
|
|
628582
628872
|
}
|
|
628583
628873
|
};
|
|
628584
628874
|
}
|
|
@@ -628587,11 +628877,15 @@ function createMcpAuthTool(serverName, config7, resolveUnexpanded = resolveUnexp
|
|
|
628587
628877
|
const authUrlPromise = new Promise((resolve53) => {
|
|
628588
628878
|
resolveAuthUrl = resolve53;
|
|
628589
628879
|
});
|
|
628590
|
-
const controller = new AbortController;
|
|
628591
628880
|
const { setAppState } = context7;
|
|
628592
|
-
const oauthPromise = performMCPOAuthFlow(serverName, sseOrHttpConfig, (u7) => resolveAuthUrl?.(u7),
|
|
628881
|
+
const oauthPromise = performMCPOAuthFlow(serverName, sseOrHttpConfig, (u7) => resolveAuthUrl?.(u7), undefined, { skipBrowserOpen: true });
|
|
628882
|
+
setActiveOAuthPromise(serverName, oauthPromise);
|
|
628593
628883
|
oauthPromise.then(async () => {
|
|
628594
628884
|
clearMcpAuthCache();
|
|
628885
|
+
if (isMcpServerDisabled(serverName) || !isMcpServerAllowedByPolicy(serverName, config7)) {
|
|
628886
|
+
logMCPDebug(serverName, "OAuth completed but the server is now disabled or policy-blocked; not reconnecting");
|
|
628887
|
+
return;
|
|
628888
|
+
}
|
|
628595
628889
|
const result = await reconnectMcpServerImpl(serverName, config7);
|
|
628596
628890
|
const prefix = getMcpPrefix(serverName);
|
|
628597
628891
|
setAppState((prev) => ({
|
|
@@ -628612,7 +628906,7 @@ function createMcpAuthTool(serverName, config7, resolveUnexpanded = resolveUnexp
|
|
|
628612
628906
|
}));
|
|
628613
628907
|
logMCPDebug(serverName, `OAuth complete, reconnected with ${result.tools.length} tool(s)`);
|
|
628614
628908
|
}).catch((err2) => {
|
|
628615
|
-
logMCPError(serverName, `OAuth flow failed after tool-triggered start: ${errorMessage(err2)}`);
|
|
628909
|
+
logMCPError(serverName, `OAuth flow failed after tool-triggered start: ${sanitizeForDisplay(redactMcpErrorDetail(serverName, config7, errorMessage(err2), resolveUnexpanded), 2000)}`);
|
|
628616
628910
|
});
|
|
628617
628911
|
try {
|
|
628618
628912
|
const authUrl = await Promise.race([
|
|
@@ -628620,29 +628914,37 @@ function createMcpAuthTool(serverName, config7, resolveUnexpanded = resolveUnexp
|
|
|
628620
628914
|
oauthPromise.then(() => null)
|
|
628621
628915
|
]);
|
|
628622
628916
|
if (authUrl) {
|
|
628917
|
+
const completeAuthToolName = buildMcpToolName(serverName, MCP_COMPLETE_AUTH_TOOL_SUFFIX);
|
|
628918
|
+
const redirectUri = extractOAuthRedirectUri(authUrl);
|
|
628919
|
+
const callbackGuidance = isRemoteOAuthSession() ? `
|
|
628920
|
+
|
|
628921
|
+
This session is remote, so after authorizing the browser will try to load \`${redirectUri}?code=...\` and show a connection error \u2014 that's expected. Ask the user to copy the full URL from the browser's address bar and paste it into chat, then call \`${completeAuthToolName}\` with that URL as \`callback_url\`.` : `
|
|
628922
|
+
|
|
628923
|
+
If the browser shows a connection error on the redirect page, ask the user to paste the full URL from the address bar and call \`${completeAuthToolName}\` with it.`;
|
|
628623
628924
|
return {
|
|
628624
628925
|
data: {
|
|
628625
628926
|
status: "auth_url",
|
|
628626
628927
|
authUrl,
|
|
628627
|
-
message: `Ask the user to open this URL in their browser to authorize the ${
|
|
628928
|
+
message: `Ask the user to open this URL in their browser to authorize the ${displayName} MCP server:
|
|
628628
628929
|
|
|
628629
628930
|
${authUrl}
|
|
628630
628931
|
|
|
628631
|
-
Once they complete the flow, the server's tools will become
|
|
628932
|
+
Once they complete the flow, the server's tools will become ${TOOLS_BECOME_AVAILABLE_TEXT}.${callbackGuidance}`
|
|
628632
628933
|
}
|
|
628633
628934
|
};
|
|
628634
628935
|
}
|
|
628635
628936
|
return {
|
|
628636
628937
|
data: {
|
|
628637
628938
|
status: "auth_url",
|
|
628638
|
-
message: `Authentication completed silently for ${
|
|
628939
|
+
message: `Authentication completed silently for ${displayName}. ${TOOLS_NOW_AVAILABLE_SENTENCE}`
|
|
628639
628940
|
}
|
|
628640
628941
|
};
|
|
628641
628942
|
} catch (err2) {
|
|
628943
|
+
const failureDetail = sanitizeForDisplay(redactMcpErrorDetail(serverName, config7, errorMessage(err2), resolveUnexpanded));
|
|
628642
628944
|
return {
|
|
628643
628945
|
data: {
|
|
628644
628946
|
status: "error",
|
|
628645
|
-
message: `Failed to start OAuth flow for ${
|
|
628947
|
+
message: `Failed to start OAuth flow for ${displayName}: ${failureDetail}. Ask the user to run /mcp and authenticate manually.`
|
|
628646
628948
|
}
|
|
628647
628949
|
};
|
|
628648
628950
|
}
|
|
@@ -628656,19 +628958,38 @@ Once they complete the flow, the server's tools will become available automatica
|
|
|
628656
628958
|
}
|
|
628657
628959
|
};
|
|
628658
628960
|
}
|
|
628659
|
-
|
|
628961
|
+
function createMcpAuthStubTools(serverName, config7, resolveUnexpanded = resolveUnexpandedMcpServers) {
|
|
628962
|
+
if (getIsNonInteractiveSession()) {
|
|
628963
|
+
return [];
|
|
628964
|
+
}
|
|
628965
|
+
return [
|
|
628966
|
+
createMcpAuthTool(serverName, config7, resolveUnexpanded),
|
|
628967
|
+
createMcpCompleteAuthTool(serverName, config7, resolveUnexpanded)
|
|
628968
|
+
];
|
|
628969
|
+
}
|
|
628970
|
+
var inputSchema46, ANTHROPIC_HOSTED_OAUTH_BLOCKED_HOSTS, COMMAND_ARG_SAFE_PATTERN;
|
|
628660
628971
|
var init_McpAuthTool = __esm(() => {
|
|
628661
628972
|
init_reject2();
|
|
628662
628973
|
init_v4();
|
|
628974
|
+
init_state();
|
|
628663
628975
|
init_auth11();
|
|
628664
628976
|
init_client12();
|
|
628977
|
+
init_config6();
|
|
628665
628978
|
init_displaySanitize();
|
|
628666
628979
|
init_mcpStringUtils();
|
|
628667
628980
|
init_redaction();
|
|
628668
628981
|
init_utils9();
|
|
628669
628982
|
init_errors();
|
|
628670
628983
|
init_log3();
|
|
628671
|
-
|
|
628984
|
+
init_McpCompleteAuthTool();
|
|
628985
|
+
init_mcpAuthStubShared();
|
|
628986
|
+
inputSchema46 = lazySchema(() => exports_external.object({}));
|
|
628987
|
+
ANTHROPIC_HOSTED_OAUTH_BLOCKED_HOSTS = [
|
|
628988
|
+
"microsoft365.mcp.claude.com",
|
|
628989
|
+
"gmail.mcp.claude.com",
|
|
628990
|
+
"gcal.mcp.claude.com"
|
|
628991
|
+
];
|
|
628992
|
+
COMMAND_ARG_SAFE_PATTERN = /^\w[\w.@-]*$/;
|
|
628672
628993
|
});
|
|
628673
628994
|
|
|
628674
628995
|
// src/utils/mcpWebSocketTransport.ts
|
|
@@ -631869,7 +632190,7 @@ async function getMcpToolsCommandsAndResources(onConnectionAttempt, mcpConfigs)
|
|
|
631869
632190
|
logMCPDebug(name3, `Skipping connection (cached needs-auth)`);
|
|
631870
632191
|
onConnectionAttempt({
|
|
631871
632192
|
client: { name: name3, type: "needs-auth", config: config7 },
|
|
631872
|
-
tools:
|
|
632193
|
+
tools: createMcpAuthStubTools(name3, config7),
|
|
631873
632194
|
commands: []
|
|
631874
632195
|
});
|
|
631875
632196
|
return;
|
|
@@ -631878,7 +632199,7 @@ async function getMcpToolsCommandsAndResources(onConnectionAttempt, mcpConfigs)
|
|
|
631878
632199
|
if (client8.type !== "connected") {
|
|
631879
632200
|
onConnectionAttempt({
|
|
631880
632201
|
client: client8,
|
|
631881
|
-
tools: client8.type === "needs-auth" ?
|
|
632202
|
+
tools: client8.type === "needs-auth" ? createMcpAuthStubTools(name3, config7) : [],
|
|
631882
632203
|
commands: []
|
|
631883
632204
|
});
|
|
631884
632205
|
return;
|
|
@@ -634624,7 +634945,7 @@ async function* queryModel(messages, systemPrompt, thinkingConfig, tools, signal
|
|
|
634624
634945
|
betas.push(ADVISOR_BETA_HEADER);
|
|
634625
634946
|
}
|
|
634626
634947
|
let advisorModel;
|
|
634627
|
-
if (isAgenticQuery && isAdvisorEnabled()) {
|
|
634948
|
+
if (isAgenticQuery && isAdvisorEnabled() && isFirstPartyAnthropicBaseUrl()) {
|
|
634628
634949
|
let advisorOption = options.advisorModel;
|
|
634629
634950
|
const advisorExperiment = getExperimentAdvisorModels();
|
|
634630
634951
|
if (advisorExperiment !== undefined) {
|
|
@@ -793589,6 +793910,7 @@ var exports_toolPool = {};
|
|
|
793589
793910
|
__export(exports_toolPool, {
|
|
793590
793911
|
mergeAndFilterTools: () => mergeAndFilterTools,
|
|
793591
793912
|
isPrActivitySubscriptionTool: () => isPrActivitySubscriptionTool,
|
|
793913
|
+
deferInitialMcpToolsToLiveState: () => deferInitialMcpToolsToLiveState,
|
|
793592
793914
|
applyCoordinatorToolFilter: () => applyCoordinatorToolFilter
|
|
793593
793915
|
});
|
|
793594
793916
|
function isPrActivitySubscriptionTool(name3) {
|
|
@@ -793608,18 +793930,48 @@ function mergeAndFilterTools(initialTools, assembled, mode) {
|
|
|
793608
793930
|
}
|
|
793609
793931
|
return tools;
|
|
793610
793932
|
}
|
|
793611
|
-
|
|
793933
|
+
function isMcpAuthStubName(name3) {
|
|
793934
|
+
return name3.endsWith(`__${MCP_AUTH_TOOL_SUFFIX}`) || name3.endsWith(`__${MCP_COMPLETE_AUTH_TOOL_SUFFIX}`);
|
|
793935
|
+
}
|
|
793936
|
+
function deferInitialMcpToolsToLiveState(initialTools, liveClients, liveTools = []) {
|
|
793937
|
+
if (liveClients.length === 0) {
|
|
793938
|
+
return initialTools;
|
|
793939
|
+
}
|
|
793940
|
+
const clientsWithPrefix = liveClients.map((client8) => ({
|
|
793941
|
+
prefix: getMcpPrefix(client8.name),
|
|
793942
|
+
type: client8.type
|
|
793943
|
+
}));
|
|
793944
|
+
const allPrefixes = clientsWithPrefix.map((entry) => entry.prefix);
|
|
793945
|
+
const prefixesWithLiveTools = new Set(clientsWithPrefix.filter(({ prefix }) => liveTools.some((t4) => t4.name?.startsWith(prefix))).map(({ prefix }) => prefix));
|
|
793946
|
+
const stubPreservingPrefixes = new Set(clientsWithPrefix.filter(({ prefix, type }) => type !== undefined && AUTH_STUB_PRESERVING_STATES.has(type) && !prefixesWithLiveTools.has(prefix)).map(({ prefix }) => prefix));
|
|
793947
|
+
return initialTools.filter((tool) => {
|
|
793948
|
+
const name3 = tool.name;
|
|
793949
|
+
if (name3 === undefined) {
|
|
793950
|
+
return true;
|
|
793951
|
+
}
|
|
793952
|
+
for (const prefix of stubPreservingPrefixes) {
|
|
793953
|
+
if (name3.startsWith(prefix) && isMcpAuthStubName(name3)) {
|
|
793954
|
+
return true;
|
|
793955
|
+
}
|
|
793956
|
+
}
|
|
793957
|
+
return !allPrefixes.some((prefix) => name3.startsWith(prefix));
|
|
793958
|
+
});
|
|
793959
|
+
}
|
|
793960
|
+
var PR_ACTIVITY_TOOL_SUFFIXES, coordinatorModeModule2, AUTH_STUB_PRESERVING_STATES;
|
|
793612
793961
|
var init_toolPool = __esm(() => {
|
|
793613
793962
|
init_featureFlags();
|
|
793614
793963
|
init_partition();
|
|
793615
793964
|
init_uniqBy();
|
|
793616
793965
|
init_tools();
|
|
793966
|
+
init_mcpStringUtils();
|
|
793617
793967
|
init_utils9();
|
|
793968
|
+
init_mcpAuthStubShared();
|
|
793618
793969
|
PR_ACTIVITY_TOOL_SUFFIXES = [
|
|
793619
793970
|
"subscribe_pr_activity",
|
|
793620
793971
|
"unsubscribe_pr_activity"
|
|
793621
793972
|
];
|
|
793622
793973
|
coordinatorModeModule2 = feature("COORDINATOR_MODE") ? (init_coordinatorMode(), __toCommonJS(exports_coordinatorMode)) : null;
|
|
793974
|
+
AUTH_STUB_PRESERVING_STATES = new Set(["failed", "disabled"]);
|
|
793623
793975
|
});
|
|
793624
793976
|
|
|
793625
793977
|
// src/hooks/useMergedTools.ts
|
|
@@ -798942,13 +799294,13 @@ function permissionPromptToolResultToPermissionDecision(result, tool, input2, to
|
|
|
798942
799294
|
decisionReason
|
|
798943
799295
|
};
|
|
798944
799296
|
}
|
|
798945
|
-
var
|
|
799297
|
+
var inputSchema47, decisionClassificationField, PermissionAllowResultSchema, PermissionDenyResultSchema, outputSchema41;
|
|
798946
799298
|
var init_PermissionPromptToolResultSchema = __esm(() => {
|
|
798947
799299
|
init_v4();
|
|
798948
799300
|
init_debug();
|
|
798949
799301
|
init_PermissionUpdate();
|
|
798950
799302
|
init_PermissionUpdateSchema();
|
|
798951
|
-
|
|
799303
|
+
inputSchema47 = lazySchema(() => v4_default.object({
|
|
798952
799304
|
tool_name: v4_default.string().describe("The name of the tool requesting permission"),
|
|
798953
799305
|
input: v4_default.record(v4_default.string(), v4_default.unknown()).describe("The input for the tool"),
|
|
798954
799306
|
tool_use_id: v4_default.string().optional().describe("The unique tool use request ID")
|
|
@@ -805689,6 +806041,7 @@ function REPL({
|
|
|
805689
806041
|
const combinedInitialTools = import_react321.useMemo(() => {
|
|
805690
806042
|
return [...localTools, ...initialTools];
|
|
805691
806043
|
}, [localTools, initialTools]);
|
|
806044
|
+
const effectiveInitialTools = import_react321.useMemo(() => deferInitialMcpToolsToLiveState(combinedInitialTools, mcp2.clients, mcp2.tools), [combinedInitialTools, mcp2.clients, mcp2.tools]);
|
|
805692
806045
|
useManagePlugins({
|
|
805693
806046
|
enabled: !isRemoteSession
|
|
805694
806047
|
});
|
|
@@ -805710,7 +806063,7 @@ function REPL({
|
|
|
805710
806063
|
useSwarmInitialization(setAppState, initialMessages, {
|
|
805711
806064
|
enabled: !isRemoteSession
|
|
805712
806065
|
});
|
|
805713
|
-
const mergedTools = useMergedTools(
|
|
806066
|
+
const mergedTools = useMergedTools(effectiveInitialTools, mcp2.tools, toolPermissionContext);
|
|
805714
806067
|
const {
|
|
805715
806068
|
tools,
|
|
805716
806069
|
allowedAgentTypes
|
|
@@ -806726,7 +807079,7 @@ Error: sandbox required but unavailable: ${reason}
|
|
|
806726
807079
|
const computeTools = () => {
|
|
806727
807080
|
const state4 = store.getState();
|
|
806728
807081
|
const assembled = assembleToolPool(state4.toolPermissionContext, state4.mcp.tools);
|
|
806729
|
-
const merged = mergeAndFilterTools(combinedInitialTools, assembled, state4.toolPermissionContext.mode);
|
|
807082
|
+
const merged = mergeAndFilterTools(deferInitialMcpToolsToLiveState(combinedInitialTools, state4.mcp.clients, state4.mcp.tools), assembled, state4.toolPermissionContext.mode);
|
|
806730
807083
|
if (!mainThreadAgentDefinition)
|
|
806731
807084
|
return merged;
|
|
806732
807085
|
return resolveAgentTools(mainThreadAgentDefinition, merged, false, true).resolvedTools;
|
|
@@ -820057,6 +820410,42 @@ var init_remoteIO = __esm(() => {
|
|
|
820057
820410
|
};
|
|
820058
820411
|
});
|
|
820059
820412
|
|
|
820413
|
+
// src/cli/mcpOAuthCallbackControl.ts
|
|
820414
|
+
async function handleOAuthCallbackUrlControl(serverName, callbackUrl, deps) {
|
|
820415
|
+
const submit = deps.getSubmitter(serverName);
|
|
820416
|
+
if (!submit) {
|
|
820417
|
+
deps.respondError(`No active OAuth flow for server: ${serverName}`);
|
|
820418
|
+
return;
|
|
820419
|
+
}
|
|
820420
|
+
let hasCodeOrError = false;
|
|
820421
|
+
try {
|
|
820422
|
+
const parsed = new URL(callbackUrl);
|
|
820423
|
+
hasCodeOrError = parsed.searchParams.has("code") || parsed.searchParams.has("error");
|
|
820424
|
+
} catch {}
|
|
820425
|
+
if (!hasCodeOrError) {
|
|
820426
|
+
deps.respondError(CALLBACK_MISSING_CODE_MESSAGE);
|
|
820427
|
+
return;
|
|
820428
|
+
}
|
|
820429
|
+
const accepted = submit(callbackUrl);
|
|
820430
|
+
if (!accepted) {
|
|
820431
|
+
deps.respondError(CALLBACK_NOT_ACCEPTED_MESSAGE);
|
|
820432
|
+
return;
|
|
820433
|
+
}
|
|
820434
|
+
deps.markManualCallbackUsed(serverName);
|
|
820435
|
+
const authPromise = deps.getAuthPromise(serverName);
|
|
820436
|
+
if (!authPromise) {
|
|
820437
|
+
deps.respondSuccess();
|
|
820438
|
+
return;
|
|
820439
|
+
}
|
|
820440
|
+
try {
|
|
820441
|
+
await authPromise;
|
|
820442
|
+
deps.respondSuccess();
|
|
820443
|
+
} catch (error52) {
|
|
820444
|
+
deps.respondError(error52 instanceof Error ? error52.message : "OAuth authentication failed");
|
|
820445
|
+
}
|
|
820446
|
+
}
|
|
820447
|
+
var CALLBACK_NOT_ACCEPTED_MESSAGE = "Callback URL was not accepted: its state does not match the flow in progress (or it carries no authorization code). The OAuth flow is still waiting \u2014 send the redirect URL from the authorization page this flow opened.", CALLBACK_MISSING_CODE_MESSAGE = "Invalid callback URL: missing authorization code. Please paste the full redirect URL including the code parameter.";
|
|
820448
|
+
|
|
820060
820449
|
// src/utils/streamlinedTransform.ts
|
|
820061
820450
|
function categorizeToolName(toolName) {
|
|
820062
820451
|
if (SEARCH_TOOLS2.some((t4) => toolName.startsWith(t4)))
|
|
@@ -822162,7 +822551,7 @@ function runHeadlessStreaming(structuredIO, mcpClients, commands7, tools, initia
|
|
|
822162
822551
|
};
|
|
822163
822552
|
const buildAllTools = (appState) => {
|
|
822164
822553
|
const assembledTools = assembleToolPool(appState.toolPermissionContext, appState.mcp.tools);
|
|
822165
|
-
let allTools = uniqBy_default(mergeAndFilterTools([...tools, ...sdkTools, ...dynamicMcpState.tools], assembledTools, appState.toolPermissionContext.mode), "name");
|
|
822554
|
+
let allTools = uniqBy_default(mergeAndFilterTools(deferInitialMcpToolsToLiveState([...tools, ...sdkTools, ...dynamicMcpState.tools], appState.mcp.clients, appState.mcp.tools), assembledTools, appState.toolPermissionContext.mode), "name");
|
|
822166
822555
|
if (options.permissionPromptToolName) {
|
|
822167
822556
|
allTools = allTools.filter((tool) => !toolMatchesName(tool, options.permissionPromptToolName));
|
|
822168
822557
|
}
|
|
@@ -822910,8 +823299,8 @@ ${m5.text}
|
|
|
822910
823299
|
}
|
|
822911
823300
|
});
|
|
822912
823301
|
});
|
|
822913
|
-
const
|
|
822914
|
-
const
|
|
823302
|
+
const activeOAuthFlows2 = new Map;
|
|
823303
|
+
const oauthCallbackSubmitters2 = new Map;
|
|
822915
823304
|
const oauthManualCallbackUsed = new Set;
|
|
822916
823305
|
const oauthAuthPromises = new Map;
|
|
822917
823306
|
let claudeOAuth = null;
|
|
@@ -823245,9 +823634,9 @@ ${m5.text}
|
|
|
823245
823634
|
sendControlResponseError(message, `Server type "${config8.type}" does not support OAuth authentication`);
|
|
823246
823635
|
} else {
|
|
823247
823636
|
try {
|
|
823248
|
-
|
|
823637
|
+
activeOAuthFlows2.get(serverName)?.abort();
|
|
823249
823638
|
const controller = new AbortController;
|
|
823250
|
-
|
|
823639
|
+
activeOAuthFlows2.set(serverName, controller);
|
|
823251
823640
|
let resolveAuthUrl;
|
|
823252
823641
|
const authUrlPromise = new Promise((resolve63) => {
|
|
823253
823642
|
resolveAuthUrl = resolve63;
|
|
@@ -823255,7 +823644,7 @@ ${m5.text}
|
|
|
823255
823644
|
const oauthPromise = performMCPOAuthFlow(serverName, config8, (url3) => resolveAuthUrl(url3), controller.signal, {
|
|
823256
823645
|
skipBrowserOpen: true,
|
|
823257
823646
|
onWaitingForCallback: (submit) => {
|
|
823258
|
-
|
|
823647
|
+
oauthCallbackSubmitters2.set(serverName, submit);
|
|
823259
823648
|
}
|
|
823260
823649
|
});
|
|
823261
823650
|
const authUrl = await Promise.race([
|
|
@@ -823315,9 +823704,9 @@ ${m5.text}
|
|
|
823315
823704
|
}).catch((error52) => {
|
|
823316
823705
|
logForDebugging(`MCP OAuth failed for ${serverName}: ${error52}`, { level: "error" });
|
|
823317
823706
|
}).finally(() => {
|
|
823318
|
-
if (
|
|
823319
|
-
|
|
823320
|
-
|
|
823707
|
+
if (activeOAuthFlows2.get(serverName) === controller) {
|
|
823708
|
+
activeOAuthFlows2.delete(serverName);
|
|
823709
|
+
oauthCallbackSubmitters2.delete(serverName);
|
|
823321
823710
|
oauthManualCallbackUsed.delete(serverName);
|
|
823322
823711
|
oauthAuthPromises.delete(serverName);
|
|
823323
823712
|
}
|
|
@@ -823328,33 +823717,15 @@ ${m5.text}
|
|
|
823328
823717
|
}
|
|
823329
823718
|
} else if (message.request.subtype === "mcp_oauth_callback_url") {
|
|
823330
823719
|
const { serverName, callbackUrl } = message.request;
|
|
823331
|
-
|
|
823332
|
-
|
|
823333
|
-
|
|
823334
|
-
|
|
823335
|
-
|
|
823336
|
-
|
|
823337
|
-
|
|
823338
|
-
|
|
823339
|
-
|
|
823340
|
-
} else {
|
|
823341
|
-
oauthManualCallbackUsed.add(serverName);
|
|
823342
|
-
submit(callbackUrl);
|
|
823343
|
-
const authPromise = oauthAuthPromises.get(serverName);
|
|
823344
|
-
if (authPromise) {
|
|
823345
|
-
try {
|
|
823346
|
-
await authPromise;
|
|
823347
|
-
sendControlResponseSuccess(message);
|
|
823348
|
-
} catch (error52) {
|
|
823349
|
-
sendControlResponseError(message, error52 instanceof Error ? error52.message : "OAuth authentication failed");
|
|
823350
|
-
}
|
|
823351
|
-
} else {
|
|
823352
|
-
sendControlResponseSuccess(message);
|
|
823353
|
-
}
|
|
823354
|
-
}
|
|
823355
|
-
} else {
|
|
823356
|
-
sendControlResponseError(message, `No active OAuth flow for server: ${serverName}`);
|
|
823357
|
-
}
|
|
823720
|
+
await handleOAuthCallbackUrlControl(serverName, callbackUrl, {
|
|
823721
|
+
getSubmitter: (name3) => oauthCallbackSubmitters2.get(name3),
|
|
823722
|
+
getAuthPromise: (name3) => oauthAuthPromises.get(name3),
|
|
823723
|
+
markManualCallbackUsed: (name3) => {
|
|
823724
|
+
oauthManualCallbackUsed.add(name3);
|
|
823725
|
+
},
|
|
823726
|
+
respondError: (errorMessageText) => sendControlResponseError(message, errorMessageText),
|
|
823727
|
+
respondSuccess: () => sendControlResponseSuccess(message)
|
|
823728
|
+
});
|
|
823358
823729
|
} else if (message.request.subtype === "claude_authenticate") {
|
|
823359
823730
|
const { loginWithClaudeAi } = message.request;
|
|
823360
823731
|
claudeOAuth?.service.cleanup();
|
|
@@ -825220,6 +825591,22 @@ var init_MCPServerDesktopImportDialog = __esm(() => {
|
|
|
825220
825591
|
jsx_runtime498 = __toESM(require_jsx_runtime(), 1);
|
|
825221
825592
|
});
|
|
825222
825593
|
|
|
825594
|
+
// src/cli/mcpOAuthPrompt.ts
|
|
825595
|
+
function promptForCallbackUrlWithRetry(io, submit) {
|
|
825596
|
+
const ask2 = () => {
|
|
825597
|
+
io.question("> ", (answer) => {
|
|
825598
|
+
if (submit(answer.trim())) {
|
|
825599
|
+
io.close();
|
|
825600
|
+
return;
|
|
825601
|
+
}
|
|
825602
|
+
io.notify(CALLBACK_REJECTED_RETRY_HINT);
|
|
825603
|
+
ask2();
|
|
825604
|
+
});
|
|
825605
|
+
};
|
|
825606
|
+
ask2();
|
|
825607
|
+
}
|
|
825608
|
+
var CALLBACK_REJECTED_RETRY_HINT = "That URL was not accepted: its state does not match the flow in progress (or it carries no authorization code). The flow is still waiting \u2014 paste the full redirect URL from the browser page this login opened:";
|
|
825609
|
+
|
|
825223
825610
|
// node_modules/.bun/@modelcontextprotocol+sdk@1.29.0/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
|
|
825224
825611
|
class ExperimentalServerTasks {
|
|
825225
825612
|
constructor(_server) {
|
|
@@ -826213,10 +826600,17 @@ ${url3}`);
|
|
|
826213
826600
|
console.log(`
|
|
826214
826601
|
After authorizing, paste the full redirect URL here and press Enter:`);
|
|
826215
826602
|
const rl = readline5.createInterface({ input: process.stdin });
|
|
826216
|
-
|
|
826217
|
-
|
|
826218
|
-
|
|
826219
|
-
|
|
826603
|
+
promptForCallbackUrlWithRetry({
|
|
826604
|
+
question: (prompt, onAnswer) => {
|
|
826605
|
+
rl.question(prompt, onAnswer);
|
|
826606
|
+
},
|
|
826607
|
+
close: () => {
|
|
826608
|
+
rl.close();
|
|
826609
|
+
},
|
|
826610
|
+
notify: (message) => {
|
|
826611
|
+
console.log(message);
|
|
826612
|
+
}
|
|
826613
|
+
}, submit);
|
|
826220
826614
|
} : undefined;
|
|
826221
826615
|
try {
|
|
826222
826616
|
await performMCPOAuthFlow(name3, server, onAuthorizationUrl, undefined, {
|
|
@@ -831945,7 +832339,7 @@ var _FEATURE_ALLOWLIST = new Set([
|
|
|
831945
832339
|
var feature2 = (name3) => _FEATURE_ALLOWLIST.has(name3);
|
|
831946
832340
|
if (typeof globalThis.MACRO === "undefined") {
|
|
831947
832341
|
globalThis.MACRO = {
|
|
831948
|
-
VERSION: "2.1.
|
|
832342
|
+
VERSION: "2.1.276",
|
|
831949
832343
|
BINARY_NAME: "occ",
|
|
831950
832344
|
BUILD_TIME: new Date().toISOString(),
|
|
831951
832345
|
FEEDBACK_CHANNEL: "",
|