Skip to content
This repository was archived by the owner on Aug 7, 2025. It is now read-only.

Commit bef22dc

Browse files
lukeclaude
andcommitted
docs: Add comprehensive Tauri plugin system analysis and MCP implementation plan
- Analyze Tauri plugin permissions, capabilities, and limitations - Document findings on plugin discovery and inter-plugin communication - Create detailed MCP server plugin implementation plan - Include security considerations and phased approach 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f4d8152 commit bef22dc

File tree

2 files changed

+471
-0
lines changed

2 files changed

+471
-0
lines changed

mcp-plugin-implementation-plan.md

Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
# MCP Server Plugin Implementation Plan for Tauri
2+
3+
## Overview
4+
5+
This document outlines a concrete implementation plan for adding MCP (Model Context Protocol) server capabilities to the Tauri application, enabling AI agents to interact with all available plugins.
6+
7+
## Architecture Design
8+
9+
### Core Components
10+
11+
1. **MCP Server Plugin** (`tauri-plugin-mcp-server`)
12+
- Implements MCP protocol using `jsonrpsee` or custom JSON-RPC
13+
- Manages client connections and authentication
14+
- Provides introspection and command routing
15+
16+
2. **Plugin Discovery System**
17+
- Event-based plugin registration
18+
- Static configuration fallback
19+
- Runtime capability detection
20+
21+
3. **Command Proxy Layer**
22+
- Translates MCP tool calls to Tauri commands
23+
- Handles async command execution
24+
- Manages response transformation
25+
26+
## Implementation Steps
27+
28+
### Phase 1: Basic MCP Server Infrastructure
29+
30+
```rust
31+
// tauri-plugin-mcp-server/src/lib.rs
32+
pub struct McpServerPlugin<R: Runtime> {
33+
server: Arc<Mutex<Option<McpServer>>>,
34+
app_handle: AppHandle<R>,
35+
plugin_registry: Arc<Mutex<PluginRegistry>>,
36+
}
37+
38+
pub struct PluginRegistry {
39+
plugins: HashMap<String, PluginMetadata>,
40+
command_map: HashMap<String, CommandInfo>,
41+
}
42+
43+
#[derive(Serialize, Deserialize)]
44+
pub struct PluginMetadata {
45+
name: String,
46+
version: String,
47+
commands: Vec<String>,
48+
permissions: Vec<String>,
49+
}
50+
```
51+
52+
### Phase 2: Plugin Discovery Implementation
53+
54+
#### Option A: Static Discovery (Simpler, More Secure)
55+
```rust
56+
impl McpServerPlugin {
57+
fn discover_static_plugins(&self) -> Result<()> {
58+
// Use the existing get_enabled_plugins() pattern
59+
let enabled = self.get_enabled_plugins();
60+
61+
// Hardcode known plugin commands based on permissions
62+
if enabled.workerd {
63+
self.register_plugin("workerd", vec![
64+
"check_workerd_installed",
65+
"start_worker",
66+
"stop_worker",
67+
// ... other commands from permissions file
68+
]);
69+
}
70+
71+
if enabled.podman {
72+
self.register_plugin("podman", vec![
73+
"check_podman_installed",
74+
"create_container",
75+
// ...
76+
]);
77+
}
78+
79+
Ok(())
80+
}
81+
}
82+
```
83+
84+
#### Option B: Dynamic Discovery (More Flexible)
85+
```rust
86+
impl McpServerPlugin {
87+
fn setup_dynamic_discovery<R: Runtime>(&self, app: &AppHandle<R>) {
88+
// Emit discovery request
89+
app.emit("mcp:discover_plugins", json!({}));
90+
91+
// Listen for plugin announcements
92+
app.listen("plugin:announce", |event| {
93+
if let Ok(metadata) = serde_json::from_str::<PluginMetadata>(&event.payload()) {
94+
self.plugin_registry.lock().unwrap().register(metadata);
95+
}
96+
});
97+
}
98+
}
99+
```
100+
101+
### Phase 3: MCP Protocol Implementation
102+
103+
```rust
104+
// MCP tool definition
105+
#[derive(Serialize, Deserialize)]
106+
struct McpTool {
107+
name: String,
108+
description: String,
109+
input_schema: serde_json::Value,
110+
}
111+
112+
// Convert Tauri commands to MCP tools
113+
impl McpServerPlugin {
114+
fn commands_to_tools(&self) -> Vec<McpTool> {
115+
let registry = self.plugin_registry.lock().unwrap();
116+
let mut tools = vec![];
117+
118+
for (plugin_name, metadata) in &registry.plugins {
119+
for command in &metadata.commands {
120+
tools.push(McpTool {
121+
name: format!("{}:{}", plugin_name, command),
122+
description: format!("Execute {} command from {} plugin", command, plugin_name),
123+
input_schema: self.get_command_schema(plugin_name, command),
124+
});
125+
}
126+
}
127+
128+
tools
129+
}
130+
}
131+
```
132+
133+
### Phase 4: Command Routing and Execution
134+
135+
```rust
136+
#[async_trait]
137+
impl McpHandler for McpServerPlugin {
138+
async fn handle_tool_call(&self, name: &str, args: Value) -> Result<Value> {
139+
// Parse plugin:command format
140+
let parts: Vec<&str> = name.split(':').collect();
141+
if parts.len() != 2 {
142+
return Err(Error::InvalidToolName);
143+
}
144+
145+
let (plugin, command) = (parts[0], parts[1]);
146+
147+
// Route to frontend for execution
148+
let result = self.app_handle
149+
.emit("mcp:execute_command", json!({
150+
"plugin": plugin,
151+
"command": command,
152+
"args": args
153+
}))
154+
.await?;
155+
156+
// Wait for response via event listener
157+
let response = self.await_command_response(plugin, command).await?;
158+
159+
Ok(response)
160+
}
161+
}
162+
```
163+
164+
### Phase 5: Frontend Integration
165+
166+
```typescript
167+
// Frontend command router
168+
import { invoke } from '@tauri-apps/api/core';
169+
import { listen } from '@tauri-apps/api/event';
170+
171+
class McpCommandRouter {
172+
async initialize() {
173+
// Listen for MCP command execution requests
174+
await listen('mcp:execute_command', async (event) => {
175+
const { plugin, command, args } = event.payload;
176+
177+
try {
178+
// Dynamically invoke the command
179+
const result = await invoke(`plugin:${plugin}:${command}`, args);
180+
181+
// Send response back to MCP server
182+
await emit('mcp:command_response', {
183+
plugin,
184+
command,
185+
result,
186+
success: true
187+
});
188+
} catch (error) {
189+
await emit('mcp:command_response', {
190+
plugin,
191+
command,
192+
error: error.message,
193+
success: false
194+
});
195+
}
196+
});
197+
}
198+
}
199+
```
200+
201+
## Security Considerations
202+
203+
### 1. Authentication and Authorization
204+
```rust
205+
pub struct McpAuthConfig {
206+
require_auth: bool,
207+
allowed_clients: Vec<String>,
208+
token_validation: Box<dyn Fn(&str) -> bool>,
209+
}
210+
211+
impl McpServerPlugin {
212+
fn validate_client(&self, token: &str) -> Result<ClientContext> {
213+
// Implement token validation
214+
// Map client to allowed permissions
215+
Ok(ClientContext {
216+
client_id: "validated_client",
217+
allowed_plugins: vec!["workerd", "podman"],
218+
rate_limit: Some(100), // requests per minute
219+
})
220+
}
221+
}
222+
```
223+
224+
### 2. Permission Enforcement
225+
```rust
226+
impl McpServerPlugin {
227+
fn check_permission(&self, client: &ClientContext, plugin: &str, command: &str) -> Result<()> {
228+
// Check if client can access this plugin
229+
if !client.allowed_plugins.contains(&plugin.to_string()) {
230+
return Err(Error::PermissionDenied);
231+
}
232+
233+
// Check specific command permission
234+
let permission = format!("{}:allow-{}", plugin, command.replace('_', "-"));
235+
if !self.has_permission(&permission) {
236+
return Err(Error::CommandNotAllowed);
237+
}
238+
239+
Ok(())
240+
}
241+
}
242+
```
243+
244+
### 3. Network Security
245+
```rust
246+
pub struct McpServerConfig {
247+
bind_address: String,
248+
tls_config: Option<TlsConfig>,
249+
max_connections: usize,
250+
}
251+
252+
impl Default for McpServerConfig {
253+
fn default() -> Self {
254+
Self {
255+
bind_address: "127.0.0.1:0".to_string(), // Local only by default
256+
tls_config: None, // Require explicit TLS configuration
257+
max_connections: 10,
258+
}
259+
}
260+
}
261+
```
262+
263+
## Testing Strategy
264+
265+
### 1. Unit Tests
266+
- Test plugin discovery mechanisms
267+
- Test command routing logic
268+
- Test permission enforcement
269+
270+
### 2. Integration Tests
271+
```rust
272+
#[cfg(test)]
273+
mod tests {
274+
#[test]
275+
async fn test_mcp_tool_execution() {
276+
let app = create_test_app();
277+
let mcp_plugin = McpServerPlugin::new();
278+
279+
// Register test plugin
280+
mcp_plugin.register_plugin("test", vec!["echo"]);
281+
282+
// Execute via MCP
283+
let result = mcp_plugin.handle_tool_call("test:echo", json!({"message": "hello"})).await;
284+
285+
assert_eq!(result.unwrap(), json!({"response": "hello"}));
286+
}
287+
}
288+
```
289+
290+
### 3. Security Tests
291+
- Test unauthorized access attempts
292+
- Test rate limiting
293+
- Test input validation
294+
295+
## Deployment Considerations
296+
297+
### 1. Feature Flag Integration
298+
```toml
299+
[features]
300+
plugin-mcp-server = ["jsonrpsee", "tokio"]
301+
mcp-dynamic-discovery = [] # Optional dynamic discovery
302+
mcp-tls = ["rustls"] # Optional TLS support
303+
```
304+
305+
### 2. Configuration
306+
```json
307+
{
308+
"mcp_server": {
309+
"enabled": true,
310+
"bind_address": "127.0.0.1:8080",
311+
"require_auth": true,
312+
"allowed_origins": ["http://localhost:*"],
313+
"plugin_whitelist": ["workerd", "podman", "bun"]
314+
}
315+
}
316+
```
317+
318+
## Timeline
319+
320+
- **Week 1**: Basic MCP server infrastructure and static plugin discovery
321+
- **Week 2**: Command routing and frontend integration
322+
- **Week 3**: Security implementation and testing
323+
- **Week 4**: Dynamic discovery (optional) and documentation
324+
325+
## Risks and Mitigations
326+
327+
| Risk | Impact | Mitigation |
328+
|------|--------|------------|
329+
| Security vulnerabilities | High | Mandatory auth, permission checks, audit logging |
330+
| Performance overhead | Medium | Async execution, connection pooling, caching |
331+
| Plugin compatibility | Low | Fallback to static discovery, version checking |
332+
| Debugging complexity | Medium | Comprehensive logging, trace IDs, error context |
333+
334+
## Conclusion
335+
336+
This implementation plan provides a secure, scalable approach to adding MCP server capabilities to Tauri applications. The phased approach allows for incremental development while maintaining security throughout.

0 commit comments

Comments
 (0)