Tutorial 2026-05-09 · ~20 min read

OpenCode CLI and Plugins Timing Out? Stabilize Terminal AI Coding With Clash in 2026

OpenCode is built for terminal AI workflows: installers served from opencode.ai, model catalogs surfaced through directories like Models.dev, optional GitHub sign-in paths, and an extensible npm plugin story that often pulls tarballs and metadata from registries you never think about until they hang. When you add MCP tools, you inherit another layer of long-lived sessions and outbound discovery calls that punish inconsistent split routing. If each hop exits through a different policy group—or DNS answers disagree with where packets really go—you see the same boring failure mode: timeouts, half-downloaded plugins, and “registry unreachable” errors that masquerade as product bugs. This guide shows how Clash turns that stack into a single, legible network story: disciplined DNS, ordered rules, and nodes chosen for steady streams rather than vanity speed tests. It sits beside our Claude Code plus MCP routing walkthrough for readers who switch tools but keep the same underlying CLI and package headaches, and pairs cleanly with the terminal HTTP and Git proxy baseline when you still rely on classic environment variables.

Why OpenCode behaves like a whole network bundle—not “one API host”

Browser-first guides train you to think in tabs. OpenCode inverts that: the CLI and its children touch installers, documentation mirrors, registries, raw Git endpoints, auth redirects, and model metadata in one sitting. A successful session is not “reach the chat server.” It is “keep every prerequisite on the same coherent egress long enough to finish the job,” including bursty npm metadata pulls and any CDN-hosted chunks your plugins expect.

The moment you route only the obvious model hostname and leave registry.npmjs.org or GitHub releases on a domestic shortcut, you manufacture split brains: streaming tokens ride a tunnel that tolerates long uploads while package installs crawl or die on a link that shape-shifts mid-session. MCP magnifies the effect because tool processes may spawn with their own environment: on macOS you might inherit sanitized proxies, on Windows under WSL you might resolve DNS on a different side of the hypervisor than where TCP exits, and on Linux systemd user services sometimes ignore desktop proxy panels entirely.

Mental model

Treat OpenCode + npm + GitHub + Models.dev + MCP as one workflow bundle inside Clash. If any leg sneaks into a different pool, you will chase timeouts that disappear only when you accidentally align paths—not because the vendor “fixed” anything.

What actually talks to the network during a representative session

Start with observability before YAML. During install, OpenCode commonly pulls bootstrap scripts from opencode.ai and related documentation or landing paths on the same site family. Follow-up updates may hit the same edges or companion hosts your organization mirrors internally—if you mirror, keep mirror names beside the public ones so internal and external developers do not diverge silently.

npm is never a single socket. Clients resolve the registry, chase 302 chains, then download tarballs from geographically scattered hosts. Corporate registries add another hostname family that must still live in your developer bucket alongside model traffic. GitHub splits across the main site, raw content, release assets, large file endpoints, and intermittent calls into objects.githubusercontent.com; OAuth device flows and Copilot bridges can introduce additional domains you only see once in a packet capture.

Model selection layers add catalog hosts—Models.dev is the public face many agent tools consult when they enumerate providers—plus whatever endpoints your chosen vendor uses for inference once you leave the catalog. Logging those outbound names once beats hand-waving “AI traffic” into a vague category that later collides with a blocklist titled “cloud storage” or “CDN.”

MCP servers, even when bound locally, regularly perform HTTPS for schema downloads, tool registration, or upstream API calls. stdio transports hide the complexity until you correlate child-process traffic in Clash logs. WebSocket or HTTP transports are easier to see but still demand the same idle tolerance as chat streams.

  • Installer and docs: keep on the same split routing group as the rest of the bundle to avoid partial upgrades.
  • npm and tarball CDNs: prioritize completeness and TLS trust over maximal blocking; aggressive filters break integrity checks quietly.
  • GitHub and Models.dev: pin explicitly above coarse GEOIP imports that might shortcut them unexpectedly.
  • MCP discovery: log once, codify what you saw; do not cargo-cult domains from forums unless you verify them locally.

Split routing order that keeps developer traffic predictable

Clash matches split rules from top to bottom. Remote rule providers are convenient until they inject a broad GEOIP,CN row above your carefully curated developer rows, or until a tracker list accidentally captures OAuth telemetry you still need. After every subscription refresh, skim the merged view and confirm your OpenCode-relevant domains stay above noisy catch-all categories.

