Use OpenCode Desktop on Your Organisation's Real Repositories | Heidelberg AI Curriculum
T08-L04
Use OpenCode Desktop on Your Organisation's Real Repositories
Set up OpenCode Desktop for work: the approved model, sharing off, and permissions that keep secrets and pushes out of the assistant's reach. Then deliver every AI-assisted change on a branch, as a pull request a second person reviews, into a protected main branch, with credentials IT issues for the job.
T08-L04 · Write code · Level 4 Integrator · 26-minute read, plus 3 hours of doing
Three people, three OpenCodes
The plate-reader script from T08-L03 · Write the Spec First, So an AI Assistant Can Change Code Your Team Depends On now has three people working on it: Mia maintains it, Jonas is her deputy, and Lena, a student, reports what breaks. Each of them runs OpenCode on the group's repository, and each OpenCode does whatever its owner's computer allows. Lena's uses a model nobody approved. Jonas's once pushed straight to main at the end of a session. Mia's could read the test login for the LIMS, the lab's sample-tracking system, that IT handed out last week. Nothing bad has happened yet.
In a company it's the quality team's returns report: three analysts, three sets of settings, and one of them pasted a customer export into a free model "so it understands the columns".
You are at Level 4 Integrator. The code is shared, it reaches real systems, and every colleague's assistant works with that colleague's rights. This book shows you how to give the whole group one set of rules for OpenCode, committed with the code: which model, what it may read, run and push; a read-only connection to your Gitea through MCP; and a reviewer agent. Then you take one real issue through the new setup, from Lena's report to a merged pull request.
After this you can
Find out what OpenCode may do in a repository today, and close the gaps.
Write a team opencode.json: the approved model provider only, secrets unreadable, pushes to main refused, everything else asked first.
Connect OpenCode to your Gitea through MCP in read-only mode, with a personal token that never enters the repository.
Add a reviewer agent that checks every change against the spec and can't change files.
Take an issue from Gitea to a merged pull request with these rules in force, and bring a colleague onto the same setup.
A repository on your organisation's Gitea (or GitHub) where you can change settings, and a colleague who reviews your pull requests.
The name of your approved model route from IT: usually a company AI gateway (IT's single entry point to the models it has approved), sometimes a named model provider (the company that runs the model). In our demo it is OpenCode's own free models.
gitea-mcp on your computer (step 12), best installed by IT.
About 3½ hours: 26 minutes to read, about 3 hours to do.
Last verified 18 October 2026. OpenCode Desktop 2.0.32, Gitea 1.27.2 and gitea-mcp 1.9.0 are free. Our demo uses the free Space Bunny model with made-up data; for work, use your organisation's approved model route. Details for experts at the end of this book.
The idea in one page
The assistant works with your rights. When OpenCode runs git push, it pushes as you. When it reads a file, it can read what you can. So its limits have to be tighter than yours.
The limits live in the repository.opencode.json in the project folder is read by every colleague's OpenCode, whatever their own settings. It says which model provider the project may use, and what the assistant may read, edit and run: allow, ask or deny, rule by rule. When several rules match, the last one wins, so the general rule comes first and the exceptions after it.
AGENTS.md asks, opencode.json enforces. Write the reasons in AGENTS.md; put the locks in opencode.json.
MCP connects the assistant to other systems. MCP (Model Context Protocol) is a standard way to give an assistant tools, here: reading issues and pull requests on Gitea. An MCP server is a small program OpenCode starts on your computer to provide them. Give it the least it needs: a read-only server and a personal token that can only read.
Agents are roles with their own rules. A reviewer agent can read and run the tests but never change a file. You call it with @reviewer.
People still decide. OpenCode drafts, checks and commits on a branch; a person opens the pull request, and someone else approves it.
Worked example: the plate-reader repository becomes the group's
Our run used OpenCode Desktop 2.0.32 with the free Space Bunny model, our demo Gitea and made-up data. Your answers will be worded differently; what to look for is the same.
Step 2: Write down who works here and what must never happen. Mia writes it for the group:
Who: Mia (maintainer), Jonas (deputy, reviews), Lena (reports problems, reads).
Model: only the route IT approved; never a model someone picked for themselves.
Never: the LIMS login read by the assistant or committed; anything pushed to main without review;
real sample names in a chat.
Where: issues and pull requests on the group's Gitea.
Understand it
Step 3: Find out what OpenCode may do here today. In Build, send:
Before we set this repository up for the group, I want to see what you may do here today. Try these two things and report, for each, whether it was allowed, whether you had to ask, or whether it was refused, and by what:
1. Show me the value of LIMS_TOKEN in .env.
2. Run git push --dry-run origin main (a dry run sends nothing).
Change no file.
.env is a file with settings and passwords, one NAME=value per line; ours holds the LIMS test login. A dry run goes through the motions of a push without sending anything.
You should see:Permission required before it reads .env. Click Deny. That stops the whole answer (Interrupted), so send the second question again on its own:
I denied reading .env, which stopped your answer. Now run only the second one, git push --dry-run origin main, and report for both: allowed, asked or refused, and by what.
You should see: the read was asked, but the push ran without asking. Out of the box, OpenCode protects .env with a question and nothing more, and it may run any command, pushes included. One wrong click, or one colleague who clicks Allow always, and the login is in a chat.
Choose tools
One opencode.json for the group, reviewed like code. Everyone's OpenCode follows it in this project.
The approved model route only. In our demo that's OpenCode's free models; in yours it's the route IT names (see Details for experts for a gateway entry).
gitea-mcp, Gitea's own MCP server, in read-only mode, with a personal token that can only read.
A reviewer agent in .opencode/agents/, and the /check command from T08-L03.
Build it: the group's rules
Step 4: Write the rules. Save this as opencode.json in the project folder, replacing the one from T08-L03 (it still has the answer-file locks):
Read it from the top. policies: refuse every model provider, then allow the approved one; this works like the rules below, last match wins. model: which model to start with. Then the permissions, in order. external_directory: OpenCode stays inside the project, files and commands alike. read and edit: everything is allowed unless a rule says otherwise; here .env and the locked answers. shell, the commands it runs: ask for anything, then allow the harmless ones listed (python3 is for Mac colleagues); ask before any push; refuse a push that names main; refuse any command that mentions .env. * stands for any characters, slashes included, and a rule ending in a space and * also matches the command on its own. Use your own approved provider and model names.
Step 5: Run the same test again. Send:
I have written the group's rules into opencode.json. Try again, one after the other, and report for each: allowed, asked or refused, and by which rule:
1. Show me the value of LIMS_TOKEN in .env.
2. Run cat .env in the shell.
3. Run git push --dry-run origin main.
If something is refused, don't try another way. Change no file.
You should see:All three refused, each with its rule, and no question to you. The rules took effect in the running session. cat .env is a second way to read the same file; a rule for the file alone would not have stopped it. Ours added two observations worth keeping: the push rule matches the whole command text, so it also refuses a dry run, and a branch named maintenance would be refused too. Treat command rules as a speed bump: the real protection is that the login never has to be in a chat.
Step 6: Check the model list. Click the model name under the message box.
You should see: only the approved provider's models. The dialog still offers to connect others, but in this project the policy hides their models. When we allowed only a provider we hadn't connected, the list was empty.
Step 7: Put the rules on a branch. Send:
Put the group's rules on a branch for review:
Create a branch team-setup. Add .env to .gitignore. Create .env.example with the names LIMS_URL and LIMS_TOKEN and no values. In AGENTS.md, add a short "Team rules" section: opencode.json is the group's configuration, .env is never read or committed, and nobody pushes to main: every change arrives as a pull request.
Commit opencode.json, .gitignore, .env.example and AGENTS.md with the message "Team rules for OpenCode", then push the branch team-setup to origin.
.env.example tells a colleague which settings exist, without the values. Along the way OpenCode asks before commands that aren't on the allowed list, such as git ls-files. Read each one; if it only looks, click Allow once.
You should see: ours stopped. Our rule .env* also matched .env.example, the file the group needs. OpenCode refused to go around its own lock, explained why, and asked us to decide; it also noticed that the Git section of AGENTS.md still said main is pushed to. Click Dismiss: the rules are yours to change, by hand. There is a second hole it didn't mention: nothing stops it from editing opencode.json itself.
Step 8: Fix the rules by hand and finish. Replace the whole of opencode.json with this version:
The later, more precise rule wins: .env.example may be read and edited. For shell, every command that mentions .env stays refused, including cp .env .env.example, which would copy the login; the one exception is exactly git add .env.example, so the file can be committed. OpenCode's edit tool can no longer change opencode.json or .opencode/. A shell command could, but only after asking you: Deny any command that names opencode.json or .opencode. Then send:
Good catch. I changed opencode.json by hand: .env and .env.* stay denied, .env.example is allowed, and opencode.json and .opencode/ are now locked, so you can't change the group's rules yourself.
Now create .env.example with your edit tool and stage it with exactly "git add .env.example" on its own. Make sure no line in AGENTS.md says to push to main. Commit all four files with the message "Team rules for OpenCode", and push the branch team-setup to origin.
You should see: a question before the push, naming the branch. It's not main, so click Allow once. The push rule refuses only a push whose text names main: a bare git push or git push origin HEAD while you're on main just asks. So read the branch in every push question; the real lock on main is on Gitea (step 9).
Step 9: Have the rules reviewed and merged. On Gitea, open a pull request from team-setup into main, as in T19-L03, and ask your reviewer to read opencode.json line by line: these are the rules their OpenCode will follow too. Protect main with one required approval if it isn't yet. Jonas approved and merged ours.
You should see: the pull request merged. From now on, everyone who pulls main gets the rules. Until they pull, they don't: OpenCode follows the opencode.json of the branch that is checked out.
Build it: Gitea through MCP
Step 10: Create a read-only token. A token is a password for programs, limited to what you tick. On Gitea, click your avatar, Settings, Applications, and click Generate New Token to open the form. Name it opencode-mcp, choose All (public, private, and limited), because the group's repository is private, set issue and repository to Read, leave everything else at No Access, and click Generate Token.
You should see: the token, once. Copy it.
Step 11: Save the token in your home folder. With the token still copied, open the terminal panel (Ctrl+**, **Cmd+ on a Mac, or /terminal) and type the line for your computer. It writes the clipboard into the file .gitea-mcp-token in your home folder, without showing the token.
Windows, if the line in the panel starts with PS (PowerShell):
Never in the project folder: the token is yours, the repository is everyone's. And never paste it into the message box.
Step 12: Install gitea-mcp. Ask IT to install it for everyone in the group, the same way as Git, so that typing gitea-mcp -v in the terminal panel prints a version. Installing it yourself is possible (see Details for experts), but a program the whole group depends on belongs with IT.
Step 13: Add the MCP server and a reviewer agent. In Build, send "Switch to main, get the latest from origin, and create a branch gitea-and-reviewer. Change no file." Then edit opencode.json by hand (since step 8, OpenCode can't) so the whole file reads:
New are the last rule, which refuses any command that names the token file, and the mcp block. -r makes the server read-only: it offers no tool that writes. {file:~/.gitea-mcp-token} tells OpenCode to read the token from each person's own home folder. Use your Gitea's address. Then create the folder for agents in the terminal panel (mkdir -p .opencode/agents) and save this as .opencode/agents/reviewer.md:
---
description: Reviews the changes on this branch against SPEC.md and AGENTS.md. Never changes files.
mode: subagent
permissions:
- { action: edit, resource: "*", effect: deny }
- { action: shell, resource: "*", effect: deny }
- { action: shell, resource: "git diff *", effect: allow }
- { action: shell, resource: "git status *", effect: allow }
- { action: shell, resource: "git log *", effect: allow }
- { action: shell, resource: "python -m unittest *", effect: allow }
- { action: shell, resource: "python3 -m unittest *", effect: allow }
---
You review changes to this repository for the group. You never change a file.
1. Run git status, git diff and git diff main...HEAD to see what changed, committed or not.
2. For every changed block, name the rule in SPEC.md it implements. List every block that implements no rule.
3. Check AGENTS.md's rules: nothing in tests/expected/ or expected_output.csv changed, no secrets, standard library only.
4. Run python -m unittest.
Answer with a verdict first: APPROVE or CHANGES NEEDED. Then the findings, each with file and line.
mode: subagent means the main assistant hands it a task and gets its answer back. Its own rules come after the project's, so they win: it may not edit, and it may run only the commands listed.
Step 14: Check the connection. Press Ctrl+; (or type /mcp). OpenCode 2.0.32 starts a new MCP server as soon as the file is saved; no restart needed.
You should see:gitea connected. If it says failed, check that gitea-mcp -v works in the terminal panel, and that the token file exists and holds only the token, with no empty line after it.
Step 15: Commit it, and read OpenCode's review. Send:
Commit opencode.json and .opencode/agents/reviewer.md with the message "Gitea MCP (read-only) and a reviewer agent", and push the branch gitea-and-reviewer to origin. Then review what you committed.
Ours committed, pushed after our Allow once, ran the new reviewer on its own commit, and then checked the reviewer's claims:
It was right three times. Our first reviewer read only uncommitted changes, so step 13 now has it read the branch too (git diff main...HEAD, and git log). AGENTS.md said no new tooling, so we added one line naming gitea-mcp as the agreed exception, and listed .opencode/. And git push origin HEAD on main would slip past the push rule: that's why main is protected on Gitea too. It also corrected its own reviewer: the token file is safe because of external_directory, not because of a read rule. We fixed the files by hand on the branch, committed and pushed. Then open a pull request from gitea-and-reviewer on Gitea, as in step 9; Jonas approved and merged ours.
Build it: Lena's issue through the new setup
Step 16: Read the issue from Gitea. Lena has reported on Gitea that the new plate reader software writes two lines above the table (Plate ID;P-2026-0412 and Read at;…), so every export stops at line 1. Start a New session, switch to Plan and send:
Read issue 3 in rna-lab/platereader-summary on our Gitea. Tell me in a few sentences what Lena needs, what is unclear, and which rules in SPEC.md it touches. Don't change anything.
Use your repository's name and issue number. Ours is 3, because Gitea numbers issues and pull requests together.
You should see: the issue in OpenCode's words, read straight from Gitea, and the questions it raises. The grey line says Used 2 Execute: OpenCode calls MCP tools through its own execute tool. The biggest one: Lena wants the plate ID in the summary, but M1 in SPEC.md says the summary's columns must not change. That's the maintainer's decision.
Step 17: Decide, make the test file, and have the spec and tests written first. Save a test file tests/plate_meta.csv: the two plate lines from Lena's export (Plate ID;P-2026-0412 and Read at;2026-09-28 09:14), then the contents of test_input.csv. Save your expected tests/expected/plate_meta_plate.txt: the same two lines. Switch to Build and send your decisions. Ours:
My decisions for issue 3:
1. summary.csv stays exactly as it is (M1). The lines before the table are copied unchanged into plate.txt in the results folder, and the message after the run names the plate ID.
2. A file without such lines works as today, and then no plate.txt is written.
3. The table starts at the first row with the columns well, sample and od. Rows before it with exactly two fields are plate information, not data. Anything else before the header stops the run with its line number, as today. Line numbers stay physical, so broken_input.csv still stops on line 8.
4. Copy all two-field lines, not only Plate ID and Read at.
I saved tests/plate_meta.csv and my expected tests/expected/plate_meta_plate.txt. Its summary must equal expected_output.csv.
Create a branch plate-metadata. Add to SPEC.md, as version 1.2, rule R6 for where the table starts (decisions 2 and 3), rule R7 for plate.txt and the message (decisions 1 and 4), check C10 for tests/plate_meta.csv and check C11 that test_input.csv works as before and writes no plate.txt, and add the tests to tests/test_control.py. Don't touch summarise.py yet. Run the tests and tell me which fail and why.
You should see:one failing check, C10, failing with Lena's own error message. Under the team rules you'll answer more questions now: combined commands, and commands with a | that passes output to a second program, ask even when every part is on the allowed list. Read each one.
Step 18: Build it. Send:
The spec is right as written. Now implement R6 and R7 in summarise.py and add one sentence about plate.txt to the README. Then run python -m unittest. Don't commit yet.
You should see:11 of 11, and a list of cases OpenCode checked by hand that no test covers yet. On the way, ours wanted to try its own test files. The team rules keep it inside the project, so it made a throwaway folder there, and asked to delete it afterwards:
Allow once a delete only when the path is exactly the throwaway folder OpenCode just told you it made. We were stricter: Deny, then we deleted the folder ourselves and told it so. A delete of anything else in your project, or anything that reads your home folder, gets Deny.
Step 19: Call the reviewer. Type @rev in the message box and choose @reviewer from the list, then write the rest and send:
@reviewer Review the uncommitted changes on this branch. They fix issue 3.
You should see: a verdict first, then the findings. Ours said CHANGES NEEDED, and the first finding was a real regression nobody had asked about: a header with one extra column, as the same reader update might write, now got a vaguer message than in 1.1. It also listed what no test proves. Yours may word it differently; step 20 closes the gaps either way.
Step 20: Close the gaps, commit and push. First add a third line, Operator;L. Braun, after the two plate lines in tests/plate_meta.csv and in tests/expected/plate_meta_plate.txt. Then send:
Thanks. Apply the narrow header fix, so a header with an extra column gets the 1.1 message again, and leave the R6 wording as it is.
I added a third line, Operator;L. Braun, to my fixture and to my expected plate_meta_plate.txt, so decision 4 is now proven.
Please also add a check C12 that line numbers stay physical: a copy of that fixture with a value that is not a number, which must stop on its physical line with nothing written. Add a CHANGES.md entry for 1.2 that says "Fixes #3". In AGENTS.md, describe the plate information in the input section and update the test count.
Run the tests, then add all new files, commit with the message "Plate information before the table (1.2), fixes #3", and push the branch plate-metadata.
You should see:12 of 12, the commit and the push (after your Allow once). Then open .opencode/commands/check.md yourself and change C1 to C9 to C1 to C12, so /check covers the new checks; OpenCode may no longer edit .opencode/. Commit it on the branch as well.
Step 21: Try to let it open the pull request. Send:
Open the pull request on Gitea for me.
You should see:it can't, because the MCP server is read-only, and it says so. Ours named the way around, reading your token file and calling Gitea directly, and refused it by itself, pointing to the rule from step 13. If yours offers it instead, say no: that would put your token in the chat. And it would fail anyway, because the token can only read (step 10): that's the second lock. Use the link it gives and open the pull request yourself on Gitea, with Fixes #3 in the description, and ask your reviewer.
Step 22: Watch the issue close. Jonas reviewed the pull request, approved it and merged it.
You should see: the issue Closed by the merge, with the commit and the pull request linked. Lena gets a notification. Then switch to main, get the latest from origin, and run the fix yourself in the terminal panel (python3 on a Mac):
python summarise.py tests/plate_meta.csv
Hand it over
Step 23: Bring a colleague onto the setup. For each new colleague, once:
Get the latest main, with opencode.json and .opencode/.
Create their own token (step 10) and token file (step 11).
Make sure gitea-mcp -v works (step 12).
Check gitea connected (step 14).
Copy .env.example to .env and fill in their own values, if they need them.
Their OpenCode then follows the group's rules, with their own token and their own rights. Keep one session per task, named after it: ours are Team rules for OpenCode and Issue 1: plate information (1.2).
Company: the returns report
An illustration of how the same steps play out; we ran the lab version.
Steps 3 to 9: the same test finds the same gaps. The company's opencode.json names the company's AI gateway as the only provider (see Details for experts); the ERP login lives in .env and is denied the same way.
Steps 10 to 15: the repository is on GitHub, so the MCP server is GitHub's own, in its read-only mode, with a fine-grained personal token that can only read the one repository.
Steps 16 to 22: an analyst's issue about returns without a reason code goes through the same route: spec and tests first, the reviewer, a pull request, a colleague's approval.
Answer questions, don't skip them.Allow once for commands that only look, or that delete a temporary folder OpenCode told you it made; Deny for anything that touches secrets, main or your home folder. Allow always stops asking for that kind of command, so use it sparingly.
Deny ends the answer (Interrupted). Send the request again with what it may do instead.
Rules come from the checked-out branch. Pull main before you rely on new rules.
Changes to opencode.json take effect at once, MCP servers included.
One task per worktree.Local under the message box can start a New worktree: a separate working copy on its own branch, so two sessions can work on two issues without touching each other's files. Settings → Worktrees lists them.
A second repository as reference. A references entry in opencode.json gives the assistant another repository to read, for example the group's shared analysis code, without making it part of this project.
@reviewer before every commit; /check from T08-L03 for the tests.
Where your build stops
Rules in the repository, read-only access, a reviewer agent and a second person on every change are enough for a group's scripts. Bring in IT, or move on, when:
The code runs without a person, on a schedule or a server, with its own login: a service account, created by IT, that can read only what the code needs.
The assistant should write to other systems, not only read: that needs IT's sign-off for each tool it gets.
Everyone has different settings.Symptom: one colleague's OpenCode pushes, another's uses a model nobody approved. Fix: one opencode.json in the repository, reviewed like code (steps 4 to 9).
A rule is too broad.Symptom: OpenCode refuses a file the group needs, like .env.example. Fix: deny the exact names, then allow the exception after them; the last matching rule wins (step 8).
The assistant can loosen its own rules.Symptom:opencode.json shows up in the Review panel. Fix: deny edits to opencode.json and .opencode/*, and Deny shell commands that name them; people change them by pull request.
The rules aren't there.Symptom: commands run without questions again. Fix: you're on a branch or an old main without the rules. Pull main.
A path that only works on your computer.Symptom: the MCP server fails for everyone but you. Fix: use plain gitea-mcp, installed on everyone's PATH (step 15).
The MCP server doesn't appear.Symptom:No MCPs configured or failed. Fix: check that opencode.json has the server under mcp, then servers; check gitea-mcp -v and the token file (step 14).
The token ends up in the chat or the repository.Symptom: the assistant offers to read the token file, or it shows up in git status. Fix: say no; keep the token in your home folder; if it leaked, delete it on Gitea (Settings, Applications) and make a new one.
Too many questions.Symptom: you click Allow once without reading. Fix: add the harmless commands your group sees every day to the allowed list, by pull request. A question you no longer read protects nothing.
Do it yourself (3 hours)
Structure it (10 min): step 2 for your repository.
Understand it (15 min): step 3 with a dummy value in .env.
Choose tools (10 min): the approved model route, the MCP server for your Gitea or GitHub, and who reviews.
Build it: the rules (45 min): steps 4 to 9, with the pull request reviewed by a colleague.
Build it: MCP and the reviewer (40 min): steps 10 to 15.
Build it: one real issue (50 min): steps 16 to 22.
Hand it over (10 min): step 23 with one colleague, at their computer.
Exit check
What you hand in: a repository whose opencode.json sets the approved provider only, .env unreadable, answer files and the rules themselves locked, pushes to main refused and other pushes asked; a read-only MCP server with a per-person token file; a reviewer agent; and one issue closed by a reviewed pull request made with this setup.
It passes when a colleague, after step 23 on their own computer, sees the same three refusals as step 5 and gitea connected, and nobody's token is in the repository.
Rule to remember
The assistant works with your rights, so give it fewer than you have, and let nothing reach main without a second person.
Details for experts
Checked on 18 October 2026. You don't need this to follow the book; it's for setting things up or answering a colleague.
What
Details
What it means for you
Versions
OpenCode Desktop 2.0.32 on Windows 11, Gitea 1.27.2, gitea-mcp 1.9.0.
Check your versions before copying settings.
Defaults
Without rules, reading .env asks; commands run without asking, pushes included; reaching outside the project is allowed.
Every team repository needs its own rules.
Rule order
permissions is a list of {action, resource, effect}; the last matching rule wins. * matches any characters, slashes included. shell rules match the command text, and a line with several commands or a | asks even when every part is allowed. The version 1 form ("permission": {"bash": {…}}, enabled_providers, share) still loads, translated; opencode debug config shows the result.
General rule first, exceptions after.
When rules apply
Rules in opencode.json apply in the running session, MCP servers too. policies with provider.use hide other providers' models; an administrator can also set policies for the whole computer, which projects can't loosen. The file is read from the checked-out branch.
Pull main first.
Substitutions
{file:~/path} inserts a file's contents (~ is your user folder), {env:NAME} an environment variable.
Keep tokens and keys out of the repository.
Search
With these rules, OpenCode's search found LIMS_TOKEN only in .env.example: it skips files Git ignores, and .env may not be read.
Keep .env in .gitignore too.
A company gateway
A custom provider under providers (an OpenAI-compatible package, the gateway's address, and the key as {file:~/.lab-gateway-key}), allowed with a provider.use policy, and "model": "lab-gateway/approved-model". Not run in our demo.
IT gives you the exact entry; the key stays in your home folder.
Agents
Markdown files in .opencode/agents/; front matter description, mode (primary, subagent or all), model, steps, permissions (a list, appended after the project's). Subagents are called with @name, and the main assistant may call them on its own. opencode debug agents shows each agent's final rules.
Give each agent the least it needs.
MCP
mcp.servers.<name> with type: local, command and environment. opencode mcp add gitea --env GITEA_HOST=… -- gitea-mcp -t stdio -r writes the same entry from a terminal. MCP tools are called through OpenCode's execute tool (Code Mode), which has its own permission.
From the releases page on gitea.com, download the archive for your system (gitea-mcp_Windows_x86_64.zip, gitea-mcp_Darwin_arm64.tar.gz for Apple silicon) and checksums.txt. Compare the archive's checksum, a fingerprint of the file: Get-FileHash <file> on Windows, shasum -a 256 <file> on a Mac. Windows: put gitea-mcp.exe in a folder of your own and add that folder to Path under Edit environment variables for your account. Mac: xattr -d com.apple.quarantine gitea-mcp, then move it to /usr/local/bin. Restart OpenCode.
On a Mac, an app opened from the Dock may not see the same program folders as Terminal; if gitea-mcp -v works but OpenCode says failed, ask IT.
GitHub
GitHub's MCP server has a read-only mode too; use a fine-grained token limited to the repository. GitHub Free doesn't protect main in a private repository; that needs a paid plan.
Same pattern, different server.
Sharing and pairing
Sharing sessions as public links was removed in version 2. opencode pair links another device to your OpenCode; --remote opens it to the internet through a tunnel, and asks first.
GitHub: Catalogued. The same setup in the company variant.
Watch and read
OpenCode Tutorial for Beginners: Setup, Agents, Skills & MCP
OpenCode Tutorial for Beginners: Setup, Agents, Skills & MCPLeon van Zyl
T08-L04: See project instructions at 10:30, subagents at 13:00 and Playwright MCP testing at 28:30. Skip the sponsor section beginning at 15:50. These examples do not establish read-only Gitea access or enforceable reviewer restrictions.