Commit Graph

142 Commits

Author SHA1 Message Date
roboomp f6005e0d67 fix(mcp): hid stdio windows subprocess consoles
Set windowsHide for every Windows stdio MCP spawn path so direct .exe servers no longer open a visible cmd.exe window.

Added a regression test covering direct Windows executable MCP server launch options.

Fixes #3535
2026-06-26 06:58:19 +00:00
roboomp 132531ee47 fix(mcp/oauth): preserve advertised origin resource indicators
Review on PR #3503 caught the last provenance edge case: some servers can
explicitly advertise an origin-only resource equal to the authorization-server
origin. That value is still authoritative provider metadata and must be sent;
only OMP-synthesized fallback resources should be stripped.

- `filterResourceIndicator` now strips same-origin values only when `stripSameOriginResource` is set. Provider-advertised `oauth.resource` and authorization-URL `?resource=` values preserve both origin-only and path-scoped forms.
- Updated grant tests to preserve advertised origin resources, trailing-slash origin resources, and URL-embedded origin resources while still stripping fallback origin/path resources for Plane.
- Updated refresh tests to preserve advertised origin resources and strip only fallback origin/path resources.
- Updated changelog wording to describe fallback-only stripping.

Fixes #3502
2026-06-25 22:04:49 +00:00
roboomp 3c3a552d49 fix(mcp/oauth): preserve authorization-url embedded path resources
Review on PR #3503 caught one remaining provenance hole: a provider can embed a
path-scoped same-host `resource` directly in the authorization URL while
`oauth.resource` remains undefined. The controller marks its separate
`config.url` resource as fallback, but the URL-embedded parameter is still
provider-authored and must not inherit the fallback same-origin stripping
policy.

- `generateAuthUrl` now filters an existing `?resource=` from the authorization URL with the path-preserving default, regardless of `stripSameOriginResource` on the caller-supplied fallback resource.
- Added a regression that simulates `authorize?resource=https://gateway.example.com/svc/mcp` plus fallback `resource=https://gateway.example.com`; the authorize URL, `flow.resource`, and token request all preserve `/svc/mcp`.
- Updated the changelog to explicitly mention authorization-URL-embedded path resources.

Fixes #3502
2026-06-25 21:55:59 +00:00
roboomp 80c0beb84a fix(mcp/oauth): preserve advertised path-scoped resource indicators
Review on PR #3503 caught that the broad same-origin filter broke valid
protected-resource discovery shapes such as
`https://gateway.example.com/my-service/mcp`: same host as the authorization
server, but a distinct MCP service identified by path. Those advertised
resources must be preserved for audience selection.

- Replaced the unconditional same-origin filter with a provenance-aware policy: exact auth-server-origin resources are always stripped, path-scoped same-origin resources are preserved by default, and only OMP-synthesized fallback resources opt into same-origin path stripping.
- Added `stripSameOriginResource` to `MCPOAuthConfig` and `RefreshMCPOAuthTokenOptions`; quick-add/reauth set it only when the resource came from `config.url` / `runtimeBaseConfig.url` fallback rather than `oauth.resource` or an existing auth resource.
- Refresh uses the same flag when `MCPManager.prepareConfig` falls back to `config.url`, and no longer persists fallback resources into the credential as if they were provider-advertised material.
- Updated RFC 8707 tests to cover both sides: gateway path resource preserved, Plane-style fallback `/http/mcp` stripped, refresh path mirrors the same distinction.

Fixes #3502
2026-06-25 21:48:14 +00:00
roboomp f7fa80e00e fix(mcp/oauth): strip same-origin path resource indicators too
Plane also rejects `resource=https://mcp.plane.so/http/mcp`, not only the bare
origin forms. That means the MCP OAuth resource filter must treat any resource
URL on the authorization-server origin as redundant for these MCP servers, not
just exact origin/origin-slash values.

- Broadened the filter to compare `new URL(resource).origin` with the persisted authorization-server origin.
- Updated grant and refresh RFC 8707 tests: same-origin path resources such as `/http/mcp` are now stripped from authorize, token exchange, and refresh; cross-origin resources remain preserved.
- Updated docs/changelog wording from exact self-referential origin to same-origin resource indicators.

