How-to guide

Give coding agents the decisions that apply to a task

Ask by task or by file path. In Claude Code, the /mneme:context command runs mneme test_query with your task. Other agents can call decision.applicable_to on Mneme’s local server with the file they are editing. Both work best when each decision has a scope word that appears in the paths it governs.

You’ll end up with: a lookup that returns the decision governing app/handlers/ for any file there, both from the command line that /mneme:context uses and from Mneme’s local server for agents.

Goal

Stop an AI coding agent from crossing an architectural boundary blocks a bad write after the agent has already written the change, and the agent then has to redo it. Showing the agent the relevant architecture decision record (ADR), here ADR-012, before it starts on app/handlers/invoices.py avoids that. Mneme can look up the decisions that match a task or a path. Claude Code reaches that lookup through the plugin's /mneme:context command. Agents that speak the Model Context Protocol (MCP), Codex among them, can reach it through Mneme's local MCP server; see Limits for what we tested.

Lookup only informs the agent. Enforcement stays with the agent hook and the continuous integration (CI) check from the other guides.

Prerequisites

Command
pipx install "mneme-hq[mcp]==0.10.0"

Configure

1. Give each decision a scope word that appears in your paths

Lookup matches whole words in the query against each decision's scope, constraints, text and rationale. A match on the scope counts most. There is no stemming, so handler doesn't match handlers. ADR-012 uses scope: handlers because every file it governs lives under app/handlers/:

File · docs/adr/ADR-012-handlers-use-repositories.md
---
id: ADR-012
title: Request handlers do not import the database client
status: accepted
priority: foundational
date: "2026-10-01"
scope: handlers
---
Handlers call repositories. A direct client import skips the transaction
and tenancy handling that lives in app/repositories.

## Constraints
- FORBID_LITERAL:
    value: from app.db.client import
    include_paths:
      - "app/handlers/**"

For the command-line lookup, words shorter than four letters never match, so api can't work as a scope there. Give every accepted ADR its own scope, too: when two share one, the import keeps only one of them (Turn an ADR into an enforceable decision shows it). A dotted scope such as handlers.invoices keeps related ADRs apart, with one catch for MCP, covered in Troubleshooting.

2. Register the MCP server with your agent

The server runs on your machine and talks to the agent over standard input and output (stdio). Each agent lists such servers in its own config file; Claude Code reads them from a .mcp.json file in the repository root. This is the entry to add:

Configuration
{
  "mcpServers": {
    "mneme": {
      "command": "mneme",
      "args": ["decision-mcp", "--proposals", ".mneme/decision_proposals.json", "--memory", ".mneme/project_memory.json"]
    }
  }
}

--memory points the server at the memory file your ADR import wrote. The server returns the decisions in its Decision Index, the versioned record of decisions inside that file, so after you edit an ADR, re-run mneme adr import with --update-existing; a running server picks the change up on its next read.

Memory created by mneme init on 0.10.0 is ready as it is. Memory from 0.9.x needs migrating first, covered in Troubleshooting. To have the server also check your ADR folder at startup, add "--adr-dir", "docs/adr"; it validates the folder and doesn’t change what the server returns. Paths in args resolve against the folder the host starts the server in, so use absolute paths if your host doesn't start in the repository root. Agents with a different config format, such as Codex, take the same command and arguments in their own format. The MCP server reference covers every option.

Run

Claude Code: /mneme:context

Type /mneme:context with a description of the task. The command asks Claude to run mneme test_query with your description. You can run the same lookup yourself, here with the query the Claude Code hook uses for an edit:

Command
mneme test_query --memory .mneme/project_memory.json --query "edit to app/handlers/invoices.py"

Any MCP host: decision.applicable_to

This small client starts the server the way an agent does and calls one tool. It imports the mcp Python package, which a pipx install keeps inside Mneme’s own environment, so run it from a virtual environment where you ran pip install "mneme-hq[mcp]==0.10.0". It passes --proposals "", so the server keeps proposals in memory and writes no file; the probe only reads decisions. Save it as mcp_probe.py in the repository root:

File · mcp_probe.py
"""Call Mneme's decision-mcp server over stdio, the way an MCP host does.

Usage:
    python mcp_probe.py list
    python mcp_probe.py <tool-name> '<json arguments>'
"""
import asyncio
import json
import os
import sys

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

SERVER = StdioServerParameters(
    command="mneme",
    args=["decision-mcp", "--proposals", "",
          "--memory", os.environ.get("MEMORY", ".mneme/project_memory.json")],
)


async def main(tool: str, arguments: str) -> None:
    async with stdio_client(SERVER) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            if tool == "list":
                tools = await session.list_tools()
                print("\n".join(t.name for t in tools.tools))
                return
            result = await session.call_tool(tool, json.loads(arguments))
            for block in result.content:
                print(json.dumps(json.loads(block.text), indent=2, sort_keys=True))


asyncio.run(main(sys.argv[1], sys.argv[2] if len(sys.argv) > 2 else "{}"))
Command
python mcp_probe.py list
python mcp_probe.py decision.applicable_to \
  '{"paths": ["app/handlers/invoices.py"]}'

Expected result

The lookup ranks ADR-012 first, matched on its scope, and prints the context an agent receives:

Recorded output
Query: edit to app/handlers/invoices.py

All decisions (ranked by score):
  [ADR-012] score=3.50 matched=decision, scope, rationale

Injected (top 3):
[Mneme decisions applied]

DECISION [ADR-012]: Request handlers do not import the database client
  Why:          Handlers call repositories. A direct client import skips the transaction
and tenancy handling that lives in app/repositories.

