Skip to content

ToolDock is a multi‑tenant MCP server with namespace routing, exposing tools via OpenAPI, MCP (streamable HTTP), and a web admin UI. Includes FastMCP registry integration and per‑namespace dependency management.

License

Notifications You must be signed in to change notification settings

ponmeloco/ToolDock

Repository files navigation

ToolDock

One Server. Every Protocol. All Your Tools.

Multi-tenant MCP server with namespace-based routing, exposing Python tools via OpenAPI, MCP, and Web GUI.

Python 3.12+ MCP Streamable HTTP OpenAPI 3.0 MIT License

Disclaimer: ToolDock is still under active development and best treated as a demo/preview.
Use at your own risk, especially in production environments.


Quickstart

# Clone and start
git clone https://github.com/ponmeloco/ToolDock.git
cd ToolDock
./start.sh

# Force rebuild images
./start.sh --rebuild

Or manually:

cp .env.example .env
nano .env  # Set BEARER_TOKEN
docker compose up -d

Verify:

curl http://localhost:18006/health   # OpenAPI
curl http://localhost:18007/health   # MCP
curl http://localhost:13000          # Admin UI

On first start, ToolDock installs and auto-starts a demo FastMCP server from the modelcontextprotocol/servers repo (time server). You can disable or change it via FASTMCP_DEMO_* in .env.


Screenshots

Screenshots (from docs/images/):

Admin Dashboard

Admin Dashboard

Playground

Playground

FastMCP Registry

FastMCP Registry

Logs

Logs

Namespaces Tool Create/Edit

Namespaces


Troubleshooting

Buildx Permission Error

If ./start.sh --rebuild fails with:

failed to update builder last activity time: ... permission denied

It means Docker Buildx created files in ~/.docker as root (often from a past sudo docker ... run).
Fix by resetting ownership, then retry:

sudo chown -R $USER:$USER ~/.docker
./start.sh --rebuild

This only fixes Docker’s local metadata directory; it does not affect your ToolDock data in tooldock_data/.


Features

Feature Description
Admin UI React dashboard with code editor, playground, and logs
Multi-Tenant Each folder becomes a separate MCP endpoint
Dual Transport OpenAPI + MCP from the same codebase
Hot Reload Reload tools without server restart
Playground Test tools via OpenAPI or MCP (real servers)
Persistent Logs Daily JSON log files with auto-cleanup
Metrics Error rates + tool call counts via SQLite + in-memory queue
FastMCP Servers Install from MCP Registry or add manual servers
Config Editor Edit server configs with YAML/JSON syntax highlighting
Unified Namespaces View all namespace types (native, fastmcp, external)

Architecture

┌──────────────────┐     ┌─────────────────────────────────────┐
│   Admin UI       │     │         ToolDock Backend             │
│   (React)        │     ├─────────────────────────────────────┤
│  Port 13000      │────→│  Port 18006 → OpenAPI/REST          │
│                  │     │  Port 18007 → MCP HTTP              │
└──────────────────┘     │  Port 18080 → Backend API           │
                         ├─────────────────────────────────────┤
LiteLLM ────────────────→│  /mcp/shared    → shared/ tools     │
Claude Desktop ─────────→│  /mcp/team1     → team1/ tools      │
                         └─────────────────────────────────────┘

Configuration

Environment Variables (.env)

# Required
BEARER_TOKEN=your_secure_token_here

# Optional - Ports
OPENAPI_PORT=18006
MCP_PORT=18007
WEB_PORT=18080
ADMIN_PORT=13000

# Optional - Logging
LOG_RETENTION_DAYS=30  # Auto-delete logs after N days
METRICS_RETENTION_DAYS=30  # SQLite metrics retention window

# Optional - MCP Protocol (strict mode)
MCP_PROTOCOL_VERSION=2025-11-25
MCP_PROTOCOL_VERSIONS=2025-11-25,2025-03-26

