Skip to content

PMAT-3954: aprender-mcp serves a caller-supplied ToolIndex; apr tools behind the default apr-tools feature - #4022

Queued
noahgift wants to merge 5 commits into
mainfrom
PMAT-3954-mcp-generic-toolset
Queued

noahgift wants to merge 5 commits into
mainfrom
PMAT-3954-mcp-generic-toolset

Conversation

@noahgift

Copy link
Copy Markdown
Contributor

Closes #3954.
keep-open: #4020 pre-existing ETXTBSY flake in a subprocess test this PR does not touch; filed separately

Why. arbiter (paiml/infra#917, ARBITER-001 row I2-2) has to serve its own MCP tools on aprender-mcp. Before this PR, AprMcpServer could serve only apr's tools:

  • the dispatcher read a global index that inventory built at link time;
  • initialize answered the const SERVER_NAME;
  • depending on the crate linked in every apr tool plus inventory, nix, anyhow and a serde_yaml build-dependency.

What.

  • ToolIndex::from_entries(Vec<(ToolDefinition, DispatchFn)>) -> Result<_, String>. Keys are owned Strings, and a duplicate name is refused.
  • AprMcpServer::with_tools(name, version, Arc<ToolIndex>). initialize, tools/list, the sync call and every worker thread dispatch through the server's own index. The worker seam now takes &ToolIndex, so FALSIFY-MCP-DRAIN-005 and default_worker_dispatch_is_the_real_tool_dispatcher still exercise the real path.
  • serve_stream/run_stdio return std::io::Result, and anyhow is removed.
  • New default feature apr-tools gates inventory, nix and serde_yaml (all optional now), the build.rs codegen, schemas, apr's tool modules, and their tests. apr-cli names apr-tools explicitly.
  • With default-features = false, features = ["native"], the normal-dependency tree is serde and serde_json plus their own dependencies.

Design. A 3-lane plan grill (three distinct gemini models) returned 3/3 FAIL on the first plan. Every consensus finding is adopted here: the anyhow leak, the worker path bypassing the index, the &'static str key, the unconditional serde_yaml build-dependency, nix not being optional, tools/args needing to stay ungated, and test gating.

Evidence.

  • RED commit: ToolIndex::from_entries and AprMcpServer::with_tools don't exist (E0599).
  • FALSIFY-MCP-TOOLSET-001 (tests/falsify_toolset_generic.rs): 5/5 with and without default features. It checks that serverInfo is the caller's, tools/list is exactly the caller's tools, tools/call reaches the caller's function through the worker, an unregistered tool errors, and a duplicate name is refused.
  • cargo test -p aprender-mcp: green on 4 full lib runs and the full --tests run. One ETXTBSY flake appeared on the first run; it's pre-existing and filed as falsify_2384_run_apr_executes_the_resolved_binary is flaky: ETXTBSY (Text file busy) under parallel lib tests #4020.
  • cargo clippy -D warnings: clean on aprender-mcp (both feature sets, all targets) and apr-cli.
  • cargo metadata --locked: rc 0 at the root and in crates/facades.

After merge: arbiter can consume this only from a published aprender-mcp. The release is the operator's.

Pmat-Ticket: PMAT-3954

🤖 Generated with Claude Code

noahgift and others added 3 commits September 23, 2026 09:00
Pmat-Ticket: PMAT-3954

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…3954)

FALSIFY-MCP-TOOLSET-001: ToolIndex::from_entries and AprMcpServer::with_tools do not exist; the test drives serve_stream with a one-tool index (initialize names the caller, tools/list is exactly its tools, tools/call reaches its dispatch through the worker, an unregistered tool errors, a duplicate name is refused).

Pmat-Ticket: PMAT-3954

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… behind the default apr-tools feature

A downstream server (arbiter, paiml/infra#917) now serves ITS OWN tools on this crate: ToolIndex::from_entries (owned String keys, a duplicate name refused) + AprMcpServer::with_tools(name, version, Arc<ToolIndex>). initialize answers the caller's serverInfo; tools/list, the sync call and every worker thread dispatch through the server's own index (the worker seam now takes &ToolIndex, so FALSIFY-MCP-DRAIN-005 still drives the real path).

With default-features = false, features = ["native"] the crate depends on serde + serde_json only: serve_stream/run_stdio return std::io::Result (anyhow removed); inventory, nix and the serde_yaml build-dependency are optional under apr-tools, the codegen in build.rs runs only with it, and apr's tool modules, schemas and their tests are gated. apr-cli names apr-tools explicitly. Shape settled by a 3-lane plan grill (3/3 FAIL on the first plan; every consensus finding adopted).

FALSIFY-MCP-TOOLSET-001 5/5 with and without default features; cargo test -p aprender-mcp green (4 full lib runs; one pre-existing ETXTBSY flake filed as #4020); clippy -D warnings clean on aprender-mcp (both feature sets, all targets) and apr-cli.

Pmat-Ticket: PMAT-3954

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 23, 2026

Copy link
Copy Markdown

§13.11 rung 1 — quorum shadow verdict

S13-SHADOW pr=4022 head=fedb6d46022d36acbde5e2ab0e97d678aefd9bae verdict=REFUSE class=Q1 arm_rc=1

Shadow mode: this records a verdict and merges nothing. A refusal
to arm is not a block (§13 adds zero rows to §7) — the pull request is
exactly as green as it was.

@noahgift

Copy link
Copy Markdown
Contributor Author

quorum-review (AD-04): NOT agreed (auto_merge: checked=true was_armed=false disarmed=false)

{
 "ticket": "PMAT-3954",
 "head": "7b02c44c7c6be6176c279589fae4cef44b8f5b24",
 "width": 3,
 "executor": "agy",
 "agreed": false,
 "auto_merge": {
  "checked": true,
  "was_armed": false,
  "disarmed": false,
  "note": "auto-merge not armed"
 },
 "lanes": [
  {
   "lane": 1,
   "verdict": "PASS",
   "findings": 5
  },
  {
   "lane": 2,
   "verdict": "FAIL",
   "findings": 2
  },
  {
   "lane": 3,
   "verdict": "PASS",
   "findings": 4
  }
 ]
}

…r-tools; roadmap aggregate regenerated (quorum round 1, lane 2)

Lane 2 measured `cargo test -p aprender-mcp --doc` failing (E0433 anyhow): the earlier green claim came from --lib/--tests runs that skip doctests. Now: the full `cargo test -p aprender-mcp` exits 0, and --doc passes with and without default features. guard-tree: docs/roadmaps/roadmap.yaml regenerated by `make roadmap-aggregate`.

Pmat-Ticket: PMAT-3954

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@noahgift

Copy link
Copy Markdown
Contributor Author

quorum-review (AD-04): three PASS — agreed (auto_merge: checked=true was_armed=false disarmed=false)

{
 "ticket": "PMAT-3954",
 "head": "43302cd1e04d6b6ba11cd076cddefdf1e612b082",
 "width": 3,
 "executor": "agy",
 "agreed": true,
 "auto_merge": {
  "checked": true,
  "was_armed": false,
  "disarmed": false,
  "note": "auto-merge not armed"
 },
 "lanes": [
  {
   "lane": 1,
   "verdict": "PASS",
   "findings": 6
  },
  {
   "lane": 2,
   "verdict": "PASS",
   "findings": 0
  },
  {
   "lane": 3,
   "verdict": "PASS",
   "findings": 0
  }
 ]
}

This branch has not been deployed

No deployments
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.

aprender-mcp: a generic McpServer over a caller-supplied ToolSet, apr tools behind a default feature (blocks paiml/infra#917)

1 participant