Skip to main content

Log Format

UMCP emits all logs as JSON lines to stderr. Each log entry has the following structure:
{
  "ts": "2024-03-15T10:30:45.123Z",
  "level": "info",
  "event": "provider.connected",
  "msg": "Connected to upstream provider",
  "context": {
    "providerId": "web_search.brave",
    "transport": "stdio"
  }
}

Log Fields

FieldTypeDescription
tsstringISO 8601 timestamp
levelstringLog level: debug, info, warn, error
eventstringEvent type (see below)
msgstringHuman-readable message
contextobjectAdditional structured data (optional)

Log Levels

UMCP supports four log levels:
  • debug (10): Detailed diagnostic information
  • info (20): General informational messages (default)
  • warn (30): Warning messages for non-critical issues
  • error (40): Error messages for failures
Logs are filtered by level weight. For example, if the log level is set to info, debug messages are suppressed.

Setting Log Level

You can control the log level using the UMCP_LOG_LEVEL environment variable:
export UMCP_LOG_LEVEL=debug
umcp serve
Valid values: debug, info, warn, error Default: info
Setting the log level to debug will show detailed diagnostic information, including internal state changes and detailed provider communication.

Log Events

UMCP emits the following event types:

Configuration Events

Emitted when UMCP auto-creates the default config file on first run.Level: infoContext:
{
  "configPath": "/Users/yan/.config/umcp/umcp.jsonc"
}
Example:
{
  "ts": "2024-03-15T10:30:45.123Z",
  "level": "info",
  "event": "config.created",
  "msg": "Created default umcp JSONC config file",
  "context": {
    "configPath": "/Users/yan/.config/umcp/umcp.jsonc"
  }
}
Emitted when configuration is successfully loaded and validated.Level: infoContext:
{
  "configPath": "/Users/yan/.config/umcp/umcp.jsonc",
  "categories": 2,
  "created": false
}
Example:
{
  "ts": "2024-03-15T10:30:45.234Z",
  "level": "info",
  "event": "config.loaded",
  "msg": "Loaded umcp config",
  "context": {
    "configPath": "/Users/yan/.config/umcp/umcp.jsonc",
    "categories": 2,
    "created": false
  }
}
Emitted when configuration validation fails.Level: errorContext:
{
  "configPath": "/Users/yan/.config/umcp/umcp.jsonc",
  "details": "categories.web_search.providers[0].command: provider.command is required when transport is stdio"
}
Example:
{
  "ts": "2024-03-15T10:30:45.345Z",
  "level": "error",
  "event": "config.invalid",
  "msg": "Config validation failed",
  "context": {
    "configPath": "/Users/yan/.config/umcp/umcp.jsonc",
    "details": "categories.web_search.providers[0].command: provider.command is required when transport is stdio"
  }
}

Provider Events

Emitted when UMCP successfully connects to an upstream provider.Level: infoContext:
{
  "providerId": "web_search.brave",
  "transport": "stdio"
}
Example:
{
  "ts": "2024-03-15T10:30:46.123Z",
  "level": "info",
  "event": "provider.connected",
  "msg": "Connected to upstream provider",
  "context": {
    "providerId": "web_search.brave",
    "transport": "stdio"
  }
}
Emitted when UMCP disconnects from an upstream provider. This can happen for:
  • Ephemeral clients (created per-request for round-robin env rotation)
  • Persistent clients (on shutdown)
Level: infoContext:
{
  "providerId": "web_search.brave"
}
Examples:Ephemeral client:
{
  "ts": "2024-03-15T10:30:47.123Z",
  "level": "info",
  "event": "provider.disconnected",
  "msg": "Disconnected ephemeral upstream client",
  "context": {
    "providerId": "web_search.brave"
  }
}
Persistent client:
{
  "ts": "2024-03-15T10:35:00.456Z",
  "level": "info",
  "event": "provider.disconnected",
  "msg": "Closed persistent upstream client",
  "context": {
    "providerId": "web_search.brave"
  }
}

Tool Events

Emitted when UMCP discovers tools from an upstream provider.Level: infoContext:
{
  "providerId": "web_search.brave",
  "count": 3
}
Example:
{
  "ts": "2024-03-15T10:30:46.234Z",
  "level": "info",
  "event": "tool.discovered",
  "msg": "Discovered upstream tools",
  "context": {
    "providerId": "web_search.brave",
    "count": 3
  }
}
Emitted when UMCP registers a unified tool with the MCP server.Level: infoContext:
{
  "finalName": "web_search.brave.search",
  "providerId": "web_search.brave",
  "upstreamName": "search"
}
Example:
{
  "ts": "2024-03-15T10:30:46.345Z",
  "level": "info",
  "event": "tool.registered",
  "msg": "Registered unified tool",
  "context": {
    "finalName": "web_search.brave.search",
    "providerId": "web_search.brave",
    "upstreamName": "search"
  }
}
Emitted when a tool is invoked and forwarded to the upstream provider.Level: infoContext:
{
  "finalName": "web_search.brave.search",
  "providerId": "web_search.brave",
  "upstreamName": "search"
}
Example:
{
  "ts": "2024-03-15T10:31:15.123Z",
  "level": "info",
  "event": "tool.called",
  "msg": "Forwarding tool call to upstream provider",
  "context": {
    "finalName": "web_search.brave.search",
    "providerId": "web_search.brave",
    "upstreamName": "search"
  }
}
Emitted when UMCP cannot convert a tool’s JSON Schema to Zod and uses a permissive schema.Level: warnContext:
{
  "finalName": "web_search.brave.search",
  "upstreamName": "search"
}
Example:
{
  "ts": "2024-03-15T10:30:46.456Z",
  "level": "warn",
  "event": "tool.schema_fallback",
  "msg": "Using permissive schema for tool",
  "context": {
    "finalName": "web_search.brave.search",
    "upstreamName": "search"
  }
}

