Using cagent with gpt-oss-120b and OVH AI endpoints

Can we do without Claude Code (and Claude.ai)?


After reading the excellent article OpenCode with Docker Model Runner for Private AI Coding by Ignasi Lopez Luna, I wondered how far an LLM like gpt-oss-120b could take me in terms of code generation good enough to do without Claude Code (and Claude.ai).


To start answering this question, I kicked off a series of experiments that will lead to a series of articles.


Today, my first experiment will use the following components:

First things first, my setup

Here’s the structure of my VSCode project:

.
├── .agents
   └── skills
       └── methodical-dev
           └── SKILL.md
├── .devcontainer
   ├── compose.yml
   ├── devcontainer.json
   ├── Dockerfile
   └── ovh.env
├── cagent.yaml
└── README.md

Let’s look at the contents of some of these files (I’ll provide a link to a Codeberg repository at the end of this article so you can easily find and reuse them all).


I always use Docker Compose in my devcontainer projects.

You can find an article about my way of doing devcontainer with Docker Compose here: My Little Arrangements with DevContainer - Part 1.

Devcontainer setup

compose.yml

The compose file is extremely simple. The important point is the env_file section which links to the ovh.env file.

services:

  cagent-workspace:
    build:
      context: .
      dockerfile: Dockerfile
    network_mode: "host"
    volumes:
      - ..:/workspaces:cached
    command: sleep infinity
    env_file:
      - ovh.env

ovh.env

OVH_API_KEY=<your-ovh-api-key>

✋ Don’t forget to create a .gitignore file at the root of your project and add ovh.env to it to avoid committing your OVH API key.

Dockerfile

These days I mainly work with Go, I also use NodeJS, Docker, Git, … and of course cagent. So here’s the content of my Dockerfile:

FROM --platform=$BUILDPLATFORM docker/cagent:1.23.5 AS coding-agent

FROM --platform=$BUILDPLATFORM ubuntu:22.04 AS base

LABEL maintainer="@k33g_org"
ARG TARGETOS
ARG TARGETARCH

ARG USER_NAME=vscode

ARG GO_VERSION=1.25.5
ARG TINYGO_VERSION=0.40.1
ARG NODE_MAJOR=22

ARG DEBIAN_FRONTEND=noninteractive

ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US.UTF-8
ENV LC_COLLATE=C
ENV LC_CTYPE=en_US.UTF-8

RUN <<EOF
apt-get update
apt-get install -y curl wget jq git build-essential xz-utils software-properties-common sudo sshpass unzip

apt-get install -y fd-find ripgrep
ln -s $(which fdfind) /usr/local/bin/fd
ln -s $(which ripgrep) /usr/local/bin/rg

apt-get clean autoclean
apt-get autoremove --yes
rm -rf /var/lib/{apt,dpkg,cache,log}/
EOF

# ------------------------------------
# Install Go
# ------------------------------------
RUN <<EOF

wget https://golang.org/dl/go${GO_VERSION}.linux-${TARGETARCH}.tar.gz
tar -xvf go${GO_VERSION}.linux-${TARGETARCH}.tar.gz
mv go /usr/local
rm go${GO_VERSION}.linux-${TARGETARCH}.tar.gz
EOF

# ------------------------------------
# Set Environment Variables for Go
# ------------------------------------
ENV GOROOT="/usr/local/go"
ENV GOPATH="/home/${USER_NAME}/go"
ENV PATH="${GOPATH}/bin:/usr/local/go/bin:${PATH}"

RUN <<EOF
go version
go install -v golang.org/x/tools/gopls@latest
go install -v github.com/ramya-rao-a/go-outline@latest
go install -v github.com/stamblerre/gocode@v1.0.0
go install -v github.com/mgechev/revive@v1.3.2
EOF

# ------------------------------------
# Install TinyGo
# ------------------------------------
RUN <<EOF
wget https://github.com/tinygo-org/tinygo/releases/download/v${TINYGO_VERSION}/tinygo_${TINYGO_VERSION}_${TARGETARCH}.deb
dpkg -i tinygo_${TINYGO_VERSION}_${TARGETARCH}.deb
rm tinygo_${TINYGO_VERSION}_${TARGETARCH}.deb
EOF

