APIs and integrationsEdit on GitHubSource: docs/deploy/mcp-http.md

Running kshana-mcp over HTTP (self-hosted)

A passage naming third-party AI clients is left out here; it is in docs/deploy/mcp-http.md on GitHub.

Kshana does not host an endpoint. There is no shared kshana server to point a client at. You run it on your own machine, container or cloud project, and you own its access control and cost. The 0.35.0 server is the one in this repository: its figures carry their VALIDATED or MODELLED labels exactly as over stdio.

The safe defaults#

Control Behaviour
Bind address Loopback only (127.0.0.1, ::1). A bare port (--http 8080) means 127.0.0.1:8080.
Non-loopback Needs --allow-remote and a bearer token in the KSHANA_MCP_HTTP_TOKEN environment variable (16 characters or more). Without both, the server refuses to start. With both it prints a warning that it is listening beyond this machine.
Authentication Authorization: Bearer <token> on every request when a token is set (it can be set on loopback too). Compared in constant time. Missing or wrong: 401.
Filesystem None. The IQ file tools (iq_*) are switched off in HTTP mode whatever KSHANA_MCP_IQ_DIR says; every other tool takes its scenario inline and refuses one that names a file. No tool argument reads or writes a path.
Body size --max-body-bytes, default 5 MiB (the scenario cap is 4 MiB). Over it: 413, whether the length is declared or the body is chunked.
Reply time --request-timeout-secs, default 120. Over it: 408. This bounds how long a caller waits, not the work: a run already under way cannot be stopped, so it carries on after the 408 and finishes in the background.
Concurrency --max-concurrent, default 8. Beyond it a request gets 503 with Retry-After at once; none queues.
Work pool Every tool call runs on a blocking thread inside a pool of --max-concurrent slots, never on the async workers, so a long run cannot stop the server answering. A slot is freed only when the run itself ends, not when the caller gives up: abandoned runs therefore cannot pile up past the pool size, and a call that finds the pool full is refused as busy.
Work budget A scenario that asks for far more than the bundled examples is refused before it runs: more than 1000 Monte Carlo realisations (runs), a parameter grid of more than 1000 nodes, more than 5000 member runs (grid nodes times realisations), or a time grid of more than 2 million epochs. The error names the field to lower. The same budget applies over stdio; the kshana command line has none.
CORS No Access-Control-* header is ever sent. A request carrying an Origin header is refused (403) unless named with --allow-origin <scheme://host[:port]>; * is not accepted.
Host header Checked against loopback names, or the bind address plus each --allowed-host. Behind a proxy or on a platform URL, pass --allowed-host your.domain. Anything else: 403.
Sessions None. Every POST gets a JSON reply; no session table and no server-sent-event stream is kept open.

TLS is not terminated by kshana-mcp. When it is reachable beyond one machine, put HTTPS in front of it (your platform's TLS, or a reverse proxy) and keep the token secret; a bearer token over plain HTTP can be read on the wire.

Local#

kshana-mcp --http 127.0.0.1:8080

Client config (a URL client; add the header only when a token is set):

{ "mcpServers": { "kshana": { "url": "http://127.0.0.1:8080/mcp",
    "headers": { "Authorization": "Bearer ${KSHANA_MCP_HTTP_TOKEN}" } } } }

Docker#

export KSHANA_MCP_HTTP_TOKEN="$(openssl rand -hex 32)"
docker run --rm -p 127.0.0.1:8080:8080 -e KSHANA_MCP_HTTP_TOKEN \
  ghcr.io/ashfordeou/kshana-mcp:0.35.0 \
  --http 0.0.0.0:8080 --allow-remote --allowed-host localhost:8080 --allowed-host 127.0.0.1:8080

Inside the container the server must listen on 0.0.0.0, so it needs --allow-remote and the token. Publishing the port as 127.0.0.1:8080:8080 keeps it on your machine; drop the 127.0.0.1: only when you mean to expose it, and then terminate TLS in front of it. docker compose and Fly.io and Cloud Run templates are in mcp-http/.

Templates (you deploy them; Kshana runs none)#

Each runs the published image, sets --allowed-host to the platform hostname you replace in the file, and relies on the platform for HTTPS.

Set a CPU and memory limit on the platform#

The timeout, pool and budget bound how much work is admitted, but one admitted call can still use a core for a long time and a large result can use hundreds of megabytes. Put a hard limit on the container as well: the templates ask for 2 CPUs and 1 GiB (cpus and mem_limit in Compose, [[vm]] in Fly, resources.limits in Cloud Run). Keep --max-concurrent at or below the CPU count.

What it is not#

  • Not a multi-tenant service. Anyone holding the token runs simulations on your hardware; there are no per-user quotas beyond the limits above.
  • Advisory, not type-approved navigation equipment: the operator remains responsible for navigation. The simulator's outputs are modelled or validated figures for analysis, as everywhere else in Kshana.