Inkan
↑↓ move↵ open
GitHub

AI and data · RFC

0001: MCP server over decisions

Status
Accepted
Date

Accepted and implemented: see MCP server for agents.

Summary#

Ship an optional Model Context Protocol (MCP) server, inkan mcp, that reads decisions.json and the Markdown copies of a published site (or a local dist/) and exposes them as tools: list records with filters, read one record, follow its lineage, search by text. Agents connected to the server fetch exactly the records they need instead of a growing llms-full.txt.

Motivation#

The current files work in two ways. llms.txt is an index an agent follows link by link; llms-full.txt is everything at once. Both are fine for a few dozen records. With several hundred records the full file no longer fits a context window, and following links one by one costs a round trip per record with no way to ask “which accepted decisions mention Kafka and are tagged messaging”.

decisions.json already holds the typed metadata and a markdown link per record. What is missing is a protocol agents speak. MCP is the one coding assistants support today; a server over the existing JSON gives agents filters and lineage without a new data format and without a database.

Proposal#

Command#

Terminal window
npx @tilaven/inkan mcp --site https://decisions.acme.dev
npx inkan mcp --dist ./dist

The server runs over stdio, the transport every MCP client supports for local servers. --site fetches decisions.json and .md copies over HTTP; --dist reads them from a built folder, for use in CI or offline. The index is loaded once at start and refreshed on demand with a refresh tool.

Tools#

ToolArgumentsReturns
list_decisionsproject?, type?, status?, tag?, author?, since?, limit?Matching entries of decisions.json, without bodies
get_decisionidThe entry plus the raw Markdown body of the record
search_decisionsquery, same filters as list_decisionsEntries whose title, description or body contains the query
lineageidThe chain of supersedes and supersededBy around a record, plus related
list_projectsnoneProjects with name, description and record counts
refreshnoneReloads the index from the site or folder

Each tool returns the same objects as decisions.json, so a client that already understands the file understands the tool output. get_decision fetches the markdown URL of the record, which is why the server needs nothing beyond what the site already publishes.

Resources#

The server also exposes llms.txt, AGENTS.md and each record as MCP resources, so a client that prefers resources to tools can attach a record to the conversation by URL.

Client configuration#

.mcp.json
{
"mcpServers": {
"decisions": {
"command": "npx",
"args": ["-y", "@tilaven/inkan", "mcp", "--site", "https://decisions.acme.dev"]
}
}
}

Scope#

  • Read-only. Writing a record stays with inkan new and a pull request; AGENTS.md explains that path.
  • No full-text ranking beyond substring matching on the loaded bodies. The search.json the build writes for external search providers (plain-text content per record) could be reused later.
  • Shipped inside the @tilaven/inkan package behind the mcp command so no second package is needed; the MCP SDK becomes a dependency only when the command runs, if that can be arranged with a lazy import.

Open questions#

  • Should the server be a separate package (@inkan/mcp) to keep the SDK out of the main dependency tree, or a lazily imported command as proposed?
  • search_decisions needs record bodies. Fetch all .md copies at start (simple, one request per record) or lazily per query (slower first search)?
  • Private sites behind SSO: pass a bearer token or cookie through an option, or support only --dist for those?
  • Does decisions.json need a version or generatedAt field so the server can tell a client how fresh the index is? That would be a small change to the JSON shape.
  • Is lineage worth a tool, or is it enough that get_decision returns supersedes and supersededBy and the client follows them?
  • Should validation of broken supersedes/related references land first, so lineage never returns dangling ids?

History 1

  1. AcceptedInkan maintainers9e5e535 commit diff