Skip to content

feat(analysis): Rust MCP tool discovery (official rmcp crate)#33

Merged
jhumel-code merged 5 commits into
mainfrom
feat/rust-mcp-discovery
Jun 8, 2026
Merged

feat(analysis): Rust MCP tool discovery (official rmcp crate)#33
jhumel-code merged 5 commits into
mainfrom
feat/rust-mcp-discovery

Conversation

@jhumel-code

Copy link
Copy Markdown
Collaborator

Adds Rust as a discovered language for MCP tools, audited by the existing mcp/ rule pack via field-based language: rust rules — no new predicates, no schema bump.

Rust-specific changes

  • astutil/rust.go — tree-sitter-rust parser via the existing smacker module (NewRustParser); no new dependency (the grammar was already vendored alongside go/csharp/php).
  • rust_mcp.goDiscoverRustMCPTools: use rmcp gate; recognizes the official rmcp crate's #[tool]-attributed methods. name = the name = "..." arg (or the method name); description = the description = "..." arg OR the method's /// doc comment — rmcp derives it from either, so discovery honors both; params from the signature. Unlike PHP, tree-sitter-rust models #[tool] as a real attribute_item preceding-sibling node, so no comment-text hack is needed.
  • Wiring: ScanManifest.RustFiles, .rs recon classification, Cargo.toml/Cargo.lock rmcp needle, ScanID fold, parseRustFiles pass, loader accepts language: rust.
  • Fixture rules MCP-021 (no description, doc-comment-aware) + MCP-022 (ambiguous name). Mirrored to the production rules repo + rulebook in their own PRs.

Untyped-params has no Rust analog (statically typed; the input schema lives in a separate #[derive(JsonSchema)] struct via Parameters<T>). Raw-string descriptions, #[tool] on free functions outside an impl, the #[prompt]/resource shapes, and Rust body-fact predicates are v1 gaps.

Tests

Rust discovery unit tests (attribute + doc-comment description + use-gate), recon .rs classification, per-rule fire/silent cases — including a dedicated silent case for description-via-///-doc-comment — and an end-to-end scanner test (an rmcp project fires MCP-021 + MCP-022). Full go test ./..., go vet, gofmt -l, and the rules-sync check all pass. Docs updated (ARCHITECTURE, COVERAGE, README).

Merge order

Last in the cross-language MCP chain. Stacked on the PHP branch, so until predecessors merge this diff shows JS+Go+C#+PHP+Rust cumulatively. Merge: #29 (JS) → #30 (Go) → #31 (C#) → #32 (PHP) → this.

Paired with: trustabl-rules Rust rules PR and trustabl-rulebook Rust docs PR. Rust is the last mainstream MCP language.

Add Go as a discovered language for MCP tools, audited by the existing mcp/ rule
pack via field-based language:go rules — no new predicates, no schema bump.

- tree-sitter-go via the existing smacker module (astutil.NewGoParser); no new
  dependency. .go files are inventoried (ScanManifest.GoFiles), classified by
  recon, folded into ScanID, and parsed in a dedicated pass (parseGoFiles).
- go.mod dep needles for mark3labs/mcp-go, the official modelcontextprotocol/
  go-sdk, and metoro-io/mcp-golang.
- DiscoverGoMCPTools (go_mcp.go): import-gated to the mcp-go modules; recognizes
  mark3labs mcp.NewTool("n", mcp.WithDescription(...), mcp.WithString(...))
  (name + description + typed params) and the official mcp.AddTool(server,
  &mcp.Tool{Name, Description}, fn) (name + description). Emits
  ToolDef{Kind: mcp_tool, Language: go}, so deriveSDKsDetected stamps SDKMCP and
  the mcp/ pack loads.
- fixture language:go rules MCP-015 (no description) + MCP-016 (ambiguous name),
  field-based. (Mirrored to the production rules repo and rulebook separately.)

Tests: Go discovery unit tests (mark3labs + official + import-gate), recon .go
classification, per-rule fire/silent cases, and an end-to-end scanner test (a
mark3labs repo discovers the tool tagged go and fires MCP-015 + MCP-016). Full
go test ./..., go vet, gofmt, and the rules-sync check all pass. Docs updated
(ARCHITECTURE, COVERAGE, README).
…col SDK)

Add C# as a discovered language for MCP tools, audited by the existing mcp/ rule
pack via field-based language:csharp rules — no new predicates, no schema bump.

- tree-sitter-c-sharp via the existing smacker module (astutil.NewCSharpParser);
  no new dependency. .cs files inventoried (ScanManifest.CSharpFiles), classified
  by recon, ScanID-folded, parsed in a dedicated pass (parseCSharpFiles).
- dep needles for ModelContextProtocol in Directory.Packages.props /
  packages.config (variable-named .csproj is a best-effort gap).
