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.
| Setting | Value |
|---|---|
| URL | http://127.0.0.1:8080/mcp (or http://kslate-gateway.kslate.svc/mcp in a cluster) |
| Transport | Streamable HTTP |
| Header | Authorization: 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
| Tool | What the model sees |
|---|---|
kslate_get | Read a shared record. Returns each field’s version; you need it to write. |
kslate_put | Write 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_append | Add an entry to a list field. Evidence is required. |
kslate_claim | Take ownership of a task for a lease period. Renew before it expires. If HELD, someone else is working on it. |
kslate_checkpoint | Save or load your progress so a replacement can continue. |
kslate_query | Find 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
initializeget a normal answer. - The credential is whatever the gateway’s identity provider accepts. A
gateway started with
--identity apikeywants an API key. The one the Helm chart installs runs--identity k8s-saand 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 blameor the HTTP endpoint. - The same enforcement and the same error bodies apply as over HTTP and gRPC.