AI & LLMs
How to hand the API to a model: drop-in packs for the coding agents, the workflow and rules they teach, a tool layer to generate, and the plain-text files a crawler or an agent reads first. Everything below is read from those packs and the spec, so what the page says is what an agent is told.
- 5Agent packs
- 12Rules in every pack
- 6Recommended tools
- 133Operations in the spec
Packs for coding agents
One file per agent, derived from the same OpenAPI document and pointing the agent back to it for the full surface. Copy it in and the agent knows the base URL, the headers, the envelope, the errors, pagination, caching, webhooks, the endpoints that matter, and where in these docs to look for the rest.
Claude Code
OpenQuery the GunSpec.io firearms REST API. Use when a task involves fetching firearms, ammunition, calibers, manufacturers, countries, or game-balance stats from GunSpec, or building an integration against api.gunspec.io. Covers authentication, the response envelope, pagination, error handling, and the official TypeScript and Python SDKs.
Goes in.claude/skills/gunspec-api/SKILL.mdmkdir -p .claude/skills/gunspec-apicurl -s https://assets.gunspec.io/ai/claude-code/skills/gunspec-api/SKILL.md \ -o .claude/skills/gunspec-api/SKILL.mdGitHub Copilot
OpenHow to consume the GunSpec.io firearms REST API (auth, envelope, errors, pagination, SDKs).
Goes in.github/instructions/gunspec-api.instructions.mdmkdir -p .github/instructionscurl -s \ https://assets.gunspec.io/ai/github-copilot/instructions/gunspec-api.instructions.md \ -o .github/instructions/gunspec-api.instructions.mdOpenAI Codex
OpenGuidance for consuming the GunSpec.io REST API: a firearms specification database (firearms, ammunition, calibers, manufacturers, countries, and game-balance stats). Merge this section into your project
AGENTS.md(repo root) or~/.codex/AGENTS.mdfor all projects.Goes inAGENTS.mdcurl -s https://assets.gunspec.io/ai/codex/AGENTS.md >> AGENTS.mdCursor
OpenHow to consume the GunSpec.io firearms REST API (auth, envelope, errors, pagination, SDKs).
Goes in.cursor/rules/gunspec-api.mdcmkdir -p .cursor/rulescurl -s https://assets.gunspec.io/ai/cursor/rules/gunspec-api.mdc \ -o .cursor/rules/gunspec-api.mdcAny agent
OpenDrop-in context that teaches an AI coding agent to consume the GunSpec.io REST API correctly. GunSpec is a firearms specification database: 9,000+ firearms with dimensions, ballistics, materials, and game-balance stats, plus ammunition, calibers, manufacturers, countries, conflicts, and aggregate statistics.
Goes inyour system prompt or rules filecurl -s https://assets.gunspec.io/ai/gunspec-api.md
Every pack deep-links 10 pages of these docs for detail: field-reference, errors, rate-limits, caching, endpoint-access, pagination, versioning, tools, ai, media.
The workflow an agent follows
The order of operations every pack and llms.txt teach, as written there. It exists because the expensive mistakes are the same ones each time: a guessed slug, a picked-for-you variant, an invented specification.
- 1
Resolve the name to an id first.
GET /v1/firearms/resolve?q={what the user said}. Pass their words through unchanged - it is built for "G19 gen 5 MOS", "AK-47", "H&K MP5". Never guess a slug:glock-19-gen5,glock-g19-gen5andglock-19are not interchangeable, and a guess returns 404 or a record for a different variant. This is not search -/v1/firearms/searchranks a page of records about a query and has no notion of certainty; resolve answers which record a query is. - 2
status: "ambiguous"means ask the user, not pick the first.firearmIdis null andalternativesholds the tied candidates with their names. "Glock 19" does not name a generation, and the generations differ in exactly the weights and dimensions people ask about.status: "not_found"is a 200, not a 404 - say we hold no such record. Anymatch: "fuzzy"candidate is a suggestion, always scores 0, and is never an answer. ReadunresolvedTokensbefore replying: those are words the resolver could not place, usually a variant the catalog does not hold.POST /v1/firearms/resolveresolves up to 50 names in one request (Studio). - 3
GET /v1/firearms/{id}for the full record.GET /v1/firearms/{id}/variantsfor the rest of the family. - 4
Never infer a missing specification.
nullmeans GunSpec does not hold the value. Do not fill it from model knowledge, from a sibling variant, or by computing it. Say it is not in the database. - 5
Units are in the field names
weightEmptyGgrams,barrelLengthMmandoverallLengthMmmillimetres,muzzleVelocityMpsm/s. Read the suffix and state the unit./v1/firearms/{id}/dimensionsgives metric and imperial. - 6
dataConfidenceis not accuracy.It is a 0-1 record-level score set from what was actually sourced and never raised by hand, not a per-field probability that a number is correct: 0.95 does not mean the barrel length is 95% likely to be right. 0.5 with
verifiedAtnull is seed model knowledge; treat anything below 0.7 as unverified. Use it to rank and triage; followsources(or theprovenanceobject on a detail record) to verify a specific figure. Bands and source order: https://docs.gunspec.io/en/field-reference#data-confidence - 7
Specifications legitimately differ by production year, factory, batch and regional variant. Where a record notes disagreement between sources, surface the disagreement rather than presenting one number as settled.
- 8
Cache what you fetch, and refresh conditionally.
Store the
ETagyou were given and send it back asIf-None-Match; a record that has not changed answers 304 with no body and no charge against your daily allowance. Compareversion(stable across plans) rather thanETagwhen reconciling mirrors held under different keys.
The first step has its own endpoints: GET /v1/firearms/resolve Resolve a name to one firearm · POST /v1/firearms/resolve Resolve many names at once
Rules for the agent
The 12 rules at the end of the brief, verbatim. A pack that omits any of them is not one of ours.
- 01Read the key from the environment. Never hardcode or print it.
- 02Prefer the official SDK. Fall back to fetch with the
X-API-Keyheader. - 03Check
successbefore readingdata. On failure, surfaceerror.codeandrequest_id. - 04Handle 429 with
Retry-Afterbackoff. - 05When unsure about a field or endpoint, fetch
https://api.gunspec.io/openapi.json. Do not guess. - 06Resolve a name to an id with
/firearms/searchbefore fetching a record. Never guess a slug. - 07When search returns several variants, ask the user which one. Do not pick for them.
- 08Never invent a missing specification.
nullmeans GunSpec does not hold it, so say so. - 09Report units from the field-name suffix (
...Mm,...G,...Mps). Never assume one. - 10Treat
dataConfidenceas record completeness and provenance, not per-field accuracy. Citesourcesfor a specific figure. - 11Cache what you fetch, and refresh with
If-None-Match. Re-downloading a record whoseversionhas not changed spends quota to learn nothing. - 12If you keep a mirror, subscribe to webhooks rather than polling for changes, and dedupe on
X-Webhook-Id.
Tool calling
Do not hand a model the raw API and the docs. Give it a small, named tool per job and let the workflow above drive the order. The layer the packs recommend, each mapped to the operation it calls; generate the schemas from the OpenAPI document rather than writing them by hand, so parameters and response types stay in step.
| Tool | Calls | Note |
|---|---|---|
search_firearms(query) | GET /v1/firearms/search | - |
get_firearm(id) | GET /v1/firearms/{id} | - |
list_variants(id) | GET /v1/firearms/{id}/variants | - |
compare_firearms(ids) | GET /v1/firearms/compare | max 5 ids |
get_manufacturer(id) | GET /v1/manufacturers/{id} | - |
get_caliber(id) | GET /v1/calibers/{id} | - |
Anthropic tool calling
import Anthropic from '@anthropic-ai/sdk' const client = new Anthropic() // One tool the model can call. Generate the rest from the OpenAPI document// rather than writing them by hand, so the parameters stay in step.const tools = [{ name: 'search_firearms', description: 'Search the GunSpec firearm database by free-text query.', input_schema: { type: 'object', properties: { q: { type: 'string', description: 'Search terms, e.g. "9mm service pistol"' }, }, required: ['q'], },}] // The HTTP call you run when the model asks for the tool.async function searchFirearms(q) { const res = await fetch( `https://api.gunspec.io/v1/firearms/search?q=${encodeURIComponent(q)}`, { headers: { 'X-API-Key': process.env.GUNSPEC_API_KEY } }, ) const { data } = await res.json() return data // hand straight back to the model as the tool result} const message = await client.messages.create({ model: 'claude-sonnet-4-6', max_tokens: 1024, tools, messages: [{ role: 'user', content: 'Compare the recoil of a Glock 17 and a SIG P320.' }],})Inside a tool, prefer the SDKs (@buun_group/gunspec-sdk, gunspec) over raw fetch: they carry the auth, the envelope, auto-paging and retries, so the tool body is one call.
Machine-readable files
Three plain-text URLs an agent can fetch at runtime or a crawler can index, each served with open CORS. llms.txt is the index; llms-full.txt is the whole brief; the OpenAPI document is the source of truth both point at.
docs.gunspec.io/llms.txtThe index: 218 lines, 15 KB. Quick facts, the envelope, pagination, versioning, the agent workflow and every endpoint in one screen.docs.gunspec.io/llms-full.txtThe full brief the packs are cut from, 21 KB. Redirects to the format-agnostic pack.api.gunspec.io/openapi.jsonThe OpenAPI 3 document, 133 operations. Generate tools, clients and tests from it.
Sections of the index:
- 01Quick facts
- 02Response shape
- 03Pagination
- 04Versioning
- 05Agent workflow (read this before calling anything)
- 06Endpoints
- 07SDKs
- 08MCP server
- 09For AI agents
Why it is easy to consume
The contract is uniform, which keeps a prompt short and a parser small. The figures are from the spec.
- One envelope on every JSON response across 133 operations:
{ success, data }, with apaginationblock on lists. The few operations that return bytes or a redirect, namely the bullet diagram, raw media and model downloads and the seller click tracker, are listed on the media page. - One error shape with a stable
codeand one of 39 specificreasons, so a model can branch without reading prose. Every code and reason. - One header for auth (
X-API-KeyorAuthorization: Bearer); 17 operations need none. Authentication. - Units live in the field names (Mps (m/s), Mm (mm), Rpm (rpm), G (g), J (J), N (N), M (m)), so a value can never be reported in the wrong unit. Field reference.
- Every catalog record carries
updatedAt,versionand anETag, so an agent can cache and refresh conditionally instead of re-reading. Caching. - Changes are pushed as signed webhooks, and the contract is versioned: additive changes land, breaking ones move to a new prefix.
Model Context Protocol
GunSpec runs a hosted MCP server at https://mcp.gunspec.io. Point any MCP client at it with your API key to give an assistant the tool layer above, under your plan.