Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .b00t/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,14 @@
"tags": [],
"created_at": "2026-09-19T12:56:26Z",
"updated_at": "2026-09-19T13:06:55Z"
},
{
"id": 53,
"title": "kr0ki: harden ReqIF and ReqIFz intake then expose a validated import contract",
"status": "pending",
"priority": 3,
"tags": [],
"created_at": "2026-09-20T06:18:55Z"
}
],
"version": "1"
Expand Down
12 changes: 10 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"
axum = "0.7"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

6 changes: 4 additions & 2 deletions containers/kr0ki-mcp/bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ def call_tool(arguments):
if tool is None:
return tool_error(f"unknown tool: {name}")

for key in ("source", "manifest"):
value = params.get(key)
for argument in tool["httpBinding"]["args"]:
if argument["placement"] != "body":
continue
value = params.get(argument["name"])
if isinstance(value, str) and len(value.encode("utf-8")) > MAX_INPUT_BYTES:
return tool_error("input exceeds the 1 MiB MCP bridge limit")

Expand Down
20 changes: 14 additions & 6 deletions crates/kr0ki-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ kr0ki-sysmlv2-client = { path = "../kr0ki-sysmlv2-client" }
# CHANGELOG guidance ("pin an exact version or a git tag"). `ontology` /
# `sysml_model` / `view` are default (not feature-gated) — no extra features
# needed here.
ufo-types = { git = "https://github.com/PromptExecution/ufo-types", tag = "v0.15.0" }
# Pinned to the feat/mbse-requirements branch tip (not yet a tagged
# release) -- bump to a `tag =` pin once ufo-types cuts one past v0.15.0
# including this commit.
ufo-types = { git = "https://github.com/PromptExecution/ufo-types", rev = "a1f6a89bb5b0f63352ff00ac7c1eaf6acef8a4af", features = ["reqif"] }
# kr0ki#13 — box 5 (b00t-graph Turtle -> D2 route): the typed TypeRelationshipGraph /
# CytoscapeGraph the D2 emitter targets. No tagged release exists yet (checked
# 2026-09-16); pinned by git rev per this issue's own operator decision, to be
Expand All @@ -42,11 +45,16 @@ oxrdf = "0.3"
# source.md #5): reads workspace Cargo.toml files to tell a workspace-sibling
# crate apart from a genuine external dependency.
toml = "0.8"
# M2 (docs/TODO.md "ReqIF / ReqIFz adapter", decided 2026-09-20 -- see
# docs/HANDOFF-2026-09-19-reqif-flexo.md): parse ReqIF 1.2 XML directly in
# Rust rather than calling `reqif-opa-mcp` as a sidecar. Apache-2.0, a direct
# Rust port of the same StrictDoc `reqif` reference implementation.
reqrs = "0.2.2"
# M2 (docs/TODO.md "ReqIF / ReqIFz adapter"): ReqIF 1.2 XML parsing + the
# ReqIfBundle -> RequirementGraph adapter moved into `ufo_types::reqif`
# (2026-09-20, `docs/DESIGN-NOTE-versioned-dialects-and-b00t-loader.md` §3) --
# enabled via the `reqif` feature above. No direct `reqrs` dependency here
# anymore; this crate only owns bounded-bytes acquisition (size/entry-count
# limits, hashing, the HTTP/MCP envelope), not parsing.
# ReqIFz is a ZIP container. kr0ki reads archives in-memory so the import
# boundary can enforce its own compressed and expanded byte limits before
# retaining any attachment metadata.
zip = { version = "2.4.2", default-features = false, features = ["deflate"] }

[dev-dependencies]
tokio = { workspace = true, features = ["rt", "macros", "fs", "net", "rt-multi-thread"] }
Expand Down
2 changes: 1 addition & 1 deletion crates/kr0ki-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub mod k8s_recognizer;
pub mod mcp_tool;
pub mod probe;
pub mod render;
pub mod reqif_adapter;
pub mod reqif_import;
/// Canonical home is `ufo_types::mbse::requirements` (kr0ki M1,
/// 2026-09-19) — re-exported here so existing `kr0ki_core::requirements::*`
/// call sites keep working without a local copy of the types.
Expand Down
28 changes: 26 additions & 2 deletions crates/kr0ki-core/src/mcp_tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub enum McpTool {
RenderKubeDiagram,
RenderK8sTopology,
RenderSysmlV2Snapshot,
ImportReqIf,
ListModelProjects,
ListModelCommits,
GetModelSnapshot,
Expand All @@ -33,6 +34,7 @@ impl McpTool {
Self::RenderKubeDiagram,
Self::RenderK8sTopology,
Self::RenderSysmlV2Snapshot,
Self::ImportReqIf,
Self::ListModelProjects,
Self::ListModelCommits,
Self::GetModelSnapshot,
Expand All @@ -49,6 +51,7 @@ impl McpTool {
Self::RenderKubeDiagram => "render_kubernetes_manifest",
Self::RenderK8sTopology => "render_kubernetes_topology",
Self::RenderSysmlV2Snapshot => "render_sysmlv2_snapshot",
Self::ImportReqIf => "import_reqif",
Self::ListModelProjects => "list_model_projects",
Self::ListModelCommits => "list_model_commits",
Self::GetModelSnapshot => "get_model_snapshot",
Expand Down Expand Up @@ -76,6 +79,9 @@ impl McpTool {
Self::RenderSysmlV2Snapshot => {
"Render a validated SysML v2 project commit from the configured model server."
}
Self::ImportReqIf => {
"Validate and normalize a UTF-8 ReqIF document. For binary ReqIFz archives, use POST /requirements/import."
}
Self::ListModelProjects => "List SysML v2 projects on the configured model server.",
Self::ListModelCommits => "List commits (immutable model snapshots) for a project.",
Self::GetModelSnapshot => {
Expand Down Expand Up @@ -129,6 +135,16 @@ impl McpTool {
"output": {"type": "string", "enum": ["svg", "png"], "default": "svg"}
}
}),
Self::ImportReqIf => serde_json::json!({
"type": "object",
"required": ["artifact"],
"properties": {
"artifact": {
"type": "string",
"description": "UTF-8 ReqIF XML. Use the HTTP upload route for binary ReqIFz."
}
}
}),
Self::ListModelProjects => serde_json::json!({"type": "object", "properties": {}}),
Self::ListModelCommits => serde_json::json!({
"type": "object", "required": ["project_id"],
Expand Down Expand Up @@ -220,6 +236,14 @@ impl McpTool {
ArgBinding { name: "output", placement: ArgPlacement::Query },
],
},
Self::ImportReqIf => HttpBinding {
method: HttpMethod::Post,
path_template: "/requirements/import",
args: &[ArgBinding {
name: "artifact",
placement: ArgPlacement::Body,
}],
},
Self::ListModelProjects => HttpBinding { method: HttpMethod::Get, path_template: "/model/projects", args: &[] },
Self::ListModelCommits => HttpBinding {
method: HttpMethod::Get, path_template: "/model/projects/{project_id}/commits",
Expand Down Expand Up @@ -322,13 +346,13 @@ mod tests {
use super::*;

#[test]
fn all_twelve_tools_have_unique_names() {
fn all_thirteen_tools_have_unique_names() {
let mut names: Vec<&str> = McpTool::ALL.iter().map(|t| t.name()).collect();
let before = names.len();
names.sort_unstable();
names.dedup();
assert_eq!(names.len(), before, "duplicate McpTool name in ALL");
assert_eq!(McpTool::ALL.len(), 12);
assert_eq!(McpTool::ALL.len(), 13);
}

#[test]
Expand Down
Loading
Loading