# ModelSpec agent guide

Guide version: 1-31f80fb67e121e42.
Canonical URL: https://modelspec.dev/agents.md
Full API reference: https://modelspec.dev/openapi.yaml
Coverage: https://modelspec.dev/api/coverage.json

ModelSpec is an analysis of alternatives for AI models: it decides which models fit a job from your requirements, sourced benchmarks and real cost, and shows its work.

## CLI, MCP or HTTP

Three ways in: keyed CLI, MCP and HTTP API. Machine data access needs a ModelSpec API key; only human lookup is free. No data download or local decision cache. CLI: `uvx --from modelspec-dev modelspec` (or `pipx install modelspec-dev`); `pip install modelspec` is an unrelated project. `modelspec help agent --json` orients; `modelspec key` explains access. No telemetry. The CLI never uses, sends or logs your provider API keys.

## First call

Call decide early with a template-based Spec; refine from reading and recovery hints. Put Musts in where: these gates exclude. Put Prefers in optimize.weights: weights rank and never exclude. Unknown values go to may_qualify. Pin snapshot for reproducibility.
Not a router: call it once per job or role, not per request, then route among the models it recommends.

HTTP: POST `https://api.modelspec.dev/v1/decide` with `Content-Type: application/json`, a real
`User-Agent`, and `Authorization: Bearer <key>`. The JSON body is the Spec itself.
MCP: call `decide` with the Spec itself as arguments, with Authorization on the
MCP connection. Do not wrap it in `spec` or `task`. Buy a plan or pack at https://modelspec.dev/pricing/. Stripe hosts Checkout; claim your key at the success link.

Minimal valid Spec, minimizing cost without claiming a quality ranking:

```json
{"spec_version":1,"optimize":{"min":"offering.cost_per_task"}}
```

`spec_version` is 1. `snapshot` defaults to latest; save the returned snapshot
and decision_id, and set snapshot to that exact ID for a reproducible retry.
Over HTTP, `explain` defaults to summary and returns full rows; full includes
every eliminated candidate and can be large. A Spec has one optimize objective: min, max, or weights.
A negative numeric weight key prefers less, for example `-offering.cost_per_task`.
Unset, cost_per_task assumes 40,000 input and 4,000 output tokens, so a small task's cost cap can exclude every model.
For boolean or enum preferences use a prefer value and weight, never a bare number.
`where` is an array. In text expressions a set is `{a, b}`, not `[a, b]`.
Structured example: `{"facet":"offering.provider","in":["openai"]}`.
`task` free text is rejected. Translate only requirements you can represent.

## Bounded answers and drill-down

MCP decide returns a bounded answer of at most 16 KB, summary included. Drill down with evidence_for. Complete rows need fields:null; explanation.fetch names what was removed and how to fetch it.

Answer, status, warnings, coverage and the top result stay. An omission is not an elimination. At `explain: none`, a missing eliminated count means eliminations were not computed. When `explanation.omitted` is set, `explanation.fetch` tells you to resend with `evidence_for` for one model, narrow `fields`, lower `limit`, or POST /v1/decide without `fields` for the complete Decision. HTTP without those controls still returns the complete Decision.

## Worked Specs

These are starting points from the registry templates, not recommendations of a
model. Keep only the gates and weights the user stated; do not silently adopt
or drop a gate or invent numeric tradeoffs. If no objective was stated,
disclose the minimal Spec's cost objective as a discovery default, not a quality
recommendation. Capabilities and weights express broad evidence, not measured
quality on the user's exact prompt. Replace the example hardware ID with the user's SKU.

### Use case

Template: `writing-balanced`.

```json
{
  "spec_version": 1,
  "where": [
    "model.class = text-generator",
    "model.lifecycle = active"
  ],
  "optimize": {
    "weights": {
      "writing": 0.7,
      "-offering.cost_per_task": 0.3
    }
  }
}
```

### Budget and policy

Template: `regulated-budget + budget-coding`.

```json
{
  "spec_version": 1,
  "where": [
    "offering.data.trains_on_customer_data = false",
    "offering.data.zero_retention = true",
    "offering.attestation.baa = true",
    "offering.cost_per_task <= 0.05"
  ],
  "optimize": {
    "weights": {
      "-offering.cost_per_task": 0.7,
      "chat_preference": 0.3
    }
  }
}
```

### Own hardware

Template: `private-self-host`.

```json
{
  "spec_version": 1,
  "where": [
    "model.class = text-generator",
    "model.weights_openness = open_weights",
    "licence.commercial_use in {permitted, permitted_with_conditions}",
    {
      "facet": "model.fits_hardware",
      "in": [
        "nvidia_rtx_4090"
      ]
    }
  ],
  "optimize": {
    "weights": {
      "chat_preference": 0.7,
      "-offering.cost_per_task": 0.3
    }
  },
  "access": "own_hardware",
  "estate": {
    "devices": [
      "nvidia_rtx_4090"
    ]
  }
}
```

### Coding

Template: `coding-balanced`.

```json
{
  "spec_version": 1,
  "where": [
    "model.class = text-generator",
    "model.lifecycle = active",
    "model.context_window >= 200000"
  ],
  "optimize": {
    "weights": {
      "software_engineering": 0.7,
      "-offering.cost_per_task": 0.3
    }
  }
}
```

## Refine and report

