#
tokens: 11350/50000 15/15 files
lines: off (toggle) GitHub
raw markdown copy
# 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:
--------------------------------------------------------------------------------

```
3.12

```

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

```
3.12

```

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

```
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info

# Virtual environments
.venv

```

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

```
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info

# Virtual environments
.venv

```

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

```
# Python-generated files
**/__pycache__/
**/*.py[oc]
**/build/
**/dist/
**/wheels/
**/*.egg-info
**/.DS_Store

# Virtual environments
**/.venv

```

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

```markdown
# minium-mcp-server

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

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

```markdown
# minium-mcp-server MCP server

A MCP server project

## Components

### Resources

The server implements a simple note storage system with:
- Custom note:// URI scheme for accessing individual notes
- Each note resource has a name, description and text/plain mimetype

### Prompts

The server provides a single prompt:
- summarize-notes: Creates summaries of all stored notes
  - Optional "style" argument to control detail level (brief/detailed)
  - Generates prompt combining all current notes with style preference

### Tools

The server implements one tool:
- add-note: Adds a new note to the server
  - Takes "name" and "content" as required string arguments
  - Updates server state and notifies clients of resource changes

## Configuration

[TODO: Add configuration details specific to your implementation]

## Quickstart

### Install

#### Claude Desktop

On MacOS: `~/Library/Application\ Support/Claude/claude_desktop_config.json`
On Windows: `%APPDATA%/Claude/claude_desktop_config.json`

<details>
  <summary>Development/Unpublished Servers Configuration</summary>
  ```
  "mcpServers": {
    "minium-mcp-server": {
      "command": "uv",
      "args": [
        "--directory",
        "--path--/minium-mcp-server/webapi",
        "run",
        "minium-mcp-server"
      ]
    }
  }
  ```
</details>

<details>
  <summary>Published Servers Configuration</summary>
  ```
  "mcpServers": {
    "minium-mcp-server": {
      "command": "uvx",
      "args": [
        "minium-mcp-server"
      ]
    }
  }
  ```
</details>

## Development

### Building and Publishing

To prepare the package for distribution:

1. Sync dependencies and update lockfile:
```bash
uv sync
```

2. Build package distributions:
```bash
uv build
```

This will create source and wheel distributions in the `dist/` directory.

3. Publish to PyPI:
```bash
uv publish
```

Note: You'll need to set PyPI credentials via environment variables or command flags:
- Token: `--token` or `UV_PUBLISH_TOKEN`
- Or username/password: `--username`/`UV_PUBLISH_USERNAME` and `--password`/`UV_PUBLISH_PASSWORD`

### Debugging

Since MCP servers run over stdio, debugging can be challenging. For the best debugging
experience, we strongly recommend using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector).


You can launch the MCP Inspector via [`npm`](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) with this command:

```bash
npx @modelcontextprotocol/inspector uv --directory --path--/minium-mcp-server/webapi run minium-mcp-server
```


Upon launching, the Inspector will display a URL that you can access in your browser to begin debugging.
```

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

```markdown
# minium-mcp-server MCP server

A MCP server project

## Components

### Resources

The server implements a simple note storage system with:
- Custom note:// URI scheme for accessing individual notes
- Each note resource has a name, description and text/plain mimetype

### Prompts

The server provides a single prompt:
- summarize-notes: Creates summaries of all stored notes
  - Optional "style" argument to control detail level (brief/detailed)
  - Generates prompt combining all current notes with style preference

### Tools

The server implements one tool:
- add-note: Adds a new note to the server
  - Takes "name" and "content" as required string arguments
  - Updates server state and notifies clients of resource changes

## Configuration

[TODO: Add configuration details specific to your implementation]

## Quickstart

### Install

#### Claude Desktop

On MacOS: `~/Library/Application\ Support/Claude/claude_desktop_config.json`
On Windows: `%APPDATA%/Claude/claude_desktop_config.json`

<details>
  <summary>Development/Unpublished Servers Configuration</summary>
  ```
  "mcpServers": {
    "minium-mcp-server": {
      "command": "uv",
      "args": [
        "--directory",
        "--path--/minium_mcp_server/stdio",
        "run",
        "minium-mcp-server",
        "--path",
        "--project-path--"
      ]
    }
  }
  ```
</details>

<details>
  <summary>Published Servers Configuration</summary>
  ```
  "mcpServers": {
    "minium-mcp-server": {
      "command": "uvx",
      "args": [
        "minium-mcp-server"
      ]
    }
  }
  ```
</details>

## Development

### Building and Publishing

To prepare the package for distribution:

1. Sync dependencies and update lockfile:
```bash
uv sync
```

2. Build package distributions:
```bash
uv build
```

This will create source and wheel distributions in the `dist/` directory.

3. Publish to PyPI:
```bash
uv publish
```

Note: You'll need to set PyPI credentials via environment variables or command flags:
- Token: `--token` or `UV_PUBLISH_TOKEN`
- Or username/password: `--username`/`UV_PUBLISH_USERNAME` and `--password`/`UV_PUBLISH_PASSWORD`

