cd /news/ai-tools/set-up-an-llm-gateway-by-asking-your… Β· home β€Ί topics β€Ί ai-tools β€Ί article
[ARTICLE Β· art-164016] src=blog.ai-process.dev β†— pub= topic=ai-tools verified=true sentiment=↑ positive

Set up an LLM gateway by asking your coding agent: the Keystone plugin for Claude Code

Keystone shipped a Claude Code plugin that installs with one command and provides eight skills plus two MCP servers for deploying and integrating its LLM gateway, with the /ktms:assess skill reading a repository to map every LLM call site by file and line before any change is made. On a three-service demo project, /ktms:assess found all five call sites, flagged that every service loads all three vendor keys through env_file, that temperature=0.3 in draft_reply is ignored at /v1, and that rerank.ts does not rerank at all, while identifying the nightly worker calling Gemini about 40,000 times a night as the largest cost and risk. The plugin's version tracks Keystone's release, and the skills call the ktms CLI, which must be on PATH from a release archive or via go install github.com/ai-process/ktms/cmd/ktms@latest.

read6 min views1 publishedNov 4, 2026

One command installs the Keystone plugin. Then /ktms:assess reads your repository and says where the gateway goes, /ktms:deploy writes the deployment and /ktms:integrate moves a service over, without a single key passing through the chat.

Putting a gateway in front of your LLM calls is not hard, but it touches a lot of places: a Compose file or a Helm release, secrets, a client module in every service, a model value at every call site, limits and routing rules. Ask a general-purpose coding agent to do it and it will happily invent environment variables that don’t exist.

So Keystone ships a Claude Code plugin: eight skills that know the real commands, flags and config fields, and two MCP servers that let the agent read what is running. This post walks through it on a small demo project.

1. Install #

claude plugin marketplace add ai-process/ktms && claude plugin install ktms@ai-process
βœ” Successfully added marketplace: ai-process (declared in user settings)
Installing plugin "ktms@ai-process"...βœ” Successfully installed plugin: ktms@ai-process (scope: user)

The plugin’s version is Keystone’s release. The skills call the ktms CLI, so put it on PATH from a release archive or with go install github.com/ai-process/ktms/cmd/ktms@latest.

Skill For
/ktms:assess Where Keystone fits the system you run in, and the plan to get there
/ktms:deploy Compose or Helm for the stack, and the first-start runbook
/ktms:integrate Moving a service’s LLM calls onto Keystone
/ktms:configure Routing, keys and limits as a ktms config file, planned and applied
/ktms:migrate Moving from LiteLLM or vendor SDKs
/ktms:node A local GPU node with cloud fallback
/ktms:usage-reports Spend questions, reports and alerts from light-tokenmeter
/ktms:production-review Checking a deployment before and after it goes live

You don’t have to remember the names: the agent picks a skill when the task fits, and the slash command starts one directly.

2. Assess: where does the gateway go? #

The demo project has three services: a support bot on the OpenAI SDK with model="gpt-4o" hardcoded twice, a search API on the Anthropic SDK plus OpenAI embeddings, and a nightly worker that calls Gemini about 40,000 times a night. Postgres, Redis and Prometheus are already in the Compose file.

/ktms:assess changes nothing. It reads the code and the deployment, lists every LLM call site with file and line, and reports where Keystone would run, what each service changes, what it brings and costs here, and which skill to run next. It also says plainly when Keystone is not worth it, for example for one service with one model and no limits to keep.

The answer is a report of about a hundred lines. It found all five call sites with file and line, and a few things nobody asked about:

Every service loads it through env_file, so each one gets all three vendor keys.

temperature=0.3 in draft_reply:/v1 ignores it, so check reply quality after the switch.