Environment Events

Emitted when an environment variable is rotated using round-robin for a provider.Level: infoContext:
{
  "providerId": "web_search.brave",
  "key": "BRAVE_API_KEY",
  "index": 1,
  "total": 3,
  "masked": "AB***YZ"
}
Example:
{
  "ts": "2024-03-15T10:31:15.234Z",
  "level": "info",
  "event": "env.rotated",
  "msg": "Rotated env key using round-robin",
  "context": {
    "providerId": "web_search.brave",
    "key": "BRAVE_API_KEY",
    "index": 1,
    "total": 3,
    "masked": "AB***YZ"
  }
}

Server Events

Emitted when the UMCP server starts successfully.Level: infoContext (stdio):
{}
Context (http):
{
  "host": "127.0.0.1",
  "port": 8787,
  "path": "/mcp"
}
Examples:Stdio transport:
{
  "ts": "2024-03-15T10:30:46.567Z",
  "level": "info",
  "event": "server.started",
  "msg": "umcp started with stdio transport"
}
HTTP transport:
{
  "ts": "2024-03-15T10:30:46.567Z",
  "level": "info",
  "event": "server.started",
  "msg": "umcp started with streamable-http transport",
  "context": {
    "host": "127.0.0.1",
    "port": 8787,
    "path": "/mcp"
  }
}
Emitted when an HTTP request fails.Level: errorContext:
{
  "message": "Connection timeout"
}
Example:
{
  "ts": "2024-03-15T10:35:20.123Z",
  "level": "error",
  "event": "server.http_error",
  "msg": "HTTP transport request failed",
  "context": {
    "message": "Connection timeout"
  }
}
Emitted when a shutdown signal (SIGINT, SIGTERM) is received.Level: infoContext:
{
  "reason": "SIGTERM"
}
Example:
{
  "ts": "2024-03-15T10:40:00.123Z",
  "level": "info",
  "event": "shutdown.signal",
  "msg": "Shutdown signal received",
  "context": {
    "reason": "SIGTERM"
  }
}

Dry-Run Events

Emitted when dry-run mode completes successfully.Level: infoContext:
{
  "count": 12
}
Example:
{
  "ts": "2024-03-15T10:30:50.123Z",
  "level": "info",
  "event": "dry_run.complete",
  "msg": "Dry-run completed",
  "context": {
    "count": 12
  }
}

CLI Events

Emitted when a CLI command fails.Level: errorContext:
{
  "message": "Config validation failed for /path/to/umcp.jsonc: categories.web_search.providers[0].command: provider.command is required when transport is stdio"
}
Example:
{
  "ts": "2024-03-15T10:30:45.789Z",
  "level": "error",
  "event": "cli.failed",
  "msg": "umcp command failed",
  "context": {
    "message": "Config validation failed"
  }
}

Debugging Tips

Capture Logs to File

Since logs are written to stderr, you can capture them separately:
umcp serve 2> umcp.log

Filter by Event Type

Use jq to filter specific events:
# Show only error events
cat umcp.log | jq 'select(.level == "error")'

# Show provider connection events
cat umcp.log | jq 'select(.event | startswith("provider."))'

# Show tool-related events
cat umcp.log | jq 'select(.event | startswith("tool."))'

Pretty-Print Logs

Use jq for human-readable output:
cat umcp.log | jq '.'

Monitor Logs in Real-Time

Tail and pretty-print logs:
tail -f umcp.log | jq '.'

Extract Context Information

Get specific context fields:
# List all provider IDs
cat umcp.log | jq -r 'select(.context.providerId) | .context.providerId' | sort -u

# Count tools by provider
cat umcp.log | jq -r 'select(.event == "tool.registered") | .context.providerId' | sort | uniq -c

# Show all final tool names
cat umcp.log | jq -r 'select(.event == "tool.registered") | .context.finalName'

Secret Masking

When logging environment variable values (like in env.rotated), UMCP masks secrets:
  • If the value is ≤ 4 characters: ***
  • If the value is > 4 characters: XX***YY (first 2 and last 2 chars shown)
This ensures sensitive API keys are not exposed in logs.

Build docs developers (and LLMs) love