Skip to main content
Glama
inbarajaldrin

MCP Tools Orchestrator

MCP Tools Orchestrator

Compose tools from multiple MCP servers into unified Python policies

MCP Tools Orchestrator is a meta-MCP server that enables "Code as Policies" across your entire MCP ecosystem. It automatically discovers tools from all your connected MCP servers and provides a unified Python API for writing complex, multi-server workflows.

🎯 What Problem Does This Solve?

Traditional MCP Usage:

Agent: I'll call tool A
β†’ Wait for result
Agent: Based on A, I'll call tool B
β†’ Wait for result
Agent: Based on B, I'll call tool C
β†’ Wait for result

With MCP Tools Orchestrator:

# Agent writes one policy script that orchestrates everything
for attempt in range(10):
    result_a = server1__tool_a()
    if result_a["success"]:
        result_b = server2__tool_b(result_a["data"])
        if result_b["status"] == "ready":
            server3__tool_c()
            break
    # Complex logic with loops, conditionals, error handling!

Benefits:

  • βœ… 10-100x faster: One execution instead of N round-trips

  • βœ… Complex logic: Loops, conditionals, error handling in Python

  • βœ… Multi-server workflows: Use tools from ANY server in one policy

  • βœ… Immediate feedback: Scripts see results and adapt without agent involvement


Related MCP server: MetaMCP MCP Server

πŸ—οΈ Architecture

Hybrid Design: No Duplicate Server Processes

MCP Tools Orchestrator leverages mcp-client's existing server connections via HTTP IPC instead of creating its own connections. This prevents duplicate server processes and resource conflicts.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     mcp-client (CLI)                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  Agent (Claude/GPT)                                   β”‚   β”‚
β”‚  β”‚  Calls: mcp-tools-orchestrator__execute_composed_code(script)  β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚         ↓                                                    β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  IPC Server (HTTP)                                    β”‚   β”‚
β”‚  β”‚  http://localhost:random_port                         β”‚   β”‚
β”‚  β”‚  Routes tool calls to appropriate MCP servers         β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         ↓ (MCP stdio)              ↑ (HTTP IPC)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   mcp-tools-orchestrator β”‚    β”‚ Python Script  β”‚
β”‚   (server.py)        β”‚    β”‚ (user policy)  β”‚
β”‚                      β”‚    β”‚                β”‚
β”‚ 1. Generates API     β”‚    β”‚ from unified_  β”‚
β”‚ 2. Executes scripts  │←───│ api import *   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

                ↓ (HTTP POST /call_tool)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚        Actual MCP Servers                  β”‚
β”‚  (ros-mcp-server, isaac-sim, etc.)         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Points:

  • Client manages all MCP server connections

  • Orchestrator never connects directly to MCP servers

  • Policy scripts call client's IPC server via HTTP

  • Single process per MCP server (no duplicates!)


πŸ“¦ Installation

Prerequisites

  1. Python 3.10+ (developed with Python 3.13)

  2. mcp-client with IPC support (see mcp-client-example)

  3. uv package manager

Install MCP Tools Orchestrator

cd /path/to/mcp-tools-orchestrator
uv sync

βš™οΈ Configuration

Step 1: Configure mcp-client

Add mcp-tools-orchestrator to your mcp_config.json (typically in ~/Documents/mcp-client-example/):

{
  "mcpServers": {
    "mcp-tools-orchestrator": {
      "disabled": false,
      "timeout": 60,
      "type": "stdio",
      "command": "/path/to/mcp-tools-orchestrator/.venv/bin/python",
      "args": ["/path/to/mcp-tools-orchestrator/server.py"]
    },
    "ros-mcp-server": {
      "disabled": false,
      "command": "bash",
      "args": ["-c", "source /opt/ros/humble/setup.bash && python server.py"]
    },
    "isaac-sim": {
      "disabled": false,
      "command": "python",
      "args": ["/path/to/isaac-sim-mcp/server.py"]
    }
  }
}

Note: The client will automatically:

  • Start an IPC HTTP server on a random port

  • Set MCP_CLIENT_IPC_URL environment variable for orchestrator

  • Pass the IPC URL when spawning mcp-tools-orchestrator

Step 2: Configure Orchestrator's Server List

