REST API Reference

Complete reference for all REST API endpoints exposed by MCP Hangar in HTTP mode.

Base URL: http://localhost:8000/api

All responses are JSON. Error responses return:

{"error": "<ExceptionType>", "message": "<description>", "status_code": <code>}

MCP servers

List MCP servers

GET /mcp_servers?state={state}
ParameterInTypeRequiredDescription
statequerystringNoFilter: cold, ready, degraded, dead

Response 200:

{
  "mcp_servers": [
    {
      "mcp_server": "math",
      "state": "ready",
      "mode": "subprocess",
      "alive": true,
      "tools_count": 5,
      "health_status": "healthy",
      "tools_predefined": false,
      "description": "Math computation mcp_server"
    }
  ]
}

Create MCP Server

POST /mcp_servers

Request body:

FieldTypeRequiredDefaultDescription
mcp_server_idstringYes--Unique identifier
modestringYes--subprocess, docker, or remote
commandlist[string]For subprocess--Command to run
imagestringFor docker--Docker image
endpointstringFor remote--HTTP endpoint URL
envdictNo{}Environment variables
idle_ttl_sintNo300Idle timeout in seconds
health_check_interval_sintNo60Health check interval
descriptionstringNo--Human-readable description
volumeslist[string]No[]Docker volume mounts
networkstringNo"none"Docker network mode
read_onlyboolNotrueRead-only filesystem (docker)

Response 201:

{"mcp_server_id": "math", "created": true}

Get MCP Server

GET /mcp_servers/{mcp_server_id}

Response 200: MCP Server detail object with tools, health, and configuration.

Response 404: MCP Server not found.

Update MCP Server

PUT /mcp_servers/{mcp_server_id}

Request body (all fields optional):

FieldTypeDescription
descriptionstringNew description
envdictNew environment variables (replaces existing)
idle_ttl_sintNew idle timeout
health_check_interval_sintNew health check interval

Response 200:

{"mcp_server_id": "math", "updated": true}

Delete MCP Server

DELETE /mcp_servers/{mcp_server_id}

Stops the MCP server if running, then removes it from the registry.

Response 200:

{"mcp_server_id": "math", "deleted": true}

Start MCP Server

POST /mcp_servers/{mcp_server_id}/start

Response 200: Start result object.

Stop MCP Server

POST /mcp_servers/{mcp_server_id}/stop

Request body (optional):

FieldTypeDefaultDescription
reasonstring"user_request"Reason for stopping

Response 200: Stop result object.

Get MCP Server Tools

GET /mcp_servers/{mcp_server_id}/tools

Response 200:

{
  "tools": [
    {"name": "add", "description": "Add two numbers", "parameters": {...}}
  ]
}

Get MCP Server Health

GET /mcp_servers/{mcp_server_id}/health

Response 200: Health status object with check history.

Get MCP Server Logs

GET /mcp_servers/{mcp_server_id}/logs?lines={n}
ParameterInTypeDefaultRangeDescription
linesqueryint1001--1000Number of recent lines

Response 200:

{
  "logs": [
    {"timestamp": "2026-03-23T10:15:30", "line": "...", "mcp_server_id": "math", "stream": "stderr"}
  ],
  "mcp_server_id": "math",
  "count": 42
}

Get Tool Invocation History

GET /mcp_servers/{mcp_server_id}/tools/history?limit={n}&from_position={pos}
ParameterInTypeDefaultRangeDescription
limitqueryint1001--500Max records
from_positionqueryint0--Event store version offset

Response 200:

{
  "mcp_server_id": "math",
  "history": [...],
  "total": 42
}

Groups

List Groups

GET /groups

Response 200:

{
  "groups": [
    {
      "group_id": "llm-pool",
      "state": "healthy",
      "strategy": "round_robin",
      "members": [...],
      "circuit_breaker_state": "closed"
    }
  ]
}

Create Group

POST /groups

Request body:

FieldTypeRequiredDefaultDescription
group_idstringYes--Unique identifier
strategystringNo"round_robin"Load balancing strategy
min_healthyintNo1Minimum healthy members
descriptionstringNo--Description

Response 201:

{"group_id": "llm-pool", "created": true}

Get Group

GET /groups/{group_id}

Response 200: Group detail with members and circuit breaker state.

Update Group

PUT /groups/{group_id}

Request body (all optional):

FieldTypeDescription
strategystringNew strategy
min_healthyintNew minimum healthy count
descriptionstringNew description

Response 200:

{"group_id": "llm-pool", "updated": true}

Delete Group

DELETE /groups/{group_id}

Response 200:

{"group_id": "llm-pool", "deleted": true}

Rebalance Group

POST /groups/{group_id}/rebalance

Re-checks member health and resets circuit breaker if applicable.

Response 200:

{"status": "rebalanced", "group_id": "llm-pool"}

Add Group Member

POST /groups/{group_id}/members

Request body:

FieldTypeRequiredDefaultDescription
member_idstringYes--MCP Server ID to add
weightintNo1Routing weight
priorityintNo1Routing priority

Response 201:

{"group_id": "llm-pool", "mcp_server_id": "llm-1", "added": true}

Remove Group Member

DELETE /groups/{group_id}/members/{member_id}

Response 200:

{"group_id": "llm-pool", "mcp_server_id": "llm-1", "removed": true}

Discovery

List Sources

GET /discovery/sources

