#
tokens: 15266/50000 15/15 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .gitignore
├── README.md
├── stdio
│   ├── .gitignore
│   ├── .python-version
│   ├── pyproject.toml
│   ├── README.md
│   ├── src
│   │   └── minium_mcp_server
│   │       ├── __init__.py
│   │       └── server.py
│   └── uv.lock
└── webapi
    ├── .gitignore
    ├── .python-version
    ├── pyproject.toml
    ├── README.md
    ├── src
    │   └── minium_mcp_server
    │       ├── __init__.py
    │       └── server.py
    ├── uv.lock
    └── web.py
```

# Files

--------------------------------------------------------------------------------
/stdio/.python-version:
--------------------------------------------------------------------------------

```
1 | 3.12
2 | 
```

--------------------------------------------------------------------------------
/webapi/.python-version:
--------------------------------------------------------------------------------

```
1 | 3.12
2 | 
```

--------------------------------------------------------------------------------
/stdio/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # Python-generated files
 2 | __pycache__/
 3 | *.py[oc]
 4 | build/
 5 | dist/
 6 | wheels/
 7 | *.egg-info
 8 | 
 9 | # Virtual environments
10 | .venv
11 | 
```

--------------------------------------------------------------------------------
/webapi/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # Python-generated files
 2 | __pycache__/
 3 | *.py[oc]
 4 | build/
 5 | dist/
 6 | wheels/
 7 | *.egg-info
 8 | 
 9 | # Virtual environments
10 | .venv
11 | 
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # Python-generated files
 2 | **/__pycache__/
 3 | **/*.py[oc]
 4 | **/build/
 5 | **/dist/
 6 | **/wheels/
 7 | **/*.egg-info
 8 | **/.DS_Store
 9 | 
10 | # Virtual environments
11 | **/.venv
12 | 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
1 | # minium-mcp-server
2 | 
3 | npx @modelcontextprotocol/inspector uv --directory /Users/roy.yan/Documents/3.Roy/python/minium_mcp_server/webapi run minium-mcp-server
```

--------------------------------------------------------------------------------
/webapi/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # minium-mcp-server MCP server
  2 | 
  3 | A MCP server project
  4 | 
  5 | ## Components
  6 | 
  7 | ### Resources
  8 | 
  9 | The server implements a simple note storage system with:
 10 | - Custom note:// URI scheme for accessing individual notes
 11 | - Each note resource has a name, description and text/plain mimetype
 12 | 
 13 | ### Prompts
 14 | 
 15 | The server provides a single prompt:
 16 | - summarize-notes: Creates summaries of all stored notes
 17 |   - Optional "style" argument to control detail level (brief/detailed)
 18 |   - Generates prompt combining all current notes with style preference
 19 | 
 20 | ### Tools
 21 | 
 22 | The server implements one tool:
 23 | - add-note: Adds a new note to the server
 24 |   - Takes "name" and "content" as required string arguments
 25 |   - Updates server state and notifies clients of resource changes
 26 | 
 27 | ## Configuration
 28 | 
 29 | [TODO: Add configuration details specific to your implementation]
 30 | 
 31 | ## Quickstart
 32 | 
 33 | ### Install
 34 | 
 35 | #### Claude Desktop
 36 | 
 37 | On MacOS: `~/Library/Application\ Support/Claude/claude_desktop_config.json`
 38 | On Windows: `%APPDATA%/Claude/claude_desktop_config.json`
 39 | 
 40 | <details>
 41 |   <summary>Development/Unpublished Servers Configuration</summary>
 42 |   ```
 43 |   "mcpServers": {
 44 |     "minium-mcp-server": {
 45 |       "command": "uv",
 46 |       "args": [
 47 |         "--directory",
 48 |         "--path--/minium-mcp-server/webapi",
 49 |         "run",
 50 |         "minium-mcp-server"
 51 |       ]
 52 |     }
 53 |   }
 54 |   ```
 55 | </details>
 56 | 
 57 | <details>
 58 |   <summary>Published Servers Configuration</summary>
 59 |   ```
 60 |   "mcpServers": {
 61 |     "minium-mcp-server": {
 62 |       "command": "uvx",
 63 |       "args": [
 64 |         "minium-mcp-server"
 65 |       ]
 66 |     }
 67 |   }
 68 |   ```
 69 | </details>
 70 | 
 71 | ## Development
 72 | 
 73 | ### Building and Publishing
 74 | 
 75 | To prepare the package for distribution:
 76 | 
 77 | 1. Sync dependencies and update lockfile:
 78 | ```bash
 79 | uv sync
 80 | ```
 81 | 
 82 | 2. Build package distributions:
 83 | ```bash
 84 | uv build
 85 | ```
 86 | 
 87 | This will create source and wheel distributions in the `dist/` directory.
 88 | 
 89 | 3. Publish to PyPI:
 90 | ```bash
 91 | uv publish
 92 | ```
 93 | 
 94 | Note: You'll need to set PyPI credentials via environment variables or command flags:
 95 | - Token: `--token` or `UV_PUBLISH_TOKEN`
 96 | - Or username/password: `--username`/`UV_PUBLISH_USERNAME` and `--password`/`UV_PUBLISH_PASSWORD`
 97 | 
 98 | ### Debugging
 99 | 
100 | Since MCP servers run over stdio, debugging can be challenging. For the best debugging
101 | experience, we strongly recommend using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector).
102 | 
103 | 
104 | You can launch the MCP Inspector via [`npm`](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) with this command:
105 | 
106 | ```bash
107 | npx @modelcontextprotocol/inspector uv --directory --path--/minium-mcp-server/webapi run minium-mcp-server
108 | ```
109 | 
110 | 
111 | Upon launching, the Inspector will display a URL that you can access in your browser to begin debugging.
```

--------------------------------------------------------------------------------
/stdio/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # minium-mcp-server MCP server
  2 | 
  3 | A MCP server project
  4 | 
  5 | ## Components
  6 | 
  7 | ### Resources
  8 | 
  9 | The server implements a simple note storage system with:
 10 | - Custom note:// URI scheme for accessing individual notes
 11 | - Each note resource has a name, description and text/plain mimetype
 12 | 
 13 | ### Prompts
 14 | 
 15 | The server provides a single prompt:
 16 | - summarize-notes: Creates summaries of all stored notes
 17 |   - Optional "style" argument to control detail level (brief/detailed)
 18 |   - Generates prompt combining all current notes with style preference
 19 | 
 20 | ### Tools
 21 | 
 22 | The server implements one tool:
 23 | - add-note: Adds a new note to the server
 24 |   - Takes "name" and "content" as required string arguments
 25 |   - Updates server state and notifies clients of resource changes
 26 | 
 27 | ## Configuration
 28 | 
 29 | [TODO: Add configuration details specific to your implementation]
 30 | 
 31 | ## Quickstart
 32 | 
 33 | ### Install
 34 | 
 35 | #### Claude Desktop
 36 | 
 37 | On MacOS: `~/Library/Application\ Support/Claude/claude_desktop_config.json`
 38 | On Windows: `%APPDATA%/Claude/claude_desktop_config.json`
 39 | 
 40 | <details>
 41 |   <summary>Development/Unpublished Servers Configuration</summary>
 42 |   ```
 43 |   "mcpServers": {
 44 |     "minium-mcp-server": {
 45 |       "command": "uv",
 46 |       "args": [
 47 |         "--directory",
 48 |         "--path--/minium_mcp_server/stdio",
 49 |         "run",
 50 |         "minium-mcp-server",
 51 |         "--path",
 52 |         "--project-path--"
 53 |       ]
 54 |     }
 55 |   }
 56 |   ```
 57 | </details>
 58 | 
 59 | <details>
 60 |   <summary>Published Servers Configuration</summary>
 61 |   ```
 62 |   "mcpServers": {
 63 |     "minium-mcp-server": {
 64 |       "command": "uvx",
 65 |       "args": [
 66 |         "minium-mcp-server"
 67 |       ]
 68 |     }
 69 |   }
 70 |   ```
 71 | </details>
 72 | 
 73 | ## Development
 74 | 
 75 | ### Building and Publishing
 76 | 
 77 | To prepare the package for distribution:
 78 | 
 79 | 1. Sync dependencies and update lockfile:
 80 | ```bash
 81 | uv sync
 82 | ```
 83 | 
 84 | 2. Build package distributions:
 85 | ```bash
 86 | uv build
 87 | ```
 88 | 
 89 | This will create source and wheel distributions in the `dist/` directory.
 90 | 
 91 | 3. Publish to PyPI:
 92 | ```bash
 93 | uv publish
 94 | ```
 95 | 
 96 | Note: You'll need to set PyPI credentials via environment variables or command flags:
 97 | - Token: `--token` or `UV_PUBLISH_TOKEN`
 98 | - Or username/password: `--username`/`UV_PUBLISH_USERNAME` and `--password`/`UV_PUBLISH_PASSWORD`
 99 | 