Create mcp_servers_config.json in the orchestrator directory:

{
  "mcpServers": {
    "ros-mcp-server": {
      "command": "bash",
      "args": [
        "-c",
        "source /opt/ros/humble/setup.bash && /home/user/.pyenv/versions/3.10.12/bin/python /path/to/server.py"
      ]
    },
    "isaac-sim": {
      "command": "/home/user/.pyenv/versions/3.10.12/bin/python",
      "args": ["/path/to/isaac-sim-mcp/server.py"]
    },
    "Resources": {
      "command": "/home/user/.pyenv/versions/3.10.12/bin/python",
      "args": ["/path/to/grasp_assembly_server/server.py"]
    }
  }
}

Purpose: This config is used only for introspection (extracting tool signatures). Orchestrator doesn't spawn these servers - the client does!


πŸš€ Usage

1. Basic Workflow

Start the mcp-client with orchestrator enabled:

cd ~/path/to/mcp-client
mcp-client --all  # Connects to all enabled servers including orchestrator

That's it! The unified API is automatically generated when mcp-tools-orchestrator starts. No manual generation step needed.

Enable orchestrator mode (optional but recommended):

/orchestrator-on

This hides all direct tools and shows only orchestrator tools, reducing context pollution.

2. Ask the Agent to Write a Policy

User: Write a script to try grasping 5 different objects and report the success rate

The agent will use execute_composed_code with a Python script:

from unified_api import *

success_count = 0
total = 5

for i in range(total):
    # Move to grasp position
    result = ros_mcp_server__move_to_grasp(
        object_name=f"object_{i}",
        grasp_id=0,
        mode="sim",
        move_to_object=True
    )

    if result.get("success"):
        # Close gripper
        ros_mcp_server__control_gripper("close", mode="sim")

        # Verify grasp
        verify = ros_mcp_server__verify_grasp(f"object_{i}", mode="sim")
        if verify.get("result") == "SUCCESS":
            success_count += 1
            print(f"βœ“ Object {i} grasped successfully")
        else:
            print(f"βœ— Object {i} grasp failed")

print(f"\nSuccess rate: {success_count}/{total} ({success_count/total*100:.1f}%)")

3. Available Orchestrator Tools

MCP Tools Orchestrator provides 4 tools to the agent:

execute_composed_code(code: str, timeout: int = 3600)

Execute Python code with access to ALL tools from ALL connected servers.

Returns: {output: str, returncode: int, status: str}

list_available_tools()

Get a structured view of all available tools with their signatures.

Returns: {servers: {...}, total_servers: int, total_tools: int}

refresh_tools()

Re-discover tools from all servers (useful if servers were updated).

Returns: {status: str, server_count: int, tool_count: int}

get_api_documentation()

Get documentation about the generated unified API.

Returns: str (formatted documentation)


πŸ“š How It Works

1. Initialization (When Orchestrator Starts)

# In server.py
async def initialize():
    # 1. Check for client IPC URL
    client_ipc_url = os.getenv("MCP_CLIENT_IPC_URL")  # Set by client

    # 2. Generate unified API using introspection
    generator = UnifiedAPIGenerator()
    generator.generate_api_from_config(
        "mcp_servers_config.json",
        "generated/unified_api.py",
        client_ipc_url
    )

    # 3. Initialize code executor
    executor = CodeExecutor("generated/unified_api.py", client_ipc_url)

2. API Generation via Introspection

# In api_generator.py
class UnifiedAPIGenerator:
    def generate_api_from_config(self, config_path, output_path, ipc_url):
        # For each server in config:
        for server_name, server_config in config["mcpServers"].items():

            # 1. Extract Python path and server script path
            python_path, server_path = self._extract_paths(server_config)

            # 2. Run introspection in isolated subprocess
            #    (avoids dependency conflicts between servers)
            tools = subprocess.run([
                python_path,
                "introspect_server.py",  # Isolated introspection script
                server_path,
                server_name
            ])

            # 3. Parse tool signatures (params, types, defaults, docstrings)
            all_tools[server_name] = parse_tools(tools.stdout)

        # 4. Generate unified_api.py using Jinja2 template
        self._generate_api_file(all_tools, output_path, ipc_url)

