Content
# Manus MCP
[](https://pypi.org/project/manus-mcp/)
[](https://pypi.org/project/manus-mcp/)
[](https://pypistats.org/packages/manus-mcp)
[](https://github.com/aruxojuyu665/Manus-MCP/blob/main/LICENSE)
[](https://github.com/aruxojuyu665/Manus-MCP/actions/workflows/ci.yml)
[](https://github.com/aruxojuyu665/Manus-MCP/actions/workflows/live.yml)
[](https://github.com/aruxojuyu665/Manus-MCP/stargazers)
An MCP server that gives Claude Code full programmatic control over Manus.im through the official Manus API v2. Implements **all 30 documented endpoints**, **3 composite tools** for common workflows, and a **local webhook receiver** with RSA-SHA256 signature verification.
- **Status**: production-ready (single-user) — v0.1.1
- Language: Python 3.11+
- Transport: stdio (native to Claude Code)
- Base URL: `https://api.manus.ai`
## Verified coverage (v0.1.1)
| Layer | Metric |
|------|---------|
| Unit tests | 60+ (server.py dispatch, webhook ASGI app, secret-leak guard, schema stability) |
| Live e2e tests | 23 (including task.update / sendMessage / confirmAction / agent.update / website.publish) |
| Composite live | 4 (including the F2 reject check) |
| Webhook live e2e | 1 (cloudflared tunnel + receiver + Manus delivery) |
| **Manus API endpoints exercised live** | **30/30** (graceful skip only when the account lacks the prerequisite — no agents / no website) |
| Coverage | ≥ 80% (gated in CI) |
| `mypy --strict` | 0 errors |
| `ruff check` | 0 errors |
See [docs/SECURITY.md](docs/SECURITY.md) and [docs/RELEASE.md](docs/RELEASE.md) for the production flow.
## What's included
### 30 direct API wrappers
| Category | Tools |
|-----------|-------------|
| Tasks (9) | `manus_task_create`, `manus_task_detail`, `manus_task_list`, `manus_task_update`, `manus_task_stop`, `manus_task_delete`, `manus_task_send_message`, `manus_task_list_messages`, `manus_task_confirm_action` |
| Projects (2) | `manus_project_create`, `manus_project_list` |
| Skills (1) | `manus_skill_list` |
| Agents (3) | `manus_agent_list`, `manus_agent_detail`, `manus_agent_update` |
| Files (3) | `manus_file_create`, `manus_file_detail`, `manus_file_delete` |
| Webhooks (4) | `manus_webhook_create`, `manus_webhook_list`, `manus_webhook_delete`, `manus_webhook_public_key` |
| Usage (3) | `manus_usage_list`, `manus_usage_team_statistic`, `manus_usage_team_log` |
| Connectors (1) | `manus_connector_list` |
| Browser (1) | `manus_browser_online_list` |
| Website (3) | `manus_website_status`, `manus_website_list_checkpoints`, `manus_website_publish` |
### 3 composite tools
- **`manus_file_upload`** — creates a presigned URL, uploads the bytes, and waits for `status=uploaded`. Accepts `path`, `base64`, or a public `url`.
- **`manus_task_wait`** — polls a task until it reaches a terminal status (`stopped` / `waiting` / `error`) and returns new messages plus wait details (event_id + response schema).
- **`manus_website_publish_and_wait`** — publishes a site and waits until it becomes `published` or `failed`.
### 3 webhook tools (read from the local SQLite DB)
- `manus_webhook_events_list` — list received events with filters.
- `manus_webhook_events_get` — fetch an event by `event_id`.
- `manus_webhook_events_clear` — delete received events.
Total: **36 MCP tools**.
## Installation
```bash
pip install manus-mcp
```
This pulls the package from PyPI. The `manus-mcp` and `manus-mcp-webhook` console scripts become available, plus the `python -m manus_mcp` and `python -m manus_mcp.webhook_receiver` module entry points.
### From source (for contributors)
```bash
git clone https://github.com/aruxojuyu665/Manus-MCP.git
cd Manus-MCP
python -m venv .venv
# Windows:
.venv\Scripts\activate
# macOS/Linux:
# source .venv/bin/activate
pip install -e ".[dev]"
```
## API key configuration
Place a `.env` file in the project root (a `.env.example` template is provided). Both variable names are accepted:
```env
MANUS_API_KEY=sk-...
# or, for backwards compatibility:
# ManusAPI=sk-...
```
Priority: `process.env` > `.env`.
Optional settings:
```env
MANUS_BASE_URL=https://api.manus.ai
MANUS_HTTP_TIMEOUT=60
MANUS_LOG_LEVEL=INFO
```
## Registering with Claude Code
Add the following to `.claude/settings.json` (project-level) or `~/.claude/settings.json` (global):
```json
{
"mcpServers": {
"manus": {
"command": "python",
"args": ["-m", "manus_mcp"],
"cwd": "path/to/Manus-MCP"
}
}
}
```
If `MANUS_API_KEY` is not set globally, pass it explicitly:
```json
{
"mcpServers": {
"manus": {
"command": "python",
"args": ["-m", "manus_mcp"],
"cwd": "path/to/Manus-MCP",
"env": { "MANUS_API_KEY": "sk-..." }
}
}
}
```
Restart Claude Code — the `manus_*` tools will appear in the tool list.
## Verification
Without launching the server:
```bash
python scripts/list_tools.py
```
Against the real API:
```bash
python scripts/smoke.py
```
Unit tests:
```bash
pytest
```
Lint and types:
```bash
ruff check .
mypy manus_mcp
```
## Usage examples from Claude Code
```
manus_task_create { "message": { "content": "Give me a 5-bullet summary of today's AI news" } }
```
The response contains a `task_id`. Then:
```
manus_task_wait { "task_id": "<id>", "timeout_sec": 600 }
```
When it finishes you get `last_assistant_message` with the final reply and `new_messages` with the conversation history.
Uploading a file and attaching it to a task:
```
manus_file_upload { "source": { "path": "C:/docs/report.pdf" } }
manus_task_create {
"message": {
"content": [
{ "type": "text", "text": "Summarize this report" },
{ "type": "file", "file_id": "<file_id>" }
]
}
}
```
## Webhook receiver (optional)
A local receiver for `task_created` / `task_stopped` events with full RSA-SHA256 signature verification per `ManusAPIDocs/webhooks/security.md`.
### 1. Configure environment variables
```env
MANUS_WEBHOOK_PUBLIC_URL=https://your-tunnel.example.com/manus/webhook
MANUS_WEBHOOK_HOST=127.0.0.1
MANUS_WEBHOOK_PORT=8787
# MANUS_WEBHOOK_DB_PATH=... # defaults to %LOCALAPPDATA%\manus-mcp\events.db on Windows
```
`MANUS_WEBHOOK_PUBLIC_URL` **must exactly match** the URL Manus calls. The Manus signing payload includes this URL, so even a typo will cause every event to be rejected.
### 2. Start a tunnel
For example, with Cloudflare Tunnel:
```bash
cloudflared tunnel --url http://localhost:8787
```
or ngrok:
```bash
ngrok http 8787
```
### 3. Run the receiver
```bash
python -m manus_mcp.webhook_receiver
# or: manus-mcp-webhook --host 127.0.0.1 --port 8787
```
### 4. Register the webhook with Manus
In Claude Code:
```
manus_webhook_create { "url": "https://your-tunnel.example.com/manus/webhook" }
```
### 5. Read events
```
manus_webhook_events_list { "event_type": "task_stopped", "limit": 20 }
manus_webhook_events_get { "event_id": "..." }
manus_webhook_events_clear { "before_received_at": 1704000000 }
```
## Architecture
```
manus_mcp/
├── __main__.py # stdio entrypoint
├── server.py # MCP Server bootstrap
├── config.py # pydantic-settings
├── logger.py # stderr-only logger
├── client/
│ ├── manus_client.py # async httpx client + retry
│ ├── rate_limiter.py # per-endpoint token bucket (from rate-limits.md)
│ ├── retry.py # exponential backoff + jitter
│ └── errors.py # ManusApiError / ManusNetworkError
├── schemas/ # pydantic models for each resource (tasks, projects, ...)
├── tools/ # @manus_tool registration for all 36 tools
│ ├── tasks.py projects.py skills.py agents.py
│ ├── files.py webhooks.py usage.py connectors.py
│ ├── browser.py website.py
│ └── composite.py # task_wait / file_upload / website_publish_and_wait
└── webhook_receiver/
├── signature.py # RSA-SHA256 verification using {ts}.{url}.{sha256_hex(body)}
├── storage.py # SQLite WAL
├── server.py # Starlette + uvicorn
├── tools.py # events_list / events_get / events_clear
└── __main__.py
```
## Rate limits
The client honours the API limits (60-second sliding window) and transparently retries 429 responses with backoff + jitter. Limits come from `ManusAPIDocs/getting-started/rate-limits.md`:
- 10/min: `task.create`, `task.sendMessage`
- 40/min: all mutations
- 100/min: all read-only calls
- 600/min: `usage.*`
## Security
- The API key is never logged.
- The webhook receiver verifies signatures and rejects timestamps older than 5 minutes.
- The public key is cached for 1 hour.
- SQLite is opened in WAL mode with `check_same_thread=False` for safe multi-reader access.
## License
MIT.