Public specification for running a small local coding model on a private inference host and exposing it to premium coding agents as tools, not as another cloud model.
This document is intentionally generic. It does not include personal hostnames, LAN addresses, usernames, or hardware serials. Those values stay in local environment files that are never committed.
Use a premium agent (Cursor, GitHub Copilot, or Claude Code) as the orchestrator. Use a local SLM on a private GPU host as cheap, bounded generation capacity.
Typical split:
| Task class | Owner |
|---|---|
| Planning, architecture, ambiguous debugging, multi-system diagnosis | Premium agent |
| Boilerplate, tests, mechanical refactors, summaries, explanations | Local SLM via MCP |
| Local answer looks wrong or incomplete | Premium agent reviews / retries |
The goal is not to replace the premium subscription. The goal is to stop spending premium tokens on repetitive generation that a 9B–24B local model can handle.
OpenRouter and other third-party routers are out of scope. Premium models come from the agent product itself (Cursor Ultra / included models, Copilot, Claude).
- One MCP implementation, three front ends. Cursor, Copilot, and Claude Code all call the same local tool server.
- Keep the inference host private. Prefer Ollama on
127.0.0.1and reach a second host withssh -L. Do not port-forward 11434 through a public router. Do not put Ollama on the internet. - Do not point Cursor at Ollama as a model provider. Cursor's normal model
selector and "Override OpenAI Base URL" path go through Cursor's servers, so
a private
http://<lan-host>:11434endpoint is not reachable. A local MCP process on the workstation can reach that LAN URL. - Public-repo safe. Committed files use placeholders and environment
variable expansion. Real IPs, hostnames, and tokens live in
.envor the user environment. - Start simple. Measure whether the local model is good enough before adding automatic classifiers, retries, or cost dashboards.
Cursor premium ────┐
Copilot premium ────┼──> local-coding-slm MCP (stdio on the workstation)
Claude premium ────┘ │
│ localhost / SSH local forward
▼
Ollama HTTP API
│
┌────────────┴────────────┐
▼ ▼
fast SLM stronger SLM
(everyday coding) (harder coding tasks)
| Role | What it is | What it is not |
|---|---|---|
| Workstation | Machine running the IDE / CLI. Hosts the MCP server. | Does not need to load the model. |
| Inference host | Machine that runs Ollama (NVIDIA GPU now; Halo-class AMD later). | Does not need the IDE installed. |
| Premium agent | Plans, reads the repo, calls MCP tools, reviews output. | Does not talk to Ollama directly. |
| Local SLM | Generates bounded artifacts (tests, diffs, explanations). | Does not own repository context or tool use in the IDE. |
Workstation and inference host may be the same computer. They may also be two machines on one private network. The MCP server only needs an HTTP URL.
C4 context, container, component, and deployment-variant diagrams: docs/c4.md.
These are sizing guidelines, not a shopping list and not a description of any specific machine.
| Component | Recommended starting point |
|---|---|
| Inference GPU | NVIDIA GPU with 16 GB VRAM is enough for the starter models below |
| Inference system RAM | 32–64 GB is plenty; extra RAM helps offload, not interactive speed |
| Workstation | Any machine that can run the IDE and reach the inference host over LAN |
| Network | Private Ethernet or Wi-Fi. Gigabit is more than enough (prompts/tokens only) |
| OS on inference host | Windows, Linux, or macOS. Ollama is first-class on all three. Dual-boot is not required. |
VRAM is the scarce resource. System RAM offload can load a larger model; it does not make it feel fast for interactive coding.
Rough fit on a 16 GB GPU:
| Model class | Typical quantized size | Expected fit | Use |
|---|---|---|---|
| ~8–9B Q4 | ~6–8 GB | Fully in VRAM with context headroom | Fast everyday coding |
| ~14B Q4 | ~8–10 GB | Fully / nearly fully in VRAM | Strong local coding |
| ~24B Q4 | ~15 GB | At the VRAM edge; some RAM offload possible | Harder agentic / multi-file work |
| 30B+ Q4 | 18 GB+ | Offload required | Usable but often too slow |
Context windows consume extra VRAM. Start at 16K–32K tokens even if a model advertises 256K.
An RTX 4080 Super has 16 GB VRAM. With 64 GB system RAM it matches the recommended starting point above. VRAM is still the limit. Extra RAM only helps if a model spills off the GPU, and that spill is usually too slow for interactive coding.
On this class of card, install the same starter pair as §5:
| Role | Tag | Why it fits |
|---|---|---|
| Fast | qwen3.5:9b (~6.6 GB) |
Fully on GPU, room for 16K context |
| Strong | devstral-small-2 (~15 GB) |
At the 16 GB edge; keep OLLAMA_NUM_CTX=16384 |
Install both. Default to the 9B. Escalate to Devstral only when the agent needs multi-file work.
Do not start with 30B+ Q4 on this card. Those need offload and will feel worse than Devstral in VRAM. Later, if Devstral is fully resident and you want more, try a ~14B Q4 coding tag — not a 30B.
A later inference host may be an AMD Ryzen AI Halo-class APU (unified
memory, ROCm, Ollama). That does not change the MCP contract. The
workstation still runs local-coding-slm; only OLLAMA_BASE_URL (or an
SSH local-forward to the same loopback URL) changes.
Do not start that lab until Phase 3 measurement and the existing second NVIDIA host are in a known state. Planning index: docs/roadmap.md. Public-safe notes: examples/halo-ryzen-ai.md.
On Halo, treat unified memory the same way this spec treats VRAM: start at 16K–32K context and the starter pair. A large advertised window is not permission to send the repository.
Treat tags as a starting point. Confirm current names and sizes on ollama.com/library before installing.
| Role | Suggested Ollama tag | Approx. download | Why |
|---|---|---|---|
| Fast | qwen3.5:9b |
~6.6 GB | Everyday coding; leaves VRAM for context on a 16 GB GPU |
| Strong | devstral-small-2 |
~15 GB | Software-engineering / multi-file work; near 16 GB VRAM |
Install both. Use the fast model by default. Escalate to the strong model only when the premium agent decides the task needs it.
Later candidates (not first experiments) include larger Qwen coding models whose Q4 weights exceed 16 GB VRAM. On a 16 GB card such as an RTX 4080 Super, stay on the starter pair first; see the worked example in §4.
Environment variables (never commit real values if they encode a private host):
OLLAMA_BASE_URL=http://127.0.0.1:11434
OLLAMA_FAST_MODEL=qwen3.5:9b
OLLAMA_STRONG_MODEL=devstral-small-2
OLLAMA_NUM_CTX=16384When the inference host is a second machine, prefer an SSH local forward and
keep OLLAMA_BASE_URL on the workstation set to
http://127.0.0.1:11436. Use a direct private-host URL only for the optional
firewalled LAN fallback in §9.3.
Cursor can use custom OpenAI-compatible endpoints via Override OpenAI Base
URL. Those requests are assembled on Cursor's servers. The default hosted
routing cannot reach a home-LAN or workstation localhost Ollama URL.
Enterprise private-connectivity configurations are separate designs; making
Ollama publicly reachable is outside this project.
Cursor also has a single OpenAI base-URL override. Pointing it at Ollama fights with Cursor-native premium models.
Do not use that path for this project. Keep Cursor Ultra / included premium models as-is. Add the local SLM as MCP tools.
Copilot Agent in the IDE can call local stdio MCP servers. That is the supported equivalent: premium Copilot orchestrates, local tools generate.
Copilot cloud agent (the default GitHub-hosted agent that opens PRs from issues) runs on GitHub's runners, outside this home-LAN profile. Enterprise self-hosted runners can be connected to internal resources, but that is a different deployment. Do not configure this workstation MCP server in repository Copilot cloud-agent settings.
Claude Code supports local stdio MCP servers and project-scoped .mcp.json
with ${VAR} / ${VAR:-default} expansion. That is the supported equivalent.
Anthropic-hosted Claude Code cloud sessions run outside this home-LAN profile. Organizations may instead configure self-hosted Claude environments; that is a separate deployment. Use this bridge from a local Claude Code session on the workstation.
Server name: local-coding-slm
Transport: stdio on the workstation. The server process calls Ollama over HTTP. Do not expose the MCP server itself on the public internet.
All tools return plain text (generated code, unified diff, or markdown). The premium agent decides whether to apply edits.
| Field | Type | Required | Notes |
|---|---|---|---|
task |
string | yes | What to produce. Bounded input. |
files |
array of { path, content } |
no | Snippets the premium agent chooses to send. Not a full-repo dump. The server refuses .env / private-key / token blobs, more than 12 files, or max_tokens above 4096 before calling Ollama. |
language |
string | no | Hint, e.g. java, typescript. |
style |
string | no | Short conventions: test framework, naming, etc. |
model |
fast | strong |
no | Default fast. |
max_tokens |
integer | no | Server enforces an upper bound. |
Keep payloads small. The premium agent already has repository context. Send only the files the SLM needs.
| Tool | Purpose | Typical caller prompt |
|---|---|---|
local_code |
Generate new code for a well-specified unit of work | "Write a health controller matching this existing style." |
local_refactor |
Mechanical, localized rewrite | "Rename this DTO field and update getters." |
local_generate_tests |
Unit / integration test bodies | "Add tests for these 12 services." |
local_explain |
Explain a snippet or flow | "What does this filter chain do?" |
local_review |
Cheap first-pass review | "Flag obvious null / auth / test gaps." |
local_status |
Health of Ollama + listed models | Used by acceptance tests and troubleshooting. |
- Call
POST {OLLAMA_BASE_URL}/api/chatwithstream: false. - Map
model=fast|strongtoOLLAMA_FAST_MODEL/OLLAMA_STRONG_MODEL.resolve_modelrefuses a tag outsideOFFICIAL_LIBRARY_TAGSunlessOLLAMA_ALLOW_UNOFFICIAL_TAGS=1. - Pass
options.num_ctxfromOLLAMA_NUM_CTX(default 16384). - Time out (suggested 120s fast / 300s strong) and return a structured error.
- Never execute shell commands, never write files, never open network ports other than the configured Ollama URL.
- Redact nothing special in committed code; do not log full prompts to disk by default.
Each tool uses a short, fixed system prompt. The premium agent supplies the
user task. Example for local_generate_tests:
You generate tests only. Match the language and framework hinted in the
request. Do not invent production code changes. Return files as markdown
fenced blocks with path comments, or a unified diff. If the request is
ambiguous, ask up to three clarifying questions instead of guessing.
These rules are for the premium agent, not for a separate classifier service (phase 1). Put a short copy in each client's project instructions (see §10).
Mechanical / repetitive / well-specified → local_* tools (fast, then strong)
Ambiguous / architectural / multi-system → premium model only
Local answer incomplete or wrong → premium model reviews
Delegate when all of these are true:
- The output shape is obvious (tests, boilerplate, rename, summary).
- The needed context fits in a few files.
- A wrong answer is cheap to reject.
Do not delegate:
- Incident / race / auth debugging across systems.
- Broad refactors with unclear invariants.
- Security-sensitive code without premium review.
- Anything that needs live repo tools (search, terminal, browser) the SLM does not have.
The premium model will still spend some tokens deciding and reviewing. That is expected. Savings come from not using it to emit thousands of lines of mechanical code.
Official install: https://ollama.com/download
ollama run qwen3.5:9b
ollama run devstral-small-2ollama run pulls the model and opens a local chat. Quit after the first
successful reply. Confirm with:
ollama list
ollama psollama ps shows whether the model is on GPU, CPU, or split.
Ollama binds 127.0.0.1:11434 by default. Leave that unchanged on both
same-machine and preferred two-machine deployments. From the workstation,
forward a local port to Ollama through the inference host's existing SSH
service:
ssh -N -T -o ExitOnForwardFailure=yes \
-L 127.0.0.1:11436:127.0.0.1:11434 user@<inference-host>Then keep the workstation setting on loopback:
OLLAMA_BASE_URL=http://127.0.0.1:11436Here 11436 is an example unused workstation port, while remote Ollama stays
on its own loopback port 11434. The real SSH user and host stay in local
configuration and must not be committed. Optional Ollama runtime settings:
OLLAMA_CONTEXT_LENGTH=16384
OLLAMA_KEEP_ALIVE=30mDo not create a public tunnel or router port-forward for 11434.
Use this only when SSH local forwarding is not practical. Bind Ollama to a
specific private interface where the operating system supports it; avoid
0.0.0.0. Allow inbound TCP 11434 only from the workstation.
Windows (Administrator PowerShell), private profile, with the workstation address substituted locally:
New-NetFirewallRule `
-DisplayName "Ollama LAN" `
-Direction Inbound `
-Protocol TCP `
-LocalPort 11434 `
-RemoteAddress <workstation-private-ip> `
-Action Allow `
-Profile PrivateLinux example with ufw, restricted to a workstation placeholder:
sudo ufw allow from <workstation-private-ip> to any port 11434 proto tcpWindows: ipconfig → IPv4 address.
Linux / macOS: ip -4 addr or ifconfig.
For the direct-LAN fallback only, record that address in the workstation
environment as OLLAMA_BASE_URL. Never commit it. The preferred SSH path keeps
OLLAMA_BASE_URL on loopback and uses the private address only in the local
SSH command or configuration.
If the inference host is Ubuntu in WSL2 on a second Windows PC, prefer an SSH local forward and keep Ollama on WSL localhost. Public-safe commands and placeholders: examples/downstairs-wsl-gpu.md.
If the inference host is a Halo-class AMD box (future Phase 4), use the same SSH-first pattern. Public-safe notes: examples/halo-ryzen-ai.md.
On the inference host:
curl http://127.0.0.1:11434/api/tagsOn the workstation (replace the placeholder; do not commit the real URL):
curl "$OLLAMA_BASE_URL/api/tags"Chat smoke test:
curl "$OLLAMA_BASE_URL/api/chat" \
-d '{
"model": "qwen3.5:9b",
"messages": [
{"role": "user", "content": "Write a one-line health-check function in Python."}
],
"stream": false,
"options": {"num_ctx": 16384}
}'The GPU on the inference host does the work. The workstation only sends HTTP.
Committed configs live under examples/ and use interpolation. Copy them to
the client path, or symlink after implementation exists. Keep real URLs in
.env / the user environment.
Premium side: leave Cursor Ultra / included models unchanged. Do not set Override OpenAI Base URL to Ollama.
MCP side: project file .cursor/mcp.json or user file ~/.cursor/mcp.json.
Cursor stdio servers support command, args, env, and envFile. Values
may use ${env:NAME}, ${workspaceFolder}, and ${userHome}.
Template: examples/cursor.mcp.json
Project instructions (Cursor rules / user rules), public-safe:
When a coding task is mechanical (tests, boilerplate, local rename, summary),
call the local-coding-slm MCP tools instead of generating the full artifact
yourself. Prefer local_generate_tests, local_code, local_refactor,
local_explain, or local_review. Use model=fast first. Escalate to model=strong
only if the fast result is too weak. Review the tool output before applying it.
Do not send secrets, .env files, or credentials to those tools.
Cursor Agent uses MCP tools automatically when they are relevant. Users can also ask for a tool by name.
Default Cursor Cloud Agents run on Cursor-managed remote VMs rather than the workstation. Cursor supports separately configured private connectivity, but this home-lab profile does not. This MCP server is for the local/desktop Cursor session.
IDE Agent (supported equivalent):
- VS Code 1.99+ with Copilot Chat in Agent mode.
- Workspace config:
.vscode/mcp.json(serverskey, notmcpServers). - User config: VS Code user
mcp.json. - Org Copilot Business/Enterprise: the "MCP servers in Copilot" policy must allow MCP.
Template: examples/vscode.mcp.json
The example uses a VS Code inputs prompt for OLLAMA_BASE_URL so a LAN
address is never committed. The inputs array must travel with
${input:ollamaBaseUrl}; a servers-only copy will not prompt.
Visual Studio, JetBrains, Xcode, and Eclipse also support MCP with similar stdio/HTTP shapes. Prefer VS Code Agent for the first integration.
Copilot CLI can load ~/.copilot/mcp-config.json with a mcpServers
block. Template: examples/copilot-cli.mcp.json.
Replace the clone-path placeholder locally. Do not commit a home path.
Operator clicks for A8 (not automated): docs/a8-a9-operator-checklist.md.
Copilot cloud agent / code review (not supported for this server):
The default agents run on GitHub-hosted runners outside this home-LAN profile.
Enterprise self-hosted runners are an exception, but they are not part of this
design. Do not add this workstation local-coding-slm server to the repository
Settings → Copilot → MCP servers page.
Local CLI (supported equivalent):
- Project scope:
.mcp.jsonat the repo root (safe to commit if it only uses${OLLAMA_BASE_URL}and defaults). - User / local scope:
~/.claude.jsonfor machine-specific overrides.
Claude Code expands ${VAR} and ${VAR:-default} in command, args,
env, url, and headers.
Template: examples/claude.mcp.json
# optional: add from the CLI instead of copying the file
claude mcp add --scope project --transport stdio local-coding-slm \
--env OLLAMA_BASE_URL -- \
python "${CLAUDE_PROJECT_DIR:-.}/src/local_coding_slm/server.py"Claude Code prompts once before enabling project-scoped servers from
.mcp.json. Reset with claude mcp reset-project-choices if needed.
Put the same routing paragraph from §10.1 in CLAUDE.md or a project skill.
Operator clicks for A9 (not automated): docs/a8-a9-operator-checklist.md.
Anthropic-hosted Claude Code cloud sessions run outside this home-LAN profile. Organization-configured self-hosted environments are an exception but are not part of this design. Use a local session on the workstation.
| Capability | Cursor desktop | Copilot IDE Agent | Claude Code local | Cursor Cloud Agent | Copilot cloud agent |
|---|---|---|---|---|---|
| Premium model as orchestrator | Yes | Yes | Yes | Yes | Yes |
| Local stdio MCP on workstation | Yes | Yes | Yes | Not on the workstation | Not on the workstation |
| Reach this home-lab Ollama profile | Yes, via local MCP | Yes, via local MCP | Yes, via local MCP | Not configured | Not configured |
| Project-shared public config | .cursor/mcp.json + env interpolation |
.vscode/mcp.json + inputs |
.mcp.json + ${VAR} |
n/a | n/a |
| Treat Ollama as a first-class model in the picker | Not for private LAN | Separate Copilot+Ollama flows; not this spec | Can use Ollama directly, but this spec uses MCP | No | No |
| OpenRouter required | No | No | No | No | No |
- Prefer Ollama on
127.0.0.1. Reach a second GPU host withssh -N -T -o ExitOnForwardFailure=yes -L 127.0.0.1:11436:127.0.0.1:11434 user@<inference-host>(seeexamples/downstairs-wsl-gpu.md). Do not bind0.0.0.0unless you have a written reason. - No public port forward. No ngrok, Cloudflare Tunnel, or similar.
- Do not send secrets, private keys,
.envfiles, or credentials into MCP tool arguments. - Committed configs must not contain IPs, hostnames, tokens, or usernames.
- The MCP server is a local process. It should talk only to
OLLAMA_BASE_URL. - If you later add any HTTP MCP transport, put it on localhost and authenticate it. Phase 1 stays on stdio.
- Treat MCP tool results as untrusted model output. The premium agent reviews before applying patches. A local model can still emit insecure or malicious code.
Run the defensive checker on the workstation after install or .env changes:
PYTHONPATH=src python3 scripts/check_deployment_safety.pyThe checker looks at this host only: OLLAMA_BASE_URL, model tags, whether
.env is ignored, placeholder IPs in git, and whether port 11434 is listening
on a wildcard. It does not scan other machines and it cannot prove weights
are clean. The stdio MCP server also calls classify_base_url at start and
exits if that check fails. Hostnames fail. Userinfo (user@host) and
decimal or integer-form IPs fail. Private LAN URLs still only warn.
Open weights are a privacy win (inference stays on your GPU). They are not an alignment or integrity win.
- Qwen is a family, not an SLM. Large Qwen models are LLMs. The starter
tag
qwen3.5:9bis the SLM this repo means: ~9B, local, bounded generation. - Official Ollama library tags only
(
qwen3.5:9b,devstral-small-2). Do not load a random GGUF, a stranger's fine-tune, or auser/nameblob just because it "codes better." - It is easy to ship a trojaned SLM: poisoned fine-tunes and unofficial
weight files can look helpful and still plant backdoors in generated code.
This repo will not document how to do that. Pull from the official library,
pin the tags in
.env, and review every patch. - The checker rejects path/URL-shaped tags, public binds, and any tag
outside
OFFICIAL_LIBRARY_TAGS(exact match; a listed family is not enough). Runtimeresolve_modelrefuses the same off-list tags unlessOLLAMA_ALLOW_UNOFFICIAL_TAGS=1. Lengthening or shortening that set changes accept/reject. The checker cannot detect a backdoor inside an otherwise normal-looking official tag. Review remains mandatory. - Do not give the SLM shell, credentials, or unattended merge rights.
The premium agent still sees the repo and the tool results. Local inference is not an air gap. Keep household SSH facts and secrets out of MCP arguments and out of git.
Allowed in git:
- This spec, README, example configs with placeholders.
- MCP server source.
.env.examplewith dummy values.
Never commit:
.env,*.local.json, realOLLAMA_BASE_URLvalues.- Screenshots or logs that show a LAN IP, username, or home hostname.
- Firewall rules that encode a personal subnet unless written as examples.
Use placeholders in docs:
http://<inference-host>:11434
OLLAMA_BASE_URL
192.168.0.0/16 # example private range, not a real home net
.
├── README.md
├── spec.md ← this document
├── .gitignore
├── .env.example
├── examples/
│ ├── cursor.mcp.json
│ ├── vscode.mcp.json
│ ├── claude.mcp.json
│ ├── copilot-cli.mcp.json ← ~/.copilot/mcp-config.json (placeholder path)
│ ├── downstairs-wsl-gpu.md ← WSL GPU host via SSH (placeholders)
│ └── halo-ryzen-ai.md ← future Halo-class AMD host (placeholders)
├── src/
│ └── local_coding_slm/
│ ├── server.py ← stdio MCP server
│ ├── ollama_client.py
│ ├── envfile.py
│ ├── prompts.py
│ └── eval/ ← scorer, corpus, stub Ollama, harness
│ ├── cases.py
│ ├── cases_extended.py
│ └── cases_harder.py ← multi-file behavior-preservation suite
├── docs/
│ ├── roadmap.md ← current vs future phases
│ ├── c4.md ← C4 context / container / component
│ ├── phase3-log.md
│ ├── evaluation-protocol.md ← transport/format/structure/behavior
│ └── a8-a9-operator-checklist.md ← Copilot A8 / Claude A9 clicks (not a pass)
├── scripts/
│ ├── run_mcp.sh ← project MCP entry (loads .env)
│ ├── prove_acceptance.py
│ ├── prove_refactor_acceptance.py
│ ├── prove_multifile_refactor.py ← harder multi-file suite; --live skip-0, --require-live skip-2
│ ├── run_eval.py ← fixture corpus; --live on the workstation
│ ├── run_harness.py ← timed MCP campaign (stub or live)
│ └── check_deployment_safety.py ← defensive bind / tag / git checks
└── tests/
├── test_ollama_client.py
├── test_envfile.py
├── test_safety.py
├── test_eval_extract.py
├── test_eval_score.py
├── test_eval_harder.py
└── test_eval_harness.py
Phase 1 of this repository is the spec, public-safe examples, and a running
local inference host. Phase 2 is the stdio MCP server in src/local_coding_slm.
- Install Ollama on the GPU machine (keep the current OS; no dual-boot).
- Pull the fast model; confirm GPU placement with
ollama ps. - If it is a second host, prefer an SSH local forward to its loopback Ollama. Use a restricted private-interface bind only as a fallback.
curl/api/tagsand/api/chatthrough the configured local URL.- Pull the strong model and repeat one prompt.
- Stop. Do not build routing until the fast model feels usable.
- Implement
src/local_coding_slmas a stdio MCP server. - Wire Cursor with
examples/cursor.mcp.json. - Run the acceptance tests in §16.
- Copy the same server into Copilot and Claude Code configs.
Track, even informally:
- Local tool success rate (accepted vs rewritten by the premium model)
- Latency (fast vs strong)
- Escalation rate to the premium model
- Rough premium-token savings
Dated baseline evidence: docs/local-acceptance-results-2026-09-06.md. It distinguishes mocked unit tests from live Ollama/MCP checks and states the limits of the current single-file refactor case.
Measurement method and committed corpus:
docs/evaluation-protocol.md. Score fixtures in
cloud/CI with scripts/run_eval.py. Repeat live runs on the workstation
with --live and record the first failing layer. Do not treat a successful
retry as pass-at-one.
The local harness (scripts/run_harness.py) measures fast→strong failover
after a task is already delegated. Routing, keep-vs-delegate, and the
premium accept/rewrite/reject apply gate are the orchestrator tests
(tests/test_eval_orchestrate.py, scripts/run_orchestration.py).
--orchestrate runs that gate after real stdio MCP (stub or live worker).
CI uses a scripted stand-in for the main LLM; it does not call Cursor or
another hosted API. GitHub Actions runs the no-GPU path on every push.
Only after that, consider automatic task classification.
Claim this only after Phase 3 has some numbers and the existing second NVIDIA/WSL host is in a known state (on, or explicitly abandoned).
- Treat Halo as another private Ollama host, not a second MCP product.
- Keep
local_*tool names andOLLAMA_*variables. - Prefer
ssh -N -T -o ExitOnForwardFailure=yes -L 127.0.0.1:11436:127.0.0.1:11434 user@<halo-host>to Halo localhost. Optional: private-interface bind plus a workstation-only firewall. No public tunnels. - Re-run A1–A7 and A11–A12 from the workstation. Run A4 (external reachability must fail).
- After the starter pair is usable, benchmark larger official tags on Halo unified memory. Record tok/s, time to first token, and peak memory in a private note — not in git if the note identifies the machine.
Planning index: docs/roadmap.md.
Run from the workstation with OLLAMA_BASE_URL set.
| ID | Check | Pass |
|---|---|---|
| A1 | curl $OLLAMA_BASE_URL/api/tags |
JSON lists qwen3.5:9b and devstral-small-2 (or the configured tags) |
| A2 | Chat prompt to the fast model | Response in a few seconds; ollama ps shows GPU |
| A3 | Chat prompt to the strong model | Completes; GPU or GPU+RAM is acceptable |
| A4 | Workstation reaches Ollama through SSH; an unauthorized LAN client cannot reach 11434 | No direct LAN exposure; skip if same-machine |
| A5 | local_status MCP tool |
Reports both models and the configured base URL host without requiring that URL in git |
| A6 | local_generate_tests with test_add_execute |
Four eval layers pass, including executed test_* functions. A def test substring is not enough |
| A7 | Cursor Agent | Premium model calls a local_* tool on a mechanical prompt |
| A8 | Copilot Agent (VS Code) | Same tool appears and runs. Operator-only; see docs/a8-a9-operator-checklist.md |
| A9 | Claude Code local | claude mcp list shows local-coding-slm connected. Operator-only; same checklist |
| A10 | Vendor-hosted cloud agents | This home-lab profile is not configured there; no private URL in repository MCP settings |
| A11 | git grep for private IPs / usernames |
No RFC1918 addresses except documented placeholders; no @ emails |
| A12 | scripts/check_deployment_safety.py |
Loopback (or SSH-forward) URL, official tags, .env ignored, and no wildcard listener on the workstation; verify a remote host separately |
| A13 | Halo ROCm + ollama ps (Phase 4) |
Accelerated placement on the Halo host; skip until that lab is claimed |
- OpenRouter or any extra paid router.
- Publishing Ollama through ngrok / Cloudflare Tunnel so Cursor can use it as a model provider.
- Dual-booting the inference host to Linux.
- Buying more system RAM solely to run larger SLMs (VRAM upgrade is the lever if 16 GB becomes the bottleneck).
- Automatic multi-model classifiers (phase 3).
- Cloud-agent access to the private GPU in this home-lab profile.