# Use Plotwork with open and self-hosted models

> Page: https://docs.plotwork.dev/mcp/open-models. Index of every page: https://docs.plotwork.dev/llms.txt

How to run Plotwork's MCP tools with an open model, such as gpt-oss, Qwen, Gemma, or Llama, served by Ollama, LM Studio, vLLM, or llama.cpp, through an MCP client such as Goose, Cline, Continue, or the OpenAI Agents SDK. It is for anyone who wants diagrams drawn by a model that runs on their own hardware.

## TL;DR

| Question | Answer |
|---|---|
| Does Plotwork work with open models? | Yes. The MCP server is the same for every model. The model needs tool calling and enough context. |
| What should I change first? | The context. Ollama runs most models at 4,096 tokens on a GPU with less than 24 GB, which is too small for any agent. Set 16,384 or more. |
| Which tools should I list? | The `core` profile: set `PLOTWORK_TOOLS=core`. It lists the eight workflow tools in about 1,700 tokens instead of about 17,500 for all eighteen. |
| Which model did best? | On 2026-10-20, gpt-oss-20b passed all four of our tasks with the core profile and gemma4:12b passed three, at a 16,384-token context. See [Results](#results). |
| Can I run it with no network at all? | Yes. Run the model locally and Plotwork from the npm package, or from the [self-hosted image](https://docs.plotwork.dev/mcp/self-hosting.md), which makes no outbound request. |
| Which hardware? | gpt-oss-20b runs on a 16 GB GPU. gpt-oss-120b needs about 80 GB of GPU memory, so serve it with vLLM on a data-center GPU or use a cloud provider. |

## 1. Serve the model

Every server below speaks the OpenAI Chat Completions API, which every MCP client in this guide can call. Pick a model that supports tool calling.

| Server | Endpoint | Context |
|---|---|---|
| Ollama | `http://localhost:11434/v1` | 4,096 tokens by default on a GPU with less than 24 GB of memory (32,768 from 24 GB, 256k from 48 GB). The `/v1` endpoint cannot change it per request, so set it on the server or in a Modelfile, below. |
| LM Studio 0.3.17 or later | `http://localhost:1234/v1` | Set the context length when you load the model. |
| vLLM | `http://<host>:8000/v1` | The model's own, limited by GPU memory. |
| llama.cpp `llama-server` | `http://localhost:<port>/v1` | `-c 32768`. Pass `--port`, because the default changed between releases. |

Raise Ollama's context for every model:

```sh
OLLAMA_CONTEXT_LENGTH=32768 ollama serve
```

Or make a model with a larger context and call it by name:

```text
FROM gpt-oss:20b
PARAMETER num_ctx 32768
```

```sh
ollama create gpt-oss-32k -f Modelfile
ollama ps   # the CONTEXT column shows what a loaded model runs with
```

Serve gpt-oss with tool calling on vLLM:

```sh
vllm serve openai/gpt-oss-20b --tool-call-parser openai --enable-auto-tool-choice
# gpt-oss-120b on one 80 GB GPU
vllm serve openai/gpt-oss-120b --tool-call-parser openai --enable-auto-tool-choice --gpu-memory-utilization 0.95 --max-num-batched-tokens 1024
```

Serve a GGUF model with tool calling on llama.cpp:

```sh
llama-server --jinja -hf bartowski/Qwen2.5-7B-Instruct-GGUF:Q4_K_M -c 32768 --port 8080
```

## 2. Choose the core profile

The server lists its tools to the client on every connection, and the client hands the list to the model before any work starts. All eighteen tools, with their descriptions and schemas, are about 36,600 characters as the model sees them (about 9,100 tokens), and the server instructions add about 5,100. A model with a 16,384-token context then has a few thousand tokens left for the task, the language reference, and the answers.

The `core` profile lists eight tools with short descriptions and short input schemas, about 6,900 characters with its instructions (about 1,700 tokens):

| Tool | What it does |
|---|---|
| `suggest_diagram` | Ranks the kinds of diagram for a request and gives a starter that parses. |
| `get_diagram_syntax` | Returns the language of one kind, with a tested example. |
| `draw_diagram` | Checks the source and draws it, with every error's line, column, and hint. |
| `edit_diagram` | Changes a diagram with typed operations. |
| `import_source` | Builds a diagram from SQL, OpenAPI, Terraform, docker-compose, Kubernetes, and more. |
| `convert_mermaid` | Converts one Mermaid diagram. |
| `sync_markdown` | Draws every diagram fence of a Markdown document. |
| `map_icons` | Finds the icons for many service names at once. |

The tools answer exactly as they do in the full profile; only what the list says about them changes. The pattern tools, `plan_document_diagrams`, and the single tools are left out, because one of these eight covers each of them.

| Set it | How |
|---|---|
| npm package | The environment variable `PLOTWORK_TOOLS=core`, or the argument `--tools core`. |
| Self-hosted image | `PLOTWORK_TOOLS=core` on the container. Any other value stops the start. |

Use `core` for a model with a context of 32,768 tokens or less, or one that picks the wrong tool from a long list. Use the full profile, the default, for a model with a large context that you want to use the patterns and single tools. `PLOTWORK_TOOLS` and `--tools` need a release of `@buun_group/plotwork-mcp` published after 2026-10-20. An older release ignores the variable and lists every tool, and refuses `--tools`, so the examples below set the variable.

## 3. Connect a client

Each snippet starts the npm package with `npx`. Status says whether we ran it ourselves on 2026-10-20 or checked it against the client's documentation only.

### Goose

`~/.config/goose/config.yaml`:

```yaml
GOOSE_PROVIDER: ollama
GOOSE_MODEL: gpt-oss:20b
extensions:
  plotwork:
    type: stdio
    name: plotwork
    enabled: true
    cmd: npx
    args: ["-y", "@buun_group/plotwork-mcp@0.1.0"]
    envs:
      PLOTWORK_TOOLS: core
    timeout: 300
```

Set `OLLAMA_HOST` when Ollama runs on another machine. Status: checked against the [Goose configuration docs](https://goose-docs.ai/docs/guides/config-files), not run.

### Cline and Roo Code

Open MCP Servers, then Configure MCP Servers, and add the server. Roo Code reads the same shape from `.roo/mcp.json` in a project, with `alwaysAllow` in place of `autoApprove`.

```json
{
  "mcpServers": {
    "plotwork": {
      "command": "npx",
      "args": [
        "-y",
        "@buun_group/plotwork-mcp@0.1.0"
      ],
      "env": {
        "PLOTWORK_TOOLS": "core"
      },
      "disabled": false,
      "autoApprove": []
    }
  }
}
```

In the settings, choose the API provider Ollama (`http://localhost:11434`) or LM Studio (`http://localhost:1234`), then the model. Status: checked against the [Cline MCP docs](https://docs.cline.bot/mcp/configuring-mcp-servers), not run.

### Continue

`config.yaml`. Continue runs MCP tools in agent mode only.

```yaml
name: Local
version: 0.0.1
schema: v1
models:
  - name: gpt-oss
    provider: ollama
    model: gpt-oss:20b
    capabilities:
      - tool_use
mcpServers:
  - name: plotwork
    type: stdio
    command: npx
    args: ["-y", "@buun_group/plotwork-mcp@0.1.0"]
    env:
      PLOTWORK_TOOLS: core
```

Status: checked against the [Continue MCP docs](https://docs.continue.dev/customize/deep-dives/mcp), not run.

### LM Studio

In the Program tab, choose Install, then Edit mcp.json:

```json
{
  "mcpServers": {
    "plotwork": {
      "command": "npx",
      "args": [
        "-y",
        "@buun_group/plotwork-mcp@0.1.0"
      ],
      "env": {
        "PLOTWORK_TOOLS": "core"
      }
    }
  }
}
```

Status: LM Studio's [MCP docs](https://lmstudio.ai/docs/app/mcp) say the file follows Cursor's `mcp.json`; not run.

### OpenAI Agents SDK

Python, with the model on Ollama and tracing off, so nothing is sent to OpenAI:

```python
import asyncio
from agents import Agent, AsyncOpenAI, OpenAIChatCompletionsModel, Runner, set_tracing_disabled
from agents.mcp import MCPServerStdio

set_tracing_disabled(True)
model = OpenAIChatCompletionsModel(model="gpt-oss:20b", openai_client=AsyncOpenAI(base_url="http://localhost:11434/v1", api_key="ollama"))

async def main():
    async with MCPServerStdio(
        name="plotwork",
        params={"command": "npx", "args": ["-y", "@buun_group/plotwork-mcp@0.1.0"], "env": {"PLOTWORK_TOOLS": "core"}},
    ) as server:
        agent = Agent(name="Diagrams", instructions="Draw diagrams with Plotwork.", model=model, mcp_servers=[server])
        print((await Runner.run(agent, "Draw the sign-in sequence of a web app.")).final_output)

asyncio.run(main())
```

Status: checked against the [Agents SDK docs](https://openai.github.io/openai-agents-python/mcp/), not run.

### Codex CLI

`~/.codex/config.toml`:

```toml
oss_provider = "ollama"

[mcp_servers.plotwork]
command = "npx"
args = ["-y", "@buun_group/plotwork-mcp@0.1.0"]
env = { PLOTWORK_TOOLS = "core" }
startup_timeout_sec = 30
```

```sh
codex exec --oss --local-provider ollama -m gpt-oss:20b "Draw the sign-in sequence of a web app and save it to signin.diagram"
```

Status: run on 2026-10-20 with Codex CLI 0.154.0 and Ollama 0.30.6, and it did not work. Codex started the server and listed its tools, but it sends a server's tools to the model as one group, and Ollama passed that group to gpt-oss as a single function named `plotwork` with no parameters, so the model could call no Plotwork tool. The run also needs a context above Ollama's default: at 4,096 tokens, Codex's own prompt fills it. Use another client until Codex and Ollama agree on grouped tools.

### A client of your own

An MCP client that bridges to an OpenAI-compatible model turns each listed tool into a function: the tool's name, its description, and its input schema as `parameters`. Every Plotwork input schema is an object with plain types, flat nullable unions, and short enums, with no `$ref`, `oneOf`, or `allOf`, which Ollama, llama.cpp, vLLM, and LM Studio all read. OpenAI's strict mode needs every object closed, so the tools that take an open map of keys (`settings`, a pattern's `slots`, an edit's `options`) must be sent with `strict: false`; in the core profile that is `edit_diagram` alone.

## 4. Run it fully offline

The npm package makes no network request by default, so a local model and `npx` already keep diagram text on your machine once the package is installed. For a team, run the [self-hosted image](https://docs.plotwork.dev/mcp/self-hosting.md) next to the model server: one container with MCP over Streamable HTTP at `/mcp`, no outbound request, and no telemetry. Start it with `PLOTWORK_TOOLS=core` and point a client that speaks Streamable HTTP at `http://<host>:3106/mcp` with `Authorization: Bearer <token>`.

## Results

We ran each model on four tasks through the stdio server on 2026-10-20: a sign-in sequence diagram saved to a file, a Mermaid flowchart converted, an AWS architecture with real AWS icons, and a Terraform snippet imported with `import_source`. A task passes when the saved file is valid, of the right kind, and meets the task's own check. Each cell is one run at temperature 0 with at most twelve model replies, on Ollama 0.30.6 and an RTX 5080 with 16 GB, at a 16,384-token context. Four tasks is a small sample: one run in either direction moves a model by a quarter, so read these as a guide, not a ranking.

| Model | Size on disk | Core profile | Full profile | Notes |
|---|---|---|---|---|
| gpt-oss:20b | 13 GB | 4 of 4 | 2 of 4 | Followed the hints to a valid diagram. With the full profile it once sent a tool call Ollama could not parse, and once saved source it had not checked. |
| gemma4:12b | 7.6 GB | 3 of 4 | 2 of 4 | Dropped a quote when it copied an imported diagram into the file. With the full profile it gave up on two tasks after repeated errors, without saving a file. |
| qwen3.5:9b | 6.6 GB | 3 of 4 | 1 of 4 | Escaped line breaks twice (`\\n`) in the AWS task until it ran out of turns. |
| qwen2.5-coder:14b | 9.0 GB | 0 of 4 | 0 of 4 | Writes tool calls as JSON in a code block, which Ollama does not read as a call. Use qwen2.5 or qwen3 instead. |
| llama3 (8B) | 4.7 GB | 0 of 4 | 0 of 4 | Ollama refuses tools for this model. |
| llama3.2:3b | 2.0 GB | 0 of 4 | 0 of 4 | Calls tools, but too often with the wrong arguments or as text. |

At Ollama's default 4,096-token context, gpt-oss:20b and gemma4:12b passed no task with either profile: the full tool list alone is longer than the context, so the model saw a cut prompt and made no call, and with the core profile the first answer from `get_diagram_syntax` filled what was left. At 32,768 tokens gpt-oss:20b passed 3 of 4 with the core profile and 2 of 4 with the full one.

What the runs changed: the parse errors these models made most now say what to write, each in one error (a Mermaid arrow such as `-->>`, a quote or line break escaped twice, a node type used as the statement, a second header line, a `title` line, an option on a line of its own, a group or actor with no label), and the core profile tells the model to save only source a tool has called valid. On the same tasks, the core profile went from 7 to 10 passes of 16 for the four models that call tools (gpt-oss, gemma, qwen3.5, and llama3.2, which passed none either time), and the full profile stayed at 5 of 16.

## See also

| Doc | Why |
|---|---|
| [Connecting an agent](https://docs.plotwork.dev/mcp/connect.md) | Client settings for Claude Code, Cursor, VS Code, and Claude Desktop |
| [MCP server](https://docs.plotwork.dev/mcp/overview.md) | Every tool, and how the local and hosted servers differ |
| [Self-hosting the engine](https://docs.plotwork.dev/mcp/self-hosting.md) | The container for running Plotwork inside your network |
| [Writing with an agent](https://docs.plotwork.dev/mcp/agent-loop.md) | The loop a model follows to write, check, and draw a diagram |
| [Tool reference](https://docs.plotwork.dev/mcp/tools.md) | The full schema of every tool |
