Model provider · localhost only

Claude Code as a model, on your own machine.

clyde serves the Claude Code CLI as an OpenAI-compatible provider on localhost. Point anything that speaks POST /v1/chat/completions at it and the replies come from claude -p running under your own Claude Code login. No API key, no tools, no MCP servers: the thing on the other end behaves like a model, not like an agent.

Python 3.12 or 3.13 Your Claude Code login Binds to 127.0.0.1 MIT licensed
$ uv run clyde serve --port 8127
$ curl localhost:8127/v1/chat/completions \
    -H 'content-type: application/json' \
    -d '{"model":"sonnet","messages":[{"role":"user","content":"say ok"}]}'

{
  "object": "chat.completion",
  "model": "sonnet",
  "choices": [{"index": 0,
               "message": {"role": "assistant", "content": "ok"},
               "finish_reason": "stop"}],
  "usage": {"prompt_tokens": …, "completion_tokens": …, "total_tokens": …}
}
One call, one runEach request is one claude -p process, one turn.
Nothing loadsNo tools, no MCP servers, no skills, an empty working directory.
Proved, not assumedStartup probes the lockdown; /ready reports what it saw.
No keys of its ownYour signed-in CLI answers. clyde holds no credential.

How it works

One chat-completions request becomes one claude -p run.

clyde is small, and most of it is there to keep one promise: the model on the other end can reach nothing. The flags are the security posture, and a test asserts each one literally.

01

The request is translated

System messages become the system prompt. A single user message is sent as it is; a longer conversation is rendered into one <conversation> of <turn role="…"> blocks. A request carrying tools is refused with a 400.

02

The CLI runs locked down

Every call passes --tools "", --disable-slash-commands and an empty --mcp-config with --strict-mcp-config, in an empty working directory, with one turn. A JSON-schema call gets four, because the CLI spends turns handing a structured answer back.

03

The reply comes back in OpenAI's shape

The text lands in choices[0].message.content, structured output included, as a string. Usage carries cached tokens under prompt_tokens_details. An empty reply is a 502, never a finished turn.

Routes

Four routes, and nothing else listening.

Errors use OpenAI's envelope, {"error": {"message", "type", "code"}}, and the message is a sentence you can act on: not_logged_in, claude_not_found, not_locked_down, tools_unsupported, claude_failed, timeout. Every refusal is also written to clyde's log.

GET /healthy

Is the process up?

{"status": "ok", "version": …} whenever the process is up. It checks nothing.

GET /ready

Would a call work?

200 when it would, otherwise 503. checks.claude says whether the binary was found; checks.lockdown lists the tools and MCP servers the startup probe saw load, and is ok only when both lists are empty.

GET /v1/models

Which models?

The aliases the CLI accepts, in OpenAI's list shape: sonnet, opus, haiku, fable.

POST /v1/chat/completions

One completion

Answered by one claude -p run. response_format with a json_schema asks for structured output; stream: true sends the same reply as server-sent events, in one piece, because the CLI returns the whole reply at once.

CLYDE_*

Configured by environment

Host, port, the path to claude, the default model, a timeout, how many processes may run at once. A CLYDE_* name no setting declares stops startup, so a typo is a crash rather than a setting that silently did nothing.

LUCY

Used by the Lucy hub

The Lucy hub knows clyde as its clyde provider at http://localhost:8127/v1, so Lucy can run on your own Claude Code login. clyde is built for the LUCY family but is not one of its keyring-verifying services.

Run it

A signed-in CLI, a sync, and a port.

You need Python 3.12 or 3.13, uv, and the Claude Code CLI signed in. clyde uses that login and never needs an API key.

  1. Install the Claude Code CLI, then run claude once in a terminal and sign in.
    npm install -g @anthropic-ai/claude-code
  2. Clone clyde and install it.
    git clone https://github.com/tochi-mba/clyde.git && cd clyde && uv sync
  3. Serve it on loopback. Startup runs claude once under the same flags as every call, which takes a few seconds and at most two minutes.
    uv run clyde serve --port 8127
  4. Ask it something.
    curl localhost:8127/v1/chat/completions -H 'content-type: application/json' -d '{"model":"sonnet","messages":[{"role":"user","content":"say ok"}]}'

clyde looks for claude on PATH, then in the places npm usually puts a global binary. A service manager often runs with a shorter PATH than your shell, so set CLYDE_CLAUDE_BINARY when it cannot be found; a missing binary does not stop the server, /ready says what is wrong. The README lists every CLYDE_* setting.

What it is not

A model, not an agent. A local runtime, not a hosted API.

Is this a hosted API, or does it have keys of its own?

Neither. clyde binds to loopback and authenticates nobody, which is the same posture as any other local runtime on the same machine. It holds no API key and no credential: the replies come from the Claude Code CLI you signed into, under its own login. Do not expose the port; that would hand a signed-in Claude Code to whoever finds it.

Can the model read my files or call tools?

No. Every call runs with no tools, no MCP servers, no skills and an empty working directory, and with one turn. That is checked, not assumed: at startup the CLI runs once under the same flags, /ready reports what it loaded, and every call is refused with a 503 unless that was nothing.

Does it support OpenAI tool calling?

No. A request carrying tools is refused with a 400 tools_unsupported. Ask for structured output through response_format instead; the JSON arrives as a string in choices[0].message.content, which is where a chat-completions client looks for it. A reply that comes back as tool-call syntax is asked for again, once.

Does it stream token by token?

Not today. stream: true sends the reply as server-sent events, one chunk with the text, one with the finish reason, one with usage, then data: [DONE], but claude -p returns the whole reply at once, so it arrives in one piece.

Docs

Written for the person who has to run it.