Verified live against `https://mcp.plane.so/authorize`: patched `MCPOAuthFlow` with `resource=https://mcp.plane.so/http/mcp` generates no `resource` parameter and Plane redirects to `/consent?txn_id=…`.

Fixes #3502
2026-06-25 21:36:32 +00:00
roboomp 11c10640f2 fix(mcp/oauth): persist authorization-server origin so refresh filters against it
Review on PR #3503 flagged that the prior fix anchored the initial-grant filter
on `authorizationUrl` but the refresh filter on `tokenUrl`. RFC 8414 lets the
authorize and token endpoints sit on different origins, so when they do, a
`config.url` fallback equal to the auth-server origin survives the refresh
filter — the credential works until expiry, then refresh resurrects the same
self-referential `resource` the authorize/token exchange intentionally
omitted.

- `MCPStoredOAuthCredential.authorizationUrl?: string` — new field, the issuer the grant was minted against.
- `MCPOAuthFlow.authorizationUrl` getter exposes the value so the persistence site can write it (symmetric with `flow.resource`).
- `refreshMCPOAuthToken` accepts `{ authorizationUrl }` via the trailing options object; filters self-referential indicators against the supplied URL, falling back to `tokenUrl`'s origin for legacy credentials. New `RefreshMCPOAuthTokenOptions` interface keeps the positional resource form working.
- `mcp-command-controller.ts` persists `flow.authorizationUrl` on credential write; `manager.ts` extracts it from the embedded credential material (legacy `MCPAuthConfig` rows lack it and continue through the `tokenUrl` fallback) and threads it to `refreshMCPOAuthToken`.
- Tests: 3 new cross-origin refresh cases — stripped when resource equals auth-server origin with cross-origin token endpoint; preserved when resource points at a third origin; legacy `tokenUrl`-anchored fallback still works without `authorizationUrl`. Plus a `flow.authorizationUrl` getter test. Updated `mcp-manager-oauth-refresh.test.ts` to account for the new opts arg.

Fixes #3502
2026-06-25 21:04:58 +00:00
roboomp dbff881fba fix(mcp/oauth): apply self-referential resource filter on refresh too
When the initial grant strips a self-referential resource (per the previous
commit), the credential is stored with `resource: undefined`. On the next
refresh, `MCPManager.prepareConfig` (`packages/coding-agent/src/mcp/manager.ts:1232-1233`)
falls back from `material?.resource` to `config.url` and pipes it into
`refreshMCPOAuthToken` — re-introducing the same self-referential value that
broke the initial authorize against strict servers like Plane. RFC 8707 §2.2
also requires the token-request indicator to match the authorize indicator,
so dropping in one mandates dropping in the other.

- Hoisted `filterSelfReferentialResource(resource, serverUrl)` to module scope so the class-method form and the free `refreshMCPOAuthToken` share the rule. `MCPOAuthFlow.#filterResourceIndicator` is now a thin delegate.
- `refreshMCPOAuthToken` now passes the resource through the same filter, using `tokenUrl` as the origin yardstick (RFC 8414 puts authorize and token endpoints on the same issuer, and MCP discovery follows that contract).
- Added 3-test `RFC 8707 resource indicator (refresh)` suite covering: resource equals token-server origin (stripped), origin with trailing slash (stripped), and a path-bearing resource (preserved).

Fixes #3502
2026-06-25 20:54:16 +00:00
roboomp 093243bff5 fix(mcp/oauth): drop self-referential resource indicator from authorize/token requests
Some MCP authorization servers reject `resource=<auth-server-origin>` with
`server_error&error_description=An+unexpected+error+occurred` before the
consent screen is shown. Plane (`https://mcp.plane.so`) is the live example:
`resource=https://mcp.plane.so` or `https://mcp.plane.so/` errors; the same
authorize request with no resource (or a path-bearing resource like
`https://mcp.plane.so/sse`) succeeds.

Per RFC 8707 §2 the resource indicator distinguishes *other* resource servers
from the authorization server, so a self-referential value is never required.
`MCPOAuthFlow` now strips a resource that equals the authorization-server
origin (with or without trailing slash) in three places:

- the constructor, when resolving the configured resource;
- `generateAuthUrl()`, including the URL-override path that re-reads `?resource=` from the authorize URL;
- `exchangeToken()`, which reads `this.#resource` (RFC 8707 §2.2 requires the token request indicator to match the authorize request, so dropping in one mandates the other).

