AI Frontier Post
Tutorials

One tiny binary, any model, plain English: hands-on with Shim

Shim (Balanov/shim, MIT, 383 GitHub stars) is a 7.29 MiB static Go binary that wires any OpenAI-compatible or Anthropic model API, MCP servers and Agent Skills into one terminal harness with no daemon, no TUI and no telemetry. This guide installs v0.1.0, runs shim setup, adds a local model and a hosted model, tests both, runs a read-only agent task, connects an MCP server, installs a skill, sets up the local-model command router and permissions, and resumes a session, with the safety limits the project states itself.

TL;DR: Shim is a free MIT-licensed 7.29 MiB terminal harness that lets one small binary talk to local and hosted models, MCP servers and Agent Skills, with API keys kept in environment variables and no telemetry. Install v0.1.0, run shim setup, add one model and test it before any agent run, and stay in the default ask permission mode, because Shim is not a sandbox and shell and MCP actions run with your user permissions.
  • Who: Shim, from GitHub user Balanov (Balanov/shim). The code uses only the Go standard library.
  • What: A tiny, local-first AI harness: one static binary, zero dependencies, no TUI, no daemon, no database and no background process. MIT licence.
  • When: Repository created 2 November 2026; v0.1.0, the initial public release, published 2 November 2026. At research time on 3 November 2026: 383 GitHub stars.
  • Size and speed: 7.29 MiB stripped binary and 9.96 ms median startup for shim --version over 30 runs, measured by the maintainer on a Windows machine; a local measurement, not a cross-platform guarantee.
  • Models: 10 presets: ollama, lmstudio, llamacpp, openai, openrouter, anthropic, gemini, github, huggingface and custom. Keys come from environment variables and are never written to the config. Local weights run through Ollama, LM Studio or llama.cpp; no model weights are bundled.
  • Safety: Default permission mode is ask: reading workspace files is allowed, writes, shell commands and MCP calls need confirmation. Agent runs cap at 16 steps by default. Config is plain JSON; sessions are append-only JSONL.

Why this matters now

Every model provider now ships its own agent interface, and every interface wants its own setup: provider IDs, endpoints, MCP configuration files, skills directories. Shim, released on 2 November 2026 by GitHub user Balanov, is the opposite bet. It is one static Go binary, 7.29 MiB stripped, that adapts between you and all of that: any model exposed through a supported HTTP API, any MCP server, and Agent Skills in the familiar SKILL.md format, driven from a plain terminal with no TUI, no daemon and no telemetry. The maintainer measured 9.96 ms median startup for shim --version over 30 runs on a Windows machine, and is explicit that this is a local measurement, not a cross-platform performance guarantee. The trade-off is stated just as plainly: Shim is a harness, not an inference engine and not a sandbox. It runs no model itself, and the shell commands and MCP servers it calls run with your user permissions.

What follows is the full setup, in the order the project documents it: install, guided setup, one local model and one hosted model, a read-only agent run, MCP tools, skills and the natural-language router, then sessions and configuration. Every command below comes from the project README, its help text, or its source, all checked for v0.1.0.

What you’ll need

A computer running Windows, macOS or Linux; a terminal; and one way to reach a model. That can be a local inference server already running (Ollama, LM Studio or llama.cpp), or a hosted account whose API key you can put in an environment variable. Shim itself needs nothing else: no Node, no Python, no Docker, no database. Go 1.25 or later is only needed if you choose the Go install or build-from-source routes in Step 1. No model weights are bundled or silently downloaded, so a local model means a server you already run.

Step 1: Install v0.1.0 and confirm the binary runs

Download the static binary for your platform from the project Releases page. The v0.1.0 release, published 2 November 2026, lists archives for Windows, macOS and Linux on both amd64 and arm64, plus a checksums file. Extract the archive, put the shim binary on your PATH, and confirm it runs:

shim --version

On PowerShell, use .\shim.exe when running the binary from the current directory. If you prefer to install with Go (1.25 or later), the documented command pins the release:

go install github.com/Balanov/shim/cmd/[email protected]
shim setup

From source, the documented route is:

git clone https://github.com/Balanov/shim.git
cd shim
go test ./...
go build -trimpath -ldflags="-s -w" -o shim ./cmd/shim
./shim setup

Windows also has scripts/build.ps1 and Unix has scripts/build.sh. Whichever route you take, the next step is the same guided setup.

