---
url: /agentic-ai/opencode-agent.md
---
# OpenCode Agent

OpenCode is ALIDA's operational agent. It exposes APIs and SSE streaming, and can be specialized for a domain through a discovery dataset.

![OpenCode workflow with MCP services](/images/opencode-agent-mcp-workflow.png)

## Inputs: datasets and MCP servers

::: tip
You can connect OpenCode to both **datasets**, which specialize the agent with instructions, skills, and tools, and **remote MCP servers**, which provide additional capabilities. Both are configured as input ports of `dataset` type, and the agent accepts multiple ports.
:::

### Dataset

During startup, OpenCode downloads the dataset and merges its files into `/opencode`, keeping their relative paths. To specialize the runtime, provide a dataset for the target domain.

The port is passed as `--input-dataset` (or `--input-dataset-N` for additional ports) with the S3 prefix, together with `.s3_bucket`, `.s3_URL`, `.s3_ACCESS_KEY`, `.s3_SECRET_KEY` and the optional `.s3_REGION` (default `garage`) and `.use_ssl` suffixes. Startup fails if a prefix contains no files.

A typical dataset structure is:

```text
AGENTS.md
opencode.json
agents/
  domain-expert.md
skills/
  domain-workflow/
    SKILL.md
tools/
  domain-tool.ts
plugins/
  domain-plugin.ts
init.d/
  ...
reference-documentation.md
```

The structure is entirely optional: you do not need to create or fill every folder. Include only the files you need; because each file is written to `/opencode` at its own path, a file with the same path is overwritten.

Dataset contents have distinct roles:

* `AGENTS.md` contains shared, global instructions that guide the main agent and subagents.
* `opencode.json` is the OpenCode configuration: model and providers, local MCP servers, permissions, and instruction files. At startup it is extended with the MLflow plugin and the remote MCP servers connected as input ports. For more details, see [Config | OpenCode](https://opencode.ai/docs/config/).
* `agents/` contains Markdown files, one for each specialized subagent. Each file defines its role, expertise, and operating instructions, for example for a domain expert. For further examples of defining a subagent, see [Agents | OpenCode](https://opencode.ai/docs/agents/#examples).
* `skills/` contains reusable procedures. Each skill has its own folder and a `SKILL.md` file describing when to apply it and which steps to perform. For more details, see [Agent skills | OpenCode](https://opencode.ai/docs/skills/).
* `tools/` contains custom tools the agent can invoke during a conversation. Tools are defined in JavaScript or TypeScript files and can run domain integrations or scripts. For more details, see [Custom tools | OpenCode](https://opencode.ai/docs/custom-tools/).
* `plugins/` contains JavaScript or TypeScript plugins that extend OpenCode through event hooks, for example to integrate external services, change agent behavior, or add tools. For more details, see [Plugins | OpenCode](https://opencode.ai/docs/plugins/).
* `init.d/` contains the scaffold copied into the working repository at startup; files already present in the repository are not overwritten. See the [Service Writer scaffold](#service-scaffold) for an example.
* Domain reference documentation remains outside `agents/` and is referenced by instructions or prompts as needed.

Prompts, skills, tools, auxiliary agents, and documentation in the dataset determine the agent's actual specialization. The same runtime can therefore be reused in different domains by changing the dataset.

### MCP server

A port becomes a remote MCP server when `--input-dataset-N.metadata` holds JSON metadata whose `datasetFileType.type` is `endpoint` and whose `datasetFileType.details.url` is the server address:

```text
--input-dataset-2.metadata='{"name":"my-mcp","datasetFileType":{"type":"endpoint","details":{"url":"http://host:8000"}}}'
```

The server name is the slugified `name` field, and the URL is normalized to end with `/mcp` and checked for reachability before being added to the OpenCode configuration. A port with `.metadata` is never downloaded from S3.

OpenCode can use capabilities provided by an MCP server, including an ADK Agent. However, it cannot be used as an MCP server by ADK.

***

## Example: ALIDA Service Writer

For the Service Writer, OpenCode works on a repository associated with the BDA and uses an ALIDA-focused dataset. The workflow generally includes:

1. defining or updating `.alida/spec.md`;
2. reading the relevant documentation;
3. implementing the Service code;
4. generating and validating `src/arguments.py` and `meta-model.json`;
5. producing documentation and CI configuration;
6. testing, committing, pushing, and opening a merge request after the pipeline succeeds.

At startup, OpenCode creates or clones a repository in the BDA namespace and, when MLflow is configured, creates or retrieves the corresponding MLflow experiment. The BDAID maintains the connection between the chat, artifacts, and operating context.

Startup behavior in detail:

* OpenCode runs in `/workspace`; the service repository is cloned into `/workspace/service`, so every service path the agent writes starts with `service/`.
* If `GIT_URL` is set, the repository is cloned and any missing mandatory files are copied into it. No GitLab project is created.
* If `GIT_URL` is empty, a GitLab project named after the slugified `BDA_ID` is created (or reused) in the group identified by `GITLAB_GROUP`, with default branch `GIT_BRANCH` or `develop`. The project is then cloned and, if newly created, seeded with the initial service scaffold.
* Git authentication uses `GITLAB_TOKEN` only, over HTTP or HTTPS.
* If `MLFLOW_TRACKING_URI` is not set, the MLflow experiment is skipped and the agent runs without the `@mlflow/opencode` plugin.

### Service scaffold

The mandatory files come from `/opencode/init.d` (overridable with `INIT_DIR`). Each file is copied into the repository only if it does not already exist, so existing files are never overwritten. Because input datasets are merged into `/opencode` before the repository is prepared, a dataset can provide its own `init.d/` to replace the scaffold.

```text
.alida/
  spec.md
  iterations.md
src/
  main.py
  arguments.py
  utils.py
tests/
  .gitlab-ci-test.yml
  seed.py
  run_scenarios.py
.gitlab-ci.yml
Dockerfile
docker-compose.yml
documentation.md
meta-model.json
requirements.txt
```

The scaffold is a placeholder and is neither valid nor runnable as is: `meta-model.json` has an empty `name` and `url`, `src/main.py` contains no logic, `docker-compose.yml` has an empty `command`, and the test files contain `FILL` markers. An empty `name` tells the `metamodel-writer` subagent that the service is new (version `1.0.0`). The agent rewrites these files during the operating cycle, and `validate-metamodel` passes only after that.

### ALIDA dataset

The dataset determines what the coding agent knows and how it behaves. For the Service Writer, the ALIDA dataset provides ALIDA-specific prompts, skills, and the `alida-documentation.md` file.

### Agent tools and operating cycle

The main agent instructed to develop ALIDA services has the following tools:

* `validate-metamodel`: validates `meta-model.json` and its `src/arguments.py` file against the ALIDA services contract.
* `generate-gitlab-ci`: generates the standard ALIDA CI configuration, including the service-registration phase.
* `commit-and-push-service`: the only way the agent delivers a service. It embeds `documentation.md` into `meta-model.json:markDown.value` (from which the platform obtains the manual displayed in the user interface), sets the version, validates the meta-model and refuses to commit if it is invalid, prepends the changelog entry to `.alida/iterations.md`, then commits and pushes to `feat/<service-name>` after confirming the repository matches `GIT_URL`. There is no standalone documentation-embedding tool.
* `get_useful_info`: returns deterministic information about the execution environment, including the current date, the active user and the GitLab `project_id`.
* `take-a-coffee`: waits 10–300 seconds (default 30) between two pipeline checks, so the agent does not exhaust its polling budget while a job is still running.
* [gitlab-mcp](https://github.com/zereight/gitlab-mcp): MCP server that lets the agent interact with GitLab. Only these tools are enabled: `list_pipelines`, `get_pipeline`, `list_pipeline_jobs`, `get_pipeline_job_output`, `list_merge_requests`, `create_merge_request`, `get_merge_request_conflicts`, `search_repositories`, `create_repository`.

The ALIDA dataset also ships these skills:

* `write-service-tests`: writes the CI end-to-end tests, with a seed and a scenario matrix (one baseline run plus one run per parameter, plus edge cases).
* `brainstorming`: turns a vague request into a settled, written specification before development starts.
* `caveman`: compressed response mode (`lite`, `full`, `ultra`) that reduces output tokens and persists for the session until turned off.

::: warning GitLab permissions
To commit, push, and create branches, the configured GitLab identity needs access to the group where the repository is written, with at least the `Maintainer` role: the `Developer` role is not sufficient to create branches.
:::

The operating cycle starts with creating or reviewing `.alida/spec.md`, then reads ALIDA documentation related to the required integrations. The agent implements the service, delegates the generation of `src/arguments.py` and `meta-model.json` to the `metamodel-writer` subagent, validates the result, and delegates user-facing documentation to the `documentation-writer` subagent. It then generates CI configuration and writes end-to-end tests. Finally, `commit-and-push-service` embeds the documentation in the meta-model, records the iteration and publishes the `feat/<service-name>` branch; the agent waits for the pipeline, polling with `take-a-coffee` up to 20 times, and, if necessary, fixes pipeline errors. After successful validation, it opens a merge request. Merging into `develop` or `dev` starts service build and registration using the provided `meta-model.json`.

```mermaid
 flowchart TD
   A["User request"] --> B{"spec.md<br/>exists?"}

   B -- No --> C["Iteration 0 - new service<br/>- v1.0.0"]
   B -- Yes --> D["Review - increment one<br/>version segment"]

   C --> E["Write .alida/spec.md<br/>propose default values,<br/>discuss with user"]
    D --> E

   E --> F["Read ALIDA<br/>documentation"]
   F --> G["Write service code<br/>main.py, Dockerfile,<br/>docker-compose,<br/>requirements.txt"]
   G --> H["metamodel-writer subagent<br/>arguments.py + meta-model.json"]
   H --> I{"validate-<br/>metamodel<br/>OK?"}

   I -- No --> H
   I -- Yes --> J["documentation-writer subagent<br/>documentation.md"]

   J --> L["write-service-tests skill<br/>gitlab-ci.yml + tests directory"]
   L --> N["commit-and-push-service tool<br/>embeds documentation, updates iterations.md,<br/>pushes to feat/&lt;service-name&gt; branch"]
   N --> O["Wait for pipeline<br/>take-a-coffee between checks"]
   O --> P{"Pipeline<br/>outcome?"}

   P -- OK --> Q["Create merge request<br/>to develop"]
   Q --> R["Communicate MR URL<br/>END"]

   P -- Failed --> S{"Remaining<br/>fix attempts?"}
   S -- No --> T["Communicate latest log<br/>END"]
   S -- "Yes, maximum 2" --> U["Analyze log and fix"]
    U --> N
```

### Reference documentation and validation

The agent reads ALIDA documentation from a read-only local copy in `/opencode/docs`, starting from `/opencode/docs/index.md`. It covers the service layout (`core.md`), the meta-model (`meta-model.md`), Spark (`spark.md`) and the port types in `ports/` (`dataset`, `model`, `stream`, `generic-io`, `rest`, `multiple`, `monitoring`).

The `validate-metamodel` tool, also run by `commit-and-push-service` before committing, enforces these rules:

* `meta-model.json` must contain `name`, `version`, `description`, `accessLevel`, `mode`, `url`, `properties` and `markDown` with its `value`.
* `name` uses lowercase letters, digits and hyphens, cannot start or end with a hyphen, and is at most 30 characters.
* Property `valueType` is one of `STRING`, `BOOLEAN`, `DOUBLE`, `JSON` (there is no `INT`).
* At most one property has `inputData: true` and at most one has `outputData: true`.
* Every property except `assets` must have a matching argument in `src/arguments.py`.
* `BOOLEAN` arguments use `type=str2bool` (not `type=bool` nor `store_true`/`store_false`), and `arguments.py` must define `str2bool`; `DOUBLE` arguments use `type=float` or `type=int`.

### Runtime configuration

* The model is set with the `OPENCODE_MODEL` environment variable (default `opencode/musa-spark-1.3-free`). A local Ollama provider at `http://localhost:11434/v1` is also configured; the `openai`, `gemini` and `gitlab` providers are disabled.
* At startup, the configuration is extended through `OPENCODE_CONFIG_CONTENT` with only the `@mlflow/opencode` plugin (when MLflow is enabled) and the remote MCP servers discovered from the input ports.
* Permissions: the agent can run shell commands and read any directory, cannot ask interactive questions, and cannot edit `/opencode/docs` or `/opencode/skills`. `AGENTS.md` is always loaded as instructions.
* OpenCode is served with `opencode serve` on port `10000` (overridable with `OPENCODE_PORT`), exposing its APIs and SSE stream. For browser clients that need CORS, the repository includes an optional `sse_proxy.js` that listens on port `3001` and forwards `/sse-proxy?target=<url>` as an event stream and `/http-proxy?target=<url>` as plain HTTP.