Why introspection?

  • Previous approach used JSON schemas β†’ functions had no parameters

  • Introspection uses Python's inspect.signature() β†’ accurate signatures

  • Each server introspected in its own environment β†’ no dependency conflicts

3. Generated API Structure

# In generated/unified_api.py (auto-generated)
import requests

_IPC_URL = "http://localhost:<random_port>"  # Client's IPC server (set dynamically)

# Tools from ros-mcp-server
def ros_mcp_server__move_to_grasp(
    object_name: str,
    grasp_id: int,
    mode: str = "sim",
    move_to_object: bool = False,
    move_to_safe_height: bool = False
) -> dict:
    """Move to grasp position..."""
    return _call_tool("ros-mcp-server", "move_to_grasp", {
        "object_name": object_name,
        "grasp_id": grasp_id,
        "mode": mode,
        "move_to_object": move_to_object,
        "move_to_safe_height": move_to_safe_height
    })

# Helper function
def _call_tool(server: str, tool: str, arguments: dict) -> dict:
    response = requests.post(
        f"{_IPC_URL}/call_tool",
        json={"server": server, "tool": tool, "arguments": arguments},
        timeout=300
    )
    return response.json()

4. Code Execution Flow

# In code_executor.py
class CodeExecutor:
    def execute_code(self, user_code: str, timeout: int) -> dict:
        # 1. Wrap user code with imports
        wrapped = f"""
import sys
sys.path.insert(0, '{self.api_dir}')
from unified_api import *

{user_code}
"""

        # 2. Create temp file and execute in subprocess
        with tempfile.NamedTemporaryFile(mode='w', suffix='.py') as f:
            f.write(wrapped)
            result = subprocess.run(
                [self.venv_python, f.name],
                capture_output=True,
                timeout=timeout,
                env={"MCP_ORCHESTRATOR_IPC_URL": self.client_ipc_url}
            )

        # 3. Return output and status
        return {
            "output": result.stdout,
            "error": result.stderr,
            "returncode": result.returncode,
            "status": "success" if result.returncode == 0 else "error"
        }

πŸ“ Project Structure

mcp-tools-orchestrator/
β”œβ”€β”€ server.py                         # Main FastMCP server entry point
β”‚
β”œβ”€β”€ src/mcp_tools_orchestrator/
β”‚   β”œβ”€β”€ api_generator.py              # Introspection-based API generator
β”‚   β”œβ”€β”€ introspect_server.py          # Isolated server introspection script
β”‚   β”œβ”€β”€ code_executor.py              # Executes policy code in subprocess
β”‚   β”œβ”€β”€ __init__.py                   # Package initialization
β”‚   └── py.typed                      # Type hints marker (PEP 561)
β”‚
β”œβ”€β”€ generated/
β”‚   └── unified_api.py                # Auto-generated API (63 tools from 3 servers)
β”‚
β”œβ”€β”€ examples/
β”‚   β”œβ”€β”€ simple_grasp.py               # Basic grasping workflow
β”‚   β”œβ”€β”€ multi_server_workflow.py      # Cross-server orchestration
β”‚   └── error_recovery.py             # Error handling patterns
β”‚
β”œβ”€β”€ mcp_servers_config.json           # Server config for introspection
β”œβ”€β”€ pyproject.toml                    # Project metadata and dependencies
β”œβ”€β”€ uv.lock                           # Locked dependencies
β”‚
β”œβ”€β”€ README.md                         # This file
β”‚
β”œβ”€β”€ .python-version                   # Python 3.13 (for pyenv)
└── .gitignore                        # Git ignore rules

Active Files (Clean Architecture):

  • server.py - Main MCP server

  • src/mcp_tools_orchestrator/api_generator.py - API generation via introspection

  • src/mcp_tools_orchestrator/introspect_server.py - Isolated introspection script

  • src/mcp_tools_orchestrator/code_executor.py - Policy code execution

Generated Files:

  • generated/unified_api.py - Auto-generated on every server startup (no manual steps needed)


πŸŽ“ Example Policies

Simple Grasp with Verification

from unified_api import *

# Move to home position
ros_mcp_server__move_home()

# Open gripper
ros_mcp_server__control_gripper("open", mode="sim")