### Debugging

Since MCP servers run over stdio, debugging can be challenging. For the best debugging
experience, we strongly recommend using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector).


You can launch the MCP Inspector via [`npm`](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) with this command:

```bash
npx @modelcontextprotocol/inspector uv --directory --path--/minium_mcp_server/stdio run minium-mcp-server --path --project-path--
```


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
from . import server
import asyncio

def main():
    """Main entry point for the package."""
    asyncio.run(server.main())

# Optionally expose other important items at package level
__all__ = ['main', 'server']
```

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

```toml
[project]
name = "minium-mcp-server"
version = "0.1.0"
description = "A MCP server project"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [ "mcp>=1.4.1", "minium"]
[[project.authors]]
name = "roy.yan"

[build-system]
requires = [ "hatchling",]
build-backend = "hatchling.build"

[project.scripts]
minium-mcp-server = "minium_mcp_server:main"

```

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

```toml
[project]
name = "minium-mcp-server"
version = "0.1.0"
description = "A MCP server project"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [ "mcp>=1.4.1", "minium", "flask"]
[[project.authors]]
name = "roy.yan"

[build-system]
requires = [ "hatchling",]
build-backend = "hatchling.build"

[project.scripts]
minium-mcp-server = "minium_mcp_server:main"

```

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

```python
from . import server
import asyncio
import argparse

def main():
    """Main entry point for the package."""
    parser = argparse.ArgumentParser(description='Minium MCP Server')
    parser.add_argument('--path', 
                       default="./MiniProgram",
                       help='Path to WeChat MiniProgram project')
    
    args = parser.parse_args()
    asyncio.run(server.main(args.path))


# Optionally expose other important items at package level
__all__ = ["main", "server"]
```

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

```python
from flask import Flask, request, jsonify
import json
import os
import sys
import minium
import base64

app = Flask(__name__)
print("Starting Minium MCP Web Server")

mini = None
project_path = ''
HOST = '0.0.0.0'
PORT = 9188

def mini_log_added(message):
    """
    小程序 log 监听回调函数
    将小程序的 log 格式化然后保存起来
    :param message: {"type": "log|warn|error", "args": [str, ..., ]}
    :return:
    """
    print(f'console.log: {message}')