# Optional - Host display (Admin UI)
HOST_DATA_DIR=./tooldock_data

# Optional - External MCP
GITHUB_TOKEN=ghp_xxxxxxxxxxxx

Namespace Routing

Each folder in tooldock_data/tools/ becomes a separate endpoint:

Folder MCP Endpoint
tools/shared/ /mcp/shared
tools/team1/ /mcp/team1
tools/finance/ /mcp/finance

External MCP Servers

FastMCP (Recommended)

Install FastMCP servers from the MCP Registry and expose them via namespaces. Use the FastMCP tab in the Admin UI to search, install, start, stop, and delete servers.

Two installation methods:

  1. From Registry - Search the MCP Registry for PyPI/npm packages:

    • Search by name in the Admin UI
    • Select a server and choose a namespace
    • Click Install to download and configure
  2. Manual Server - Add servers using Claude Desktop config format:

    • Specify command (e.g., python, node, npx)
    • Add arguments (e.g., -m my_module --config config.yaml)
    • Set environment variables (e.g., API_KEY=xxx)

Server Management:

Click on any installed server to open the detail panel:

  • View server status, PID, and port
  • Edit start command (command, args, env)
  • Edit config files with syntax highlighting (YAML/JSON)
  • Start, stop, or delete the server

Config files are stored in tooldock_data/external/servers/{namespace}/.

The default database is SQLite at /data/db/tooldock.db. You can switch to Postgres via:

DATABASE_URL=postgresql+psycopg://user:pass@host:5432/tooldock

Legacy External Config (STDIO)

Configure external servers in tooldock_data/external/config.yaml (or $DATA_DIR/external/config.yaml) and reload without restart:

curl -X POST http://localhost:18080/api/servers/reload \
  -H "Authorization: Bearer change_me"

These servers are exposed as namespaces like /mcp/github.


Adding Tools

Via Admin UI

  1. Open http://localhost:13000
  2. Tools → select namespace → New Tool
  3. Edit the template → Save (valid code required; auto-reloads)

Via File

Create tooldock_data/tools/shared/my_tool.py:

from pydantic import BaseModel, Field, ConfigDict
from app.registry import ToolDefinition, ToolRegistry

class MyToolInput(BaseModel):
    model_config = ConfigDict(extra="forbid")
    query: str = Field(..., description="The query")

async def handler(payload: MyToolInput) -> dict:
    return {"result": f"Processed: {payload.query}"}

def register_tools(registry: ToolRegistry) -> None:
    MyToolInput.model_rebuild(force=True)
    registry.register(ToolDefinition(
        name="my_tool",
        description="Processes a query",
        input_model=MyToolInput,
        handler=handler,
    ))

Then reload:

curl -X POST http://localhost:18080/api/reload/shared \
  -H "Authorization: Bearer change_me"

Dependencies (Per Namespace)

Each namespace gets its own Python venv stored in tooldock_data/venvs/{namespace}.
Install dependencies via the Admin UI Tools → Dependencies, or via API:

curl -X POST http://localhost:18080/api/folders/shared/tools/deps/install \
  -H "Authorization: Bearer change_me" \
  -H "Content-Type: application/json" \
  -d '{"packages": ["requests==2.32.0"]}'

Create/Delete venv:

curl -X POST http://localhost:18080/api/folders/shared/tools/deps/create \
  -H "Authorization: Bearer change_me"

curl -X POST http://localhost:18080/api/folders/shared/tools/deps/delete \
  -H "Authorization: Bearer change_me"

After install, ToolDock auto-reloads the namespace so imports work immediately.

Uninstall (pip/setuptools/wheel are protected):

curl -X POST http://localhost:18080/api/folders/shared/tools/deps/uninstall \
  -H "Authorization: Bearer change_me" \
  -H "Content-Type: application/json" \
  -d '{"packages": ["requests"]}'

Connecting Clients

LiteLLM

