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
152 changes: 79 additions & 73 deletions POLICY_INDEX.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions autogen/POLICY_INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ Risk score = `severity_weight × confidence × 100` (engine formula; weights: lo
| -- | ------- | ------- | ----- | --------------------------------------------------- | ------------------------------------------------------------------------ | -------- | ---------- | ---- | --------------------------------------------------------------------------------------------------------- |
| 1 | AG2-001 | AutoGen | agent | autogen_conversable_agent, autogen_user_proxy_agent | AutoGen executor runs code on the host without Docker | high | 0.90 | 63.0 | [agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/autogen/agent_safety.yaml) |
| 2 | AG2-002 | AutoGen | agent | autogen_conversable_agent, autogen_user_proxy_agent | AutoGen executor runs code with no human review (human_input_mode=NEVER) | high | 0.85 | 59.5 | [agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/autogen/agent_safety.yaml) |
| 3 | AG2-004 | AutoGen | agent | autogen_group_chat_manager | AutoGen GroupChatManager has no max_round bound | medium | 0.80 | 32.0 | [agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/autogen/agent_safety.yaml) |
| 3 | AG2-004 | AutoGen | agent | autogen_group_chat_manager | AutoGen GroupChatManager has no explicit max_round bound | low | 0.60 | 9.0 | [agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/autogen/agent_safety.yaml) |
| 4 | AG2-005 | AutoGen | agent | autogen_assistant_agent | AutoGen AssistantAgent enables code execution on the LLM agent | medium | 0.70 | 28.0 | [agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/autogen/agent_safety.yaml) |
| 5 | AG2-006 | AutoGen | agent | autogen_conversable_agent, autogen_user_proxy_agent | AutoGen executor with code execution has no auto-reply cap | medium | 0.70 | 28.0 | [agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/autogen/agent_safety.yaml) |
| 5 | AG2-006 | AutoGen | agent | autogen_conversable_agent, autogen_user_proxy_agent | AutoGen executor with code execution has no explicit auto-reply cap | medium | 0.70 | 28.0 | [agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/autogen/agent_safety.yaml) |
| 6 | AG2-007 | AutoGen | tool | autogen_tool | AutoGen tool has no description | low | 0.90 | 13.5 | [tool_definition.yaml](https://github.com/trustabl/trustabl-rules/blob/main/autogen/tool_definition.yaml) |
| 7 | AG2-008 | AutoGen | tool | autogen_tool | AutoGen tool parameters are not type-annotated | medium | 0.85 | 34.0 | [tool_definition.yaml](https://github.com/trustabl/trustabl-rules/blob/main/autogen/tool_definition.yaml) |
| 8 | AG2-009 | AutoGen | tool | autogen_tool | AutoGen tool body spawns a subprocess | high | 0.85 | 59.5 | [shell_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/autogen/shell_safety.yaml) |
| 9 | AG2-010 | AutoGen | tool | autogen_tool | AutoGen tool body evaluates dynamic code | high | 0.85 | 59.5 | [code_execution.yaml](https://github.com/trustabl/trustabl-rules/blob/main/autogen/code_execution.yaml) |
| 10 | AG2-011 | AutoGen | tool | autogen_tool | AutoGen tool fetches a caller-controlled URL (SSRF) | high | 0.80 | 56.0 | [ssrf.yaml](https://github.com/trustabl/trustabl-rules/blob/main/autogen/ssrf.yaml) |
| 11 | AG2-012 | AutoGen | tool | autogen_tool | AutoGen tool network call has no timeout | medium | 0.80 | 32.0 | [network.yaml](https://github.com/trustabl/trustabl-rules/blob/main/autogen/network.yaml) |
| 11 | AG2-012 | AutoGen | tool | autogen_tool | AutoGen tool network call has no timeout | high | 0.85 | 59.5 | [network.yaml](https://github.com/trustabl/trustabl-rules/blob/main/autogen/network.yaml) |
| 12 | AG2-201 | AutoGen | repo | autogen | AutoGen project ships no agent-guidance doc (AGENTS.md/CLAUDE.md) | low | 0.90 | 13.5 | [repo_hygiene.yaml](https://github.com/trustabl/trustabl-rules/blob/main/autogen/repo_hygiene.yaml) |
6 changes: 3 additions & 3 deletions claude_sdk/POLICY_INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,19 @@ Risk score = `severity_weight × confidence × 100` (engine formula; weights: lo
| 2 | CSDK-002 | Claude SDK | tool | claude_sdk_tool | Tool parameters are not type-annotated | medium | 0.90 | 36.0 | [tool_definition.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/tool_definition.yaml) |
| 3 | CSDK-003 | Claude SDK | tool | claude_sdk_tool | Network call has no timeout | high | 0.85 | 59.5 | [network.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/network.yaml) |
| 4 | CSDK-004 | Claude SDK | tool | claude_sdk_tool | Path parameter used in I/O without validation | high | 0.70 | 49.0 | [path_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/path_safety.yaml) |
| 5 | CSDK-005 | Claude SDK | tool | claude_sdk_tool | Tool raises exceptions without a structured error contract | medium | 0.60 | 24.0 | [error_handling.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/error_handling.yaml) |
| 5 | CSDK-005 | Claude SDK | tool | claude_sdk_tool | Tool raises exceptions without a structured error contract | low | 0.60 | 9.0 | [error_handling.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/error_handling.yaml) |
| 6 | CSDK-006 | Claude SDK | tool | claude_sdk_tool | Mutating tool has no idempotency key | medium | 0.55 | 22.0 | [idempotency.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/idempotency.yaml) |
| 7 | CSDK-007 | Claude SDK | tool | claude_sdk_tool | Ambiguous tool name | low | 0.90 | 13.5 | [tool_definition.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/tool_definition.yaml) |
| 8 | CSDK-008 | Claude SDK | tool | claude_sdk_tool | Tool exposes **kwargs without explicit input_schema | medium | 0.80 | 32.0 | [tool_definition.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/tool_definition.yaml) |
| 9 | CSDK-009 | Claude SDK | tool | claude_sdk_tool | Tool fetches a caller-controlled URL (SSRF) | high | 0.60 | 42.0 | [ssrf.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/ssrf.yaml) |
| 10 | CSDK-010 | Claude SDK | tool | claude_sdk_tool | TypeScript Claude SDK tool shells out to the OS | high | 0.70 | 49.0 | [shell_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/shell_safety.yaml) |
| 11 | CSDK-011 | Claude SDK | tool | claude_sdk_tool | TypeScript Claude SDK tool evaluates dynamic code | high | 0.90 | 63.0 | [code_execution.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/code_execution.yaml) |
| 12 | CSDK-012 | Claude SDK | tool | claude_sdk_tool | TypeScript Claude SDK tool writes to the filesystem | medium | 0.50 | 20.0 | [path_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/path_safety.yaml) |
| 12 | CSDK-012 | Claude SDK | tool | claude_sdk_tool | TypeScript Claude SDK tool writes to the filesystem | low | 0.50 | 7.5 | [path_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/path_safety.yaml) |
| 13 | CSDK-013 | Claude SDK | tool | claude_sdk_tool | TypeScript Claude SDK tool fetches a caller-controlled URL (SSRF) | high | 0.60 | 42.0 | [ssrf.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/ssrf.yaml) |
| 14 | CSDK-014 | Claude SDK | tool | claude_sdk_tool | TypeScript Claude SDK tool has no description | low | 0.90 | 13.5 | [tool_definition.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/tool_definition.yaml) |
| 15 | CSDK-016 | Claude SDK | tool | claude_sdk_tool | TypeScript Claude SDK mutating tool has no idempotency key | medium | 0.50 | 20.0 | [idempotency.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/idempotency.yaml) |
| 16 | CSDK-101 | Claude SDK | agent | claude_agent_definition | Claude subagent is granted the Bash tool | high | 0.80 | 56.0 | [agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/agent_safety.yaml) |
| 17 | CSDK-102 | Claude SDK | agent | claude_agent_definition | Claude subagent is granted the WebSearch tool | high | 0.80 | 56.0 | [agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/agent_safety.yaml) |
| 17 | CSDK-102 | Claude SDK | agent | claude_agent_definition | Claude subagent is granted the WebSearch tool | medium | 0.80 | 32.0 | [agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/agent_safety.yaml) |
| 18 | CSDK-103 | Claude SDK | agent | claude_agent_definition | AgentDefinition sets permissionMode to bypassPermissions | high | 0.90 | 63.0 | [agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/agent_safety.yaml) |
| 19 | CSDK-104 | Claude SDK | agent | claude_agent_definition | Claude subagent is granted filesystem-write built-ins | high | 0.80 | 56.0 | [agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/agent_safety.yaml) |
| 20 | CSDK-105 | Claude SDK | agent | claude_agent_definition | Claude subagent is granted the WebFetch tool | high | 0.75 | 52.5 | [agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/claude_sdk/agent_safety.yaml) |
Expand Down
124 changes: 120 additions & 4 deletions docs/Policy/mcp/tool_definition.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,44 @@ rules:
confidence: 0.85
scope: tool
fix_type: code
- id: MCP-015
severity: low
confidence: 0.85
scope: tool
fix_type: code
- id: MCP-016
severity: low
confidence: 0.85
scope: tool
fix_type: code
- id: MCP-017
severity: low
confidence: 0.85
scope: tool
fix_type: code
- id: MCP-018
severity: low
confidence: 0.85
scope: tool
fix_type: code
- id: MCP-019
severity: low
confidence: 0.85
scope: tool
fix_type: code
- id: MCP-020
severity: low
confidence: 0.85
scope: tool
fix_type: code
references: [LLM06]
---

# Policy Rationale: MCP Tool Definition Hygiene

**Policy ID:** `mcp_tool_definition`
**File:** `mcp/tool_definition.yaml`
**Rules:** MCP-001, MCP-002, MCP-003, MCP-011
**Rules:** MCP-001, MCP-002, MCP-003, MCP-011, MCP-015, MCP-016, MCP-017, MCP-018, MCP-019, MCP-020
**References:** LLM06 (Excessive Agency)

> Shares the structural-hygiene threat model with
Expand All @@ -44,8 +74,14 @@ references: [LLM06]
The structural hygiene of Model Context Protocol tool registrations — the
Python decorator forms (`@server.tool` / `@mcp.tool` / `.register_tool`,
predicate `mcp_tool` kind) and the TypeScript `@modelcontextprotocol/sdk`
`server.registerTool(...)` / `server.tool(...)` forms. MCP-001/002/003 are the
Python rules; MCP-011 is the TypeScript description rule.
`server.registerTool(...)` / `server.tool(...)` forms, the Go SDKs
(mark3labs/mcp-go's `mcp.NewTool(...)` and the official go-sdk's
`mcp.AddTool(server, &mcp.Tool{...}, fn)`), the official C# SDK's
`[McpServerTool]`-attributed methods, and the PHP SDKs' (official mcp/sdk +
community php-mcp/server) `#[McpTool]`-attributed methods. MCP-001/002/003 are
the Python rules; MCP-011 is the TypeScript description rule; MCP-015/016 are the
Go rules; MCP-017 (no description) and MCP-018 (ambiguous name) are the C# rules;
MCP-019 (no description) and MCP-020 (ambiguous name) are the PHP rules.

## Why definition hygiene is sharper for MCP than for an in-process SDK

Expand Down Expand Up @@ -107,6 +143,74 @@ the registration config's `description` is the model's routing signal. Confidenc
0.85 (vs MCP-001's 0.9) reflects that the TypeScript capture can miss a
description supplied through an unusual expression shape.

### MCP-015 — Go MCP tool has no description (Severity: low, Confidence: 0.85, Fix type: code)

**What we detect:** a Go MCP tool whose description is empty
(`has_docstring: false`, reading the captured `Description`) — a mark3labs
`mcp.NewTool("name", ...)` with no `mcp.WithDescription(...)` option, or an
official-SDK `mcp.Tool{...}` with no `Description` field.

**Why it is flaggable:** identical mechanism to MCP-001 / MCP-011 on the Go
SDKs — the description is what the server advertises to connecting clients as the
model's routing signal. Confidence 0.85 mirrors the other description rules; the
residual gap is a description supplied through a non-literal expression, captured
as empty.

### MCP-016 — Ambiguous Go MCP tool name (Severity: low, Confidence: 0.85, Fix type: code)

**What we detect:** a Go MCP tool whose name — the first argument to
`mcp.NewTool(...)`, or the `Name` field of an `mcp.Tool` — is in the fixed
ambiguous set (`process`, `handle`, `run`, ...) via `name_in`.

**Why it is flaggable:** identical to MCP-003 — an ambiguous name gives the model
no intent signal and collides across servers in a shared session, and the cost is
paid by every uncontrolled consumer of the published catalog.

### MCP-017 — C# MCP tool has no description (Severity: low, Confidence: 0.85, Fix type: code)

**What we detect:** an `[McpServerTool]`-attributed C# method with no co-located
`[Description("...")]` attribute (`has_docstring: false`, reading the captured
`Description`).

**Why it is flaggable:** identical mechanism to MCP-001 / MCP-011 / MCP-015 on
the official ModelContextProtocol C# SDK — `[Description]` is what the server
advertises to connecting clients as the model's routing signal. Confidence 0.85
mirrors the other description rules.

### MCP-018 — Ambiguous C# MCP tool name (Severity: low, Confidence: 0.85, Fix type: code)

**What we detect:** an `[McpServerTool]` method whose name (the method name — the
SDK default) is in the fixed ambiguous set (`process`, `handle`, `run`, ...) via
`name_in` (case-insensitive, so PascalCase `Process` matches).

**Why it is flaggable:** identical to MCP-003 / MCP-016 — an ambiguous name gives
the model no intent signal and collides across servers in a shared session.

### MCP-019 — PHP MCP tool has no description (Severity: low, Confidence: 0.85, Fix type: code)

**What we detect:** a `#[McpTool]`-attributed PHP method whose attribute carries
no `description:` argument (`has_docstring: false`, reading the captured
`Description`). The smacker tree-sitter-php grammar parses a single-line `#[...]`
attribute as a comment, so discovery reads the `description:` argument out of the
attribute's comment text.

**Why it is flaggable:** identical mechanism to MCP-001 / MCP-011 / MCP-015 /
MCP-017 on the PHP MCP SDKs — the attribute's `description:` is what the server
advertises to connecting clients as the model's routing signal. Confidence 0.85
mirrors the other description rules; the residual gap is a multi-line attribute
form, which discovery does not currently read.

### MCP-020 — Ambiguous PHP MCP tool name (Severity: low, Confidence: 0.85, Fix type: code)

**What we detect:** a `#[McpTool]` method whose name — the attribute's `name:`
argument, or the method name when that argument is omitted — is in the fixed
ambiguous set (`process`, `handle`, `run`, ...) via `name_in`.

**Why it is flaggable:** identical to MCP-003 / MCP-016 / MCP-018 — an ambiguous
name gives the model no intent signal and collides across servers in a shared
session, and the cost is paid by every uncontrolled consumer of the published
catalog.

---

## What this policy does not cover
Expand All @@ -116,4 +220,16 @@ and the low-level `Server` + `setRequestHandler` authoring shape (tools there ar
returned from a `ListTools` handler, not named at a registration call site, so no
per-tool definition is extracted). Resource and prompt registrations
(`@mcp.resource` / `@mcp.prompt`, `registerResource` / `registerPrompt`) are not
yet discovered.
yet discovered. For Go, untyped-params has no analog (Go is statically typed, so
there is no MCP-002 equivalent), the official SDK's handler-struct input schema
and metoro-io/mcp-golang's reflection-based `RegisterTool` are not yet extracted,
and body-fact rules (shell / SSRF / timeout) await Go AST predicates. For C#,
untyped-params likewise has no analog (C# is statically typed), the
`[McpServerTool(Name = "...")]` name override is not read, and body-fact rules
plus the Semantic Kernel `[KernelFunction]` / AutoGen `[Function]` shapes await
later work. For PHP, the multi-line `#[...]` attribute form is not read (the
grammar parses single-line attributes as comments), `#[McpResource]` /
`#[McpPrompt]` are not discovered, and body-fact rules await PHP AST predicates;
unlike Go and C#, PHP type hints are optional, so an untyped-params analog of
MCP-002 *is* meaningful — discovery already captures `HasTypedParams`, and that
rule is a deliberate fast-follow rather than not applicable.
Loading
Loading