ExMCP.Telemetry (ex_mcp v0.9.2)

View Source

Telemetry integration for ExMCP.

This module defines telemetry events emitted by ExMCP components and provides convenience functions for attaching handlers.

Events

All events follow the naming convention [:ex_mcp, :component, :action, :status].

Client Events

Connection

  • [:ex_mcp, :client, :connected] - Client connection established
    • Metadata: %{transport: module()}
  • [:ex_mcp, :client, :disconnected] - Client disconnected
    • Metadata: %{}

Request Lifecycle

  • [:ex_mcp, :client, :request, :sent] - Request sent to server
    • Metadata: %{method: String.t()}
  • [:ex_mcp, :client, :request, :completed] - Response matched to pending request
    • Metadata: %{method: String.t() | nil, request_id: integer()}

Receiver

  • [:ex_mcp, :client, :receiver, :started] - Receiver task started
    • Metadata: %{mode: :push | :pull}

  • [:ex_mcp, :client, :receiver, :message] - Message received in receive loop
    • Metadata: %{}

State & Progress (via span/3)

  • [:ex_mcp, :request, :start] - Request processing starts
    • Measurements: %{system_time: integer()}
    • Metadata: %{request_id: String.t(), method: String.t(), server: atom()}
  • [:ex_mcp, :request, :stop] - Request processing completes
    • Measurements: %{duration: integer()}
    • Metadata: %{request_id: String.t(), method: String.t(), server: atom(), status: :ok | :error}

  • [:ex_mcp, :request, :exception] - Request processing fails
    • Measurements: %{duration: integer()}
    • Metadata: %{request_id: String.t(), method: String.t(), server: atom(), kind: atom(), error: term(), stacktrace: list()}

Server Events

Request Processing

  • [:ex_mcp, :server, :request, :received] - Transport message arrives (Legacy server)
    • Metadata: %{method: String.t()}
  • [:ex_mcp, :server, :request, :completed] - Response sent back (Legacy server)
    • Metadata: %{method: String.t()}
  • [:ex_mcp, :server, :request, :processed] - MessageProcessor.process/2 completes
    • Metadata: %{method: String.t(), has_response: boolean()}
  • [:ex_mcp, :server, :initialize, :completed] - Server initialization completes
    • Metadata: %{server_name: String.t()}

Tool Execution

  • [:ex_mcp, :server, :tool, :called] - Tool call dispatched
    • Metadata: %{tool_name: String.t(), mode: :direct | :genserver | :handler}

  • [:ex_mcp, :tool, :start] - Tool execution starts (via span/3)
    • Measurements: %{system_time: integer()}
    • Metadata: %{tool_name: String.t(), request_id: String.t()}
  • [:ex_mcp, :tool, :stop] - Tool execution completes (via span/3)
    • Measurements: %{duration: integer()}
    • Metadata: %{tool_name: String.t(), request_id: String.t(), status: :ok | :error}

Resource Operations

  • [:ex_mcp, :server, :resource, :read] - Resource read dispatched
    • Metadata: %{uri: String.t(), mode: :direct | :genserver | :handler}

  • [:ex_mcp, :resource, :read, :start] - Resource read starts (via span/3)
    • Measurements: %{system_time: integer()}
    • Metadata: %{uri: String.t(), request_id: String.t()}
  • [:ex_mcp, :resource, :read, :stop] - Resource read completes (via span/3)
    • Measurements: %{duration: integer(), bytes: integer() | nil}

    • Metadata: %{uri: String.t(), request_id: String.t(), status: :ok | :error}

Prompt Rendering

  • [:ex_mcp, :server, :prompt, :rendered] - Prompt get dispatched
    • Metadata: %{name: String.t(), mode: :direct | :genserver | :handler}

HTTP Transport

  • [:ex_mcp, :server, :http, :request] - HTTP request received
    • Metadata: %{method: String.t(), path: String.t()}
  • [:ex_mcp, :server, :http, :response] - HTTP response sent
    • Metadata: %{status: integer()}

Transport Events (via span/3)

  • [:ex_mcp, :connection, :established] - Connection established
    • Measurements: %{system_time: integer()}
    • Metadata: %{transport: atom(), server: atom()}
  • [:ex_mcp, :connection, :lost] - Connection lost
    • Measurements: %{system_time: integer(), uptime: integer()}
    • Metadata: %{transport: atom(), server: atom(), reason: term()}

Authorization Events

Authorization events are emitted by the OAuth 2.1 subsystem:

  • [:ex_mcp, :authorization, :flow, :start | :stop] - OAuth flow lifecycle

  • [:ex_mcp, :authorization, :discovery, :start | :stop] - Metadata discovery

  • [:ex_mcp, :authorization, :token, :start | :stop] - Token exchange

  • [:ex_mcp, :authorization, :authorize, :start | :stop] - Authorization request

ACP Events

Agent Communication Protocol events:

  • [:ex_mcp, :acp, :session, :start | :stop] - ACP session lifecycle

  • [:ex_mcp, :acp, :prompt, :start | :stop] - ACP prompt processing

  • [:ex_mcp, :acp, :transport, :start | :stop] - ACP transport operations

Usage

# Attach a simple logger
ExMCP.Telemetry.attach_default_logger()

# Attach custom handlers
:telemetry.attach(
  "my-handler",
  [:ex_mcp, :server, :tool, :called],
  &MyApp.handle_event/4,
  nil
)

Summary

Functions

Attaches a default logger that logs all ExMCP events.

Detaches the default logger.

Executes a function and emits telemetry events.

Functions

attach_default_logger()

Attaches a default logger that logs all ExMCP events.

This is useful for debugging and development.

detach_default_logger()

Detaches the default logger.

span(event_prefix, metadata, fun)

Executes a function and emits telemetry events.

This is a convenience function for wrapping operations with telemetry.

Examples

ExMCP.Telemetry.span([:ex_mcp, :custom, :operation], %{id: "123"}, fn ->
  # Do some work
  {:ok, result}
end)