mcp_servers:
  - server_name: "tooldock"
    # If LiteLLM runs in Docker on the same network, use the service name:
    url: "http://tooldock-backend:8007/mcp/shared"
    api_key_header: "Authorization"
    api_key_value: "Bearer change_me"

If LiteLLM runs on the host (not in Docker), use: http://localhost:18007/mcp/shared

Claude Desktop

Add to ~/.config/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "tooldock": {
      "url": "http://localhost:18007/mcp/shared",
      "headers": {
        "Authorization": "Bearer change_me"
      }
    }
  }
}

API Reference

OpenAPI (Port 18006)

Endpoint Method Description
/health GET Health check
/tools GET List tools
/tools/{name} POST Execute tool

MCP (Port 18007)

Endpoint Method Description
/health GET Health check
/mcp/namespaces GET List namespaces
/mcp POST JSON-RPC endpoint (all namespaces)
/mcp/{namespace} POST JSON-RPC endpoint (namespace)
/mcp/info GET Non-standard discovery
/mcp/{namespace}/info GET Non-standard discovery

Backend API (Port 18080)

Endpoint Method Description
/api/folders GET List namespaces
/api/folders/{ns}/tools GET/POST List/upload tools
/api/reload POST Hot reload all
/api/reload/{ns} POST Hot reload namespace
/api/admin/logs GET View logs
/api/admin/logs/files GET List log files
/api/admin/metrics GET Metrics for dashboard (error rates + tool calls)
/api/admin/namespaces GET List all namespaces (native, fastmcp, external)
/api/playground/tools GET List tools for playground
/api/playground/execute POST Execute tool (OpenAPI/MCP via real servers)

FastMCP API (Port 18080)

Endpoint Method Description
/api/fastmcp/registry/servers GET Search MCP Registry
/api/fastmcp/registry/health GET Check registry connectivity
/api/fastmcp/servers GET List installed servers
/api/fastmcp/servers POST Install from registry
/api/fastmcp/servers/manual POST Add manual server
/api/fastmcp/servers/{id} GET Get server details
/api/fastmcp/servers/{id} PUT Update server config
/api/fastmcp/servers/{id} DELETE Delete server
/api/fastmcp/servers/{id}/start POST Start server
/api/fastmcp/servers/{id}/stop POST Stop server
/api/fastmcp/servers/{id}/config GET Get config file content
/api/fastmcp/servers/{id}/config PUT Update config file
/api/fastmcp/servers/{id}/config/files GET List config files

MCP Strict Mode Notes

  • GET /mcp and GET /mcp/{namespace} open SSE streams (requires Accept: text/event-stream).
  • POST requests require Accept: application/json (include text/event-stream if you can handle streaming responses).
  • JSON-RPC batching is rejected.
  • Notifications-only requests return 202 with no body.
  • Origin header is validated against CORS_ORIGINS.
  • MCP-Protocol-Version is validated if present; supported versions configured via MCP_PROTOCOL_VERSIONS.

Metrics

  • Metrics are aggregated from a hybrid in-memory queue + SQLite store at tooldock_data/metrics.sqlite.
  • Dashboard reads GET /api/admin/metrics for error rates and tool call counts.
  • Retention is controlled by METRICS_RETENTION_DAYS (default 30 days).

Testing

# Run all tests (470+)
pytest tests/ -v

# Run with coverage
pytest tests/ --cov=app --cov-report=html

# Install test dependencies (optional, for development)
pip install pytest pytest-asyncio pytest-cov

Note: Tests are skipped automatically on production servers without pytest installed. When using ./start.sh, tests run inside the backend container (Python 3.12) to avoid host interpreter incompatibilities.


License

MIT License - see LICENSE for details.

About

ToolDock is a multi‑tenant MCP server with namespace routing, exposing tools via OpenAPI, MCP (streamable HTTP), and a web admin UI. Includes FastMCP registry integration and per‑namespace dependency management.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •