MCP Explained: Build a Natural-Language PostgreSQL Assistant for Claude
MCP explained through a project: how Model Context Protocol turns N x M integrations into N + M, then a read-only PostgreSQL server Claude Desktop can query.
This article is published in English.
Sources: MCP Explained: The Protocol That Finally Gives AI a Standard Way to Talk to the World
The glue-code problem
At some point every developer building AI products hits the same wall. The model is capable, and there's useful data sitting in a database, a file system or some API. What's missing is a clean, standard way to join the two.
So the glue code starts: an adapter here, a brittle wrapper there. It holds up until the model changes, or a second data source appears, or a new teammate has to reverse-engineer the whole plumbing layer.
Anthropic's answer arrived in November 2024 as the Model Context Protocol (MCP). Within roughly two years it moved from a niche experiment to mainstream agent infrastructure: 97 million SDK downloads a month by mid-2026 and nearly 10,000 active public servers in the official registry.
Consider a product where users ask questions about company data in plain English. The idea is simple, and the build is painful. The system must interpret the question, turn it into a database query, run it, hand the rows back to the model and format a reply. Multiply that by every tool the AI should touch, such as a calendar, a GitHub repo or a Slack workspace, and each becomes a one-off integration with its own API shapes, auth flows and error formats.
Some engineers label the result "spaghetti integration." Count the models as N and the tools as M, and every pairing demands its own code. Bring in one more model and every tool needs rework, while one more tool means updating each model's integration.
What MCP is
MCP is an open standard, layered on JSON-RPC 2.0, that gives models one shared vocabulary for reaching external tools and data. The N × M mess shrinks to N + M, since a single server serves any client that follows the protocol.
The favorite analogy is USB-C. Chargers used to differ across laptops, phones, cameras and headphones, each wanting its own cable. USB-C changed the connector and not the electricity, and now one cable covers the lot. MCP plays that role for AI: a model that speaks the protocol can reach any tool that exposes it, so a server written once works with every compatible model.
Governance made 2026 a turning point. Anthropic gave MCP to the Agentic AI Foundation in December 2025, a directed fund inside the Linux Foundation that Anthropic, Block and OpenAI co-founded, which signaled that the protocol was no longer Anthropic's alone. Public backing followed: OpenAI's CEO endorsed it and announced support across the agents SDK and ChatGPT, and Google DeepMind confirmed within days that Gemini would use MCP for tools. The protocol is now the default route from models to outside tools, data and services.
The three parts
The host is the application the user actually touches: Claude Desktop, a VS Code extension or an in-house chatbot.
The MCP client sits inside the host. It keeps connections open to one or more MCP servers and routes messages between the model and those servers, so when the model decides to call a tool, the client carries the request.
The MCP server is where the capability lives. Each server wraps a tool or data source behind a standard interface and can offer three kinds of things:
- Tools are callable functions such as
run_sql_query,search_weborsend_email. The model uses them to take an action or fetch data. - Resources are readable data, like files, database rows or documents, that the model can pull into its context window.
- Prompts are reusable prompt templates a server hands to the host.
Messages are encoded as JSON-RPC 2.0 and travel over one of two transports. stdio fits a server running as a local process on the client's machine. Streamable HTTP targets remote servers and cloud deployments, using ordinary HTTP requests and optionally Server-Sent Events (SSE) for streamed responses and server-to-client notifications. Because of that split, one protocol covers both local development and production-scale systems.
One question, step by step
Here's what happens between a user typing a question and getting an answer.
- A user asks, "How many orders did we get last week?"
- The model sees the question next to the list of available tools, fetched from the MCP server at startup.
- It decides
query_databaseis needed and emits a structured tool call. - The MCP client catches the call and sends it to the right server.
- The server runs the query and returns the result as JSON.
- The model reads that result and writes a plain-language answer.
The model never touches the database itself, and it doesn't need the connection string up front, because the MCP server handles all of that.
The project: a chat interface for PostgreSQL
Theory aside, here's something concrete. A team keeps its order data in PostgreSQL, and non-technical colleagues keep asking the one person who knows SQL for reports. The goal is a chat interface where they ask in plain English and get live answers with no SQL written.
The plan is an MCP server in front of the PostgreSQL database, offering a single query_database tool and hooked up to Claude. Anybody on the team can then ask a question in everyday language and get real data back.
Setup
Start with a new project folder and install the dependencies.
mkdir nl-db-assistant
cd nl-db-assistant
npm init -y
npm install @modelcontextprotocol/sdk pg dotenv zod
The packages split the work. @modelcontextprotocol/sdk is Anthropic's official JavaScript and TypeScript kit for MCP servers, pg is the usual Node.js PostgreSQL client, and dotenv stops database credentials from landing in source code. zod checks schemas at runtime, and MCP relies on it to produce the JSON schema through which the model learns how to call the tool.
Create a .env file at the project root holding the connection string.
DATABASE_URL=postgresql://your_user:your_password@localhost:5432/your_database
Add .env to .gitignore right away, since credentials must never be committed.
The server skeleton
Next comes server.js, which begins with the imports, the server's name and version, and a database pool.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import pg from "pg";
import dotenv from "dotenv";
dotenv.config();
// Set up the PostgreSQL connection pool
const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL,
});
// Create the MCP server instance
const server = new McpServer({
name: "postgres-assistant",
version: "1.0.0",
});
The name and version are advertised to a client during the capability-negotiation handshake when it connects. The connection pool is created once and reused for every query, which is far more efficient than opening a new connection each time.
Exposing the schema
For the model to write useful queries, it must know which tables and columns exist. The live schema is exposed as a readable MCP resource it can pull into context.
server.resource(
"database-schema",
"schema://main",
async (uri) => {
const result = await pool.query(`
SELECT
table_name,
column_name,
data_type
FROM information_schema.columns
WHERE table_schema = 'public'
ORDER BY table_name, ordinal_position
`);
const schema = result.rows.reduce((acc, row) => {
if (!acc[row.table_name]) acc[row.table_name] = [];
acc[row.table_name].push(`${row.column_name} (${row.data_type})`);
return acc;
}, {});
const schemaText = Object.entries(schema)
.map(([table, cols]) => `${table}:\n ${cols.join("\n ")}`)
.join("\n\n");
return {
contents: [{
uri: uri.href,
text: schemaText,
}],
};
}
);
information_schema.columns is a built-in PostgreSQL view describing every table and column. The code queries it, reshapes the rows into readable text and returns that as a resource. The model reads it before deciding how to write a query, so it has the full schema and doesn't guess at valid SQL.
The query tool
This is the heart of the server, the tool the model calls to run SQL against the database.
server.tool(
"query_database",
"Run a read-only SQL query against the PostgreSQL database. Use this to answer questions about data, counts, trends, or summaries.",
{
sql: z.string().describe(
"A valid PostgreSQL SELECT query. Only SELECT statements are allowed."
),
},
async ({ sql }) => {
// Safety gate: only allow SELECT statements
const trimmed = sql.trim().toUpperCase();
if (!trimmed.startsWith("SELECT")) {
return {
content: [{
type: "text",
text: "Error: only SELECT queries are permitted through this interface.",
}],
isError: true,
};
}
try {
const result = await pool.query(sql);
const output = JSON.stringify(result.rows, null, 2);
return {
content: [{
type: "text",
text: `Query returned ${result.rowCount} row(s):\n\n${output}`,
}],
};
} catch (err) {
return {
content: [{
type: "text",
text: `Query failed: ${err.message}`,
}],
isError: true,
};
}
}
);
Pay close attention to the description string, the second argument, because the model reads it when deciding whether and how to use the tool. Write it like instructions for a sharp intern who's never seen the database. Zod's schema for the sql parameter turns automatically into the JSON schema the model sees, which spells out the argument shape. A guard at the top accepts SELECT statements only, so no write gets through deliberately or by mistake.
Starting the server
Finally the server starts on the stdio transport.
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("PostgreSQL MCP server is running");
With stdio, the MCP client launches the script as a child process and talks to it over standard input and output. It's the simplest and most reliable transport for local development. The log line uses console.error deliberately, so it doesn't contaminate the stdout channel MCP uses for protocol messages.
Connecting Claude Desktop
Open the Claude Desktop configuration file. On macOS it lives here:
~/Library/Application Support/Claude/claude_desktop_config.json
On Windows the path is:
%APPDATA%\Claude\claude_desktop_config.json
Add an entry for the new server:
{
"mcpServers": {
"postgres-assistant": {
"command": "node",
"args": ["/absolute/path/to/nl-db-assistant/server.js"]
}
}
}
Restart Claude Desktop, and a small tools icon appears in the interface to confirm that MCP servers are connected and ready.
Trying it out
Everyday phrasing works: how many orders came in during the past week, which product category earned the most revenue this month, or who the five biggest customers by total order value are.
Claude reads the live schema resource, writes suitable SQL, calls query_database, receives the results and turns them into a clean answer. Non-technical colleagues never see a query, only a question and a response.
About 80 lines of JavaScript yield a server that shows any connected model the live PostgreSQL schema, converts natural-language questions into SQL, keeps access read-only and returns structured output a model can summarize. It was written once, and any MCP-compatible model, Claude now or another tomorrow, can use it with no server changes. That's the payoff of a shared protocol.
Adoption numbers
Adoption has been steep. Monthly SDK downloads climbed from 100,000 at launch to 97 million by March 2026, about 970 times higher in 18 months. React's npm package, by contrast, took roughly three years to hit 100 million monthly downloads.
Production use includes Block, Bloomberg, Amazon and hundreds of Fortune 500 firms. Pinterest described its production MCP setup in April 2026: domain-specific servers for Presto, Spark and Airflow, with a human approving sensitive operations. Around 66,000 tool invocations a month from 844 active users reportedly save about 7,000 engineering hours monthly.
Official or community-maintained MCP servers now exist for Slack, GitHub, Google, Salesforce and Stripe, as well as HubSpot, Shopify, Notion, Linear, Sentry, Figma, Webflow, Cloudflare and Postman.
The 2026 roadmap's theme is enterprise readiness. The main items are OAuth 2.1 sign-in tied to enterprise identity providers, multi-agent coordination where one agent is callable like a tool server, and a curated registry of verified servers carrying security ratings.
Common questions
Is MCP merely function calling with extra steps? No, since the two sit at different layers. Function calling is a model choosing to invoke a tool, and MCP is the protocol joining model and tool. Function calling decides whether to call a tool, while MCP handles how to reach it and converse with it. Typical modern setups combine them.
Does it only work with Claude? Not at all, which is one of its best properties. OpenAI, Google DeepMind and Microsoft have adopted MCP, along with thousands of dev teams. A model with an MCP-compatible client can use any MCP server regardless of who built either side.
Is it production-ready in 2026? For most uses, yes, given the large deployments above. For latency-critical systems such as real-time trading or sub-100ms requirements, though, the abstraction has a cost, and direct API connections may beat MCP-mediated ones.
Is it secure enough for enterprise use? It's getting there. The March 2025 spec update added a comprehensive OAuth 2.1-based authorization framework, and the 2026 roadmap targets SAML and OIDC integration with identity providers like Okta and Azure AD. Highly regulated sectors such as finance or healthcare still need extra compliance layers for now.
Who governs it, and could Anthropic change direction? Since December 2025 the protocol sits under the Agentic AI Foundation at the Linux Foundation. That makes it vendor-neutral, community-governed shared infrastructure, comparable to HTTP or TCP/IP, and no single company can unilaterally redirect it.
What separates a tool from a resource? A tool is an action the model chooses to take, such as running a query, sending a message or creating a file. A resource is data it reads passively, such as a file, a schema or a document. Resources are loaded into context, but tools run on demand during the conversation.
Can one server cover several databases or services? Yes. A server may offer as many tools and resources as make sense, and a frequent design is a data-platform server giving read access to several databases, each exposed as a distinct tool or resource.