Getting started
tungsten compiles an OpenAPI 3.0 or 3.1 description into TypeScript, Python and Rust SDKs, a command-line client, an MCP server and LLM-oriented docs, all built for autonomous callers.
Why tungsten
Most SDK generators optimize for a person reading autocomplete. An agent has a finite context window, no memory between retries, and no sense of what is irreversible. tungsten changes the design goal of each stage:
- Diagnostic error envelopes. Every failure, from argument validation to a transport error to an HTTP status, is one closed JSON shape with a category, the failing parameter, the received value, what was expected, a remediation sentence and an explicit
retryableverdict. The same 14 members in every runtime, ready to paste into a model's context. - Safety tiers. Each operation is
read_only,mutating,destructiveorirreversible, defaulted from the HTTP method and refined inagent.yml. Tiers drive retries, MCP tool annotations and confirmation rules. - Previews and confirmation tokens. A destructive or irreversible call must be previewed first. The preview returns the request that would be sent and its effects, plus a confirmation token that is valid only for identical arguments. The generated CLI replaces tokens with
--yes(and--i-understandfor irreversible operations). - Idempotency and unknown outcomes. Mutations are retried only with an idempotency key (or an identity body), always with the same key. A timeout or reset after a write is reported as
OUTCOME_UNKNOWNwith the call to verify it, not as a failure to retry blindly. - Verification. An operation can name a read to run after success and the state to expect, attached to the result.
- MCP progressive disclosure. Past a tool-count threshold the server lists a few meta-tools (
search_tools,describe_tool,invoke,preview,list_clusters) instead of every operation. In the maintainers' measurement on the ZROtext pilot (October 2026, BPE token counts), the progressive listing of its 41 tools costs 765 tokens, against 9,473 for the 14 tools of that project's hand-written MCP server. - Deterministic output. The same inputs produce byte-identical files. Generated files record the generator version and the digest of their inputs, and
tungsten generate --checkfails when output is stale.
Targets
| Target | Emits | Runtime it depends on |
|---|---|---|
typescript | Typed SDK, Result values, Zod validation | @elysra/tungsten-runtime |
python | Sync and async SDK, strict Pydantic v2 models | tungsten-runtime (PyPI) |
rust | Async SDK (serde, reqwest) and, optionally, a generated CLI | tungsten-runtime and tungsten-cli-kit (crates) |
mcp | MCP server over stdio or Streamable HTTP, discrete or progressive | @elysra/tungsten-mcp |
webmcp | Browser ESM package with a tool manifest and explicit registration helper | @elysra/tungsten-webmcp and the generated TypeScript SDK |
docs | llms.txt, llms-full.txt, tools.json, README | none |
go (external) | Go SDK package (models, resources, typed client, pagination and stream helpers), generated by tungsten-emit-go from emitters/go | github.com/pboachie/tungsten/runtimes/go |
Targets and the mock server (tungsten mock) share one IR, so they agree on operations, types and rules. The three SDK runtimes implement identical semantics: as of this writing a shared contract suite of 58 scenarios (kept in the maintainers' repository) passes unchanged through the TypeScript, Python and Rust drivers.
Install
Prebuilt binaries for Linux, macOS and Windows are attached to each GitHub release. The installer checks the archive against the release's SHA256SUMS before it installs anything:
curl -fsSL https://raw.githubusercontent.com/pboachie/tungsten/main/install.sh | sh # Linux and macOS, into ~/.local/bin
irm https://raw.githubusercontent.com/pboachie/tungsten/main/install.ps1 | iex # Windows
Pass --version v0.1.0 to install.sh (or -Version v0.1.0 to install.ps1) to pin a release, and --dir (-InstallDir) to choose the target directory.
Or build from source, with Rust 1.95 or newer (the repository pins 1.97.0 in rust-toolchain.toml for development):
git clone https://github.com/pboachie/tungsten
cd tungsten
cargo install --path crates/tungsten-cli # installs the `tungsten` binary
tungsten doctor # reports optional tools (node, python3, uv, formatters)
Node, Python and the formatters are optional: a missing tool only disables the target or --format option that needs it.
Quickstart
tungsten init --from openapi.json # writes tungsten.yml and agent.yml
init writes no targets, so generate has nothing to do until you add some. Edit tungsten.yml to add targets, and agent.yml to review every operation with side effects (init lists them as TODO):
# tungsten.yml
targets:
typescript: { package: "@acme/petstore", out: generated/typescript }
python: { package: acme-petstore, module: acme_petstore, out: generated/python }
docs: { out: generated/docs }
tungsten check # validate specs and manifests
tungsten generate # write every configured target
tungsten mock . --port 8099 # serve the API from the IR, no backend needed
Then call the generated SDK. Python (the TypeScript client is symmetrical: const res = await client.pets.list({ limit: 10 })):
from acme_petstore import SwaggerPetstoreClient, Ok
client = SwaggerPetstoreClient(base_url="http://127.0.0.1:8099")
res = client.pets.list(limit=10)
if isinstance(res, Ok):
print(res.value)
else:
print(res.error["category"], res.error["remediation"])
Calls return a Result and do not raise for API or transport errors. Arguments are validated before anything is sent, so a wrong type produces this envelope (real output of the snippet above with limit="ten", no request made):
{
"status": "error",
"category": "VALIDATION_FAILED",
"operation": "petstore.listPets",
"http_status": null,
"code": null,
"failed_parameter": "args.limit",
"received_value": "ten",
"expected": "Input should be a valid integer",
"remediation": "Fix args.limit (Input should be a valid integer) and call again.",
"retryable": "never",
"retry_after_ms": null,
"next_action": null,
"request_id": null,
"trace": { "attempts": 0 }
}
Destructive operations use the preview flow. From the TypeScript SDK generated for the ZROtext pilot:
const preview = await client.public.webhooks.disable.preview({ endpointId });
if (preview.ok && preview.value.confirmation_token) {
console.log(preview.value.effects);
await client.public.webhooks.disable({ endpointId }, { confirm: preview.value.confirmation_token });
}
Architecture
- The frontend reads OpenAPI 3.0 and 3.1, resolves references and applies overlays.
- The IR is a versioned JSON document (
tungsten ir dump, schema viatungsten schema ir). - The agent compiler merges
agent.ymlinto it: safety tiers, idempotency, confirmation, remediation text, verification hooks, macros and the disclosure mode for MCP. - Emitters write each target from the IR. They are pure functions of their inputs.
- Runtimes hold the behaviour shared by generated code: validation, auth, retries, idempotency stores, the envelope, previews, pagination and middleware.
Repository layout: crates/ is the compiler (AGPL-3.0-only); the compiler, the compiler, the compiler, the compiler and the compiler are the libraries generated code depends on (Apache-2.0).
Licensing
- The compiler is licensed under AGPL-3.0-only.
- The runtime libraries that generated code depends on and the external Go emitter (
emitters/go) are licensed under Apache-2.0. - Output generated by tungsten is not a work covered by the AGPL and carries no licence from tungsten. The owner of the API description chooses the licence of the generated code.