# Move to grasp
ros_mcp_server__move_to_grasp(
    object_name="block_1",
    grasp_id=0,
    mode="sim",
    move_to_object=True
)

# Close gripper
ros_mcp_server__control_gripper("close", mode="sim")

# Move to safe height
ros_mcp_server__move_to_grasp(
    object_name="block_1",
    grasp_id=0,
    mode="sim",
    move_to_safe_height=True
)

# Verify grasp
result = ros_mcp_server__verify_grasp("block_1", mode="sim")
if result["result"] == "SUCCESS":
    print("βœ“ Grasp successful!")
else:
    print("βœ— Grasp failed")

Multi-Server Workflow with Error Recovery

from unified_api import *

# Save scene state before attempting grasps
scene_id = isaac_sim__save_scene_state()
print(f"Saved scene state: {scene_id}")

# Try multiple grasp poses
for grasp_id in range(5):
    print(f"\nAttempting grasp {grasp_id}...")

    # Move to grasp
    ros_mcp_server__move_to_grasp(
        object_name="gear",
        grasp_id=grasp_id,
        mode="sim",
        move_to_object=True
    )

    # Close gripper
    ros_mcp_server__control_gripper("close", mode="sim")

    # Move to safe height
    ros_mcp_server__move_to_grasp(
        object_name="gear",
        grasp_id=grasp_id,
        mode="sim",
        move_to_safe_height=True
    )

    # Verify
    result = ros_mcp_server__verify_grasp("gear", mode="sim")

    if result["result"] == "SUCCESS":
        print(f"βœ“ Grasp {grasp_id} succeeded!")
        break
    else:
        print(f"βœ— Grasp {grasp_id} failed, restoring scene...")
        isaac_sim__restore_scene_state()
else:
    print("All grasp attempts failed")

Complex Assembly with Resource Tracking

from unified_api import *

# Get successful grasp configurations from resource server
assembly_id = "3"
configs = Resources__get_object_grasp_configs_by_result(
    assembly_id=assembly_id,
    object_name="gear",
    result="SUCCESS"
)

print(f"Found {len(configs)} successful grasp configs")

# Try each successful configuration
for config in configs:
    grasp_id = config["grasp_id"]
    gripper_state = config["gripper_state"]

    print(f"\nTrying grasp {grasp_id} with gripper {gripper_state}")

    # Set gripper state BEFORE grasping (important!)
    ros_mcp_server__control_gripper(gripper_state, mode="sim")

    # Attempt grasp
    ros_mcp_server__move_to_grasp(
        object_name="gear",
        grasp_id=grasp_id,
        mode="sim",
        move_to_object=True
    )

    # Verify
    result = ros_mcp_server__verify_grasp("gear", mode="sim")

    if result["result"] == "SUCCESS":
        print(f"βœ“ Successfully grasped using config {grasp_id}")

        # Save this trial to resource server
        Resources__write_assembly_resource(
            assembly_id=assembly_id,
            object_name="gear",
            sequence_id=1,
            assembled_into="base",
            tools_trials=[{
                "trial_id": 1,
                "grasp_id": grasp_id,
                "gripper_state": gripper_state,
                "tools": ["move_to_grasp", "verify_grasp"],
                "result": "SUCCESS"
            }]
        )
        break

More examples in the examples/ directory!


πŸ”§ Development

Running in Development

# The server requires MCP_CLIENT_IPC_URL to be set
# Normally set by mcp-client, but for testing:
export MCP_CLIENT_IPC_URL="http://localhost:<port>"
python server.py

Note: The API is automatically generated on startup. The sections below are for development/debugging only.

Regenerating the API Manually (Development Only)

python src/mcp_tools_orchestrator/api_generator.py \
    mcp_servers_config.json \
    generated/unified_api.py \
    http://localhost:<port>

Testing Introspection

# Test introspection of a specific server
python src/mcp_tools_orchestrator/introspect_server.py \
    /path/to/server.py \
    server-name

🚨 Important Notes

Environment Variables

Required:

  • MCP_CLIENT_IPC_URL - Set automatically by mcp-client when spawning orchestrator

Optional:

  • MCP_CLIENT_OUTPUT_DIR - Shared outputs directory (set by client)

Introspection Requirements