## Constraints
- FORBID_LITERAL:
    value: from app.db.client import
    include_paths:
      - "app/handlers/**"

  Scope:        handlers
  Typed rules:
    - FORBID_LITERAL: from app.db.client import
      Applies to: app/handlers/**

A task description finds ADR-012 only when it uses the ADR’s own words. “handler” isn’t “handlers”, so this query finds nothing:

Command
mneme test_query --memory .mneme/project_memory.json --query "add a list_invoices handler"
Recorded output
Query: add a list_invoices handler

All decisions (ranked by score):
  [ADR-012] score=0.00 matched=(no match)

This one uses “request handlers”, from the ADR’s title, and matches:

Command
mneme test_query --memory .mneme/project_memory.json \
  --query "add an invoices endpoint to the request handlers"
Recorded output
Query: add an invoices endpoint to the request handlers

All decisions (ranked by score):
  [ADR-012] score=4.50 matched=decision, scope, rationale

The MCP server lists six tools:

Recorded output
decision.propose
decision.propose_batch
decision.get
decision.search
decision.applicable_to
decision.trace

decision.applicable_to returns ADR-012, because its scope handlers appears in the path:

Recorded output
{
  "canonical_scope_matches": [
    {
      "context_scope": [
        "handlers"
      ],
      "decision_id": "ADR-012",
      "lifecycle_status": "active",
      "statement": "Request handlers do not import the database client",
      "version": "1",
      "version_id": "dver-07b2d935ba6c357186624774df0c2845"
    }
  ],
  "proposal_hint_matches": []
}

Evidence

Each result shows why it matched. test_query prints a score and the fields that matched (decision, scope, rationale). The MCP result carries the context_scope that overlapped the path and the decision's lifecycle_status.

Troubleshooting

A lookup for a generic file name returns nothing

A bare file name like utils.py shares no words with any decision:

Command
mneme test_query --memory .mneme/project_memory.json --query "edit to utils.py"
Recorded output
Query: edit to utils.py

All decisions (ranked by score):
  [ADR-012] score=0.00 matched=(no match)

Injected (top 3):
(none)

Use the full path, or describe the task in words that appear in the decision.

A scope like api never matches in the CLI lookup

Here ADR-012 governs app/api/ with scope: api. The path contains the scope, but three letters is below the CLI lookup's four-letter minimum:

Recorded output
Query: edit to app/api/invoices.py

All decisions (ranked by score):
  [ADR-012] score=0.00 matched=(no match)

Injected (top 3):
(none)

The MCP tool decision.applicable_to works differently: it matches the scope as a substring of the path, so api matches app/api/invoices.py there, and also rapid_import/. Pick a scope word of four letters or more that appears in the governed paths and not by accident in others, such as handlers.

decision.applicable_to doesn't return a dotted-scope ADR for its file

The tool matches the whole scope string inside the path. With scope: handlers.invoices, the string doesn't appear in app/handlers/invoices.py, so the tool returns nothing for that path, though mneme test_query still finds the ADR:

Recorded output
{
  "canonical_scope_matches": [],
  "proposal_hint_matches": []
}

To reproduce this, import the dotted-scope version of ADR-012 into a second memory file:

Command
mneme init --path .mneme/dotted.json > /dev/null && mneme adr import troubleshooting/dotted --memory .mneme/dotted.json --apply > /dev/null

Pass the scope in context as well and the ADR comes back:

Command
MEMORY=.mneme/dotted.json python mcp_probe.py decision.applicable_to \
  '{"paths": ["app/handlers/invoices.py"], "context": ["handlers.invoices"]}'
Recorded output
{
  "canonical_scope_matches": [
    {
      "context_scope": [
        "handlers.invoices"
      ],
      "decision_id": "ADR-012",
      "lifecycle_status": "active",
      "statement": "Request handlers do not import the database client",

Or use scopes that appear in your paths as written. (MEMORY only points this guide’s probe at a second memory file, imported from the dotted-scope ADR.)

The MCP server won't start

Memory written by Mneme 0.9.x has no Decision Index yet, so the server stops and prints what to run:

Recorded output
ERROR: project memory troubleshooting/memory-from-0.9/project_memory.json has no authoritative decision_index section.
  Preview the migration:  mneme decision-index migrate --memory troubleshooting/memory-from-0.9/project_memory.json
  After reviewing it, apply: mneme decision-index migrate --memory troubleshooting/memory-from-0.9/project_memory.json --apply

Run the first command, read the preview, then run the second. Before you apply, move every process that writes the file to 0.10.0, including CI jobs and teammates’ installs: if anything still running 0.9.2 writes to the migrated file, the write succeeds without a warning, and 0.10.0 then refuses to read the file. The migrate reference covers what the migration changes.

An --adr-dir problem stops the server too:

Recorded output
ERROR: ADR directory troubleshooting/missing does not exist

The server refuses to start when the --adr-dir folder doesn't exist, is given as an empty string, or holds an ADR that fails parsing, validation or precedence checks. An empty folder passes. Fix the path or the ADR, or remove --adr-dir: the server doesn’t need it to return decisions.

Limits

  • Lookup gives the agent context. It doesn't stop anything. Keep the hook and the CI check in place.
  • Matching is by shared words, not meaning. A task phrased in words the decision doesn't use won't find it.
  • The MCP setup is the generic process definition. We haven't verified it inside Codex or another named agent.
  • The MCP server is local and talks over stdio only. It has no HTTP endpoint and no authentication.
  • Over MCP an agent can read decisions and submit proposals. It can't accept a proposal or change enforcement; people review proposals with the CLI.

Next