Skip to main content
Glama
hexsupport

HexagonML ModelManager MCP Server

by hexsupport

HexagonML ModelManager MCP Server

A modular MCP server for interacting with the HexagonML ModelManager API, with full tool discoverability and clean architecture.

Architecture

server/
├── config.py                          # Environment configuration and server setup
├── auth.py                            # Per-caller ModelManager credential resolution
├── clients.py                         # ModelManager API client factory and context
├── health.py                          # HTTP /health endpoint
├── main.py                            # Main entry point (standalone mode)
├── server_simple.py                   # FastMCP CLI entry point
├── tools/
│   ├── model_tools.py                 # Model management tools
│   ├── usecase_tools.py               # Usecase management tools
│   ├── modelcard_tools.py             # Model card management tools
│   ├── forecasting_tools.py           # Forecasting tools
│   └── forecasting_governance_tools.py # Governance report tools
├── handlers/
│   ├── response_handlers.py           # API response classification and formatting
│   └── prompt_handler.py              # MCP prompt templates
└── utils/
    ├── helpers.py                     # Response handling and validation helpers
    ├── validators.py                  # Payload validation functions
    └── base.py                        # Base classes and common patterns

Related MCP server: RAGFlow MCP Server

Available Tools

Model Management

Tool

Description

add_model

Upload a new ML model

update_model

Update model metadata or configuration

delete_model

Permanently delete a model

get_latest_metrics

Retrieve model performance metrics

Usecase Management

Tool

Description

add_usecase

Create a new usecase (supports Forecasting type)

update_usecase

Update usecase configuration

delete_usecase

Permanently delete a usecase

get_usecase_data

List all usecases

Model Cards

Tool

Description

create_modelcard

Generate a model card for a model

get_modelcard_data

Retrieve model card data with optional filtering

Forecasting

Tool

Description

get_forecast

Retrieve forecasts for a usecase

MCP Prompts

Prompt

Description

forecast_presentation_guide

Instructs the LLM how to narrate forecast results to users

Configuration

Copy .env.example to .env and fill in your values:

cp .env.example .env

Variable

Required

Default

Description

MM_API_BASE_URL

Yes

—

Base URL of the ModelManager API

OUTPUT_DIR

Yes

—

Directory for generated output files

MM_API_KEY

No

—

Fallback ModelManager credential, used only when the calling client sends none. Set it for single-user servers; leave it unset for shared deployments (see Authentication)

HOST

No

0.0.0.0

Network interface the server listens on

PORT

No

9000

Port the server listens on

Docker note: MM_API_BASE_URL=http://127.0.0.1:8000 will not resolve from inside a container. Use http://host.docker.internal:8000 on Mac/Windows, or http://172.17.0.1:8000 on Linux. In Kubernetes use the in-cluster Service DNS name, e.g. http://mm.tai-mm-ws.svc.cluster.local:8000.

Authentication

Every ModelManager call this server makes is authenticated with an mmgr3 credential. Whose credential depends on how you deploy:

Deployment

MM_API_KEY

Who ModelManager sees

Shared — one HTTP server, many people

unset

Each caller, via the key they send on the request

Single-user — local stdio, CI job

set

The account that owns MM_API_KEY

A caller-supplied credential always wins; MM_API_KEY applies only when the caller sent none.

Callers pass their credential in either header:

Authorization: Api-Key <your-personal-key>     # preferred
X-MM-API-Key: <your-personal-key>              # when a proxy already uses Authorization

Authorization: Bearer <jwt> also works, since mmgr3 accepts JWTs on the same endpoints — but a JWT expires after 60 minutes, so personal API keys are the right choice for MCP clients. Any other scheme is ignored rather than forwarded.

Users generate their own key on the MCP Endpoints page (/mcp) in ModelManager; it is displayed exactly once. Keys are named, revocable, and optionally expiring, so each client can be revoked independently.

Don't set MM_API_KEY on a shared deployment. It becomes an anonymous fallback identity that any caller who can reach the port inherits, which defeats per-user attribution and revocation. The published image excludes .env, so a deployed container is passthrough-only unless you explicitly set the variable.

