MCP server for pCloudy
Project description
pCloudy MCP Server
Connect AI agents to real devices and QPilot automation — via the Model Context Protocol.
What is MCP?
MCP (Model Context Protocol) is an open standard that lets AI tools (Claude, Cursor, VS Code Copilot, etc.) call external tools and APIs in a structured, vendor-neutral way. Think of it as a USB-C port for AI — one protocol, any device.
- Plug AI assistants into real APIs without writing glue code
- Switch LLM vendors with zero changes to your tools
- Keep data and credentials inside your own infrastructure
What does pCloudy MCP do?
pCloudy-MCP is an MCP server that bridges AI agents to the pCloudy Device Cloud and QPilot test automation engine. With it, an AI agent can:
- Book and release real Android/iOS devices or cloud browsers
- Upload and install apps
- Create, manage, and execute QPilot test cases using natural language
- Inspect test step results and debug failures in real-time
Architecture Overview
┌─────────────────────────────────────────────────────────────────┐
│ AI Client (Claude / Cursor / etc.) │
└────────────────────────────┬────────────────────────────────────┘
│ MCP Protocol (stdio / SSE)
┌────────────────────────────▼────────────────────────────────────┐
│ pCloudy MCP Server │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Device Cloud │ │ Browser Cloud│ │ QPilot Tools │ │
│ │ Tools │ │ Tools │ │ (v2 architecture) │ │
│ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────┘ │
│ │ │ │ │
│ ┌──────▼───────┐ ┌──────▼───────┐ ┌──────────▼───────────┐ │
│ │ Device API │ │ Browser API │ │ QPilot API v2 │ │
│ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────┘ │
└─────────┼─────────────────┼──────────────────── ┼─────────────┘
│ │ │
▼ ▼ ▼
pCloudy Device pCloudy Browser QPilot Backend
Cloud REST Cloud REST REST + WebSocket
QPilot v2 — How It Works
QPilot v2 introduced a real-time WebSocket playground model. Each test execution is a live session, not a batch job. Here is the full lifecycle:
┌──────────────────────────────────────────────────────────────────┐
│ QPilot Session Lifecycle │
│ │
│ 1. create_qpilot_playground_session │
│ │ │
│ ├─► Books device / browser via pCloudy │
│ ├─► Opens Socket.IO connection to QPilot backend │
│ ├─► Receives req_play_id + context_id │
│ └─► Writes session file: .qpilot-session-details/<test_id>/ │
│ │
│ 2. add_qpilot_step / insert_qpilot_step / retry_qpilot_step │
│ │ │
│ ├─► Calls QPilot REST API to submit the step │
│ └─► Waits for step result via 3-path resolution: │
│ │
│ Path 1 (fastest) ── In-memory event list │
│ Written by WebSocket listener │
│ │
│ Path 2 (fallback) ─ .playground_events.jsonl file │
│ Written in parallel by the socket │
│ │
│ Path 3 (120s+) ──── REST API polling │
│ get_test_list until terminal status │
│ │
│ → First path to return a terminal status wins. │
│ Other paths are cancelled via stop_event. │
│ │
│ 3. close_qpilot_playground_session │
│ │ │
│ ├─► Calls finish_test API │
│ ├─► Closes Socket.IO connection │
│ └─► Cleans up local session files │
└──────────────────────────────────────────────────────────────────┘
Step Status Resolution (in detail)
When a step is submitted, pCloudy MCP immediately spawns three concurrent workers:
| Worker | Mechanism | Activates |
|---|---|---|
| Memory | Reads events pushed by the live WebSocket connection | Instantly |
| File | Tails .playground_events.jsonl written by the socket |
Within 60s of file appearing |
| API Poller | Polls GET /api/v2/test/fetch with the test ID |
After 120s timeout guard |
The first worker to report a terminal status (passed, failed, error, skipped, Aborted) wins. The other two are stopped via a shared threading.Event.
Project Structure
src/pcloudy_mcp/
├── main.py # CLI entry point — loads config, sets up logging, starts server
├── server.py # FastMCP server factory and lifecycle
│
├── api/ # Raw HTTP wrappers (one class per domain)
│ ├── http_client.py # Base async HTTPX client with retry + token masking
│ ├── authenticate.py # Token fetch + cache
│ ├── device_booking.py # Device book/release/live-view
│ ├── browser_booking.py # Browser VM book/view
│ └── qpilot.py # QPilot v2 REST endpoints
│
├── socket/
│ └── qpilot/
│ ├── qpilot_ws_client.py # Socket.IO client (PlaygroundSocket)
│ └── qpilot_ws_connection.py # Session open/close/send, _active_sessions registry
│
├── tools/ # MCP tool definitions (registered into FastMCP)
│ ├── device_booking.py # Device cloud tools
│ ├── browser_booking.py # Browser cloud tools
│ ├── app_management.py # App upload/install tools
│ └── qpilot.py # All QPilot tools (23 tools)
│
├── utils/
│ ├── config.py # Env var loading via Pydantic BaseSettings
│ ├── file_utils.py # Path helpers (project root, ensure_path)
│ ├── qpilot_utils.py # get_step_status (3-path resolution), session helpers
│ └── tools_response_format.py # Standardised MCP response builder
│
├── Constant/
│ └── constant.py # All API endpoints, QPilot server routing, retry config
│
├── logger/
│ └── logger.py # Custom VERBOSE log level (level 5), setup_logging()
│
├── validation/
│ └── validation.py # Pydantic settings model — validates all env vars
│
└── errors/
└── exception.py # McpToolError, ServerInitializationError
Prerequisites
- pCloudy account — Sign up here
- Python ≥ 3.10
- uv — Install uv
Configuration
Environment Variables
| Variable | Required | Description |
|---|---|---|
PCLOUDY_USERNAME |
Yes | Your pCloudy account email |
PCLOUDY_API_KEY |
Yes | API key from your pCloudy account settings |
PCLOUDY_CLOUD_URL |
Yes | Your pCloudy instance URL e.g. https://device.pcloudy.com |
PCLOUDY_VERBOSE_LOGS |
No | Set to true to enable verbose logging (default: false) |
Claude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"pCloudy": {
"command": "uvx",
"args": ["pcloudy-mcp"],
"env": {
"PCLOUDY_USERNAME": "<your_pcloudy_username>",
"PCLOUDY_API_KEY": "<your_pcloudy_api_key>",
"PCLOUDY_CLOUD_URL": "<your_pcloudy_cloud_url>"
}
}
}
}
Cursor (mcp.json)
{
"mcpServers": {
"pCloudy": {
"command": "uvx",
"args": ["pcloudy-mcp"],
"env": {
"PCLOUDY_USERNAME": "<your_pcloudy_username>",
"PCLOUDY_API_KEY": "<your_pcloudy_api_key>",
"PCLOUDY_CLOUD_URL": "<your_pcloudy_cloud_url>"
}
}
}
}
Tool Reference
Device Cloud
| Tool | Description |
|---|---|
get_device_list |
List available real devices filtered by platform (android / ios) |
get_device_details |
Get detailed information about a specific device by ID |
book_device |
Book a device by name for a testing session |
release_device |
Release a booked device using its booking ID (RID) |
get_live_view_url |
Get the live view URL for a currently booked device |
refresh_authentication |
Refresh the user authentication token |
App Management
| Tool | Description |
|---|---|
get_all_application |
List all apps uploaded to the pCloudy account |
upload_app |
Upload an APK or IPA file to pCloudy |
install_app |
Install an uploaded app on a booked device |
resign_app |
Re-sign an IPA to make it installable on a specific iOS device |
Browser Cloud
| Tool | Description |
|---|---|
get_browser_list |
List available browser VMs by machine type |
book_browser |
Book a cloud browser session on a specified VM |
get_browser_live_view_url |
Get the live view URL for a booked browser session |
release_browser |
Release a booked browser session |
QPilot — Project & Test Management
| Tool | Description |
|---|---|
get_qpilot_projects |
List all owned QPilot projects |
create_qpilot_project |
Create a new QPilot project |
delete_qpilot_project |
Delete a QPilot project by name |
get_qpilot_folders |
List all folders across owned projects |
create_qpilot_folder |
Create a new folder inside a project |
delete_qpilot_folder |
Delete a folder by name |
get_qpilot_tests |
List all tests with step counts, grouped by project and folder |
create_qpilot_test |
Create a new test for android, ios, or web |
delete_qpilot_test |
Delete a test by name |
QPilot — Playground Session
| Tool | Description |
|---|---|
create_qpilot_playground_session |
Open a live WebSocket session for a test; books the required device or browser |
close_qpilot_playground_session |
Close an active session, release the device/browser, and clean up local files |
QPilot — Step Operations
| Tool | Description |
|---|---|
add_qpilot_step |
Append a natural language step and block until it reaches a terminal status |
insert_qpilot_step |
Insert a step at a specific 0-based index, shifting later steps forward |
delete_qpilot_step |
Delete the step at a specific 0-based index |
get_qpilot_test_steps |
List all steps with their index, content, status, and AI reasoning |
retry_qpilot_step |
Retry a single step by its 0-based index |
retry_qpilot_steps_from |
Retry a step and all subsequent steps (cascade retry) |
restart_qpilot_test |
Re-execute all steps from the beginning |
QPilot — Test Data
Test data is a JSON key-value store attached to a test, used to inject dynamic values into steps.
| Tool | Description |
|---|---|
get_qpilot_test_data |
Retrieve all key-value pairs stored on a test |
add_qpilot_test_data |
Add new keys or overwrite existing ones with a JSON object |
modify_qpilot_test_data |
Update values for existing keys only (cannot add new keys) |
delete_qpilot_test_data |
Remove specific keys; at least one key must remain |
Typical QPilot Workflow
1. Create project and folder (once per project)
get_qpilot_projects → create_qpilot_project → create_qpilot_folder
2. Create a test
create_qpilot_test (platform: android/ios/web, folder_id, app info)
3. (Optional) Pre-populate test data
add_qpilot_test_data → { "username": "demo@example.com", "password": "secret" }
4. Open a session
get_device_list → create_qpilot_playground_session (test_id, device_id)
5. Add steps one at a time (each call blocks until the step completes)
add_qpilot_step → "Tap on Login button"
add_qpilot_step → "Enter username from test data"
add_qpilot_step → "Verify home screen is visible"
6. Inspect, retry, or fix steps
get_qpilot_test_steps ← see all steps + status + reasoning
retry_qpilot_step ← retry step 2 only
retry_qpilot_steps_from ← retry from step 2 onwards
7. Close and save
close_qpilot_playground_session
Verbose Logging
Set PCLOUDY_VERBOSE_LOGS=true to enable detailed logs including:
- WebSocket connection lifecycle (connect, disconnect, booking details)
- Session open/close events with
req_play_idandcontext_id - Step index calculations and submission
- API polling activation (when WS/file paths take > 120s)
"env": {
"PCLOUDY_VERBOSE_LOGS": "true"
}
Log levels in order of verbosity: VERBOSE (5) → DEBUG → INFO → ERROR
Security note: Tokens and API keys are always masked as
*************in logs, regardless of log level.
Development
git clone https://github.com/Smart-Software-Testing-Solutions-Opkey/pcloudy-mcp-server.git
cd pcloudy-mcp-server
uv pip install .[dev]
Developer commands
poe format # black + isort
poe lint-check # ruff linter
poe typecheck # mypy
poe check-server # verify server starts correctly
Adding a new tool
- Create or extend a file in
src/pcloudy_mcp/tools/ - Define a function decorated with
@mcp_instance.tool(name=..., description=...) - Register it in the tool config (see
utils/tools_auto_registry.py) - Add corresponding API methods to
src/pcloudy_mcp/api/if needed - Add the endpoint constant to
Constant/constant.py
Contributors
Maintained by the pCloudy / SSTS team.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pcloudy_mcp-0.1.4.tar.gz.
File metadata
- Download URL: pcloudy_mcp-0.1.4.tar.gz
- Upload date:
- Size: 92.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.10.18
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cf8a30ab380253f2ade58754efcfd59cf1225cc062042421b5a68b9ec970e187
|
|
| MD5 |
b23ed6b8e34764b4be4bc43f2a612b7d
|
|
| BLAKE2b-256 |
b0ccab8feea1a5133f4431eb43735c9ed412ee3e16bba9f61538140cea41d397
|
File details
Details for the file pcloudy_mcp-0.1.4-py3-none-any.whl.
File metadata
- Download URL: pcloudy_mcp-0.1.4-py3-none-any.whl
- Upload date:
- Size: 47.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.10.18
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7f070e68207832dbe3fa3429ea59fbd8469c9561aa183faa8c126f3e8b071ec3
|
|
| MD5 |
c70983969848b126faa2bc6bb7c18c45
|
|
| BLAKE2b-256 |
31480074733dc0a851dcb9b2f89a6f68dcb00b3b09001156a1f748ce951b73fa
|