Summary
Add a ToolCapability dataclass to MCPToolDefinition that describes the semantic properties of each tool — enabling the engine to understand what a tool does, not just its name.
Background
Currently MCPToolDefinition (src/ouroboros/mcp/types.py:90) only has:
name, description, parameters, server_name
The engine treats all tools equally — it cannot distinguish Read (safe, idempotent) from Bash (potentially destructive, non-idempotent). This is the "hammer, knife, ruler" problem identified by the maintainer (JQ) in the AS-IS architecture discussion.
Proposed Design
# src/ouroboros/mcp/types.py
class SideEffect(StrEnum):
NONE = "none" # Pure computation (no external state)
READ = "read" # Reads external state (WebSearch, ListFiles)
WRITE = "write" # Modifies external state (Edit, CreateFile)
DESTRUCTIVE = "destructive" # Hard to reverse (Delete, Bash rm)
class ApprovalLevel(StrEnum):
AUTO = "auto" # Execute without confirmation
SUGGEST = "suggest" # Log warning, proceed
REQUIRED = "required" # Block until explicit approval
@dataclass(frozen=True, slots=True)
class ToolCapability:
"""Semantic metadata for tool policy decisions."""
side_effect: SideEffect = SideEffect.NONE
parallel_safe: bool = True
approval: ApprovalLevel = ApprovalLevel.AUTO
idempotent: bool = True
Add to MCPToolDefinition:
@dataclass(frozen=True, slots=True)
class MCPToolDefinition:
name: str
description: str
parameters: tuple[MCPToolParameter, ...] = field(default_factory=tuple)
server_name: str | None = None
capability: ToolCapability | None = None # NEW — optional for backward compat
Why These 4 Fields
Following ttaco's community feedback: "Start with a small set of enforceable fields... then expand only after validating against real usage."
| Field |
Answers |
Example |
side_effect |
"Does this tool change external state?" |
Bash(rm) → destructive |
parallel_safe |
"Can multiple instances run concurrently?" |
DB write → false |
approval |
"Does this need human confirmation?" |
delete_branch → required |
idempotent |
"Is re-execution safe?" |
Read → yes, POST → no |
Files to Modify
src/ouroboros/mcp/types.py — Add SideEffect, ApprovalLevel, ToolCapability, extend MCPToolDefinition
tests/unit/mcp/test_types.py — Unit tests for new types
- Handler definitions (each handler's
.definition property) — Annotate with appropriate capability
Acceptance Criteria
Dependencies
None — this is the foundation for Policy Plane (#mid-term-1) and Role Envelope (#mid-term-3).
Priority
Short-term — Can be implemented immediately with no architectural changes.
Summary
Add a
ToolCapabilitydataclass toMCPToolDefinitionthat describes the semantic properties of each tool — enabling the engine to understand what a tool does, not just its name.Background
Currently
MCPToolDefinition(src/ouroboros/mcp/types.py:90) only has:name,description,parameters,server_nameThe engine treats all tools equally — it cannot distinguish
Read(safe, idempotent) fromBash(potentially destructive, non-idempotent). This is the "hammer, knife, ruler" problem identified by the maintainer (JQ) in the AS-IS architecture discussion.Proposed Design
Add to
MCPToolDefinition:Why These 4 Fields
Following ttaco's community feedback: "Start with a small set of enforceable fields... then expand only after validating against real usage."
side_effectBash(rm)→ destructiveparallel_safeapprovaldelete_branch→ requiredidempotentRead→ yes,POST→ noFiles to Modify
src/ouroboros/mcp/types.py— AddSideEffect,ApprovalLevel,ToolCapability, extendMCPToolDefinitiontests/unit/mcp/test_types.py— Unit tests for new types.definitionproperty) — Annotate with appropriate capabilityAcceptance Criteria
ToolCapabilitydataclass defined with 4 fieldsMCPToolDefinition.capabilityis optional (backward compatible)to_input_schema()unaffected (capability is metadata, not input schema)Dependencies
None — this is the foundation for Policy Plane (#mid-term-1) and Role Envelope (#mid-term-3).
Priority
Short-term — Can be implemented immediately with no architectural changes.