Skip to content

unblu-mcp

ci documentation pypi version python versions license FastMCP Ruff uv

A Model Context Protocol (MCP) server for interacting with Unblu deployments. This server provides AI assistants with token-efficient access to 300+ Unblu API endpoints through progressive disclosure.

Table of Contents

🔒 Security First: This server includes built-in safety controls. The call_api tool is marked with destructiveHint: true to trigger client confirmations, and optional Eunomia integration provides server-side policy enforcement to block destructive operations. Learn more →

Features

This server implements best practices from Anthropic's guide on building effective agents:

  • Progressive Disclosure: 5 discovery tools instead of 300+ API definitions upfront, dramatically reducing token usage
  • Clear Tool Interfaces: Descriptive parameters with examples, helpful error messages that suggest alternatives
  • Full API Coverage: Access to all Unblu REST API v4 endpoints
  • Smart Discovery: Search and browse operations by service category or keyword
  • Field Filtering: Request only the fields you need to reduce response size
  • Response Truncation: Limit response sizes to prevent token overflow

Built with FastMCP 2.14+, leveraging cutting-edge features:

  • MCP Annotations: Tools include readOnlyHint, destructiveHint, and openWorldHint metadata for smarter AI decision-making
  • Response Caching: Discovery tools cache results via FastMCP middleware for faster repeated queries
  • Policy-Based Authorization: Optional Eunomia integration for controlling which API operations are allowed
  • Built-in Logging: Automatic file-based logging with daily rotation for debugging and usage analysis
  • MCP 2025-11-25 Spec: Full support for the latest Model Context Protocol specification

Installation

This package is available on PyPI and designed to be run directly via uvx - no installation required. See MCP Client Configuration below.

For development or customization, clone from source:

From source

git clone https://github.com/detailobsessed/unblu-mcp.git
cd unblu-mcp
uv sync

Configuration

MCP Client Configuration

Add the server to your MCP client configuration (Claude Desktop, Windsurf, etc.):

The K8s provider automatically manages kubectl port-forward connections to your Unblu deployment. This is the most tested configuration.

macOS (~/Library/Application Support/Claude/claude_desktop_config.json or IDE MCP config):

{
  "mcpServers": {
    "unblu": {
      "command": "uvx",
      "args": ["unblu-mcp", "--provider", "k8s", "--environment", "dev"]
    }
  }
}

Windows (%APPDATA%\Claude\claude_desktop_config.json):

{
  "mcpServers": {
    "unblu": {
      "command": "uvx",
      "args": ["unblu-mcp", "--provider", "k8s", "--environment", "dev"]
    }
  }
}

See K8s Provider Configuration below for setting up your environments.

With Environment Variables

For direct API access without Kubernetes:

{
  "mcpServers": {
    "unblu": {
      "command": "uvx",
      "args": ["unblu-mcp"],
      "env": {
        "UNBLU_BASE_URL": "https://your-instance.unblu.cloud/app/rest/v4",
        "UNBLU_API_KEY": "your-api-key"
      }
    }
  }
}

Environment Variables