Verified live against `https://mcp.plane.so/authorize`: pre-fix the generated
URL produces `302 → /callback?error=server_error&…`; post-fix it produces
`302 → /consent?txn_id=…`.

Fixes #3502
2026-06-25 20:48:30 +00:00
roboomp 184f6dd809 style: bun run fix 2026-06-25 11:41:56 +00:00
roboomp 60348404a0 fix(mcp): omitted unused optional tool args
Pruned empty optional MCP argument placeholders before tools/call while preserving required fields and meaningful falsy values.

Added regression coverage for active and deferred MCP tools.

Fixes #3302
2026-06-23 10:29:36 +00:00
roboomp 0b88ab32f8 fix(mcp): wrap unresolvable Windows commands in cmd.exe for PATHEXT lookup
Bun.spawn -> CreateProcess only appends `.exe` to extensionless names; `.cmd`/`.bat` are never tried. Bare MCP commands like `npx` (which exists only as `npx.cmd` on Windows) crashed the subprocess ~140ms after spawn with ENOENT/EINVAL whenever our own PATH walk couldn't pin the file down (empty `Bun.env.PATH` under a restricted parent process, UNC mounts that reject `fs.access`, locked-down shells).

`resolveStdioSpawnCommand` now routes any unresolved bare command through `cmd.exe /d /s /c` so Windows's PATHEXT search runs Windows-native. Direct-spawn fast path is preserved for resolved `.exe`/`.com` files; the existing `.cmd`/`.bat` wrap is unchanged. The reporter's stated cause ("process.env not merged") was incorrect — the merge has been in place at `transports/stdio.ts:316-319` since well before 16.1.14 — but the symptom they hit is real.

Fixes #3250
2026-06-22 10:49:42 +00:00
roboomp 75b21611f7 fix(mcp): sanitized startup server names
Sanitized MCP server names before status formatting so configured keys cannot leak home paths, tabs, newlines, or oversized text into the TUI.

Fixes #3150
2026-06-20 23:43:16 +02:00
roboomp 8b2012420f fix(mcp): sanitized startup failure status
Sanitized MCP failure text before it reaches the startup status formatter, including tabs, newlines, home paths, and long server output.

Fixes #3150
2026-06-20 21:23:27 +00:00
roboomp 655fed1e48 fix(mcp): updated startup connection status
Emitted MCP connection lifecycle events through the startup event bus so the TUI can replace the initial connecting banner with connected, pending, or failed server state.

Added manager and interactive-mode coverage for mixed success/failure MCP startup updates.