100 | ### Debugging
101 | 
102 | Since MCP servers run over stdio, debugging can be challenging. For the best debugging
103 | experience, we strongly recommend using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector).
104 | 
105 | 
106 | You can launch the MCP Inspector via [`npm`](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) with this command:
107 | 
108 | ```bash
109 | npx @modelcontextprotocol/inspector uv --directory --path--/minium_mcp_server/stdio run minium-mcp-server --path --project-path--
110 | ```
111 | 
112 | 
113 | Upon launching, the Inspector will display a URL that you can access in your browser to begin debugging.
```

--------------------------------------------------------------------------------
/webapi/src/minium_mcp_server/__init__.py:
--------------------------------------------------------------------------------

```python
1 | from . import server
2 | import asyncio
3 | 
4 | def main():
5 |     """Main entry point for the package."""
6 |     asyncio.run(server.main())
7 | 
8 | # Optionally expose other important items at package level
9 | __all__ = ['main', 'server']
```

--------------------------------------------------------------------------------
/stdio/pyproject.toml:
--------------------------------------------------------------------------------

```toml
 1 | [project]
 2 | name = "minium-mcp-server"
 3 | version = "0.1.0"
 4 | description = "A MCP server project"
 5 | readme = "README.md"
 6 | requires-python = ">=3.12"
 7 | dependencies = [ "mcp>=1.4.1", "minium"]
 8 | [[project.authors]]
 9 | name = "roy.yan"
10 | 
11 | [build-system]
12 | requires = [ "hatchling",]
13 | build-backend = "hatchling.build"
14 | 
15 | [project.scripts]
16 | minium-mcp-server = "minium_mcp_server:main"
17 | 
```

--------------------------------------------------------------------------------
/webapi/pyproject.toml:
--------------------------------------------------------------------------------

```toml
 1 | [project]
 2 | name = "minium-mcp-server"
 3 | version = "0.1.0"
 4 | description = "A MCP server project"
 5 | readme = "README.md"
 6 | requires-python = ">=3.12"
 7 | dependencies = [ "mcp>=1.4.1", "minium", "flask"]
 8 | [[project.authors]]
 9 | name = "roy.yan"
10 | 
11 | [build-system]
12 | requires = [ "hatchling",]
13 | build-backend = "hatchling.build"
14 | 
15 | [project.scripts]
16 | minium-mcp-server = "minium_mcp_server:main"
17 | 
```

--------------------------------------------------------------------------------
/stdio/src/minium_mcp_server/__init__.py:
--------------------------------------------------------------------------------

```python
 1 | from . import server
 2 | import asyncio
 3 | import argparse
 4 | 
 5 | def main():
 6 |     """Main entry point for the package."""
 7 |     parser = argparse.ArgumentParser(description='Minium MCP Server')
 8 |     parser.add_argument('--path', 
 9 |                        default="./MiniProgram",
10 |                        help='Path to WeChat MiniProgram project')
11 |     
12 |     args = parser.parse_args()
13 |     asyncio.run(server.main(args.path))
14 | 
15 | 
16 | # Optionally expose other important items at package level
17 | __all__ = ["main", "server"]
```

--------------------------------------------------------------------------------
/webapi/web.py:
--------------------------------------------------------------------------------

```python
  1 | from flask import Flask, request, jsonify
  2 | import json
  3 | import os
  4 | import sys
  5 | import minium
  6 | import base64
  7 | 
  8 | app = Flask(__name__)
  9 | print("Starting Minium MCP Web Server")
 10 | 
 11 | mini = None
 12 | project_path = ''
 13 | HOST = '0.0.0.0'
 14 | PORT = 9188
 15 | 
 16 | def mini_log_added(message):
 17 |     """
 18 |     小程序 log 监听回调函数
 19 |     将小程序的 log 格式化然后保存起来
 20 |     :param message: {"type": "log|warn|error", "args": [str, ..., ]}
 21 |     :return:
 22 |     """
 23 |     print(f'console.log: {message}')
 24 | 
 25 | @app.route('/api/command', methods=['POST'])
 26 | def handle_command():
 27 |     global mini, project_path
 28 |     try:
 29 |         command = request.json
 30 |         arguments = command['arguments']
 31 | 
 32 |         print(f"COMMAND: {json.dumps(command)}")
 33 |         
 34 |         match command['name']:
 35 |             case "open":
 36 |                 if sys.platform == 'darwin':  # macOS
 37 |                     dev_tool_path = '/Applications/wechatwebdevtools.app/Contents/MacOS/cli'
 38 |                 elif sys.platform == 'win32':  # Windows
 39 |                     dev_tool_path = 'C:/Program Files (x86)/Tencent/微信web开发者工具/cli.bat'
 40 |                 else:
 41 |                     raise Exception("Unsupported operating system")
 42 | 
 43 |                 project_path = arguments['path']
 44 |                 try:
 45 |                     mini = minium.Minium({
 46 |                         "project_path": project_path,
 47 |                         "dev_tool_path": dev_tool_path,
 48 |                         "debug_mode": "error",
 49 |                         "audits": True,
 50 |                         "autofix": True
 51 |                     })
 52 |                     mini.app.enable_log()
 53 |                     mini.app.add_observer("App.logAdded", mini_log_added)
 54 |                 except Exception as e:
 55 |                     # 重试
 56 |                     return jsonify({
 57 |                         "status": "error",
 58 |                         "message": str(e)
 59 |                     })
 60 | 
 61 |                 return jsonify({
 62 |                     "status": "success",
 63 |                     "message": "Opened"
 64 |                 })
 65 | 
 66 |             case "get_system_info":
 67 |                 return jsonify({
 68 |                     "status": "success",
 69 |                     "message": mini.get_system_info()
 70 |                 })
 71 | 
 72 |             case "shutdown":
 73 |                 mini.shutdown()
 74 |                 return jsonify({
 75 |                     "status": "success", 
 76 |                     "message": "Closed"
 77 |                 })
 78 | 
 79 |             case "screen_shot":
 80 |                 output_path = os.path.join(project_path, "screenshots/{}_screen_shot.png".format(mini.app.get_current_page().page_id))
 81 |                 if not os.path.isdir(os.path.dirname(output_path)):
 82 |                     os.makedirs(os.path.dirname(output_path))
 83 |                 if os.path.isfile(output_path):
 84 |                     os.remove(output_path)
 85 |                 mini.app.screen_shot(output_path)
 86 |                 # 获取截图
 87 |                 with open(output_path, "rb") as f:
 88 |                     image = f.read()
 89 |                     # 返回base64编码的图片
 90 |                     image_base64 = base64.b64encode(image).decode('utf-8')
 91 |                 # 删除截图
 92 |                 os.remove(output_path)
 93 |                 return jsonify({
 94 |                     "status": "success",
 95 |                     "type": "image",
 96 |                     "data": image_base64
 97 |                 })
 98 |             
 99 |             case "get_all_pages_path_and_method":