- DiscoverCSharpMCPTools (csharp_mcp.go): gated to files that `using` a
  ModelContextProtocol namespace; recognizes [McpServerTool]-attributed methods
  (name = method name, description from a co-located [Description(...)], typed
  params). Emits ToolDef{Kind: mcp_tool, Language: csharp} -> SDKMCP.
- fixture language:csharp rules MCP-017 (no description) + MCP-018 (ambiguous
  name), field-based. (Mirrored to the production rules repo + rulebook separately.)
- loader accepts language: csharp.

Tests: C# discovery unit tests (attribute + using-gate), recon .cs
classification, per-rule fire/silent cases, and an end-to-end scanner test (a
ModelContextProtocol project fires MCP-017 + MCP-018). Full go test ./..., go
vet, gofmt, and the rules-sync check all pass. Docs updated (ARCHITECTURE,
COVERAGE, README).
Add PHP as a discovered language for MCP tools, audited by the existing mcp/ rule
pack via field-based language:php rules — no new predicates, no schema bump.

- tree-sitter-php via the existing smacker module (astutil.NewPHPParser); no new
  dependency. .php files inventoried (ScanManifest.PHPFiles), classified by
  recon, ScanID-folded, parsed in a dedicated pass (parsePHPFiles).
- dep needles for mcp/sdk and php-mcp/server in composer.json.
- DiscoverPHPMCPTools (php_mcp.go): gated to files whose `use` statements
  reference an Mcp namespace (official Mcp\... + community PhpMcp\...); recognizes
  #[McpTool]-attributed methods (name from the attribute's name: arg or the
  method name, description from its description: arg, params + typed-params from
  the signature). The smacker grammar parses a single-line #[...] as a comment,
  so the attribute is read from the preceding comment text via regex. Emits
  ToolDef{Kind: mcp_tool, Language: php} -> SDKMCP.
- fixture language:php rules MCP-019 (no description) + MCP-020 (ambiguous name),
  field-based. (Mirrored to the production rules repo + rulebook separately.)
- loader accepts language: php.

Unlike Go/C#, PHP type hints are optional, so an untyped-params analog of MCP-002
is meaningful — discovery already captures HasTypedParams; that rule is a
deliberate fast-follow. Multi-line attributes, #[McpResource]/#[McpPrompt], and
PHP body-fact predicates are v1 gaps.

Tests: PHP discovery unit tests (attribute-as-comment + use-gate), recon .php
classification, per-rule fire/silent cases, and an end-to-end scanner test (a
php-mcp/server project fires MCP-019 + MCP-020). Full go test ./..., go vet,
gofmt, and the rules-sync check all pass. Docs updated (ARCHITECTURE, COVERAGE,
README).
Add Rust as a discovered language for MCP tools, audited by the existing mcp/ rule
pack via field-based language:rust rules — no new predicates, no schema bump.

- tree-sitter-rust via the existing smacker module (astutil.NewRustParser); no new
  dependency. .rs files inventoried (ScanManifest.RustFiles), classified by recon,
  ScanID-folded, parsed in a dedicated pass (parseRustFiles).
- dep needle for the rmcp crate in Cargo.toml / Cargo.lock.
- DiscoverRustMCPTools (rust_mcp.go): gated to files that `use` rmcp; recognizes
  the official SDK's #[tool]-attributed methods. name = the name = "..." arg (or
  the method name); description = the description = "..." arg OR the method's ///
  doc comment (rmcp derives it from either, so discovery honors both); params from
  the signature. Unlike PHP, tree-sitter-rust models #[tool] as a real
  attribute_item preceding-sibling node, so no comment-text hack is needed. Emits
  ToolDef{Kind: mcp_tool, Language: rust} -> SDKMCP.
- fixture language:rust rules MCP-021 (no description, doc-comment-aware) + MCP-022
  (ambiguous name), field-based. (Mirrored to the production rules repo + rulebook
  separately.)
- loader accepts language: rust.

Untyped-params has no Rust analog (statically typed; the input schema lives in a
separate #[derive(JsonSchema)] struct passed via Parameters<T>). Raw-string
descriptions, #[tool] on free functions outside an impl, the #[prompt]/resource
shapes, and Rust body-fact predicates are v1 gaps.

Tests: Rust discovery unit tests (attribute + doc-comment description + use-gate),
recon .rs classification, per-rule fire/silent cases (including the
description-via-doc-comment silent case), and an end-to-end scanner test (an rmcp
project fires MCP-021 + MCP-022). Full go test ./..., go vet, gofmt, and the
rules-sync check all pass. Docs updated (ARCHITECTURE, COVERAGE, README).
@jhumel-code jhumel-code merged commit 84bc679 into main Jun 8, 2026
3 of 4 checks passed
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.

2 participants