Step 2: Run setup, add a local model and a hosted model, and test both

Run the guided setup. It asks for a language, looks for local models already running, and, if it finds any, offers to select one and to use it for natural-language commands. If no default model exists yet, it then asks for a hosted preset and a model ID. Shim never writes API keys to its config; it reads them from environment variables.

shim setup
shim status
shim doctor

To add every running local server at once, discover first, then discover with --add. The discovery covers Ollama, LM Studio and llama.cpp only:

shim model discover
shim model discover --add
shim model list

shim model list marks the default model and the router model. For a hosted model, set the preset credential variable first, then add the model. The PowerShell example in the README uses OpenRouter; replace the model ID with one you can use:

$env:OPENROUTER_API_KEY = "..."
shim model add openrouter your/model --name main --use
shim model test main

The ten presets are ollama, lmstudio, llamacpp, openai, openrouter, anthropic, gemini, github, huggingface and custom. Their credential variables are: none for the three local presets; OPENAI_API_KEY, OPENROUTER_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, GITHUB_TOKEN and HF_TOKEN for the hosted presets; and optional and configurable for custom. Any OpenAI-compatible endpoint can be added as a custom model:

shim model add custom my-model --name mine --base-url http://127.0.0.1:8000/v1 --use

shim model test sends a tiny test request to the named model, or to the default when no name is given, and reports the result with token counts. Run it before you trust a new model with a real task. If the credential variable is empty, Shim tells you to set it and does not store the key. Switch the default later with shim model use NAME, and remove a model with shim model remove NAME.

Step 3: Run one agent task read-only, then talk to it interactively

Start with a one-shot run that cannot write anything. The --read-only flag disables writes, shell commands and MCP for that run:

shim run --read-only "explain this repository"

Run flags go before the prompt. --model NAME overrides the default model, --max-steps N sets the agent loop limit (the default is 16), and --verbose prints tool names as the agent works. The --yes flag allows writes, shell and MCP without prompts; the README example pairs it with --verbose:

shim run --yes --verbose "fix the tests and verify the result"

Use --yes only on work you can throw away, in a copy of the project. --yes and --read-only are mutually exclusive. For everyday interactive use, just run shim with no arguments. Ordinary text goes to the agent; slash commands manage Shim. Inside chat, /help lists the commands, /models and /use NAME switch models, /discover finds local models, /tools, /status and /doctor inspect the setup, and /exit leaves. shim help from your shell prints the same full command surface.

Abstract illustration of a small robot pausing at a glowing checkpoint gate before a row of file folders, with a shield above the gate
Illustration generated for this tutorial: the default ask mode pauses before writes, shell commands and MCP calls, and asks first.

Permissions deserve a deliberate choice. The default mode is ask: reading, listing and searching workspace files is allowed, while writing or editing files, shell commands and MCP calls require confirmation, and file tools reject paths that escape the workspace by lexical traversal or through symlinks. The three modes are:

shim permissions read-only  # no writes, shell, or MCP
shim permissions ask        # confirm consequential actions
shim permissions auto       # allow the agent to act without prompts

The README advises preferring a per-run flag (--read-only or --yes) over changing the global default. Stay on ask until a workflow has earned auto, and remember the limit the project states itself: Shim is not an OS sandbox, workspace path checks do not contain a malicious process, and untrusted workloads belong in a container, a virtual machine or an OS sandbox.

Step 4: Give it tools with MCP, still in ask mode

MCP servers are added by name, either as a stdio command or as a Streamable HTTP URL. The README example wires up the filesystem server, scoped to the current directory. Note that this particular server runs through npx, so it needs Node even though Shim itself does not:

shim mcp add filesystem npx -y @modelcontextprotocol/server-filesystem .
shim mcp tools filesystem
shim mcp call filesystem list_allowed_directories '{}'

For a Streamable HTTP server, pass the URL instead of a command. The README uses a placeholder address; substitute your own server:

shim mcp add internal https://mcp.example.com/mcp

MCP tools are namespaced as mcp_<server>_<tool> and require confirmation in the default ask mode. List what the agent can see with shim tools, list servers with shim mcp list, and remove one with shim mcp remove filesystem. HTTP headers and stdio environment variables can be added to the JSON config, and values support ${ENV_VAR} expansion, so secrets stay in the environment rather than in files. MCP subprocesses live only for the current Shim process and are closed on exit; there is no background daemon holding your servers open.

