feat(analysis): Rust MCP tool discovery (official rmcp crate)#33
Merged
Conversation
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).
trustabl
approved these changes
Jun 7, 2026
This was referenced Jun 7, 2026
# Conflicts: # internal/rules/loader.go
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.
Adds Rust as a discovered language for MCP tools, audited by the existing
mcp/rule pack via field-basedlanguage: rustrules — 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.go—DiscoverRustMCPTools:use rmcpgate; recognizes the official rmcp crate's#[tool]-attributed methods. name = thename = "..."arg (or the method name); description = thedescription = "..."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 realattribute_itempreceding-sibling node, so no comment-text hack is needed.ScanManifest.RustFiles,.rsrecon classification,Cargo.toml/Cargo.lockrmcpneedle, ScanID fold,parseRustFilespass, loader acceptslanguage: rust.Untyped-params has no Rust analog (statically typed; the input schema lives in a separate
#[derive(JsonSchema)]struct viaParameters<T>). Raw-string descriptions,#[tool]on free functions outside animpl, the#[prompt]/resource shapes, and Rust body-fact predicates are v1 gaps.Tests
Rust discovery unit tests (attribute + doc-comment description + use-gate), recon
.rsclassification, 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). Fullgo 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-rulesRust rules PR andtrustabl-rulebookRust docs PR. Rust is the last mainstream MCP language.