File size: 5,463 Bytes
a21c316
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
use serde_json::Value;
use super::super::tool_adapter::{ToolAdapter, append_hint_to_schema};

/// Pencil MCP 工具适配器
/// 
/// 为 Pencil 绘图工具提供特定的 Schema 优化:
/// 1. 处理非标准的视觉属性字段 (cornerRadius, strokeWidth 等)
/// 2. 优化文件路径参数的描述
/// 3. 添加 Pencil 特定的使用提示
pub struct PencilAdapter;

impl ToolAdapter for PencilAdapter {
    fn matches(&self, tool_name: &str) -> bool {
        tool_name.starts_with("mcp__pencil__")
    }
    
    fn pre_process(&self, schema: &mut Value) -> Result<(), String> {
        if let Value::Object(map) = schema {
            // 1. 处理视觉属性字段
            self.handle_visual_properties(map);
            
            // 2. 优化文件路径参数
            self.optimize_path_parameters(map);
        }
        Ok(())
    }
}

impl PencilAdapter {
    /// 处理 Pencil 特有的视觉属性字段
    fn handle_visual_properties(&self, map: &mut serde_json::Map<String, Value>) {
        // Pencil 使用的非标准视觉属性
        let visual_props = ["cornerRadius", "strokeWidth", "opacity", "rotation"];
        
        for prop in visual_props {
            if map.contains_key(prop) {
                let hint = format!("Visual property: {}", prop);
                append_hint_to_schema(&mut Value::Object(map.clone()), &hint);
            }
        }
        
        // 处理 properties 中的视觉属性
        if let Some(Value::Object(props)) = map.get_mut("properties") {
            for (key, value) in props.iter_mut() {
                if visual_props.contains(&key.as_str()) {
                    if let Value::Object(prop_map) = value {
                        prop_map
                            .entry("description".to_string())
                            .and_modify(|d| {
                                if let Value::String(s) = d {
                                    if !s.contains("visual property") {
                                        *s = format!("{} (visual property for UI elements)", s);
                                    }
                                }
                            })
                            .or_insert_with(|| {
                                Value::String("Visual property for UI elements".to_string())
                            });
                    }
                }
            }
        }
    }
    
    /// 优化文件路径相关参数的描述
    fn optimize_path_parameters(&self, map: &mut serde_json::Map<String, Value>) {
        if let Some(Value::Object(props)) = map.get_mut("properties") {
            for (key, value) in props.iter_mut() {
                // 识别路径相关参数
                let is_path_param = key.contains("path") 
                    || key.contains("file") 
                    || key.contains("File")
                    || key.contains("Path");
                
                if is_path_param {
                    if let Value::Object(prop_map) = value {
                        prop_map
                            .entry("description".to_string())
                            .and_modify(|d| {
                                if let Value::String(s) = d {
                                    if !s.contains("absolute path") {
                                        *s = format!("{} (use absolute path, e.g., /path/to/file.pen)", s);
                                    }
                                }
                            })
                            .or_insert_with(|| {
                                Value::String("File path (use absolute path)".to_string())
                            });
                    }
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::json;

    #[test]
    fn test_pencil_adapter_matches() {
        let adapter = PencilAdapter;
        assert!(adapter.matches("mcp__pencil__create_shape"));
        assert!(adapter.matches("mcp__pencil__update_properties"));
        assert!(!adapter.matches("mcp__filesystem__read"));
    }

    #[test]
    fn test_visual_properties_handling() {
        let adapter = PencilAdapter;
        let mut schema = json!({
            "type": "object",
            "properties": {
                "cornerRadius": {
                    "type": "number"
                },
                "color": {
                    "type": "string"
                }
            }
        });
        
        adapter.pre_process(&mut schema).unwrap();
        
        // 验证 cornerRadius 的 description 被添加
        assert!(schema["properties"]["cornerRadius"]["description"].is_string());
        let desc = schema["properties"]["cornerRadius"]["description"]
            .as_str()
            .unwrap();
        assert!(desc.contains("Visual property"));
    }

    #[test]
    fn test_path_parameter_optimization() {
        let adapter = PencilAdapter;
        let mut schema = json!({
            "type": "object",
            "properties": {
                "filePath": {
                    "type": "string",
                    "description": "Path to the file"
                }
            }
        });
        
        adapter.pre_process(&mut schema).unwrap();
        
        let desc = schema["properties"]["filePath"]["description"]
            .as_str()
            .unwrap();
        assert!(desc.contains("absolute path"));
    }
}