Step 5: Add a skill, and let a local model translate plain language

Shim discovers SKILL.md files in four places: the global skills/ directory next to Shim config, .shim/skills/, .agents/skills/ and .claude/skills/. Only skill names and descriptions enter the system prompt; the full instructions load on demand through load_skill, so an unused skill costs almost no context. Install a skill from GitHub, a URL or a local path:

shim skill add owner/repository
shim skill add https://github.com/owner/repository
shim skill add ./local/skill-or-collection
shim skill list
shim skill show name
shim skill remove name

Skills are untrusted instructions, in the README wording. Review a skill before use, especially when it requests shell or network access, and treat model output, repository text, skills and MCP responses as untrusted data. The same caution applies to the natural-language router. Shim has deterministic offline phrases for common English and Russian management commands, so typing show my tools proposes the /tools command, and a mutating proposal still asks for confirmation before it runs. For other languages, assign a local model as the router:

shim router use ollama/qwen3:4b

That is the README example; use the name shim model list shows for your local model. The router sees non-command chat text so it can tell a configuration request from an agent task, and its output is parsed, checked against an allowlist and confirmed when mutating. It never generates arbitrary shell commands. Turn it off with shim router off. Use a loopback model if you want the routing step to stay on your machine, and remember that your primary model handles ordinary conversation in whatever language it supports.

Abstract illustration of a small laptop with a glowing cube translating a speech bubble into a short row of command chips, with a plug, a wrench and a document nearby
Illustration generated for this tutorial: a local router model turns plain-language management requests into allowlisted commands, and skills load only when called.

Step 6: Resume a session, and find where everything lives

Sessions are append-only JSONL files next to the config, so a crashed run does not lose its transcript. List and start sessions, and resume one by ID:

shim session list
shim session new
shim run --resume SESSION_ID "continue"

Replace SESSION_ID with the ID from shim session list. Configuration lives at the operating system user-config location, in shim/config.json; override it with the SHIM_CONFIG environment variable or the --config flag, and inspect it with:

shim config path
shim config show
shim status
shim doctor

Global flags, listed by shim help, are --config PATH, --workspace PATH, --lang en|ru and --version. Network traffic happens only for explicit model calls, skill downloads, local discovery, and the MCP servers you configure. Commands, config, skills, sessions and deterministic routing all work offline.

What you built, and where it stops

You now have a terminal harness that starts in about 10 ms, talks to local and hosted models through one small command surface, reaches MCP tools under confirmation, loads skills only when needed, and keeps its state in plain JSON and JSONL files you can read, copy and fork. The project is explicit about what it deliberately is not: no TUI or server surface, no daemon, no desktop app, and no attempt to beat foundation-model vendors at their own agent interface. Its different bet is a smaller local-first control plane that is easy to wire together and easy to fork, using only the Go standard library.

The limits are part of the design and worth repeating before you rely on it. Shim runs no model itself, so without a local server or a hosted API key there is nothing to talk to. It is not a sandbox: shell and MCP actions run as your user, and the stated roadmap items, streaming output and cancellation polish, OS-level sandbox profiles, OAuth helpers for remote MCP, context compaction and richer session export, and additional deterministic language packs, are not built yet. It is a 0.x project with a single public release, v0.1.0, and its config carries an explicit version so future changes can migrate rather than silently drop your settings. For a personal terminal harness you control end to end, that is the point. For untrusted code, untrusted skills or untrusted MCP servers, put a container or a virtual machine between Shim and anything you care about, and keep the permission mode on ask.

Frequently asked questions

Does Shim run AI models itself?

No. Shim is the harness, not an inference engine. It talks to models through supported HTTP APIs, and local weights run through Ollama, LM Studio, llama.cpp or another compatible inference server. No model weights are bundled or silently downloaded.

Where do my API keys go?

In environment variables, never in Shim config. Each hosted preset names its credential variable, such as OPENAI_API_KEY or OPENROUTER_API_KEY, and Shim reports when the variable is empty instead of storing the key.

Is it safe to use --yes or auto mode?

Only on work you can throw away. Those modes allow writes, shell commands and MCP calls without prompts, and they run with your user permissions. Shim is not an OS sandbox, so prefer the default ask mode, or a per-run --read-only flag, and use a container or virtual machine for untrusted workloads.