Metadata-Version: 2.4
Name: manus-mcp
Version: 0.1.1
Summary: MCP server exposing the full Manus API v2 surface to Claude Code
Project-URL: Homepage, https://github.com/aruxojuyu665/Manus-MCP
Project-URL: Repository, https://github.com/aruxojuyu665/Manus-MCP
Project-URL: Issues, https://github.com/aruxojuyu665/Manus-MCP/issues
Project-URL: Changelog, https://github.com/aruxojuyu665/Manus-MCP/blob/main/CHANGELOG.md
Author: Manus MCP Contributors
License: MIT
License-File: LICENSE
Keywords: ai-agent,claude-code,manus,mcp
Requires-Python: >=3.11
Requires-Dist: cryptography>=42.0
Requires-Dist: httpx>=0.27
Requires-Dist: mcp>=1.2.0
Requires-Dist: pydantic-settings>=2.3
Requires-Dist: pydantic>=2.7
Requires-Dist: python-dotenv>=1.0
Requires-Dist: starlette>=0.37
Requires-Dist: uvicorn>=0.30
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pre-commit>=4; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-cov>=5; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Description-Content-Type: text/markdown

# Manus MCP

[![CI](https://github.com/aruxojuyu665/Manus-MCP/actions/workflows/ci.yml/badge.svg)](https://github.com/aruxojuyu665/Manus-MCP/actions/workflows/ci.yml)
[![Live](https://github.com/aruxojuyu665/Manus-MCP/actions/workflows/live.yml/badge.svg)](https://github.com/aruxojuyu665/Manus-MCP/actions/workflows/live.yml)

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
cd path/to/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.
