🗄️ 为 PostgreSQL 构建 MCP 服务器:使用 Claude 与任意 AI 客户端查询您的数据库 祝构建愉快
Source: Dev.to
介绍
模型上下文协议(MCP)正在改变 AI 应用与数据源交互的方式。虽然天气服务器和基础示例是很好的起点,但大多数真实世界的应用都需要数据库访问。在本指南中,我们将构建一个面向生产的 PostgreSQL MCP 服务器,使 Claude、VS Code 以及任何 MCP 客户端能够安全、高效地查询你的数据库。
完成后,你将拥有:
- 一个功能完整的 PostgreSQL MCP 服务器
- SELECT 查询、聚合和数据检索的示例
- 一个可以部署到生产环境的公共仓库
- 与 Claude Desktop 和其他 MCP 客户端的集成
这解决了什么问题?
想象一下向 Claude 提问:
“本季度收入最高的前 10 位客户是谁?”
或者:
“显示过去 7 天内所有严重程度为 critical 的事件。”
有了 PostgreSQL MCP 服务器,Claude 可以:
- 直接连接到你的数据库
- 执行安全的预定义查询
- 返回结构化数据
- 实时帮助你分析并对数据采取行动
这非常适用于:
- 由 AI 驱动的 BI 仪表板
- 事件管理自动化
- 客户数据分析
- 实时报告和洞察
架构概览
我们的 MCP 服务器充当 MCP 客户端(Claude、VS Code)与 PostgreSQL 之间的桥梁:
MCP Client (Claude)
|
MCP Protocol
|
MCP Server (TypeScript)
|
pg (PostgreSQL Driver)
|
PostgreSQL Database
前置条件
- Node.js 18+
- TypeScript 基础
- PostgreSQL 数据库
- 基本的 MCP 概念
第一步:项目设置
创建项目:
mkdir mcp-postgres-server
cd mcp-postgres-server
npm init -y
npm install @modelcontextprotocol/sdk pg dotenv
npm install -D typescript @types/node @types/pg
创建 tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "node",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true
}
}
第二步:数据库服务
创建 src/database.ts:
import { Pool } from 'pg';
import dotenv from 'dotenv';
dotenv.config();
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
export class DatabaseService {
async queryUsers(): Promise {
const result = await pool.query(
'SELECT id, name, email FROM users LIMIT 10'
);
return result.rows;
}
async queryIncidents(status: string): Promise {
const result = await pool.query(
'SELECT id, title, status, severity FROM incidents WHERE status = $1 ORDER BY created_at DESC',
[status]
);
return result.rows;
}
async getStats(): Promise {
const result = await pool.query(`
SELECT
COUNT(*) as total_incidents,
SUM(CASE WHEN severity = 'critical' THEN 1 ELSE 0 END) as critical_count,
SUM(CASE WHEN status = 'open' THEN 1 ELSE 0 END) as open_count
FROM incidents
`);
return result.rows[0];
}
}
第三步:MCP 服务器实现
创建 src/server.ts:
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { DatabaseService } from './database.js';
const server = new Server({
name: 'postgres-mcp-server',
version: '1.0.0',
});
const db = new DatabaseService();
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'get_users',
description: 'Retrieve recent users from the database',
inputSchema: {
type: 'object',
properties: {},
required: [],
},
},
{
name: 'get_open_incidents',
description: 'Get all open incidents sorted by date',
inputSchema: {
type: 'object',
properties: {},
required: [],
},
},
{
name: 'get_database_stats',
description: 'Get incident statistics (total, critical, open)',
inputSchema: {
type: 'object',
properties: {},
required: [],
},
},
],
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name } = request.params;
try {
if (name === 'get_users') {
const users = await db.queryUsers();
return {
content: [{ type: 'text', text: JSON.stringify(users, null, 2) }],
};
}
if (name === 'get_open_incidents') {
const incidents = await db.queryIncidents('open');
return {
content: [{ type: 'text', text: JSON.stringify(incidents, null, 2) }],
};
}
if (name === 'get_database_stats') {
const stats = await db.getStats();
return {
content: [{ type: 'text', text: JSON.stringify(stats, null, 2) }],
};
}
return {
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
isError: true,
};
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
},
],
isError: true,
};
}
});
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('PostgreSQL MCP Server running on stdio');
}
main().catch(console.error);
第四步:配置
创建 .env:
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
更新 package.json 脚本:
{
"scripts": {
"build": "tsc",
"start": "node dist/server.js",
"dev": "tsc && npm start"
},
"type": "module"
}
第五步:本地运行
npm run build
npm start
第六步:连接 Claude Desktop
编辑你的 Claude Desktop 配置文件:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\\Claude\\claude_desktop_config.json
{
"mcpServers": {
"postgres": {
"command": "node",
"args": ["/absolute/path/to/dist/server.js"],
"env": {
"DATABASE_URL": "postgresql://user:password@localhost:5432/mydb"
}
}
}
}
重启 Claude Desktop。现在你应该能看到数据库图标并访问这些工具!
生产环境最佳实践
- 连接池:使用
pgPool(代码中已实现) - 参数化查询:始终使用
$1、$2占位符 - 错误处理:捕获并返回有意义的错误信息
- 输入校验:在查询前验证工具输入
- 监控:记录所有查询及响应时间
- 限流:对高并发请求实现背压
公共仓库
完整源码可在以下地址获取:
GitHub Repository: mcp‑postgres‑server
仓库包含:
- 完整的 TypeScript 实现
- Docker 支持
- 部署脚本
- 示例 schema
- 集成测试
部署选项
Google Cloud Run
gcloud run deploy mcp-postgres \
--source . \
--set-env-vars DATABASE_URL=your_connection_string
本地 Docker
docker build -t mcp-postgres .
docker run -e DATABA