Response 200:

{"sources": [{"source_id": "...", "type": "docker", "mode": "additive", "enabled": true, "last_scan": "..."}]}

Register Source

POST /discovery/sources

Request body:

FieldTypeRequiredDefaultDescription
source_typestringYes--docker, filesystem, kubernetes, entrypoint
modestringYes--additive or authoritative
enabledboolNotrueActivate immediately
configdictNo{}Source-specific configuration

Response 201:

{"source_id": "...", "registered": true}

Update Source

PUT /discovery/sources/{source_id}

Request body (all optional): mode, enabled, config.

Response 200:

{"source_id": "...", "updated": true}

Delete Source

DELETE /discovery/sources/{source_id}

Response 200:

{"source_id": "...", "deregistered": true}

Trigger Scan

POST /discovery/sources/{source_id}/scan

Response 200:

{"source_id": "...", "scan_triggered": true, "mcp_servers_found": 3}

Enable/Disable Source

PUT /discovery/sources/{source_id}/enable

Request body:

{"enabled": true}

Response 200:

{"source_id": "...", "enabled": true}

List Pending MCP servers

GET /discovery/pending

Response 200:

{"pending": [{"name": "new-mcp-server", "source": "docker", "mode": "remote", ...}]}

List Quarantined MCP servers

GET /discovery/quarantined

Response 200:

{"quarantined": {...}}

Approve MCP Server

POST /discovery/approve/{name}

Response 200: Approval result.

Reject MCP Server

POST /discovery/reject/{name}

Response 200: Rejection result.


Configuration

Get Config

GET /config

Returns the current server configuration with sensitive fields stripped.

Response 200:

{"config": {"mcp_servers": [...]}}

Reload Config

POST /config/reload

Request body (optional):

FieldTypeDefaultDescription
config_pathstringserver defaultPath to config file
gracefulbooltrueGraceful reload

Response 200:

{"status": "reloaded", "result": {...}}

Export Config

POST /config/export

Serializes current in-memory state to YAML.

Response 200:

{"yaml": "mcp_servers:\n  math:\n    mode: subprocess\n    ..."}

Backup Config

POST /config/backup

Creates a rotating backup of the current configuration.

Response 200:

{"backup_path": "/path/to/backup.yaml", "created": true}

Config Diff

GET /config/diff

Compares on-disk configuration with current in-memory state.

Response 200:

{"diff": "--- on-disk\n+++ in-memory\n@@ ...", "has_changes": true}

System

Get System Info

GET /system

Response 200:

{
  "system": {
    "total_mcp_servers": 5,
    "mcp_servers_by_state": {"ready": 3, "cold": 2},
    "total_tools": 15,
    "total_tool_calls": 42,
    "uptime_seconds": 3600.5,
    "version": "1.1.0"
  }
}

Auth Management

Create API Key

POST /auth/keys

Request body:

FieldTypeRequiredDefaultDescription
principal_idstringYes--Principal this key authenticates as
namestringYes--Human-readable key name
created_bystringNo"system"Creator principal
expires_atstringNo--ISO8601 expiry datetime

Response 201:

{"key_id": "...", "raw_key": "mcp_...", "principal_id": "...", "name": "..."}

!!! warning The raw_key is returned only once. Store it securely.

Revoke API Key

DELETE /auth/keys/{key_id}

Request body (optional):

FieldTypeDefaultDescription
revoked_bystring"system"Revoking principal
reasonstring""Revocation reason

List API Keys

GET /auth/keys
ParameterInTypeRequiredDescription
principal_idquerystringYesPrincipal whose keys to list
include_revokedqueryboolNoInclude revoked keys (default true)

List All Roles

GET /auth/roles/all
ParameterInTypeRequiredDescription
include_builtinqueryboolNoInclude built-in roles (default true)

List Built-in Roles

GET /auth/roles

Get Role

GET /auth/roles/{role_name}

Create Custom Role

POST /auth/roles

Update Custom Role

PATCH /auth/roles/{role_name}

Delete Custom Role

DELETE /auth/roles/{role_name}

Assign Role

POST /auth/roles/assign

Request body:

{"principal_id": "...", "role_name": "developer", "scope": "global", "assigned_by": "system"}

Revoke Role

DELETE /auth/roles/revoke

Request body:

{"principal_id": "...", "role_name": "developer", "scope": "global", "revoked_by": "system"}

List Principals

GET /auth/principals

List Roles for Principal

GET /auth/principals/roles
ParameterInTypeRequiredDescription
principal_idquerystringYesPrincipal whose roles to list
scopequerystringNoScope filter (default * = all)

Check Permission

POST /auth/check-permission

Request body:

{"principal_id": "...", "permission": "mcp_servers:start"}

Get Tool Access Policy

GET /auth/policies/{scope}/{target_id}
ParameterInTypeRequiredDescription
scopepathstringYesprovider, group, or member
target_idpathstringYesIdentifier of the provider, group, or member

Set Tool Access Policy

POST /auth/policies/{scope}/{target_id}

Request body:

{"allow_list": ["tool_a", "tool_b*"], "deny_list": ["tool_c"]}

Clear Tool Access Policy

DELETE /auth/policies/{scope}/{target_id}

WebSocket Endpoints

Events Stream

ws://host:port/api/ws/events

Streams all domain events as JSON frames.

See the WebSockets guide for connection details.