100 |                 all_pages_path = mini.app.get_all_pages_path()
101 |                 with open(os.path.join(project_path, "app.json"), "r", encoding="utf-8") as file:  # 建议指定 encoding
102 |                     app = json.load(file)  # 解析 JSON 文件 → Python 字典/列表
103 |                     tabbar = app.get('tabBar').get('list')
104 |                 
105 |                 result = []
106 |                 for path in all_pages_path:
107 |                     if path in [item.get('pagePath') for item in tabbar]:
108 |                         result.append({
109 |                             "path": f"/{path}",
110 |                             "method": "minium_switch_tab"
111 |                         })
112 |                     else:
113 |                         result.append({
114 |                             "path": f"/{path}",
115 |                             "method": "minium_navigate_to"
116 |                         })
117 |                 return jsonify({
118 |                     "status": "success",
119 |                     "message": f"```json\n{json.dumps(result, indent=4, ensure_ascii=False)}```"
120 |                 })
121 |             
122 |             case "get_navigate_method_of_page":
123 |                 with open(os.path.join(project_path, "app.json"), "r", encoding="utf-8") as file:  # 建议指定 encoding
124 |                     app = json.load(file)  # 解析 JSON 文件 → Python 字典/列表
125 |                     tabbar = app.get('tabBar').get('list')
126 |                     if arguments["path"] in [item.get('pagePath') for item in tabbar]:
127 |                         return jsonify({
128 |                             "status": "success",
129 |                             "message": "minium_switch_tab"
130 |                         })
131 |                     else:
132 |                         return jsonify({    
133 |                             "status": "success",
134 |                             "message": "minium_navigate_to"
135 |                         })
136 | 
137 |             case "go_home":
138 |                 page = mini.app.go_home()
139 |                 if page is not None:
140 |                     return jsonify({
141 |                         "status": "success",
142 |                         "message": "Successfully enter the home page"
143 |                     })
144 |                 else:
145 |                     return jsonify({
146 |                         "status": "error",
147 |                         "message": "Failed to enter the home page"
148 |                     })
149 | 
150 |             case "navigate_to":
151 |                 # 如果arguments中没有params,则默认为空字符串
152 |                 if arguments.get("params") is None:
153 |                     arguments["params"] = {}
154 |                 page = mini.app.navigate_to(arguments["path"], arguments["params"])
155 |                 if page is not None:
156 |                     return jsonify({
157 |                         "status": "success",
158 |                         "message": f"Successfully enter the {arguments['path']} page"
159 |                     })
160 |                 else:
161 |                     return jsonify({
162 |                         "status": "error",
163 |                         "message": f"Failed to enter the {arguments['path']} page"
164 |                     })
165 | 
166 |             case "navigate_back":
167 |                 page = mini.app.navigate_back()
168 |                 if page is not None:
169 |                     return jsonify({
170 |                         "status": "success",
171 |                         "message": "Successful return to the previous page"
172 |                     })
173 |                 else:
174 |                     return jsonify({
175 |                         "status": "error",
176 |                         "message": f"Failed to return to the previous page"
177 |                     })
178 | 
179 |             case "switch_tab":
180 |                 page = mini.app.switch_tab(arguments["path"])
181 |                 if page is not None:
182 |                     return jsonify({
183 |                         "status": "success",
184 |                         "message": "Successfully switch tab"
185 |                     })
186 |                 else:
187 |                     return jsonify({
188 |                         "status": "error",
189 |                         "message": "Failed to switch tab"
190 |                     })
191 | 
192 |             case "redirect_to":
193 |                 if arguments.get("params") is None:
194 |                     arguments["params"] = {}
195 |                 page = mini.app.redirect_to(arguments["path"], arguments["params"])
196 |                 if page is not None:
197 |                     return jsonify({
198 |                         "status": "success",
199 |                         "message": "Successfully redirect to"
200 |                     })
201 |                 else:
202 |                     return jsonify({
203 |                         "status": "error",
204 |                         "message": "Failed to redirect to"
205 |                     })
206 | 
207 |             # case "evaluate":
208 |             #     msg_id = mini.app.evaluate(arguments["code"], arguments["params"], sync=False)
209 |             #     result = mini.app.get_async_response(msg_id, 5)
210 |             #     return jsonify({
211 |             #         "status": "success",
212 |             #         "message": f"Evaluate, Result: {result}"
213 |             #     })
214 | 
215 |             case "call_method":
216 |                 if arguments.get("params") is None:
217 |                     arguments["params"] = {}
218 |                 page = mini.app.get_current_page()
219 |                 result = page.call_method(arguments["method"], arguments["params"])
220 |                 return jsonify({
221 |                     "status": "success",
222 |                     "message": f"Call method, Result: {result}"
223 |                 })
224 | 
225 |             case "page_scroll_to":
226 |                 page = mini.app.get_current_page()
227 |                 page.scroll_to(arguments["top"], arguments["duration"])
228 |                 return jsonify({
229 |                     "status": "success",
230 |                     "message": f"Page scroll to, Top: {arguments['top']}, Duration: {arguments['duration']}"
231 |                 })
232 | 
233 |             case "page_get_wxml":
234 |                 page = mini.app.get_current_page()
235 |                 wxml = page.wxml
236 |                 # 分离wxml和css
237 |                 # 查询最后一个tag的位置
238 |                 last_tag_index = wxml.rfind("</")
239 |                 # 分割wxml和css
240 |                 wxml_content = wxml[:last_tag_index]
241 |                 css_content = wxml[last_tag_index:]
242 |                 first_tag_index = css_content.find(">")
243 |                 wxml_content += css_content[:first_tag_index+1]
244 |                 css_content = css_content[first_tag_index+1:]
245 |                 
246 |                 # 格式化输出
247 | 
248 |                 return jsonify({
249 |                     "status": "success",
250 |                     "message": f"```xml\n{wxml_content}```"
251 |                 })
252 | 
253 |             case "page_get_css":
254 |                 page = mini.app.get_current_page()
255 |                 wxml = page.wxml
256 |                 # 分离wxml和css
257 |                 # 查询最后一个tag的位置
258 |                 last_tag_index = wxml.rfind("</")
259 |                 # 分割wxml和css
260 |                 wxml_content = wxml[:last_tag_index]
261 |                 css_content = wxml[last_tag_index:]
262 |                 first_tag_index = css_content.find(">")
263 |                 wxml_content += css_content[:first_tag_index+1]
264 |                 css_content = css_content[first_tag_index+1:]
265 |                 
266 |                 # 格式化输出
267 | 
268 |                 return jsonify({
269 |                     "status": "success",
270 |                     "message": f"```css\n{css_content}```"
271 |                 })
272 |             
273 |             case "page_get_data":
274 |                 page = mini.app.get_current_page()
275 |                 return jsonify({
276 |                     "status": "success",
277 |                     "message": f"```json\n{page.data}```"
278 |                 })
279 |             
280 |             case "page_set_data":
281 |                 page = mini.app.get_current_page()
282 |                 data = page.data
283 |                 data[arguments['key']] = json.load(arguments['value'])
284 |                 return jsonify({
285 |                     "status": "success",
286 |                     "message": f"```json\n{page.data}```"
287 |                 })
288 | 
289 |             case "tap":
290 |                 page = mini.app.get_current_page()
291 |                 el = page.get_element(arguments["selector"])
292 |                 el.tap()
293 |                 return jsonify({
294 |                     "status": "success",
295 |                     "message": "Tapped"
296 |                 })
297 | 
298 |             case "long_press":
299 |                 page = mini.app.get_current_page()
300 |                 el = page.get_element(arguments["selector"])
301 |                 el.long_press()
302 |                 return jsonify({
303 |                     "status": "success",
304 |                     "message": "Long pressed"
305 |                 })
306 | 
307 |             case "move":
308 |                 page = mini.app.get_current_page()
309 |                 el = page.get_element(arguments["selector"])
310 |                 el.move(arguments["left"], arguments["top"])
311 |                 return jsonify({
312 |                     "status": "success",
313 |                     "message": f"Moved to, Top: {arguments['top']}, Left: {arguments['left']}"
314 |                 })
315 | 
316 |             case "input":
317 |                 page = mini.app.get_current_page()
318 |                 el = page.get_element(arguments["selector"])
319 |                 el.input(arguments["text"])
320 |                 return jsonify({
321 |                     "status": "success",
322 |                     "message": f"Input, Text: {arguments['text']}"
323 |                 })
324 | 
325 |             case "switch":
326 |                 page = mini.app.get_current_page()
327 |                 el = page.get_element(arguments["selector"])
328 |                 el.switch()
329 |                 return jsonify({
330 |                     "status": "success",
331 |                     "message": "Switched"
332 |                 })
333 | 
334 |             case "slide_to":
335 |                 page = mini.app.get_current_page()
336 |                 el = page.get_element(arguments["selector"])
337 |                 el.slide_to(arguments["value"])
338 |                 return jsonify({
339 |                     "status": "success",
340 |                     "message": f"Slided to, Value: {arguments['value']}"
341 |                 })
342 | 
343 |             case "pick":
344 |                 page = mini.app.get_current_page()
345 |                 el = page.get_element(arguments["selector"])
346 |                 el.pick(arguments["option"])
347 |                 return jsonify({
348 |                     "status": "success",
349 |                     "message": f"Picked, Option: {arguments['option']}"
350 |                 })
351 | 
352 |             case _:
353 |                 return jsonify({
354 |                     "status": "error",
355 |                     "message": f"Unknown command: {command['name']}"
356 |                 })
357 | 
358 |     except Exception as e:
359 |         return jsonify({
360 |             "status": "error",
361 |             "message": str(e)
362 |         })
363 | 
364 | if __name__ == "__main__":
365 |     app.run(host=HOST, port=PORT)
366 | 
```

--------------------------------------------------------------------------------
/webapi/src/minium_mcp_server/server.py:
--------------------------------------------------------------------------------

```python
  1 | import os
  2 | import sys
  3 | import logging
  4 | import json
  5 | import asyncio
  6 | import requests
  7 | from mcp.server import NotificationOptions, Server
  8 | from mcp.server.models import InitializationOptions
  9 | 
 10 | from mcp import types
 11 | from typing import Any
 12 | import mcp.server.stdio
 13 | 
 14 | HOST = 'http://127.0.0.1'
 15 | # HOST = 'http://192.168.3.42'
 16 | PORT = 9188
 17 | 
 18 | # reconfigure UnicodeEncodeError prone default (i.e. windows-1252) to utf-8
 19 | if sys.platform == "win32" and os.environ.get('PYTHONIOENCODING') is None:
 20 |     sys.stdin.reconfigure(encoding="utf-8")
 21 |     sys.stdout.reconfigure(encoding="utf-8")
 22 |     sys.stderr.reconfigure(encoding="utf-8")
 23 | 
 24 | logger = logging.getLogger('minium-mcp-server')
 25 | print("Starting Minium MCP Server")
 26 | 
 27 | async def main():
 28 |     server = Server("minium-mcp-server")
 29 | 
 30 |     @server.list_tools()
 31 |     async def handle_list_tools() -> list[types.Tool]:
 32 |         """List available tools"""
 33 |         return [
 34 |             types.Tool(
 35 |                 name="minium_open",
 36 |                 description="Open a project",
 37 |                 inputSchema={
 38 |                     "type": "object",
 39 |                     "properties": {
 40 |                         "path": {"type": "string", "description": "Project path"},
 41 |                     },
 42 |                     "required": ["path"],
 43 |                 }
 44 |             ),
 45 |             types.Tool(
 46 |                 name="minium_get_system_info",
 47 |                 description="Get system info",
 48 |                 inputSchema={
 49 |                     "type": "object",
 50 |                     "properties": {},
 51 |                     "required": [],
 52 |                 }
 53 |             ),
 54 |             types.Tool(
 55 |                 name="minium_shutdown",
 56 |                 description="Shutdown the developer tool",
 57 |                 inputSchema={
 58 |                     "type": "object",
 59 |                     "properties": {},
 60 |                     "required": [],
 61 |                 }
 62 |             ),
 63 |             types.Tool(
 64 |                 name="minium_screen_shot",
 65 |                 description="Take a screenshot of the current page",
 66 |                 inputSchema={
 67 |                     "type": "object",
 68 |                     "properties": {},
 69 |                     "required": [],
 70 |                 },
 71 |             ),
 72 |             types.Tool(
 73 |                 name="minium_get_all_pages_path_and_method",
 74 |                 description="Get paths of all pages and the method used to navigate to them",
 75 |                 inputSchema={
 76 |                     "type": "object",
 77 |                     "properties": {},
 78 |                     "required": [],
 79 |                 }
 80 |             ),
 81 |             types.Tool(
 82 |                 name="minium_get_navigate_method_of_page",
 83 |                 description="Get the method used to navigate to a page",
 84 |                 inputSchema={
 85 |                     "type": "object",
 86 |                     "properties": {
 87 |                         "path": {"type": "string", "description": "Page path"},
 88 |                     },
 89 |                     "required": ["path"],
 90 |                 }
 91 |             ),
 92 |             types.Tool(
 93 |                 name="minium_go_home",
 94 |                 description="Go to the home page",
 95 |                 inputSchema={
 96 |                     "type": "object",
 97 |                     "properties": {},
 98 |                     "required": [],
 99 |                 }
100 |             ),
101 |             types.Tool(
102 |                 name="minium_navigate_to",
103 |                 description="Navigate to a page. Please get path of all pages before using this tool.",
104 |                 inputSchema={
105 |                     "type": "object",
106 |                     "properties": {
107 |                         "path": {"type": "string", "description": "Page path"},
108 |                         "params": {"type": "object", "description": "Query parameters"},
109 |                     },
110 |                     "required": ["path"],
111 |                 }
112 |             ),
113 |             types.Tool(
114 |                 name="minium_navigate_back",
115 |                 description="Navigate back to the previous page",
116 |                 inputSchema={
117 |                     "type": "object",
118 |                     "properties": {},
119 |                     "required": [],
120 |                 }
121 |             ),
122 |             types.Tool(
123 |                 name="minium_switch_tab",
124 |                 description="Switch to a tab. Please get path of all pages before using this tool.",
125 |                 inputSchema={
126 |                     "type": "object",
127 |                     "properties": {
128 |                         "path": {"type": "string", "description": "Page path"},
129 |                     },
130 |                     "required": ["path"],
131 |                 }
132 |             ),
133 |             types.Tool(
134 |                 name="minium_redirect_to",
135 |                 description="Redirect to a page",
136 |                 inputSchema={
137 |                     "type": "object",
138 |                     "properties": {
139 |                         "path": {"type": "string", "description": "Page path"},
140 |                         "params": {"type": "object", "description": "Query parameters"},
141 |                     },
142 |                     "required": ["path"],
143 |                 }
144 |             ),
145 |             types.Tool(
146 |                 name="minium_relaunch",
147 |                 description="Close all pages and open a new one",
148 |                 inputSchema={
149 |                     "type": "object",
150 |                     "properties": {
151 |                         "path": {"type": "string", "description": "Page path"},
152 |                         "params": {"type": "object", "description": "Query parameters"},
153 |                     },
154 |                     "required": ["path"],
155 |                 }
156 |             ),
157 |             # types.Tool(
158 |             #     name="evaluate",
159 |             #     description="Evaluate a JavaScript(es5) code",
160 |             #     inputSchema={
161 |             #         "type": "object",
162 |             #         "properties": {
163 |             #             "code": {"type": "string", "description": "Script code"},
164 |             #             "params": {"type": "object", "description": "Script parameters"},
165 |             #         },
166 |             #         "required": ["code", "params"],
167 |             #     }
168 |             # ),
169 |             types.Tool(
170 |                 name="minium_call_method",
171 |                 description="Call a method of page",
172 |                 inputSchema={
173 |                     "type": "object",
174 |                     "properties": {
175 |                         "method": {"type": "string", "description": "Method name"},
176 |                         "params": {"type": "object", "description": "Method parameters"},
177 |                     },
178 |                     "required": ["method", "params"],
179 |                 }
180 |             ),
181 |             types.Tool(
182 |                 name="minium_page_scroll_to",
183 |                 description="Scroll to the specified position of an page",
184 |                 inputSchema={
185 |                     "type": "object",
186 |                     "properties": {
187 |                         "top": {"type": "number", "description": "Scroll to the top"},
188 |                         "duration": {"type": "number", "description": "Scroll duration"},
189 |                     },
190 |                     "required": ["top", "duration"],
191 |                 }
192 |             ),
193 |             types.Tool(
194 |                 name="page_get_wxml",
195 |                 description="Get Dom structure of an page",
196 |                 inputSchema={
197 |                     "type": "object",
198 |                     "properties": {},
199 |                     "required": [],
200 |                 }
201 |             ),
202 |             types.Tool(
203 |                 name="page_get_css",
204 |                 description="Get CSS structure of an page",
205 |                 inputSchema={
206 |                     "type": "object",
207 |                     "properties": {},
208 |                     "required": [],
209 |                 }
210 |             ),
211 |             types.Tool(
212 |                 name="minium_page_get_data",
213 |                 description="Get data of an page",
214 |                 inputSchema={
215 |                     "type": "object",
216 |                     "properties": {},
217 |                     "required": [],
218 |                 }
219 |             ),
220 |             types.Tool(
221 |                 name="minium_page_set_data",
222 |                 description="Set data of an page",
223 |                 inputSchema={
224 |                     "type": "object",
225 |                     "properties": {
226 |                         "key": {"type": "string", "description": "key of data"},
227 |                         "value": {"type": "string", "description": "value of data"},
228 |                     },
229 |                     "required": ["key", "value"],
230 |                 }
231 |             ),
232 |             types.Tool(
233 |                 name="minium_tap",
234 |                 description="Tap an element",
235 |                 inputSchema={
236 |                     "type": "object",
237 |                     "properties": {
238 |                         "selector": {
239 |                             "type": "string", 
240 |                             "description": "CSS selector or XPath expression"
241 |                         },
242 |                     },
243 |                     "required": ["selector"],
244 |                 }
245 |             ),
246 |             types.Tool(
247 |                 name="minium_long_press",
248 |                 description="Long press an element",
249 |                 inputSchema={
250 |                     "type": "object",
251 |                     "properties": {
252 |                         "selector": {
253 |                             "type": "string", 
254 |                             "description": "CSS selector or XPath expression"
255 |                         },
256 |                     },
257 |                     "required": ["selector"],
258 |                 }
259 |             ),
260 |             types.Tool(
261 |                 name="minium_move",
262 |                 description="Perform gestures on the element",
263 |                 inputSchema={
264 |                     "type": "object",
265 |                     "properties": {
266 |                         "selector": {
267 |                             "type": "string", 
268 |                             "description": "CSS selector or XPath expression"
269 |                         },
270 |                         "top": {
271 |                             "type": "number",
272 |                             "description": "Move to the top coordinate"
273 |                         },
274 |                         "left": {
275 |                             "type": "number", 
276 |                             "description": "Move to the left coordinate"
277 |                         },
278 |                     },
279 |                     "required": ["selector", "top", "left"],
280 |                 }
281 |             ),
282 |             types.Tool(
283 |                 name="minium_input",
284 |                 description="Input text to an element",
285 |                 inputSchema={
286 |                     "type": "object",
287 |                     "properties": {
288 |                         "selector": {
289 |                             "type": "string", 
290 |                             "description": "CSS selector or XPath expression"
291 |                         },
292 |                         "text": {
293 |                             "type": "string", 
294 |                             "description": "Text to input"
295 |                         },
296 |                     },
297 |                     "required": ["selector", "text"],
298 |                 }
299 |             ),
300 |             types.Tool(
301 |                 name="minium_switch",
302 |                 description="Change the switch status of an element",
303 |                 inputSchema={
304 |                     "type": "object",
305 |                     "properties": {
306 |                         "selector": {
307 |                             "type": "string", 
308 |                             "description": "CSS selector or XPath expression"
309 |                         },
310 |                     },
311 |                     "required": ["selector"],
312 |                 }
313 |             ),
314 |             types.Tool(
315 |                 name="minium_slide_to",
316 |                 description="Slide to the specified position of an element",
317 |                 inputSchema={
318 |                     "type": "object",
319 |                     "properties": {
320 |                         "selector": {
321 |                             "type": "string", 
322 |                             "description": "CSS selector or XPath expression"
323 |                         },
324 |                         "value": {
325 |                             "type": "number", 
326 |                             "description": "Slide value"
327 |                         },
328 |                     },
329 |                     "required": ["selector", "value"],
330 |                 }
331 |             ),
332 |             types.Tool(
333 |                 name="minium_pick",
334 |                 description="Pick an option of an element",
335 |                 inputSchema={
336 |                     "type": "object",
337 |                     "properties": {
338 |                         "selector": {
339 |                             "type": "string", 
340 |                             "description": "CSS selector or XPath expression"
341 |                         },
342 |                         "option": {
343 |                             "type": "string", 
344 |                             "description": "Option value"
345 |                         },
346 |                     },
347 |                     "required": ["selector", "option"],
348 |                 }
349 |             )
350 |         ]
351 | 
352 |     @server.call_tool()
353 |     async def handle_call_tool(
354 |         name: str, arguments: dict[str, Any] | None
355 |     ):
356 |         """Handle tool execution requests"""
357 |         print(f"Received call tool request: {name} with args: {arguments}")
358 | 
359 |         try:
360 |             # Send HTTP request to web server
361 |             response = requests.post(
362 |                 f"{HOST}:{PORT}/api/command",
363 |                 json={
364 |                     "name": name.replace("minium_", ""),
365 |                     "arguments": arguments or {}
366 |                 },
367 |                 timeout=600000
368 |             )
369 |             
370 |             if response.status_code != 200:
371 |                 raise Exception(f"HTTP error: {response.status_code}")
372 |                 
373 |             response_data = response.json()
374 |             if response_data.get("status") == "error":
375 |                 raise Exception(response_data.get("message", "Unknown error"))
376 | 
377 |             if response_data.get("type") == "image":
378 |                 return [types.ImageContent(type="image", mimeType="image/png", data=response_data.get("data"))]
379 |                 
380 |             return [types.TextContent(type="text", text=response_data.get("message"))]
381 |             
382 |         except Exception as e:
383 |             logger.error(f"Error handling tool request: {str(e)}")
384 |             raise
385 | 
386 |     async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
387 |         print("Server running with stdio transport")
388 |         await server.run(
389 |             read_stream,
390 |             write_stream,
391 |             InitializationOptions(
392 |                 server_name="minium",
393 |                 server_version="0.1.0",
394 |                 capabilities=server.get_capabilities(
395 |                     notification_options=NotificationOptions(),
396 |                     experimental_capabilities={},
397 |                 ),
398 |             ),
399 |         )
```

--------------------------------------------------------------------------------
/stdio/src/minium_mcp_server/server.py:
--------------------------------------------------------------------------------

```python
  1 | import os
  2 | import sys
  3 | import logging
  4 | import minium
  5 | import base64
  6 | from mcp.server import NotificationOptions, Server
  7 | from mcp.server.models import InitializationOptions
  8 | from mcp import types
  9 | from typing import Any
 10 | import mcp.server.stdio
 11 | 
 12 | # reconfigure UnicodeEncodeError prone default (i.e. windows-1252) to utf-8
 13 | if sys.platform == "win32" and os.environ.get('PYTHONIOENCODING') is None:
 14 |     sys.stdin.reconfigure(encoding="utf-8")
 15 |     sys.stdout.reconfigure(encoding="utf-8")
 16 |     sys.stderr.reconfigure(encoding="utf-8")
 17 | 
 18 | logger = logging.getLogger('minium-mcp-server')
 19 | logger.info("Starting Minium MCP Server")
 20 | 
 21 | async def main(project_path: str):
 22 |     server = Server("minium-mcp-server")
 23 |     # 根据操作系统设置开发者工具cli路径
 24 |     if sys.platform == 'darwin':  # macOS
 25 |         dev_tool_path = '/Applications/wechatwebdevtools.app/Contents/MacOS/cli'
 26 |     elif sys.platform == 'win32':  # Windows
 27 |         dev_tool_path = 'C:/Program Files (x86)/Tencent/微信web开发者工具/cli.bat'
 28 |     else:
 29 |         raise Exception("Unsupported operating system")
 30 | 
 31 |     mini = minium.Minium({
 32 |         "project_path": project_path, # 替换成你的【小程序项目目录地址】
 33 |         "dev_tool_path": dev_tool_path,
 34 |         "debug_mode": "error"
 35 |     })
 36 |     mini.app.enable_log()
 37 | 
 38 |     @server.list_tools()
 39 |     async def handle_list_tools() -> list[types.Tool]:
 40 |         """List available tools"""
 41 |         return [
 42 |             types.Tool(
 43 |                 name="minium_get_system_info",
 44 |                 description="Get system info",
 45 |                 inputSchema={
 46 |                     "type": "object",
 47 |                     "properties": {},
 48 |                     "required": [],
 49 |                 }
 50 |             ),
 51 |             types.Tool(
 52 |                 name="minium_shutdown",
 53 |                 description="Shutdown the developer tool",
 54 |                 inputSchema={
 55 |                     "type": "object",
 56 |                     "properties": {},
 57 |                     "required": [],
 58 |                 }
 59 |             ),
 60 |             types.Tool(
 61 |                 name="minium_screen_shot",
 62 |                 description="Take a screenshot of the current page",
 63 |                 inputSchema={
 64 |                     "type": "object",
 65 |                     "properties": {},
 66 |                     "required": [],
 67 |                 },
 68 |             ),
 69 |             types.Tool(
 70 |                 name="minium_get_all_pages_path_and_method",
 71 |                 description="Get paths of all pages and navigate method of each page",
 72 |                 inputSchema={
 73 |                     "type": "object",
 74 |                     "properties": {},
 75 |                     "required": [],
 76 |                 }
 77 |             ),
 78 |             types.Tool(
 79 |                 name="minium_get_navigate_method_of_page",
 80 |                 description="Get navigate method of a page",
 81 |                 inputSchema={
 82 |                     "type": "object",
 83 |                     "properties": {
 84 |                         "path": {"type": "string", "description": "Page path"},
 85 |                     },
 86 |                     "required": ["path"],
 87 |                 }
 88 |             ),
 89 |             types.Tool(
 90 |                 name="minium_go_home",
 91 |                 description="Go to the home page",
 92 |                 inputSchema={
 93 |                     "type": "object",
 94 |                     "properties": {},
 95 |                     "required": [],
 96 |                 }
 97 |             ),
 98 |             types.Tool(
 99 |                 name="minium_navigate_to",
100 |                 description="Navigate to a page. Please get path of all pages before using this tool.",
101 |                 inputSchema={
102 |                     "type": "object",
103 |                     "properties": {
104 |                         "path": {"type": "string", "description": "Page path"},
105 |                         "query": {"type": "string", "description": "Query parameters"},
106 |                     },
107 |                     "required": ["path"],
108 |                 }
109 |             ),
110 |             types.Tool(
111 |                 name="minium_navigate_back",
112 |                 description="Navigate back to the previous page",
113 |                 inputSchema={
114 |                     "type": "object",
115 |                     "properties": {},
116 |                     "required": [],
117 |                 }
118 |             ),
119 |             types.Tool(
120 |                 name="minium_switch_tab",
121 |                 description="Switch to a tab. Please get path of all pages before using this tool.",
122 |                 inputSchema={
123 |                     "type": "object",
124 |                     "properties": {
125 |                         "path": {"type": "string", "description": "Page path"},
126 |                     },
127 |                     "required": ["path"],
128 |                 }
129 |             ),
130 |             types.Tool(
131 |                 name="minium_redirect_to",
132 |                 description="Redirect to a page",
133 |                 inputSchema={
134 |                     "type": "object",
135 |                     "properties": {
136 |                         "path": {"type": "string", "description": "Page path"},
137 |                         "query": {"type": "string", "description": "Query parameters"},
138 |                     },
139 |                     "required": ["path"],
140 |                 }
141 |             ),
142 |             types.Tool(
143 |                 name="minium_relaunch",
144 |                 description="Close all pages and open a new one",
145 |                 inputSchema={
146 |                     "type": "object",
147 |                     "properties": {
148 |                         "path": {"type": "string", "description": "Page path"},
149 |                         "query": {"type": "string", "description": "Query parameters"},
150 |                     },
151 |                     "required": ["path"],
152 |                 }
153 |             ),
154 |             # types.Tool(
155 |             #     name="evaluate",
156 |             #     description="Evaluate a JavaScript(es5) code",
157 |             #     inputSchema={
158 |             #         "type": "object",
159 |             #         "properties": {
160 |             #             "code": {"type": "string", "description": "Script code"},
161 |             #             "params": {"type": "string", "description": "Script parameters"},
162 |             #         },
163 |             #         "required": ["code", "params"],
164 |             #     }
165 |             # ),
166 |             types.Tool(
167 |                 name="minium_call_method",
168 |                 description="Call a method of page",
169 |                 inputSchema={
170 |                     "type": "object",
171 |                     "properties": {
172 |                         "method": {"type": "string", "description": "Method name"},
173 |                         "params": {"type": "object", "description": "Method parameters"},
174 |                     },
175 |                     "required": ["method", "params"],
176 |                 }
177 |             ),
178 |             types.Tool(
179 |                 name="minium_page_scroll_to",
180 |                 description="Scroll to the specified position of an page",
181 |                 inputSchema={
182 |                     "type": "object",
183 |                     "properties": {
184 |                         "top": {"type": "number", "description": "Scroll to the top"},
185 |                         "duration": {"type": "number", "description": "Scroll duration"},
186 |                     },
187 |                     "required": ["top", "duration"],
188 |                 }
189 |             ),
190 |             types.Tool(
191 |                 name="page_get_wxml",
192 |                 description="Get Dom structure of an page",
193 |                 inputSchema={
194 |                     "type": "object",
195 |                     "properties": {},
196 |                     "required": [],
197 |                 }
198 |             ),
199 |             types.Tool(
200 |                 name="minium_page_get_data",
201 |                 description="Get data of an page",
202 |                 inputSchema={
203 |                     "type": "object",
204 |                     "properties": {},
205 |                     "required": [],
206 |                 }
207 |             ),
208 |             types.Tool(
209 |                 name="minium_page_set_data",
210 |                 description="Set data of an page",
211 |                 inputSchema={
212 |                     "type": "object",
213 |                     "properties": {
214 |                         "key": {"type": "string", "description": "key of data"},
215 |                         "value": {"type": "any", "description": "value of data"},
216 |                     },
217 |                     "required": [],
218 |                 }
219 |             ),
220 |             types.Tool(
221 |                 name="minium_tap",
222 |                 description="Tap an element",
223 |                 inputSchema={
224 |                     "type": "object",
225 |                     "properties": {
226 |                         "selector": {
227 |                             "type": "string", 
228 |                             "description": "CSS selector or XPath expression"
229 |                         },
230 |                     },
231 |                     "required": ["selector"],
232 |                 }
233 |             ),
234 |             types.Tool(
235 |                 name="minium_long_press",
236 |                 description="Long press an element",
237 |                 inputSchema={
238 |                     "type": "object",
239 |                     "properties": {
240 |                         "selector": {
241 |                             "type": "string", 
242 |                             "description": "CSS selector or XPath expression"
243 |                         },
244 |                     },
245 |                     "required": ["selector"],
246 |                 }
247 |             ),
248 |             types.Tool(
249 |                 name="minium_move",
250 |                 description="Perform gestures on the element",
251 |                 inputSchema={
252 |                     "type": "object",
253 |                     "properties": {
254 |                         "selector": {
255 |                             "type": "string", 
256 |                             "description": "CSS selector or XPath expression"
257 |                         },
258 |                         "top": {
259 |                             "type": "number",
260 |                             "description": "Move to the top coordinate"
261 |                         },
262 |                         "left": {
263 |                             "type": "number", 
264 |                             "description": "Move to the left coordinate"
265 |                         },
266 |                     },
267 |                     "required": ["selector", "top", "left"],
268 |                 }
269 |             ),
270 |             types.Tool(
271 |                 name="minium_input",
272 |                 description="Input text to an element",
273 |                 inputSchema={
274 |                     "type": "object",
275 |                     "properties": {
276 |                         "selector": {
277 |                             "type": "string", 
278 |                             "description": "CSS selector or XPath expression"
279 |                         },
280 |                         "text": {
281 |                             "type": "string", 
282 |                             "description": "Text to input"
283 |                         },
284 |                     },
285 |                     "required": ["selector", "text"],
286 |                 }
287 |             ),
288 |             types.Tool(
289 |                 name="minium_switch",
290 |                 description="Change the switch status of an element",
291 |                 inputSchema={
292 |                     "type": "object",
293 |                     "properties": {
294 |                         "selector": {
295 |                             "type": "string", 
296 |                             "description": "CSS selector or XPath expression"
297 |                         },
298 |                     },
299 |                     "required": ["selector"],
300 |                 }
301 |             ),
302 |             types.Tool(
303 |                 name="minium_slide_to",
304 |                 description="Slide to the specified position of an element",
305 |                 inputSchema={
306 |                     "type": "object",
307 |                     "properties": {
308 |                         "selector": {
309 |                             "type": "string", 
310 |                             "description": "CSS selector or XPath expression"
311 |                         },
312 |                         "value": {
313 |                             "type": "number", 
314 |                             "description": "Slide value"
315 |                         },
316 |                     },
317 |                     "required": ["selector", "value"],
318 |                 }
319 |             ),
320 |             types.Tool(
321 |                 name="minium_pick",
322 |                 description="Pick an option of an element",
323 |                 inputSchema={
324 |                     "type": "object",
325 |                     "properties": {
326 |                         "selector": {
327 |                             "type": "string", 
328 |                             "description": "CSS selector or XPath expression"
329 |                         },
330 |                         "option": {
331 |                             "type": "string", 
332 |                             "description": "Option value"
333 |                         },
334 |                     },
335 |                     "required": ["selector", "option"],
336 |                 }
337 |             )
338 |         ]
339 | 
340 |     @server.call_tool()
341 |     async def handle_call_tool(
342 |         name: str, arguments: dict[str, Any] | None
343 |     ):
344 |         """Handle tool execution requests"""
345 |         logger.info(f"Received call tool request: {name} with args: {arguments}")
346 |         try:
347 |             match name.replace("minium_", ""):
348 |                 case "get_system_info":
349 |                     return [types.TextContent(type="text", text=f"Error: {mini.get_system_info()}")]
350 |                 case "shutdown":
351 |                     mini.shutdown()
352 |                     return [types.TextContent(type="text", text=f"Success: Shutdown")]
353 |                 case "screen_shot":
354 |                     output_path = os.path.join(project_path, "screenshots/{}_screen_shot.png".format(mini.app.get_current_page().page_id))
355 |                     if not os.path.isdir(os.path.dirname(output_path)):
356 |                         os.mkdir(os.path.dirname(output_path))
357 |                     if os.path.isfile(output_path):
358 |                         os.remove(output_path)
359 |                     mini.app.screen_shot(output_path)
360 |                     # 获取截图
361 |                     with open(output_path, "rb") as f:
362 |                         image = f.read()
363 |                         # 返回base64编码的图片
364 |                         image_base64 = base64.b64encode(image).decode('utf-8')
365 |                     # 删除截图
366 |                     os.remove(output_path)
367 |                     return [types.ImageContent(type="image", mimeType="image/png", data=image_base64)]
368 |                 
369 |                 case "get_all_pages_path_and_method":
370 |                     all_pages_path = mini.app.get_all_pages_path()
371 |                     with open(os.path.join(project_path, "app.json"), "r", encoding="utf-8") as file:  # 建议指定 encoding
372 |                         app = json.load(file)  # 解析 JSON 文件 → Python 字典/列表
373 |                         tabbar = app.get('tabBar').get('list')
374 |                     result = []
375 |                     for path in all_pages_path:
376 |                         if path in [item.get('pagePath') for item in tabbar]:
377 |                             result.append({
378 |                                 "path": f"/{path}",
379 |                                 "method": "switch_tab"
380 |                             })
381 |                         else:
382 |                             result.append({
383 |                                 "path": f"/{path}",
384 |                                 "method": "navigate_to"
385 |                             })
386 |                     return [types.TextContent(type="text", text=f"```json\n{json.dumps(result, indent=4, ensure_ascii=False)}```")]
387 |                 case "get_navigate_method_of_page":
388 |                     with open(os.path.join(project_path, "app.json"), "r", encoding="utf-8") as file:  # 建议指定 encoding
389 |                         app = json.load(file)  # 解析 JSON 文件 → Python 字典/列表
390 |                         tabbar = app.get('tabBar').get('list')
391 |                         print(tabbar)
392 |                         if arguments["path"] in [item.get('pagePath') for item in tabbar]:
393 |                             return [types.TextContent(type="text", text=f"Success: switch_tab")]
394 |                         else:
395 |                             return [types.TextContent(type="text", text=f"Success: navigate_to")]
396 | 
397 |                 case "go_home":
398 |                     mini.app.go_home()
399 |                     return [types.TextContent(type="text", text=f"Success: Go home")]
400 |                 case "navigate_to":
401 |                     mini.app.navigate_to(arguments["path"], arguments["query"])
402 |                     return [types.TextContent(type="text", text=f"Success: Navigate to")]
403 |                 case "navigate_back":
404 |                     mini.app.navigate_back()
405 |                     return [types.TextContent(type="text", text=f"Success: Navigate back")]
406 |                 case "switch_tab":
407 |                     mini.app.switch_tab(arguments["path"])
408 |                     return [types.TextContent(type="text", text=f"Success: Switch tab")]
409 |                 case "redirect_to":
410 |                     mini.app.redirect_to(arguments["path"], arguments["query"])
411 |                     return [types.TextContent(type="text", text=f"Success: Redirect to")]
412 | 
413 |                 case "evaluate":
414 |                     msg_id = mini.app.evaluate(arguments["code"], arguments["params"], sync=False)
415 |                     # 你可以做一些其他操作后, 再通过get_async_response方法获取前面注入代码的运行结果
416 |                     result = mini.app.get_async_response(msg_id, 5)
417 |                     return [types.TextContent(type="text", text=f"Success: Evaluate, Result: {result}")]
418 |                 case "call_method":
419 |                     page = mini.app.get_current_page()
420 |                     result = page.call_method(arguments["method"], arguments["params"])
421 |                     return [types.TextContent(type="text", text=f"Success: Call method, Result: {result}")]
422 |                 case "page_scroll_to":
423 |                     page = mini.app.get_current_page()
424 |                     page.scroll_to(arguments["top"], arguments["duration"])
425 |                     return [types.TextContent(type="text", text=f"Success: Page scroll to, Top: {arguments['top']}, Duration: {arguments['duration']}")]
426 |                 case "page_get_wxml":
427 |                     page = mini.app.get_current_page()
428 |                     wxml = page.wxml
429 |                     # 分离wxml和css
430 |                     # 查询最后一个tag的位置
431 |                     last_tag_index = wxml.rfind("</")
432 |                     # 分割wxml和css
433 |                     wxml_content = wxml[:last_tag_index]
434 |                     css_content = wxml[last_tag_index:]
435 |                     first_tag_index = css_content.find(">")
436 |                     wxml_content += css_content[:first_tag_index+1]
437 |                     css_content = css_content[first_tag_index+1:]
438 | 
439 |                     return [types.TextContent(type="text", text=f"```xml\n{wxml_content}```\n\n```css\n{css_content}```")]
440 |                 case "page_get_data":
441 |                     page = mini.app.get_current_page()
442 |                     return [types.TextContent(type="text", text=f"```json\n{json.dumps(page.data, indent=4, ensure_ascii=False)}```")]
443 |                 case "page_set_data":
444 |                     page = mini.app.get_current_page()
445 |                     data = page.data
446 |                     data[arguments['key']] = arguments['value']
447 |                     return [types.TextContent(type="text", text=f"```json\n{json.dumps(page.data, indent=4, ensure_ascii=False)}```")]
448 | 
449 |                 case "tap":
450 |                     page = mini.app.get_current_page()
451 |                     el = page.get_element(arguments["element"])
452 |                     el.tap()
453 |                     return [types.TextContent(type="text", text=f"Success: Tap")]
454 |                 case "long_press":
455 |                     page = mini.app.get_current_page()
456 |                     el = page.get_element(arguments["element"])
457 |                     el.long_press()
458 |                     return [types.TextContent(type="text", text=f"Success: Long press")]
459 |                 case "move":
460 |                     page = mini.app.get_current_page()
461 |                     el = page.get_element(arguments["element"])
462 |                     el.move(arguments["left"], arguments["top"])
463 |                     return [types.TextContent(type="text", text=f"Success: Move to, Top: {arguments['top']}, Left: {arguments['left']}")]
464 |                 case "input":
465 |                     page = mini.app.get_current_page()
466 |                     el = page.get_element(arguments["element"])
467 |                     el.input(arguments["text"])
468 |                     return [types.TextContent(type="text", text=f"Success: Input, Text: {arguments['text']}")]
469 |                 case "switch":
470 |                     page = mini.app.get_current_page()
471 |                     el = page.get_element(arguments["element"])
472 |                     el.switch()
473 |                     return [types.TextContent(type="text", text=f"Success: Switch")]
474 |                 case "slide_to":
475 |                     page = mini.app.get_current_page()
476 |                     el = page.get_element(arguments["element"])
477 |                     el.slide_to(arguments["value"])
478 |                     return [types.TextContent(type="text", text=f"Success: Slide to, Value: {arguments['value']}")]
479 |                 case "pick":
480 |                     page = mini.app.get_current_page()
481 |                     el = page.get_element(arguments["element"])
482 |                     el.pick(arguments["option"])
483 |                     return [types.TextContent(type="text", text=f"Success: Pick, Option: {arguments['option']}")]
484 |                 case _:
485 |                     raise ValueError(f"Unknown tool: {name}")
486 |         except Exception as e:
487 |             logger.error(f"Error executing tool: {e}")
488 |             return [types.TextContent(type="text", text=f"Error: {str(e)}")]
489 | 
490 |     async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
491 |         logger.info("Server running with stdio transport")
492 |         await server.run(
493 |             read_stream,
494 |             write_stream,
495 |             InitializationOptions(
496 |                 server_name="minium",
497 |                 server_version="0.1.0",
498 |                 capabilities=server.get_capabilities(
499 |                     notification_options=NotificationOptions(),
500 |                     experimental_capabilities={},
501 |                 ),
502 |             ),
503 |         )
504 | 
```