Skip to main content
Glama
YawLabs

@yawlabs/postgres-mcp

by YawLabs

I/O statistics and in-flight async I/O

pg_io_stats
Read-onlyIdempotent

Identify which PostgreSQL subsystem performs I/O and through which path, using cumulative per-backend-type stats and in-flight async I/O handles to pinpoint stalled or heavy I/O on PostgreSQL 16+.

Instructions

I/O observability: cumulative per-backend-type I/O from pg_stat_io (PostgreSQL 16+), plus in-flight asynchronous I/O handles from pg_aios (PostgreSQL 18+). This is the layer underneath pg_top_queries and pg_health -- it says WHICH subsystem is doing the I/O (client backends vs autovacuum vs checkpointer vs walwriter) and through which path, which a per-query or per-table view cannot.

  • io: one row per (backend_type, io_object, io_context) combination. Counters reads / writes / extends / writebacks / hits / evictions / reuses / fsyncs are bigints returned as decimal strings; read_time_ms / write_time_ms / writeback_time_ms / extend_time_ms / fsync_time_ms are float8 milliseconds. A timing of 0 next to a non-zero op count means track_io_timing is off, NOT that the I/O was free -- turn it on to get real numbers. A NULL counter means the operation is not possible for that combination, which is different from 0.

  • io[].read_bytes / write_bytes / extend_bytes: a normalized byte figure that means the same thing on every supported server. On PG16-17 it is computed as op_bytes * <op count>; on PG18 op_bytes was removed and the server reports bytes directly. The top-level byte_accounting field says which source produced the numbers.

  • io[].stats_reset: these are CUMULATIVE counters, so a row is only interpretable next to its reset point. Reported per row because that is how the view reports it; pg_stat_reset_shared('io') resets them together in practice, but this tool does not assert that.

  • Rows whose counters are all zero are omitted by default (pg_stat_io is mostly zeros on a quiet system, and the noise buries the handful of rows that matter). Pass includeZeroRows: true for the full matrix.

  • in_flight + io_method: PostgreSQL 18+ ONLY, and both keys are ABSENT on older servers rather than empty/null -- an empty in_flight array would read as 'nothing is stalled' when the truth is 'this server cannot tell you'. in_flight is live, currently-outstanding async I/O (pid, io_id, op, state, off, length, target_desc), which is what you want while a stall is happening rather than after it. io_method (worker / io_uring / sync) explains what in_flight can contain: with io_method = sync there is no asynchronous submission, so the array is legitimately empty no matter how much I/O is running. Requires PostgreSQL 16+. Sub-queries that fail (pg_stat_io and pg_aios are permission-gated on some managed providers) append to _warnings and set their field to null; the rest of the response still returns.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
limitNoMax rows per section (default 200). pg_stat_io has well under 200 combinations, so this effectively bounds the in-flight list on a busy PG18 server.
includeZeroRowsNoIf true, return every (backend_type, io_object, io_context) row including the ones with no recorded activity. Default false -- the view is mostly zeros on a quiet system.

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
ioYesNull (not []) when the fetch was refused -- [] is a real answer on a freshly reset cluster.
_warningsNo
in_flightNoPostgreSQL 18+ only. Currently-outstanding async I/O. Null (not []) when the fetch was refused -- reading a denial as 'nothing outstanding' would point the investigation the wrong way.
io_methodNoPostgreSQL 18+ only. worker | io_uring | sync. With `sync` there is no async submission, so `in_flight` is legitimately empty however much I/O is running.
byte_accountingYesWhich source produced read_bytes / write_bytes / extend_bytes: native columns, or op_bytes * ops.
include_zero_rowsYesEchoed because it changes what an empty `io` means: no recorded I/O, vs the view returned nothing.
server_version_numYesEchoed so a caller can tell WHY the PG18-only keys are absent without a second round-trip.

Schema Changelog

Changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. Changed4 schema fields changedv0.12.1
    • changedOutput schema / properties / in_flight / anyOf
      Previous value: -[
      -  {
      -    "items": {
      -      "additionalProperties": false,
      -      "properties": {
      -        "io_id": {
      -          "type": "number"
      -        },
      -        "length": {
      -          "anyOf": [
      -            {
      -              "type": "string"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ]
      -        },
      -        "off": {
      -          "anyOf": [
      -            {
      -              "type": "string"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ],
      -          "description": "File offset, cast to text so a widened column stays lossless."
      -        },
      -        "op": {
      -          "anyOf": [
      -            {
      -              "type": "string"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ]
      -        },
      -        "pid": {
      -          "description": "Line this up against pg_health / pg_inspect_locks output for the same backend.",
      -          "type": "number"
      -        },
      -        "state": {
      -          "anyOf": [
      -            {
      -              "type": "string"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ]
      -        },
      -        "target_desc": {
      -          "anyOf": [
      -            {
      -              "type": "string"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ]
      -        }
      -      },
      -      "required": [
      -        "pid",
      -        "io_id",
      -        "op",
      -        "state",
      -        "off",
      -        "length",
      -        "target_desc"
      -      ],
      -      "type": "object"
      -    },
      -    "type": "array"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]New value: +[
      +  {
      +    "items": {
      +      "additionalProperties": false,
      +      "properties": {
      +        "io_id": {
      +          "type": "number"
      +        },
      +        "length": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "off": {
      +          "description": "File offset, cast to text so a widened column stays lossless.",
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "op": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "pid": {
      +          "description": "Line this up against pg_health / pg_inspect_locks output for the same backend.",
      +          "type": "number"
      +        },
      +        "state": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "target_desc": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        }
      +      },
      +      "required": [
      +        "pid",
      +        "io_id",
      +        "op",
      +        "state",
      +        "off",
      +        "length",
      +        "target_desc"
      +      ],
      +      "type": "object"
      +    },
      +    "type": "array"
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
    • changedOutput schema / properties / io / anyOf
      Previous value: -[
      -  {
      -    "items": {
      -      "additionalProperties": false,
      -      "properties": {
      -        "backend_type": {
      -          "type": "string"
      -        },
      -        "evictions": {
      -          "anyOf": [
      -            {
      -              "type": "string"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ]
      -        },
      -        "extend_bytes": {
      -          "anyOf": [
      -            {
      -              "type": "string"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ]
      -        },
      -        "extend_time_ms": {
      -          "anyOf": [
      -            {
      -              "type": "number"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ]
      -        },
      -        "extends": {
      -          "anyOf": [
      -            {
      -              "type": "string"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ]
      -        },
      -        "fsync_time_ms": {
      -          "anyOf": [
      -            {
      -              "type": "number"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ]
      -        },
      -        "fsyncs": {
      -          "anyOf": [
      -            {
      -              "type": "string"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ]
      -        },
      -        "hits": {
      -          "anyOf": [
      -            {
      -              "type": "string"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ]
      -        },
      -        "io_context": {
      -          "description": "The view's `context` column, renamed for symmetry with io_object.",
      -          "type": "string"
      -        },
      -        "io_object": {
      -          "description": "The view's `object` column, renamed -- it is a postgres keyword.",
      -          "type": "string"
      -        },
      -        "read_bytes": {
      -          "anyOf": [
      -            {
      -              "type": "string"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ],
      -          "description": "Source named by the top-level `byte_accounting`."
      -        },
      -        "read_time_ms": {
      -          "anyOf": [
      -            {
      -              "type": "number"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ],
      -          "description": "0 next to a non-zero op count means track_io_timing is OFF, not that the I/O was free."
      -        },
      -        "reads": {
      -          "anyOf": [
      -            {
      -              "type": "string"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ]
      -        },
      -        "reuses": {
      -          "anyOf": [
      -            {
      -              "type": "string"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ]
      -        },
      -        "stats_reset": {
      -          "anyOf": [
      -            {
      -              "type": "string"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ],
      -          "description": "These counters are cumulative SINCE this point. Null = never reset."
      -        },
      -        "write_bytes": {
      -          "anyOf": [
      -            {
      -              "type": "string"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ]
      -        },
      -        "write_time_ms": {
      -          "anyOf": [
      -            {
      -              "type": "number"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ]
      -        },
      -        "writeback_time_ms": {
      -          "anyOf": [
      -            {
      -              "type": "number"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ]
      -        },
      -        "writebacks": {
      -          "anyOf": [
      -            {
      -              "type": "string"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ]
      -        },
      -        "writes": {
      -          "anyOf": [
      -            {
      -              "type": "string"
      -            },
      -            {
      -              "type": "null"
      -            }
      -          ]
      -        }
      -      },
      -      "required": [
      -        "backend_type",
      -        "io_object",
      -        "io_context",
      -        "reads",
      -        "read_bytes",
      -        "read_time_ms",
      -        "writes",
      -        "write_bytes",
      -        "write_time_ms",
      -        "writebacks",
      -        "writeback_time_ms",
      -        "extends",
      -        "extend_bytes",
      -        "extend_time_ms",
      -        "hits",
      -        "evictions",
      -        "reuses",
      -        "fsyncs",
      -        "fsync_time_ms",
      -        "stats_reset"
      -      ],
      -      "type": "object"
      -    },
      -    "type": "array"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]New value: +[
      +  {
      +    "items": {
      +      "additionalProperties": false,
      +      "properties": {
      +        "backend_type": {
      +          "type": "string"
      +        },
      +        "evictions": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "extend_bytes": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "extend_time_ms": {
      +          "type": [
      +            "number",
      +            "null"
      +          ]
      +        },
      +        "extends": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "fsync_time_ms": {
      +          "type": [
      +            "number",
      +            "null"
      +          ]
      +        },
      +        "fsyncs": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "hits": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "io_context": {
      +          "description": "The view's `context` column, renamed for symmetry with io_object.",
      +          "type": "string"
      +        },
      +        "io_object": {
      +          "description": "The view's `object` column, renamed -- it is a postgres keyword.",
      +          "type": "string"
      +        },
      +        "read_bytes": {
      +          "description": "Source named by the top-level `byte_accounting`.",
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "read_time_ms": {
      +          "description": "0 next to a non-zero op count means track_io_timing is OFF, not that the I/O was free.",
      +          "type": [
      +            "number",
      +            "null"
      +          ]
      +        },
      +        "reads": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "reuses": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "stats_reset": {
      +          "description": "These counters are cumulative SINCE this point. Null = never reset.",
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "write_bytes": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "write_time_ms": {
      +          "type": [
      +            "number",
      +            "null"
      +          ]
      +        },
      +        "writeback_time_ms": {
      +          "type": [
      +            "number",
      +            "null"
      +          ]
      +        },
      +        "writebacks": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "writes": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        }
      +      },
      +      "required": [
      +        "backend_type",
      +        "io_object",
      +        "io_context",
      +        "reads",
      +        "read_bytes",
      +        "read_time_ms",
      +        "writes",
      +        "write_bytes",
      +        "write_time_ms",
      +        "writebacks",
      +        "writeback_time_ms",
      +        "extends",
      +        "extend_bytes",
      +        "extend_time_ms",
      +        "hits",
      +        "evictions",
      +        "reuses",
      +        "fsyncs",
      +        "fsync_time_ms",
      +        "stats_reset"
      +      ],
      +      "type": "object"
      +    },
      +    "type": "array"
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
    • removedOutput schema / properties / io_method / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / properties / io_method / type
      Added value: +[
      +  "string",
      +  "null"
      +]
  2. Addedv0.12.0

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description goes far beyond the readOnlyHint/idempotent annotations, covering cumulative counter semantics, the difference between NULL and 0, the track_io_timing caveat, zero-row omission, version-gated absence of keys, and permission-failure fallback behavior. It also warns that stats_reset governs interpretability. This is exemplary transparency for a read-only observability tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but densely informative, and every paragraph earns its place by explaining behavior that is not inferable from schema or annotations. It is logically organized: primary purpose, counter semantics, byte normalization, zero-row handling, version differences, and failure behavior. No filler is present.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity, the description covers version requirements, permission failures, counter reset caveats, timing limitations, zero-row filtering, and PG18-specific behavior. The output schema exists and parameters are fully documented, so nothing an agent needs to correctly invoke and interpret this tool is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the input schema already describes both parameters well. The description adds extra value by explaining why the limit effectively bounds the in-flight list and why zero rows are omitted by default. It enriches, rather than merely repeats, the schema definitions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a precise statement of what the tool does: cumulative per-backend-type I/O from pg_stat_io plus in-flight async I/O from pg_aios. It explicitly distinguishes itself from pg_top_queries and pg_health by describing it as the subsystem-level layer underneath them, so an agent can tell it apart from sibling observability tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear context for when this tool is appropriate: it identifies the subsystem doing I/O, which per-query or per-table views cannot, and notes that in-flight async I/O is useful during a stall. It names sibling tools but does not explicitly state 'use pg_top_queries instead if you need per-query I/O', so the guidance is strong but slightly implicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/YawLabs/postgres-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server