Metrics Tools
The MCP server provides tools for querying historical metrics collected by the pgEdge AI DBA Workbench collector. These tools access the datastore database, which contains time-series metrics from all monitored PostgreSQL servers.
Database Architecture
The AI DBA Workbench uses a two-tier database architecture:
- The datastore database contains metrics collected by
the collector over time; the
list_probes,describe_probe, andquery_metricstools query this database. - The monitored databases are live PostgreSQL servers
being monitored; the
query_database,get_schema_info, andexecute_explaintools access these databases.
Available Tools
list_probes
The list_probes tool lists all available metrics
probes in the datastore.
Parameters: None
Returns: A TSV table with the following columns:
namecontains the probe name for use withdescribe_probeandquery_metrics.descriptioncontains a human-readable description.row_countcontains the approximate number of metric rows collected.scopeindicates "server" for server-wide metrics or "database" for per-database metrics.
In the following example, the tool lists all probes:
{
"tool": "list_probes",
"arguments": {}
}
describe_probe
The describe_probe tool returns detailed information
about a specific metrics probe including all available
columns and their data types.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
probe_name |
string | Yes | The name of the probe (from list_probes). |
Returns: A TSV table with the following columns:
column_namecontains the name of the column.data_typecontains the PostgreSQL data type.descriptioncontains a human-readable description.column_typeindicates "metric" for numeric values or "dimension" for identifiers.
In the following example, the tool describes the
pg_stat_database probe:
{
"tool": "describe_probe",
"arguments": {
"probe_name": "pg_stat_database"
}
}
query_metrics
The query_metrics tool queries collected metrics with
time-based aggregation into buckets.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
probe_name |
string | Yes | - | The name of the probe. |
connection_id |
integer | Yes | - | The ID of the monitored connection. |
time_start |
string | No | "1h" | The start time (ISO 8601 or relative: "1h", "24h", "7d"). |
time_end |
string | No | "now" | The end time (ISO 8601 or "now"). |
buckets |
integer | No | 150 | The number of time buckets (1-500). |
metrics |
string | No | all | A comma-separated list of metric columns. |
database_name |
string | No | - | A filter by database name. |
schema_name |
string | No | - | A filter by schema name. |
table_name |
string | No | - | A filter by table name. |
aggregation |
string | No | "avg" | The aggregation method: avg, sum, min, max, last. |
Returns: A TSV table with the following columns:
bucket_timecontains the start time of each bucket.- One column per requested metric contains the aggregated values.
In the following example, the tool queries database statistics for the last 24 hours:
{
"tool": "query_metrics",
"arguments": {
"probe_name": "pg_stat_database",
"connection_id": 1,
"time_start": "24h",
"metrics": "numbackends,xact_commit,xact_rollback",
"buckets": 100
}
}
Derived Metrics
Most PostgreSQL statistics columns are cumulative
counters that only ever rise, which makes the raw
values hard to read on a chart. The metrics query
endpoint of the REST API,
GET /api/v1/metrics/query, therefore accepts five
derived names in its metrics parameter in addition
to the columns the probe collects. The query_metrics
MCP tool accepts the probe's own columns only, and
rejects the derived names described here.
The following table describes the derived metrics:
| Name | Unit | Description |
|---|---|---|
<column>_per_sec |
/s |
The per-second rate of change of a cumulative counter column, computed from consecutive samples. |
<column>_delta |
ms or empty |
The increase in a counter or time counter column within each time bucket, summed across the samples in the bucket. |
<column>_pct |
% |
The share of wall-clock time that a cumulative millisecond column advanced by, such as blk_read_time_pct on pg_stat_database. |
<column>_sessions |
sessions |
The average number of sessions in a state over the interval, from session_time, active_time or idle_in_transaction_time on pg_stat_database. |
dead_tuple_ratio |
% |
The percentage of tuples that are dead, from n_live_tup and n_dead_tup. |
The dead_tuple_ratio metric requires a probe that
collects both n_live_tup and n_dead_tup, such as
pg_stat_all_tables.
Column Kinds
Each derived form applies only to columns of a particular kind, and the server keeps a registry of the kind of every counter-like column in the probes it collects. The rules are:
_per_secapplies only to a cumulative event or byte counter, such asxact_commitorwal_bytes._deltaapplies to a cumulative counter, to a cumulative time counter, which stores elapsed milliseconds asblk_read_timedoes, and to the session time columns ofpg_stat_database; the unit ismsfor either time kind and empty for a counter._pctapplies only to a cumulative time counter._sessionsapplies only to thesession_time,active_timeandidle_in_transaction_timecolumns ofpg_stat_database.
A column that the registry does not list is a gauge.
A gauge such as n_dead_tup, a lifetime watermark
such as mean_exec_time and a ratio such as a CPU
percentage cannot be differenced meaningfully, so a
request for _per_sec on one of them fails with HTTP
status 400 rather than returning a misleading series.
The error message names the metric, the probe, the
base column and the kind the registry records for it:
metric "n_dead_tup_per_sec" not supported for probe "pg_stat_all_tables": "n_dead_tup" is a gauge, not a cumulative counter
A _per_sec request on a time counter is refused in
the same way, because a millisecond total divided by
seconds is a dimensionless number that only looks like
a rate; the message adds a hint naming the _pct or
_sessions form to request instead.
A _pct value can exceed 100 on a probe that stores
one row per monitored entity in each sample, because
the shares of all entities are added together: five
databases that each spent half the interval reading
blocks give a blk_read_time_pct of 250.
Response Shape
The endpoint returns a JSON array with one object per
requested metric. Each object carries the requested
name, the base metric column, a unit and a
data array with one point per time bucket; a point
holds the bucket time and a value, which is either
a number or null. The unit is /s, ms, % or
sessions for a derived metric, and empty for a raw
column or a counter delta.
In the following example, the response reports one rate series with a gap in the second bucket:
[
{
"name": "xact_commit_per_sec",
"metric": "xact_commit",
"unit": "/s",
"data": [
{"time": "2026-09-14T10:00:00Z", "value": 42.5},
{"time": "2026-09-14T10:05:00Z", "value": null},
{"time": "2026-09-14T10:10:00Z", "value": 40.1}
]
}
]
Every series in a response contains every bucket, so
all series share the same length and bucket times. A
connection with no samples in the window returns a
series of null values rather than an empty array.
Per-Entity Differencing
For a probe that stores one row per monitored entity in each sample, such as one row per network interface or per database, every derived form computes the change of each entity's counter separately and then adds the changes together. An entity that disappears between samples does not produce a negative change, and an entity that appears does not contribute its existing total.
The pg_sys_network_info probe leaves the loopback
interfaces lo and lo0 out of every query, raw or
derived, because traffic on them is not network
traffic; the exclusion also applies to the
query_metrics MCP tool.
Statistics Resets
A counter falls only when the statistics are reset
rather than because work was undone, so a negative
change between two samples is discarded rather than
charted. Where a probe stores the stats_reset
timestamp of the view it reads, the server also
compares the marker of consecutive samples, so a reset
is detected even when the counter has already climbed
past its previous value. The following probes carry a
marker:
pg_stat_database,pg_stat_io,pg_stat_recovery_prefetch,pg_replication_slotsandpg_stat_subscriptionstorestats_reset.pg_stat_walstoresstats_reset, andarchiver_stats_resetforarchived_countandfailed_count.pg_stat_checkpointerstoresstats_reset, andbgwriter_stats_resetforbuffers_clean,maxwritten_cleanandbuffers_alloc.pg_stat_statementsstoresstats_resetfrompg_stat_statements_info, a view that the extension provides from version 1.9, shipped with PostgreSQL 14; the collector recordsnullwhere the view is absent, and the column is added by collector schema migration 10.
An interval whose marker changed reports null for
_per_sec, _pct and _sessions, and contributes
zero to _delta. A probe without a marker relies on
the negative-change rule alone.
Collection Gaps
Each probe runs on a collection interval, and the
server resolves the interval for a request from the
collector's probe_configs table: the server-scope
row for the connection, then the global row, then the
collector's default of 300 seconds. When a request
spans several connections, the largest interval
applies, because one bucket width serves every series
in the response.
The configured interval describes how the collector runs now, whilst the stored samples were collected under whatever interval was configured at the time, so the interval used to judge them is widened to the spacing the samples in the window actually show when that spacing is wider. Tightening a probe's interval therefore leaves the history collected at the old, wider one intact rather than reading all of it as gaps.
An interval between two consecutive samples longer
than three times that interval is a gap in collection
rather than a measurement, and it reports null for
every derived form, _delta included. A bucket whose
only samples fall in such a gap is null rather than
zero. A bucket with no samples of its own reports zero
for _delta when an accepted sample interval spans
it, because the events in it are counted by the next
sample's delta; where no accepted interval spans the
bucket, nothing counts them anywhere and the bucket is
null, so a collection outage breaks a _delta
series exactly as it breaks a _per_sec one. Buckets
before a probe's first sample and after its last are
null for the same reason.
The first sample in the window is compared with the most recent sample before the window, provided that sample is no older than the larger of three bucket widths and 30 minutes before the window starts. The gap rule still applies to the borrowed sample, so a long outage before the window does not appear as a spike in the first bucket.
Buckets and Missing Values
The requested buckets count is clamped so that no
bucket is narrower than the collection interval; a
narrower bucket cannot hold a sample of its own and
would only alternate between zero and repeated values.
A one-hour window on a probe collected every 300
seconds therefore yields twelve buckets however many
were requested; the series holds 13 points, because
the last point marks the end of the window.
A bucket with no sample is filled according to the kind of metric:
- a raw column and
dead_tuple_ratiorepeat the last observed value for at most three collection intervals, after which the bucket isnull. _per_sec,_delta,_pctand_sessionsnever repeat a value; a bucket without a valid interval isnull, except that_deltareports zero for a bucket an accepted interval spans, as described above.
The bucket value of a _per_sec, _pct or
_sessions metric follows the aggregation
parameter, and avg gives the most representative
result. A _delta answers "how many events happened
during this bucket", which suits a bar chart of rare
events such as checkpoints, and the aggregation
parameter does not apply to it.
Requesting Derived Metrics
In the following example, the request returns the commit and rollback rates for the last six hours:
curl -H "Authorization: Bearer $TOKEN" \
"https://workbench.example.com/api/v1/metrics/query?probe_name=pg_stat_database&connection_id=1&time_range=6h&buckets=72&aggregation=avg&metrics=xact_commit_per_sec,xact_rollback_per_sec"
A request for a derived metric whose base column the probe does not collect, or whose kind does not suit the derived form, fails rather than returning empty data. The endpoint answers with HTTP status 400 and an error message that names the metric and the probe, and the dashboards display that message in the affected chart panel rather than an empty chart.
Common Probes
Server-Wide Probes
| Probe | Description |
|---|---|
pg_stat_activity |
Current database connections and queries. |
pg_stat_replication |
Streaming replication and WAL receiver status. |
pg_stat_wal |
WAL activity and archiver statistics. |
pg_settings |
PostgreSQL configuration settings. |
pg_stat_checkpointer |
Checkpoint and background writer statistics. |
pg_stat_io |
I/O and SLRU cache statistics. |
pg_stat_connection_security |
SSL and GSSAPI connection security. |
Database-Scoped Probes
| Probe | Description |
|---|---|
pg_stat_database |
Per-database statistics. |
pg_stat_database_conflicts |
Replication conflicts. |
pg_stat_user_tables |
Per-table statistics. |
pg_stat_user_indexes |
Per-index statistics. |
pg_stat_statements |
Query execution statistics. |
System Probes
| Probe | Description |
|---|---|
pg_sys_cpu_info |
System CPU usage. |
pg_sys_memory_info |
System memory usage. |
pg_sys_disk_info |
Disk usage statistics. |
pg_sys_network_info |
Network I/O statistics. |
pg_sys_load_avg_info |
System load averages. |
Use Cases
Performance Analysis
In the following example, the tool identifies performance trends over seven days:
{
"tool": "query_metrics",
"arguments": {
"probe_name": "pg_stat_database",
"connection_id": 1,
"time_start": "7d",
"metrics": "xact_commit,tup_returned,tup_fetched",
"aggregation": "sum",
"buckets": 168
}
}
Query Statistics
In the following example, the tool analyzes slow
queries from pg_stat_statements:
{
"tool": "query_metrics",
"arguments": {
"probe_name": "pg_stat_statements",
"connection_id": 1,
"time_start": "24h",
"metrics": "total_exec_time,calls,mean_exec_time",
"buckets": 48
}
}
System Resource Monitoring
In the following example, the tool tracks system resource usage over six hours:
{
"tool": "query_metrics",
"arguments": {
"probe_name": "pg_sys_cpu_info",
"connection_id": 1,
"time_start": "6h",
"buckets": 72
}
}
Best Practices
Follow these guidelines when querying metrics:
- Start with
list_probesto discover what metrics are collected before querying. - Use
describe_probeto understand available columns before constructing queries. - Limit metrics by specifying only the columns you need to reduce response size.
- Choose an appropriate bucket count; use 50 to 150 buckets for an overview and fewer for quick checks.
- Select time ranges carefully; start with shorter ranges (1h, 6h) and expand as needed.
-
Choose the right aggregation method:
-
avgis best for rates and averages over time. sumis best for cumulative metrics like transaction counts.maxis best for peak values like connection counts.minis best for minimum thresholds.-
lastis best for point-in-time values. -
Request a derived metric rather than a raw counter column when charting activity over time through the REST API;
_per_secsuits a line chart and_deltasuits a bar chart of rare events.
Configuration
The datastore tools are enabled by default. The tools can be disabled in the server configuration.
In the following example, the configuration disables the datastore tools:
builtins:
tools:
list_probes: false
describe_probe: false
query_metrics: false
The tools require the server to be configured with a datastore connection. For configuration details, see Server Configuration.
Related Documentation
- Server Information describes the server details endpoint.
- Probes covers probe management and configuration.