Purpose
-------
Optional Prometheus instrumentation for the ``oct-mcp`` server (Phase 5C).
Exposes five metrics:
* ``oct_mcp_tool_calls_total`` - Counter; labels ``tool``, ``decision``.
* ``oct_mcp_tool_duration_seconds`` - Histogram; label ``tool``.
* ``oct_mcp_rate_limit_hits_total`` - Counter; label ``tool``.
* ``oct_mcp_redactions_total`` - Counter; label ``tool``.
* ``oct_mcp_validation_failures_total`` - Counter; label ``tool``.
All public methods are **no-ops** when ``prometheus_client`` is not
installed, so the rest of the pipeline never needs to guard with
``if metrics is not None``.
Responsibilities
----------------
- Define the five metric objects above against a supplied or default
``CollectorRegistry``.
- Record one observation per tool call, rate-limit hit, redaction and
validation failure.
- Start the Prometheus HTTP exporter on demand, in a daemon thread.
- Degrade to a no-op stub when ``prometheus_client`` is not installed,
and never raise for the caller.
Enabling metrics
----------------
Install the optional extra::
pip install 'oct[metrics]'
Then start the server with ``--metrics-port``::
python -m oct.mcp --project-root /path/to/project --metrics-port 9090
Diagnostics
-----------
Domain: MCP
Levels:
L1 - errors: metrics server startup failures
L2 - lifecycle: metrics server started
L3 - (none): this module emits startup failures and lifecycle only
L4 - (none): no deep trace is emitted
Contracts
---------
- This module never raises for the caller - all methods swallow errors
and degrade gracefully.
- When ``prometheus_client`` is absent, ``PROMETHEUS_AVAILABLE`` is
``False`` and :class:`McpMetrics` is a lightweight no-op stub.
- The metrics HTTP server is started in a **daemon** background thread
so it does not prevent process exit.
- :class:`McpMetrics` accepts an optional ``registry`` parameter
(``CollectorRegistry``). The default is the module-level
``prometheus_client.REGISTRY`` so production ``/metrics`` works
without any caller change. Tests and multi-tenant callers pass a
private ``CollectorRegistry()`` to avoid ``Duplicated timeseries``
collisions across instances.
Dependencies
------------
- oct.core.diagnostics (_dbg structured logger)
- prometheus_client (Counter, Histogram, CollectorRegistry and the /metrics HTTP exporter, optional)