@app.route('/api/command', methods=['POST'])
def handle_command():
    global mini, project_path
    try:
        command = request.json
        arguments = command['arguments']

        print(f"COMMAND: {json.dumps(command)}")
        
        match command['name']:
            case "open":
                if sys.platform == 'darwin':  # macOS
                    dev_tool_path = '/Applications/wechatwebdevtools.app/Contents/MacOS/cli'
                elif sys.platform == 'win32':  # Windows
                    dev_tool_path = 'C:/Program Files (x86)/Tencent/微信web开发者工具/cli.bat'
                else:
                    raise Exception("Unsupported operating system")

                project_path = arguments['path']
                try:
                    mini = minium.Minium({
                        "project_path": project_path,
                        "dev_tool_path": dev_tool_path,
                        "debug_mode": "error",
                        "audits": True,
                        "autofix": True
                    })
                    mini.app.enable_log()
                    mini.app.add_observer("App.logAdded", mini_log_added)
                except Exception as e:
                    # 重试
                    return jsonify({
                        "status": "error",
                        "message": str(e)
                    })

                return jsonify({
                    "status": "success",
                    "message": "Opened"
                })

            case "get_system_info":
                return jsonify({
                    "status": "success",
                    "message": mini.get_system_info()
                })

            case "shutdown":
                mini.shutdown()
                return jsonify({
                    "status": "success", 
                    "message": "Closed"
                })

            case "screen_shot":
                output_path = os.path.join(project_path, "screenshots/{}_screen_shot.png".format(mini.app.get_current_page().page_id))
                if not os.path.isdir(os.path.dirname(output_path)):
                    os.makedirs(os.path.dirname(output_path))
                if os.path.isfile(output_path):
                    os.remove(output_path)
                mini.app.screen_shot(output_path)
                # 获取截图
                with open(output_path, "rb") as f:
                    image = f.read()
                    # 返回base64编码的图片
                    image_base64 = base64.b64encode(image).decode('utf-8')
                # 删除截图
                os.remove(output_path)
                return jsonify({
                    "status": "success",
                    "type": "image",
                    "data": image_base64
                })
            
            case "get_all_pages_path_and_method":
                all_pages_path = mini.app.get_all_pages_path()
                with open(os.path.join(project_path, "app.json"), "r", encoding="utf-8") as file:  # 建议指定 encoding
                    app = json.load(file)  # 解析 JSON 文件 → Python 字典/列表
                    tabbar = app.get('tabBar').get('list')
                
                result = []
                for path in all_pages_path:
                    if path in [item.get('pagePath') for item in tabbar]:
                        result.append({
                            "path": f"/{path}",
                            "method": "minium_switch_tab"
                        })
                    else:
                        result.append({
                            "path": f"/{path}",
                            "method": "minium_navigate_to"
                        })
                return jsonify({
                    "status": "success",
                    "message": f"```json\n{json.dumps(result, indent=4, ensure_ascii=False)}```"
                })
            
            case "get_navigate_method_of_page":
                with open(os.path.join(project_path, "app.json"), "r", encoding="utf-8") as file:  # 建议指定 encoding
                    app = json.load(file)  # 解析 JSON 文件 → Python 字典/列表
                    tabbar = app.get('tabBar').get('list')
                    if arguments["path"] in [item.get('pagePath') for item in tabbar]:
                        return jsonify({
                            "status": "success",
                            "message": "minium_switch_tab"
                        })
                    else:
                        return jsonify({    
                            "status": "success",
                            "message": "minium_navigate_to"
                        })

            case "go_home":
                page = mini.app.go_home()
                if page is not None:
                    return jsonify({
                        "status": "success",
                        "message": "Successfully enter the home page"
                    })
                else:
                    return jsonify({
                        "status": "error",
                        "message": "Failed to enter the home page"
                    })

            case "navigate_to":
                # 如果arguments中没有params,则默认为空字符串
                if arguments.get("params") is None:
                    arguments["params"] = {}
                page = mini.app.navigate_to(arguments["path"], arguments["params"])
                if page is not None:
                    return jsonify({
                        "status": "success",
                        "message": f"Successfully enter the {arguments['path']} page"
                    })
                else:
                    return jsonify({
                        "status": "error",
                        "message": f"Failed to enter the {arguments['path']} page"
                    })

            case "navigate_back":
                page = mini.app.navigate_back()
                if page is not None:
                    return jsonify({
                        "status": "success",
                        "message": "Successful return to the previous page"
                    })
                else:
                    return jsonify({
                        "status": "error",
                        "message": f"Failed to return to the previous page"
                    })

            case "switch_tab":
                page = mini.app.switch_tab(arguments["path"])
                if page is not None:
                    return jsonify({
                        "status": "success",
                        "message": "Successfully switch tab"
                    })
                else:
                    return jsonify({
                        "status": "error",
                        "message": "Failed to switch tab"
                    })

            case "redirect_to":
                if arguments.get("params") is None:
                    arguments["params"] = {}
                page = mini.app.redirect_to(arguments["path"], arguments["params"])
                if page is not None:
                    return jsonify({
                        "status": "success",
                        "message": "Successfully redirect to"
                    })
                else:
                    return jsonify({
                        "status": "error",
                        "message": "Failed to redirect to"
                    })

            # case "evaluate":
            #     msg_id = mini.app.evaluate(arguments["code"], arguments["params"], sync=False)
            #     result = mini.app.get_async_response(msg_id, 5)
            #     return jsonify({
            #         "status": "success",
            #         "message": f"Evaluate, Result: {result}"
            #     })

            case "call_method":
                if arguments.get("params") is None:
                    arguments["params"] = {}
                page = mini.app.get_current_page()
                result = page.call_method(arguments["method"], arguments["params"])
                return jsonify({
                    "status": "success",
                    "message": f"Call method, Result: {result}"
                })

            case "page_scroll_to":
                page = mini.app.get_current_page()
                page.scroll_to(arguments["top"], arguments["duration"])
                return jsonify({
                    "status": "success",
                    "message": f"Page scroll to, Top: {arguments['top']}, Duration: {arguments['duration']}"
                })

            case "page_get_wxml":
                page = mini.app.get_current_page()
                wxml = page.wxml
                # 分离wxml和css
                # 查询最后一个tag的位置
                last_tag_index = wxml.rfind("</")
                # 分割wxml和css
                wxml_content = wxml[:last_tag_index]
                css_content = wxml[last_tag_index:]
                first_tag_index = css_content.find(">")
                wxml_content += css_content[:first_tag_index+1]
                css_content = css_content[first_tag_index+1:]
                
                # 格式化输出

                return jsonify({
                    "status": "success",
                    "message": f"```xml\n{wxml_content}```"
                })

            case "page_get_css":
                page = mini.app.get_current_page()
                wxml = page.wxml
                # 分离wxml和css
                # 查询最后一个tag的位置
                last_tag_index = wxml.rfind("</")
                # 分割wxml和css
                wxml_content = wxml[:last_tag_index]
                css_content = wxml[last_tag_index:]
                first_tag_index = css_content.find(">")
                wxml_content += css_content[:first_tag_index+1]
                css_content = css_content[first_tag_index+1:]
                
                # 格式化输出

                return jsonify({
                    "status": "success",
                    "message": f"```css\n{css_content}```"
                })
            
            case "page_get_data":
                page = mini.app.get_current_page()
                return jsonify({
                    "status": "success",
                    "message": f"```json\n{page.data}```"
                })
            
            case "page_set_data":
                page = mini.app.get_current_page()
                data = page.data
                data[arguments['key']] = json.load(arguments['value'])
                return jsonify({
                    "status": "success",
                    "message": f"```json\n{page.data}```"
                })

            case "tap":
                page = mini.app.get_current_page()
                el = page.get_element(arguments["selector"])
                el.tap()
                return jsonify({
                    "status": "success",
                    "message": "Tapped"
                })

            case "long_press":
                page = mini.app.get_current_page()
                el = page.get_element(arguments["selector"])
                el.long_press()
                return jsonify({
                    "status": "success",
                    "message": "Long pressed"
                })

            case "move":
                page = mini.app.get_current_page()
                el = page.get_element(arguments["selector"])
                el.move(arguments["left"], arguments["top"])
                return jsonify({
                    "status": "success",
                    "message": f"Moved to, Top: {arguments['top']}, Left: {arguments['left']}"
                })

            case "input":
                page = mini.app.get_current_page()
                el = page.get_element(arguments["selector"])
                el.input(arguments["text"])
                return jsonify({
                    "status": "success",
                    "message": f"Input, Text: {arguments['text']}"
                })

            case "switch":
                page = mini.app.get_current_page()
                el = page.get_element(arguments["selector"])
                el.switch()
                return jsonify({
                    "status": "success",
                    "message": "Switched"
                })

            case "slide_to":
                page = mini.app.get_current_page()
                el = page.get_element(arguments["selector"])
                el.slide_to(arguments["value"])
                return jsonify({
                    "status": "success",
                    "message": f"Slided to, Value: {arguments['value']}"
                })

            case "pick":
                page = mini.app.get_current_page()
                el = page.get_element(arguments["selector"])
                el.pick(arguments["option"])
                return jsonify({
                    "status": "success",
                    "message": f"Picked, Option: {arguments['option']}"
                })

            case _:
                return jsonify({
                    "status": "error",
                    "message": f"Unknown command: {command['name']}"
                })

    except Exception as e:
        return jsonify({
            "status": "error",
            "message": str(e)
        })

