Multi-Tool MCP Server
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Multi-Tool MCP Serverrun a Python script to fetch and compare stock prices from multiple sources"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Multi-Tool MCP Server
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
Related MCP server: AgentExecMPC
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 .env2. Test Validation System
# Verify configuration
make test-validation3. Build and Run
make build
make run4. Verify Installation
# Check health
make health
# Validate MCP tools
make validateExample:
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-localValidation Modes
Default Mode (Recommended)
make run
# Warns on failures, continues startupStrict Mode (Production)
docker run -e STRICT_VALIDATION=true ...
# Exits on any validation failureNo-Install Mode (Testing)
python3 scripts/validate_mcp_tools.py --no-install
# Only validates, doesn't installConfiguration
Environment Variables
Variable | Default | Description |
|
| JSON string of MCP server configurations (required) |
|
| HTTP server port |
|
| HTTP server host |
|
| Logging level (DEBUG, INFO, WARNING, ERROR) |
|
| Enable development mode with MCP Inspector (requires |
|
| Max execution time in seconds |
|
| Max Python code size in bytes (1MB) |
|
| Directory for runtime scripts |
|
| 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
"""
passCRITICAL: 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 |
|
postgres | PostgreSQL operations | Connection string |
brave_search | Web search |
|
kubernetes | K8s cluster operations | Kube config |
aws | AWS services | AWS credentials |
google_drive | Google Drive operations | OAuth tokens |
google_maps | Google Maps API |
|
sentry | Error tracking |
|
slack | Slack integration |
|
everart | AI image generation |
|
axiom | Log analytics |
|
linear | Issue tracking |
|
jira | Atlassian integration | Jira credentials |
raygun | Crash reporting |
|
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:latestLocally (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.pyLocal 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-lintRunning Tests
# Test validation system
make test-validation
# In container
make test
# Locally
make test-localProject 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 testsSecurity
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=trueDisable 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 healthResponse:
{
"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 psTroubleshooting
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 configurationBrowser Version Mismatch: Rebuild container to update dynamic browser detection
Playwright Timeout: Increase
waitUntiltimeout in navigation callsScreenshot Failures: Check
/tmp/directory permissions
Execution Timeouts
Increase
EXECUTION_TIMEOUTif legitimate operations take longerOptimize Python code for better performance
Check for infinite loops or blocking operations
Monitor execution logs for bottlenecks
Memory Issues
Monitor with
docker statsReduce
MAX_SCRIPT_SIZEif neededLimit 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 buildReduces 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 buildConnection 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 |
| Show all available commands |
| Test validation system |
| Build Docker image |
| Run container |
| Stop and remove container |
| Restart container |
| Follow container logs |
| View last 100 lines |
| Check server health |
| Validate MCP tools |
| Validate with strict mode |
| Validate locally |
| Open shell in container |
| Stop and remove everything |
| Clean, build, and run |
| Start with docker-compose |
| Stop docker-compose services |
| 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 startupInstalls Playwright browsers with dynamic version detection
Build-Time Package Installation:
Dynamically reads
.envfile to extract MCP server configurationsExecutes
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 + explanationCore 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 reportingValidation 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 result4. 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 objectsExtracts 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 explanationIssues 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 helpGitHub 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
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/brguru90/multi_tool_executer_mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server