What We Are Building
Forget feature lists. The best way to learn a tool is to build something with it.
In this tutorial, you will use OpenCode — the open-source AI coding agent with 120K+ GitHub stars — to build a complete bookmark manager from scratch. The app will have:
- A REST API with Express.js and TypeScript
- A SQLite database with full-text search
- A tag system for organizing bookmarks
- A simple but functional frontend
- Unit tests
Every OpenCode feature gets introduced the moment you need it — not in an abstract features section. By the end, you will know how to install OpenCode, configure models, use Build and Plan modes, set up MCP servers, and create custom agents — all because you used them to build a real project.
Time: ~30 minutes Prerequisites: Node.js 18+ installed, a terminal (macOS, Linux, or WSL on Windows) Cost: Free (using OpenCode Zen models)
Phase 1: Installing OpenCode and Setting Up the Project (5 minutes)
Install OpenCode
Open your terminal and run one of these commands:
# Option 1: npm (recommended)
npm i -g opencode-ai@latest
# Option 2: Homebrew (macOS/Linux)
brew install sst/tap/opencode
# Option 3: One-line installer
curl -fsSL https://opencode.ai/install.sh | bash
Verify the installation:
opencode --version
You should see the version number. As of March 2026, the latest stable release is available on the OpenCode GitHub repository.
Create the Project Directory
mkdir bookmark-manager && cd bookmark-manager
git init
npm init -y
Launch OpenCode for the First Time
opencode
This opens OpenCode's TUI (Terminal User Interface) — a full-screen interactive interface built with Bubble Tea. You will see a chat input at the bottom and a conversation pane in the main area.
Choose a Model
On first launch, OpenCode will ask you to select a model. You have three paths:
Free (Zen models): Type /models and select one of the free Zen models. For this tutorial, Grok Code Fast 1 or GLM 4.7 work well. These are free and require no API key.
Local (Ollama): If you have Ollama installed, OpenCode will detect it automatically. The recommended model for Ollama is glm-4.7:cloud.
Paid (bring your own key): Type /connect to link your Anthropic, OpenAI, or Google API key. Keys are stored locally in ~/.local/share/opencode/auth.json.
For this tutorial, any model works. Higher-capability models (Claude Sonnet 4.6, GPT-5.4) will produce better results on complex tasks, but the free Zen models handle everything we are building here.
Phase 2: Scaffolding the Project with Build Mode (4 minutes)
OpenCode starts in Build mode by default. Build mode gives the AI agent full access to create files, edit code, and run commands. This is what we want for scaffolding.
Your First Prompt
Type this into the OpenCode chat:
Set up a TypeScript Node.js project with Express for a bookmark manager REST API.
Use SQLite via better-sqlite3 for the database. Add TypeScript, ts-node, and
nodemon as dev dependencies. Create a src/ directory with an index.ts entry point
that starts an Express server on port 3000.
OpenCode will:
- Create a
tsconfig.json - Install dependencies with
npm install - Create
src/index.tswith the Express server setup - Update
package.jsonwith start scripts
Watch the TUI — you will see the agent executing terminal commands, creating files, and explaining what it does at each step. This is not code generation in a vacuum; OpenCode has full access to your filesystem and terminal.
Verify It Works
Once OpenCode finishes, tell it:
Run the server and verify it starts correctly on port 3000.
OpenCode will run npx nodemon src/index.ts and confirm the server is listening. You should see output like Server running on http://localhost:3000.
What Just Happened (Feature: Build Mode)
Build mode is OpenCode's default agent. It has access to all tools: file read/write, terminal execution, and code editing. According to the OpenCode documentation, you can think of it as an AI pair programmer with full access to your development environment.
Phase 3: Creating the Data Model and Database (4 minutes)
Design the Schema
Type this prompt:
Create a database module at src/db.ts that:
1. Initializes a SQLite database at ./data/bookmarks.db
2. Creates a bookmarks table with: id (auto-increment), url (text, unique),
title (text), description (text, nullable), created_at (datetime),
updated_at (datetime)
3. Creates a tags table with: id (auto-increment), name (text, unique)
4. Creates a bookmark_tags junction table for many-to-many relationships
5. Enables WAL mode for concurrent read performance
6. Creates an FTS5 virtual table for full-text search on title and description
Export a function to get the database instance.
OpenCode will generate the full database module. Pay attention to how it handles the FTS5 (Full-Text Search 5) setup — this is a SQLite feature that will power our search functionality later.
Initialize the Database on Server Start
Import the database module in index.ts and initialize it when the server starts.
Log a confirmation message.
Create the opencode.json Config
This is a good moment to introduce project configuration. Create a file by telling OpenCode:
Create an opencode.json file in the project root with the project name
"bookmark-manager" and a rule that says "This is a TypeScript Express API
using better-sqlite3. Follow RESTful conventions. Use async/await. Return
JSON responses with consistent error formatting."
This creates the project configuration file that OpenCode reads on startup. It gives the AI persistent context about your project — coding standards, architecture decisions, and constraints — so you do not have to repeat them in every prompt.
Phase 4: Building the REST API Endpoints (5 minutes)
Generate the Routes
Create a routes module at src/routes/bookmarks.ts with these endpoints:
- POST /api/bookmarks — create a bookmark (validate url and title are present)
- GET /api/bookmarks — list all bookmarks with pagination (page, limit params)
- GET /api/bookmarks/:id — get a single bookmark with its tags
- PUT /api/bookmarks/:id — update a bookmark
- DELETE /api/bookmarks/:id — delete a bookmark and its tag associations
Use proper HTTP status codes. Return JSON with a consistent shape:
{ success: boolean, data: any, error?: string }
Register the Routes
Import the bookmark routes in index.ts and register them. Also add
express.json() middleware and a global error handler.
Test with curl
Start the server, then create a test bookmark using curl:
curl -X POST http://localhost:3000/api/bookmarks \
-H "Content-Type: application/json" \
-d '{"url": "https://opencode.ai", "title": "OpenCode - AI Coding Agent"}'
Then fetch all bookmarks to verify it was saved.
OpenCode will execute these commands in your terminal and show you the responses. This is where the tool shines — you never leave the terminal, and the AI verifies its own work.
Phase 5: Adding Search with FTS5 (4 minutes)
Introduce Plan Mode
Before building search, let us use Plan mode to think through the implementation. Switch modes:
/plan
Now you are in Plan mode. The AI can read your code and analyze it but cannot modify any files. This is useful for planning before making changes.
Analyze my current database schema and FTS5 table. Plan how to implement a
search endpoint that queries the FTS5 table and returns bookmarks ranked by
relevance. Consider edge cases: empty queries, special characters, partial
matches.
OpenCode will examine your code, write a plan to .opencode/plans/, and explain its approach without touching any files. Read the plan, adjust if needed, then switch back:
/build
Implement Search
Based on the plan, implement a GET /api/bookmarks/search?q=query endpoint
that uses the FTS5 virtual table for full-text search. Include snippet
highlighting and relevance ranking. Handle edge cases from the plan.
Test Search
Create three more test bookmarks with different titles and descriptions,
then test the search endpoint with a query that should match two of them.
What Just Happened (Feature: Plan Mode)
The Plan/Build workflow is how experienced developers use OpenCode on complex tasks. Plan first, review the approach, then build. The plan files are saved to .opencode/plans/ and can be referenced later by the build agent. This prevents the AI from making large structural decisions on the fly.
Phase 6: Implementing the Tag System (4 minutes)
Create Tag Endpoints
Create a routes module at src/routes/tags.ts with:
- POST /api/tags — create a new tag
- GET /api/tags — list all tags with bookmark counts
- POST /api/bookmarks/:id/tags — add a tag to a bookmark
- DELETE /api/bookmarks/:id/tags/:tagId — remove a tag from a bookmark
- GET /api/bookmarks?tag=tagname — filter bookmarks by tag (update existing
list endpoint to support this query parameter)
Register the tag routes in index.ts.
Test the Tag Flow
Create two tags: "dev-tools" and "tutorials". Add the "dev-tools" tag to
the OpenCode bookmark. Then fetch bookmarks filtered by the "dev-tools" tag.
Phase 7: Building a Simple Frontend (5 minutes)
Generate the Frontend
Create a public/ directory with a single-page frontend:
- index.html with a clean, minimal design (no framework, just HTML/CSS/JS)
- A form to add bookmarks (url, title, description)
- A search bar that queries the search endpoint with debounced input
- A list of bookmarks showing title, url, tags, and a delete button
- A tag filter sidebar that shows all tags with counts
- Use fetch() for API calls. Mobile responsive. No build step needed.
Serve the public/ directory as static files from Express.
OpenCode will generate the complete frontend — HTML structure, CSS styling, and JavaScript for API interactions — in one pass. The advantage of building this in the terminal with an AI agent is that it can immediately test whether the frontend connects to the API.
Test the Full Stack
Start the server, open http://localhost:3000 in a description, and verify
that adding a bookmark from the form, searching, and filtering by tag all
work correctly.
Phase 8: Adding Tests with a Custom Agent (5 minutes)
Create a Custom Test Agent
This is where OpenCode's custom agents feature comes in. Instead of using the generic Build agent for testing, we will create a specialized one.
Add this to your opencode.json:
{
"agents": {
"test": {
"name": "Test Writer",
"instructions": "You are a testing specialist. Write comprehensive tests using Vitest. Focus on edge cases and error paths. Never modify source code — only create and edit test files.",
"tools": ["read", "write", "terminal"]
}
}
}
Now switch to the test agent:
/agent test
Generate Tests
Write comprehensive tests for the bookmark API using Vitest and supertest.
Cover:
- Creating a bookmark with valid data
- Creating a bookmark with missing required fields
- Fetching all bookmarks with pagination
- Searching with FTS5 (matching and non-matching queries)
- Adding and removing tags
- Deleting a bookmark removes its tag associations
Use an in-memory SQLite database for test isolation.
Run the Tests
Install vitest and supertest as dev dependencies, add a test script to
package.json, then run the test suite.
What Just Happened (Feature: Custom Agents)
Custom agents let you create focused personas with specific instructions and tool access. The test agent we created can only read files, write test files, and run terminal commands — it cannot modify source code. This constraint prevents the common problem of AI "fixing" your source code when tests fail instead of fixing the tests.
You can create agents for any specialized workflow: code review, documentation, database migrations, DevOps scripts. The OpenCode agents documentation has more examples.
Bonus: Connecting an MCP Server for Enhanced Functionality
If you want to extend your bookmark manager, you can connect external tools via MCP (Model Context Protocol) servers.
Example: Adding a Link Preview MCP Server
Imagine you want OpenCode to automatically fetch metadata (title, description, favicon) from URLs when creating bookmarks. You can connect an MCP server that does HTTP fetching:
Add this to your opencode.json:
{
"mcp": {
"link-preview": {
"type": "local",
"command": ["npx", "link-preview-mcp-server"]
}
}
}
Now OpenCode has a new tool available: it can fetch URL metadata as part of its workflow. When you ask it to "add a bookmark for https://example.com and auto-fill the title and description," it will use the MCP server to fetch that data.
Remote MCP Servers
For cloud services, use remote MCP servers with automatic OAuth handling:
{
"mcp": {
"cloud-storage": {
"type": "remote",
"url": "https://mcp.example.com/storage",
"headers": {
"Authorization": "Bearer ${STORAGE_TOKEN}"
}
}
}
}
OpenCode detects 401 responses and initiates the OAuth flow automatically if the server supports Dynamic Client Registration.
Bonus: Using OpenCode in CI/CD
OpenCode has a CLI mode that runs without the TUI — perfect for automation. You can use it in GitHub Actions or any CI pipeline:
# Run a single prompt and exit
opencode -p "Review the changes in the last commit for security issues" --no-tui
# Use a specific model
opencode -m "claude-sonnet-4-6" -p "Generate a changelog entry for this release" --no-tui
For our bookmark manager, you could add a CI step that runs OpenCode to review PRs automatically. The OpenCode GitHub integration provides a more streamlined version of this workflow.
The Complete Project Structure
After following this tutorial, your project should look like this:
bookmark-manager/
opencode.json # OpenCode project config + custom agents + MCP
package.json
tsconfig.json
src/
index.ts # Express server entry point
db.ts # SQLite database module with FTS5
routes/
bookmarks.ts # CRUD + search endpoints
tags.ts # Tag management endpoints
public/
index.html # Single-page frontend
tests/
bookmarks.test.ts # API test suite
data/
bookmarks.db # SQLite database (gitignored)
.opencode/
plans/ # Plans from Plan mode sessions
What You Learned
By building a real project, you practiced these OpenCode capabilities:
| Feature | Where You Used It |
|---|---|
| Installation | Phase 1 — npm, Homebrew, or curl |
| Zen models (free) | Phase 1 — choosing a model without an API key |
| Build mode | Phases 2-7 — full development with file and terminal access |
| Plan mode | Phase 5 — analyzing code and planning search before implementing |
| opencode.json | Phase 3 — project config with rules for consistent AI behavior |
| Custom agents | Phase 8 — a test-writing agent that cannot modify source code |
| MCP servers | Bonus — extending OpenCode with external tools |
| CLI mode | Bonus — running OpenCode in CI/CD without the TUI |
The key difference between OpenCode and other AI coding tools is that it is entirely open source, runs in your terminal, and does not store your code or context data. You control the models, the data stays local, and the community of 800+ contributors keeps it evolving fast.
For teams using multiple AI tools, OpenCode works well as the terminal-based complement to IDE assistants. At ZBuild, we often use it alongside editor-based tools for different parts of the development workflow.
Next Steps
Now that you have a working bookmark manager and a solid understanding of OpenCode, here are some directions to explore:
- Add authentication — Use the Build agent to add JWT-based auth with user accounts
- Deploy to production — Ask OpenCode to generate a Dockerfile and deploy to Fly.io or Railway
- Build a browser extension — Create a Chrome extension that adds bookmarks via the API
- Explore more Zen models — Try Big Pickle, MiniMax M2.1, or run a local model through Ollama
- Create more custom agents — A "refactor" agent, a "docs" agent, or a "security audit" agent
The OpenCode documentation and the awesome-opencode repository have more plugins, themes, and community configurations to explore.
Sources
- OpenCode — Official Documentation
- OpenCode — GitHub Repository
- OpenCode — Zen Models
- OpenCode — Agents Documentation
- OpenCode — MCP Servers
- OpenCode — CLI Mode
- OpenCode — Configuration
- OpenCode — GitHub Integration
- Ollama — OpenCode Integration
- awesome-opencode — Community Resources
- Composio — MCP with OpenCode
- Computing for Geeks — Setup OpenCode AI