if __name__ == "__main__":
    app.run(host=HOST, port=PORT)

```

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

```python
import os
import sys
import logging
import json
import asyncio
import requests
from mcp.server import NotificationOptions, Server
from mcp.server.models import InitializationOptions

from mcp import types
from typing import Any
import mcp.server.stdio

HOST = 'http://127.0.0.1'
# HOST = 'http://192.168.3.42'
PORT = 9188

# reconfigure UnicodeEncodeError prone default (i.e. windows-1252) to utf-8
if sys.platform == "win32" and os.environ.get('PYTHONIOENCODING') is None:
    sys.stdin.reconfigure(encoding="utf-8")
    sys.stdout.reconfigure(encoding="utf-8")
    sys.stderr.reconfigure(encoding="utf-8")

logger = logging.getLogger('minium-mcp-server')
print("Starting Minium MCP Server")

async def main():
    server = Server("minium-mcp-server")

    @server.list_tools()
    async def handle_list_tools() -> list[types.Tool]:
        """List available tools"""
        return [
            types.Tool(
                name="minium_open",
                description="Open a project",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "path": {"type": "string", "description": "Project path"},
                    },
                    "required": ["path"],
                }
            ),
            types.Tool(
                name="minium_get_system_info",
                description="Get system info",
                inputSchema={
                    "type": "object",
                    "properties": {},
                    "required": [],
                }
            ),
            types.Tool(
                name="minium_shutdown",
                description="Shutdown the developer tool",
                inputSchema={
                    "type": "object",
                    "properties": {},
                    "required": [],
                }
            ),
            types.Tool(
                name="minium_screen_shot",
                description="Take a screenshot of the current page",
                inputSchema={
                    "type": "object",
                    "properties": {},
                    "required": [],
                },
            ),
            types.Tool(
                name="minium_get_all_pages_path_and_method",
                description="Get paths of all pages and the method used to navigate to them",
                inputSchema={
                    "type": "object",
                    "properties": {},
                    "required": [],
                }
            ),
            types.Tool(
                name="minium_get_navigate_method_of_page",
                description="Get the method used to navigate to a page",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "path": {"type": "string", "description": "Page path"},
                    },
                    "required": ["path"],
                }
            ),
            types.Tool(
                name="minium_go_home",
                description="Go to the home page",
                inputSchema={
                    "type": "object",
                    "properties": {},
                    "required": [],
                }
            ),
            types.Tool(
                name="minium_navigate_to",
                description="Navigate to a page. Please get path of all pages before using this tool.",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "path": {"type": "string", "description": "Page path"},
                        "params": {"type": "object", "description": "Query parameters"},
                    },
                    "required": ["path"],
                }
            ),
            types.Tool(
                name="minium_navigate_back",
                description="Navigate back to the previous page",
                inputSchema={
                    "type": "object",
                    "properties": {},
                    "required": [],
                }
            ),
            types.Tool(
                name="minium_switch_tab",
                description="Switch to a tab. Please get path of all pages before using this tool.",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "path": {"type": "string", "description": "Page path"},
                    },
                    "required": ["path"],
                }
            ),
            types.Tool(
                name="minium_redirect_to",
                description="Redirect to a page",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "path": {"type": "string", "description": "Page path"},
                        "params": {"type": "object", "description": "Query parameters"},
                    },
                    "required": ["path"],
                }
            ),
            types.Tool(
                name="minium_relaunch",
                description="Close all pages and open a new one",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "path": {"type": "string", "description": "Page path"},
                        "params": {"type": "object", "description": "Query parameters"},
                    },
                    "required": ["path"],
                }
            ),
            # types.Tool(
            #     name="evaluate",
            #     description="Evaluate a JavaScript(es5) code",
            #     inputSchema={
            #         "type": "object",
            #         "properties": {
            #             "code": {"type": "string", "description": "Script code"},
            #             "params": {"type": "object", "description": "Script parameters"},
            #         },
            #         "required": ["code", "params"],
            #     }
            # ),
            types.Tool(
                name="minium_call_method",
                description="Call a method of page",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "method": {"type": "string", "description": "Method name"},
                        "params": {"type": "object", "description": "Method parameters"},
                    },
                    "required": ["method", "params"],
                }
            ),
            types.Tool(
                name="minium_page_scroll_to",
                description="Scroll to the specified position of an page",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "top": {"type": "number", "description": "Scroll to the top"},
                        "duration": {"type": "number", "description": "Scroll duration"},
                    },
                    "required": ["top", "duration"],
                }
            ),
            types.Tool(
                name="page_get_wxml",
                description="Get Dom structure of an page",
                inputSchema={
                    "type": "object",
                    "properties": {},
                    "required": [],
                }
            ),
            types.Tool(
                name="page_get_css",
                description="Get CSS structure of an page",
                inputSchema={
                    "type": "object",
                    "properties": {},
                    "required": [],
                }
            ),
            types.Tool(
                name="minium_page_get_data",
                description="Get data of an page",
                inputSchema={
                    "type": "object",
                    "properties": {},
                    "required": [],
                }
            ),
            types.Tool(
                name="minium_page_set_data",
                description="Set data of an page",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "key": {"type": "string", "description": "key of data"},
                        "value": {"type": "string", "description": "value of data"},
                    },
                    "required": ["key", "value"],
                }
            ),
            types.Tool(
                name="minium_tap",
                description="Tap an element",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "selector": {
                            "type": "string", 
                            "description": "CSS selector or XPath expression"
                        },
                    },
                    "required": ["selector"],
                }
            ),
            types.Tool(
                name="minium_long_press",
                description="Long press an element",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "selector": {
                            "type": "string", 
                            "description": "CSS selector or XPath expression"
                        },
                    },
                    "required": ["selector"],
                }
            ),
            types.Tool(
                name="minium_move",
                description="Perform gestures on the element",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "selector": {
                            "type": "string", 
                            "description": "CSS selector or XPath expression"
                        },
                        "top": {
                            "type": "number",
                            "description": "Move to the top coordinate"
                        },
                        "left": {
                            "type": "number", 
                            "description": "Move to the left coordinate"
                        },
                    },
                    "required": ["selector", "top", "left"],
                }
            ),
            types.Tool(
                name="minium_input",
                description="Input text to an element",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "selector": {
                            "type": "string", 
                            "description": "CSS selector or XPath expression"
                        },
                        "text": {
                            "type": "string", 
                            "description": "Text to input"
                        },
                    },
                    "required": ["selector", "text"],
                }
            ),
            types.Tool(
                name="minium_switch",
                description="Change the switch status of an element",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "selector": {
                            "type": "string", 
                            "description": "CSS selector or XPath expression"
                        },
                    },
                    "required": ["selector"],
                }
            ),
            types.Tool(
                name="minium_slide_to",
                description="Slide to the specified position of an element",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "selector": {
                            "type": "string", 
                            "description": "CSS selector or XPath expression"
                        },
                        "value": {
                            "type": "number", 
                            "description": "Slide value"
                        },
                    },
                    "required": ["selector", "value"],
                }
            ),
            types.Tool(
                name="minium_pick",
                description="Pick an option of an element",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "selector": {
                            "type": "string", 
                            "description": "CSS selector or XPath expression"
                        },
                        "option": {
                            "type": "string", 
                            "description": "Option value"
                        },
                    },
                    "required": ["selector", "option"],
                }
            )
        ]

    @server.call_tool()
    async def handle_call_tool(
        name: str, arguments: dict[str, Any] | None
    ):
        """Handle tool execution requests"""
        print(f"Received call tool request: {name} with args: {arguments}")

        try:
            # Send HTTP request to web server
            response = requests.post(
                f"{HOST}:{PORT}/api/command",
                json={
                    "name": name.replace("minium_", ""),
                    "arguments": arguments or {}
                },
                timeout=600000
            )
            
            if response.status_code != 200:
                raise Exception(f"HTTP error: {response.status_code}")
                
            response_data = response.json()
            if response_data.get("status") == "error":
                raise Exception(response_data.get("message", "Unknown error"))

            if response_data.get("type") == "image":
                return [types.ImageContent(type="image", mimeType="image/png", data=response_data.get("data"))]
                
            return [types.TextContent(type="text", text=response_data.get("message"))]
            
        except Exception as e:
            logger.error(f"Error handling tool request: {str(e)}")
            raise

    async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
        print("Server running with stdio transport")
        await server.run(
            read_stream,
            write_stream,
            InitializationOptions(
                server_name="minium",
                server_version="0.1.0",
                capabilities=server.get_capabilities(
                    notification_options=NotificationOptions(),
                    experimental_capabilities={},
                ),
            ),
        )
