Appendix: MCP Documentation Server Reference¶
The mechanics behind MCP Documentation Server: how the setup script decides it
may write to a directory, how installs and updates share that directory,
the methods and errors the server answers, and how a page fetch is
bounded. The wire itself belongs to kinetis/mcp-protocol and is
described in Appendix: MCP Reference. For setup and use, see MCP Documentation Server.
“The install directory”, “Install, verify and register” and “Updates”
describe setup.sh, which is how this package is registered as a server
of its own. The catalogue, protocol and fetch sections below hold for
either way it is reached: Orbitron composes the same
DocsApplication in its own process, publishing these resources beside
its own and delegating every read to it, with no installer, no update
lock and no registration of its own.
The install directory¶
setup.sh installs into KINETIS_MCP_DOCS_DIR, or ~/.kinetis-mcp-docs
when it is unset. It accepts no argument, or codex, and nothing else.
Before writing anything it checks that the Docker daemon answers and
that the chosen client’s CLI — claude or codex — is on PATH.
The directory must be absent, empty, or already carry the marker file
the script writes into every install it owns: .kinetis-mcp-docs, a
regular file holding exactly one fixed line. Ownership is that content,
not the name, so a symlink, a directory or different content under that
name is refused rather than reused, as is a path that exists and is not
a directory. A mistyped KINETIS_MCP_DOCS_DIR never has a
composer.json written into it.
Every Docker step runs the composer:2 image as the invoking user’s uid
and gid, with HOME and COMPOSER_HOME under /tmp, so nothing it
writes is owned by root and the host needs no PHP, Composer or sudo.
Install, verify and register¶
One container holds an exclusive flock on .update.lock in the
install directory while it:
removes
composer.lockand rewritescomposer.jsonto requirekinetis/mcp-docs^1.0;runs
composer install;writes the current time to
.last-update-check, only when the install succeeded.
The script then sends four messages through the command it is about to
register — initialize, notifications/initialized, resources/list,
and resources/read for kinetis://docs/index — and requires exactly
three responses carrying serverInfo, resources and contents. The
notification must draw nothing. The timestamp from step 3 turns that
run’s update check into a no-op.
Registration names the server kinetis-docs. For Claude Code the script
removes an existing user-scope registration of that name and adds one at
user scope; for Codex it runs codex mcp remove and codex mcp add.
The registered command runs sh vendor/kinetis/mcp-docs/start.sh in an
interactive composer:2 container over the install directory, so a
client session starts the server.
Updates¶
start.sh runs from the installed package on every spawn, whether or
not the session reads a page. It takes the same .update.lock and reads
.last-update-check. When more than 86,400 seconds have passed, it runs
composer update kinetis/mcp-docs --with-all-dependencies, which stays
within ^1.0, and records the time only when the update succeeded. A
spawn inside the window starts immediately; a failed update — no
network, say — starts the installed server anyway, and the next spawn
retries rather than waiting out the window.
A spawn that finds the lock held waits for it, so it starts from a
finished vendor tree and sees the timestamp the other process wrote. A
session spawned while setup runs waits for the install, and the first
spawn after it starts without a check of its own. The lock is an open
file descriptor, which the kernel releases if its holder dies, and
start.sh closes it before starting the server, so a long session never
holds off the next update. Composer’s output goes to stderr, because
stdout carries protocol frames. An update replaces start.sh too, since
it runs from vendor/.
Protocol¶
The server speaks MCP 2025-06-18 and no other revision, over
kinetis/mcp-protocol; Appendix: MCP Reference’s “Protocol revision” and
“Stdio framing” are the wire contract, shared with the application MCP
server. initialize always answers with 2025-06-18, whichever revision
the client asked for. Its result declares an empty resources capability
— no tools, since the catalogue has none, and no listChanged or
subscribe — names the server kinetis-mcp-docs at the package’s
version, and carries instructions telling the agent to read these pages
rather than answer from memory, naming kinetis://docs/agent-workflow as
the starting resource and warning that a served page can describe
behavior newer than the client’s installed release. No method depends on
an earlier initialize.
Method |
Behavior |
|---|---|
|
Selects |
|
Accepted and answered with nothing, as every notification is. |
|
An empty result. |
|
The whole catalogue in one response, each entry with |
|
Requires a non-empty string |
There are no tools, prompts or subscriptions.
Errors¶
Code |
Answered when |
|---|---|
|
The line is not valid JSON. |
|
The JSON is not an object — a top-level array (a batch) included — or |
|
The method is not in the table, including |
|
|
|
|
|
The page could not be fetched or is not valid UTF-8. The message is |
A parse error, a batch and an unreadable id answer under id: null;
any other error answers under the request’s own id.
A notification — a valid envelope with no id — is never answered and
never dispatched, whatever its method or params shape: JSON-RPC 2.0
gives its sender no response to read, and a frame matching no outstanding
request can desynchronize a strict client. A message that fails the
envelope check is not a notification, and still answers with -32600 or
-32700 even without an id. A well-formed client response message is
ignored rather than answered: this server sends no requests.
Stdio framing¶
Framing belongs to kinetis/mcp-protocol and is described once in
Appendix: MCP Reference’s “Stdio framing”: bounded reads, a 2 MiB payload cap,
only \r/\n stripped, and every frame written whole. A write that
makes no progress ends this binary with exit code 1 and a message on
stderr rather than leaving a truncated frame. End of input ends the loop
and the process exits 0. Nothing but JSON-RPC frames reaches stdout.
Page fetch¶
resources/read requests
https://raw.githubusercontent.com/kinetis-dev/kinetis/main/docs/<slug>.md,
built from constants: there is no origin, ref or path to configure, no
local copy, and no cache between reads.
TLS verifies the peer and the host name, at TLS 1.2 or later.
Redirects are not followed, and any status other than
200fails.A 10-second idle timeout and a 30-second total deadline bound the request.
The body is streamed and abandoned once it passes 4 MiB, rather than accumulated.
Every failure is -32603, with the reason on stderr.
The catalogue¶
The resource list is written out in Kinetis\McpDocs\DocsCatalogue,
because the package ships no copy of the documentation. A monorepo test
pairs that list against docs/*.md in both directions, so a page
without an entry, or an entry naming no page, fails the suite. A release
serves the list it was cut with, while each read fetches that page’s
current content from main: a page renamed or removed on main after a
release stays listed there and answers -32603 when read.
See also¶
MCP Documentation Server — setup and use.
Orbitron — the harness that composes this package to serve the same resources from one project-local connection.
Appendix: Satellite Packages —
Kinetis\McpDocsin the package map.