ExMCP.Telemetry (ex_mcp v1.0.0-rc.0)

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
    • Metadata: %{method: String.t()}
  • [:ex_mcp, :server, :request, :completed] - Response sent back
    • 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: :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: :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: :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)