Fixes #3150
2026-06-20 21:14:41 +00:00
can1357 50bc9353aa fix(mcp): keep resources when resources/templates/list unimplemented (#2838) 2026-06-18 02:46:48 +02:00
can1357 adaa032333 style(coding-agent/mcp): reformatted MCP render functions for multiline callback formatting
- Reflowed `renderMCPCall` and `renderMCPResult` `WidthAwareText` calls over multiline formatting only.
2026-06-17 20:42:46 +02:00
can1357 c81adde1f1 fix(coding-agent/mcp): made MCP rendering adapt to available content width
- Wrapped MCP call and result renderers in WidthAwareText so formatting can use runtime content width.
- Computed inline argument preview budget from the available content width instead of a fixed 70-character cap.
- Updated raw text truncation in MCP results to target the measured content width while preserving expand hints and warnings.
2026-06-17 19:16:12 +02:00
jms830 6a0e80ee56 fix(mcp): treat resources/templates/list -32601 as empty, not a resource-load failure 2026-06-16 21:25:03 -04:00
can1357 eaba315cbd security(coding-agent): sanitized artifact names and wrapped extension and MCP tools
- Sanitized artifact filenames by normalizing tool names before composing spill paths.
- Applied `wrapToolWithMetaNotice` to custom tool adapters and RPC-host tools in agent-session setup.
- Wrapped SDK-registered extension/custom tools with the same meta-notice adapter during session creation.
2026-06-17 01:53:24 +02:00
can1357 3f82589ec1 fix: fixed OAuth and profile-boundary regressions across CLI and env handling
- Fixed OAuth credentials to keep unknown fields in schema while preserving existing shape checks.
- Fixed MCP OAuth IDs to be profile-scoped and avoid deleting credentials from non-active profiles.
- Fixed string-flag parsing so PROFILE_BOOTSTRAP_BOUNDARY tokens are not consumed as values.
- Fixed active-profile directory resolution to refresh after env updates so profile .env overrides apply.
2026-06-15 03:20:45 +02:00
Ogrodev f9bc96e96c fix(coding-agent): harden profile auth shipping gaps 2026-06-14 20:30:50 -03:00
Ogrodev 0123a46f83 Merge remote-tracking branch 'upstream/main' into feat/profiles-and-alias
# Conflicts:
#	packages/coding-agent/src/cli/args.ts
2026-06-14 19:10:31 -03:00
metaphorics 8e21f4b41d fix(coding-agent): route MCP connecting banner through the render tree
Deferred MCP discovery wrote 'Connecting to MCP servers: …' straight to process.stderr while the TUI owned the terminal, overdrawing the chat input box border. onMCPConnecting now emits McpConnectingEvent on the mcp:connecting channel; InteractiveMode subscribes and renders it via showStatus (status container), mirroring the LSP-startup pattern. New mcp/startup-events.ts holds the channel, type, and formatMCPConnectingMessage.
2026-06-14 17:24:21 +02:00
usr_bin_roygbiv 15d481c84e fix(mcp): prevent stdio transport close from hanging on blocked read loop 2026-06-14 00:59:20 -05:00
Ogrodev 6450d3b46a Merge remote-tracking branch 'upstream/main' into feat/profiles-and-alias
# Conflicts:
#	packages/coding-agent/src/cli/args.ts
2026-06-13 18:05:16 -03:00
can1357 a2e63c70b0 feat(coding-agent): enabled discovered MCP server auth and namespaced reauth targets
- Allowed colon-separated MCP server names in validation and updated validation errors.
- Added discovery-aware MCP config resolution for /mcp auth, test, and unauth flows.
- Changed streaming behavior so superseded agent_end events don't stop active loaders.
2026-06-13 14:31:33 +02:00
Ogrodev 6c844c86a1 Merge remote-tracking branch 'upstream/main' into feat/profiles-and-alias 2026-06-12 07:50:40 -03:00
can1357 1ceae07764 fix(coding-agent): prevented non-node Windows cmd shims from being launched as node
- Updated the Windows npm shim resolver to resolve shim files against `cwd` and inspect `_prog` before treating a batch wrapper as a node launcher.
- Added a node-only guard so non-node wrappers, such as python shims, fall back to standard cmd.exe execution.
- Adjusted mcp stdio tests with a new non-node shim fixture and a simplified notify race case to validate transport teardown behavior.
2026-06-12 10:01:55 +02:00
roboomp 4938f47254 fix(mcp): preserved cwd precedence for unqualified .cmd commands
Restored cmd.exe's lookup order on Windows so an unqualified MCP command (e.g. server.cmd) checks the configured cwd before iterating PATH, keeping a project-local shim from being shadowed by a same-named global one.
2026-06-12 07:28:13 +00:00
roboomp aa862b8b32 fix(mcp): launched npm cmd shims directly
Resolved Windows npm-generated .cmd MCP shims to their Node entrypoint before spawning so CodeGraph keeps ownership of stdio instead of disconnecting behind a transient cmd.exe wrapper.

Fixes #2367
2026-06-12 07:22:10 +00:00
Ogrodev ef3ae501fb Merge upstream/main into feat/profiles-and-alias 2026-06-11 13:07:52 -03:00
Can Bölük d35efd67e4 Merge branch 'main' into fix-mcp-oauth-resource 2026-06-11 15:53:15 +02:00
roboomp ae49f83913 fix(mcp): hid windows cmd stdio shims
Wrap Windows MCP .cmd/.bat stdio launches through a hidden cmd.exe invocation so PATH shims such as npx keep stdio attached without flashing a console.

Fixes #2287
2026-06-11 04:00:05 +00:00
Ogrodev 2f60eaf938 feat(coding-agent): bind MCP OAuth credentials per profile via url-keyed ids
Store MCP OAuth credentials under deterministic mcp_oauth:<url> ids in each
profile's agent.db with refresh material embedded, so a definition-only entry
in a shared project mcp.json resolves each profile's own credential instead
of profiles clobbering each other's auth.credentialId pointer.

- Refresh material is single-source: embedded credential fields win over the
  config auth block (which may belong to another profile); legacy rows fall
  back to the auth block wholesale
- Wire the 401 refresh hook off the resolvable credential, not the auth
  block, so definition-only bindings refresh mid-session too
- The url-keyed fallback never overrides a pinned Authorization header
- Send prompt=consent by default (oauth.prompt to override, "" to omit) so
  reauth can switch accounts past an active browser session
- /mcp reauth fails fast on stdio transports (with an mcp-remote ~/.mcp-auth
  hint), probes http/sse without OAuth injection, GCs the superseded legacy
  row only after the flow succeeds, and leaves definition-only entries
  untouched on disk
- DCR-issued client secrets stay embedded in the stored credential and are
  never written into config files; user-supplied secrets survive reauth
2026-06-10 18:40:07 -03:00
Clark Tomlinson df97002df9 fix: include MCP OAuth resource indicator 2026-06-10 15:58:25 -04:00
can1357 7b71a6016d fix(coding-agent): routed Windows batch stdio launches through cmd.exe
- Added Windows batch-command detection and COMSPEC-based cmd.exe resolution for MCP stdio spawns.
- Escaped and quoted batch command arguments, then routed .cmd and .bat commands through cmd /d /s /c.
- Updated the stdio transport tests to assert wrapped cmd.exe command arrays and escaping behavior.
2026-06-10 04:40:08 +02:00
roboomp 376675253f fix(mcp): preserved windows cmd argv launch
Resolved the Windows stdio MCP regression by keeping resolved .cmd shims on the direct argv spawn path instead of rewriting them through cmd.exe /c. Added regression coverage for explicit and PATHEXT-resolved codegraph.cmd commands.\n\nFixes #2220
2026-06-10 02:18:35 +00:00
can1357 39c08f5434 fix(coding-agent): stopped web-search query mangling and API-key log leakage
removed the rewrite replacing every 202x with the current year (corrupted CVE ids); MCP request logs redact key/token/secret/auth query params; fetch honors declared charsets, surfaces transport causes, retries 429 once abort-aware, flags mid-stream truncation, stops double-downloading binaries; browser tab reopen/registry/single-flight races fixed, queued opens honor abort, init failures release the temp hold; MCP calls get a default timeout and per-line SSE parse guards.
2026-06-10 01:28:04 +02:00
roboomp db351abe49 fix(mcp): escaped cmd shim quotes
Escaped literal double quotes with cmd caret syntax before invoking Windows .cmd MCP shims so JSON args cannot break out of the quoted argument.

Refs #2174
2026-06-09 08:35:44 +00:00
roboomp 6b4bcb81e4 fix(mcp): preserved percent args in cmd shims
Escaped literal percent signs before joining Windows cmd.exe shim command strings so MCP server args are not consumed by cmd environment expansion.

Refs #2174
2026-06-09 08:30:16 +00:00
roboomp b48960e63e fix(mcp): resolved windows stdio shims
Resolved Windows stdio MCP commands through PATHEXT before spawning so tools installed as .cmd shims launch from bare command configs.

Fixes #2174
2026-06-09 08:16:45 +00:00
can1357 eb1a46baf5 feat: added injectable fetch transport across AI and coding network flows
- Added optional FetchImpl fields to compaction, proxy, AI, coding-agent, and mnemopi options.
- Threaded injected fetch implementations through OAuth, discovery, and search/LLM request flows.
- Removed exported hookFetch utility and its package entrypoint from utils.
- Replaced global-fetch test monkeypatching with per-test FetchImpl mocks across test suites.
2026-06-09 04:51:17 +02:00
can1357 48e6009b67 Merge remote-tracking branch 'origin/farm/daabf133/mcp-startup-no-block-on-slow-servers' 2026-06-09 00:23:48 +02:00
roboomp 54e7ccb468 fix(coding-agent): unblock omp startup when an MCP server stalls
MCPManager.connectServers used to fall through to an unbounded
Promise.allSettled over every still-pending server without cached tools,
so a single MCP server stuck waiting on the per-request MCP timeout
(OMP_MCP_TIMEOUT_MS, default 30 000 ms) gated the entire UI ready
signal — exactly the 30.282 s stall the reporter observed against
sbox-superdocs in #2100.

Drop the fallback wait. Pending-without-cache servers are left in flight
and their tools surface via the existing background #onToolsChanged ->
refreshMCPTools path the moment the connect completes; failures continue
to log through the background catch handler (gated on
allowBackgroundLogging) so users still see which server failed.

Adds a regression test that spawns an unresponsive stdio MCP fixture
and asserts connectServers returns inside the 250 ms STARTUP_TIMEOUT_MS
window (padded for CI jitter). The same test times out at 15 s without
the patch.

Fixes #2100
2026-06-08 20:41:51 +00:00
can1357 31b6f0bf31 refactor(ai): consolidated provider config into single-source registry
- Derived descriptors, default-model map, env keys, login list, and refresh dispatch from one ProviderDefinition per provider.
- Disabled OpenAI Codex stream obfuscation and interrupted whitespace-only tool-call argument deltas.
- Derived auth-broker callback ports and paste-code login set from the registry.
2026-06-08 18:48:43 +02:00
can1357 bda3102451 ux(coding-agent): updated status glyphs and fixed extension model discovery refresh
- Added status.done and tool.* symbols to theme mappings and presets.
- Replaced generic success glyphs with contextual +/-, tool icons, and warnings.
- Mapped tool/task/job completions to status.done or status.enabled with icon overrides.
- Triggered runtime provider refresh after extension registration and warned on failure.
2026-06-08 18:28:15 +02:00
Guts 83c8105be6 fix(mcp): declare approval tier for MCP tools to prevent hangs in non-yolo mode
MCPTool and DeferredMCPTool now declare approval = 'write' instead of
implicitly defaulting to 'exec'. Without this, the approval system
requires user confirmation for every MCP tool call in non-yolo modes,
but the confirmation prompt never renders in the TUI while streaming,
causing the agent to hang indefinitely.

Also propagate the approval property through customToolToDefinition()
in sdk.ts, which was silently dropping it during CustomTool ->
ToolDefinition conversion.
2026-06-07 16:22:33 +02:00
roboomp 95f64b6142 fix(mcp): clear stale OAuth credential on definitive refresh failure
When an HTTP MCP server returns invalid_grant (or invalid_token / revoked /
plain 401 from the token endpoint) during OAuth refresh, MCPManager
previously logged "MCP OAuth refresh failed, using existing token" and
re-attached the stale access token as Authorization: Bearer on every
subsequent request. The next tool-load 401'd with invalid_token, future
sessions repeated the loop, and the only recovery was to hand-clear the
credential row in agent.db. Reported with Logfire as the trigger; any
remote HTTP MCP that rotates / revokes refresh tokens is affected.

#resolveAuthConfig now reuses pi-ai's isDefinitiveOAuthFailure classifier
(same one auth-broker and AuthStorage use for first-party providers): on
a definitive failure it calls AuthStorage.remove(credentialId), drops the
Bearer entirely, and the next request surfaces a clean auth error so the
user can /mcp reauth <server> (or /mcp unauth) to recover. Transient
failures (network/fetch failed/ECONNREFUSED) still fall back to the
existing token to ride out blips.

Verified with new mcp-manager-oauth-refresh.test.ts (invalid_grant, 401,
transient fallback, happy-path rotation). The full mcp-* test set
(45 tests across 5 files) still passes.

Fixes #1908
2026-06-05 05:47:09 +00:00
can1357 7a7479f7e3 feat(coding-agent/modes): added arrow-key tab switching to dashboards
- Mapped left/right arrows to switch tabs in the extension dashboard.
- Updated agent and extension dashboard footers to show arrow controls.
- Reformatted HTTP transport assignment and reordered an import.
2026-06-04 16:37:50 +02:00
can1357 51865fc1ea test(coding-agent): updated assertions for condensed prompt wording
- Aligned handoff, reminder, and system-prompt expectations with shortened copy.
- Added HTTP transport test for required initialize failures.
- Guarded SSE startup timeout against stale connection races.
2026-06-04 16:35:45 +02:00