Each server in mcp_servers_config.json must:

  1. Be a valid Python script

  2. Use MCP decorators (@mcp.tool())

  3. Have type-hinted function signatures

  4. Be runnable in its specified Python environment

Python Version Compatibility

Developed with: Python 3.13 Minimum required: Python 3.10

The .python-version file specifies 3.13 for consistency. If you encounter issues, ensure your environment matches or update .python-version to your Python version.

Generated API Location

The unified API is always generated at:

<project-root>/generated/unified_api.py

This path is determined by server.py:

script_dir = Path(__file__).parent  # Repository root
generated_dir = script_dir / "generated"

πŸ’‘ Benefits Over Alternatives

vs. Manual Tool Calls (Traditional MCP)

Aspect

Manual Tool Calls

MCP Tools Orchestrator

Speed

~2s per tool call

All tools in one execution

Complexity

Limited to agent's planning

Full Python: loops, conditionals, functions

Knowledge

Agent must track state

Script has full context

Latency

N round-trips

1 execution

vs. Per-Server Custom APIs

Aspect

Custom APIs

MCP Tools Orchestrator

Maintenance

Write API for each server

Auto-generated

Updates

Manual sync

Auto-refresh

Cross-server

Complex coordination

Natural in policy code

Type safety

Manual typing

Auto-extracted from servers


πŸ› Known Limitations

  1. Abort Signal Handling

    • Client-side abort functionality is fully implemented (press 'a' to abort)

    • Orchestrator's generated API needs update to detect [ABORTED] prefix

    • Scripts currently treat abort as normal error instead of immediate termination

  2. Introspection Edge Cases

    • Bash-wrapped commands require parsing (works but fragile)

    • Very large servers may timeout during introspection

  3. Error Context

    • Stack traces from policy scripts can be verbose

    • Errors don't always indicate which server/tool failed


πŸ—ΊοΈ Future Enhancements

  • Implement proper abort signal detection in unified_api.py

  • Cache introspection results for faster startup

  • WebSocket support for lower IPC latency

  • Script library/registry for reusable policies

  • Better error messages with server/tool context

  • Support for streaming tool results

  • Interactive debugging mode


πŸ“„ License

MIT License - See LICENSE file for details


πŸ‘€ Author

Aldrin Inbaraj Email: aaugus11@asu.edu GitHub: [Your GitHub Profile]


πŸ™ Acknowledgments


πŸ“ž Support

For issues, questions, or contributions:

  1. Review the documentation in this README

  2. Check example policies in examples/

  3. Open an issue on GitHub with:

    • Clear description of the problem

    • Relevant logs/error messages

    • Steps to reproduce


Happy Policy Writing! πŸš€

Available Tools

2 tools
execute_composed_codeA

Execute Python code that orchestrates tools from multiple MCP servers.

Used to automate a heuristic search or sequential execution using loops and error handling.

Example: ```python from unified_api import *

items = ["item_a", "item_b", "item_c"]
results = []

for item in items:
    server__prepare(target=item, mode="sim")
    result = server__execute_action(name=item, value=123, mode="sim")

    if result.get("result") != "success":
        server__restore_state()
        continue

    server__finalize(item=item)
    results.append({"item": item, "status": "success"})

print(f"Processed {len(results)}/{len(items)} successfully")
```

Returns: output: stdout/stderr from execution (partial results if aborted/timed out) returncode: 0=success, 1=error, -1=timeout status: "success" | "failed" | "aborted" | "timeout" session_id: present when persistent=True and status="success" session_vars: list of saved variable names, present with session_id reason: present when status="aborted" β€” user cancelled operation. Do not retry. tool: present when status="aborted" β€” which tool was cancelled

