Dario's built-in TOOL_MAP carries 66 schema-verified entries covering the tool schemas of every major coding agent. On the Claude backend, tool calls translate to CC's native Bash / Read / Write / Edit / Glob / Grep / WebSearch / WebFetch on the outbound path (so the request stays on the subscription wire shape) and rebuild to your agent's exact expected shape on the inbound path (so your validator is happy). No flag required.
For a one-page status table of every tool dario supports — working / inferred / untested — see compat-matrix.md. This page covers per-tool setup; the matrix covers "does it work?" at a glance.
| Agent | Covered tool names (subset) |
|---|---|
| Claude Code / Claude Agent SDK | default — CC / SDK tools (same schema as of CC v2.1.114 / @anthropic-ai/claude-agent-sdk@0.2.x) |
| Cline / Roo Code / Kilo Code | execute_command, write_to_file, replace_in_file, apply_diff, list_files, search_files, read_file |
| Cursor | run_terminal_cmd, edit_file, search_replace, codebase_search, grep_search, file_search, list_dir, read_file (target_file) |
| Windsurf | run_command, view_file, write_to_file, replace_file_content, find_by_name, grep_search, list_dir, search_web, read_url_content |
| Continue.dev | builtin_run_terminal_command, builtin_read_file, builtin_create_new_file, builtin_edit_existing_file, builtin_file_glob_search, builtin_grep_search, builtin_ls |
| GitHub Copilot | run_in_terminal, insert_edit_into_file, semantic_search, codebase_search, list_dir, fetch_webpage |
| OpenHands | execute_bash, str_replace_editor |
| OpenClaw | exec, process, web_search, web_fetch, browser, message |
| hands (askalf/hands) | Anthropic beta computer-use tools (computer, bash, str_replace_based_edit_tool) — auto-preserved via system-prompt identity match (v3.33.0) |
| Hermes Agent (Nous Research) | terminal, process, read_file, write_file, patch, search_files, web_search, web_extract, todo mapped directly. Hermes-specific tools (browser_*, vision_analyze, image_generate, skill_*, memory, session_search, cronjob, send_message, ha_*, mixture_of_agents, delegate_task, execute_code, text_to_speech) have no CC equivalent and auto-preserve through the identity detector. Also consider --max-tokens=client so Hermes's 64k/128k per-model caps survive dario's outbound pin. |
Text-tool clients (Cline / Kilo Code / Roo Code and forks) are auto-detected via system-prompt identity markers and automatically flipped into preserve-tools mode, because mixing CC's tools array with their XML protocol makes the model emit <function_calls><invoke> that their parsers can't read. The same identity path also catches hands (askalf's computer-use agent) — its tool names overlap with TOOL_MAP but its schemas diverge, so identity match → preserve-tools is the only correct routing. If you run dario specifically for wire-level fidelity and would rather pick --preserve-tools yourself, --no-auto-detect (v3.20.1, aka --no-auto-preserve) disables the heuristic — explicit operator choice then wins.
Beyond the identity path, dario falls back to a structural check: when a request carries 3+ tools and ≥80% of them aren't in TOOL_MAP, that's a custom client whose tool surface has effectively no overlap with CC's, and round-robin remap onto CC fallback slots silently corrupts the calls. The structural fallback flips those requests to preserve-tools too, with client: 'unknown-non-cc' in the request log. This catches in-house agents and OpenClaw derivatives that we haven't added an explicit pattern for, without needing per-client maintenance. --no-auto-detect disables both paths.
Platform-scoped CC natives (PowerShell, Glob, Grep — win32-only in CC's own tool list) map by the client's declaration, not the proxy host's platform (v4.8.136): a win32 CC client through a Linux-hosted dario gets their canonical definitions instead of having them dropped from the advertised array.
MCP tools take a third path: anything named mcp__<server>__<tool> is neither remapped nor flipped to preserve — it forwards verbatim and its calls flow back untouched (v4.8.135). That's what real CC does with session-attached MCP servers; their schemas are operator-supplied and have no canonical template entry, so passthrough is the CC wire shape. Before this, a CC session with an MCP server attached had its mcp__* surface dropped from the advertised array and its history references round-robined onto fallback slots (tool substitution: 28/52 client tools not in TOOL_MAP in the live log), and two or three attached servers were enough to trip the 80% structural threshold on their own. MCP names no longer count toward that threshold.
If your agent's tool names aren't pre-mapped and its tools carry fields CC's schema doesn't have, there are two escape hatches: --preserve-tools (forward your schema verbatim, lose the CC wire shape) or --hybrid-tools (keep the CC wire shape, fill request-context fields from headers). See Custom tool schemas.
The OpenAI-compat backend forwards tool definitions byte-for-byte and doesn't need any of this.
⚠️ Architectural mismatch (read this before configuring)Cursor's BYOK is backend-mediated, not client-side. When you set "Override OpenAI Base URL" in Cursor, the Electron app sends that URL up to Cursor's own backend (
api2.cursor.sh), and Cursor's servers make the outbound LLM call — not your machine. Their backend has an SSRF (Server-Side Request Forgery) guard that rejects RFC1918 + loopback addresses by design, sohttp://localhost:3456is structurally unreachable. The error surfaces as eitherProvider returned error: Access to private networks is forbidden(older form) or{"error":{"type":"client","reason":"ssrf_blocked","message":"connection to private IP is blocked"}}(current form).Confirmed by Cursor staff in their own words across multiple forum threads — Colin, Feb 9 2026: "we have SSRF (Server-Side Request Forgery) protection that blocks connections to private/internal IP ranges" (thread); Dean Rie, Jan 20 2026: "BYOK API keys work through Cursor's backend. All requests go through our servers" (thread). No fix ETA. dario#190 + every other local-proxy project (e.g. mergd/ccproxy) hits the same wall.
The simple path: if you want a frictionless setup, use Claude Code, Continue.dev, OpenHands, Aider, Cline, or Zed instead (sections below). Those clients make the outbound call from your machine, so
http://localhost:3456Just Works — no tunnel required.The Cursor path: expose dario behind a public HTTPS tunnel (cloudflared, ngrok, etc.) so Cursor's backend can reach it. Walkthrough below.
In two terminals:
# Terminal 1 — dario as usual
dario proxy --verbose
# Terminal 2 — cloudflared quick tunnel (free, no signup)
cloudflared tunnel --url http://localhost:3456
# → prints something like https://random-words-here.trycloudflare.comCopy the https://...trycloudflare.com URL — you'll paste it into Cursor next.
🔐 The tunnel URL is a credential.
.trycloudflare.comURLs are unauthenticated by default — anyone who learns the URL can spend your Claude subscription against it. Random subdomains keep casual exposure low-risk, but don't paste it publicly, and kill the tunnel when you're done. For anything beyond a quick test:
- ngrok with
--basic-author a reserved domain + auth, or- Named cloudflared tunnels behind Cloudflare Access policies, or
- dario behind your own VPS reverse proxy with TLS + auth.
Cmd/Ctrl + , → Models. Under the OpenAI API Key section:
- Check Override OpenAI Base URL:
https://random-words-here.trycloudflare.com/v1(your tunnel URL +/v1; the checkbox must be enabled, not just the field populated) - API key:
dario - (Recent Cursor versions removed the explicit "Verify" button — the green toggle on its own is sufficient.)
Two name gotchas to dodge in this step. Both are about Cursor's behavior, not dario's.
Gotcha A — built-in name collision. Cursor recognizes any model name it ships natively (e.g., claude-opus-4-7, claude-sonnet-4-6, claude-haiku-4-5, gpt-5, gpt-4o). Add one of those raw and Cursor pops a "this model is already available as Opus 4.7" toast and silently routes it through its own Anthropic gateway — billing your Cursor API credits, never reaching the override URL. The Override OpenAI Base URL only takes effect for model names Cursor does not recognize as built-ins.
Gotcha B — Anthropic-format-switcher (matters for Agent mode). Cursor inspects the model-name string and switches its outbound tool-call format based on substring match. From Cursor staff Dean Rie: "When Cursor sees a model name like claude-*, it switches to a Claude-specific tool-calling format, which isn't compatible with OpenAI-compatible API endpoints" (forum thread). So claude:claude-opus-4-7 (or any name containing claude-) makes Cursor send Anthropic-shape tool blocks to the OpenAI-compat /v1/chat/completions endpoint — dario's OpenAI-compat handler can't parse those, the model receives a confused tool surface, and you get text-form tool calls in the response instead of structured edits. dario#190 is the canonical case.
Use the provider prefix form that dodges both gotchas:
- Claude —
anthropic:opus,anthropic:sonnet,anthropic:haiku(or full IDs:anthropic:claude-opus-4-7/anthropic:claude-sonnet-4-6/anthropic:claude-haiku-4-5). Theanthropic:prefix routes through dario's Claude backend identically toclaude:, but the visible model name doesn't contain theclaude-substring, so Cursor ships OpenAI-shapetool_callsand dario's translator handles them cleanly. - OpenAI (if you've run
dario backend add openai --key=sk-...) —openai:gpt-4o,openai:gpt-5,openai:o1, etc. Theopenai:prefix dodges Cursor'sgpt-*collision the same way. - Other OpenAI-compat backends (Groq, OpenRouter, local LiteLLM, Ollama, etc.) —
groq:llama-3.3-70b,openrouter:moonshotai/kimi-k2,local:qwen-coder-32b, etc.
dario v3.36+ resolves anthropic:fable/opus/sonnet/haiku (and fable1m/opus1m/sonnet1m) shortcuts to canonical Anthropic model IDs at request time. Older dario versions (≤ v3.35) need the full canonical form: anthropic:claude-opus-4-7 etc.
Older docs / muscle memory note: earlier versions of this guide recommended the
claude:prefix. That works fine on tool-less Chat (Gotcha B doesn't fire when no tools are sent) but breaks Agent mode. Preferanthropic:going forward — it's drop-in compatible with every dario version that supportsclaude:.
Select one of the registered models in Cursor's model picker.
Cursor's surfaces handle tools differently:
| Surface | Shortcut | Tools forwarded to BYOK? |
|---|---|---|
| Chat (right-pane chat tab) | — | No — chat-only, no tools array sent |
| Agent / Composer (Cmd-I) | Cmd-I (Mac) / Ctrl-I (Windows/Linux) | Yes — full tools array sent in OpenAI function-calling format |
| Tab Apply (autocomplete) | — | First-party model, BYOK ignored |
| Cmd-K (inline) | Cmd-K / Ctrl-K | Variable; uses its own model selection |
If you point dario at the Chat surface, the request body has no tools array, but dario still replays Claude Code's full system prompt (which tells the model "you have Bash/Read/Write/Edit/Grep/Glob…") — the model improvises by narrating tool calls in plain text. Same root cause as the "system instructs me to default to no comments…" leak: the model is decision-narrating because the wire shape it expected (full agent harness) doesn't match what arrived (plain chat with no tools).
For agent-style work, open Cmd-I / Ctrl-I (Agent / Composer pane), not the Chat tab. Pick one of the anthropic:* models in the picker and send your request. dario's logs should show the request/response cycle for each tool call, with tool_use blocks translated to OpenAI tool_calls on the way back to Cursor.
With dario proxy --verbose running, send a test message in Cursor's Agent pane. You should see:
- A
provider prefix: anthropic:opus → claude backend with model claude-opus-4-6line in dario's logs - One or more
POST /v1/chat/completionslines per turn (one per tool round-trip) - An incremented request count in
dario doctor --usage
If dario's logs stay silent and Usage 5h (all) stays at 0.0%, the request never reached the tunnel. Three likely causes:
Access to private networks is forbidden/ssrf_blockederror in Cursor — you pasted thelocalhost:3456URL, not the tunnel URL. Check step 2.- Cursor's name-collision toast fired — the model name you added matches a Cursor built-in (Gotcha A). Use the
anthropic:form (step 3). - You're testing in Chat, not Agent — open Cmd-I / Ctrl-I and test there (step 4).
If logs show traffic but the model emits text-form tool calls (Tool: Read\n{"file_path":...}) instead of structured calls, you're hitting Gotcha B — your model name still contains claude-. Switch to anthropic:opus etc. (step 3).
Why no "Override Anthropic Base URL"? Cursor doesn't have one. There's a year-old open feature request and no plans to ship it. Routing Claude through dario is only possible via the OpenAI-compat path with a prefixed model name as above.
In ~/.continue/config.yaml (or the Continue settings UI, which edits the same file):
models:
- name: Claude Sonnet (dario)
provider: anthropic
model: claude-sonnet-4-6
apiBase: http://localhost:3456
apiKey: dario
- name: Claude Opus (dario)
provider: anthropic
model: claude-opus-4-7
apiBase: http://localhost:3456
apiKey: darioprovider: anthropic + apiBase: http://localhost:3456 points Continue's Anthropic SDK path at dario instead of api.anthropic.com. dario runs the full Claude Code wire replay on the outbound path.
export ANTHROPIC_BASE_URL=http://localhost:3456
export ANTHROPIC_API_KEY=dario
aider --model sonnetAider's Anthropic path honors ANTHROPIC_BASE_URL directly. --model opus, --model haiku, or any explicit claude-* model name works.
Cline and its forks use a UI-based "API Provider" dropdown. Pick Anthropic as the provider and fill in:
- API Key:
dario - Anthropic Base URL:
http://localhost:3456 - Model:
claude-sonnet-4-6/claude-opus-4-7/claude-haiku-4-5
Cline's tool-invocation protocol is XML-based (<execute_command>, <write_to_file>, etc.), not Anthropic's tool-use format. Dario auto-detects Cline-family clients via system-prompt identity markers and flips into preserve-tools mode automatically — Cline's own tool schema passes through, your commands route back to Cline's parser. No flag required. Override: --no-auto-detect if you'd rather force the CC wire shape and deal with the parser mismatch yourself.
Zed's Anthropic provider config (~/.config/zed/settings.json or Cmd/Ctrl+,):
{
"language_models": {
"anthropic": {
"api_url": "http://localhost:3456",
"version": "2023-06-01"
}
}
}Set the ANTHROPIC_API_KEY env var to dario before launching Zed. Model picker then shows Claude models routed through your subscription.
export LLM_BASE_URL=http://localhost:3456
export LLM_API_KEY=dario
export LLM_MODEL=anthropic/claude-sonnet-4-6
openhands --task "task description"Prefix the model with anthropic/ so LiteLLM (OpenHands' inner routing layer) knows to hit the Anthropic path, which dario is now fronting.
For a full end-to-end walkthrough — install, battletested model picks, subscription-billing verification, retries, multi-account pool, and the gotchas that bite first-time users — see openhands-walkthrough.md.
export ANTHROPIC_BASE_URL=http://localhost:3456
export ANTHROPIC_API_KEY=dario
openclaw "task description"OpenClaw uses the standard ANTHROPIC_BASE_URL and ANTHROPIC_API_KEY env vars. Dario's structural-fallback tool detection auto-translates OpenClaw's exec / process / web_search / web_fetch / browser / message tools to CC's canonical set — no flag required.
Heads up: OpenClaw 2026.2.17+ reads ~/.openclaw/agents/main/agent/auth-profiles.json before checking env vars, so a stale Anthropic key in that file silently overrides ANTHROPIC_API_KEY=dario. If you see 401s, see the auth-profiles entry in faq.md. Dario's default template-replay mode also strips the openclaw.inbound_meta.v1 classifier-trigger string from your local git context at the proxy boundary, so subscription billing is preserved on OpenClaw-namespaced projects without you doing anything.
For a full end-to-end walkthrough — auth-profiles handling, classifier-filter protection, subscription-billing verification, multi-account pool, and the gotchas that bite first-time users — see openclaw-walkthrough.md.
hands is a sister project to dario — a local computer-use agent that drives your OS through its native shell instead of a screenshot loop. Two modes: Claude Login (uses the claude CLI directly, no dario required) and SDK mode (audit-logged, supports --dry-run, routes through dario for $0 per task).
# SDK mode — env vars route the Anthropic SDK through dario
export ANTHROPIC_BASE_URL=http://localhost:3456
export ANTHROPIC_API_KEY=dario
dario proxy --verbose &
hands auth # pick "API Key", paste: dario
hands run "open notepad and type hello world"Dario v3.33.0+ auto-detects hands via system-prompt identity match and preserves the Anthropic computer-use beta tools (computer, bash, str_replace_based_edit_tool) instead of remapping them. No flag required — the anthropic-beta: computer-use-* header survives the proxy and the wire shape stays subscription-eligible.
For the full end-to-end walkthrough — both auth modes, audit log, dry-run patterns, voice mode, multi-account pool, and the gotchas that bite first-time users — see hands-walkthrough.md.
If your tool isn't listed, check whether it reads OPENAI_BASE_URL / ANTHROPIC_BASE_URL from the environment. Most do. For tools that don't, look in their settings for "Base URL" / "API URL" / "Endpoint" / "OpenAI-compatible endpoint" — all of those map to dario's http://localhost:3456 (Anthropic-protocol) or http://localhost:3456/v1 (OpenAI-protocol). If the tool only accepts https://, you'll need a loopback TLS shim (out of scope here — open an issue if you need one for a specific tool).
By default, on the Claude backend, dario replaces your client's tool definitions with the real Claude Code tools (Bash, Read, Write, Edit, Grep, Glob, WebSearch, WebFetch) and translates parameters back and forth. That's what keeps the request on the CC wire shape, which is what keeps the session on subscription billing instead of per-token API pricing. For the agents listed in the table above, the translation is pre-mapped and runs automatically — nothing to configure.
The trade-off shows up when you're running something that isn't in the pre-mapped list and whose tools carry fields CC's schema doesn't have — a sessionId, a custom request id, a channel-bound context token, a confidence score the model is supposed to emit. Those fields don't survive the round trip.
Symptom: your tool calls come back looking stripped-down, or your runtime complains about a required field being absent only when routed through dario's Claude backend.
Fix: run dario with --preserve-tools. That skips the CC tool remap entirely, passes your client's tool definitions through to the model unchanged, and lets the model populate every field your schema expects.
dario proxy --preserve-toolsThe cost: requests no longer look like CC on the wire, so the subscription-billing wire shape is gone. On a subscription plan, that means the request may be counted against your API usage rather than your subscription quota. Hybrid tool mode below is the compromise that keeps both.
The OpenAI-compat backend is unaffected — it forwards tool definitions byte-for-byte and doesn't need this flag.
For the very common case where the "missing" fields on your client's tool are request context — sessionId, requestId, channelId, userId, timestamp — dario can remap to CC tools and inject those values on the reverse path. The CC wire shape stays intact, the model still sees only CC's tools (so subscription billing still routes), and your validator still sees the fields it requires because dario fills them from request headers on the way back.
dario proxy --hybrid-toolsHow it works. On each request, dario builds a RequestContext from headers (x-session-id, x-request-id, x-channel-id, x-user-id) plus its own generated ids and the current timestamp. After translateBack produces the client-shaped tool call on the response path, any field declared on the client's tool schema whose name matches a known context field (sessionId/session_id, requestId/request_id, channelId/channel_id, userId/user_id, timestamp/created_at/createdAt) and isn't already populated gets filled from the context. Fields the model genuinely populated are never overwritten.
When to use which flag:
| Your situation | Flag | Why |
|---|---|---|
| Your agent is listed in the table at the top | (neither) | Pre-mapped in TOOL_MAP; the default path already handles it. |
| Your custom fields are request context (session/request/channel/user ids, timestamps) | --hybrid-tools |
Keeps the CC wire shape and your validator is satisfied. |
Your custom fields need the model's reasoning (e.g. confidence, reasoning_trace, tool_selection_rationale) |
--preserve-tools |
The model has to see the real schema to populate these. Accept the CC-wire-shape loss. |
Your client's tools are already a subset of CC's Bash/Read/Write/Edit/Grep/Glob/WebSearch/WebFetch |
(neither) | Default mode works as-is. |
| You're on a text-tool client (Cline / Kilo Code / Roo Code) and want to override the auto-detect | --no-auto-detect (plus --preserve-tools or not, your call) |
Operator choice outranks the heuristic. |