Multi Tool Server
БесплатноНе проверенA Docker-based FastMCP server for dynamic Python code execution with support for multiple MCP clients, automatic validation, runtime installation, and browser a
Описание
A Docker-based FastMCP server for dynamic Python code execution with support for multiple MCP clients, automatic validation, runtime installation, and browser automation.
README
A powerful Docker-based FastMCP server that enables dynamic Python code execution with access to multiple MCP (Model Context Protocol) clients, featuring automatic validation and runtime installation of MCP tools.
Table of Contents
- Features
- Architecture
- Quick Start
- MCP Tools Validation
- Configuration
- Usage
- Examples
- Pre-configured MCP Servers
- Development
- Security
- Monitoring
- Troubleshooting
- API Reference
- Performance
- Implementation Details
- Issues Resolved
- Browser Automation
- Manual Install Support
Features
Core Features
- 🚀 Dynamic Code Execution: Execute Python code at runtime with full type safety
- 🔌 Multiple MCP Clients: Connect to and orchestrate multiple MCP servers simultaneously (9 clients)
- 🐳 Docker-Based: Lightweight, isolated, and easy to deploy
- 🔒 Secure Sandboxing: Isolated execution with timeout enforcement and resource limits
- 🌐 HTTP Transport: RESTful API access via FastMCP's HTTP transport
- ⚡ High Performance: Async/await architecture with connection pooling
- 🛡️ Production Ready: Health checks, monitoring, and graceful shutdown
- 🌐 Browser Automation: Playwright & Chrome DevTools integration with isolated instances
Validation & Installation Features
- ✅ Build-Time Package Installation: Pre-installs MCP packages during Docker build from
.envconfiguration - ✅ Automatic Validation: Validates all MCP servers on container startup
- ✅ Runtime Installation: Auto-installs npm and Python packages on-demand (with caching)
- ✅ Fast Startup: Pre-installed packages load in <1 second (70% faster than runtime installation)
- ✅ HTTP Server Checks: Verifies remote server reachability
- ✅ STDIO Server Support: Validates local command availability
- ✅ Flexible Modes: Default, strict, and no-install validation modes
- ✅ Manual Install Support: Custom installation commands for URLs, Git repos, build scripts
- ✅ Comprehensive Logging: Detailed progress with status indicators
- ✅ 27 Pre-configured Servers: Ready-to-use MCP server configurations
Browser Automation Features
- ✅ Real Browser Control: Headless Chromium with full page rendering
- ✅ Isolated Instances: Parallel requests with separate browser profiles
- ✅ Dynamic Version Detection: Automatic browser version handling
- ✅ Screenshot Capture: Full-page and element screenshots
- ✅ DOM Extraction: Retrieve visible text and HTML content
- ✅ JavaScript Execution: Run scripts in page context
- ✅ Network Monitoring: Track requests and responses
Architecture
┌─────────────────────────────────────────────────────────────┐
│ Docker Container │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Validation & Installation Layer │ │
│ │ - Validates MCP servers on startup │ │
│ │ - Installs npm packages (npx servers) │ │
│ │ - Pre-caches Python packages (uvx servers) │ │
│ │ - Verifies HTTP server reachability │ │
│ │ - Executes manual_install_command (custom installs) │ │
│ └────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ FastMCP HTTP Server (Port 8000) │ │
│ │ │ │
│ │ Tools: │ │
│ │ - list_tools() : Lists available MCP clients │ │
│ │ - multi_tool_executer(): Executes dynamic Python code│ │
│ └────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ MCP Clients Manager (9 clients) │ │
│ │ - context7_mcp_client │ │
│ │ - fetch_mcp_client │ │
│ │ - playwright_mcp_client (32 tools) │ │
│ │ - chrome-devtools_mcp_client (26 tools) │ │
│ │ - sqlite_mcp_client (6 tools) │ │
│ │ - git_mcp_client │ │
│ │ - ... (9 total) │ │
│ └────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Runtime Script Execution Engine │ │
│ │ - Generate unique UID per execution │ │
│ │ - Isolated sandbox environment │ │
│ │ - Timeout enforcement (60s default) │ │
│ │ - Automatic cleanup │ │
│ │ - Async/await support for MCP clients │ │
│ │ - extract_content helper for result parsing │ │
│ └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
External MCP Servers (HTTP/STDIO)
Quick Start
See QUICKSTART.md for a 5-minute setup guide.
Prerequisites
- Docker and Docker Compose
- Internet connection (for HTTP MCP servers and package installation)
1. Clone and Setup
git clone <repository-url>
cd wasm_mcp
# Copy example environment file
cp .env.example .env
2. Test Validation System
# Verify configuration
make test-validation
3. Build and Run
make build
make run
4. Verify Installation
# Check health
make health
# Validate MCP tools
make validate
Example:
- prompt: list complete documentations of vg-ui lib, using multi_mcp_executer
- tool_call_name: mcp__multi-mcp-executer__multi_tool_executer
- input:
{
"python_code": "async def execute_tools(initial_input, **kwargs):
"""
List all VG-UI components using the vg_ui_lib_mcp_server.
This will:
1. Get startup instructions
2. List all available components
3. Get component categories
"""
vg_ui_client = kwargs.get("vg_ui_lib_mcp_server_mcp_client")
extract_content = kwargs.get("extract_content")
if not vg_ui_client:
return {
"result": None,
"result_explanation": "VG-UI MCP client is not available in the configuration."
}
try:
results = {}
# Step 1: Get startup instructions
async with vg_ui_client:
startup_result = await vg_ui_client.call_tool(
"StartupInstructions",
{}
)
# Extract startup instructions
if hasattr(startup_result, 'content'):
content = startup_result.content
if isinstance(content, list) and len(content) > 0:
startup_text = content[0].text if hasattr(content[0], 'text') else str(content[0])
else:
startup_text = str(content)
else:
startup_text = str(startup_result)
results['startup_instructions'] = startup_text
# Step 2: List all components
async with vg_ui_client:
components_result = await vg_ui_client.call_tool(
"list_components",
{}
)
# Extract components list
if hasattr(components_result, 'content'):
content = components_result.content
if isinstance(content, list) and len(content) > 0:
components_text = content[0].text if hasattr(content[0], 'text') else str(content[0])
else:
components_text = str(content)
else:
components_text = str(components_result)
results['components'] = components_text
# Step 3: List categories
async with vg_ui_client:
categories_result = await vg_ui_client.call_tool(
"list_categories",
{}
)
# Extract categories
if hasattr(categories_result, 'content'):
content = categories_result.content
if isinstance(content, list) and len(content) > 0:
categories_text = content[0].text if hasattr(content[0], 'text') else str(content[0])
else:
categories_text = str(content)
else:
categories_text = str(categories_result)
results['categories'] = categories_text
# Step 4: List CSS categories
async with vg_ui_client:
css_categories_result = await vg_ui_client.call_tool(
"list_css_categories",
{}
)
# Extract CSS categories
if hasattr(css_categories_result, 'content'):
content = css_categories_result.content
if isinstance(content, list) and len(content) > 0:
css_categories_text = content[0].text if hasattr(content[0], 'text') else str(content[0])
else:
css_categories_text = str(content)
else:
css_categories_text = str(css_categories_result)
results['css_categories'] = css_categories_text
# Step 5: List schemas
async with vg_ui_client:
schemas_result = await vg_ui_client.call_tool(
"list_schemas",
{}
)
# Extract schemas
if hasattr(schemas_result, 'content'):
content = schemas_result.content
if isinstance(content, list) and len(content) > 0:
schemas_text = content[0].text if hasattr(content[0], 'text') else str(content[0])
else:
schemas_text = str(content)
else:
schemas_text = str(schemas_result)
results['schemas'] = schemas_text
return {
"result": results,
"result_explanation": "Successfully retrieved VG-UI library documentation including startup instructions, component list, categories, CSS categories, and schemas."
}
except Exception as e:
import traceback
error_details = traceback.format_exc()
return {
"result": None,
"result_explanation": f"Error while fetching VG-UI documentation: {str(e)}\
\
Traceback:\
{error_details}"
}
",
"input": {}
}
- output:
{
"success": true,
"result": {
"startup_instructions": "{"role":"assistant","content":{"type":"text","text":"\nThis MCP server provides access to VG UI Library web components documentation from the Storybook component registry.\nYou can use the tools to fetch, categorize, and retrieve information about VG web components, their schemas, CSS styles, and examples.\n\n# VG UI Library Web Components Documentation Server\n\n## Overview\nThis server provides access to comprehensive VG UI Library web component documentation including:\n- Component definitions with props, events, and slots\n- TypeScript type schemas and definitions\n- CSS variables and styling information (with categorization support)\n- Component examples and usage patterns with framework-specific filtering\n- Category-based component organization\n\n## Framework Filtering\nWhen fetching component examples, you can use the `--use-framework` command-line argument to filter sources for a specific framework:\n- Supported frameworks: `html`, `react`, `react19`, `vue`, `angular`, `lit`\n- Command-line usage: `--use-framework vue` will return only Vue.js code samples\n- Example: `python main.py --use-framework vue` or `fastmcp run main.py --use-framework react`\n- If no argument is provided or framework not found, all framework sources are returned\n\n## Instructions\n- List all VG UI Library components initially before starting implementation to understand available options\n- For any slot in web components, if documentation shows a `default` value, it can be used directly in the template\n- Use VG UI Library web components extensively and minimize custom HTML elements\n- Minimize custom CSS/styling and custom classes; prefer using VG component CSS properties and variables\n- In CSS: avoid absolute pixels, avoid custom colors (use VG CSS variables instead), prefer flexbox over grid\n- All VG UI Library components are prefixed with `vg-` (e.g., `vg-button`, `vg-card`, `vg-flex`)\n\n## Usage Guidelines\n1. Start by listing available components to understand what's available\n2. Use schema tools to understand TypeScript types and interfaces\n3. Get detailed component documentation for specific components\n4. Access CSS definitions for styling information\n5. Browse examples to see component usage patterns\n\n## Recommended Tool Usage Flow\n\n### For Finding and Using Components:\n1. **list_components** - Get overview of all available components (recommended first step)\n2. **search_components** - Search for components by name or category\n3. **get_component_by_tag** - Get specific component documentation with props, events, slots\n4. **get_component_example** - Get usage examples for specific component and example ID\n5. **list_categories** - Browse components by category\n\n### For TypeScript Types and Schemas:\n1. **list_schemas** - See available TypeScript types\n2. **get_schema_definition** - Get specific type definitions for interfaces, enums, and type aliases\n\n### For CSS Styles and Variables:\n1. **categorize_css** - Categorize CSS styles using LLM (run once per session, avoid unless necessary)\n2. **list_css_categories** - List all CSS categories\n3. **get_css_for_category** - Fetch CSS variables and styles for a specific category\n\n## Indexing and Categorizing\n- **categorize_css**: Avoid using this tool unless you want to re-categorize the CSS styles, as it will take time to process and categorize using LLM sampling.\n- Only call the categorization tool once per server session or if the quality of categorization is not good.\n- If CSS categories are not available, run `categorize_css` first, then use `list_css_categories` and `get_css_for_category`.\n\n## Best Practices\n- Always start with `list_components` or `search_components` to discover available components\n- Use `get_component_by_tag` to understand component APIs (props, events, slots) before implementation\n- Reference `get_component_example` for real usage patterns and framework-specific code samples\n- Use VG CSS variables for consistent theming and styling instead of hardcoded values\n- Leverage TypeScript schemas for type-safe component integration\n","annotations":null,"_meta":null}}",
"components": "["vg-input","vg-button","vg-dropdown","vg-theme-provider","vg-card"]",
"categories": "[{"category":"Input","components_count":1,"components":["vg-input"]},{"category":"Button","components_count":1,"components":["vg-button"]},{"category":"Dropdown","components_count":1,"components":["vg-dropdown"]},{"category":"ThemeProvider","components_count":1,"components":["vg-theme-provider"]},{"category":"Card","components_count":1,"components":["vg-card"]}]",
"css_categories": "No CSS categories found. Please run `categorize_css` to categorize the CSS styles.",
"schemas": "["ButtonVariant","ButtonSize","ButtonNativeType","ButtonClickDetail","CardVariant","CardActionDetail","DropdownOption","DropdownChangeDetail","InputType","InputChangeDetail","ThemeMode","ThemeChangeDetail"]"
},
"result_explanation": "Successfully retrieved VG-UI library documentation including startup instructions, component list, categories, CSS categories, and schemas."
}
- prompt: list all the tools from multi_mcp_executer
- tool_call_name: mcp__multi-mcp-executer__list_tools
{
"success": true,
"client_count": 9,
"tools": {
"context7": [
"resolve-library-id(libraryName: string) -> dict",
"get-library-docs(context7CompatibleLibraryID: string, topic: string, tokens: number) -> dict"
],
"pulsemcp": [],
"mcp_compass": [],
"git": [],
"fetch": [
"fetch(url: string, max_length: integer, start_index: integer, raw: boolean) -> dict"
],
"playwright": [
"start_codegen_session(options: object) -> dict",
"end_codegen_session(sessionId: string) -> dict",
"get_codegen_session(sessionId: string) -> dict",
"clear_codegen_session(sessionId: string) -> dict",
"playwright_navigate(url: string, browserType: string, width: number, height: number, timeout: number, waitUntil: string, headless: boolean) -> dict",
"playwright_screenshot(name: string, selector: string, width: number, height: number, storeBase64: boolean, fullPage: boolean, savePng: boolean, downloadsDir: string) -> dict",
"playwright_click(selector: string) -> dict",
"playwright_iframe_click(iframeSelector: string, selector: string) -> dict",
"playwright_iframe_fill(iframeSelector: string, selector: string, value: string) -> dict",
"playwright_fill(selector: string, value: string) -> dict",
"playwright_select(selector: string, value: string) -> dict",
"playwright_hover(selector: string) -> dict",
"playwright_upload_file(selector: string, filePath: string) -> dict",
"playwright_evaluate(script: string) -> dict",
"playwright_console_logs(type: string, search: string, limit: number, clear: boolean) -> dict",
"playwright_close() -> dict",
"playwright_get(url: string) -> dict",
"playwright_post(url: string, value: string, token: string, headers: object) -> dict",
"playwright_put(url: string, value: string) -> dict",
"playwright_patch(url: string, value: string) -> dict",
"playwright_delete(url: string) -> dict",
"playwright_expect_response(id: string, url: string) -> dict",
"playwright_assert_response(id: string, value: string) -> dict",
"playwright_custom_user_agent(userAgent: string) -> dict",
"playwright_get_visible_text() -> dict",
"playwright_get_visible_html(selector: string, removeScripts: boolean, removeComments: boolean, removeStyles: boolean, removeMeta: boolean, cleanHtml: boolean, minify: boolean, maxLength: number) -> dict",
"playwright_go_back() -> dict",
"playwright_go_forward() -> dict",
"playwright_drag(sourceSelector: string, targetSelector: string) -> dict",
"playwright_press_key(key: string, selector: string) -> dict",
"playwright_save_as_pdf(outputPath: string, filename: string, format: string, printBackground: boolean, margin: object) -> dict",
"playwright_click_and_switch_tab(selector: string) -> dict"
],
"sqlite": [
"read_query(query: string) -> dict",
"write_query(query: string) -> dict",
"create_table(query: string) -> dict",
"list_tables() -> dict",
"describe_table(table_name: string) -> dict",
"append_insight(insight: string) -> dict"
],
"chrome-devtools": [
"list_console_messages() -> dict",
"emulate_cpu(throttlingRate: number) -> dict",
"emulate_network(throttlingOption: string) -> dict",
"click(uid: string, dblClick: boolean) -> dict",
"drag(from_uid: string, to_uid: string) -> dict",
"fill(uid: string, value: string) -> dict",
"fill_form(elements: array) -> dict",
"hover(uid: string) -> dict",
"upload_file(uid: string, filePath: string) -> dict",
"get_network_request(url: string) -> dict",
"list_network_requests(pageSize: integer, pageIdx: integer, resourceTypes: array) -> dict",
"close_page(pageIdx: number) -> dict",
"handle_dialog(action: string, promptText: string) -> dict",
"list_pages() -> dict",
"navigate_page(url: string, timeout: integer) -> dict",
"navigate_page_history(navigate: string, timeout: integer) -> dict",
"new_page(url: string, timeout: integer) -> dict",
"resize_page(width: number, height: number) -> dict",
"select_page(pageIdx: number) -> dict",
"performance_analyze_insight(insightName: string) -> dict",
"performance_start_trace(reload: boolean, autoStop: boolean) -> dict",
"performance_stop_trace() -> dict",
"take_screenshot(format: string, quality: number, uid: string, fullPage: boolean, filePath: string) -> dict",
"evaluate_script(function: string, args: array) -> dict",
"take_snapshot() -> dict",
"wait_for(text: string, timeout: integer) -> dict"
],
"vg_ui_lib_mcp_server": [
"ClearCache() -> dict",
"StartupInstructions() -> dict",
"InitialProjectSetup() -> dict",
"list_components() -> dict",
"get_component_by_tag(component_tag: string) -> dict",
"search_components(search_term: string) -> dict",
"list_schemas() -> dict",
"get_schema_definition(schema_name: string) -> dict",
"list_categories() -> dict",
"get_component_example(component_tag: string, example_id: string) -> dict",
"categorize_css() -> dict",
"list_css_categories() -> dict",
"get_css_for_category(category_name: string) -> dict"
]
}
}
MCP Tools Validation
The server includes a comprehensive validation system that automatically checks and installs MCP tools on startup.
Validation Features
- HTTP Server Validation: Tests reachability of remote MCP servers
- STDIO Command Verification: Checks availability of local commands (npx, uvx)
- Automatic Installation: Installs missing npm and Python packages
- Manual Installation: Execute custom installation commands (URLs, Git, build scripts)
- Comprehensive Reporting: Detailed logs with success/failure indicators
- Flexible Modes: Default (warns), strict (fails), no-install (validates only)
Validation Output Example
==========================================
Multi-Tool MCP Server - Starting
==========================================
[2025-01-11 10:30:15] Configuration loaded with 27 MCP servers
[2025-01-11 10:30:15] Validating and installing MCP tools...
==========================================
Starting MCP Tools Validation
==========================================
[1/27] Validating: context7
✅ Status: ok - HTTP 200
[2/27] Validating: filesystem
✅ Status: ok - Installed successfully
[3/27] Validating: git
✅ Status: ok - Pre-cached successfully
[4/27] Validating: github
⏭️ Status: skipped - Server disabled
==========================================
Validation Summary
==========================================
Total servers: 27
✅ Available: 13
⏭️ Skipped: 12
❌ Failed: 2
==========================================
Validation Commands
# Test validation system
make test-validation
# Validate in running container
make validate
# Validate with strict mode (fails on errors)
make validate-strict
# Validate locally (no Docker)
make validate-local
Validation Modes
Default Mode (Recommended)
make run
# Warns on failures, continues startup
Strict Mode (Production)
docker run -e STRICT_VALIDATION=true ...
# Exits on any validation failure
No-Install Mode (Testing)
python3 scripts/validate_mcp_tools.py --no-install
# Only validates, doesn't install
Configuration
Environment Variables
| Variable | Default | Description |
|---|---|---|
MCP_SERVERS_CONFIGS |
{} |
JSON string of MCP server configurations (required) |
SERVER_PORT |
8000 |
HTTP server port |
SERVER_HOST |
0.0.0.0 |
HTTP server host |
LOG_LEVEL |
INFO |
Logging level (DEBUG, INFO, WARNING, ERROR) |
FASTMCP_DEV_MODE |
false |
Enable development mode with MCP Inspector (requires fastmcp[dev]) |
EXECUTION_TIMEOUT |
60 |
Max execution time in seconds |
MAX_SCRIPT_SIZE |
1048576 |
Max Python code size in bytes (1MB) |
SCRIPTS_DIR |
/tmp/scripts |
Directory for runtime scripts |
STRICT_VALIDATION |
false |
Enable strict validation mode |
MCP Servers Configuration
The .env file includes 27 pre-configured MCP servers. Configuration format:
HTTP Server
{
"context7": {
"type": "http",
"url": "https://mcp.context7.com/mcp",
"description": "Library documentation"
}
}
STDIO Server (NPM)
{
"puppeteer": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-puppeteer"],
"description": "Browser automation"
}
}
STDIO Server (Python)
{
"git": {
"type": "stdio",
"command": "uvx",
"args": ["mcp-server-git", "--repository", "."],
"description": "Git operations"
}
}
With Manual Installation
{
"custom_server": {
"type": "stdio",
"command": "uvx",
"args": ["custom-server", "--option", "value"],
"manual_install_command": "uv tool install https://example.com/package.whl",
"description": "Custom package from URL"
}
}
Browser with Isolated Mode
{
"chrome-devtools": {
"type": "stdio",
"command": "npx",
"args": ["chrome-devtools-mcp@latest", "--headless=true", "--isolated"],
"description": "Chrome DevTools with isolated instances"
}
}
Disabled Server
{
"github": {
"type": "stdio",
"enabled": false,
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
}
}
}
Usage
Connecting to the Server
VSCode / Claude Desktop Configuration
{
"mcpServers": {
"multi_mcp_tool_executer": {
"type": "http",
"url": "http://localhost:8000/mcp",
"transport": "http"
}
}
}
Available Tools
1. list_tools
Lists all available tools from configured MCP clients with their signatures.
Example Response:
{
"success": true,
"client_count": 9,
"tools": {
"context7_mcp_client": [
"resolve_library_id(library_name: str) -> dict",
"get_library_docs(library_id: str, topic: str, tokens: int) -> str"
],
"playwright_mcp_client": [
"playwright_navigate(url: str, browserType: str, waitUntil: str) -> dict",
"playwright_screenshot(name: str, savePng: bool) -> dict",
"... (32 tools total)"
],
"chrome_devtools_mcp_client": [
"chrome_navigate(url: str) -> dict",
"chrome_click(selector: str) -> dict",
"... (26 tools total)"
]
}
}
2. multi_tool_executer
Executes custom Python code that can orchestrate multiple MCP tools.
Parameters:
python_code(str): Python code containingexecute_toolsfunctioninput(Any): Input data to pass to the function
Required Function Signature:
async def execute_tools(initial_input: Any, **kwargs: Dict[str, Any]) -> Dict[str, Any]:
"""
Args:
initial_input: The input data provided
**kwargs: MCP clients and helper functions
- {name}_mcp_client: MCP client objects
- extract_content: Helper to parse CallToolResult objects
Returns:
Dict with 'result' and 'result_explanation' keys
"""
pass
CRITICAL: MCP Client Usage Requirements
All MCP client methods are async and MUST be used with the 'async with' context manager:
✅ CORRECT - Use async with context manager:
async def execute_tools(initial_input, **kwargs):
fetch_client = kwargs.get("fetch_mcp_client")
extract_content = kwargs.get("extract_content")
# MUST use 'async with' before calling any client methods
async with fetch_client:
result = await fetch_client.call_tool(
"fetch",
{"url": "https://api.ipify.org?format=json"}
)
# Use extract_content to parse the CallToolResult
parsed_data = extract_content(result)
return {
"result": parsed_data,
"result_explanation": f"Fetched IP: {parsed_data.get('ip', 'N/A')}"
}
❌ WRONG - Will raise "Client is not connected" error:
async def execute_tools(initial_input, **kwargs):
fetch_client = kwargs.get("fetch_mcp_client")
# Missing 'async with' - THIS WILL FAIL
result = await fetch_client.call_tool("fetch", {"url": "..."})
return {"result": result, "result_explanation": "..."}
Multiple clients example:
async def execute_tools(initial_input, **kwargs):
context7_client = kwargs.get("context7_mcp_client")
fetch_client = kwargs.get("fetch_mcp_client")
extract_content = kwargs.get("extract_content")
# Use each client within its own async with block
async with context7_client:
docs = await context7_client.call_tool(
"get-library-docs",
{"context7CompatibleLibraryID": "/python/requests"}
)
async with fetch_client:
data = await fetch_client.call_tool(
"fetch",
{"url": "https://api.example.com"}
)
return {
"result": {
"docs": extract_content(docs),
"data": extract_content(data)
},
"result_explanation": "Retrieved docs and fetched data"
}
Examples
Example 1: Simple Library Documentation Lookup
python_code = """
async def execute_tools(initial_input: Any, **kwargs: Dict[str, Any]) -> Dict[str, Any]:
context7 = kwargs.get("context7_mcp_client")
extract_content = kwargs.get("extract_content")
library_name = initial_input["library"]
async with context7:
result = await context7.call_tool(
"resolve-library-id",
{"libraryName": library_name}
)
library_data = extract_content(result)
return {
"result": library_data,
"result_explanation": f"Found documentation for {library_name}"
}
"""
input_data = {"library": "fastmcp"}
Example 2: Browser Automation - Extract IP Address
python_code = """
async def execute_tools(initial_input: dict, **kwargs) -> dict:
playwright_client = kwargs.get("playwright_mcp_client")
extract_content = kwargs.get("extract_content")
# 1. Launch browser and navigate
async with playwright_client:
await playwright_client.call_tool("playwright_navigate", {
"url": "https://whatismyipaddress.com/",
"browserType": "chromium",
"waitUntil": "load",
"headless": True
})
# 2. Extract page content
async with playwright_client:
text_result = await playwright_client.call_tool(
"playwright_get_visible_text", {}
)
text = extract_content(text_result)
# 3. Capture screenshot
async with playwright_client:
await playwright_client.call_tool("playwright_screenshot", {
"name": "ip_page",
"savePng": True
})
# 4. Parse IP from page text
import re
ip_match = re.search(r'IPv4:.*?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})', text)
ip_address = ip_match.group(1) if ip_match else "Not found"
# 5. Cleanup
async with playwright_client:
await playwright_client.call_tool("playwright_close", {})
return {
"result": {"ip_address": ip_address, "page_text": text[:200]},
"result_explanation": f"Successfully extracted IP: {ip_address}"
}
"""
input_data = {}
Example 3: Multi-Server File Processing
python_code = """
async def execute_tools(initial_input: Any, **kwargs: Dict[str, Any]) -> Dict[str, Any]:
filesystem = kwargs.get("filesystem_mcp_client")
git = kwargs.get("git_mcp_client")
extract_content = kwargs.get("extract_content")
filepath = initial_input["file"]
# Read file
async with filesystem:
content_result = await filesystem.call_tool("read_file", {"path": filepath})
content = extract_content(content_result)
# Check git status
async with git:
status_result = await git.call_tool("git_status", {"repository": "."})
git_status = extract_content(status_result)
return {
"result": {
"file": filepath,
"content_length": len(content),
"git_tracked": "tracked" in str(git_status).lower()
},
"result_explanation": f"Processed {filepath} with git status check"
}
"""
input_data = {"file": "/path/to/file.txt"}
Example 4: Data Processing Pipeline
python_code = """
async def execute_tools(initial_input: Any, **kwargs: Dict[str, Any]) -> Dict[str, Any]:
items = initial_input["items"]
processed_items = []
for item in items:
processed_items.append({
"original": item,
"processed": item.upper(),
"length": len(item)
})
return {
"result": {
"total_processed": len(processed_items),
"items": processed_items
},
"result_explanation": f"Successfully processed {len(items)} items"
}
"""
input_data = {"items": ["hello", "world", "fastmcp"]}
Example 5: VG-UI Library Documentation Retrieval
This example demonstrates fetching complete documentation from a custom MCP server (VG-UI Library).
python_code = """
async def execute_tools(initial_input, **kwargs):
'''
List all VG-UI components using the vg_ui_lib_mcp_server.
This will:
1. Get startup instructions
2. List all available components
3. Get component categories
4. List CSS categories
5. List TypeScript schemas
'''
vg_ui_client = kwargs.get("vg_ui_lib_mcp_server_mcp_client")
extract_content = kwargs.get("extract_content")
if not vg_ui_client:
return {
"result": None,
"result_explanation": "VG-UI MCP client is not available in the configuration."
}
try:
results = {}
# Step 1: Get startup instructions
async with vg_ui_client:
startup_result = await vg_ui_client.call_tool(
"StartupInstructions",
{}
)
# Extract startup instructions
if hasattr(startup_result, 'content'):
content = startup_result.content
if isinstance(content, list) and len(content) > 0:
startup_text = content[0].text if hasattr(content[0], 'text') else str(content[0])
else:
startup_text = str(content)
else:
startup_text = str(startup_result)
results['startup_instructions'] = startup_text
# Step 2: List all components
async with vg_ui_client:
components_result = await vg_ui_client.call_tool(
"list_components",
{}
)
# Extract components list
if hasattr(components_result, 'content'):
content = components_result.content
if isinstance(content, list) and len(content) > 0:
components_text = content[0].text if hasattr(content[0], 'text') else str(content[0])
else:
components_text = str(content)
else:
components_text = str(components_result)
results['components'] = components_text
# Step 3: List categories
async with vg_ui_client:
categories_result = await vg_ui_client.call_tool(
"list_categories",
{}
)
# Extract categories
if hasattr(categories_result, 'content'):
content = categories_result.content
if isinstance(content, list) and len(content) > 0:
categories_text = content[0].text if hasattr(content[0], 'text') else str(content[0])
else:
categories_text = str(content)
else:
categories_text = str(categories_result)
results['categories'] = categories_text
# Step 4: List CSS categories
async with vg_ui_client:
css_categories_result = await vg_ui_client.call_tool(
"list_css_categories",
{}
)
# Extract CSS categories
if hasattr(css_categories_result, 'content'):
content = css_categories_result.content
if isinstance(content, list) and len(content) > 0:
css_categories_text = content[0].text if hasattr(content[0], 'text') else str(content[0])
else:
css_categories_text = str(content)
else:
css_categories_text = str(css_categories_result)
results['css_categories'] = css_categories_text
# Step 5: List schemas
async with vg_ui_client:
schemas_result = await vg_ui_client.call_tool(
"list_schemas",
{}
)
# Extract schemas
if hasattr(schemas_result, 'content'):
content = schemas_result.content
if isinstance(content, list) and len(content) > 0:
schemas_text = content[0].text if hasattr(content[0], 'text') else str(content[0])
else:
schemas_text = str(content)
else:
schemas_text = str(schemas_result)
results['schemas'] = schemas_text
return {
"result": results,
"result_explanation": "Successfully retrieved VG-UI library documentation including startup instructions, component list, categories, CSS categories, and schemas."
}
except Exception as e:
import traceback
error_details = traceback.format_exc()
return {
"result": None,
"result_explanation": f"Error while fetching VG-UI documentation: {str(e)}\\n\\nTraceback:\\n{error_details}"
}
"""
input_data = {}
Expected Output:
{
"success": true,
"result": {
"startup_instructions": "{\"role\":\"assistant\",\"content\":{...}}",
"components": "[\"vg-input\",\"vg-button\",\"vg-dropdown\",\"vg-theme-provider\",\"vg-card\"]",
"categories": "[{\"category\":\"Input\",\"components_count\":1,...}]",
"css_categories": "No CSS categories found. Please run `categorize_css` to categorize the CSS styles.",
"schemas": "[\"ButtonVariant\",\"ButtonSize\",\"ButtonNativeType\",...]"
},
"result_explanation": "Successfully retrieved VG-UI library documentation including startup instructions, component list, categories, CSS categories, and schemas."
}
Key Features Demonstrated:
- Multiple sequential MCP tool calls to gather comprehensive documentation
- Handling different response formats (content lists vs direct strings)
- Graceful error handling with detailed traceback
- Structured result aggregation from multiple tool calls
- Working with custom MCP servers beyond standard HTTP/STDIO
Pre-configured MCP Servers
HTTP Remote Servers (No API Keys Required)
| Server | Description | Auto-Install |
|---|---|---|
| context7 | Library documentation and code examples | N/A (HTTP) |
| pulsemcp | MCP community hub and newsletter | N/A (HTTP) |
| mcp_compass | Intelligent server recommendations | N/A (HTTP) |
STDIO Servers (No API Keys Required)
| Server | Description | Package Manager | Auto-Install | Tools Count |
|---|---|---|---|---|
| filesystem | Secure file operations | npm | ✅ Yes | Multiple |
| git | Git repository operations | Python (uv) | ✅ Yes | Multiple |
| fetch | Web content fetching | Python (uv) | ✅ Yes | 1 |
| playwright | Cross-browser testing & automation | npm | ✅ Yes | 32 |
| chrome-devtools | Chrome DevTools integration | npm | ✅ Yes | 26 |
| sqlite | SQLite database operations | Python (uv) | ✅ Yes | 6 |
| memory | Knowledge graph memory | npm | ✅ Yes | Multiple |
| docker | Container management | npm | ✅ Yes | Multiple |
| time | Timezone operations | npm | ✅ Yes | Multiple |
| sequential_thinking | Problem-solving | npm | ✅ Yes | Multiple |
STDIO Servers (API Keys Required - Disabled by Default)
Enable by adding API keys to .env and setting "enabled": true
| Server | Description | Requires |
|---|---|---|
| github | GitHub integration | GITHUB_TOKEN |
| postgres | PostgreSQL operations | Connection string |
| brave_search | Web search | BRAVE_API_KEY |
| kubernetes | K8s cluster operations | Kube config |
| aws | AWS services | AWS credentials |
| google_drive | Google Drive operations | OAuth tokens |
| google_maps | Google Maps API | GOOGLE_MAPS_API_KEY |
| sentry | Error tracking | SENTRY_AUTH_TOKEN |
| slack | Slack integration | SLACK_BOT_TOKEN |
| everart | AI image generation | EVERART_API_KEY |
| axiom | Log analytics | AXIOM_API_TOKEN |
| linear | Issue tracking | LINEAR_API_KEY |
| jira | Atlassian integration | Jira credentials |
| raygun | Crash reporting | RAYGUN_API_KEY |
Development
Development Mode with MCP Inspector
FastMCP supports a development mode that runs the server alongside the MCP Inspector for interactive debugging and testing.
Enable Development Mode
Set the FASTMCP_DEV_MODE environment variable to true:
# In .env file
FASTMCP_DEV_MODE=true
# Or via Docker command
docker run -e FASTMCP_DEV_MODE=true ...
Requirements
Development mode requires the FastMCP dev dependencies:
pip install 'fastmcp[dev]'
Features
- 🔍 MCP Inspector UI: Interactive web interface for testing tools
- 📊 Real-time Debugging: See tool calls and responses in real-time
- 🧪 Interactive Testing: Test tools without writing client code
- 📝 Request/Response Inspection: Detailed view of all MCP protocol messages
- ⚡ Hot Reload: Automatically restarts on code changes
Using Development Mode
With Docker:
# Update .env
FASTMCP_DEV_MODE=true
# Rebuild and run
make rebuild
# Or run directly
docker run -d \
-e FASTMCP_DEV_MODE=true \
-p 8000:8000 \
--env-file .env \
multi-mcp-tool-executer:latest
Locally (without Docker):
# Set environment variable
export FASTMCP_DEV_MODE=true
# Run server
python -m src.server
# Or use FastMCP CLI directly
fastmcp dev src/server.py
Local Development
# Install dependencies
make dev-install
# Run locally (without Docker)
make dev-run
# Run in dev mode with Inspector
export FASTMCP_DEV_MODE=true
make dev-run
# Format code
make dev-format
# Run linters
make dev-lint
Running Tests
# Test validation system
make test-validation
# In container
make test
# Locally
make test-local
Project Structure
wasm_mcp/
├── Dockerfile # Multi-stage Docker build
├── docker-compose.yml # Docker Compose configuration
├── Makefile # Build automation
├── requirements.txt # Python dependencies
├── .env # Environment configuration (27 MCP servers)
├── README.md # Comprehensive documentation (this file)
├── QUICKSTART.md # Quick start guide
├── CLAUDE.md # Project instructions
│
├── src/
│ ├── __init__.py
│ ├── server.py # Main FastMCP server
│ ├── client_manager.py # MCP clients manager
│ ├── executor.py # Runtime code executor
│ ├── config.py # Configuration management
│ └── utils.py # Utility functions
│
└── scripts/
├── validate_mcp_tools.py # MCP tools validation script
├── install_mcp_packages.py # Build-time package installer
├── docker_entrypoint.sh # Container startup script
└── test_validation.sh # Validation system tests
Security
Execution Sandbox
- Timeout Enforcement: 60s default maximum execution time
- Resource Limits: CPU and memory constraints via Docker
- Isolated Execution: Each execution in separate UID directory
- No Persistence: Scripts cleaned after execution
- Input Validation: Size limits and type checking
- Clean Separation: No access to host filesystem
Network Security
- HTTPS Support: Use reverse proxy (nginx, traefik) for TLS
- Authentication: Bearer token support for MCP clients
- CORS: Configurable via Starlette middleware
- Rate Limiting: Implement via reverse proxy or custom middleware
Validation Security
- Only installs packages from trusted sources (npm, PyPI)
- Validates commands before execution
- Logs all installation activities
- Supports strict mode for production environments
Browser Security
- Isolated Profiles: Each browser instance uses separate profile
- Headless Mode: No UI exposure
- Resource Cleanup: Automatic browser session termination
- Sandboxed Execution: Chromium sandbox enabled
Best Practices
- Always use HTTPS in production
- Validate all inputs before execution
- Monitor resource usage via Docker stats
- Review execution logs regularly
- Keep dependencies updated
- Use secrets management for sensitive configurations
- Enable strict validation in production:
STRICT_VALIDATION=true - Disable unused servers to reduce attack surface
- Use isolated mode for browser automation:
--isolatedflag
Monitoring
Health Checks
# Check server health
curl http://localhost:8000/health
make health
Response:
{
"status": "healthy",
"server": "Multi-Tool MCP Executer",
"clients_configured": 9,
"clients_initialized": 9
}
Logs
# Follow logs
make logs
# Last 100 lines
make logs-tail
# Docker Compose logs
make compose-logs
# View validation logs
make logs | grep "Validation"
Resource Monitoring
# Container stats
make stats
# Inspect container
make inspect
# Check running processes
make ps
Troubleshooting
Server Won't Start
Check validation logs:
docker logs multi-mcp-tool-executerTest validation system:
make test-validationVerify configuration:
echo $MCP_SERVERS_CONFIGS | jq .Check port availability:
lsof -i :8000
Validation Failing
Run test suite:
make test-validationValidate locally:
make validate-localCheck specific server:
docker exec multi-mcp-tool-executer curl https://mcp.context7.com/mcpManual package installation:
docker exec multi-mcp-tool-executer npm install -g package-name
Client Connection Failures
- Verify downstream MCP servers are accessible
- Check network connectivity from container
- Review client initialization logs
- Test with curl from inside container
Browser Automation Issues
- Profile Lock Error: Add
--isolatedflag to chrome-devtools configuration - Browser Version Mismatch: Rebuild container to update dynamic browser detection
- Playwright Timeout: Increase
waitUntiltimeout in navigation calls - Screenshot Failures: Check
/tmp/directory permissions
Execution Timeouts
- Increase
EXECUTION_TIMEOUTif legitimate operations take longer - Optimize Python code for better performance
- Check for infinite loops or blocking operations
- Monitor execution logs for bottlenecks
Memory Issues
- Monitor with
docker stats - Reduce
MAX_SCRIPT_SIZEif needed - Limit concurrent executions
- Implement result streaming for large outputs
API Reference
Health Endpoint
GET /health
Returns server health status and client information.
Response:
{
"status": "healthy",
"server": "Multi-Tool MCP Executer",
"clients_configured": 9,
"clients_initialized": 9
}
MCP Endpoint
POST /mcp
Standard MCP protocol endpoint. Use FastMCP client to interact.
Performance
Benchmarks
- Docker build time: ~4 minutes (one-time, includes package pre-installation)
- Cold start: ~2-3 seconds (with pre-installed packages)
- Request latency: <100ms for list_tools
- Execution overhead: ~50-100ms
- Concurrent executions: 10 (configurable)
- Memory per execution: ~50-100MB
- Validation time: 5-15 seconds (with pre-installed packages), 10-30 seconds (without)
- Package load time: <1 second (pre-installed), 120+ seconds (runtime install)
- Browser launch: 2-3 seconds per instance
- Screenshot capture: <1 second
Performance Improvements
Build-Time Package Installation:
- Pre-installs packages from
.envduring Docker build - Reduces runtime startup by 70% for common packages
- Example:
mcp-server-gitinstalls in 0.08s (was timing out before) - Example:
@modelcontextprotocol/server-filesysteminstalls in 3s (was 120s+)
Package Installation Performance:
| Package | Runtime Install | Pre-installed | Improvement |
|---|---|---|---|
| filesystem | ~120s | ~3s | 97% faster |
| mcp-server-git | Timeout | 0.08s | ✅ Now works |
| mcp-server-fetch | Timeout | 0.03s | ✅ Now works |
| playwright | ~120s | ~11s | 90% faster |
Docker Layer Caching:
- Base dependencies - System packages, Node.js (cached)
- Scripts + .env - Installation scripts (cached separately)
- Playwright install - pip + npm packages (~18s, cached)
- Browser download - Chromium 175MB (~141s, cached separately)
- MCP packages - Pre-install from .env (only on .env changes)
- Application code - Last layer (frequent changes)
Result: ~5-10s rebuilds after initial build
Optimization Tips
- Build-Time Installation: Packages are automatically pre-installed from
.envduring build - Connection Pooling: Reuse MCP client connections
- Package Caching: Pre-installed packages are cached in Docker layers
- Async Operations: All I/O is async
- Resource Limits: Configure Docker memory/CPU limits
- Code Optimization: Minimize execution time in user code
- Disable Unused Servers: Reduce startup time and resource usage
- Browser Pooling: Reuse browser instances with isolated profiles
Make Commands Reference
| Command | Description |
|---|---|
make help |
Show all available commands |
make test-validation |
Test validation system |
make build |
Build Docker image |
make run |
Run container |
make stop |
Stop and remove container |
make restart |
Restart container |
make logs |
Follow container logs |
make logs-tail |
View last 100 lines |
make health |
Check server health |
make validate |
Validate MCP tools |
make validate-strict |
Validate with strict mode |
make validate-local |
Validate locally |
make shell |
Open shell in container |
make clean |
Stop and remove everything |
make rebuild |
Clean, build, and run |
make compose-up |
Start with docker-compose |
make compose-down |
Stop docker-compose services |
make stats |
Show resource usage |
Implementation Details
Docker Build Strategy
The project uses a multi-stage Docker build for optimal image size and build caching:
Stage 1: Base Image
- Python 3.11-slim with system dependencies (gcc, g++, curl)
- Installs build tools required for Python package compilation
Stage 2: Dependencies
- Installs Python packages from requirements.txt
- Separate stage enables efficient caching of dependencies
- Only rebuilds when requirements.txt changes
Stage 3: Runtime
- Minimal runtime image with only necessary components
- Copies Python packages from dependencies stage
- Installs Node.js, npm, and uv for MCP server support
- Includes validation scripts and entrypoint
- Sets up directory structure and permissions
- Pre-installs MCP packages from
.envduring build for faster startup - Installs Playwright browsers with dynamic version detection
Build-Time Package Installation:
- Dynamically reads
.envfile to extract MCP server configurations - Executes
manual_install_commandfor custom installations (URLs, Git, build scripts) - Installs npm packages globally:
npm install -g <package> - Pre-caches Python packages:
uv tool install <package> - Validates package names to skip invalid paths/URLs
- Gracefully handles timeouts (packages retry at runtime)
- Result: 70% faster startup time for pre-installed packages
Build Optimization Benefits:
- Reduces final image size by ~40%
- Faster rebuilds through layer caching
- Separates build-time and runtime dependencies
- Improves security by minimizing installed packages
- Pre-installed packages load in <1 second vs 120+ seconds at runtime
Execution Flow
1. Container Startup
├── docker_entrypoint.sh executes
├── Dynamic browser version detection and symlinking
└── Calls validate_mcp_tools.py
├── Parses MCP_SERVERS_CONFIGS
├── HTTP Servers: Tests reachability
├── STDIO Servers: Installs packages
├── Manual Install: Executes custom commands
└── Reports validation results
2. FastMCP Server Initialization
├── Loads configuration from environment
├── Initializes MCP client connections (async with support)
└── Exposes HTTP endpoint on port 8000
3. Client Request Handling
├── Receives tool call via HTTP
├── Routes to list_tools or multi_tool_executer
└── Returns structured response
4. Code Execution (multi_tool_executer)
├── Generates unique UID
├── Creates isolated directory: /tmp/scripts/{UID}/
├── Writes Python code to script.py
├── Dynamic import and execution (async support)
├── Provides extract_content helper for result parsing
├── Captures result and logs
├── Cleanup temporary files
└── Returns result + explanation
Core Components
1. Validation System (scripts/validate_mcp_tools.py)
Class Structure:
class MCPToolValidator:
def __init__(self, config_str: str)
# Parses MCP_SERVERS_CONFIGS JSON
def check_http_server(self, url: str) -> Tuple[bool, str]
# Tests HTTP server reachability with timeout
def check_command_available(self, command: str) -> bool
# Verifies command exists in PATH
def install_npx_package(self, package: str) -> Tuple[bool, str]
# Runs: npm install -g {package}
def install_uvx_package(self, package: str) -> Tuple[bool, str]
# Runs: uv tool install {package}
def validate_and_install_server(self, name: str, config: Dict) -> Dict
# Orchestrates validation and installation
def run_validation(self, strict: bool, no_install: bool) -> Dict
# Main validation loop with reporting
Validation Logic:
- HTTP servers: GET request with 5-second timeout
- NPX servers: Checks npm command, extracts package name, installs globally
- UVX servers: Checks uvx command, pre-caches package
- Manual install: Executes custom bash commands (URLs, Git, build scripts)
- Skips disabled servers (
"enabled": false)
2. Package Installer (scripts/install_mcp_packages.py)
Manual Install Support:
def run_manual_install(command: str, server_name: str) -> bool:
"""Execute manual installation command"""
result = subprocess.run(
command,
shell=True,
capture_output=True,
text=True,
timeout=600 # 10 minutes for manual installations
)
Installation Priority:
- manual_install_command (if present)
- npm packages (npx command)
- uv packages (uvx command)
3. Server Manager (src/server.py)
Key Components:
- FastMCP HTTP server initialization
- Tool registration and routing
- MCP client lifecycle management
- Request/response handling
- Error handling and logging
- Health check endpoint
Tool Implementation:
@mcp.tool(name="list_tools")
def list_tools() -> dict:
# Queries all MCP clients
# Returns unified tool list with signatures
@mcp.tool(name="multi_tool_executer")
def multi_tool_executer(python_code: str, input: Any) -> dict:
# Generates UID
# Writes code to isolated directory
# Dynamically imports and executes (async support)
# Provides extract_content helper
# Returns structured result
4. Client Manager (src/client_manager.py)
Responsibilities:
- Parse MCP_SERVERS_CONFIGS environment variable
- Initialize FastMCP clients (HTTP and STDIO transports)
- Maintain connection pools and lifecycle
- Handle connection failures and retries
- Provide unified API for tool discovery
Configuration Parsing:
- Validates JSON structure
- Extracts transport type (http/stdio)
- Handles authentication headers
- Manages environment variable interpolation
- Supports MCPConfig dict format for STDIO
5. Runtime Executor (src/executor.py)
Security Features:
- UUID-based execution isolation
- Timeout enforcement (default 60s)
- Resource limits via Docker constraints
- Input size validation (max 1MB)
- Automatic cleanup after execution
- No persistent storage
Helper Functions:
extract_content(): Automatically parses CallToolResult objects- Extracts JSON data using regex
- Falls back to text content extraction
- Returns raw string if parsing fails
Execution Process:
1. Generate UUID4 for execution instance
2. Create directory: /tmp/scripts/{uuid}/
3. Write user code to {uuid}/script.py
4. Add helper functions (extract_content)
5. Import: importlib.import_module(f"scripts.{uuid}.script")
6. Execute: await module.execute_tools(input, **mcp_clients) if async
7. Capture result dictionary
8. Clean up temporary directory
9. Return result with explanation
Issues Resolved
Import Errors ✅
Problem: ImportError: attempted relative import with no known parent package
Solution: Added sys.path manipulation for absolute imports in all source files
FastMCP Lifespan Error ✅
Problem: RuntimeError: Task group is not initialized
Solution: Combined FastMCP's original lifespan with custom startup/shutdown logic
- File: src/server.py
HTTP Client Initialization ✅
Problem: Client.__init__() got an unexpected keyword argument 'headers'
Solution: Removed headers parameter from HTTP client initialization
- File: src/client_manager.py
STDIO Client Initialization ✅
Problem: Could not infer a valid transport from: ['uvx', ...]
Solution: Used MCPConfig dict format for STDIO transports
- File: src/client_manager.py
Tool Listing Format ✅
Problem: 'list' object has no attribute 'tools'
Solution: Handle both list and object response formats dynamically
- File: src/client_manager.py
Async Execution Support ✅
Problem: Coroutines not awaited, helper functions undefined
Solution: Added async/await support with automatic coroutine detection
- Files: src/executor.py, src/server.py
Chrome DevTools Profile Lock ✅
Problem: Browser profile locked, blocking parallel requests
Solution: Added --isolated flag to create temporary profiles per request
- File: [.env](. env#L59)
Playwright Browser Version Mismatch ✅
Problem: Expecting chromium-1179, but chromium-1187 installed
Solution: Dynamic browser detection in entrypoint script
Browser Automation
Successful Test: IP Extraction
Test Objective: Extract IP address from https://whatismyipaddress.com/ using browser tools only
Result: ✅ SUCCESS
- IP Address: 152.57.51.5
- Location: Udupi, Karnataka, India
- ISP: Reliance Jio Infocomm Limited
- Method: Playwright MCP with real Chromium browser
Browser Capabilities
Playwright MCP (32 tools):
- Navigate to URLs with various wait strategies
- Extract visible text and HTML content
- Capture screenshots and PDFs
- Execute JavaScript in page context
- Handle forms, clicks, and interactions
- Monitor network requests and console logs
- Close and cleanup browser sessions
Chrome DevTools MCP (26 tools):
- Advanced browser control and debugging
- Performance monitoring
- Network traffic analysis
- Console log capture
- Element inspection
Browser Automation Steps
# 1. Launch browser and navigate
async with playwright_client:
await playwright_client.call_tool("playwright_navigate", {
"url": "https://example.com",
"browserType": "chromium",
"waitUntil": "load",
"headless": True
})
# 2. Extract page content
async with playwright_client:
text_result = await playwright_client.call_tool(
"playwright_get_visible_text", {}
)
# 3. Capture screenshot
async with playwright_client:
await playwright_client.call_tool("playwright_screenshot", {
"name": "page_screenshot",
"savePng": True
})
# 4. Cleanup
async with playwright_client:
await playwright_client.call_tool("playwright_close", {})
Isolated Browser Instances
Both Playwright and Chrome DevTools support isolated instances for parallel requests:
Playwright: Automatically creates separate browser contexts per request
Chrome DevTools: Use --isolated flag in configuration:
{
"chrome-devtools": {
"args": ["chrome-devtools-mcp@latest", "--headless=true", "--isolated"]
}
}
Manual Install Support
Overview
Added support for manual_install_command field in MCP server configuration to handle custom installation scenarios.
Installation Priority
1. manual_install_command (if present)
2. npm packages (npx command)
3. uv packages (uvx command)
Usage Examples
Install from URL (wheel file)
{
"custom_server": {
"type": "stdio",
"command": "uvx",
"args": ["custom-server", "--option", "value"],
"manual_install_command": "uv tool install https://example.com/package.whl",
"description": "Custom package from URL"
}
}
Install from Git Repository
{
"git_mcp": {
"type": "stdio",
"command": "uvx",
"args": ["git-mcp-server"],
"manual_install_command": "uv tool install git+https://github.com/user/mcp.git",
"description": "Custom MCP from Git"
}
}
Install with pip
{
"special_mcp": {
"type": "stdio",
"command": "python",
"args": ["-m", "special_mcp"],
"manual_install_command": "pip install special-mcp-package==1.2.3",
"description": "MCP requiring specific pip install"
}
}
Custom Build Script
{
"build_mcp": {
"type": "stdio",
"command": "node",
"args": ["dist/server.js"],
"manual_install_command": "cd /tmp && git clone https://github.com/user/mcp.git && cd mcp && npm install && npm run build",
"description": "MCP requiring build from source"
}
}
Behavior
When manual_install_command is present:
- ✅ Executes the manual install command
- ✅ Logs output (first 200 chars)
- ✅ Continues on failure (will retry at runtime)
- ✅ Skips standard npm/uv package extraction for that server
- ✅ Uses shell=True for complex commands
- ✅ 10-minute timeout for complex installations
Benefits
✅ Flexibility: Install packages from any source (URL, Git, local path) ✅ Custom Commands: Run complex installation sequences ✅ System Dependencies: Install system packages before Python/Node packages ✅ Build Scripts: Compile from source if needed ✅ Version Control: Pin to specific commits, tags, or releases
Security Considerations
⚠️ Shell Injection Risk: Commands are executed with shell=True
- Only use manual_install_command from trusted sources
- Validate commands before adding to .env
- Docker build-time execution provides some isolation
License
MIT License - See LICENSE file for details
Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
Support
- Quick Start: QUICKSTART.md
- Project Instructions: CLAUDE.md
- Configuration: .env
- Commands: Run
make help - GitHub Issues:
/issues
Roadmap
Current Features (Phase 1)
- ✅ Dynamic Python code execution
- ✅ Multiple MCP client orchestration (9 clients)
- ✅ Docker-based deployment
- ✅ HTTP transport support
- ✅ Automatic MCP tools validation
- ✅ Runtime package installation
- ✅ Manual installation support
- ✅ Browser automation (Playwright + Chrome DevTools)
- ✅ Isolated browser instances
- ✅ Dynamic browser version detection
- ✅ 27 pre-configured servers
- ✅ Comprehensive logging
- ✅ Health checks
Phase 2 Features
- WebSocket support for real-time communication
- Streaming execution logs
- Persistent execution sessions
- Tool versioning
- OAuth2 authentication
- Metrics dashboard
- Redis caching layer
- Load balancing support
Advanced Features
- Code analysis before execution
- Quota management
- Audit logging
- Signature verification
- Worker pools
- Result streaming
- Pre-warming optimization
- Automatic package version management
- Custom validation rules
- Plugin system for custom servers
- WebAssembly runtime integration (original goal)
- RustPython support with FastMCP
- Virtual file system for dynamic scripts
Built with ❤️ using FastMCP, Docker, Playwright, and Python
Current Status: ✅ Production Ready | All Systems Operational | 9/9 Clients Initialized
Установка Multi Tool Server
У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.
▸ github.com/brguru90/multi_tool_executer_mcpFAQ
Multi Tool Server MCP бесплатный?
Да, Multi Tool Server MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для Multi Tool Server?
Нет, Multi Tool Server работает без API-ключей и переменных окружения.
Multi Tool Server — hosted или self-hosted?
Доступен hosted-вариант: Unyly запускает сервер в облаке, локальная установка не обязательна.
Как установить Multi Tool Server в Claude Desktop, Claude Code или Cursor?
Открой Multi Tool Server на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
GitHub
PRs, issues, code search, CI status
автор: GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
автор: mcpdotdirectCompare Multi Tool Server with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории development
