How I use coding agents
Published: 2026-02-03
TL;DR
I use opencode with Ollama Cloud ($20/mo) and Anthropic ($20/mo) as my main LLM providers, plus pay-as-you-go API credits in OpenAI, xAI, Google, and Opencode ZEN. I use the python ddgs library as a websearch tool (gist) and have permissions set to "ask" for edit, webfetch, write, bash, MCP tools, and external directory access. MCPs are disabled by default. I also use a custom .aiexclude plugin to block the agent from reading sensitive files.
What This Post Is
Setting up a coding agent properly involves a lot of decisions - which provider, which permissions, what to expose and what to lock down. I’m sharing my setup and the reasoning behind each choice.
The motivation came from a meeting I organized with the dev team at work. We don’t have company-provided AI accounts - everyone uses their own subscriptions, API keys, or free plans - and I wanted to set some ground rules before bad habits formed. Pasting company code into a web chat is not OK, even if that chat belongs to OpenAI, Anthropic, or Google. I wanted everyone to turn off training on their data, use short-retention privacy options where available, and be aware of the many ways context can leak.
What surprised me was the gap between what I was seeing online and what was happening at work. Only 2-3 people out of about 12 were using coding agents at all. The rest were still copy-pasting into regular chats. Meanwhile, on Hacker News, people were running projects like Gas Town - Steve Yegge’s multi-agent orchestrator for coordinating dozens of coding agents in parallel - and OpenClaw - Peter Steinberger’s autonomous AI agent that runs on your own devices and connects to your messaging apps. I was living in the part of the internet where everyone was burning through tokens and racking up monthly bills, while my coworkers hadn’t even started.
This post is for them - and for anyone else just getting into coding agents.
What Is a Coding Agent?
Before diving into my setup, it’s important to understand some key concepts. If you’re already familiar with how agents, tool calls, and MCPs work, feel free to skip ahead to My Setup.
LLM vs. Agent
An LLM (Large Language Model) is, at its core, a prediction engine. You feed it text, and it predicts what comes next - one token at a time. That’s all it does. It doesn’t browse the web, it doesn’t edit files, it doesn’t remember your last conversation. It takes text in and produces text out.
Everything else you associate with “AI” - the chat interface, the conversation history, the ability to search the web or write code to a file - is built by software around the LLM. Even something as basic as ChatGPT or Claude.ai is already a wrapper: it formats your messages, keeps track of the conversation, and sends the accumulated context to the LLM with each new turn. The LLM itself has no memory between calls - the chat application creates the illusion of a continuous conversation by replaying the entire history every time.
A coding agent takes this further. It’s software that gives the LLM the appearance of being able to act in the world - reading files, editing code, running commands. But here’s the key insight: the LLM still just outputs text. When it “decides” to edit a file, what actually happens is that it generates tokens that match a specific tool-call format. The agent software watches for these patterns, intercepts them, executes the real action (writing to your filesystem, running a shell command), and feeds the result back as more text. The LLM then continues generating based on that new context.
The LLM doesn’t know it’s editing your files. It doesn’t know files exist. It’s producing structured text, and the agent is the one turning that text into actions. As Simon Willison put it - echoing Anthropic’s own definition - an agent is just “a model using tools in a loop.” The model requests an action, the agent executes it, the result goes back to the model, and the loop continues until the task is done.
Tool Calls
So how does the LLM “know” what tools are available? The agent tells it - by injecting tool definitions into the system prompt before the conversation even starts. These definitions describe each tool’s name, what it does, and what parameters it accepts. Here’s a simplified example:
<tool_description>
<tool_name>bash</tool_name>
<description>Executes a bash command in a persistent shell session</description>
<parameters>
<parameter name="command" type="string" required="true">The command to execute</parameter>
<parameter name="timeout" type="integer" required="false">Optional timeout</parameter>
</parameters>
</tool_description>
When the LLM wants to use a tool, it doesn’t reach out and run a command. It simply generates text that follows a structured format - something like:
<tool_call>
<tool_name>read</tool_name>
<parameters>
{
"filePath": "/home/user/documents/report.txt",
"limit": 50
}
</parameters>
</tool_call>
The agent watches the stream of generated tokens, recognizes this pattern, and says: “That’s a tool call - let me execute it.” It reads the file, takes the result, and injects it back into the conversation as if the LLM had “seen” the file. The LLM then continues generating its response with that new context. It’s text all the way down.
Each provider (Anthropic, OpenAI, Google, etc.) has its own format for these definitions and tool calls. This is one of the challenges of building a provider-agnostic agent - the software has to translate between different conventions and handle edge cases, like what happens when you switch providers in the middle of a conversation.
MCPs (Model Context Protocol)
Agents come with built-in tools - reading files, writing files, running shell commands. But what if you want the agent to control a browser, or query a database, or draw a diagram? That’s where MCP comes in. It’s a standard protocol that lets you plug external tools into the agent. You run an MCP server (a small program), and the agent communicates with it to execute tool calls - just like with built-in tools. From the LLM’s perspective, there’s no difference. It sees the same kind of tool definition; it doesn’t know whether the tool runs locally or talks to a server on the other side of the world.
And that distinction matters. Some MCPs run entirely on your machine - like Playwright for controlling a browser, or tmux for controlling a terminal session. But others, like context7, send parts of your prompt to external servers to fetch documentation. The LLM decides what to send to the tool, and the agent forwards it - potentially to a third party with a different privacy policy than the one you signed up for. This is why you need to be careful about which MCPs you install and when you enable them.
Context Enrichment
An LLM only knows what you put in the prompt. If you want it to work effectively on your project, you need to give it context - and there are several ways to do this beyond just typing in the chat.
Rules (AGENTS.md) are files that get loaded automatically at the beginning of each chat session. Think of them as a briefing document for the agent. If you work on a project repeatedly, you don’t want to explain every time that it’s a PHP project using CakePHP 5, that user search lives in a separate plugin, and that you prefer tabs over spaces. You write it once in an AGENTS.md file, and the agent reads it every time you start a new session. These files can live on a per-directory basis, so different parts of your codebase can have different rules. Read more in the opencode docs on rules.
Skills (SKILL.md) solve a different problem: context you only need sometimes. Rules are always loaded; skills are loaded on demand. For example, I have a youtube-summarizer skill that instructs the agent how to extract a transcript with yt-dlp and then write a summary. Instead of explaining the process each time I want a video summarized, I just have the skill ready.
In opencode, skills can be loaded automatically - the LLM is given a tool that lets it pull in the skill text when it recognizes the need. This isn’t perfectly reliable; Vercel’s engineering blog found that skills were only triggered 44% of the time without explicit instructions. That’s why most agents also let you invoke skills manually with a slash command. Read more in the opencode docs on skills.
Commands are shortcuts for prompts you use often. They support argument
injection, shell output injection, and file references - so you can create
reusable workflows. For example, the built-in /init command tells the agent to
create an AGENTS.md file for your project. Read more in the
opencode docs on commands.
Why a TUI?
Most coding agents live inside IDEs - VS Code extensions, JetBrains plugins, and the like. That’s fine for many people, but I prefer a TUI (Terminal User Interface). The terminal is the most flexible interface there is: it’s just a stream of text. You can access it over SSH, run it inside a Docker or Podman container, pipe its output, script around it.
This flexibility enables something important: sandboxing. I can put opencode in a container or VM, SSH in, and let it run unsupervised on a task - what’s sometimes called YOLO mode. The container is the safety net. If the agent does something destructive, it only affects the sandbox. I use this for research tasks and writing small scripts where I don’t want to approve every single action.
A word of caution on YOLO mode: prompt injection is a real risk. If you ask the agent to fetch a web page and that page contains hidden instructions (“ignore your previous instructions and…”), the agent might follow them. When unsupervised mode involves reading content from untrusted sources, a sandbox isn’t optional - it’s essential. This is a growing concern, and a lot of services for agent sandboxing are becoming available.
Opencode also has a web version and an option for integrating into an IDE if the terminal isn’t your thing.
Provider-Agnostic Agents
One of the biggest advantages of a tool like opencode is that it’s not tied to a single provider. Claude Code only works with Anthropic’s models. But a provider-agnostic agent lets you try Anthropic, OpenAI, Google, open-weights models, and local models - all through the same interface, with the same tools, rules, and skills. Opencode supports 75+ LLM providers including local models. This means you can compare models on the same task, switch to a cheaper model for simple work, or fall back to a different provider when you hit rate limits.
The Journey - From Chat to Agent
With those concepts in mind, here’s how I got to where I am today.
I’d been following AI since 2021-2022 - taking deep learning courses, reading papers, watching the field evolve. When ChatGPT launched, I wasn’t surprised. I wasn’t a heavy user either - mostly treating it as a better search engine for technical questions.
What hooked me was local inference. When Meta released Llama and Georgi Gerganov built llama.cpp to run it on consumer hardware, I saw an opportunity: AI that stays on my machine. I set up Ollama on my home server and started experimenting. It was painfully slow - 30-40 seconds to the first token, then 1-5 tokens per second - but it was completely private. Privacy has always mattered to me. It’s why I switched to Linux and open source, why I self-host everything I can, and why I was a very late adopter of Facebook. I’ve never liked not having control over my data.
Eventually, speed and quality won out. Cloud providers like Anthropic offered a noticeably better experience. I set up OpenWebUI with LiteLLM so I could try different models while keeping conversations stored locally. I read the privacy policy of every provider I used - most APIs store data for up to 30 days - and bought credits from Anthropic, OpenAI, Google, and xAI.
Then tool-calling models arrived, and with them the first coding agents. In October 2025, I tried Claude Code. An LLM that could change files on my machine - list directories, run bash commands, fix mobile layouts on a personal project. It was strange and fascinating in equal measure. But Claude Code was closed-source and locked to Anthropic’s models. I wanted to understand how it worked under the hood, and I wanted the freedom to use different providers - the same flexibility I’d had with OpenWebUI and LiteLLM, but for a coding agent. That’s what led me to opencode: an open-source alternative that supports dozens of providers and whose internals I could actually inspect.
Why opencode?
Opencode is an open-source coding agent and an alternative to Claude Code. It has over 95,000 GitHub stars, 650 contributors, and is used by over 2.5 million developers monthly. It supports many providers - including unofficial support for Anthropic monthly plans - and has a very active community on GitHub, which matters a lot for an open-source project you’re going to depend on.
My initial motivation was curiosity. I wanted to understand what a coding agent
actually sends to the API - what system prompts, what tool definitions, what data
might be going to third parties. So I set up
mitmproxy and configured the
environment variables HTTPS_PROXY and NODE_EXTRA_CA_CERTS (opencode is built
with Node.js/Bun) to intercept the traffic.
I found something unexpected: telemetry was being sent out. It turned out to be from a third-party PHP LSP, so I turned off all LSPs. I also noticed that opencode shipped in YOLO mode by default - no permission prompts for editing or writing files - while Claude Code asked for permissions. That difference made me want to dig deeper into how “open” opencode really was.
And this is where it gets recursive. Having access to the source code meant I could use the coding agent to explore its own codebase. I had a bug where pasting strings that looked like image file names wouldn’t work - the text just wouldn’t appear. I asked opencode to find where in its own code this was happening. It traced the issue to a broken check in the image-detection logic for vision-capable models. I patched it, built the fix locally, found the existing GitHub ticket, and posted my solution.
That’s the promise of open source combined with coding agents: when you have access to the source, the agent can help you understand and improve the very tool you’re using.
My Setup - Providers
Ollama Cloud ($20/mo): Zero data retention, access to open-weights models. I nearly canceled this subscription - most open models were falling behind Anthropic and OpenAI. Then Moonshot released kimi-k2.5 and everything changed. I now use it instead of Sonnet for many tasks, and sometimes even instead of Opus. It’s not just good at coding - it has a broader understanding of context and intent that makes it feel genuinely smart. In the first few days after release, providers like Fireworks were experiencing rate-limiting issues from the demand - so it wasn’t just me. Ollama Cloud also has generous usage policies; I haven’t been able to hit the 5-hour or weekly quotas. Besides coding, qwen3-vl through Ollama is my go-to for image recognition.
Anthropic ($20/mo): Claude remains my primary model for coding. Sonnet for everyday work, Opus when I need deeper planning, harder debugging, or more nuanced problem-solving.
Pay-as-you-go: OpenAI, xAI, Google, Opencode ZEN. These are my fallbacks - I use them when I’ve exhausted my hourly or weekly limits, or when I want to try a specific model. Opencode ZEN is from the same team behind opencode - it acts as a proxy for multiple providers at the same prices, which makes it a convenient single point of access. Google occasionally releases interesting models worth trying - like Nano Banana.
A note on the privacy tradeoff: I can’t run competitive models locally. The choice was either send some data to the cloud or don’t use LLMs at all. I chose LLMs - but with all privacy precautions in place.
To configure providers in opencode, call /connect in the TUI - you choose the
provider and follow the steps.
(If you’re just getting started, $20/mo at Anthropic is enough. You can also enable extra usage in your account settings - once you hit your plan’s included usage limit, you continue at standard API rates on a pay-as-you-go basis.)
My Setup - Privacy & Permissions
This is the most important section of this post. Getting the tools to work is the easy part. Making sure they don’t leak your data is where it gets serious.
Update your privacy settings at every provider. Go to each provider’s privacy settings, opt out of data training, and disable data retention wherever possible. Every provider handles this differently. Anthropic even offers a zero-retention API arrangement if you contact their sales team with your use case.
Choose providers deliberately. Prefer providers with zero data retention (Ollama Cloud, Opencode ZEN) or at most 30 days. I specifically searched for and read the privacy policy of every provider I use.
Set all permissions to “ask”. This is the single most important configuration choice. Here’s why each one matters:
- Edit and write - the agent can accidentally overwrite files you didn’t intend it to touch. It happens more often than you’d think.
- Webfetch - if the agent gets prompt-injected, HTTP requests are the primary way to exfiltrate data. Always require approval.
- Bash - the most powerful and most dangerous tool. With bash, the agent can do anything your user account can do. You could remove every other tool and leave only bash, and most models would still complete their tasks - through shell commands alone. That versatility is exactly why it needs a permission gate.
- MCP tools - your chat context can be forwarded to third-party servers with their own privacy policies.
- External directory access - the agent should only touch the directories you’re working in. Nothing else.
Disable MCPs by default. Two reasons: privacy (context can leak to third parties) and efficiency (MCP tool definitions consume significant context tokens
- see Anthropic’s guide on advanced tool use). Enable them when you need them - opencode lets you do this mid-session.
I currently have 3 MCP servers configured:
- tmux - lets me inspect commands and their output in real time. I start a tmux session in another terminal and tell the agent to execute commands there. This also lets me authenticate sudo calls manually - the agent requests, I approve.
- playwright-cdp
- paired with a skill that starts Chrome with CDP (Chrome DevTools Protocol) enabled. I can watch the agent navigate my browser in real time, and because I’m the one who authenticates, the agent can work on pages behind a login.
- excalidraw
- diagram creation. I use it when I need the agent to sketch wireframes or visualize architecture.
Never let LLMs read files with passwords, API keys, or any other sensitive data. I built a plugin - .aiexclude
- that works like
.gitignorefor AI. List file patterns in a.aiexcludefile, and the plugin blocks the agent from reading, writing, editing, or accessing those files - even through bash commands. I used the coding agent itself to build it.
My Setup - Tools, Skills, Plugins
Websearch tool: I use DuckDuckGo in my browser and wanted the same in my coding agent. The python ddgs package provides DuckDuckGo search without an official API and supports a few other search providers too. Here’s my custom tool implementation.
Skills I use:
youtube-summarizer- extracts a transcript via yt-dlp, then summarizes itplaywright-cdp- starts Chrome with CDP enabled for browser automationgithub-duplicate-issue-finder- searches for duplicate issues in GitHub repossqlcmd- runs SQL Server queriesmemory- stores and recalls information using semantic vector searchticket- coordinates multi-agent work through a local ticket systemmonth-dates- generates formatted date lists for a given month
You can download an archive of my custom skills here. I also occasionally use skills from Anthropic’s official skills repository - like the ones for PDF and XLSX creation - and the skill-creator skill for building new ones.
Plugins: The .aiexclude plugin described in the Privacy & Permissions
section.
My Setup - Configuration
Everything discussed above comes together in the configuration file. Here’s my
opencode.json, trimmed to the interesting parts:
{
"$schema": "https://opencode.ai/config.json",
"small_model": "ollama-cloud/ministral-3:14b",
"share": "disabled",
"lsp": false,
"autoupdate": "notify",
"mcp": {
"playwright-cdp": {
"type": "local",
"command": [
"node",
"/srv/ai/mcp/playwright/node_modules/@playwright/mcp/cli.js",
"--cdp-endpoint=http://localhost:9222"
],
"enabled": false
},
"tmux": {
"type": "local",
"command": [
"node",
"/srv/ai/mcp/tmux/build/index.js"
],
"enabled": false
},
"excalidraw": {
"type": "local",
"environment": {
"EXPRESS_SERVER_URL": "http://localhost:3000",
"ENABLE_CANVAS_SYNC": "true"
},
"command": [
"node",
"/srv/ai/mcp/mcp_excalidraw/dist/index.js"
],
"enabled": false
}
},
"agent": {
"build": {
"prompt": "{file:/srv/ai/open_code/system_prompt_long.md}"
},
"plan": {
"prompt": "{file:/srv/ai/open_code/system_prompt_long.md}"
}
},
"keybinds": {
"leader": "alt+a"
},
"permission": {
"edit": "ask",
"bash": "ask",
"webfetch": "ask",
"websearch": "ask",
"lsp": "ask",
"codesearch": "ask",
"doom_loop": "ask",
"external_directory": "ask",
"tmux_*": "ask",
"playwright-cdp_*": "ask",
"excalidraw_*": "ask"
},
"experimental": {
"disable_paste_summary": true,
"openTelemetry": false
},
"provider": {
"ollama-alternative": {
"npm": "@ai-sdk/openai-compatible",
"name": "Ollama (Alternative)",
"options": {
"baseURL": "http://127.0.0.1:11434/v1"
},
"models": {
"kimi-k2.5:cloud": {
"name": "kimi-k2.5:cloud",
"modalities": {
"input": ["text", "image"],
"output": ["text"]
}
},
"qwen3-vl:235b-cloud": {
"name": "qwen3-vl:235b-cloud",
"modalities": {
"input": ["text", "image"],
"output": ["text"]
}
}
// ... plus deepseek-v3.1, devstral-2, mistral-large-3,
// glm-4.7, minimax-m2.1, cogito-2.1, and others
}
}
}
}
A few things worth noting:
- All MCPs start disabled - I turn them on mid-session when I need them.
- Every permission is set to
"ask"- including wildcard patterns for MCP tools (tmux_*,playwright-cdp_*, etc.), so each individual MCP tool call also requires approval. - LSP and telemetry are off -
"lsp": falsebecause of the telemetry issue I discovered (see the Why opencode? section), and"openTelemetry": falseas an extra precaution. - The
ollama-alternativeprovider - Ollama Cloud has built-in support in opencode, so normally you don’t need to configure it manually. I set it up as a custom provider because I run a local Ollama instance that proxies requests to Ollama Cloud, letting me route cloud models through my own endpoint. - Custom system prompts are loaded from a file for both build and plan modes - this is where I put project-agnostic instructions that I want in every session. This is entirely optional; opencode ships with built-in system prompts that are even provider-specific, so it works well out of the box.
Installing opencode
Opencode offers several installation methods. Pick whichever suits you:
curl (the quickest way to get started):
curl -fsSL https://opencode.ai/install | bash
npm:
npm install -g opencode
brew (macOS/Linux):
brew install opencode
There’s also a desktop app in beta for macOS, Windows, and Linux.
Building from source - this is what I do. It gives me full control over what’s running on my machine, and I can audit changes between releases. If you want the same:
- Install Bun (I use a Podman container with Bun pre-installed, since Bun’s own installer is also a downloaded shell script).
- Clone the source:
git clone https://github.com/anomalyco/opencode - Check out the tag for the version you want, or use the
devbranch for the latest. - Install dependencies:
bun install - Build:
bun packages/opencode/script/build.ts --single - The resulting binary will be at
packages/opencode/dist/opencode-linux-x64/bin/opencode(replaceopencode-linux-x64with your platform).
What This Changed For Me
I’ve been a developer for about 13 years. For the last 4-5 of those, I was losing interest. The work had become repetitive - setting up CMS input fields for yet another page section, creating ACF fields, wiring up models, controllers, and view logic in the same patterns I’d built a hundred times before. The problems weren’t hard. They were just tedious.
Coding agents made it interesting again. I had a backlog of project ideas I’d been sitting on for years, but the thought of writing the same CRUD scaffolding one more time kept me from starting. Now I can hand that work to the agent and focus on the parts I actually enjoy - architecture, design decisions, the creative work.
The way I think about work changed too. I used to plan in my head, jot down notes, and then start writing code. Now I write my idea directly into the agent’s chat. The act of explaining what I want - clearly enough for the LLM to act on it - forces me to think through the details. It’s rubber duck debugging taken to its logical conclusion: the duck listens, understands, and then writes the code.
I recently completed a client website where almost all the code was generated by AI. I bootstrapped the core WordPress functions file, then told the agent what to build and how. I reviewed all of the generated code and sometimes edited by hand, but the bulk of the implementation was AI-written. Without coding agents, I would have had to write all of it myself - and honestly, it would have felt like burden.
Coding agents are a genuine shift in how software gets built. If you’re a developer and you haven’t tried one yet, now is the time.