# ------------------------------------
# Install NodeJS
# ------------------------------------
ARG NODE_MAJOR
RUN <<EOF
apt-get update && apt-get install -y ca-certificates curl gnupg
curl -fsSL https://deb.nodesource.com/setup_${NODE_MAJOR}.x | bash -
apt-get install -y nodejs
EOF

# ------------------------------------
# Install cagent
# ------------------------------------
COPY --from=coding-agent /cagent /usr/local/bin/cagent

# ------------------------------------
# Install Docker
# ------------------------------------
RUN <<EOF
# Add Docker's official GPG key:
apt-get update
apt-get install -y ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg

# Add the repository to Apt sources:
echo \
    "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
    $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
    tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-model-plugin
EOF

# ------------------------------------
# Create a new user
# ------------------------------------
# Create new regular user `${USER_NAME}` and disable password and gecos for later
# --gecos explained well here: https://askubuntu.com/a/1195288/635348
RUN adduser --disabled-password --gecos '' ${USER_NAME}

#  Add new user `${USER_NAME}` to sudo group
RUN adduser ${USER_NAME} sudo

# Ensure sudo group users are not asked for a password when using
# sudo command by ammending sudoers file
RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers

RUN <<EOF
groupadd docker
usermod -aG docker ${USER_NAME}
newgrp docker
EOF

#  Add new user `${USER_NAME}` to docker group
RUN adduser ${USER_NAME} docker

# Set the working directory
WORKDIR /home/${USER_NAME}

# Set the user as the owner of the working directory
RUN chown -R ${USER_NAME}:${USER_NAME} /home/${USER_NAME}

# Switch to the regular user
USER ${USER_NAME}

# Avoid the message about sudo
RUN touch ~/.sudo_as_admin_successful

# ------------------------------------
# Install OhMyBash
# ------------------------------------
RUN <<EOF
bash -c "$(curl -fsSL https://raw.githubusercontent.com/ohmybash/oh-my-bash/master/tools/install.sh)"
EOF

I used the multi-stage build approach to install cagent in my custom Docker image. Basically, I used the official cagent image as the first stage to copy the cagent binary into my final image.


Now let’s look at the cagent configuration.

cagent configuration with cagent.yaml

I’ve trimmed the instruction part which you can find in its entirety here: cagent.yaml

agents:
  root:
    model: gpt-oss
    skills: true
    instruction: |
      You are Bob, a senior software engineer and code expert. You are an interactive CLI tool specialized in coding, code analysis, code review, debugging, architecture, and software expertise.
      <see the entire instruction here: https://codeberg.org/ai-sovereignty/cagent-ovh/src/branch/main/cagent.yaml>

    toolsets:
      - type: shell
      - type: filesystem
      - type: fetch
      - type: todo
      - type: think
      - type: lsp
        command: gopls

providers:
  ovh_provider:
    api_type: openai_chatcompletions
    base_url: https://oai.endpoints.kepler.ai.cloud.ovh.net/v1
    token_key: OVH_API_KEY

models:

  # Models using the ovh provider
  gpt-oss:
    provider: ovh_provider
    model: gpt-oss-120b

  qwen3-coder:
    provider: ovh_provider
    model: Qwen3-Coder-30B-A3B-Instruct

The key things to note in this configuration are:

First contact with cagent and gpt-oss-120b

Once the devcontainer is up and running, open a terminal in VSCode and run the following command to start cagent:

cagent run cagent.yaml

You should see this in your terminal:

cagent

And you can start interacting with your coding agent Bob:

cagent

First code generation: a small Go “microservice” using my “methodical-dev” skill

So I asked Bob (and cagent) to generate a small Go microservice that exposes an HTTP endpoint and returns a static JSON response. I also asked Bob to use my “methodical-dev” skill to do this.

cagent

cagent properly used the “methodical-dev” skill to break down the task into several steps and generate code at each step:

cagent

cagent

I then asked for a small documentation to be generated for this microservice:

cagent

So, cagent properly created a demos folder at the root of my project, with a README.md file containing the generated microservice documentation, and of course a main.go file containing the microservice code.


