Skip to content

Remove the MCP surface - #28

Merged
BY571 merged 3 commits into
masterfrom
remove/mcp
Aug 19, 2026
Merged

Remove the MCP surface#28
BY571 merged 3 commits into
masterfrom
remove/mcp

Conversation

@BY571

@BY571 BY571 commented Aug 19, 2026

Copy link
Copy Markdown
Owner

The repo's About section still advertised MCP, and it had stopped being true. Topics and
package keywords are already fixed; this removes the code.

Why

The CLI has been the primary surface since the measurement that demoted MCP. That change
kept the server as the fallback for a client with no shell — not a wrong argument, just an
outweighed one:

  • ~2,340 tokens of schema and instructions in every session, against ~304 for
    knoten --help, and only when asked.
  • A second surface to keep in step with ops.py forever. Every cross-surface drift bug
    this project has recorded came from having two: update's refusal built twice with
    different keys, --field coercing 2 to 2.0 on one side and not the other,
    ops.index(query=...) reachable from one surface with no flag on the other.

What went

src/knoten/mcp_server.py 327 lines
test_mcp.py, test_mcp_attach.py, test_tool_descriptions.py ~510 lines
[mcp] extra, knoten-mcp entrypoint, anyio dev dep packaging
README paragraph, CI install flag docs/config

The package is now 2,432 lines, one runtime dependency, no optional ones — verified by
building the wheel and listing it.

Removing the transport removed no enforcement

Worth stating plainly: the gate lives in commit, not in the surface. A status: alive
node with no kn:survivedGate edge is refused exactly as before. That is what extracting
commit.py and ops.py out of the server bought.

Tests kept their coverage

  • The created-stamping tests called the MCP tool; they now call commit() directly,
    which is where the stamp has lived since the extraction.
  • test_skill.py existed to bind SKILL.md's steps to the server's instructions step for
    step. With nothing to compare against, it now pins what SKILL.md can be held to alone:
    contiguous numbering, every step naming a real command, every command an agent needs
    present.
  • The suite is 270 passed, 0 skipped, up from 266 passed, 6 skipped — every skip
    was mcp-gated, so the whole suite now actually runs.

SPEC records the reversal

§8 already documented demoting MCP; it now documents removing it, plus the four things the
server taught that outlived it: derive the schema rather than write one, state the loop
once, put the gate under both doors, and an id authored by an LLM is not a path.

BY571 added 3 commits August 19, 2026 15:56
The About section still advertised it, and it had stopped being true. The CLI has been
the primary surface since the measurement that demoted MCP; it kept the code as the
fallback for a client with no shell, and that argument was not wrong — only outweighed.

It cost ~2,340 tokens of schema and instructions in every session against ~304 for
`knoten --help`, and it was a second surface that had to be kept in step with ops.py
forever. Every cross-surface drift bug this project has recorded came from having two:
`update`'s refusal built twice with different keys, `--field` coercing `2` to `2.0` on
one side and not the other, `ops.index(query=...)` reachable from one surface with no
flag on the other. The shell is the interface.

Gone: mcp_server.py (327 lines), test_mcp.py, test_mcp_attach.py, test_tool_descriptions.py
(~510 lines), the `[mcp]` extra, the `knoten-mcp` entrypoint, `anyio` (a dev dependency
that existed only for those async tests), and the README paragraph. The package is 2,432
lines with one runtime dependency and no optional ones.

REMOVING THE TRANSPORT REMOVED NO ENFORCEMENT, which is the part worth stating: the gate
lives in `commit`, not in the surface, so a `status: alive` node with no
`kn:survivedGate` edge is refused exactly as before. That was the point of extracting
`commit.py` and `ops.py` out of the server in the first place.

