AsDecided MCP Server
AsDecided MCP serves your repository's requirements,
decisions, designs, and roadmaps to coding agents as callable tools. It ships
as the native decided-mcp binary.
1. Install
brew install asdecided/tap/asdecided-core
No Python runtime or extra is needed. The server is read-only and has no
network side channel. The native decided telemetry command records local
compatibility state only; it has no sender and does not change MCP responses
(ADR-131).
Protocol compatibility
decided-mcp supports both MCP lifecycle eras:
- existing clients can continue using the initialize-based revisions through
2025-11-25; - current clients can use the stateless
2026-07-28revision throughserver/discoverand per-request metadata.
No configuration change is required for stdio clients: a current host discovers the current revision, while an older host follows the established initialize flow. The six read-only tools and their grounding results are the same in both eras.
For shared HTTP deployments, current clients send
MCP-Protocol-Version, Mcp-Method, and the applicable Mcp-Name headers.
AsDecided validates these before dispatch and never creates an MCP session.
Authentication remains the responsibility of the fronting deployment proxy;
the engine's mandatory HTTP audit posture is unchanged.
2. Configure your client
Replace /path/to/your/repo with the absolute path to the directory that
contains your AsDecided artifacts (or the decisions/ subdirectory within it). Use the
path you would pass to decided validate.
Adding a client that is not listed here? Every harness connects on the same two surfaces (the generated agent-instructions file and the
asdecidedMCP server), so a new integration is a documented recipe, not engine work — follow the integration recipes authoring guide.
Claude Code
Command form (adds the server to your Claude Code session):
claude mcp add asdecided -- decided-mcp --root /path/to/your/repo
.mcp.json form — create or edit .mcp.json in your project root:
{
"mcpServers": {
"asdecided": {
"command": "decided-mcp",
"args": ["--root", "/path/to/your/repo"]
}
}
}
Claude Desktop
Open claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json; Windows: %APPDATA%\Claude\claude_desktop_config.json) and add an entry under mcpServers:
{
"mcpServers": {
"asdecided": {
"command": "decided-mcp",
"args": ["--root", "/path/to/your/repo"]
}
}
}
Restart Claude Desktop after saving.
Cursor
Create or edit .cursor/mcp.json in your project root:
{
"mcpServers": {
"asdecided": {
"command": "decided-mcp",
"args": ["--root", "/path/to/your/repo"]
}
}
}
Omnigent
Omnigent is a meta-harness: its custom agents are defined
in a config.yaml, and an MCP server is a first-class tool type. Add an asdecided
entry under the agent's tools: section:
tools:
asdecided:
type: mcp
command: decided-mcp
args: ["--root", "/path/to/your/repo"]
The tool travels with the agent definition, so it stays attached whichever
harness Omnigent routes to. A worked setup — including pointing the agent's
instructions at the generated AGENTS.md — is in
examples/omnigent/.
3. Point AsDecided at a repository
--root accepts any directory. It does not have to be the top of a Git
repository — point it at the folder where your RAC Markdown artifacts live.
To check that the path is right before configuring your client:
decided index /path/to/your/repo
That should list your artifacts. If it shows nothing, run
decided init /path/to/your/repo to initialize the repository.
To try AsDecided against a ready-made corpus before using your own, point --root
at the included examples:
decided-mcp --root examples/guide
The examples/guide/ corpus contains one requirement, decision, design, and
roadmap for a fictional user management service — enough to explore all six
tools.
Response budgets
The native server caps each successful tool payload at 10,000 characters by
default. Set a different startup budget for either transport with
--budget N (the minimum is 128 characters):
decided-mcp --root /path/to/your/repo --budget 20000
The limit is measured on the JSON payload in content[0].text, not the outer
JSON-RPC frame. Collection results are truncated deterministically at whole
items and carry truncated, omitted, and hint fields. Artifact content
and retrieval excerpts may be shortened by character prefix. If fixed fields
alone cannot fit, the tool returns a small response_budget_exceeded error
instead of an oversized success. get_artifact and retrieve_grounding also
accept a positive per-call budget that can lower the startup value; values
below 128 are rejected as a tool error.
4. Your first grounded question
Once the server is connected, ask your agent:
What decisions has this repository recorded about data deletion?
The agent should call search_artifacts with a keyword like "delete" or
"soft-delete", retrieve ADR-001: Soft-Delete User Records via get_artifact,
and cite the decision ID in its response.
If you are pointing at your own repository, substitute a topic you know a decision covers.
Trusting what the server serves
AsDecided returns repository text; it does not make that text safe to follow. Treat each returned artifact as untrusted data. This includes bodies, excerpts, titles, relationships, and Markdown that looks like a system, agent, or tool instruction or that tells the agent to disregard another instruction. The read-only server protects the corpus from mutation; it does not protect the consuming agent from poisoned content (ADR-065).
Human pull-request review is the trust boundary. Prefer artifacts whose
get_artifact response reports provenance.status: Accepted, and treat
Proposed content as draft context. That status is a recorded lifecycle fact,
not a safety verdict or a promise that the prose is correct. If content in the
corpus conflicts with the user's task or with system instructions, surface the
conflict and treat the content as data rather than following its embedded
command.
Before merging corpus changes, run the normal validation and review gates plus the deterministic review aid:
decided doctor decisions/
Its injection-style-content finding is a warning for a human reviewer. It
does not sanitize or rewrite artifacts, and a clean result does not replace
human pull-request review.
5. The six tools
| Tool | When the agent calls it |
|---|---|
get_summary |
Once at session start — counts artifacts, flags health issues |
search_artifacts |
Before designing or implementing anything that a recorded decision might cover — keyword search across the corpus |
retrieve_grounding |
For one-call task grounding, optionally scoped to a path, with ranked excerpts and provenance |
find_decisions |
To find live decisions by topic or the decisions whose declared scope governs a code path |
get_artifact |
When an artifact ID appears, or before changing anything a decision covers |
get_related |
After retrieving an artifact — finds what else the change could affect |
get_related takes an optional depth (default 1, capped at 5): the default
returns immediate neighbours only, while depth>1 additionally returns a
neighborhood array of artifacts two or more hops away, each tagged with its
hops distance — for transitive context such as a decision a requirement's
roadmap depends on. The walk is bounded (depth, frontier, visited-set, and a work
budget) and deterministic; a truncation marker is set if any cap stops it.
Each search_artifacts match carries an additive recency object —
last_committed, age_days, and a stale flag derived from git history
(ADR-045) — so an agent choosing between results can see which artifact has
decayed without a follow-up get_artifact. stale is true once a file's age
exceeds the freshness threshold (default 180 days, set per repository under
freshness.stale_after_days in .decided/config.yaml). It is advisory data beside
its date, never a correctness verdict, and never changes which artifacts match or
their order; outside git the fields degrade to null. The join respects the
response budget — matches truncate whole, exactly as before.
The tool descriptions contain the trigger language; well-tuned agents call them without being told to.
6. Team setup: route CLAUDE.md to a RAC prompt (Claude Code)
The tool descriptions are sufficient on their own — the grounding demo proves
that — but teams adopting RAC can raise the call rate by giving every session
standing guidance. Rather than pasting instructions into CLAUDE.md, record
the guidance as a RAC prompt artifact and route to it, the same pattern this
repository uses for its own agent guidance:
decided new prompt decisions/prompts/agent-session-start.md
Fill the artifact with your team's standing instructions, for example:
- at session start, call
get_summaryto learn what recorded knowledge exists - before designing or implementing, call
search_artifactsfor the feature area — recorded decisions take precedence over conventions inferred from the code - when an artifact ID is mentioned, call
get_artifact; callget_relatedbefore changing anything an artifact covers - cite decisions by ID; if a task conflicts with a recorded decision, say so instead of silently overriding it
Then make CLAUDE.md a router:
# Agent session context
Canonical agent guidance lives in `decisions/prompts/` as validated RAC artifacts.
@decisions/prompts/agent-session-start.md
Claude Code inlines the referenced artifact at session start, so the effect is
identical to pasting the text — but the guidance is now a governed artifact:
decided validate checks it in CI, it is versioned and diffable like any other
decision, and AsDecided itself can serve it (get_artifact retrieves your usage
instructions — the system is self-describing).
Two caveats:
- The import inlines the artifact verbatim, YAML frontmatter included. That is harmless, and the agent then knows the artifact's own ID.
@importsyntax is Claude Code-specific. For Cursor or Claude Desktop, carry the same pointer in their native convention (for example.cursor/rules); the prompt artifact remains the single source of truth.
7. Read-access audit log (enterprise, opt-in)
For regulated installs that must record who consulted which decision, when, and which artifact references came back — information the response payload does not carry — the server can append one JSON line per read-tool call to a local file (ADR-084).
It is content-bearing by design and off by default: with no audit: stanza
nothing is written and responses are byte-identical to a server with no recorder.
It is local-only — the engine never transmits it; shipping the log to a sink
(Loki, S3, Elastic) is a separate collector's job. The log records the query and
the returned artifact references, never artifact bodies. Each reference
contains id, resolved, and a body-free provenance.path back into the
served corpus (ADR-127).
Enable it in .decided/config.yaml (committed and team-wide, so an auditor has one
git-diffable artifact to point at):
audit:
enabled: true
# path: /var/log/asdecided/audit.jsonl # optional; default: $XDG_STATE_HOME/decided/audit.jsonl
# on_write_error: warn # warn (default) | block
path— where the JSONL is written. Default$XDG_STATE_HOME/decided/audit.jsonl; override per machine with theDECIDED_AUDIT_PATHenvironment variable (for data residency).on_write_error—warn(the default) reports a write failure on stderr and keeps serving;blockrefuses the call with anaudit-unavailableerror rather than returning un-audited content.
Each line records ts, a per-process session, the principal, the transport
(stdio or http), the attribution (asserted or local), the tool, the
query, the returned artifact references, outcome, and duration_ms. The
principal is attributable, not authenticated (ADR-084): it defaults to the git
user.name/user.email in the served repository and can be overridden with the
DECIDED_AUDIT_PRINCIPAL environment variable. The enforced access boundary stays the
repository ACL plus human pull-request review — the log records who claimed to
query, not a verified identity. When enabled, the server announces it on stderr at
startup; it is never silent.
Per-caller attribution on a shared server
On a shared HTTP endpoint (--transport http) one process serves the whole team,
so a single construction-time principal would record every caller as the host.
Each request instead asserts who it is with the canonical X-AsDecided-Principal
header, and the audit line records that per-request principal with transport: http and
attribution: asserted
(ADR-098):
X-AsDecided-Principal: Alice Ng <alice@example.com>
This stays attribution, not authentication: the engine records what the caller
claimed and never verifies it, and the principal is never an access-control input —
tool responses are identical whatever the header says. If you need the assertion to
be trustworthy, your fronting proxy authenticates the caller and overwrites the
header it trusts (ADR-085). Empty values are absent and duplicate carriers are
rejected with HTTP 400 (-32023). A request that asserts nothing is recorded with
attribution: local, and the shared server's fallback
skips the host's git identity (it resolves DECIDED_AUDIT_PRINCIPAL, else
unattributed) so a caller's read is never mislabelled as the host. Shared HTTP
serving is also mandatory audit-on: it refuses to start without a working sink,
and a sink write failure blocks the call. When enabled, startup announces the
resolved path, recorded scope, transport, and write-failure mode on stderr.
8. Shared HTTP endpoint (team scale)
By default decided-mcp speaks stdio: one server process per developer, against
that developer's own checkout. At team scale you may instead want one
always-current endpoint every agent points at, so reads come from a single
main-backed source of truth rather than checkouts that lag between pulls. The
server gains a streamable HTTP transport for exactly this
(ADR-098):
decided-mcp --root /path/to/your/repo --transport http --host 127.0.0.1 --port 8000 --path /mcp --budget 10000
--transport—stdio(default) orhttp. Baredecided-mcpis unchanged, so every existing.mcp.jsonkeeps working.--host/--port/--path— where the HTTP server binds and serves (defaults127.0.0.1,8000,/mcp). It binds to loopback by default; exposing it to a network is a deliberate deployment choice.--behind-proxy— required when--hostis non-loopback. This is an explicit deployment acknowledgement, not authentication; put an authenticating TLS proxy in front and follow the deployment hardening checklist.
The HTTP transport is serving-layer only: the six tools are unchanged, the server re-reads the repository per call (no cache, no session state), and an HTTP response is payload-identical to stdio for the same corpus bytes.
Authentication is your proxy's job, not the engine's. The endpoint is read-only and unauthenticated by design — the engine grows no SSO, RBAC, or credential handling (ADR-085). Front it with a reverse proxy that authenticates callers and terminates TLS; identity in the audit log stays attributable, not authenticated (ADR-084).
HTTP serving is mandatory audit-on. Because a shared endpoint serves reads
no single developer's git identity can attribute, the HTTP transport refuses
to start without a working audit log — enable the audit: stanza (see
section 7) first, or the server
exits with an actionable error. stdio is unaffected.
Keeping the fronted checkout current with main — a merge webhook or a periodic
git pull — is a deployment concern outside the engine. The full recipe —
container, authenticating proxy, keep-current step, and observability — is on the
Shared Server page.
Derived-index cache
By default the server reuses the derived structures — the repository index, the relationship graph, and the search token vectors — across calls, kept fresh by an event-sourced watcher (ADR-099, default-on per ADR-112):
decided-mcp --root /path/to/your/repo --transport http
The cache is content-addressed and disposable. It is keyed on a hash of the
corpus bytes, so any change to any artifact — an edit, add, remove, or rename —
changes the key and forces a rebuild; freshness is confirmed before every call,
so no call ever serves stale state. Output with the cache is byte-identical
to the uncached path. The cache lives at $XDG_CACHE_HOME/decisions/derived
(override with DECIDED_CACHE_DIR); deleting it costs only latency, never
correctness — the files in git remain the single source of truth. Pass
--no-cache (or set DECIDED_NO_CACHE=1) to restore the zero-state posture where
every tool call re-reads the repository from disk.
9. Troubleshooting
Server not listed in the client
- Confirm
decided-mcpis on the PATH the client uses. Test with:bash which decided-mcp decided --version - Check the client's MCP server log for startup errors.
Wrong root (AsDecided answers from the wrong repository)
- Verify the
--rootpath in your config matches the directory you intend. - Run
decided index /path/to/your/repoto confirm the right artifacts are visible. - In Claude Code, run
/mcpto inspect the server configuration.
Empty corpus (AsDecided says no artifacts found)
When the server starts against a root with no RAC artifacts it prints a diagnostic to stderr:
decided-mcp: no RAC artifacts found under '/path/to/your/repo'. Point --root at a
directory containing RAC Markdown artifacts, or run 'decided init' to initialize
a new repository. The server is running; get_summary will report the empty state.
This is not a fatal error — the server runs and get_summary reports zero
artifacts. To fix it:
- Check that
--rootpoints at the right directory. - Run
decided index /path/to/your/repoto confirm artifacts are visible. - If the directory has no RAC artifacts yet, run
decided init /path/to/your/repoand start creating artifacts withdecided new.
get_summary returns all zeros
Same cause as the empty corpus diagnostic above. Either --root is wrong or
the repository has not been initialized. See the troubleshooting steps above.
Further reading
- CLI reference — every
decidedcommand includingdecided-mcp - Artifact types — what requirements, decisions, designs, roadmaps, and prompts look like
- Repository workflow — how to organize a RAC repository
- Examples corpus — the ready-made AsDecided corpus