A PromEx plugin for monitoring ConduitMCP operations.
This plugin captures metrics from the ConduitMCP library's telemetry events:
- Request metrics (all MCP method calls)
- Tool execution metrics
- Resource read metrics
- Prompt retrieval metrics
- HTTP and message rate limit metrics
- Authentication metrics
- Cancellation metrics
- Janitor cleanup metrics
Every event in ConduitMcp.Telemetry.events/0 feeds at least one metric.
Installation
Add :prom_ex to your dependencies:
def deps do
[
{:conduit_mcp, "~> 0.9.0"},
{:prom_ex, "~> 1.11"}
]
endUsage
Add this plugin to your PromEx module's plugins list:
defmodule MyApp.PromEx do
use PromEx, otp_app: :my_app
@impl true
def plugins do
[
PromEx.Plugins.Application,
PromEx.Plugins.Beam,
{ConduitMcp.PromEx, otp_app: :my_app}
]
end
@impl true
def dashboard_assigns do
[
datasource_id: "prometheus",
default_selected_interval: "30s"
]
end
endThen add your PromEx module to your supervision tree:
def start(_type, _args) do
children = [
MyApp.PromEx,
# ... other children ...
]
Supervisor.start_link(children, strategy: :one_for_one)
endConfiguration Options
:otp_app(required) - Your application name:duration_unit(optional) - Unit for duration metrics (default::millisecond)
Metrics Exposed
All metrics are prefixed with {otp_app}_conduit_mcp_.
Request Metrics
{prefix}_request_total- Counter of MCP requestsTags:
method(e.g., "tools/list"),status(:ok | :error)
{prefix}_request_duration_milliseconds- Distribution of request durations- Tags:
method,status - Buckets: [10, 50, 100, 250, 500, 1_000, 2_500, 5_000, 10_000]
- Tags:
Tool Metrics
{prefix}_tool_execution_total- Counter of tool executions- Tags:
tool_name,status
- Tags:
{prefix}_tool_duration_milliseconds- Distribution of tool execution durations- Tags:
tool_name,status - Buckets: [10, 50, 100, 500, 1_000, 5_000, 10_000, 30_000]
- Tags:
Resource Metrics
{prefix}_resource_read_total- Counter of resource reads- Tags:
status
- Tags:
{prefix}_resource_read_duration_milliseconds- Distribution of resource read durations- Tags:
status - Buckets: [10, 50, 100, 500, 1_000, 5_000]
- Tags:
Prompt Metrics
{prefix}_prompt_get_total- Counter of prompt retrievals- Tags:
prompt_name,status
- Tags:
{prefix}_prompt_get_duration_milliseconds- Distribution of prompt retrieval durations- Tags:
prompt_name,status - Buckets: [10, 50, 100, 500, 1_000]
- Tags:
Rate Limit Metrics
{prefix}_rate_limit_check_total- Counter of HTTP rate limit checksTags:
status(:allow | :deny)
{prefix}_rate_limit_check_duration_milliseconds- Distribution of check durations- Tags:
status - Buckets: [1, 5, 10, 25, 50, 100, 250]
- Tags:
Message Rate Limit Metrics
{prefix}_message_rate_limit_check_total- Counter of message rate limit checksTags:
status(:allow | :deny),method(MCP method name)
{prefix}_message_rate_limit_check_duration_milliseconds- Distribution of check durations- Tags:
status,method - Buckets: [1, 5, 10, 25, 50, 100, 250]
- Tags:
Authentication Metrics
{prefix}_auth_verify_total- Counter of authentication attemptsTags:
strategy(:bearer_token | :api_key | :function),status
{prefix}_auth_verify_duration_milliseconds- Distribution of auth verification durations- Tags:
strategy,status - Buckets: [1, 5, 10, 25, 50, 100, 250]
- Tags:
Cancellation Metrics
{prefix}_request_cancelled_total- Counter of recordednotifications/cancelled(no tags: request ids, scopes and reasons are unbounded or client-supplied){prefix}_cancellation_cleanup_removed_total- Sum of expired cancellation rows removed{prefix}_cancellation_cleanup_in_flight_removed_total- Sum of in-flight rows of dead request processes removed{prefix}_cancellation_janitor_runs_total- Counter of cancellation janitor runs (itsremovedduplicates the cleanup sum, so it is counted, not summed)
Cleanup Metrics
{prefix}_session_cleanup_removed_total- Sum of expired sessions evicted- Tags:
store
- Tags:
{prefix}_tasks_cleanup_removed_total- Sum of terminal tasks evicted{prefix}_ext_tasks_cleanup_removed_total- Sum of expired tasks-extension tasks evicted- Tags:
store
- Tags:
MRTR Metrics
{prefix}_mrtr_input_required_total- Counter ofInputRequiredResults sent- Tags:
method(tools/call,resources/read,prompts/get)
- Tags:
{prefix}_mrtr_input_requests_total- Sum of input requests carried by them- Tags:
method
- Tags:
Client Metrics
From ConduitMcp.Client. Tool names and notification methods come from
the server, and client names may be per-tenant, so none of them is a tag.
{prefix}_client_request_start_total,{prefix}_client_request_exception_total- Counters of requests sent and of requests whose caller raised- Tags:
method
- Tags:
{prefix}_client_request_total- Counter of finished requests- Tags:
method,status,error_kind
- Tags:
{prefix}_client_request_duration_milliseconds- Distribution of request durations- Tags:
method,status
- Tags:
{prefix}_client_tool_call_start_total,{prefix}_client_tool_call_exception_total- Counters of tool calls{prefix}_client_tool_call_total- Counter of finished tool calls- Tags:
status,error_kind,is_error
- Tags:
{prefix}_client_tool_call_duration_milliseconds- Distribution of tool call durations- Tags:
status
- Tags:
{prefix}_client_connection_transition_total- Counter of connection state changes- Tags:
to,reason
- Tags:
{prefix}_client_request_cancel_total- Counter of requests the client cancelled- Tags:
reason
- Tags:
{prefix}_client_response_dropped_total- Counter of late responses dropped{prefix}_client_notification_total- Counter of server notifications received{prefix}_client_cache_refresh_start_total- Counter of cache refreshes started- Tags:
kind
- Tags:
{prefix}_client_cache_refresh_total- Counter of finished cache refreshes- Tags:
kind,status,error_kind
- Tags:
{prefix}_client_cache_refresh_duration_milliseconds- Distribution of cache refresh durations- Tags:
kind,status
- Tags:
{prefix}_client_tool_rejected_total- Counter of listed tools dropped at intake- Tags:
reason
- Tags:
{prefix}_client_tool_definition_changed_total- Counter of known tools whose definition changed{prefix}_client_policy_decision_total- Counter of policy decisions on governed calls, reads and gets- Tags:
target,decision,source,reason,approval
- Tags:
{prefix}_client_hook_total- Counter of hook runs that returned within their contract- Tags:
kind,status
- Tags:
{prefix}_client_hook_duration_milliseconds- Distribution of hook durations- Tags:
kind
- Tags:
{prefix}_client_hook_exception_total- Counter of failed hook runs- Tags:
kind,reason
- Tags:
{prefix}_client_hub_server_failed_total- Counter of hub server clients that exited- Tags:
reason
- Tags:
{prefix}_client_hub_server_restarted_total- Counter of hub servers started again after a failure{prefix}_client_hub_collision_total- Counter of exposed tool names left out because several tools mangled to them{prefix}_client_adapter_strict_fallback_total- Counter of OpenAI tools sent non-strict because their schema has no strict form- Tags:
api,reason
- Tags:
{prefix}_client_oauth_{op}_start_total,{prefix}_client_oauth_{op}_total,{prefix}_client_oauth_{op}_exception_total- Counters of the client's OAuth operations,opone ofdiscovery,registration,token_exchange,refresh,step_up- Tags (
_totalonly):status,reason
- Tags (
{prefix}_client_oauth_authorization_required_total- Counter of times the user had to authorize- Tags:
reason
- Tags:
Subscription Metrics
{prefix}_subscription_start_total- Counter ofsubscriptions/listenstreams opened{prefix}_subscription_stop_total- Counter of streams ended- Tags:
reason(client_closed,max_lifetime,token_expired,idle_timeout)
- Tags:
{prefix}_subscription_duration_milliseconds- Distribution of how long streams stayed open- Tags:
reason - Buckets: [1 000, 10 000, 60 000, 300 000, 900 000, 1 800 000, 3 600 000]
- Tags:
{prefix}_subscription_drop_total- Counter of notifications skipped for a stream whose mailbox reachedConduitMcp.Subscriptions.max_queue/0- Tags:
topic(tools,prompts,resources,resource,task)
- Tags:
PromQL Examples
Request Rate by Method
rate({otp_app}_conduit_mcp_request_total[5m])Error Rate Percentage
100 * (
rate({otp_app}_conduit_mcp_request_total{status="error"}[5m])
/
rate({otp_app}_conduit_mcp_request_total[5m])
)P95 Request Duration
histogram_quantile(0.95,
rate({otp_app}_conduit_mcp_request_duration_milliseconds_bucket[5m])
)Slow Tool Executions (>5s)
histogram_quantile(0.95,
rate({otp_app}_conduit_mcp_tool_duration_milliseconds_bucket[5m])
) > 5000Authentication Success Rate
100 * (
rate({otp_app}_conduit_mcp_auth_verify_total{status="ok"}[5m])
/
rate({otp_app}_conduit_mcp_auth_verify_total[5m])
)Alert Examples
High Error Rate
- alert: ConduitMcpHighErrorRate
expr: |
100 * (
rate(myapp_conduit_mcp_request_total{status="error"}[5m])
/
rate(myapp_conduit_mcp_request_total[5m])
) > 5
for: 5m
annotations:
summary: "High error rate in ConduitMCP ({{ $value }}%)"Slow Tool Executions
- alert: ConduitMcpSlowTools
expr: |
histogram_quantile(0.95,
rate(myapp_conduit_mcp_tool_duration_milliseconds_bucket[5m])
) > 5000
for: 10m
annotations:
summary: "Tool executions are slow (p95: {{ $value }}ms)"Authentication Failures
- alert: ConduitMcpAuthFailures
expr: |
rate(myapp_conduit_mcp_auth_verify_total{status="error"}[5m]) > 0.1
for: 5m
annotations:
summary: "Authentication failures detected"Cardinality Considerations
This plugin is designed to minimize metric cardinality:
- ✅ LOW:
method(limited set of MCP methods) - ✅ LOW:
status(only :ok or :error) - ✅ LOW:
strategy(only 3 auth strategies) - ✅ LOW:
tool_name(user-defined but typically limited) - ✅ LOW:
prompt_name(user-defined but typically limited) - ❌ HIGH:
uriis NOT included (unbounded) - ❌ HIGH:
server_moduleis NOT included (not useful) - ❌ HIGH: the rate limit
key, cancellationrequest_id,scopeandreason, and subscriptionfilters, are NOT included (unbounded, client-supplied, or a list)
All string values are normalized to prevent cardinality explosion.