A pragmatic pattern is a dedicated policy group—call it AI_DEV or similar—and reference it for opencode.ai, models.dev, npm registry families, GitHub suffixes you validated, and any MCP catalog hosts your logs surfaced. Keep domestic-only services on DIRECT with explicit DOMAIN-SUFFIX entries so they never inherit a foreign exit by accident. Push ultra-broad MATCH rules to the bottom, and treat imported lists as suggestions that you override locally when collisions appear.

Through 2026, merged lists often collide at CDNs shared between entertainment and developer artifacts. When a blocklist swallows a release tarball or a model weight mirror, the symptom is not a crisp 403—it is slow progress bars and eventual timeouts. Narrow the upstream rule or insert a more specific local row above it; the goal is predictable routing for paid work tools, not maximum denylists.

Rule collisions are silent

The first match wins forever. After you change providers, re-verify that OpenCode, npm, GitHub, and Models.dev still sit above generic GEOIP or “CDN” buckets that might have crept upward in the merge.

DNS alignment, fake-ip, and why npm “randomly” hangs

Misaligned DNS invents mystery timeouts. Under fake-ip, applications can receive synthetic addresses that only resolve inside Clash’s mapping. If any resolver bypasses the core while packets still traverse it, you get impossible-to-reproduce partial failures. Under redir-host or TUN, ensure operating-system resolvers, DHCP-provided servers, and Clash nameserver blocks tell the same story about which queries go direct versus forwarded.

OpenCode plugin installs that lean on JavaScript tooling may invoke npm or Bun-driven fetches with aggressive parallelism. That amplifies DNS fan-out: each failed lookup stalls a worker thread until the client gives up. Capture failing names alongside the matched policy in Clash; if the policy is correct but lookup latency swings wildly, tune nameserver/fallback ordering, or disable experimental DNS features one at a time until behavior stabilizes.

WSL2 deserves special suspicion: Linux and Windows sometimes disagree about where DNS lives while TCP takes yet another path. Our WSL2 mirrored networking guide walks through port exposure patterns that reduce that class of bug. On macOS, local daemons spawned by the terminal may ignore system proxy panes unless you adopt TUN or explicit per-binary configuration—exactly the gap Clash can close when you commit to kernel capture.

If npm reports ETIMEDOUT after you touched DNS, read the fake-ip troubleshooting article before rotating dozens of exit cities that were never the problem.

Long-lived streams, SSE-shaped traffic, and MCP transports

Terminal chats are not disposable fetch bursts. Model streams and many MCP transports hold connections open across tool rounds, large file reads, or progressive responses that resemble server-sent events. Middle boxes that recycle TCP sessions early, or nodes that enforce harsh idle cutoffs, surface as mid-stream stalls rather than immediate hard errors.

Design your AI_DEV pool around idle tolerance. Short-chain relays beat daisy-chained hops unless measurement proves otherwise. If a plugin downloads weights over QUIC or parallel TLS, watch whether your path treats those differently from vanilla HTTPS—some residential links deprioritize UDP-heavy flows even when HTTPS looks fine.

stdio MCP bridges add nuance: the parent CLI might honor proxy variables while the child does not, or vice versa depending on how your terminal emulator launches sessions. When you see outbound calls in Clash from the child PID, align those rows with the same policy group as your model traffic so OAuth exchanges, tool metadata, and completions share fate. When you see no outbound traffic at all, the issue is local IPC—no amount of upstream hopping fixes it.

Node selection in 2026: optimize for steady CLI sessions

Synthetic benchmarks rarely predict how an exit behaves during a twenty-minute agent-assisted refactor with several GitHub pushes in between. Prefer routes with stable loss and jitter toward both your model vendor edges and GitHub anycast fronts, not only the city that won a weekend speed test. Watch small-packet RTT; developer tooling often cares more about tail latency than headline megabits.

If your provider exposes health checks, tune intervals so you neither flap between good nodes nor cling to a degrading favorite. When large npm installs contend with streaming tokens, split bulk download pools from interactive API pools if your subscription allows concurrent selections; otherwise schedule installs consciously to avoid starving chatty flows.

When TUN or process rules beat environment variables