It also noticed that rerank.ts doesn’t rerank at all, only explains results, and that the nightly worker is where most of the money and risk is. This is where it puts the gateway:

  Compose project "demo-shop" (one network, nothing exposed to the host)
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  support-bot ─┐                                                 β”‚
  β”‚  search-api ──┼── HTTP :8080 /v1 ──▢  ktms ──▢ OpenAI           β”‚
  β”‚  digest-workerβ”˜   (gRPC :9090 later        β”œβ”€β–Ά Anthropic        β”‚
  β”‚                    for batch)              └─▢ Gemini           β”‚
  β”‚                                  β”‚  β”‚  β”‚                        β”‚
  β”‚      postgres:16 ◀── db "ktms" β”€β”€β”˜  β”‚  └── :9091 ◀── prometheus β”‚
  β”‚      redis:7 ◀──── limits β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                           β”‚
  β”‚      light-tokenmeter ◀── usage events (spend per action_id)    β”‚
  β”‚      ktms-admin  (internal only; reach it via SSH tunnel/VPN)   β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The verdict comes with a condition:

Verdict: worth it. If the system were only support-bot on gpt-4o, it wouldn’t be.

The plan that follows starts with the nightly worker, because it has one call site, isn’t user-facing and a failed night can simply be re-run.

3. Deploy and configure, with your keys out of the chat #

/ktms:deploy asks first: Compose on one host or Kubernetes with Helm, managed or in-cluster Postgres and Redis, where secrets live, hostnames, monitoring. Then it writes the files with every secret as a reference to your store, probes on /readyz, and a first-start runbook.

The runbook has one step the agent leaves to you on purpose: ktms init in your own terminal. It asks for the admin key and the vendor keys without echo, so they never reach the chat, writes ktms.yaml with default models and rules, shows the plan and applies it.

From then on /ktms:configure works on that file, like Terraform: edit, ktms config plan, show the plan, apply only after you agree. The guard rails are in the tools, not in the prompt:

  • The ktms MCP server isread-only by default . Write tools exist only if you add--allow-write to its arguments.
  • config_plan returns aplan_id ;config_apply applies exactly that plan, once, and refuses if Keystone changed in between.
  • A vendor key in a plan is read from the MCP server’s environment, and only from variables named KTMS_VENDOR_KEY_* , so the agent cannot point a config at the admin key or another secret.
  • Names and values that come back from Keystone are marked as data, not instructions.

4. Integrate one service #

/ktms:integrate starts by listing call sites and changing nothing. Then it puts the base URL, key, timeouts, retries and attribution in one small client module, and replaces each hardcoded model with an effort, with a one-line reason per call site: low for classification and short answers, medium for ordinary generation, high for reasoning and long documents.

It also turns off the SDK’s own retries and applies Keystone’s error contract once (wait out Retry-After on 429, back off on 503, never retry a configuration error), and flags what doesn’t map: embeddings keep an exact model id, and temperature, top_p, stop and seed are ignored on Keystone’s OpenAI-compatible API.

The check at the end uses real routing, not a guess:

$ ktms route explain billing --effort low
rule: default   effort: low   route mode: direct
1  gpt-4o-mini       openai  kept
2  gemini-2.5-flash  google  dropped: no_vendor_key
3  gpt-4o            openai  dropped: effort
tries: gpt-4o-mini

That output is from Keystone 1.3.6: a chain that ends in a kept model is fine; one that ends in no_vendor_key means a key is missing before the service ever goes live.

5. Why the skills don’t drift #

A skill that names a flag removed two releases ago sends the agent into an error, and the agent will then improvise. So Keystone’s CI reads every SKILL.md and fails when one names a ktms command, a flag, an MCP tool or a README section that doesn’t exist in that version.

Other agents #

The skills are plain Agent Skills, a folder with a SKILL.md: copy plugin/skills/* into the skills directory of Codex, Cursor or another agent that reads them, and add the two MCP servers to its config. Assistants without skills or MCP can use the same instructions as prompts.

── more in #ai-tools 4 stories Β· sorted by recency
── more on @keystone 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/set-up-an-llm-gatewa…] indexed:0 read:6min 2026-11-04 Β· β€”