Never add a gate or constraint the user didn't state. Relax only constraints the user stated, one at a time, and say what was relaxed and why. Prefer the answer's own `relax` suggestions. A tie stays a tie. Don't promote a member by knowledge from outside the response. Cite only evidence in the response. Never add benchmark figures from memory. Tied or partial answer: report the tie and caveats, pick no member; a null answer names no pick and says what's missing. A null cost is unknown. Never show it as $0.00 or "free". One vocabulary lookup per unknown facet, then decide. Read `next` before another call. Size `task_tokens` from the user's own task, and say what you assumed. Don't copy the guide's example sizes. Template weights are examples, not requirements. If the evidence basis or board does not match the task, say so: Arena web-dev evidence does not establish chat quality. p_best is the probability of ranking best under the Spec and evidence uncertainty, not the probability of matching the user's task or satisfying its requirements. Read reading when present. Present answer.kind=tied among all answer.members, including with_estate; tie-breakers are conditional. State requirements in reading.not_applied or error.issues that were not applied, including requirements removed after a rejected call. A retry does not verify them. Report estimates as estimates: fits_hardware membership is not measured fit for a specific quantization and context workload. Follow reading.do_not_claim. Never claim a rank the response does not show. Present `summary_for_user` to the user unchanged and keep every `must_mention` item. When `next_move` is present, offer it instead of a pick.

If `reading.omitted` reports truncation, use the complete answer.members and issues
lists. These rules also apply when reading is absent: inspect answer, issues, and
result evidence directly. `may_qualify` means a required fact is unknown, not a pass.
`status=partial` is incomplete evidence. `no_match` is not a winner. No single leader
is an answer: report the tie. Do not fall back to `rank`. An undetermined policy is
not permission.

On refusal read error.issues and recovery hints, including their paths and valid
examples. Correct the specified fields and retry. Tell the user any requirement
you removed; a successful retry does not prove that removed requirement.
Use `vocab` with `{"section":"starter"}` for a compact first lookup, then search
or id/ids for the missing term. HTTP lookup:
`GET https://api.modelspec.dev/v1/vocabulary?section=starter` where available.
The starter response includes a ready-to-send minimal Spec and a next hint.
If a requirement is unsupported, disclose that limit instead of inventing a facet.

## Per-call token budget

These are planning allowances for agent context, not server response caps.
Token accounting uses cl100k_base when available, otherwise characters / 4.
JSON schemas also consume tokens; each tool definition can exceed its description.

| Item | Planning allowance | Credits | Use |
| --- | ---: | ---: | --- |
| Guide, stored once | <= 4,000 tokens | 0 | Load once per guide version |
| MCP initialize instructions | <= 1,000 tokens | 0 | Pointer, version, five rules |
| Each MCP description | <= 1,500 tokens | 0 | Schemas counted separately |
| Spec arguments | 1,000 tokens | 0 | Start with an example above |
| vocab starter/search | 2,000 tokens for reading | 0 | Compact, at most 20 rows per page |
| MCP decide, bounded default | <= 3,000 tokens | 1 | explain none, limit 10, compact rows |
| decide drill-down (evidence_for) | <= 2,000 tokens | 1 at explain none | One model's evidence |
| decide summary | Reserve 16,000 tokens for reading | 1 | HTTP default; size depends on candidates |
| decide full | Can exceed 160,000 tokens | 2 | Only when eliminated-candidate detail is needed |
| rank / policy_check | Size depends on rows | 1 / 5 | Deprecated ranking, not a decide fallback / policy checks |
| model_info / list_use_cases | Size depends on card/profiles | 0 | One card / legacy profiles |
| feedback | 500 tokens for reading | 0 | After acting, never include secrets or a prompt |

If a response exceeds the client context budget, retain the original outside the prompt and select answer, reading, recovery/error.issues and relevant rows for reporting. Do not treat a truncated list as complete. These token allowances are separate from ModelSpec credits and the agent vendor's token billing.

## Store once

Store this guide locally, then include its instructions in the agent's context.
A link alone does not guarantee any client fetches it. Refresh when
`x-modelspec-guide-version` changes. Every /v1/ response links here with
`Link: <https://modelspec.dev/agents.md>; rel="describedby"`. MCP initialize includes instructions
and the guide version. Local storage does not guarantee vendor prompt caching.
Do not store an API key in these files.

Claude Code, project CLAUDE.md, or the body of a manually loaded skill:

```sh
mkdir -p .agents
curl -fsS https://modelspec.dev/agents.md -o .agents/modelspec.md
cat >> CLAUDE.md <<'EOF'
When choosing models, read .agents/modelspec.md. Call decide early with a
template-based Spec; refine from reading and recovery hints.
EOF
```

Codex, project AGENTS.md:

```sh
mkdir -p .agents
curl -fsS https://modelspec.dev/agents.md -o .agents/modelspec.md
cat >> AGENTS.md <<'EOF'
When choosing models, read .agents/modelspec.md. Call decide early with a
template-based Spec; refine from reading and recovery hints.
EOF
```

Cursor, project rule file:

```sh
mkdir -p .agents .cursor/rules
curl -fsS https://modelspec.dev/agents.md -o .agents/modelspec.md
cat > .cursor/rules/modelspec.mdc <<'EOF'
---
description: ModelSpec model selection
alwaysApply: true
---
When choosing models, read .agents/modelspec.md. Call decide early with a
template-based Spec; refine from reading and recovery hints.
EOF
```

Plain MCP clients:

```sh
mkdir -p .agents
curl -fsS https://modelspec.dev/agents.md -o .agents/modelspec.md
```

The client must read initialize.result.instructions and inject that text plus
.agents/modelspec.md into its agent context once per version, alongside tools/list.
Keep the guide outside individual tool results. Reuse it for subsequent calls;
refresh if a new initialize advertises a different version. The MCP server cannot
write client files or force a client to honor instructions.