ParametersJSON Schema
NameRequiredDescriptionDefault
codeYesPython code to execute. Has access to all MCP tools via 'from unified_api import *'. Tools are called as Python functions: server_name__tool_name(param=value). Replace dashes with underscores in server names.
persistentNoIf True, variables from this execution are saved and restored on the next call with the same session_id. Useful for multi-step workflows where later calls need results from earlier ones.
session_idNoSession identifier for persistent execution. Use the same ID across calls to share state between executions.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With annotations already indicating non-read-only, non-idempotent, open-world behavior, the description adds rich operational detail: timeout and abort statuses, partial results, session persistence behavior, and the instruction not to retry after user cancellation. This goes well beyond what the annotations convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with purpose and usage, but includes a lengthy example and a detailed return-value list that duplicates information available in the output schema. The example is useful for a code-execution tool, but the return section is not fully earning its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex code-execution tool, the description covers execution context, example usage, return statuses, session behavior, and abort handling. It is nearly complete, though it could mention additional constraints such as sandboxing, permissions, or resource limits.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents code, persistent, and session_id. The description provides an example of the calling convention and return details, but adds little semantic meaning about the parameters themselves beyond what the schema states.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states a specific verb and resource: execute Python code that orchestrates tools from multiple MCP servers. It distinguishes itself from list_available_tools by function, though it does not explicitly name the sibling or contrast the two.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives clear usage context, saying the tool is used to automate heuristic search or sequential execution with loops and error handling. It does not provide explicit when-not-to-use guidance or name alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_available_toolsB
Read-only

List all available tools from connected MCP servers with their signatures.

Returns dict with keys: servers (dict mapping server name to tool list), total_servers (int), total_tools (int).

ParametersJSON Schema
NameRequiredDescriptionDefault
include_descriptionsNoIf True, include full docstrings for each tool. Defaults to False to minimize context usage. Skip if you already have tool descriptions from schemas.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, destructiveHint=false, and openWorldHint=false, so the safety profile is fully covered. The description's return-value detail adds context but largely duplicates the output schema. No auth, rate-limit, or pagination behavior is disclosed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose is front-loaded in the first sentence and the whole definition is short. The return-dict breakdown is somewhat redundant given the output schema, costing a little space.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a parameterless read-only listing tool, the definition is adequate: annotations cover the safety profile and the output schema covers return values. The main gap is the absence of guidance on when to prefer this over the sibling execution tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and the parameter's own description is thorough, so the baseline is 3. The phrase 'with their signatures' loosely hints at the include_descriptions toggle but adds nothing beyond what the schema already documents.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: lists all available tools from connected MCP servers, adding the useful detail 'with their signatures.' However, it does not differentiate itself from the sibling execute_composed_code, leaving the agent to infer the distinction between enumerating tools and executing composed code.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives no when-to-use guidance, prerequisites, or named alternatives. The only usage hint ('Skip if you already have tool descriptions from schemas') lives in the schema's parameter description, not in the tool description itself.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 2 tool updatesv0.1.0
    • First observedexecute_composed_code
    • First observedlist_available_tools

TDQS

A3.7/5.0

Scored across 2 tools

Disambiguation5/5

The two tools have clearly distinct purposes: one discovers available MCP tools and signatures, while the other executes composed Python that orchestrates those tools. There is no overlap in resource or action, so an agent can easily select the right tool.

Naming Consistency5/5

Both names use consistent snake_case and follow a clear verb_noun pattern: execute_composed_code and list_available_tools. The convention is predictable and readable.

Tool Count3/5

Only two tools are provided for an orchestration server, which is thin even if the pair is focused. Each tool earns its place, but the surface lacks supporting operations like session management or execution status checks.

Completeness4/5

Discovery and execution are the core operations needed for code-based orchestration, and both are present. Minor gaps remain around session lifecycle management (e.g., listing, resuming, or deleting saved sessions) and direct cancellation controls.

Maintenance

ActivityInactive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    C
    quality
    D
    maintenance
    This is an MCP server that facilitates building tools for interacting with various APIs and workflows, supporting Python-based development with potential for customizable prompts and user configurations.
    1
    -
  • A
    license
    B
    quality
    A
    maintenance
    A meta-MCP server that manages and aggregates other MCP servers, enabling LLMs to dynamically extend their own capabilities by searching for, adding, and configuring tool servers.
    16
    85 PyPI
    142
    AGPL 3.0
  • F
    license
    Not graded
    quality
    D
    maintenance
    A meta-MCP server that acts as a universal gateway, allowing users to discover and execute tools from thousands of other MCP servers through semantic search. It dynamically loads servers on demand and provides standardized functions for searching, discovering, and running tools across the entire MCP ecosystem.
    6
    -