```

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

```python
import os
import sys
import logging
import minium
import base64
from mcp.server import NotificationOptions, Server
from mcp.server.models import InitializationOptions
from mcp import types
from typing import Any
import mcp.server.stdio

# reconfigure UnicodeEncodeError prone default (i.e. windows-1252) to utf-8
if sys.platform == "win32" and os.environ.get('PYTHONIOENCODING') is None:
    sys.stdin.reconfigure(encoding="utf-8")
    sys.stdout.reconfigure(encoding="utf-8")
    sys.stderr.reconfigure(encoding="utf-8")

logger = logging.getLogger('minium-mcp-server')
logger.info("Starting Minium MCP Server")

async def main(project_path: str):
    server = Server("minium-mcp-server")
    # 根据操作系统设置开发者工具cli路径
    if sys.platform == 'darwin':  # macOS
        dev_tool_path = '/Applications/wechatwebdevtools.app/Contents/MacOS/cli'
    elif sys.platform == 'win32':  # Windows
        dev_tool_path = 'C:/Program Files (x86)/Tencent/微信web开发者工具/cli.bat'
    else:
        raise Exception("Unsupported operating system")

    mini = minium.Minium({
        "project_path": project_path, # 替换成你的【小程序项目目录地址】
        "dev_tool_path": dev_tool_path,
        "debug_mode": "error"
    })
    mini.app.enable_log()

    @server.list_tools()
    async def handle_list_tools() -> list[types.Tool]:
        """List available tools"""
        return [
            types.Tool(
                name="minium_get_system_info",
                description="Get system info",
                inputSchema={
                    "type": "object",
                    "properties": {},
                    "required": [],
                }
            ),
            types.Tool(
                name="minium_shutdown",
                description="Shutdown the developer tool",
                inputSchema={
                    "type": "object",
                    "properties": {},
                    "required": [],
                }
            ),
            types.Tool(
                name="minium_screen_shot",
                description="Take a screenshot of the current page",
                inputSchema={
                    "type": "object",
                    "properties": {},
                    "required": [],
                },
            ),
            types.Tool(
                name="minium_get_all_pages_path_and_method",
                description="Get paths of all pages and navigate method of each page",
                inputSchema={
                    "type": "object",
                    "properties": {},
                    "required": [],
                }
            ),
            types.Tool(
                name="minium_get_navigate_method_of_page",
                description="Get navigate method of a page",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "path": {"type": "string", "description": "Page path"},
                    },
                    "required": ["path"],
                }
            ),
            types.Tool(
                name="minium_go_home",
                description="Go to the home page",
                inputSchema={
                    "type": "object",
                    "properties": {},
                    "required": [],
                }
            ),
            types.Tool(
                name="minium_navigate_to",
                description="Navigate to a page. Please get path of all pages before using this tool.",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "path": {"type": "string", "description": "Page path"},
                        "query": {"type": "string", "description": "Query parameters"},
                    },
                    "required": ["path"],
                }
            ),
            types.Tool(
                name="minium_navigate_back",
                description="Navigate back to the previous page",
                inputSchema={
                    "type": "object",
                    "properties": {},
                    "required": [],
                }
            ),
            types.Tool(
                name="minium_switch_tab",
                description="Switch to a tab. Please get path of all pages before using this tool.",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "path": {"type": "string", "description": "Page path"},
                    },
                    "required": ["path"],
                }
            ),
            types.Tool(
                name="minium_redirect_to",
                description="Redirect to a page",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "path": {"type": "string", "description": "Page path"},
                        "query": {"type": "string", "description": "Query parameters"},
                    },
                    "required": ["path"],
                }
            ),
            types.Tool(
                name="minium_relaunch",
                description="Close all pages and open a new one",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "path": {"type": "string", "description": "Page path"},
                        "query": {"type": "string", "description": "Query parameters"},
                    },
                    "required": ["path"],
                }
            ),
            # types.Tool(
            #     name="evaluate",
            #     description="Evaluate a JavaScript(es5) code",
            #     inputSchema={
            #         "type": "object",
            #         "properties": {
            #             "code": {"type": "string", "description": "Script code"},
            #             "params": {"type": "string", "description": "Script parameters"},
            #         },
            #         "required": ["code", "params"],
            #     }
            # ),
            types.Tool(
                name="minium_call_method",
                description="Call a method of page",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "method": {"type": "string", "description": "Method name"},
                        "params": {"type": "object", "description": "Method parameters"},
                    },
                    "required": ["method", "params"],
                }
            ),
            types.Tool(
                name="minium_page_scroll_to",
                description="Scroll to the specified position of an page",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "top": {"type": "number", "description": "Scroll to the top"},
                        "duration": {"type": "number", "description": "Scroll duration"},
                    },
                    "required": ["top", "duration"],
                }
            ),
            types.Tool(
                name="page_get_wxml",
                description="Get Dom structure of an page",
                inputSchema={
                    "type": "object",
                    "properties": {},
                    "required": [],
                }
            ),
            types.Tool(
                name="minium_page_get_data",
                description="Get data of an page",
                inputSchema={
                    "type": "object",
                    "properties": {},
                    "required": [],
                }
            ),
            types.Tool(
                name="minium_page_set_data",
                description="Set data of an page",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "key": {"type": "string", "description": "key of data"},
                        "value": {"type": "any", "description": "value of data"},
                    },
                    "required": [],
                }
            ),
            types.Tool(
                name="minium_tap",
                description="Tap an element",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "selector": {
                            "type": "string", 
                            "description": "CSS selector or XPath expression"
                        },
                    },
                    "required": ["selector"],
                }
            ),
            types.Tool(
                name="minium_long_press",
                description="Long press an element",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "selector": {
                            "type": "string", 
                            "description": "CSS selector or XPath expression"
                        },
                    },
                    "required": ["selector"],
                }
            ),
            types.Tool(
                name="minium_move",
                description="Perform gestures on the element",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "selector": {
                            "type": "string", 
                            "description": "CSS selector or XPath expression"
                        },
                        "top": {
                            "type": "number",
                            "description": "Move to the top coordinate"
                        },
                        "left": {
                            "type": "number", 
                            "description": "Move to the left coordinate"
                        },
                    },
                    "required": ["selector", "top", "left"],
                }
            ),
            types.Tool(
                name="minium_input",
                description="Input text to an element",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "selector": {
                            "type": "string", 
                            "description": "CSS selector or XPath expression"
                        },
                        "text": {
                            "type": "string", 
                            "description": "Text to input"
                        },
                    },
                    "required": ["selector", "text"],
                }
            ),
            types.Tool(
                name="minium_switch",
                description="Change the switch status of an element",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "selector": {
                            "type": "string", 
                            "description": "CSS selector or XPath expression"
                        },
                    },
                    "required": ["selector"],
                }
            ),
            types.Tool(
                name="minium_slide_to",
                description="Slide to the specified position of an element",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "selector": {
                            "type": "string", 
                            "description": "CSS selector or XPath expression"
                        },
                        "value": {
                            "type": "number", 
                            "description": "Slide value"
                        },
                    },
                    "required": ["selector", "value"],
                }
            ),
            types.Tool(
                name="minium_pick",
                description="Pick an option of an element",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "selector": {
                            "type": "string", 
                            "description": "CSS selector or XPath expression"
                        },
                        "option": {
                            "type": "string", 
                            "description": "Option value"
                        },
                    },
                    "required": ["selector", "option"],
                }
            )
        ]

    @server.call_tool()
    async def handle_call_tool(
        name: str, arguments: dict[str, Any] | None
    ):
        """Handle tool execution requests"""
        logger.info(f"Received call tool request: {name} with args: {arguments}")
        try:
            match name.replace("minium_", ""):
                case "get_system_info":
                    return [types.TextContent(type="text", text=f"Error: {mini.get_system_info()}")]
                case "shutdown":
                    mini.shutdown()
                    return [types.TextContent(type="text", text=f"Success: Shutdown")]
                case "screen_shot":
                    output_path = os.path.join(project_path, "screenshots/{}_screen_shot.png".format(mini.app.get_current_page().page_id))
                    if not os.path.isdir(os.path.dirname(output_path)):
                        os.mkdir(os.path.dirname(output_path))
                    if os.path.isfile(output_path):
                        os.remove(output_path)
                    mini.app.screen_shot(output_path)
                    # 获取截图
                    with open(output_path, "rb") as f:
                        image = f.read()
                        # 返回base64编码的图片
                        image_base64 = base64.b64encode(image).decode('utf-8')
                    # 删除截图
                    os.remove(output_path)
                    return [types.ImageContent(type="image", mimeType="image/png", data=image_base64)]
                
                case "get_all_pages_path_and_method":
                    all_pages_path = mini.app.get_all_pages_path()
                    with open(os.path.join(project_path, "app.json"), "r", encoding="utf-8") as file:  # 建议指定 encoding
                        app = json.load(file)  # 解析 JSON 文件 → Python 字典/列表
                        tabbar = app.get('tabBar').get('list')
                    result = []
                    for path in all_pages_path:
                        if path in [item.get('pagePath') for item in tabbar]:
                            result.append({
                                "path": f"/{path}",
                                "method": "switch_tab"
                            })
                        else:
                            result.append({
                                "path": f"/{path}",
                                "method": "navigate_to"
                            })
                    return [types.TextContent(type="text", text=f"```json\n{json.dumps(result, indent=4, ensure_ascii=False)}```")]
                case "get_navigate_method_of_page":
                    with open(os.path.join(project_path, "app.json"), "r", encoding="utf-8") as file:  # 建议指定 encoding
                        app = json.load(file)  # 解析 JSON 文件 → Python 字典/列表
                        tabbar = app.get('tabBar').get('list')
                        print(tabbar)
                        if arguments["path"] in [item.get('pagePath') for item in tabbar]:
                            return [types.TextContent(type="text", text=f"Success: switch_tab")]
                        else:
                            return [types.TextContent(type="text", text=f"Success: navigate_to")]

                case "go_home":
                    mini.app.go_home()
                    return [types.TextContent(type="text", text=f"Success: Go home")]
                case "navigate_to":
                    mini.app.navigate_to(arguments["path"], arguments["query"])
                    return [types.TextContent(type="text", text=f"Success: Navigate to")]
                case "navigate_back":
                    mini.app.navigate_back()
                    return [types.TextContent(type="text", text=f"Success: Navigate back")]
                case "switch_tab":
                    mini.app.switch_tab(arguments["path"])
                    return [types.TextContent(type="text", text=f"Success: Switch tab")]
                case "redirect_to":
                    mini.app.redirect_to(arguments["path"], arguments["query"])
                    return [types.TextContent(type="text", text=f"Success: Redirect to")]

                case "evaluate":
                    msg_id = mini.app.evaluate(arguments["code"], arguments["params"], sync=False)
                    # 你可以做一些其他操作后, 再通过get_async_response方法获取前面注入代码的运行结果
                    result = mini.app.get_async_response(msg_id, 5)
                    return [types.TextContent(type="text", text=f"Success: Evaluate, Result: {result}")]
                case "call_method":
                    page = mini.app.get_current_page()
                    result = page.call_method(arguments["method"], arguments["params"])
                    return [types.TextContent(type="text", text=f"Success: Call method, Result: {result}")]
                case "page_scroll_to":
                    page = mini.app.get_current_page()
                    page.scroll_to(arguments["top"], arguments["duration"])
                    return [types.TextContent(type="text", text=f"Success: Page scroll to, Top: {arguments['top']}, Duration: {arguments['duration']}")]
                case "page_get_wxml":
                    page = mini.app.get_current_page()
                    wxml = page.wxml
                    # 分离wxml和css
                    # 查询最后一个tag的位置
                    last_tag_index = wxml.rfind("</")
                    # 分割wxml和css
                    wxml_content = wxml[:last_tag_index]
                    css_content = wxml[last_tag_index:]
                    first_tag_index = css_content.find(">")
                    wxml_content += css_content[:first_tag_index+1]
                    css_content = css_content[first_tag_index+1:]

                    return [types.TextContent(type="text", text=f"```xml\n{wxml_content}```\n\n```css\n{css_content}```")]
                case "page_get_data":
                    page = mini.app.get_current_page()
                    return [types.TextContent(type="text", text=f"```json\n{json.dumps(page.data, indent=4, ensure_ascii=False)}```")]
                case "page_set_data":
                    page = mini.app.get_current_page()
                    data = page.data
                    data[arguments['key']] = arguments['value']
                    return [types.TextContent(type="text", text=f"```json\n{json.dumps(page.data, indent=4, ensure_ascii=False)}```")]

                case "tap":
                    page = mini.app.get_current_page()
                    el = page.get_element(arguments["element"])
                    el.tap()
                    return [types.TextContent(type="text", text=f"Success: Tap")]
                case "long_press":
                    page = mini.app.get_current_page()
                    el = page.get_element(arguments["element"])
                    el.long_press()
                    return [types.TextContent(type="text", text=f"Success: Long press")]
                case "move":
                    page = mini.app.get_current_page()
                    el = page.get_element(arguments["element"])
                    el.move(arguments["left"], arguments["top"])
                    return [types.TextContent(type="text", text=f"Success: Move to, Top: {arguments['top']}, Left: {arguments['left']}")]
                case "input":
                    page = mini.app.get_current_page()
                    el = page.get_element(arguments["element"])
                    el.input(arguments["text"])
                    return [types.TextContent(type="text", text=f"Success: Input, Text: {arguments['text']}")]
                case "switch":
                    page = mini.app.get_current_page()
                    el = page.get_element(arguments["element"])
                    el.switch()
                    return [types.TextContent(type="text", text=f"Success: Switch")]
                case "slide_to":
                    page = mini.app.get_current_page()
                    el = page.get_element(arguments["element"])
                    el.slide_to(arguments["value"])
                    return [types.TextContent(type="text", text=f"Success: Slide to, Value: {arguments['value']}")]
                case "pick":
                    page = mini.app.get_current_page()
                    el = page.get_element(arguments["element"])
                    el.pick(arguments["option"])
                    return [types.TextContent(type="text", text=f"Success: Pick, Option: {arguments['option']}")]
                case _:
                    raise ValueError(f"Unknown tool: {name}")
        except Exception as e:
            logger.error(f"Error executing tool: {e}")
            return [types.TextContent(type="text", text=f"Error: {str(e)}")]

    async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
        logger.info("Server running with stdio transport")
        await server.run(
            read_stream,
            write_stream,
            InitializationOptions(
                server_name="minium",
                server_version="0.1.0",
                capabilities=server.get_capabilities(
                    notification_options=NotificationOptions(),
                    experimental_capabilities={},
                ),
            ),
        )

```