MCP server
kapi exposes its format-aware content engine as an
MCP (Model Context Protocol) server. An
assistant connected to it can read the content inside a .docx, a JSON catalog
or a Markdown file, ask what the project's own context says about a phrase,
edit the content, verify it against the project's checks, and write it back
byte-for-byte. Each step is a structured tool call, so the assistant never
edits the file's bytes directly.
For the agent-skills path (Claude Code calling the kapi CLI), see
Use the Kapi Agent Skills. The two can be
used together.
The tables below are generated by connecting to a real kapi mcp server and
asking it for its tools and its resources. A CI drift gate fails the build if
they stop matching the binary.
Quick Start
Start the MCP server:
kapi mcp
This launches a JSON-RPC server on stdio. You don't run it manually; your AI
tool starts it as a subprocess. With no flags it serves the
writing set; --tools names others.
Setup
Claude Desktop
Add to your Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows):
{
"mcpServers": {
"kapi": {
"command": "kapi",
"args": ["mcp"]
}
}
}
Restart Claude Desktop. Kapi tools will appear in the tool picker.
Claude Code
Add to your project's .mcp.json file (or create it at the repository root):
{
"mcpServers": {
"kapi": {
"command": "kapi",
"args": ["mcp"]
}
}
}
Claude Code will automatically discover and connect to the kapi MCP server.
VS Code (GitHub Copilot / Copilot Chat)
Add to your VS Code settings (.vscode/settings.json or user settings):
{
"mcp": {
"servers": {
"kapi": {
"command": "kapi",
"args": ["mcp"]
}
}
}
}
Or add to .vscode/mcp.json in your project:
{
"servers": {
"kapi": {
"command": "kapi",
"args": ["mcp"]
}
}
}
Cursor
Add to your Cursor MCP config (~/.cursor/mcp.json):
{
"mcpServers": {
"kapi": {
"command": "kapi",
"args": ["mcp"]
}
}
}
Windsurf
Add to your Windsurf MCP config (~/.windsurf/mcp.json):
{
"mcpServers": {
"kapi": {
"command": "kapi",
"args": ["mcp"]
}
}
}
If kapi is not in your $PATH, use the full path to the binary (e.g. /usr/local/bin/kapi or $HOME/go/bin/kapi).
Available tools
kapi mcp serves tools in named sets. An assistant reads every tool description
it is offered and chooses among them, so a server offers the sets its work
needs. --tools takes a comma-separated list, and a name that is not a set is
refused with the list of sets:
kapi mcp # the writing set
kapi mcp --tools writing,translation # writing, plus the loop that fills target languages
kapi mcp --tools all # every set
writing is the default: the context:// resources and these
8 tools, which ask what applies, record what
the assistant notices, read and edit a file's blocks with its structure kept,
and check what it changed.
| Tool | What it does |
|---|---|
apply_edits | Apply a kapi.change/v1 change set: the one write verb. Each content operation addresses one edition of one block with at, the ref read_blocks reports, and carries if_match, the rev you read. Several operations may name one block, each with the rev you read. set_content replaces the text (keep the <x id="…"/> placeholders; with if_match "absent" and an edition it creates that translation, which is how a translation's file is written), replace_text changes part of it by find (placeholder text, as read_blocks shows it), by start and end, or by range, set_attribute changes an inline code's attribute such as a link's href (code, name, value), mark wraps text in a new code such as fmt:bold, and remove_edition drops a translation. A plural or select branch is the operation's path. In a JSON, YAML or ARB catalog, insert_block adds a key (doc, name, after or before, and its text per language in editions) and delete_block removes one with its translations (at, and if_match mapping the catalog's own language to the rev you read). describe_format says which operations a format supports. A refused change set writes nothing: an edition that moved since you read it is refused as stale with its current revision and text, an edit that drops, invents or unbalances an inline code or flattens a plural is refused as guard, an edit that introduces a failing term or voice finding is refused as gate_failed with the findings (rewrite the wording they name and resend; only a person can override), and every other operation reports not_applied. Each refusal carries a code and the field at fault: re-read, fix the operation and resend. Status partial means some of it landed: a write interrupted after some documents were written (docs says which), or a store operation refused after the content was written, which carries its error. Re-read the documents before you send more. mode preview computes and checks the change set and returns a diff per document without writing. A source edit lists the translations it made stale under invalidates. Every operation is recorded as yours, the calling agent's, in this server's session. Writing a term, a content-memory pair or a recipe field and deciding a review are a person's: those operations are refused as not_permitted. Record a term rule as a suggestion with context_note, with from and to for wording you changed. Each document's findings say what the commit check found on the edit; run check_file after a native write, or for what the commit check does not run. |
check_file | Check a file you changed against the voice and terms in force at its location, in any format kapi reads (Markdown, JSON, Word, XLIFF and more). Run it on every file you changed and fix what it reports before you say the work is done. To check only what a change touched, pass diff, diff_against (a git revision), staged, or diff_range (A..B). Returns a kapi.check/v2 report: findings, and which analyzers covered the content. A pass is not semantic approval. |
context_note | Record what you noticed about how this project writes, as soon as you notice it, including what your own text does not use: a product, feature or plan name as written, the spelling variety, or a word chosen over a common alternative. For a name or word, pass `term` with the form the files use and `instead_of` with the form they avoid: the split form of a one-word name, the other spelling, or the other word. kapi adds the spacing, hyphen and case variants. Say anything else, such as the spelling variety, in `text`. Give `path` and `quote` from a file that was there before you started. Leave alone, with no note, a word the files write more than one way, an interface label, and wording taken from your task. When the person changes your wording, pass `from` (what you wrote), `to` (what they wrote) and `path`, and set `suggest` to also suggest the rule it implies. To take back a note of your own recorded wrongly, pass its id in `withdraw`. Every note is a suggestion: checks report it, none fails on it, and it holds only where you saw it until a person decides about it in review. |
context_read | Read everything that applies to one file: the voice in full, the words to use and to avoid, the old names that stay correct there, and what has been suggested but not yet established. kapi's section of AGENTS.md and CLAUDE.md states the same rules, so call this for a file no section covers or when you need the full answer. `path` is project-relative, e.g. `docs/guide.md`, or `profile/<name>` for a named profile. Set `comments` before writing a comment in a source file: the answer is then for the file's comments. The same text the context://<path> resource returns. |
context_search | Ask what this project says about one word or phrase: what it is called here, whether it is discouraged and what to say instead, and wording the project has already approved. For everything that applies to a file, call context_read instead. An empty answer means nothing is recorded about the word; if the files always write it one way, record that with context_note, and leave it alone, with no note, if they write it more than one way. `attention` says what a person must act on, such as context files nobody has imported. |
context_session_summary | Report what this session recorded with context_note, and what the hooks' check runs found in it (the edit hook's run on each edited file, and the Stop hook's ship gates): `checked` runs, the `failing` findings the runs left, the `caught` findings and the `files` the session edited. A finding is caught when a hook run reported it and the next run on the same file no longer found it. The hook records its runs under the assistant host's session id. Without `session`, it summarizes the session with the project's latest check run, or the session that recorded last when none ran a check, and names it in `chosen`. Call it only if you recorded a note, and end your report with what it says, including the command a person reviews the session with. |
describe_format | Say what apply_edits can do in a format: whether the format keeps a document's translations in one file or one per file, and for each content operation what it accepts (the content forms of set_content, the code types a writer can create, the attributes set_attribute can change per code type), or null where the format refuses the operation as unsupported. Name a format, or a document to describe the format kapi reads it in. apply_edits' input schema gives each operation's fields; on the command line, kapi apply --schema OP prints one operation's schema. |
read_blocks | Read a document's blocks, a page at a time: the read leg of apply_edits. Each block carries ref (doc, block and edition) to copy into an operation's at, rev to send as its if_match, and text with inline codes as <x id="…"/> placeholders, which an edit keeps. codes lists each code by the id its placeholder shows, with its type and attributes; structures lists each plural or select with the path that reaches a branch and the branch's text; editions lists the block's translations with their revision, text and status; ops lists the operations the block accepts. A page holds up to limit blocks: pass next back as cursor for the following page. A cursor from a document that changed since is refused as stale; read it again from the start. Reads the document as apply_edits writes it, through the format and configuration the project binds. |
content works on content directly:
| Tool | What it does |
|---|---|
check_text | Check a draft snippet with deterministic content rules. Before drafting, read the context://<project-relative-path> resource for the applicable guidance. Supply context_path to check with that destination's voice and terms; without it, project guidance is not resolved. Explicit profile_pack/profile_file are available only without context_path. Returns a kapi.check/v2 Report with findings, analyzer coverage and configuration warnings, which never change pass; pass is not semantic approval. After saving edits, use check_file to verify the actual file. |
detect_format | Detect the file format from a file path based on its extension |
redact | Replace sensitive spans with protected placeholders before processing |
term-check | Terminology Check |
voice_check | Score text against a voice profile using deterministic vocabulary rules; returns a 0-100 compliance score and findings |
voice_rewrite | Rewrite text to comply with a voice profile by substituting forbidden/competitor terms (deterministic, offline). A rule that names no replacement, and a match on an inflected form of a term, are left in place and listed under skipped with the term, its list, severity and reason; rewrite those by hand and verify with voice_check. |
translation runs the loop that fills target languages:
| Tool | What it does |
|---|---|
stats | Size files before processing them. Reports per-file and total content metrics: blocks (translatable and not), words, characters (with and without spaces, plus the unique-character inventory), segments when available, and a by-role breakdown. Works on any supported format (Word, PowerPoint, JSON, XLIFF, Markdown, HTML, …) and returns the same JSON `kapi stats --json` emits. |
translate | Translate content with an LLM or machine-translation provider (select an engine, then a provider) |
up | Bring a kapi project up to date against its ship gates: re-extract drifted sources, run the default flow (content memory reuse then AI translate) over every target language, concurrently per language, loop until every gated locale is shippable or waits for review, and run the project's bound checks each pass. In a project connected to a Bowrain server the run happens there (push, converge on the org's keys and shared content memory, pull the results); pass local to run the loop on this machine instead. Never fails on pending target work: blocks waiting for review are reported, not thrown. Returns the structured result (per-locale standing, locales waiting for review, materialized files). Use up_plan first to see the pending work and token estimate. |
up_plan | Dry-run the catch-up work for a kapi project: per (collection, locale), the blocks missing a target, exact-content-memory leverage, the remaining AI work, and a rough token estimate. No provider calls, nothing written. The pre-flight for the up tool. |
review is the review queue and the review picture of one block, with
apply_edits from the writing set, which records a pre-review:
| Tool | What it does |
|---|---|
review_block | Read one block's review picture: ref and rev, the reference and revision of the edition under review, and the block itself, under block: source and target text, ladder status, the last recorded decision (with identity), and the context the decision is made in: the point governing the file (voice guidance, term rules, coordinates), the blocks before and after it as run sequences, the prior approved version and the content-memory match with its wording, the check findings with their run anchors, and the AI pre-review score. Address it with the ref a review_queue row carries, or a ref read_blocks reports with the translation's edition. A block in the project's source language is read from its source file and returns its authoring rung with no target half. To record a pre-review, send apply_edits a decide operation with at set to ref, if_match set to rev, outcome advise, a score from 0 to 100 and your reasons: it is bound to the wording you read, a later edit drops it, and it never approves the block. |
review_queue | List the review queue: every block awaiting a person, each with ref, the reference review_block reads it by ({doc, block, edition}: the source document, the block key and the translation's language; no edition for a source block). One queue holds every language, the project's source language among them: a translated block not yet approved is one row, and a source block held below the project's translate_after level is another, marked `isSource`. The result also carries `languages`, the pending count per language. Filter with language, locale and/or collection. Read-only, derived from the content files and the project state store; blocks annotated by an AI pre-review carry their score. Lean by design: call review_block for a block's context (the point governing it, its neighbourhood, its prior version, its findings). |
Project scoping applies on top. Started inside a kapi project (discovered by a git-style upward walk, exactly as the CLI discovers it), the tool set is narrowed to the sources the recipe declares and the project's first target language becomes the default for the translating tools.
Tools from installed plugins
An installed plugin adds its own tools to this surface. kapi mcp starts each
plugin's server once per session and passes calls through, so the tools a plugin
brings answer on the same connection that serves the loop verbs. Configure one
MCP server, not one per plugin.
A plugin contributes the tools its manifest declares. A plugin that is not installed adds nothing, one that fails to start is left off with a note on stderr while the rest of the surface keeps working, and a plugin tool whose name kapi already uses is skipped so the kapi tool keeps the name.
Widening the surface
Two flags on kapi mcp add tools no set holds, for debugging and for callers
who really are assembling a pipeline by hand:
kapi mcp --all-toolsadds every CLI-visible processing tool, plus the listing verbs:case-transform,create-target,diff-leverage,dnt-check,encoding-detect,entity-extract,inline-codes-remove,list_formats,list_tools,media-refine,placeholder-check,pseudo-translate,pseudo_translate,qa,recycle,remove-target,review,search-replace,segmentation,term-extract,translate-after,unredact,voice-check,voice-infer,whitespace-correct,xml-validation.kapi mcp --all-flowsadds the flow-running verbs:list_flows,run_flow.kapi mcp --allserves every set and adds both.
Two tools are withheld from every surface, including the widened ones:
external-command and script run arbitrary commands and JavaScript, which is
a different class of decision from "show me every tool". Both remain available
through kapi exec.
Available resources
Retrieval comes in two shapes, a tool and a resource. Asking what a word
means is a call with arguments, so it is the context_search tool. Asking what
applies at a location is reading something that already exists at an address,
so it is a resource:
| Address | Renders as | What it answers |
|---|---|---|
context://profile/{name}{?format,project} | text/markdown | What applies when you write at one place: the voice, the words to use and to avoid, and what has been suggested but not yet established. kapi's section of AGENTS.md and CLAUDE.md states the same rules, so read it for a file no section covers or for the full answer. An answer with nothing recorded says so. Returns markdown; append `?format=json` for the structured answer, which also names the point, the project and the revision that answered. Names a profile instead of a file, e.g. `context://profile/marketing`. |
context://{+path}{?format,project,comments} | text/markdown | What applies when you write at one place: the voice, the words to use and to avoid, and what has been suggested but not yet established. kapi's section of AGENTS.md and CLAUDE.md states the same rules, so read it for a file no section covers or for the full answer. An answer with nothing recorded says so. Returns markdown; append `?format=json` for the structured answer, which also names the point, the project and the revision that answered. The path is project-relative, e.g. `context://docs/guide.md`; `?project=<path>` reads another project. |
The two addresses are one primitive. A path answers what applies here; a
profile name answers the same question for a caller with no file in hand (an
ad-hoc run, or a starter pack). The markdown is the brief a writer reads: the
voice, one list of words to use and to avoid, and what has been suggested but
not yet established, as kapi context <path>
prints it. The JSON adds the point the location resolved to, the full voice
guide, the terms, the governance windows and the provenance.
The suggestions list is reported apart from everything in force. Each entry
carries its status, suggested or contested (with contested_by naming the
other side), the rule or note it states, who suggested it, in which session and
the evidence behind it, with the operation id a person keeps it by. A check
reports every suggestion and fails on none of them.
The server offers these addresses as resource templates, and many clients list
no resources from a template, so an assistant there cannot discover them on its
own. context_read returns exactly the same text as a tool,
the server's instructions name the address, and kapi context <path> prints
the same text for an assistant that can run a shell command.
The rendering is a property of the read. ?format=json
returns the same answer as application/json (the document kapi context <path> --json prints) and everything else returns text/markdown. An unrecognised
format is refused, so a program never receives prose it cannot parse.
context://docs/guide.md # markdown, for a model to read
context://docs/guide.md?format=json # the same answer, for a program
context://profile/marketing # a named profile, no location needed
The CLI half is kapi context <path>, which
calls the same host function and prints the same markdown. A conformance test
holds the two surfaces to the same answer.
Example conversations
"How many words need translating?"
How many translatable words are in
src/locales/en.json?
The assistant calls stats with the file path and returns a structured answer
with word, block, character, and segment counts.
"What do we call this here?"
Is "sign-in" the term we use, and how do we say it in French?
The assistant calls context_search. One question reaches every store the
project binds (its terms, its content memory, its voice profile) and the
answer says what the project calls the thing, whether it is discouraged and
what to say instead, and any wording already approved. Asking before writing is
cheaper than learning the same fact from a failing check afterwards.
Every answer carries notes, and the first of them says whether the context,
the terms or the decisions moved since this session last read them. What a
person must act on, such as a checkout whose context nobody has imported, is also in
attention, and it is the part of the notes the text answer shows. The
comparison is against what the project last observed of its server, held on
disk, so it costs a small file read and no round trip, and it reports rather
than resolves: an assistant told the terms moved asks again before continuing,
because the wording it had settled on was chosen against a context that has
since changed.
"Show me the content of this file"
Show me the translatable strings in
messages.json
The assistant calls read_blocks with the document's path and returns a page
of blocks: each block's reference and revision, its text with inline codes as
<x id="…"/> placeholders, the codes and their attributes, and the operations
the block accepts.
"Edit the content, then check it"
Tighten the intro paragraph in
report.docx, keeping it on brand
The assistant reads the blocks with read_blocks, rewrites the text itself
(no second model), and sends a change set through apply_edits: one
replace_text or set_content operation per block, addressed by the ref the
read reported and guarded by its rev. kapi writes it through the format's
writer, keeping the document's structure and inline codes, then the assistant
calls check_file to confirm the gate passes, looping until it does.
"Catch the project up"
What translation work is outstanding, and how much would it cost?
The assistant calls up_plan, a dry run reporting, per collection and locale,
the blocks missing a target, the content-memory leverage, the remaining AI work
and a rough token estimate. Nothing is written and no provider is called. up
then does the work: re-extract what drifted, reuse the content memory, translate
the rest, run the project's checks, and loop until every gated locale is
shippable or waits for review.
"Review this translation"
Show me the French blocks waiting for review, and pre-review them for me
The assistant calls review_queue, reads each candidate with review_block
using the ref its row carries (the result holds the block's text, status and
review context under block), and records a pre-review through
apply_edits: a decide operation at that ref, with if_match set to the
rev review_block returned, outcome advise, a score from 0 to 100 and its
reasons. The pre-review is bound to the wording the assistant read and
recorded under the identity agent/<client>, so a later edit drops it. It
decides nothing: the block stays in the queue until a person approves or
rejects it, and an assistant's establish is refused.
review_queue returns one queue across the project's languages. Each row
carries language and, when that language is the project's source, isSource: true and a status on the authoring ladder; languages counts the pending
blocks per language, so an assistant can narrow with the language argument to
one that has work. review_block reads a source-language block the same way it
reads a translation, from the source file, and returns its point and
neighbourhood with no target half.
Tool reference
context_read
Read everything that applies to one file: the voice in full, the words to use
and to avoid, the old names that stay correct there, and what has been
suggested but not yet established. kapi's section of AGENTS.md and
CLAUDE.md states the same rules, which an assistant loads without a call
(rules files), so an assistant calls this for a
file no section covers, or when it needs the full answer. It returns exactly
the text the context://<path> resource returns, for a client that lists no
resources from a template. path is project-relative, or
profile/<name> for a named profile; comments answers for the comments in the
file instead of its content.
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | yes | the project-relative file you are about to write, or profile/<name> |
comments | boolean | no | answer for the comments in the file rather than its content |
format | string | no | markdown (default) or json |
project | string | no | the project this call acts on: its kapi.yaml recipe, its root directory, or any path inside it (default: the project the MCP server started in) |
context_search
A word somebody has suggested a rule or a note about comes back under
suggestions, apart from the terms in force.
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | yes | the word or phrase to ask about |
limit | integer | no | max results per group (default 10) |
locale | string | no | narrow results to one language (e.g. en, fr) |
memory | string | no | path to a standalone content memory (default: the project's own store) |
project | string | no | the project this call acts on: its kapi.yaml recipe, its root directory, or any path inside it (default: the project the MCP server started in) |
terms | string | no | path to a standalone terms store (default: the project's own store) |
context_note
Record what the files do every time, or what the person changed. Use text for
a note, or term and instead_of for a suggested term rule: kapi adds the
spacing, hyphen and case variants of a compound, so Quickcast instead of
Quick cast also avoids Quick-cast and QuickCast. The lower-case
spelling is never added; name it in instead_of when the project avoids it. For wording
the person changed, pass from, to and path; suggest also records the
rule the change implies. A term rule and a change need path. Pass withdraw
with an id this session recorded to take that note back; a person handles
established rules and notes from other sessions.
The result is a suggestion: every check reports it, and none fails on it
until a person keeps it in kapi context review. It holds only where the
assistant saw it, the part of the project path belongs to, until a person
applies it more widely. It is the same recording as
kapi context note on the command line.
| Parameter | Type | Required | Description |
|---|---|---|---|
advisory | boolean | no | with suggest: the rule should only report once a person keeps it; unset, a kept rule fails a check |
block | string | no | the ref.block that read_blocks or kapi inspect reports |
from | string | no | for a change a person made: the wording that was there |
instead_of | array | no | forms the project avoids for term, e.g. Quick cast; spacing, hyphen and case variants are added for you |
path | string | no | the project-relative file you saw it in, or the person changed it in |
point | object | no | narrows where the note holds, axis to value, within the point the file it was seen in sits at, such as market set to no; it may narrow and never widen |
project | string | no | the project this call acts on: its kapi.yaml recipe, its root directory, or any path inside it (default: the project the MCP server started in) |
quote | string | no | the wording you saw, quoted from the file |
suggest | boolean | no | with from and to: also record the rule the change implies, so the next use of the old wording is reported |
term | string | no | the form this project uses for a name or word, e.g. Quickcast |
text | string | no | the fact, in one sentence, in your own words |
to | string | no | for a change a person made: the wording that replaced it |
valid_from | string | no | the note holds from this date (2006-01-02) or RFC3339 instant on |
valid_to | string | no | the note holds until this date (2006-01-02) or RFC3339 instant, not including it |
why | string | no | what the person said about the change, or what was wrong with the note withdrawn |
withdraw | string | no | the id of a note this session recorded wrongly, to take it back |
context_session_summary
An assistant calls it only after it recorded a note. It returns what this
session recorded and what became of it, counted as observed,
corrected, suggested, contested, established, withdrawn, dropped
and reset, with the sentence to end a task report on. The report ends with
the kapi context review --session <id> command a person reviews the session's
suggestions with.
It also returns what the hooks' check runs found in the session: the edit hook's run on each file an edit wrote, and the Stop hook's run of the ship gates:
| Field | Counts |
|---|---|
checked | the runs the hooks recorded |
failing | the failing findings the runs left, each file as the latest run that checked it found it |
caught | the findings a run reported that the next run on the same file no longer found |
files | the files the session's turns edited |
Each field is present at zero, so checked: 0 means no run was recorded. Each
hook records a run under the assistant host's session id, and the MCP server
records a note under its own.
Without session, the tool summarizes the session whose latest check run is
the project's most recent, so a note written after a hook run leaves the run's
outcomes in view. When no session holds a check run, it summarizes the session
that recorded last. chosen names the session, the rule that applied and when
the session last recorded. When the log holds no session's operations, the
answer is one line in report saying that the edit hook records one each time
it checks an edited file, and the Stop hook each time it runs the project's
checks. Pass session to read another session.
| Parameter | Type | Required | Description |
|---|---|---|---|
project | string | no | the project this call acts on: its kapi.yaml recipe, its root directory, or any path inside it (default: the project the MCP server started in) |
session | string | no | the session to summarize (default: the latest session with a check run, named in chosen) |
Keeping, dropping, widening, resetting and syncing are reserved for people,
through kapi context review, reset and sync or Kapi Desktop, and are
excluded from the MCP tools. See
Growing context.
read_blocks
Read a document's blocks, a page at a time. Each block carries ref, the
reference an operation names (doc, block and, for a translation,
edition), and rev, the revision of the edition the read showed, which an
operation sends as if_match. text holds the content with inline codes as
<x id="…"/> placeholders, which an edit keeps; codes names what each
placeholder stands for, and structures lists the branches of a plural or a
select with the path that reaches each. Pass next back as cursor for the
following page. Inside a project, doc is a path relative to the project's
root, and the file of a translation reads that edition of its source. The
format is the one the project's recipe binds to the file, or the one kapi
detects, and apply_edits writes the file in the same format. A source code
file whose comments kapi checks is refused as unsupported with the
capability comment: edit a comment in the file itself, then run
check_file on it.
| Parameter | Type | Required | Description |
|---|---|---|---|
doc | string | yes | the document: a path inside the project, relative to its root or absolute, or outside a project a path under the server's working directory; container!entry names an archive member; the file of a translation reads that edition of its source |
blocks | array | no | only these blocks, by the block key a read reports; empty reads every block |
cursor | string | no | continue a read: the next value of the page before |
editions | array | no | translations kept in files of their own to show beside each block, such as fr or de; the editions the document holds itself are always shown |
limit | integer | no | the most blocks a page holds (default 100, at most 1000) |
project | string | no | the project this call acts on: its kapi.yaml recipe, its root directory, or any path inside it (default: the project the MCP server started in) |
apply_edits
The write verb. apply_edits takes a kapi.change/v1 change set, the contract
every kapi surface sends (E-09),
plus the per-call project.
| Parameter | Type | Required | Description |
|---|---|---|---|
ops | array | yes | the operations, applied in order; a position names the content as read, and an empty list changes nothing. Several operations may name one block, each sending the rev you read, since every if_match is checked against the content the change set began with |
evidence | array | no | where the wording behind the change was seen |
gate | string | no | enforce (the default): a failing finding the change introduces refuses it; report: the change lands with its findings |
mode | string | no | apply (the default), or preview: compute and check, write nothing |
note | string | no | one line a person reads in history and review |
project | string | no | the project this call acts on: its kapi.yaml recipe, its root directory, or any path inside it (default: the project the MCP server started in) |
require_basis | boolean | no | refuse a derived-edition write whose authoritative edition moved since it was read |
schema | string | no |
| Operation | What it changes |
|---|---|
set_content | an edition's whole text, keeping its placeholders; with if_match: "absent" and an edition, it creates that translation, adding its key to a JSON, YAML or ARB translation file that lacks it |
replace_text | part of the text, named by find (the text as a read shows it, a <x id="…"/> placeholder naming a code), by start and end, or by range; a path names the plural form or select case it is in |
remove_edition | drops a translation: from a bilingual file such as PO, or with its key from a JSON, YAML or ARB translation file |
insert_block | adds a key to a JSON, YAML or ARB catalog, with its text in each language |
delete_block | removes a key from such a catalog and from each translation's file |
decide | a pre-review: outcome advise, with a score and reasons |
describe_format lists what each format supports; an operation the format
refuses is refused as unsupported and writes nothing.
{
"note": "Name the handbook",
"ops": [
{
"op": "replace_text",
"at": { "doc": "docs/guide.html", "block": "p" },
"if_match": "r:3f9a1c0e7b2d4a55",
"edits": [{ "find": "shop guide", "text": "handbook" }]
}
]
}
A refused change set writes nothing. An edition that changed since it was
read is refused as stale, and the refusal carries its current revision and
text, so the assistant can rebase without another read. An edit that drops,
invents or unbalances an inline code, or flattens a plural or select, is
refused as guard. A reference that names no block is not_found, and so is
a find that matches nothing, with the text it searched. A block the format
reads as not translatable, such as a code block, is unsupported. Each
document in docs carries the findings the edit introduced, failing or not, an
empty list when the check found nothing; a failing one refuses the change as
gate_failed, and the finding names the wording a term rule asks for.
Every other operation of a refused change set reports not_applied, and the
result is an error. The status partial is an error too: some of the change
set landed. Either a write stopped after some files were written, and docs
names them, or the content was written and a decision recorded after it was
refused, and that operation carries the error. The assistant reads the
documents again before it sends more. mode: "preview" computes the result
and a diff per document and writes nothing. A source edit lists the
translations it made stale under invalidates.
apply_edits sends every change set as the calling agent in the server's
session; a change set has no field that names its sender. Writing a term, a
content-memory pair or a field of kapi.yaml is a person's decision, so a
change set holding a term, memory or recipe operation, an if_match of
* or gate: "report" is refused as not_permitted and writes nothing. The assistant records a term rule as a
suggestion with context_note, with from and to for wording it changed,
and a person keeps it. A review decision is a person's too: an
assistant's decide takes only the outcome advise, with the score it gives.
A person sends the same operations with kapi apply in a terminal.
describe_format
What apply_edits can do in a format: whether the format keeps a document's
translations in one file (in-file) or one per file (one-per-file), and for
each operation what it accepts, or null where the format refuses it.
| Parameter | Type | Required | Description |
|---|---|---|---|
doc | string | no | a document, to describe the format kapi reads it in; give this or format |
format | string | no | a format name, such as html, markdown or po |
project | string | no | the project this call acts on: its kapi.yaml recipe, its root directory, or any path inside it (default: the project the MCP server started in) |
check_file
| Parameter | Type | Required | Description |
|---|---|---|---|
diff | string | no | unified diff text (git diff output); only the content blocks it touches are checked |
diff_against | string | no | git revision to diff the working tree against, read-only, with untracked files as added; only the content blocks changed are checked |
diff_range | string | no | two commits as A..B, or A...B for the change B made since its merge base with A; each file is read from B and nothing from the working tree; only the content blocks changed are checked |
dnt | array | no | do-not-translate terms that must survive verbatim into the target |
file | string | no | path to the file whose content should be checked; with diff, diff_against, staged or diff_range it narrows the scope to this file, and may be omitted |
forbid | array | no | regex that must NOT appear in the content |
max_chars | integer | no | flag content longer than this many characters (0 = off) |
max_words | integer | no | flag content with more than this many words (0 = off) |
profile_file | string | no | explicit voice override loaded from YAML; bypasses the file-scoped project voice and channel; omit to use project guidance |
profile_pack | string | no | explicit voice override; omitting profile_pack and profile_file preserves the file-scoped project voice and channel |
project | string | no | the project this call acts on: its kapi.yaml recipe, its root directory, or any path inside it (default: the project the MCP server started in) |
require | array | no | regex that MUST appear in the content |
staged | boolean | no | check the changes staged for commit: the index diffed against HEAD, with each file read from the index, leaving out unstaged edits and untracked files; only the content blocks changed are checked |
target | string | no | translated target file to check against the source (enables the bilingual source-against-target checks) |
target_lang | string | no | locale of the target file (e.g. de) |
validate | string | no | reader structure/encoding validation: off|report|strict (report folds structure.*/encoding.* findings into the report; strict also fails on a Major+ structure/encoding problem). Default off. |
check_text
| Parameter | Type | Required | Description |
|---|---|---|---|
text | string | yes | the text to verify |
context_path | string | no | project-relative destination whose voice and terms govern this draft; may not exist yet; requires a project; cannot combine with profile_pack or profile_file |
forbid | array | no | regex that must NOT appear in the content |
max_chars | integer | no | flag content longer than this many characters (0 = off) |
max_words | integer | no | flag content with more than this many words (0 = off) |
profile_file | string | no | path to a voice profile YAML |
profile_pack | string | no | built-in profile pack to check vocabulary against (e.g. marketing-blog) |
project | string | no | the project this call acts on: its kapi.yaml recipe, its root directory, or any path inside it (default: the project the MCP server started in) |
require | array | no | regex that MUST appear in the content |
stats
| Parameter | Type | Required | Description |
|---|---|---|---|
files | array | yes | paths of the files to summarize |
format | string | no | input format override applied to every file (default: auto-detect by extension/content) |
up_plan
| Parameter | Type | Required | Description |
|---|---|---|---|
no_checks | boolean | no | plan the run up makes with no_checks: a language that only fails the project's bound checks gets no pass, so the plan prices none for it |
project | string | no | the project this call acts on: its kapi.yaml recipe, its root directory, or any path inside it (default: the project the MCP server started in) |
up
| Parameter | Type | Required | Description |
|---|---|---|---|
jobs | integer | no | how many languages to catch up concurrently per pass (0 = project default, else 4) |
local | boolean | no | in a server-connected project, run the loop on this machine and push the results, instead of running it on the server |
materialize | boolean | no | after the loop, write the target-language files for every shippable locale (overrides the recipe's materialize policy) |
no_checks | boolean | no | skip the project's bound checks inside the loop (failing blocks then count as translated) |
passes | integer | no | maximum reconciliation passes (0 = loop until up to date or waiting for review; 1 = single pass) |
project | string | no | the project this call acts on: its kapi.yaml recipe, its root directory, or any path inside it (default: the project the MCP server started in) |
detect_format
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | yes | File path to detect format from |
How it works
Kapi MCP uses the same machinery as the CLI commands: the format registry for detection, the executor for flow orchestration, and the same built-in tools. The MCP server exposes them as typed, discoverable tools over the Model Context Protocol stdio transport.
No server process, ports, or authentication needed. Your AI tool launches
kapi mcp as a child process, communicates over stdin/stdout, and shuts it
down when the session ends.
Most tools here are rule-based and need no API key: context_search,
check_text, check_file, voice_check, voice_rewrite, stats,
read_blocks, apply_edits, describe_format, the context write tools and
the review tools all run offline. The
ones that reach a language model (translate, and up when there is work the
content memory cannot cover) need a provider credential, which they read from
the same credential store the CLI uses (kapi credentials add).
Related
- Use the Kapi Agent Skills: the agent-skills path.
- Kapi CLI guides
- Context retrieval: why one question reaches every store.
- Command reference