feat(M8): implement MCP (Model Context Protocol) integration with stdio transport
Milestone M8 implementation adds MCP integration for connecting to external tool servers and resources. New crate: crates/integration/mcp-client - JSON-RPC 2.0 protocol implementation - Stdio transport for spawning MCP server processes - Capability negotiation (initialize handshake) - Tool operations: * tools/list: List available tools from server * tools/call: Invoke tools with arguments - Resource operations: * resources/list: List available resources * resources/read: Read resource contents - Async design using tokio for non-blocking I/O MCP Client Features: - McpClient: Main client with subprocess management - ServerCapabilities: Capability discovery - McpTool: Tool definitions with JSON schema - McpResource: Resource definitions with URI/mime-type - Automatic request ID management - Error handling with proper JSON-RPC error codes Permission Integration: - Added Tool::Mcp to permission system - Pattern matching support for mcp__server__tool format * "filesystem__*" matches all filesystem server tools * "filesystem__read_file" matches specific tool - MCP requires Ask permission in Plan/AcceptEdits modes - MCP allowed in Code mode (like Bash) Tests added (3 new tests with mock Python servers): 1. mcp_server_capability_negotiation - Verifies initialize handshake 2. mcp_tool_invocation - Tests tool listing and calling 3. mcp_resource_reads - Tests resource listing and reading Permission tests added (2 new tests): 1. mcp_server_pattern_matching - Verifies server-level wildcards 2. mcp_exact_tool_matching - Verifies tool-level exact matching All 75 tests passing (up from 68). Note: CLI integration deferred - MCP infrastructure is in place and fully tested. Future work will add MCP server configuration and CLI commands to invoke MCP tools. Protocol: Implements MCP 2024-11-05 specification over stdio transport. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@ pub enum Tool {
|
||||
SlashCommand,
|
||||
Task,
|
||||
TodoWrite,
|
||||
Mcp,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
@@ -136,7 +137,7 @@ impl PermissionManager {
|
||||
// Edit/Write operations allowed
|
||||
Tool::Edit | Tool::Write | Tool::NotebookEdit => PermissionDecision::Allow,
|
||||
// Bash and other dangerous operations still require asking
|
||||
Tool::Bash | Tool::WebFetch | Tool::WebSearch => PermissionDecision::Ask,
|
||||
Tool::Bash | Tool::WebFetch | Tool::WebSearch | Tool::Mcp => PermissionDecision::Ask,
|
||||
// Utility tools allowed
|
||||
Tool::TodoWrite | Tool::SlashCommand | Tool::Task => PermissionDecision::Allow,
|
||||
},
|
||||
@@ -209,4 +210,31 @@ mod tests {
|
||||
assert!(rule.matches(Tool::Read, Some("any context")));
|
||||
assert!(rule.matches(Tool::Read, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mcp_server_pattern_matching() {
|
||||
// Allow all tools from a specific server
|
||||
let rule = PermissionRule {
|
||||
tool: Tool::Mcp,
|
||||
pattern: Some("filesystem__*".to_string()),
|
||||
action: Action::Allow,
|
||||
};
|
||||
|
||||
assert!(rule.matches(Tool::Mcp, Some("filesystem__read_file")));
|
||||
assert!(rule.matches(Tool::Mcp, Some("filesystem__write_file")));
|
||||
assert!(!rule.matches(Tool::Mcp, Some("database__query")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mcp_exact_tool_matching() {
|
||||
// Allow only a specific tool from a server
|
||||
let rule = PermissionRule {
|
||||
tool: Tool::Mcp,
|
||||
pattern: Some("filesystem__read_file".to_string()),
|
||||
action: Action::Allow,
|
||||
};
|
||||
|
||||
assert!(rule.matches(Tool::Mcp, Some("filesystem__read_file")));
|
||||
assert!(!rule.matches(Tool::Mcp, Some("filesystem__write_file")));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user