Variable Required Description
UNBLU_BASE_URL No Your Unblu API base URL (default: https://unblu.cloud/app/rest/v4)
UNBLU_API_KEY One of these API key for authentication (Bearer token)
UNBLU_USERNAME One of these Username for basic authentication
UNBLU_PASSWORD With username Password for basic authentication

Available Tools

The server exposes 5 tools for progressive API discovery and execution:

Tool Description
list_services() List all API service categories (e.g., Conversations, Users, Bots)
list_operations(service) List operations in a specific service
search_operations(query) Search for operations by keyword
get_operation_schema(operation_id) Get full schema for an operation
call_api(operation_id, ...) Execute any API operation

Example Workflow

  1. Discover services: list_services() → See categories like "Conversations", "Users", "Bots"
  2. Browse operations: list_operations("Conversations") → See available conversation operations
  3. Get details: get_operation_schema("conversationsGetById") → See required parameters
  4. Execute: call_api("conversationsGetById", path_params={"conversationId": "abc123"})

Advanced Options

The call_api tool supports additional parameters for token efficiency:

call_api(
    operation_id="conversationsSearch",
    body={"query": "support"},
    fields=["id", "topic", "creationTimestamp"],  # Only return these fields
    max_response_size=10000  # Truncate response if larger than 10KB
)

Command Line Usage

# Run with stdio transport (default, for MCP clients)
unblu-mcp

# Use a custom swagger.json location
unblu-mcp --spec /path/to/swagger.json

# Run with Eunomia authorization policy (requires unblu-mcp[safety])
unblu-mcp --policy /path/to/mcp_policies.json

# Run with K8s provider (auto-starts kubectl port-forward)
unblu-mcp --provider k8s --environment dev

# Run with K8s provider using custom config file
unblu-mcp --provider k8s --environment my-env --k8s-config /path/to/k8s_environments.yaml

# Show version
unblu-mcp --version

# Show debug info
unblu-mcp --debug-info

CLI Arguments

Argument Values Default Description
--spec path auto-detect Path to swagger.json
--policy path none Eunomia policy file
--provider default, k8s default Connection provider
--environment string dev K8s environment (with --provider k8s)
--k8s-config path none Custom K8s environments YAML file

Logging & Observability

The server automatically logs all tool calls to help with debugging and usage analysis.

Log Location

Logs are written to ~/.unblu-mcp/logs/ with daily rotation:

~/.unblu-mcp/logs/
├── unblu-mcp.log              # Current log
├── unblu-mcp.log.2025-01-14   # Yesterday
├── unblu-mcp.log.2025-01-13   # Day before
└── ...

Configuration

Environment Variable Description
UNBLU_MCP_LOG_DIR Custom log directory (default: ~/.unblu-mcp/logs)
UNBLU_MCP_LOG_DISABLE Set to 1, true, or yes to disable file logging

Log Format

2025-01-15 14:30:22 | INFO     | fastmcp | tools/call request: call_api(operation_id="conversationsGetById", ...)

Logs include: - Timestamp (UTC) - Log level - Tool name and arguments - Response summaries - Request duration (duration_ms) for performance analysis

Logs are retained for 30 days and automatically rotated at midnight UTC.

Safety & Authorization

The call_api tool can execute any Unblu API operation, including destructive ones (DELETE, PUT, POST). This is a powerful capability that requires appropriate controls.

Two Layers of Protection

Layer Type Description
MCP Annotations Client-side destructiveHint: true signals clients to prompt for confirmation
Eunomia Policies Server-side Block unauthorized operations before they execute

Layer 1: Tool Annotations (Built-in)

The call_api tool includes these MCP annotations: - destructiveHint: true — Signals this tool may modify data - idempotentHint: false — Repeated calls may have different effects - openWorldHint: true — Interacts with external systems

Well-behaved MCP clients (like Claude Desktop) will prompt for user confirmation before executing tools marked as destructive.

Layer 2: Policy-Based Authorization (Optional)

For server-side enforcement, use Eunomia — a FastMCP middleware library for policy-based authorization — to define what operations are allowed:

# Install with safety features
pip install unblu-mcp[safety]

# Run with policy enforcement
unblu-mcp --policy config/mcp_policies.json

The included config/mcp_policies.json provides a sensible default:

Operation Type Policy Examples
Discovery tools ✅ Allowed list_services, list_operations, search_operations, get_operation_schema
Read-only API calls ✅ Allowed ~190 operations like *Get*, *Search*, *List*, *Read*, *Find*, *Check*
Destructive API calls ❌ Blocked ~140 operations like *Create*, *Update*, *Delete*, *Send*, *Login*, etc.

Custom Policies

To allow additional operations beyond read-only, create a custom policy file. For example, to allow creating and updating conversations:

{
  "version": "1.0",
  "name": "custom-policy",
  "default_effect": "deny",
  "rules": [
    {
      "name": "allow-all-discovery",
      "effect": "allow",
      "resource_conditions": [
        {"path": "attributes.tool_name", "operator": "in",
         "value": ["list_services", "list_operations", "search_operations", "get_operation_schema"]}
      ],
      "actions": ["execute"]
    },
    {
      "name": "allow-conversation-operations",
      "effect": "allow",
      "resource_conditions": [
        {"path": "attributes.tool_name", "operator": "eq", "value": "call_api"},
        {"path": "attributes.args.operation_id", "operator": "regex",
         "value": "^conversations(Get|Search|Read|Create|Update|Set)"}
      ],
      "actions": ["execute"]
    }
  ]
}

To allow all operations (no restrictions):

{
  "version": "1.0",
  "name": "allow-all",
  "default_effect": "allow",
  "rules": []
}

See the Eunomia documentation for advanced policy configuration.

Programmatic Usage

Connection Providers

The server supports pluggable connection providers for different deployment scenarios:

from unblu_mcp import create_server, DefaultConnectionProvider

# Default provider (uses environment variables)
server = create_server()

# Custom provider with explicit credentials
provider = DefaultConnectionProvider(
    base_url="https://my-instance.unblu.cloud/app/rest/v4",
    api_key="my-api-key",
)
server = create_server(provider=provider)

Kubernetes Port-Forward Provider

For Kubernetes deployments, use the built-in K8s provider:

Prerequisites: - kubectl installed and in PATH - Authenticated to your K8s cluster (kubectl auth login or valid kubeconfig) - Permissions to access services in the target namespace

The provider will check authentication before starting port-forward and provide helpful error messages if something is misconfigured.

from unblu_mcp import create_server, K8sConnectionProvider

# Connect to a K8s environment (starts kubectl port-forward automatically)
provider = K8sConnectionProvider(environment="dev")
server = create_server(provider=provider)

K8s environments are configured in ~/.unblu-mcp/k8s_environments.yaml:

environments:
  t1:
    local_port: 8084
    namespace: unblu-dev
    service: haproxy
    service_port: 8080
    api_path: /app/rest/v4

Custom Connection Providers

Implement the ConnectionProvider interface for custom connectivity:

from unblu_mcp import ConnectionProvider, ConnectionConfig

class MyProvider(ConnectionProvider):
    async def setup(self) -> None:
        # Initialize connection (e.g., start tunnel)
        pass

    async def teardown(self) -> None:
        # Clean up resources
        pass

    def get_config(self) -> ConnectionConfig:
        return ConnectionConfig(
            base_url="https://api.example.com",
            headers={"X-Custom-Header": "value"},
        )

Troubleshooting

GUI Apps Don't Inherit Shell Environment (macOS)

Problem: MCP servers fail to start in Windsurf, Claude Desktop, or other GUI apps with errors like: - kubectl not found in PATH - invalid peer certificate: UnknownIssuer (TLS/proxy issues) - Package installation failures from corporate PyPI mirrors

Cause: macOS GUI applications launch from launchd with a minimal environment, not from your shell. They don't inherit PATH or other environment variables from ~/.zshrc or ~/.bashrc.

Solution: Add the env block to your MCP configuration:

{
  "mcpServers": {
    "unblu": {
      "command": "uvx",
      "args": ["unblu-mcp", "--provider", "k8s", "--environment", "dev"],
      "env": {
        "PATH": "/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin"
      }
    }
  }
}

Add any directories containing tools your setup needs (e.g., kubectl, docker) to the PATH.

Corporate Proxy/PyPI Mirror Issues

Problem: When using uvx, you get TLS certificate errors or timeouts connecting to corporate PyPI mirrors.

Solution: Use --no-config to bypass uv.toml settings and --native-tls to use system certificates:

{
  "mcpServers": {
    "unblu": {
      "command": "uvx",
      "args": [
        "--no-config",
        "--native-tls",
        "unblu-mcp",
        "--provider", "k8s",
        "--environment", "dev"
      ],
      "env": {
        "PATH": "/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin"
      }
    }
  }
}

Finding MCP Server Logs

Different clients store logs in different locations:

Client Log Location
Windsurf Check Output panel → select "MCP" or "Windsurf"
Claude Desktop ~/Library/Logs/Claude/ (macOS)
Warp ~/Library/Group Containers/2BBY89MBSN.dev.warp/Library/Application Support/dev.warp.Warp-Stable/mcp/*.log

Testing the Server Locally

To debug without an MCP client, use the included test script:

# Test with K8s provider
uv run scripts/test_client.py --provider k8s --environment dev

# Test with default provider (requires UNBLU_BASE_URL)
UNBLU_BASE_URL=https://your-instance.unblu.cloud/app/rest/v4 \
UNBLU_API_KEY=your-key \
uv run scripts/test_client.py

Development

# Clone the repository
git clone https://github.com/detailobsessed/unblu-mcp.git
cd unblu-mcp

# Install dependencies
uv sync --all-extras --dev

# Run tests
uv run poe test

# Run linting
uv run poe lint

# Build documentation
uv run poe docs

License

ISC License