klaridian()
How-to guides

Target language

Generate the server in TypeScript (default) or Python.

klaridian generates the MCP server in TypeScript by default. Pass --language python to generate a Python server instead. Both languages produce the same tools, the same annotations, and the same observability behavior from the same OpenAPI spec—the choice is about the runtime you want to deploy and maintain.

TypeScript (the default)

npx klaridian generate --spec ./api.yaml --out ./my-server

The generated project uses the official @modelcontextprotocol/server SDK. Run it with npm install && npm run build && npm start, or let klaridian prepare it for you with --install.

Python

npx klaridian generate --spec ./api.yaml --out ./my-server --language python

The generated project uses the official mcp Python SDK. It ships with a requirements.txt, a pyproject.toml, and a server.py entry point. Set it up and run it with:

cd ./my-server
python -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
export KLARIDIAN_BASE_URL=https://api.example.com
python server.py

Once the virtual environment is prepared, klaridian start ./my-server launches the Python server too—it finds the project's .venv and runs server.py for you, the same single command that starts a TypeScript server.

For the network transport, add the flags to both the generate command and the run command:

npx klaridian generate --spec ./api.yaml --out ./my-server \
  --language python --transport streamable-http --port 3000

python server.py --transport streamable-http --port 3000

Runtime requirements

  • TypeScript servers need Node.js 22 or later (engines in the generated package.json).
  • Python servers need Python 3.11 or later (requires-python in the generated pyproject.toml).

These are the oldest release lines that are still supported upstream, and klaridian's CI runs its end-to-end suite on both the oldest and the newest supported line of each runtime. klaridian deploy builds on newer images: node:24-slim for TypeScript and python:3.13-slim for Python.

What's the same across both languages

  • Tools. One tool per OpenAPI operation, with identical names and input schemas.
  • Output schemas. When an operation's success response is a JSON object, the tool advertises a matching outputSchema and returns structuredContent alongside the text result. Identical in both languages. See Output schemas below.
  • Schema dialect. Every tool's input and output schema is expressed in JSON Schema 2020-12—the default dialect for MCP schema definitions—and declares it with a $schema field. Identical in both languages. See Schema dialect below.
  • Annotations. Read-only, destructive, idempotent, and open-world hints are computed the same way and appear the same way on the wire.
  • Curation. --include-tags, --exclude-tags, path and method filters all work the same—they act on the spec before either language emits.
  • Plugins. The otel, posthog, amplitude, and mixpanel plugins all support both languages. Each ships as readable, vendored instrumentation source in the language you chose.
  • Environment contract. Both servers read KLARIDIAN_BASE_URL for the upstream host and KLARIDIAN_AUTH_TOKEN for a Bearer token.
  • Conformance. An unknown tool returns a JSON-RPC -32602 protocol error; invalid arguments return a tool-error result the model can read and react to.
  • Code-mode. --architecture code-mode works in both. The model writes TypeScript in both, run by the same Deno sandbox; in Python, Deno is installed by pip. See Code-mode.
  • OAuth. --oauth-* generates a resource server that validates bearer tokens (JWKS signature, expiry, issuer, audience, required scopes) on --transport streamable-http. See OAuth 2.1.

What the Python target doesn't support yet

This is TypeScript-only for now:

  • --install. The Python prepare step (virtual environment plus pip install) is planned separately. Follow the printed next steps to set the project up by hand.

klaridian fails with a clear message if you combine --language python with it, rather than generating a server that silently drops the feature.

Output schemas

When an OpenAPI operation declares a JSON object success response, klaridian advertises it as the tool's outputSchema and the generated handler returns structuredContent (the parsed response body) next to the usual text result. A client can then consume a typed, structured result instead of re-parsing text. This works the same in TypeScript and Python, from the same spec.

klaridian reads the response schema from the spec itself—including resolving $refs into components.schemas—so you get the rich, named shape the API author wrote, not a guess.

When a tool gets an outputSchema: the operation's success response (preferring 200, then 201, then any other 2xx, then default) has an application/json body whose schema is a JSON object.

When it doesn't (the tool stays text-only, exactly as before):

  • the success body is an array, a primitive, or a top-level oneOf/anyOf/allOf (ambiguous to honour faithfully);
  • the operation has no JSON success body (for example a 204 No Content, or a binary download returned as a resource link);
  • the operation declares no success response schema at all.

This is deliberate. The MCP spec requires an outputSchema root to be an object, and the TypeScript SDK validates a successful result's structuredContent against the advertised schema. klaridian only advertises a schema it can faithfully honour: on a successful call the handler attaches the parsed body; on an error response it returns a tool error (no structured content expected); and if a response body isn't valid JSON, it falls back to a plain text result. You get precise output typing where the spec supports it, and no spurious validation failures where it doesn't.

Schema dialect

Every tool's input and output schema is emitted as JSON Schema 2020-12, the default dialect for MCP schema definitions since MCP revision 2025-11-25. Each schema declares the dialect explicitly with a $schema field:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": { "count": { "type": "number", "exclusiveMinimum": 0 } },
  "required": ["count"]
}

Your OpenAPI spec's schemas are converted from their OpenAPI-3.x (JSON Schema draft-07-style) form to 2020-12 during generation. The conversion is small and bounded, because OpenAPI-derived schemas only differ from 2020-12 in a few places—most notably the exclusive bounds: an OpenAPI 3.0 { "minimum": 0, "exclusiveMinimum": true } becomes the 2020-12 numeric form { "exclusiveMinimum": 0 }. This is identical in TypeScript and Python, from the same spec.

On this page