HTTP_PROXY helps many tools but not every binary that OpenCode spawns. Kernel-level TUN interception catches stubborn helpers that ignore environment variables, and Clash Meta-compatible cores can pair TUN with PROCESS-NAME or PROCESS-PATH rows when you need surgical splits without rerouting the entire machine. The tradeoff is operational: permissions on macOS, driver quirks on Windows, and the need to revisit DNS the moment more processes participate.

After enabling TUN, validate with a boring triad: curl against your model vendor’s API host, npm view against your registry, and a shallow git ls-remote to GitHub. All three should log under the same policy group when your split rules are correct. Any divergence means ordering or resolver work remains before blaming OpenCode itself.

YAML sketch and ongoing maintenance

The snippet below is illustrative—replace AI_DEV with your real policy group and expand suffixes from your own logs. Keep OpenCode, npm, GitHub, and catalog domains near the top of personal overrides, above imported GEOIP blocks.

# Illustrative rules — expand domains from your logs; verify policy group names
rules:
  - DOMAIN-SUFFIX,opencode.ai,AI_DEV
  - DOMAIN-SUFFIX,open-code.ai,AI_DEV
  - DOMAIN-SUFFIX,models.dev,AI_DEV
  - DOMAIN-SUFFIX,npmjs.org,AI_DEV
  - DOMAIN-SUFFIX,github.com,AI_DEV
  - DOMAIN-SUFFIX,githubusercontent.com,AI_DEV
  # Provider endpoints you confirmed (examples only):
  # - DOMAIN-SUFFIX,api.anthropic.com,AI_DEV
  # - DOMAIN-SUFFIX,openai.com,AI_DEV
  # Add MCP discovery hosts you observed from logs:
  # - DOMAIN-SUFFIX,example-registry.tools,AI_DEV
  - GEOIP,CN,DIRECT
  - MATCH,AI_DEV

Maintenance never ends because CDNs and vendor edges move. Schedule a quarterly pass: diff local overrides against Clash connection logs, delete stale hostnames, and add newcomers the day you first observe them on a real project—not weeks later when nobody remembers what changed.

Symptom First check Second check
Models list empty or stale Policy match for models.dev and provider catalog hosts DNS mode vs TUN interaction
Plugins install halfway Tarball CDN host in logs Parallel npm connections hitting different pools
MCP tools never register Child process proxy inheritance PROCESS-NAME or TUN coverage
Chat stalls mid-run Node idle timeout behavior Relay chain length and jitter

FAQ

Why does OpenCode stall on plugins but chats still work?

Plugin installs lean on npm registries and CDNs that may not share the same split routing path as your model host. Pin registry and tarball domains into the same developer bundle as inference traffic.

Do MCP servers need the same rules as OpenCode itself?

Yes when they call outbound services. Log their destinations and align them with your AI_DEV group so discovery and model calls share a single egress story.

Is TUN mandatory?

Not always, but it is the reliable fix when spawned binaries ignore proxy variables while your interactive shell respects them.

Practical checklist

  1. Capture hostnames from a real install, plugin sync, and model-backed task.
  2. Insert explicit split rules for OpenCode, npm, GitHub, and Models.dev above broad imports.
  3. Align DNS with TUN or system proxy mode; retest with curl, npm, and git.
  4. Pick nodes for long streams instead of peak Mbps snapshots.
  5. Change one variable per regression until timeouts disappear end-to-end.

Make your profile legible to future you

Many “one-click” utilities optimize for quick browser wins yet leave terminal stacks fractured: half the traffic honors legacy proxy variables, half rides default routes, and MCP children inherit whichever environment the IDE happened to spawn that day. You end up rerunning the same installs, muttering about unreliable AI, when the failure is simply inconsistent egress.

Clash rewards explicit structure—named policy groups, auditable rule order, and logs that show which row decided each flow. When OpenCode, npm, GitHub, catalogs, and MCP share one coherent path, timeouts shrink into normal networking bugs with normal fixes instead of mystical tool errors.

If you want that predictability on your machine in 2026, download Clash for free and map your developer bundle once; future sessions stay boring in the best way.

Keep terminal AI on one egress story

Use Clash split routing so OpenCode, npm, GitHub, Models.dev, and MCP traffic share predictable paths in 2026.

Download Clash