Skip to main content

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

Learn more about MCP


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

  1. pCloudy accountSign up here
  2. Python ≥ 3.10
  3. uvInstall 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_id and context_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)DEBUGINFOERROR

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

  1. Create or extend a file in src/pcloudy_mcp/tools/
  2. Define a function decorated with @mcp_instance.tool(name=..., description=...)
  3. Register it in the tool config (see utils/tools_auto_registry.py)
  4. Add corresponding API methods to src/pcloudy_mcp/api/ if needed
  5. Add the endpoint constant to Constant/constant.py

Contributors

Maintained by the pCloudy / SSTS team.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pcloudy_mcp-0.1.4.tar.gz (92.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pcloudy_mcp-0.1.4-py3-none-any.whl (47.7 kB view details)

Uploaded Python 3

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

Hashes for pcloudy_mcp-0.1.4.tar.gz
Algorithm Hash digest
SHA256 cf8a30ab380253f2ade58754efcfd59cf1225cc062042421b5a68b9ec970e187
MD5 b23ed6b8e34764b4be4bc43f2a612b7d
BLAKE2b-256 b0ccab8feea1a5133f4431eb43735c9ed412ee3e16bba9f61538140cea41d397

See more details on using hashes here.

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

Hashes for pcloudy_mcp-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 7f070e68207832dbe3fa3429ea59fbd8469c9561aa183faa8c126f3e8b071ec3
MD5 c70983969848b126faa2bc6bb7c18c45
BLAKE2b-256 31480074733dc0a851dcb9b2f89a6f68dcb00b3b09001156a1f748ce951b73fa

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page