A tool call with no usable credential is rejected before the tool runs, with a message naming the header to send.

Running the Server

fastmcp run server/server_simple.py --transport http --host 0.0.0.0 --port 9000

Option 2: Standalone Python

python server/main.py

Option 3: Development / Inspector

fastmcp dev server/server_simple.py

Option 4: Docker Compose

# Build and start
docker compose up --build

# Run in background
docker compose up --build -d

# Stop
docker compose down

Option 5: Docker (manual)

# Build
docker build -t hex-mm-mcp:latest .

# Run
docker run -d --name hex-mm-mcp \
  -p 9000:9000 \
  --env-file .env \
  hex-mm-mcp:latest

Connecting an MCP client

Two things are needed in every case: the server's URL (its MCP endpoint is /mcp) and your own ModelManager API key from the /mcp page in ModelManager.

A deployed server (the normal case)

Point the client at the deployed URL and send your key as a header. This is the shared-deployment shape — everyone uses the same URL with their own key.

Claude Code — one command:

claude mcp add --transport http hex-mm-mcp https://mcp.example.com/mcp \
  --header "Authorization: Api-Key ${MM_API_KEY}"

Claude Code / VS Code / Cursor — or by config file (.mcp.json in a project, or the client's own settings):

{
  "mcpServers": {
    "hex-mm-mcp": {
      "type": "http",
      "url": "https://mcp.example.com/mcp",
      "headers": {
        "Authorization": "Api-Key YOUR_PERSONAL_KEY"
      }
    }
  }
}

Claude Desktop and other clients that only launch local commands — bridge to the remote server with mcp-remote:

{
  "mcpServers": {
    "hex-mm-mcp": {
      "command": "npx",
      "args": [
        "-y", "mcp-remote",
        "https://mcp.example.com/mcp",
        "--header", "Authorization:Api-Key YOUR_PERSONAL_KEY"
      ]
    }
  }
}

Replace https://mcp.example.com/mcp with your deployment's URL and YOUR_PERSONAL_KEY with the key you generated. Prefer your client's secret/environment-variable support over pasting the key into a file that gets committed.

Verify the endpoint is reachable before wiring up a client:

curl https://mcp.example.com/health
# {"status":"healthy","service":"hexagonml-modelmanager-mcp"}

A local server you run yourself

Single-user, so the key can live in the server's environment (MM_API_KEY in .env) instead of in each client's config:

{
  "mcpServers": {
    "hex-mm-mcp": {
      "command": "fastmcp",
      "args": [
        "run",
        "/path/to/hex-mm-mcp/server/server_simple.py",
        "--transport", "http",
        "--host", "127.0.0.1",
        "--port", "9000"
      ]
    }
  }
}

Local Docker

{
  "mcpServers": {
    "hex-mm-mcp": {
      "command": "docker",
      "args": [
        "run", "--rm", "-i", "--network=host",
        "-e", "MM_API_KEY",
        "-e", "MM_API_BASE_URL",
        "-e", "OUTPUT_DIR",
        "hex-mm-mcp:latest"
      ],
      "env": {
        "MM_API_KEY": "your-api-key",
        "MM_API_BASE_URL": "http://host.docker.internal:8000",
        "OUTPUT_DIR": "/tmp/mm-output"
      }
    }
  }
}

LLM Presentation Guidance

The get_forecast tool uses two complementary mechanisms to guide how the LLM presents forecast data to users.

Embedded _llm_instructions

Every get_forecast response includes a _llm_instructions field. The LLM reads this automatically as part of the tool result and follows the rules when composing its reply — no client configuration needed.

Key rules embedded in every response:

  • Lead with the summary (date range, value range min/max/avg)

  • Explain lower_bound/upper_bound as a confidence interval

  • Reference last_actual_update to indicate data freshness

  • Flag resolved: false as an unresolvable request

  • Avoid echoing raw JSON back to the user

MCP Prompt (forecast_presentation_guide)

A named MCP prompt is also registered on the server. MCP-aware clients (Claude Desktop, MCP Inspector) can fetch it and inject it as a system prompt at the start of a conversation, priming the LLM for the whole session rather than per-call.

To use it in a client that supports MCP prompts:

prompts/get  →  forecast_presentation_guide

Mechanism

When it fires

Client support needed

_llm_instructions

Every get_forecast call

None — automatic

forecast_presentation_guide

On explicit prompt request

MCP prompt-capable client

Development

Tests

uv run --with pytest python -m pytest tests/ -q

tests/conftest.py puts server/ on sys.path, so tests import the modules the same way fastmcp runs them (from config import mcp).

CI (.github/workflows/mcp-ci.yml) gates every push on those tests plus a check that the server still imports and registers its tools, then publishes modelmanagerdev/mcp:<git-sha> — an immutable tag, so a deployment always traces back to an exact commit.

Adding a New Tool

  1. Create a new module (e.g., server/new_tools.py)

  2. Import mcp from config and use the @mcp.tool decorator

  3. Import the module in server/server_simple.py

# server/new_tools.py
from fastmcp import Context
from config import mcp
from clients import get_mm_client
from utils import safe_response_to_dict, create_error_response
import asyncio

@mcp.tool(
    name="new_tool",
    description="Description of the tool",
    tags={"category", "modelmanager"},
    meta={"version": "1.0", "author": "HexagonML"},
)
async def new_tool(ctx: Context, required_param: str, optional_param: str = None) -> dict:
    """Tool docstring shown in MCP playgrounds."""
    if not required_param or not required_param.strip():
        return create_error_response(
            message="required_param is required",
            error_type="ValidationError"
        )

    await ctx.info("Starting operation")
    await ctx.report_progress(progress=20, total=100)

    try:
        client = get_mm_client(ctx, 'client_type')
        await ctx.report_progress(progress=40, total=100)

        response = await asyncio.to_thread(client.api_method, required_param)
        await ctx.report_progress(progress=80, total=100)

        result = safe_response_to_dict(response)
        await ctx.info("Operation completed")
        await ctx.report_progress(progress=100, total=100)
        return result

    except ValueError as e:
        await ctx.error(f"Validation error: {str(e)}")
        return create_error_response(
            message="A validation error occurred. Please check your input.",
            error_type="ValidationError"
        )
    except Exception as e:
        await ctx.error(f"Operation failed: {str(e)}")
        return create_error_response(
            message="An internal error occurred. Please try again.",
            error_type="InternalError"
        )

Troubleshooting

Port already in use

kill -9 $(lsof -t -i:9000)
# or use a different port
fastmcp run server/server_simple.py --port 9001

Missing environment variables
The server exits at startup with a message listing which of MM_API_BASE_URL and OUTPUT_DIR are missing. MM_API_KEY is optional and its absence is normal for a shared deployment.

"No ModelManager credential for this request"
The calling client sent no credential and the server has no MM_API_KEY fallback. Add Authorization: Api-Key <key> to the client's config (see Connecting an MCP client).

401 from ModelManager on every tool call
The credential reached ModelManager but was rejected — the key is revoked, expired, or mistyped. Check its status badge on the /mcp page in ModelManager and generate a new one if needed. Note the scheme is Api-Key, not Bearer.

Connection refused / API errors

  • Confirm the ModelManager API is reachable at MM_API_BASE_URL

  • When running in Docker, see the Docker note in the Configuration section above

  • Verify the key in use is a valid, non-expired key issued from ModelManager's /mcp page and that its owning account can access the use case

License

MIT License — see the LICENSE file for details.

Related MCP Connectors

Related MCP Servers

  • F
    license
    B
    quality
    D
    maintenance
    Implements the Model Context Protocol (MCP) to provide AI models with a standardized interface for connecting to external data sources and tools like file systems, databases, or APIs.
    1
    153
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides a comprehensive Model Context Protocol interface for RAGFlow, enabling AI models to perform semantic retrieval, manage datasets, and handle document chunks. It supports advanced features like GraphRAG and RAPTOR for sophisticated knowledge base management and natural language querying.
    MIT