And it works:

cagent

This first contact is pretty nice. But it’s far from enough to answer my initial question: can I do without Claude Code (and Claude.ai)?

The “WebAssembly” test 😉

I’ve had a passion for quite some time for the “WebAssembly” topic and especially for the “WebAssembly System Interface” (WASI) specification which allows running WebAssembly code outside the browser, notably on the server side. To this day I stick with WASI Preview 1.0 which is more than enough for my current needs and is supported by many WASI tools and runtimes.


So here’s what I asked Bob (and therefore gpt-oss-120b) for this second test:

I'm working with Go and TinyGo.
- You'll work in the `/demos` folder

### Guest side (the WASM module in TinyGo)
- I'd like to create a WASM module in TinyGo (following the WASI specification) and run it from Go.
- The WASM module will expose a `greetings` function with a string parameter (e.g. `name`) and will return a string.
- To compile the wasm module following the WASI specification, you'll use TinyGo: https://github.com/tinygo-org/tinygo

### Host side (Go)

- On the Go side, I'd like to be able to call this `greetings` function by passing it a string (e.g. `name="Bob"`) and get the returned string.
- To run the module you'll use the wazero framework: https://github.com/wazero/wazero
- You'll provide a Go example to call the wasm module compiled with TinyGo and executed with wazero.

… And it was a big fail! 🤣 But I’m not surprised, not long ago even ChatGPT, Claude.ai or Gemini were failing miserably on this topic.


✋✋✋ One important thing when you’re using coding agents, regardless of the platform you’re using, if you want code that meets your expectations: clean, functional, understandable, maintainable, … it is absolutely necessary (in my opinion) to provide code examples, documentation … to help the agent understand what you expect from it. And this is even more true for a topic like “WebAssembly” which is still very young and not necessarily well covered in the training data of language models.


As you saw, cagent recognizes the concept of “skills” and is able to use them. So I used this to provide “contextual” knowledge to the Bob agent, meaning knowledge specific to my project.

The “WebAssembly” test, second attempt with “knowledge” skills

So I created “knowledge skills” (I’m not even sure the term exists, but it says exactly what it means). To create these skills I used the content of this example project: https://codeberg.org/wasmxp/wasi-demos along with the associated tutorial: https://codeberg.org/wasmxp/wasi-demos/src/branch/main/docs/tutorial-en.md. I ended up with 3 skills:

And I added these skills to the .agents/skills folder:

.
├── .agents
   └── skills
       ├── methodical-dev
   └── SKILL.md
       ├── wasm-host-function
   └── SKILL.md
       ├── wasm-numbers
   └── SKILL.md
       └── wasm-string
           └── SKILL.md
├── .devcontainer
   ├── compose.yml
   ├── devcontainer.json
   ├── Dockerfile
   └── ovh.env
├── cagent.yaml
└── README.md

You can find the content of these skills here: https://codeberg.org/ai-sovereignty/cagent-ovh/src/branch/main/.agents/skills


I then asked Bob to redo the same exercise as before, namely generate a WASM module in TinyGo that exposes a greetings function taking a string parameter and returning a string, and to make it so I can call this function from Go using wazero, but this time using the wasm-string skill.

using /wasm-string implement <all the requirements from the previous task>

cagent

And there, 🎉, Bob/cagent managed to generate working code: the wasm module was built successfully. And the host code correctly calls the greetings function from the wasm module and displays the result.

Conclusion

Even though we’re far from the “magic” 🧙‍♂️ of Claude Code, we can nonetheless, with the right resources, the right examples, … leverage a language model like gpt-oss-120b to generate code that is functional, clean, understandable, … And on top of that, you’ll get your brain working (a bit) again. 🧠


Of course, I need to experiment more to improve my prompts, my skills and the configuration of cagent, and to work on other projects. But it’s already a good start. This also means that we can build AI solutions with a somewhat “beefed up” machine (to run the language model). And we’ve also seen the value of cagent which is highly configurable, allowing it to be adapted to very specific use cases.

© 2026 k33g Project | Built with Gu10berg

Subscribe: 📡 RSS | ⚛️ Atom