Skip to content
Any MCP client

Any MCP client

The gateway serves MCP at /mcp on the same port as the HTTP API. Any MCP client that speaks streamable HTTP can use it: a desktop assistant, an IDE, an agent framework, or curl. The model gets six tools. The gateway applies the same checks as it does to every other write.

Connect

You need the gateway URL and a bearer token. The token is an API key from the gateway’s key file. The identity it maps to must have a role in the class you want to use.

SettingValue
URLhttp://127.0.0.1:8080/mcp (or http://kslate-gateway.kslate.svc/mcp in a cluster)
TransportStreamable HTTP
HeaderAuthorization: Bearer <token>

For a client that takes a JSON config, the shape is usually:

{
  "mcpServers": {
    "kslate": {
      "url": "http://127.0.0.1:8080/mcp",
      "headers": { "Authorization": "Bearer rem-token" }
    }
  }
}

Check your client’s documentation for where that file lives and the exact key names.

The six tools

ToolWhat the model sees
kslate_getRead a shared record. Returns each field’s version; you need it to write.
kslate_putWrite one field. Pass the version you read. If rejected as STALE, another agent changed it since you read it: re-read and decide again. Optionally list the fields and versions your decision is based on; if any moved, the write is rejected as STALE_PREMISE.
kslate_appendAdd an entry to a list field. Evidence is required.
kslate_claimTake ownership of a task for a lease period. Renew before it expires. If HELD, someone else is working on it.
kslate_checkpointSave or load your progress so a replacement can continue.
kslate_queryFind records in a class by field values.

The descriptions are written for the model. They tell it what to do on a rejection.

What this path cannot do

The model reads and writes the record itself, so kslate cannot make it read again after a rejection. A tool error comes back to the model, and what happens next is the model’s decision. The loop in the agent loop re-reads and re-decides in code instead, and handling rejections says what it does. The kagent guide compares the two.

Try it with curl

List the tools:

curl -s -X POST http://127.0.0.1:8080/mcp \
  -H 'Authorization: Bearer rem-token' \
  -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

The response is a server-sent event carrying one JSON-RPC message with the six tools and their input schemas. Call one:

curl -s -X POST http://127.0.0.1:8080/mcp \
  -H 'Authorization: Bearer rem-token' \
  -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"kslate_get","arguments":{"record":"incidents/inc-7","fields":["planned_action"]}}}'
event: message
data: {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"{\n  \"record\": \"incidents/inc-7\",\n  \"record_version\": 3,\n  \"fields\": {\n    \"planned_action\": {\n      \"value\": \"roll back api deployment\",\n      \"version\": 2,\n      \"author\": \"apikey:diagnoser\",\n      \"evidence\": \"api logs show config parse error at 14:02:11\",\n      \"updated_at\": \"2026-10-02T18:42:20.060834-07:00\"\n    }\n  }\n}"}]}}

A rejection comes back as a tool error with the same body the HTTP API would send. The credential is the exception. It is checked before any protocol method runs, so a missing or bad one is refused on the transport, before initialize or tools/list answer. Without a credential:

HTTP/1.1 401 Unauthorized
Content-Type: application/json

{"error":"UNAUTHENTICATED"}

Too many bad credentials from one address get the same treatment with 429 and RATE_LIMITED.

What the model has to do

The model reads with kslate_get, which returns versions. To write a versioned field it passes expected_version. To say what a write relies on it passes based_on. If a call returns STALE or STALE_PREMISE, the tool description tells it to read again and decide again.

kslate cannot make the model do that. If it ignores the error or sends the same write again, the gateway rejects it again and records the attempt. kslate verify shows an agent that behaves this way.

based_on without the model’s help

Most models will not fill in based_on reliably. The gateway can work it out instead. Send the same X-Kslate-Scope header on every request for one task or conversation. The gateway then treats what that header value read as the based_on of what it writes. The model never sees the header.

"headers": {
  "Authorization": "Bearer rem-token",
  "X-Kslate-Scope": "task-7f3a"
}

Use one value per task. A fixed value for the whole client would base every write on every read the client ever made. Reads count for 15 minutes. based_on has the rules.

In kagent, the allowedHeaders field on the Agent’s tool passes this header through from each invocation. The kagent guide shows it.

Details

  • The MCP endpoint is stateless. Each request carries its own credential and scope header. There is no session to initialise, though clients that send initialize get a normal answer.
  • The credential is whatever the gateway’s identity provider accepts. A gateway started with --identity apikey wants an API key. The one the Helm chart installs runs --identity k8s-sa and wants a projected ServiceAccount token. Most MCP clients can only send a fixed header, so in practice they pair with an API-key gateway.
  • There is no tool for history. Use kslate blame or the HTTP endpoint.
  • The same enforcement and the same error bodies apply as over HTTP and gRPC.