Skip to content

feat(adapters): cherry-studio chat-client adapter#487

Open
rynfar wants to merge 1 commit into
mainfrom
feat/cherry-studio-adapter
Open

feat(adapters): cherry-studio chat-client adapter#487
rynfar wants to merge 1 commit into
mainfrom
feat/cherry-studio-adapter

Conversation

@rynfar
Copy link
Copy Markdown
Owner

@rynfar rynfar commented May 6, 2026

Closes #481.

Hold on merging. This needs end-user verification in actual Cherry Studio UI before shipping — the adapter is server-side correct but I can't drive a desktop Electron app from CI. @BenIsLegit / @rynfar to confirm before merging.

Why

Cherry Studio is a desktop chat client that, until this PR, falls through to the OpenCode adapter (the default fallback). OpenCode blocks WebSearch and WebFetch because it has MCP equivalents (websearch_web_search_exa). Cherry Studio has no MCP equivalent and no client-side web access — when blocked, the user sees:

WebSearch tool not exposed in the session

and Claude can't search the web. Reported by @BenIsLegit in #481.

What

A new cherry-studio adapter — the first chat-client adapter (every existing adapter is a CLI coding agent). The chat-client shape is the inverse of the coding-agent shape: web tools enabled, filesystem tools off-by-default, no MCP, no passthrough.

Property Coding-agent adapters (OpenCode, Crush, …) Cherry Studio
WebSearch / WebFetch blocked (agent has MCP equivalent) allowed
Read / Write / Bash / etc. passed through to agent blocked by default
usesPassthrough varies (often true) false — SDK runs tools, returns results inline
getAllowedMcpTools mcp__opencode__read, etc. none
buildSdkAgents subagent routing none

Detection

Cherry Studio's User-Agent is overridden (CherryHQ#10209) so auto-detection isn't reliable. Routes via:

  • x-meridian-agent: cherry-studio header (per-request — the documented path)
  • MERIDIAN_DEFAULT_AGENT=cherry-studio env var (global default)

Aliases: cherry-studio, cherrystudio (no hyphen).

Bonus fix: single source of truth for the adapter list

While in there I noticed the settings UI hardcoded ["opencode", "crush", "forgecode", "pi", "droid", "passthrough"] in two places — both drifted from ADAPTER_MAP. claude-code was registered for detection but never rendered in /settings, so its feature toggles were silently inaccessible. New ADAPTER_LABELS / ADAPTER_NAMES exports in detect.ts are the single source; sdkFeatures.ts and settingsPage.ts consume them. Adding cherry-studio AND fixing the latent claude-code gap is automatic. An audit test guards against future drift.

Why filesystem tools are blocked by default

A chat-style LLM shouldn't be able to enumerate ~/.ssh or read /etc/secrets on the proxy host because the user typed "what's in my home directory?" into Cherry Studio. Even on localhost, the user is rarely thinking "this chat sends Read/Bash to the server." Operators who genuinely want filesystem access can use a coding-agent adapter (where tool calls are surveilled by the calling tool) or open a follow-up issue if there's demand for a "trusted chat client" mode with broader access.

Tests

21 new tests in cherry-studio-adapter.test.ts:

  • Identity, session/CWD shape (5)
  • Load-bearing: WebSearch / WebFetch NOT in blocked + incompatible lists (2)
  • Filesystem tools blocked (2)
  • Claude-Code orchestration tools blocked (1)
  • Chat-client behavior toggles — usesPassthrough, supportsThinking, etc. (7)
  • x-meridian-agent detection incl. alias and case-insensitivity (3)
  • Adapter-list audit: every key in ADAPTER_LABELS resolves via ADAPTER_MAP, gets a feature config from getAllFeatureConfigs, and includes cherry-studio + claude-code (4)

Full suite 1742/0. Build clean. Typecheck clean.

End-to-end verification (server-side)

Booted a test proxy on :3500 (separate from prod on :3456, prod undisturbed throughout):

curl -X POST http://127.0.0.1:3500/v1/messages \
  -H 'x-meridian-agent: cherry-studio' \
  -d '{"model":"claude-haiku-4-5","max_tokens":400,"messages":[{"role":"user",
       "content":"Use WebSearch to find the latest version of node.js. Reply with just the version number and a citation URL."}]}'

Result:

  • stop_reason: tool_use (initial turn) → WebSearch invoked with {"query": "latest node.js version 2026"}
  • 4 content blocks: thinkingtool_use(WebSearch)thinkingtext("**26**\n\nSources:\n- [Node.js 26.0.0 (Current)](https://nodejs.org/en/blog/release/v26.0.0)")
  • WebSearch ran, real result returned with citation, thinking blocks forwarded.

So the proxy / SDK / Anthropic side of the chain is verified. Cherry Studio's actual UI rendering of those blocks (citation hyperlinks, streaming, thinking display) can only be verified end-user-side.

Verification asks before merge

