Immutable. This exact content is served forever at /api/v1/blob/aefb5c6cd94868ee.
---
name: unikit-mcp-trap
description: >-
Record a finding about the configured engine MCP server into
.unikit/MCP-RECHECK-NOTES.md — the project's log of what has to be re-checked
here. Use right after a call reported success while changing nothing, ate an
argument, or validated a broken state, e.g. "write this down", "record this MCP
finding", "the server lied — note it", "add a recheck note", "trap this". Takes
findings already in the session first (they cost nothing), then optionally scans
the "## MCP Findings" table of plans touched since the last audit — the table
only, never the plan body. Writes in the one allowed genre — a check to perform;
never a lifted gate, never a named workaround, never a claim about what the
server can or cannot do. Makes zero MCP calls and needs no editor. To replay,
retire, or upstream existing notes use /unikit-mcp-audit instead.
argument-hint: "[optional: the finding in one line] (writes .unikit/MCP-RECHECK-NOTES.md; zero MCP calls)"
allowed-tools:
- Read
- Write
- Edit
- Glob
- Grep
- AskUserQuestion
- Bash(date *)
disable-model-invocation: false
user-invocable: true
metadata:
author: unikit
version: "1.0"
category: tools
---
# MCP Trap — record a finding
Write one durable line about the engine MCP server this project talks to, into
`.unikit/MCP-RECHECK-NOTES.md`.
**This skill makes zero calls to the engine MCP.** It needs no editor, no running
project, and no gate. A finding is something that was *already observed*; re-observing
it here would cost a session and could record the wrong thing. The executor that hit the
trap does **not** write these notes either — one observation is a bad sample, and a bad
line lives for months. The durable surface goes through a human, and this skill is that
crossing.
**This skill owns the notes format.** `references/notes-format.md` is the shared
specification: this skill writes to it, `/unikit-mcp-audit` curates against it. Load it
before writing anything.
## Language Awareness — BLOCKING PRE-REQUISITE
**BEFORE producing ANY output**, silently read `.unikit/system/LANGUAGE_RULES.md` and
apply it to all output (fall back to English if it is missing). The prose of a note is
authored in the project's artifact language; the `id`, the `area` keyword, and the whole
observation protocol below the line stay **English** — they are grepped by other skills.
Do not announce the language setting.
## The one rule that makes this file safe
> A note may contain **only a check**. Never a lifted gate, never a named workaround,
> never an assertion about the state of the server.
This is not style. A check that has gone stale costs one extra call and **fails safe** —
the pipeline stops instead of driving past. A lifted gate lifts an obligation *forever*,
and a "use Y instead of X" outlives the day X was fixed. The genre is the only real
protection this file has; the freshness of its contents is not.
Read `references/notes-format.md` → "Genre" for the rejection table, and apply it to
every candidate before it is offered.
## Bootstrap
Silently load — do not narrate:
1. **`{{skills_dir}}/{{self_name}}/references/notes-format.md`** — the format this skill
owns. Mandatory; without it, do not write.
2. **`.unikit/system/engine-mcp/INDEX.md`**, first lines only — the delivery stamp
(`server:` / `version:`). This is the configured server, the second side of every
header comparison. File absent → there are no known exceptions for this server; that
restricts nothing and switches nothing to `⏸️ MANUAL`. Carry the server as unknown
and say so once.
3. **`.unikit/MCP-RECHECK-NOTES.md`** if it exists — its header (`server:` / `version:` /
`audited:`) and the ids already taken.
**Header mismatch is a WARN, never a stop.** If the notes header names a different
server or version than the stamp, print exactly one line and keep going:
```
WARN [mcp-trap] server/version in notes header ≠ configured (<notes> ≠ <configured>)
```
The existing entries stay in force — they are *suspect*, not void, and suspect entries
still fail safe. Retiring them is `/unikit-mcp-audit`'s job, not this skill's.
## Step 1 — the current session
Findings already in this conversation cost nothing to collect: they were observed here,
with the raw response still in context.
Scan the session for observations that match a failure class from
`.unikit/system/dev-principles.md` → A3 — a call that reported done over an unchanged
state, an argument that was accepted and ignored, a checker that went green over a
broken artifact, an undo that reported done and left the state, a batch whose total hid
what it skipped, a read that predated the change it was meant to prove.
**Something found → go to Step 5 and stop.** Do not scan plans. The session is the
cheapest and the most reliable source; the plan scan exists only because sessions end.
## Step 2 — ask before reading anything from disk
Nothing in the session → count candidate plans first, then ask once:
```
No findings in this session. <N> plans changed after <audited>. Review them? [y/N]
```
`<audited>` is the `audited:` date from the notes header; `never` means every plan is a
candidate. Candidate plans are `.unikit/code/PLAN.md` and
`.unikit/code/plans/*/TASKS.md`, filtered by modification time against `<audited>`.
**Declined → print one line and stop:**
```
No candidates were searched — the plan scan was declined.
```
That line is not decoration. A silent stop here is indistinguishable from "there was
nothing to find", and the next run would repeat the same offer against the same plans.
## Step 3 — read the table, never the body
Agreed → for each candidate plan read **only the findings table**:
1. `Grep` the heading `## MCP Findings` in the plan.
2. Take the window from that heading to the next `##` heading, or 30 lines, whichever
comes first.
3. Read nothing else from that file. Not the tasks, not the brief, not the checklist.
The window is the whole contract. A plan is a large file written for a different
purpose, and reading it whole to harvest three rows is how a cheap maintenance skill
turns into an expensive one.
## Step 4 — drop what was already transferred
Every note carries `from: <plan>#<id>` in its observation row when it came from a plan.
Drop every candidate row whose `<plan>#<id>` already appears in the notes.
This is why no registry of processed plans is needed: the cursor is the `audited:` date
plus the `from:` back-references, both of which live in the notes file itself. A repeat
run over the same plans is idempotent by construction — and a plan that grew new rows
after the last transfer is picked up correctly, which a "seen plans" list would not do.
## Step 5 — offer, in the genre
Present the surviving candidates and let the user choose which to record. For each one,
draft **both halves** the format requires:
- **above the line** — the check: `id | area | confirm that …`, phrased as an
instruction to verify, with **no tool name in it**;
- **below the line** — the observation: `id | observed | replay | evidence | from`,
where `evidence` is the raw call and the raw answer it gave.
`area` comes from the 12-word vocabulary in `dev-principles.md` → A8 — the check table
is keyed by area precisely so it survives a server change, and a key outside that
vocabulary is unreachable by the greps the executors run.
`replay` defaults to **`manual`**. Promote it to `safe` only when every condition in
`references/notes-format.md` → "replay: safe" holds. When in doubt it is `manual`; the
cost of a wrong `manual` is that an audit shows the row instead of replaying it, and the
cost of a wrong `safe` is an audit that mutates a project.
**Reject, do not rewrite silently.** A candidate that asserts a capability, pre-declares
a gate, or names a replacement call is refused with the reason and offered back in check
form. If it cannot be expressed as a check, it does not belong in this file.
## Step 6 — write
Write `.unikit/MCP-RECHECK-NOTES.md` per `references/notes-format.md`:
- **File absent** → create it with the full header (`server:` / `version:` from the
delivery stamp, `audited: never`), both section headings, and the accepted rows.
- **File present** → append rows to both tables, allocating fresh ids. Do **not** touch
the header: `server:` / `version:` describe where the existing entries came from, and
`audited:` belongs to `/unikit-mcp-audit`.
Both halves of a note are written together, always. A check with no observation behind
it cannot be replayed, retired, or upstreamed — it is a rumour with an id.
Then confirm what landed:
```
CLAIM: <n> findings recorded in .unikit/MCP-RECHECK-NOTES.md
EVIDENCE: <the ids written> · read back from the file after the write
VERDICT: CONFIRMED | NOT CONFIRMED
```
Read the file back. The rule that a response is not evidence applies to this skill's own
writes as much as to a server's.
## What this skill never does
- **Never calls the engine MCP.** No verification of the finding, no re-observation, no
catalog lookup. If the finding needs replaying, that is `/unikit-mcp-audit`.
- **Never edits a plan.** Plans are read through a 30-line window and left untouched;
the transfer is recorded on the notes side, as `from:`.
- **Never edits the packaged rules tree.** `.unikit/system/engine-mcp/` is rewritten by
every `init` / `update`; an edit there is lost, and lost silently. A finding that
deserves to ship to everyone goes upstream through `/unikit-mcp-audit`.
- **Never writes `⏸️ MANUAL`,** and never treats a missing rules file as a restriction.
Absence of rules means no known exceptions, not absence of capability.
## See also
- `/unikit-mcp-audit` — curate what is here: re-stamp, replay, retire, upstream.
- `.unikit/system/dev-principles.md` — the evidence contract (A1/A2), the nine failure
classes (A3), and the `kind` / area vocabularies (A8) this skill keys its rows by.