cua-mcp-server 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cua-mcp-server might be problematic. Click here for more details.

@@ -0,0 +1,125 @@
1
+ Metadata-Version: 2.1
2
+ Name: cua-mcp-server
3
+ Version: 0.1.0
4
+ Summary: MCP Server for Computer-Use Agent (CUA)
5
+ Author-Email: TryCua <gh@trycua.com>
6
+ Requires-Python: <3.13,>=3.10
7
+ Requires-Dist: mcp<2.0.0,>=1.6.0
8
+ Requires-Dist: cua-agent<0.2.0,>=0.1.0
9
+ Requires-Dist: cua-computer<0.2.0,>=0.1.0
10
+ Description-Content-Type: text/markdown
11
+
12
+ <div align="center">
13
+ <h1>
14
+ <div class="image-wrapper" style="display: inline-block;">
15
+ <picture>
16
+ <source media="(prefers-color-scheme: dark)" alt="logo" height="150" srcset="../../img/logo_white.png" style="display: block; margin: auto;">
17
+ <source media="(prefers-color-scheme: light)" alt="logo" height="150" srcset="../../img/logo_black.png" style="display: block; margin: auto;">
18
+ <img alt="Shows my svg">
19
+ </picture>
20
+ </div>
21
+
22
+ [![Python](https://img.shields.io/badge/Python-333333?logo=python&logoColor=white&labelColor=333333)](#)
23
+ [![macOS](https://img.shields.io/badge/macOS-000000?logo=apple&logoColor=F0F0F0)](#)
24
+ [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?&logo=discord&logoColor=white)](https://discord.com/invite/mVnXXpdE85)
25
+ [![PyPI](https://img.shields.io/pypi/v/cua-computer?color=333333)](https://pypi.org/project/cua-computer/)
26
+ </h1>
27
+ </div>
28
+
29
+ **cua-mcp-server** is a MCP server for the Computer-Use Agent (CUA), allowing you to run CUA through Claude Desktop or other MCP clients.
30
+ ### Get started with Agent
31
+
32
+ ## Installation
33
+
34
+ Install the package from PyPI:
35
+
36
+ ```bash
37
+ pip install cua-mcp-server
38
+ ```
39
+
40
+ This will install:
41
+ - The MCP server
42
+ - CUA agent and computer dependencies
43
+ - An executable `cua-mcp-server` script in your PATH
44
+
45
+ ## Easy Setup Script
46
+
47
+ If you want to simplify installation, you can use this one-liner to download and run a setup script:
48
+
49
+ ```bash
50
+ curl -fsSL https://raw.githubusercontent.com/trycua/cua/main/libs/mcp-server/scripts/run_mcp_server.sh | bash
51
+ ```
52
+
53
+ Or use the script directly in your MCP configuration like this:
54
+
55
+ ```json
56
+ {
57
+ "mcpServers": {
58
+ "cua-agent": {
59
+ "command": "/bin/bash",
60
+ "args": ["-c", "curl -fsSL https://raw.githubusercontent.com/trycua/cua/main/libs/mcp-server/scripts/run_mcp_server.sh | bash"],
61
+ "env": {
62
+ "CUA_AGENT_LOOP": "OMNI",
63
+ "CUA_MODEL_PROVIDER": "ANTHROPIC",
64
+ "CUA_MODEL_NAME": "claude-3-7-sonnet-20250219",
65
+ "ANTHROPIC_API_KEY": "your-api-key"
66
+ }
67
+ }
68
+ }
69
+ }
70
+ ```
71
+
72
+ This script will automatically check if cua-mcp-server is installed, install it if needed, and run it.
73
+
74
+ ## Claude Desktop Integration
75
+
76
+ To use with Claude Desktop, add an entry to your Claude Desktop configuration (`claude_desktop_config.json`, typically found in `~/.config/claude-desktop/`):
77
+
78
+ For more information on MCP with Claude Desktop, see the [official MCP User Guide](https://modelcontextprotocol.io/quickstart/user).
79
+
80
+ ## Cursor Integration
81
+
82
+ To use with Cursor, add an MCP configuration file in one of these locations:
83
+
84
+ - **Project-specific**: Create `.cursor/mcp.json` in your project directory
85
+ - **Global**: Create `~/.cursor/mcp.json` in your home directory
86
+
87
+ After configuration, you can simply tell Cursor's Agent to perform computer tasks by explicitly mentioning the CUA agent, such as "Use the computer control tools to open Safari."
88
+
89
+ For more information on MCP with Cursor, see the [official Cursor MCP documentation](https://docs.cursor.com/context/model-context-protocol).
90
+
91
+ ### First-time Usage Notes
92
+
93
+ **API Keys**: Ensure you have valid API keys:
94
+ - Add your Anthropic API key, or other model provider API key in the Claude Desktop config (as shown above)
95
+ - Or set it as an environment variable in your shell profile
96
+
97
+ ## Configuration
98
+
99
+ The server is configured using environment variables (can be set in the Claude Desktop config):
100
+
101
+ | Variable | Description | Default |
102
+ |----------|-------------|---------|
103
+ | `CUA_AGENT_LOOP` | Agent loop to use (OPENAI, ANTHROPIC, OMNI) | OMNI |
104
+ | `CUA_MODEL_PROVIDER` | Model provider (ANTHROPIC, OPENAI, OLLAMA, OAICOMPAT) | ANTHROPIC |
105
+ | `CUA_MODEL_NAME` | Model name to use | None (provider default) |
106
+ | `CUA_PROVIDER_BASE_URL` | Base URL for provider API | None |
107
+ | `CUA_MAX_IMAGES` | Maximum number of images to keep in context | 3 |
108
+
109
+ ## Available Tools
110
+
111
+ The MCP server exposes the following tools to Claude:
112
+
113
+ 1. `run_cua_task` - Run a single Computer-Use Agent task with the given instruction
114
+ 2. `run_multi_cua_tasks` - Run multiple tasks in sequence
115
+
116
+ ## Usage
117
+
118
+ Once configured, you can simply ask Claude to perform computer tasks:
119
+
120
+ - "Open Chrome and go to github.com"
121
+ - "Create a folder called 'Projects' on my desktop"
122
+ - "Find all PDFs in my Downloads folder"
123
+ - "Take a screenshot and highlight the error message"
124
+
125
+ Claude will automatically use your CUA agent to perform these tasks.
@@ -0,0 +1,7 @@
1
+ cua_mcp_server-0.1.0.dist-info/METADATA,sha256=zLOH8ezpPPF07DrAo_fvXdHkaappV_97ypFC_2oBsFE,4716
2
+ cua_mcp_server-0.1.0.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
3
+ cua_mcp_server-0.1.0.dist-info/entry_points.txt,sha256=Y3uEunDRfoc-RUDS3HnD942RCxYKquiyk-2HRSqphoc,74
4
+ mcp_server/__init__.py,sha256=G5Bps3KxzYfH79B1TDVQI9vbzjamC_mdgi7GJMgbVcA,575
5
+ mcp_server/__main__.py,sha256=BE2ManEiNpz56nqc7Z_asNjQ6TPtvyu5AbWbyJFePnM,132
6
+ mcp_server/server.py,sha256=RdM0kytzt8uF-vbqPXQ3oay-jtGhum4k_Z0jTDZmfoc,6547
7
+ cua_mcp_server-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: pdm-backend (2.4.4)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,5 @@
1
+ [console_scripts]
2
+ cua-mcp-server = mcp_server.server:main
3
+
4
+ [gui_scripts]
5
+
mcp_server/__init__.py ADDED
@@ -0,0 +1,19 @@
1
+ """MCP Server for Computer-Use Agent (CUA)."""
2
+
3
+ import sys
4
+ import os
5
+
6
+ # Add detailed debugging at import time
7
+ with open("/tmp/mcp_server_debug.log", "w") as f:
8
+ f.write(f"Python executable: {sys.executable}\n")
9
+ f.write(f"Python version: {sys.version}\n")
10
+ f.write(f"Working directory: {os.getcwd()}\n")
11
+ f.write(f"Python path:\n{chr(10).join(sys.path)}\n")
12
+ f.write(f"Environment variables:\n")
13
+ for key, value in os.environ.items():
14
+ f.write(f"{key}={value}\n")
15
+
16
+ from .server import server, main
17
+
18
+ __version__ = "0.1.0"
19
+ __all__ = ["server", "main"]
mcp_server/__main__.py ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env python
2
+ """Entry point for the MCP server module."""
3
+
4
+ from .server import main
5
+
6
+ if __name__ == "__main__":
7
+ main()
mcp_server/server.py ADDED
@@ -0,0 +1,193 @@
1
+ import asyncio
2
+ import logging
3
+ import os
4
+ import sys
5
+ import traceback
6
+ from typing import Any, Dict, List, Optional, Union
7
+
8
+ # Configure logging to output to stderr for debug visibility
9
+ logging.basicConfig(
10
+ level=logging.DEBUG, # Changed to DEBUG
11
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
12
+ stream=sys.stderr,
13
+ )
14
+ logger = logging.getLogger("mcp-server")
15
+
16
+ # More visible startup message
17
+ logger.debug("MCP Server module loading...")
18
+
19
+ try:
20
+ from mcp.server.fastmcp import Context, FastMCP
21
+
22
+ logger.debug("Successfully imported FastMCP")
23
+ except ImportError as e:
24
+ logger.error(f"Failed to import FastMCP: {e}")
25
+ traceback.print_exc(file=sys.stderr)
26
+ sys.exit(1)
27
+
28
+ try:
29
+ from computer import Computer
30
+ from agent import ComputerAgent, LLMProvider, LLM, AgentLoop
31
+
32
+ logger.debug("Successfully imported Computer and Agent modules")
33
+ except ImportError as e:
34
+ logger.error(f"Failed to import Computer/Agent modules: {e}")
35
+ traceback.print_exc(file=sys.stderr)
36
+ sys.exit(1)
37
+
38
+ # Global computer instance for reuse
39
+ global_computer = None
40
+
41
+
42
+ def get_env_bool(key: str, default: bool = False) -> bool:
43
+ """Get boolean value from environment variable."""
44
+ return os.getenv(key, str(default)).lower() in ("true", "1", "yes")
45
+
46
+
47
+ def serve() -> FastMCP:
48
+ """Create and configure the MCP server."""
49
+ server = FastMCP("cua-agent")
50
+
51
+ @server.tool()
52
+ async def run_cua_task(ctx: Context, task: str) -> str:
53
+ """
54
+ Run a Computer-Use Agent (CUA) task and return the results.
55
+
56
+ Args:
57
+ ctx: The MCP context
58
+ task: The instruction or task for the agent to perform
59
+
60
+ Returns:
61
+ A string containing the agent's response
62
+ """
63
+ global global_computer
64
+
65
+ try:
66
+ logger.info(f"Starting CUA task: {task}")
67
+
68
+ # Initialize computer if needed
69
+ if global_computer is None:
70
+ global_computer = Computer(verbosity=logging.INFO)
71
+ await global_computer.run()
72
+
73
+ # Determine which loop to use
74
+ loop_str = os.getenv("CUA_AGENT_LOOP", "OMNI")
75
+ if loop_str == "OPENAI":
76
+ loop = AgentLoop.OPENAI
77
+ elif loop_str == "ANTHROPIC":
78
+ loop = AgentLoop.ANTHROPIC
79
+ else:
80
+ loop = AgentLoop.OMNI
81
+
82
+ # Determine provider
83
+ provider_str = os.getenv("CUA_MODEL_PROVIDER", "ANTHROPIC")
84
+ provider = getattr(LLMProvider, provider_str)
85
+
86
+ # Get model name (if specified)
87
+ model_name = os.getenv("CUA_MODEL_NAME", None)
88
+
89
+ # Get base URL for provider (if needed)
90
+ provider_base_url = os.getenv("CUA_PROVIDER_BASE_URL", None)
91
+
92
+ # Create agent with the specified configuration
93
+ agent = ComputerAgent(
94
+ computer=global_computer,
95
+ loop=loop,
96
+ model=LLM(
97
+ provider=provider,
98
+ name=model_name,
99
+ provider_base_url=provider_base_url,
100
+ ),
101
+ save_trajectory=False,
102
+ only_n_most_recent_images=int(os.getenv("CUA_MAX_IMAGES", "3")),
103
+ verbosity=logging.INFO,
104
+ )
105
+
106
+ # Collect all results
107
+ full_result = ""
108
+ async for result in agent.run(task):
109
+ logger.info(f"Agent step complete: {result.get('id', 'unknown')}")
110
+
111
+ # Add response ID to output
112
+ full_result += f"\n[Response ID: {result.get('id', 'unknown')}]\n"
113
+
114
+ # Extract and concatenate text responses
115
+ if "text" in result:
116
+ # Handle both string and dict responses
117
+ text_response = result.get("text", "")
118
+ if isinstance(text_response, str):
119
+ full_result += f"Response: {text_response}\n"
120
+ else:
121
+ # If it's a dict or other structure, convert to string representation
122
+ full_result += f"Response: {str(text_response)}\n"
123
+
124
+ # Log detailed information
125
+ if "tools" in result:
126
+ tools_info = result.get("tools")
127
+ logger.debug(f"Tools used: {tools_info}")
128
+ full_result += f"\nTools used: {tools_info}\n"
129
+
130
+ # Process output if available
131
+ outputs = result.get("output", [])
132
+ for output in outputs:
133
+ output_type = output.get("type")
134
+ if output_type == "reasoning":
135
+ logger.debug(f"Reasoning: {output}")
136
+ full_result += f"\nReasoning: {output.get('content', '')}\n"
137
+ elif output_type == "computer_call":
138
+ logger.debug(f"Computer call: {output}")
139
+ action = output.get("action", "")
140
+ result_value = output.get("result", "")
141
+ full_result += f"\nComputer Action: {action}\nResult: {result_value}\n"
142
+
143
+ # Add separator between steps
144
+ full_result += "\n" + "-" * 40 + "\n"
145
+
146
+ logger.info(f"CUA task completed successfully")
147
+ return full_result or "Task completed with no text output."
148
+
149
+ except Exception as e:
150
+ error_msg = f"Error running CUA task: {str(e)}\n{traceback.format_exc()}"
151
+ logger.error(error_msg)
152
+ return f"Error during task execution: {str(e)}"
153
+
154
+ @server.tool()
155
+ async def run_multi_cua_tasks(ctx: Context, tasks: List[str]) -> str:
156
+ """
157
+ Run multiple CUA tasks in sequence and return the combined results.
158
+
159
+ Args:
160
+ ctx: The MCP context
161
+ tasks: List of tasks to run in sequence
162
+
163
+ Returns:
164
+ Combined results from all tasks
165
+ """
166
+ results = []
167
+
168
+ for i, task in enumerate(tasks):
169
+ logger.info(f"Running task {i+1}/{len(tasks)}: {task}")
170
+ result = await run_cua_task(ctx, task)
171
+ results.append(f"Task {i+1}: {task}\nResult: {result}\n")
172
+
173
+ return "\n".join(results)
174
+
175
+ return server
176
+
177
+
178
+ server = serve()
179
+
180
+
181
+ def main():
182
+ """Run the MCP server."""
183
+ try:
184
+ logger.debug("Starting MCP server...")
185
+ server.run()
186
+ except Exception as e:
187
+ logger.error(f"Error starting server: {e}")
188
+ traceback.print_exc(file=sys.stderr)
189
+ sys.exit(1)
190
+
191
+
192
+ if __name__ == "__main__":
193
+ main()