@BenIsLegit / @rynfar — please:

  1. Check out this branch (or wait for it to merge to a release).
  2. Configure Cherry Studio's Anthropic provider with:
    • API URL: http://127.0.0.1:3456
    • Custom request header: x-meridian-agent: cherry-studio
  3. Ask Cherry Studio something that should trigger web search — e.g. "what's the latest news about ?"
  4. Confirm:
    • Web search actually fires (Cherry Studio shows search results)
    • Sources/citations render correctly in the UI
    • No "WebSearch tool not exposed" error
    • Per-adapter toggles (Client Prompt, Thinking Passthrough, Thinking) are now visible at /settings under a "Cherry Studio" section
    • Streaming works if Cherry Studio uses streaming responses

If anything's off, comment on the PR and we'll iterate.

Merge strategy

Squash merge. Single commit by @rynfar.

Out of scope (follow-ups, not blocking this PR)

  • Filesystem tool toggle for chat-client adapters (per-feature, like additionalDirectories).
  • Claude Desktop adapter — same archetype, would alias to cherry-studio's shape under a different name.
  • Auto-detection via header sniffing if Cherry Studio adds a stable header in a future release.

Cherry Studio is a desktop Electron chat client. It's the first non-CLI
client to be onboarded — every existing adapter (OpenCode, Crush, Pi,
ForgeCode, Droid, Claude Code) is for a coding agent with its own MCP
tool runtime, but Cherry Studio is a pure chat UI that needs Claude's
server-side tools (WebSearch / WebFetch) to work natively.

Without an adapter, Cherry Studio falls through to OpenCode (the
default), which blocks WebSearch in favor of OpenCode's MCP equivalent.
The user sees "WebSearch tool not exposed in the session" and web
search is silently broken.

The new cherry-studio adapter:
  - Allows WebSearch / WebFetch (verified independently against Max
    OAuth: bundled claude binary + WebSearch returns real results).
  - Blocks filesystem / shell tools by default. A chat-style LLM
    shouldn't enumerate files on the proxy host unsupervised, even on
    localhost. Operators who genuinely want filesystem access can use
    a coding-agent adapter where tool calls are surveilled.
  - Blocks Claude-Code-only orchestration tools (cron, plan/worktree
    mode, etc.) — no chat-client equivalent.
  - usesPassthrough = false. The SDK runs tools internally and folds
    results into the assistant turn — exactly what a chat UI renders.
  - supportsThinking = true. Cherry Studio renders thinking blocks
    when the user enables them in its UI.
  - No subagent routing, no MCP server, no file-change tracking.

Detection: Cherry Studio doesn't send a stable User-Agent (CherryHQ#10209
documents UA being overridden). Routes via:
  - x-meridian-agent: cherry-studio (per request — the documented path)
  - MERIDIAN_DEFAULT_AGENT=cherry-studio (global default)

Bonus fix: single source of truth for the adapter list. The settings UI
hardcoded ["opencode", "crush", "forgecode", "pi", "droid", "passthrough"]
in two places — both drifted from ADAPTER_MAP. claude-code was registered
for detection but never rendered in the settings UI, so its toggles were
silently inaccessible. ADAPTER_LABELS / ADAPTER_NAMES exports in detect.ts
become the single source; sdkFeatures.ts and settingsPage.ts consume them.
Adding cherry-studio + claude-code to the UI is automatic. Audit test
guards against future drift.

Tests: 21 new in cherry-studio-adapter.test.ts covering identity,
session/CWD, the load-bearing WebSearch/WebFetch unblocking, filesystem
blocking, chat-client behavior toggles, x-meridian-agent header
detection (incl. the cherrystudio alias and case-insensitivity), and
the adapter-list audit.

End-to-end verified by booting a test proxy on :3500 and POSTing
/v1/messages with `x-meridian-agent: cherry-studio` and a "WebSearch
the latest node.js version" prompt. Result: SDK invoked WebSearch,
returned real search result with citation, thinking blocks forwarded.

Full suite 1742/0. Build clean. Typecheck clean.

Note: tested without Cherry Studio itself in the loop — the adapter
correctness, detection, and SDK round-trip are verified server-side,
but Cherry Studio's specific UI rendering of citations/streaming needs
end-user verification. @BenIsLegit can validate before merge.
@BenIsLegit
Copy link
Copy Markdown

Thanks! I'll try it out today

@BenIsLegit
Copy link
Copy Markdown

BenIsLegit commented May 7, 2026

Looks like it's still struggling and using the wrong tools to do web searches.
image
image
image

Here's a separate attempt where I unselected "Web Search" in Cherry Studio and then asked it explicitly to do a web search:
image
image
image

I'll investigate a bit more and see why it's getting in a loop. Seems like it fails a Cherry Studio web search tool, silently completes it using CC web search, then commenting re-attempting and re-reporting

@BenIsLegit
Copy link
Copy Markdown

Realized I had Thinking and Thinking Passthrough disabled. Turned those on, but it seems to really think it doesn't have web search tools available now
image

@rynfar
Copy link
Copy Markdown
Owner Author

rynfar commented May 8, 2026 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cherry Studio Support + Web Search Issues

2 participants