Two tests kept their coverage rather than dying with it: the `created` stamping tests
now call `commit()` directly, which is where the stamp has lived since the extraction.
`test_skill.py` lost the cross-surface binding it existed for — it compared SKILL.md's
steps against the server's instructions — so it now pins what SKILL.md can be held to
alone: contiguous numbering, every step naming a real command, every command an agent
needs present. It also stops being skipped: the suite is 270 passed, 0 skipped, where it
was 266 passed and 6 skipped, because every skip was mcp-gated.

SPEC §8 records the reversal instead of quietly dropping it, along with the four things
the server taught that outlived it — derive the schema rather than write one, state the
loop once, put the gate under both doors, and an id authored by an LLM is not a path.
Deleting the module left roughly twenty comments and docstrings across the package and
the tests explaining WHY the code is shaped the way it is by naming a thing that no
longer exists. Past-tense history is worth keeping — `commit.py` and `ops.py` exist as
separate modules because domain logic once sat in a transport layer, and a reader
deserves to know that — but the history reads fine without the dead brand name in it.

So the reasoning stays and the name goes: "this lived inside the transport layer",
"a second surface since removed", "one surface guarded the id and it was not the one
people used". SPEC §8 keeps the whole reversal, the token measurements and the four
lessons that outlived the server; it just no longer needs the reader to know what the
protocol was called.

Also untracked `src/knoten.egg-info/`, which was carrying the deleted `knoten-mcp`
entrypoint and the `anyio` dependency in committed build metadata.

`grep -ri mcp` over the repo now returns nothing.
A simplifier pass mutation-tested every behaviour the four deleted files asserted, and
the PR's headline claim turned out to be only half right. "Removing the transport
removed no enforcement" is true — every write gate still fails under mutation. But the
transport's test files were the ONLY cover for five behaviours, and deleting them
deleted the coverage silently, because on a machine without the optional SDK those
tests had been skipping all along.

Ported, each verified by reintroducing the bug and watching the new test fail:

- THE DUPLICATE WARNING, the worst of them. `_similar` carries a 15-line docstring
  arguing for its exact thresholds — two shared title words, settled claims only,
  warn but never block — and every one of those four thresholds was free-floating.
  It is the mechanism that stops the graph holding two answers to one question, which
  is the failure this project exists to prevent. Also a test that the warning actually
  reaches the reader; the dict carried it and nothing printed it.
- FOUR ATTACHMENT GUARDS: symlink flagged, >1MB flagged, missing source refused with
  nothing written, unknown node refused. User-facing, not transport-specific. Rewritten
  against the current API, which returns an `Attached` and raises rather than returning
  a REJECTED dict.
- `killed_by` AND `why_it_died` IN A QUERY RESULT — jointly the answer knoten exists to
  give, asserted nowhere.
- `commit()` NEVER RAISES for a bad candidate. Its docstring still promised it, and that
  promise matters more now that this is the Python API.
- `query`'s OWN RESPONSE CAP. `INDEX_LIMIT` was covered; `QUERY_LIMIT` went with the
  deleted file.

test_skill.py also lost the guarantee it existed for. It bound SKILL.md to a second
document; with that gone, nothing checked the skill against the CODE — renaming the
`attach` subcommand left the suite green while SKILL.md taught a verb that no longer
existed. It now reads the real argparse parser, which is the thing that can drift.
Verified: the rename is caught. Its `get`/ALIAS/`knoten[_ ]` machinery existed solely to
reconcile a tool name with a CLI verb and is gone.

Also: 15 surviving `knoten_commit`-style tool names — worse than the word they replaced,
since they read as callables in a repo where `knoten.commit.commit` exists; a comment
claiming `frontier` goes through `retrieve()` (it does not, and the anonymised rewrite
invented that caller); the id-guard comment that had lost the reason the guard exists;
a comparison-table column renamed to "CLI" although it assesses OTHER systems, now
"agent-native surface"; two unused imports; and a `KNOTEN_GRAPH` delenv guarding an env
var nothing reads any more.
@BY571
BY571 merged commit a17a2d5 into master Aug 19, 2026
2 checks passed
@BY571
BY571 deleted the remove/mcp branch August 19, 2026 15:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant