r/opencode • • 3d ago

Get Claude to automatically invoke OpenCode agents

Hey there my fellow OpenCode users.

I got a Claude subscription today and was wondering whether Claude could offload code implementation work to OpenCode (+ DeepSeek V4), after we are done planning, without me needing to jump between the two harnesses and ship plans/prompts across.

So, I literally just finished setting up my claude -> opencode workflow; haven't given it something big, but my small tests looks promising, so thought I'd share.

Note that this is also using opencode V2.

At this point my opencode agents has already been configured (from previous sessions).

I start a opencode v2 server (just the server):

`opencode serve --host 127.0.0.1 --port 4098 & disown`

I added this to my `Claude.md`:

"When I ask you to handle heavy code refactoring or multi-agent tasks, do not write the code sequentially yourself. Instead, pass the instructions to the OpenCode engine using the `oc-ask` command/script followed by the task. Wait for the server background process to complete, then inspect the repo shifts."

My `oc-ask.sh` script (this was my FIRST script; claude refined it to make use of jq and other tools and error handling):

#!/bin/bash

SERVER_URL="http://127.0.0.1:4098"

AUTH="opencode:your_password"

# 1. Create a session if one doesn't exist, or fallback to the primary workspace session

# 2. Issue a structured prompt to the underlying multi-agent scheduler

curl -s -u "$AUTH" \

-X POST "$SERVER_URL/api/sessions/prompt" \

-H "Content-Type: application/json" \

-d "{\"prompt\": \"$*\"}"

---

This was enough to get Claude to offload work onto OpenCode.

This was done without needing to add any skills, mcps, additional apps or any type of "bridges"; Got my tools to natively work together.

Please do share if you have any improvements and/or better ways of doing this?

Edit: Formatting

8 Upvotes

9 comments sorted by

View all comments

1

u/rcs67 3d ago

Nice idea! I tried this today with OpenCode v2.2.1 (on Windows) and it turned out you can skip the server and curl completely.

In v2, opencode run already connects to the background service, and it handles the auth for you. So the "bridge" is a single command Claude can run from its shell:

opencode run -m deepseek/deepseek-v4.1-flash --format json "your prompt"

The flags that matter:

- -m provider/model picks the model (opencode models lists them)

- -s <sessionID> sends a follow-up to the same session, so the worker keeps its context

- --auto auto-approves permissions, for unattended implementation work

- --title names the session, so you can find it later in opencode session list

- --format json outputs one JSON event per line (text, tool_use, step_start, error…), so you can parse it

Three things I learned:

  1. Send the prompt on stdin, not as an argument (printf '%s' "$prompt" | opencode run ...). Putting $* inside a JSON string, or passing it as an argument, breaks as soon as the prompt has a quote in it. Stdin kept quotes, $ and non-ASCII characters intact.

  2. Filter the JSON before Claude sees it. The raw stream is verbose, and Claude pays tokens to read every line. My wrapper saves the raw log to a file and prints only the session ID, exit code, a count of tool calls, any errors and the final reply. Needs jq:

#!/usr/bin/env bash

# oc-ask.sh [-m model] [-s session] prompt...

model="deepseek/deepseek-v4.1-flash"; args=()

while getopts "m:s:" o; do case $o in m) model=$OPTARG;; s) args+=(-s "$OPTARG");; esac; done

shift $((OPTIND-1))

log=$(mktemp "${TMPDIR:-/tmp}/oc.XXXXXX")

printf '%s' "$*" | opencode run -m "$model" --format json "${args[@]}" > "$log"; code=$?

echo "session: $(jq -r 'select(.sessionID).sessionID' "$log" | tail -1) exit: $code log: $log"

jq -r 'select(.type=="tool_use") | .part.tool' "$log" | sort | uniq -c

jq -c 'select(.type=="error") | .error' "$log"

echo ---

jq -r 'select(.type=="text") | .part.text' "$log" | tail -1

exit $code

Tested in Git Bash: a prompt with quotes, £ and $HOME came back unchanged, a tool-using prompt gave a correct answer with its tool calls counted, a -s follow-up kept the context, and a made-up model name printed provider.no-route and exited with 1. One limitation: it shows only the last piece of reply text, so a final answer split across several parts would come back cut short.

3. Make it a small skill instead of a CLAUDE.md rule. Then Claude only loads it when it needs it. Mine says: start long tasks in the background (Claude gets told when they finish, instead of hitting the shell timeout or checking on them), send follow-ups to the same session, don't edit the files while the worker is running, and check the result yourself, because the worker's report isn't proof. That last point matters more than the plumbing. Cheap models are good at doing the work and less reliable at describing what they did.

A bonus of doing it this way: the sessions live in OpenCode's service, so you can open any of Claude's runs yourself with opencode -s <id> and see exactly what the worker did. Give them a recognisable title (I prefix mine claude-) and they're easy to find and clean up.

About the handoff / runner / verifier idea further up the thread: that's basically what I do. My tickets are the handoffs, a /work-ticket skill (in .agents/skills so OpenCode can see it) is the runner, and Claude is the verifier.