* fix(vscode): store MCP OAuth in shared settings file like the CLI (ENG-2108)
VSCode stored MCP OAuth tokens in a single mcpOAuthSecrets secrets blob
keyed by sha256(name:url), while the CLI/SDK store per-server oauth state in
cline_mcp_settings.json. The two never interoperated (CLI auth was invisible to
VSCode), and VSCode's read-whole-blob/write-whole-blob through StateManager's
non-refreshing cache meant concurrent windows clobbered each other's tokens.
- Store MCP OAuth state in the shared settings file in @cline/core's format.
- Reads are fresh from disk; writes are scoped read-modify-write of one
server's oauth key via updateMcpServerOAuthState (now atomic temp+rename).
- Replace the vscode:// callback flow with HTTP-based token collection via
authorizeMcpServerOAuth (same local loopback flow the CLI uses).
- Reconnect an unauthenticated server when its tokens appear (e.g. CLI auth).
- One-time migration of legacy mcpOAuthSecrets tokens into the shared file.
- Remove McpOAuthRedirectResolver, mcpOAuthFlow, completeOAuth, and the
mcp-auth URI callback route.
* feat(vscode): add --instances/--random-port to MCP OAuth test server
Lets you start several independent test servers, each on its own OS-assigned
random port, so you can add multiple streamableHttp MCP servers to Cline at
once and exercise concurrent OAuth flows. baseUrl now reflects the actually
bound port so discovery metadata and redirect URIs stay correct under random
ports.
* fix(vscode): stop MCP OAuth handshake writes from livelocking the settings watcher (ENG-2108)
Now that codeVerifier/clientInformation live in the shared settings file, the
MCP SDK's per-connect-attempt saveCodeVerifier() writes were tripping the
settings watcher, which re-entered updateServerConnections -> connectToServer
-> another write, looping forever. It was especially bad with two+
unauthenticated servers, where each server's verifier churn re-triggered the
other (visible as a flickering, ever-changing codeVerifier nonce).
The watcher now compares a connection-relevant fingerprint (full per-server
config minus the oauth block, plus a boolean for whether an access token
exists) and skips writes that only churn OAuth-handshake fields. A token
appearing/disappearing still changes the fingerprint, so CLI/other-window
authorization continues to trigger a reconnect via serverGainedOAuthTokens.
* feat(vscode): print paste-ready MCP settings fragment from OAuth test server
On startup the test server now emits an mcpServers JSON fragment (nested
transport shape, matching cline_mcp_settings.json) alongside the banner, so you
can paste it straight into the settings file instead of hand-writing it. With
--instances the entries get distinct names (oauth-test-1, ...), each carrying
its actual bound port.
* fix(vscode): atomic MCP settings writes + fingerprint gate; drop timer guards (CLINE-2097)
Deleting one MCP server could empty the whole list. Root cause: settings
writes were non-atomic (fs.writeFile), so chokidar (and any other process)
could read a transient empty/torn file mid-write and reconcile to zero servers.
The previous fix only masked this with a per-process isUpdatingClineSettings
boolean cleared on a 300ms timer — it did nothing for the CLI or other windows
and was racy.
Replace both timer guards (isUpdatingClineSettings, isUpdatingFromRemoteConfig)
with two deterministic, process-agnostic mechanisms:
- writeSettingsFile(): atomic temp-file + rename for every settings write, so
any reader always sees a complete file. Holds for any number of concurrent
writers (CLI, multiple windows, SDK OAuth handshake).
- content fingerprint: the watcher reconciles only when the connection-relevant
view changed. writeSettingsFile pre-seeds the fingerprint so our own write is
a no-op, while a genuine change from any other process is still processed.
Because reconcile is idempotent and reads are never torn, a missed
suppression is at worst a redundant reconnect, never data loss.
All RPC writers (toggle disabled, autoApprove x2, timeout, add, delete) and the
remote-config sync now go through writeSettingsFile. Removes all setTimeout(.,
300) flag juggling.
* feat(vscode): add a non-guessable 'frozzle' tool to the MCP OAuth test server
The MCP OAuth test server now serves tools/list + tools/call exposing a
'frozzle' tool whose output cannot be derived without calling it (reverse the
string and swap each letter's case, wrapped in guillemets). This gives an eval
a reliable end-to-end signal that the OAuth-authenticated MCP round-trip really
happened: a correct 'frozzle <text>' answer can't be hallucinated. The
transform is easy to verify at a glance and invertible. Adds frozzle.test.ts.
* fix(sdk): drop lingering OAuth callback sockets on close so deny->approve re-auth works (ENG-2108)
The local OAuth callback server's close() called Server.close(), which only
stops accepting new connections and lets existing keep-alive sockets linger.
The browser / global-fetch connection pool keeps such a socket to the fixed
callback port (1456) alive. So after the user denied an MCP OAuth request and
retried, the retry's approve callback could be delivered over the pooled socket
to the FIRST (already-settled) server. That server's settle() was a no-op, so
waitForCallback() never resolved, finishAuth()/token exchange never ran, and no
token was saved — the server stayed unauthenticated (the deny->approve repro).
Call server.closeAllConnections() in close() so no pooled socket outlives the
server. Adds a regression test driving a keep-alive agent across close().
* fix(vscode): actually reconnect MCP server when toggled back on (ENG-2108)
toggleServerDisabledRPC only flipped the in-memory disabled flag and set status
to 'connecting', but never rebuilt the connection. A disabled server's
connection has no live transport/client, so re-enabling left it stuck on the
yellow 'connecting' indicator forever and never re-advertised its tools to the
agent.
Tear down and rebuild the connection through deleteConnection + connectToServer
(which opens a real transport when enabled, or a disconnected stub when
disabled), then notifyWebviewOfServerChanges so the SDK session's tool list is
refreshed. OAuth state is preserved (deleteConnection doesn't clear it). Adds
McpHub.toggleServerDisabledRPC.test.ts.
* fix(vscode): reload MCP tools silently without chat spam (ENG-2108)
Restarting the SDK session to pick up MCP tool changes appended visible chat
messages ('MCP tools changed - reloading...' and 'MCP tools reloaded
successfully...') plus a completion_result banner. Toggling several servers
piled up many of these. Tool reloading should be transparent.
Emit only the session status transitions (running -> idle) via
emitSessionEvents([], ...) instead of appendAndEmit, so no chat messages or
completion banner are shown. Genuine reload failures still surface an error
message. Updates sdk-mcp-coordinator.test.ts accordingly.
* docs(mcp): clean up comments to describe current behavior
Revise comments across the MCP OAuth and settings code to document the code as
it stands, dropping references to prior implementations, task IDs, and
before/after narration. Also reflow the auth-server regression test to the
repository's formatter. No behavior change.
* fix(vscode): atomic fallback write in remote MCP sync; document sync OAuth I/O
Make the no-McpHub branch of syncRemoteMcpServersToSettings write via an
atomic temp-file + rename so a concurrent reader never observes a torn or
empty settings file, matching every other settings write.
Document why the OAuth state read-modify-write in McpOAuthManager is
synchronous: it serializes this process's shared-file updates without a
Promise queue, which we prefer over async I/O for reliability of the
cross-process settings file.
* fix(mcp): serialize settings read-modify-writes
* docs(vscode): clarify MCP settings create race
* fix(vscode): create MCP settings atomically
* fix(cli): keep clearing missing MCP OAuth state a no-op
* fix(vscode): avoid yielding while holding MCP settings lock (#11596)
* fix(mcp): async lock acquisition for VSCode MCP settings/OAuth writes
Add updateMcpSettingsFile/updateMcpServerOAuthStateAsync to @cline/core that
yield the event loop while acquiring the cross-process settings lock instead of
blocking it with Atomics.wait. The critical section stays synchronous and the
mutator stays pure, so the lock is never held across an await and serialization
is preserved without an in-process queue.
Route the VSCode extension host's OAuth state writes (McpOAuthManager) through
the async variant so a connection-time OAuth callback can no longer freeze the
extension host event loop or deadlock against an in-flight updateMcpSettingsFile
whose lock-releasing continuation needs the loop.
Unify the sync and async acquisition paths on a shared reentrancy guard
(activeLocks) so a nested settings update on the same file fails fast instead of
self-deadlocking.
Tests: contended async serialization asserting zero Atomics.wait calls, async
stale-lock reclaim, reentrancy fail-fast, and uncontended run+release.
* fix(mcp): bootstrap missing settings file inside the lock; tidy docs
Creating the MCP settings file now happens in one place: the locked
read-modify-write helpers. A missing file reads as an empty settings object, so
the first write to a fresh path (e.g. a fresh-install `cline mcp add`) creates
it inside the lock instead of throwing ENOENT. The SDK (updateMcpSettingsFile /
updateMcpSettingsFileSync) and the VSCode lock helper share this contract, so
callers no longer need to pre-create the file. Add regression tests for the
SDK, the CLI wizard addServer(), and the VSCode helper on a missing path.
Also flag the synchronous SDK entry points (updateMcpSettingsFileSync,
updateMcpServerOAuthState) as preferring their async siblings, with a TODO to
delete them once all callers migrate, and tighten the lock-helper doc comments
to describe current behavior.
* fix(vscode): finish npm->bun migration in dev tooling, tasks, and docs
The npm->bun migration (#11632) updated package scripts, .vscodeignore and .vscode-test.mjs but left a trail of npm/npx/node invocations in editor configs, dev scripts, and docs. Following the breadcrumbs from 'npm run protos':
- .vscode/launch.json: standalone-core debug uses 'bun <file>.ts' (was npx tsx); Open Storybook uses 'bun run' (was npm run).
- .vscode/tasks.json: all task commands use 'bun run' (was npm run).
- scripts/run-extension-host.sh and .claude/hooks/claude-code-for-web-setup.sh: 'bun run' (was npm run).
- debug-harness/server.ts: shebang 'bun'; build steps use 'bun run protos', 'bun esbuild.mjs', 'bunx vite build' (were npm/node/npx).
- dev script shebangs (test-hostbridge-server, test-standalone-core-api-server, testing-platform-orchestrator, interactive-playwright): '#!/usr/bin/env bun' (was npx tsx).
- WebviewProvider HMR hint, e2e README, copilot-instructions, PR template, mcp-oauth-test-server docs, generate-state-proto message, tsconfig.test comment, state-keys test comment: bun.
Left untouched (correct per .clinerules/bun-and-node): Node-runtime invocations (node build.mjs), prebuild-install --target=<node>, vsce, 'npm install -g cline' (user CLI install), and App.stories.tsx mock chat fixtures.
---------
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: Cline Agent <cline-agent@users.noreply.github.com>
Omnibus squash of the 10 oldest SDK-migration commits (authored 2026-05-27
through 2026-06-02), collapsed during the 2026-06-09 rebase onto origin/main.
Squashed commits:
- sdk migration: squashed pre-2026-05-27 work
- sdk migration: squashed 06-05-2026 -- instead of listHistory, use host.get(sessionId) instead
- updat gitignore
- fix xai provider
- fix(vscode): forward Bedrock region + AWS auth to the SDK gateway
- fix(vscode): keep in-progress MCP OAuth flow across reconnects
- fix(vscode): wire auto compact into SDK sessions (#11197)
- fix(vscode): compact Codex OAuth before input cap (#11194)
- fix unauthed user flow
- fix(llms): strip Cerebras reasoning history (#11214)
* Make sections expandable and add ClinePass
* fix label
* Map clinePass models to clinePass and the rest to cline
* gate the models behind the feature flag
* Do not allow custom model on cline pass
* Do not show the count on the mode list
* fix clinepass model list
* fix focus when expanding a section
* update the model data when the provider doesn't match
* Add ClinePass to the onboarding screen
* Fix the auth flow not starting
* Hide option behind a feature flag
* Update icon
* Remove unrelated changes
* Hide the custom model id
* fix model names
* remove custom model id option
* fix tests
* Fix names
* Display the clinepass models in the onboarding
* Throw a specific error when the user isn't subscribed
* properly render the error message
* fix error detection
* refactor
* Update the ResponseErrorHandler type
* re-add trailing slash
* Make sections expandable and add ClinePass
* fix label
* Map clinePass models to clinePass and the rest to cline
* gate the models behind the feature flag
* Do not allow custom model on cline pass
* Do not show the count on the mode list
* fix clinepass model list
* fix focus when expanding a section
* update the model data when the provider doesn't match
* Add ClinePass to the onboarding screen
* Fix the auth flow not starting
* Hide option behind a feature flag
* Update icon
* Remove unrelated changes
* Hide the custom model id
* fix model names
* remove custom model id option
* fix tests
* Fix names
* Display the clinepass models in the onboarding
* feat(cli): add cline skill command aliasing the open skills CLI
Adds a 'cline skill' command that forwards to Vercel's open skills CLI
via 'npx -y skills@latest <args>', giving parity with 'cline plugin
install' and 'cline mcp' without reimplementing skill installation.
install/add/i default to '--agent cline' (unless the user passes their
own -a/--agent) so installs land in a directory Cline already scans;
use/list/remove pass through verbatim.
* fix(cli): scope skill update to cline
* Make sections expandable and add ClinePass
* fix label
* Map clinePass models to clinePass and the rest to cline
* gate the models behind the feature flag
* Do not allow custom model on cline pass
* Do not show the count on the mode list
* fix clinepass model list
* fix focus when expanding a section
* update the model data when the provider doesn't match
* Update sdk/packages/core/src/services/llms/cline-recommended-models.ts
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* style: format Cline recommended models fallback
* Build models
---------
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
* Centralize OAuth management to the SDK
* Update mock
* Cleanup TUI cline-account logic
* clean save credentails
* Remove unused code
* Reduce mocks
* use normalizeStoredAccessToken
* Add Cline Pass
* Properly read storageProviderId
* Use the name for the model generation
* Use the model for the capabilities lookup
* Fix capability discovery
* Fix getLastUsedProviderSettings
* remove the provider id from the resolveWithSingleFlight return
* Fix tests
* Remove the entry.name check
* Execute model API calls separetely
* Hide Cline Pass pricing
* update model list
* Unselect the org when selecting Cline Pass
* deduplicate onProviderChange calls
---------
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
* Centralize OAuth management to the SDK
* Update mock
* Cleanup TUI cline-account logic
* clean save credentails
* Remove unused code
* Reduce mocks
* use normalizeStoredAccessToken
* Add Cline Pass
* Properly read storageProviderId
* Use the name for the model generation
* Use the model for the capabilities lookup
* Fix capability discovery
* Fix getLastUsedProviderSettings
* remove the provider id from the resolveWithSingleFlight return
* Fix tests
* Remove the entry.name check
* Execute model API calls separetely
* Hide Cline Pass pricing
* Display cline-pass only if the feature flag is enabled
* unselect cline pass when the feature flag is off
* Store and read feature flag cache
* Add comment
* Do not return userId
* fix tests
* Revert unrelated changes
* Centralize OAuth management to the SDK
* Update mock
* Cleanup TUI cline-account logic
* clean save credentails
* Remove unused code
* Reduce mocks
* use normalizeStoredAccessToken
* Add Cline Pass
* Properly read storageProviderId
* Use the name for the model generation
* Use the model for the capabilities lookup
* Fix capability discovery
* Fix getLastUsedProviderSettings
* remove the provider id from the resolveWithSingleFlight return
* Fix tests
* Remove the entry.name check
* Execute model API calls separetely
* Hide Cline Pass pricing
* update model list
* Address PR feedback
* revert unrelated changes
* Update comment
* Update check
* Introduce PostHog as a Feature Flag provider
* Set-up auth after login
* Update the context when something changes in the CLI
* Make the distinctId not be optional
* Dispose of the feature flag service
* Remove the distinctId from the options
* get rid of isSharedClient
* Remove timeoutMs from the posthog options
* Rename functions to not refer cli
* Change the PostHogFeatureFlagsProvider API
* feat: Enforce a production singleton Cline Hub
This PR changes local Hub startup/discovery so production uses one stable daemon per user machine instead of silently creating additional hubs on random ports.
Replace resolveSharedHubOwnerContext with resolveProductionHubOwnerContext
across doctor and hub server lifecycle management to scope hub discovery
to the production owner.
Additionally:
- Preserve and propagate auth tokens when retiring incompatible hubs
- Throw a clear error when a compatible hub is already running but its
discovery record is missing, guiding users to run 'cline doctor fix'
- Gate port fallback behind an explicit allowPortFallback override
- Update tests to mock the new production hub owner context
* patches
* fix
* hasExplicitPort
* Restored daemon cron startup, made discovery auth tokens required again, and fixed graceful hub stop/restart paths to use the selected production/shared owner context.
* clean up
* patches
* fix Polynomial regular expression
* test
* fix: require explicit hub port fallback in production
* fix(cli): stop pgrep from parsing the hub daemon marker as an option
pgrep treats the "--cline-hub-daemon" pattern as an unknown long option
and exits 2, so doctor never found stale daemons from compiled-binary
installs, which are exactly the processes 'cline doctor fix' is told to
clean up. Pass "--" before the pattern to end option parsing.
* fix(hub): retire legacy shared-owner hubs on production startup
Pre-singleton production builds tracked the local hub under the shared
owner discovery path and spawned daemons on random fallback ports. The
production owner context never reads that path, so upgrades would leave
those daemons running indefinitely with no way to reuse or stop them.
Retire the recorded legacy hub (its record carries the auth token and
pid needed for a graceful stop) and clear the legacy record before
resolving the production hub.
* refactor(hub): simplify stale discovery clearing, share capability list
shouldClearStaleHubDiscovery was only ever called with
discoveredVerified=false (the true assignment sits on a return path),
so the expected-hub probe and compatibility check had no effect and the
condition reduced to "a discovery record exists and was not reused".
Replace it with a plain conditional and drop the tests that exercised
unreachable states.
Also move the hub capability list into a typed HUB_CAPABILITIES
constant in @cline/shared next to HubCapabilityName so the server
cannot drift from the type.
---------
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
* fix(cli): suppress flickering console windows on Windows by setting windowsHide on child processes
On Windows, child_process.spawn/execFile default to windowsHide: false,
so console-subsystem children (powershell, rg, git, node, npm) can
allocate a new visible console window - guaranteed when detached: true
is used. In the CLI this caused constant short-lived window flashes
from run_commands, the git status bar polling, ripgrep searches and
indexing, clipboard helpers, and hook/plugin node subprocesses.
Set windowsHide: true (CREATE_NO_WINDOW; a no-op on non-Windows) on all
remaining spawn/spawnSync/execFile call sites in the SDK core, CLI,
Cline Hub, and example plugins, matching the pattern already used by
the MCP client, checkpoint-hooks, and StandaloneTerminalProcess.
* Update apps/cli/src/commands/kanban.ts
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
---------
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix(cli): connector thread session routing & stale hub session
Fix connector thread session routing and stale hub session recovery
**PR Description**
This fixes connector messages from separate chat threads being routed into the wrong active runtime session.
**Issue**
In Slack, if a user sent a message in a different thread while another thread was still processing, the new message could be treated as a steer message for the active task. Users could also see errors like:
```text
Slack bridge error: session not found: 1780596180501_ms45m
```
when a connector thread had a persisted session id that no longer existed in the hub, such as after a hub restart.
**Cause**
Connector conversation bindings and active turn queues were using participant identity as the primary key in several paths. That allowed messages from the same user in different chat threads to resolve to the same connector session/active turn.
Separately, persisted connector `sessionId` values were trusted without checking whether the hub still had that runtime session. After a hub restart, the connector could try to send input to a stale session id.
**Fix**
- Store connector conversation bindings by thread id instead of participant key.
- Key connector active turn queues by thread id across Slack, Discord, Telegram, Google Chat, Linear, and WhatsApp adapters.
- Only treat a follow-up as a steer message when the active turn belongs to the same thread.
- Keep participant key/label as metadata instead of using it as the conversation binding key.
- Validate a persisted session id with the hub before reusing it.
- If the persisted session is missing, clear it from thread state and start a fresh runtime session.
- Update schedule delivery metadata to target thread ids while preserving participant metadata.
- Add regression coverage for cross-thread active sessions and stale persisted session ids.
**Verification**
```bash
bun -F @cline/cli typecheck
bunx vitest run apps/cli/src/connectors/connector-host.test.ts apps/cli/src/connectors/thread-bindings.test.ts apps/cli/src/connectors/adapters/slack.test.ts apps/cli/src/connectors/adapters/telegram.test.ts apps/cli/src/connectors/adapters/discord.test.ts apps/cli/src/connectors/adapters/gchat.test.ts apps/cli/src/connectors/adapters/linear.test.ts apps/cli/src/connectors/adapters/whatsapp.test.ts
```
* patches
* docs(cli): release the SDK before the CLI in the publish-cli skill
Add a Step 0 to the publish-cli skill that gates a CLI release on an SDK
release when the SDK changed since its last release, and relocate the
publish-cli skill to the repo root.
Why release the SDK alongside the CLI: the CLI bundles the SDK source via
workspace:*, so the CLI always ships the latest SDK code, but the hub
daemon stamps a buildId that defaults to the @cline/core version and a
running hub is only respawned when that buildId changes. Bumping the SDK
version forces a stale hub to be retired and respawned with the new code.
It also keeps SDK releases on a regular cadence in step with the CLI.
Step 0 covers detecting unreleased sdk/packages changes, bumping the
shared SDK version + llms CHANGELOG, committing to main, kicking off
sdk-publish.yml on the latest channel, and waiting for it before cutting
the CLI release. Also fixes the now-stale working-directory note (commands
run from the repo root, not sdk/) and updates the DEVELOPMENT.md path.
* chore: move opentui skill to repo root
Relocate the opentui TUI skill from apps/cli to the repo root, matching
the real-dir + symlink convention used by the other root skills (real dir
in .agents/skills, symlink from .claude/skills).
* docs(sdk): reformat the SDK changelog and move it to sdk/CHANGELOG.md
The changelog covers all SDK packages (they share one version and release
together), so move it from sdk/packages/llms/ to the SDK root, parallel to
apps/cli/CHANGELOG.md. Reformat to match the CLI changelog: a titled
header with flat, version-only sections, newest on top, no dates, and no
Next Release bucket. The unreleased entry that bucket held is captured
from commits when the next SDK release is drafted. Update the publish-cli
skill to draft SDK notes from commits and prepend a ## <version> section
at sdk/CHANGELOG.md.
* fix(slack): normalize channel mentions to original post thread
Route top-level Slack channel mentions to the originating post thread so
replies land in the correct conversation. Add `resolveSlackChannelMentionThread`
to rewrite non-DM mention threads using the message's `thread_ts`/`ts` and
channel, while preserving DM threads and already-correct threads.
Includes unit tests covering normalization, no-op, and DM cases.
* thread id