Skip to main content
Glama

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 .env configuration

  • βœ… 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 containing execute_tools function

  • input (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

  1. Always use HTTPS in production

  2. Validate all inputs before execution

  3. Monitor resource usage via Docker stats

  4. Review execution logs regularly

  5. Keep dependencies updated

  6. Use secrets management for sensitive configurations

  7. Enable strict validation in production: STRICT_VALIDATION=true

  8. Disable unused servers to reduce attack surface

  9. Use isolated mode for browser automation: --isolated flag

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

  1. Check validation logs:

    docker logs multi-mcp-tool-executer
  2. Test validation system:

    make test-validation
  3. Verify configuration:

    echo $MCP_SERVERS_CONFIGS | jq .
  4. Check port availability:

    lsof -i :8000

Validation Failing

  1. Run test suite:

    make test-validation
  2. Validate locally:

    make validate-local
  3. Check specific server:

    docker exec multi-mcp-tool-executer curl https://mcp.context7.com/mcp
  4. Manual package installation:

    docker exec multi-mcp-tool-executer npm install -g package-name

Client Connection Failures

  1. Verify downstream MCP servers are accessible

  2. Check network connectivity from container

  3. Review client initialization logs

  4. Test with curl from inside container

Browser Automation Issues

  1. Profile Lock Error: Add --isolated flag to chrome-devtools configuration

  2. Browser Version Mismatch: Rebuild container to update dynamic browser detection

  3. Playwright Timeout: Increase waitUntil timeout in navigation calls

  4. Screenshot Failures: Check /tmp/ directory permissions

Execution Timeouts

  1. Increase EXECUTION_TIMEOUT if legitimate operations take longer

  2. Optimize Python code for better performance

  3. Check for infinite loops or blocking operations

  4. Monitor execution logs for bottlenecks

Memory Issues

  1. Monitor with docker stats

  2. Reduce MAX_SCRIPT_SIZE if needed

  3. Limit concurrent executions

  4. 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 .env during Docker build

  • Reduces runtime startup by 70% for common packages

  • Example: mcp-server-git installs in 0.08s (was timing out before)

  • Example: @modelcontextprotocol/server-filesystem installs 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:

  1. Base dependencies - System packages, Node.js (cached)

  2. Scripts + .env - Installation scripts (cached separately)

  3. Playwright install - pip + npm packages (~18s, cached)

  4. Browser download - Chromium 175MB (~141s, cached separately)

  5. MCP packages - Pre-install from .env (only on .env changes)

  6. Application code - Last layer (frequent changes)

Result: ~5-10s rebuilds after initial build

Optimization Tips

  1. Build-Time Installation: Packages are automatically pre-installed from .env during build

  2. Connection Pooling: Reuse MCP client connections

  3. Package Caching: Pre-installed packages are cached in Docker layers

  4. Async Operations: All I/O is async

  5. Resource Limits: Configure Docker memory/CPU limits

  6. Code Optimization: Minimize execution time in user code

  7. Disable Unused Servers: Reduce startup time and resource usage

  8. 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 .env during build for faster startup

  • Installs Playwright browsers with dynamic version detection

Build-Time Package Installation:

  • Dynamically reads .env file to extract MCP server configurations

  • Executes manual_install_command for 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:

  1. manual_install_command (if present)

  2. npm packages (npx command)

  3. 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

HTTP Client Initialization βœ…

Problem: Client.__init__() got an unexpected keyword argument 'headers'

Solution: Removed headers parameter from HTTP client initialization

STDIO Client Initialization βœ…

Problem: Could not infer a valid transport from: ['uvx', ...]

Solution: Used MCPConfig dict format for STDIO transports

Tool Listing Format βœ…

Problem: 'list' object has no attribute 'tools'

Solution: Handle both list and object response formats dynamically

Async Execution Support βœ…

Problem: Coroutines not awaited, helper functions undefined

Solution: Added async/await support with automatic coroutine detection

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:

  1. βœ… Executes the manual install command

  2. βœ… Logs output (first 200 chars)

  3. βœ… Continues on failure (will retry at runtime)

  4. βœ… Skips standard npm/uv package extraction for that server

  5. βœ… Uses shell=True for complex commands

  6. βœ… 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

  1. Fork the repository

  2. Create a feature branch

  3. Make your changes

  4. Add tests

  5. Submit a pull request

Support

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

F
license - not found
-
quality - not tested
D
maintenance

Maintenance

–Maintainers
–Response time
–Release cycle
–Releases (12mo)
Commit activity

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

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