Class McpMetricsExporter

java.lang.Object
com.mcpdbwizard.pub.McpMetricsExporter
All Implemented Interfaces:
AutoCloseable

public final class McpMetricsExporter extends Object implements AutoCloseable
Serves a McpMetrics registry at /metrics for Prometheus to scrape.
Environment
MCP_METRICS_PORT Port to listen on. Unset means no listener — see below.
MCP_METRICS_HOST Address to bind, default "127.0.0.1".
MCP_METRICS_HOST_MANAGED Set by a supervising runtime that chose the bind address itself, inside a private network namespace. Suppresses the exposure warning only — see below.

Why there is no default port

Generation-time PROMETHEUS_SERVER=YES emits this; the environment variable is what starts it. The same split as MCP_HTTP_TOKEN, and here it is not merely tidy: the web application launches up to twenty generated servers at once and the start-up harness forks twenty-one per box. A default port would give one of them a listener and the other twenty a bind failure to log, every run.

It uses the JDK's own HTTP server, and it is a separate port on purpose

com.sun.net.httpserver costs no dependency and works identically under both MCP transports — a stdio server has no Jetty to hang a servlet on, and stdio is where a locally-run server lives. A separate port also keeps the scrape off /mcp/*, whose bearer-token and Origin filters exist to guard a surface that can write, and which a Prometheus scraper would not satisfy anyway.

Exposure warns rather than refuses

Binding a non-loopback address logs a warning. It does not refuse the way McpHttpPolicy refuses an unauthenticated MCP port: this endpoint is read-only and carries no data from the database. It does publish the schema's object names and the shape of the traffic, which is worth a line in the log and worth a network policy, but it is not the same risk as an open call surface.

Why a container is allowed to bind every interface without being warned at

MCP_METRICS_HOST_MANAGED exists because the warning above is advice an operator cannot act on when a supervising runtime placed this server inside a private network namespace. Binding 0.0.0.0 there is not an exposure: nothing outside can reach the namespace until the deployment publishes the port, and THAT is the deliberate act the warning is really about. Telling twenty child servers' logs to "restrict it at the network" when the namespace already does is how operators learn to scroll past warnings.

It suppresses only the warning, and only when the runtime supplied the address itself. An operator who sets MCP_METRICS_HOST by hand is still told, because then the choice — and the responsibility for a network policy — is genuinely theirs. Nothing here changes what is bound, and the loopback default is untouched for the ordinary case of this class running as a library in somebody's own JVM, where measuring a server must not be what puts it on a network.

A failure here must not take the server down

An unusable port is reported and the MCP server carries on. Metrics are an observability sideline, and refusing to serve tools because nothing could be told about them would be the wrong trade — unlike the fail-closed security guards, where refusing IS the protection. A port that will not parse is different and does throw: that is an operator typo, and silently ignoring it leaves someone believing they are collecting metrics when they are not. Copyright 2003-2026 ATB Consultancy Services Ltd (formerly Orinda Software Ltd, Dublin, Ireland)

  • Field Details

    • PORT_VARIABLE

      public static final String PORT_VARIABLE
      See Also:
    • HOST_VARIABLE

      public static final String HOST_VARIABLE
      See Also:
    • HOST_MANAGED_VARIABLE

      public static final String HOST_MANAGED_VARIABLE
      Set by a supervising runtime that chose HOST_VARIABLE itself, inside a private network namespace. Suppresses the exposure warning; binds nothing differently.
      See Also:
    • DEFAULT_BIND_HOST

      public static final String DEFAULT_BIND_HOST
      Loopback, so a server is not put on the network by the act of measuring it.
      See Also:
    • METRICS_PATH

      public static final String METRICS_PATH
      The scrape path. Anything else is a 404.
      See Also:
    • CONTENT_TYPE

      public static final String CONTENT_TYPE
      Prometheus text exposition format, version 0.0.4.
      See Also:
  • Method Details

    • startIfConfigured

      public static McpMetricsExporter startIfConfigured(McpMetrics theMetrics, LogInterface theLog)
      Start the exporter if PORT_VARIABLE names a port, otherwise do nothing.
      Parameters:
      theMetrics - the registry to serve
      theLog - where a bind failure or an exposure warning is reported; may be null
      Returns:
      the running exporter, or null when no port was configured or the port could not be bound
      Throws:
      IllegalArgumentException - if the port or host setting cannot be understood
    • start

      public static McpMetricsExporter start(McpMetrics theMetrics, String theHost, int thePort) throws IOException
      Start an exporter on a given address, bypassing the environment.
      Parameters:
      thePort - the port, or 0 to let the operating system choose one
      Throws:
      IOException
    • getPort

      public int getPort()
      The port actually bound — the point of the exercise when 0 was requested.
    • close

      public void close()
      Specified by:
      close in interface AutoCloseable