feat: spike reqif-opa-mcp over MCP + Requirement converter - #186
Open
elasticdotventures wants to merge 8 commits into
Open
feat: spike reqif-opa-mcp over MCP + Requirement converter#186elasticdotventures wants to merge 8 commits into
elasticdotventures wants to merge 8 commits into
Conversation
…t converter
Spike per decision 6 (docs/systems-modeling-registry-rescope.md §5/§6
task 5): reqif-opa-mcp is wrapped over MCP, not ported to Rust;
arc-kit-au stays the canonical decision+cost ledger.
New crate reqif-mcp-spike:
- McpHttpClient: minimal blocking client for reqif-opa-mcp's Streamable-
HTTP MCP server (FastMCP 3.0.0b1, protocol 2024-11-05). Handles the
initialize handshake + mcp-session-id header, the mandatory
notifications/initialized follow-up, and tools/call, extracting the
JSON-RPC result out of the single-frame SSE response body.
- RequirementRecord: mirrors reqif-opa-mcp's requirement-record.schema.json
exactly (uid/key/subtypes/status/policy_baseline/rubrics/text/attrs).
- requirement_record_to_node(): converts a RequirementRecord into the
arc-kit-au Requirement struct from ledgrrr#184 (ArtifactKind::Requirement /
NodeType::Requirement). requirement_id<-uid, title<-key, rationale<-text,
source<-attrs.source_standard(+source_url) falling back to the policy
baseline id, status passed through, related_decisions always empty
(reqif-opa-mcp carries no decision links; those are created later in
arc-kit-au itself).
Verified live end-to-end against a real reqif-opa-mcp checkout
(uv sync --extra ingest-lite; uv run python -m reqif_mcp --http --port
8123) parsing both its own sample derived baselines,
samples/standards/derived/{nist_ssdf_dogfood,owasp_asvs_cwe}.reqif (4
and 7 requirements respectively), through reqif_parse -> reqif_query ->
requirement_record_to_node -> Requirement::node_id(), all producing
correct, deterministic req: NodeIds. That live run is captured as an
#[ignore]'d integration test (tests/live_server.rs) since it needs an
external repo checkout + Python/uv, not something CI or a fresh clone
has; re-run manually with REQIF_MCP_URL set to a running server.
cargo test -p reqif-mcp-spike: 5 unit tests pass, 1 ignored (live).
cargo check --workspace --all-features: clean. cargo clippy -p
reqif-mcp-spike --all-targets: clean.
…ask 6) - EvidenceArgs gains import_requirement/record_decision/record_cost actions, each constructing the corresponding arc-kit-au node (Requirement/Decision/Cost from ledgrrr#184) and inserting it into the evidence graph via EvidenceGraph::add_node. DuplicateNode is treated as idempotent success (content-hash dedup), matching the idempotent semantics already established for EvidenceBuilder's ensure_* methods. - parse_evidence_node_type gains requirement/req, decision/dec, cost (plus previously-missing rnd_activity/tax_offset, same class of gap). - Summary's node_counts and ListNodes' invalid-type error message extended to include the 3 new types. - EVIDENCE_TOOL's contract.rs action list and purpose string updated. Verified: new tests/evidence_requirement_decision_cost.rs (3 tests, covering import+list+detail+summary, record_decision+record_cost, and idempotent re-import) all pass; cargo build -p ledgerr-mcp --features legacy clean (1 pre-existing unrelated warning).
This was referenced Aug 22, 2026
Task 6 (ledgrrr#186's second commit) added import_requirement/ record_decision/record_cost to ledgerr_evidence's EVIDENCE_TOOL actions in contract.rs but never re-ran regen-docs, so the checked-in mcp-capability-contract.md drifted — caught by CI's check-drift step on downstream stacked PRs (#190, #193). Ran: cargo run -p ledgerr-mcp --bin regen-docs
…0.0) Pre-existing drift, unrelated to Requirement/Decision/Cost content (28 objects, unchanged) -- the workspace version was bumped to 1.10.0 at some point after this artifact was last regenerated. Only surfaced now because check-drift's earlier mcp-capability-contract.md failure (fixed in dd168f7) was masking this second, independent drift. Ran: cargo run -p xtask-mcpb -- export-viz-manifest
…t' into feat/reqif-opa-mcp-client-spike
elasticdotventures
marked this pull request as ready for review
August 22, 2026 13:09
…t' into feat/reqif-opa-mcp-client-spike
…t' into feat/reqif-opa-mcp-client-spike
…t' into feat/reqif-opa-mcp-client-spike
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PromptExecution/reqif-opa-mcpover MCP (decision 6 — wrap over MCP, don't port to Rust) and converting its output into theRequirementnode type from ledgrrr#184reqif-mcp-spike:McpHttpClient(minimal blocking client for reqif-opa-mcp's Streamable-HTTP MCP server — handshake, session header, the mandatorynotifications/initialized,tools/call+ SSE-frame result extraction),RequirementRecord(mirrors itsrequirement-record.schema.jsonexactly), andrequirement_record_to_node()(the second-stage converter intoarc_kit_au::node::Requirement)reqif-opa-mcp(uv sync --extra ingest-lite,uv run python -m reqif_mcp --http --port 8123), parsing both of its own sample derived baselines —samples/standards/derived/nist_ssdf_dogfood.reqif(4 requirements) andowasp_asvs_cwe.reqif(7 requirements) — throughreqif_parse→reqif_query→requirement_record_to_node→Requirement::node_id(), producing correct, deterministicreq:NodeIds for all 11tests/live_server.rs,#[ignore]'d by default (needs an external repo checkout + Python/uv that CI/a fresh clone won't have) — re-run manually withREQIF_MCP_URLset to a running serverField mapping (documented in the module doc comment)
uid→requirement_id,key→title,text→rationale,attrs.source_standard(+source_url) falling back topolicy_baseline.id→source,statuspassed through,related_decisionsalways empty (reqif-opa-mcp carries no decision links — those get created later in arc-kit-au itself).Stacked on #185 → #184 → #183 (same epic branch chain).
Test plan
cargo test -p reqif-mcp-spike— 5 unit tests pass, 1 ignored (live-server test)cargo check --workspace --all-features— clean (1 pre-existing unrelated warning inledgerr-mcp/src/fbar.rs)cargo clippy -p reqif-mcp-spike --all-targets -- -D warnings— cleanhttps://claude.ai/code/session_01XUAuQ6291UNn9sWcaviosB