Connecting Claude to your database with MCP (Model Context Protocol) turns Claude into a natural language interface for your data. Instead of writing SQL or MongoDB queries, you ask Claude questions in plain English and it generates and executes the queries, formats the results, and explains what it found. This Claude database MCP connection tutorial covers PostgreSQL, MySQL, and MongoDB โ€” the three most common databases in enterprise deployments.

MCP is Anthropic's open protocol for connecting AI models to external tools and data sources. It's purpose-built for this exact use case: giving Claude controlled, secure access to systems it doesn't natively know about. The MCP protocol guide explains the full architecture. This tutorial focuses on the implementation โ€” getting a working database connection running in your environment.

โš  Security first: Database connections from AI models require read-only accounts, query validation, and rate limiting. Never connect Claude to a database with write permissions unless you've implemented proper guardrails. This tutorial covers the security requirements in detail in Step 6.

What Is MCP and Why Use It for Databases?

The Model Context Protocol is an open standard that lets Claude communicate with external tools through a defined JSON-RPC interface. An MCP server sits between Claude and your database, translating Claude's tool calls into actual database queries and returning the results in a format Claude can reason about.

Before MCP, connecting Claude to a database required custom backend code for every tool call โ€” you had to write functions that Claude could call via the API's tool use feature, implement the database connection logic yourself, and handle schema introspection manually. MCP standardises this. You run a compliant MCP server, register it with Claude, and Claude handles the rest. The same server works with Claude Code, Claude Cowork, and any Claude API integration. Our MCP server development service builds production-grade MCP servers for enterprise database environments.

What Claude can do with database MCP access

With a properly configured database MCP connection, Claude can answer questions like "Which customers haven't ordered in the last 90 days?", "What's the average deal size by region this quarter?", "Which products have stock levels below reorder threshold?" โ€” all without the requester knowing SQL. Claude introspects your schema, constructs the query, executes it, and returns a human-readable analysis of the results.

Architecture Overview

The architecture has three components: Claude (running in Claude Code, Cowork, or via the API), the MCP server (a process running on your infrastructure), and the database (PostgreSQL, MySQL, or MongoDB). Claude communicates with the MCP server over a local socket or stdio pipe; the MCP server communicates with the database over your standard database connection protocol.

Claude (AI model)
    โ†•  MCP protocol (JSON-RPC over stdio/socket)
MCP Database Server (Node.js or Python process)
    โ†•  Database driver (pg / mysql2 / mongodb)
Database (PostgreSQL / MySQL / MongoDB)
    โ†•
Your data

The MCP server is the security boundary. It controls which tables Claude can access, validates queries before execution, enforces row limits, and logs all queries for audit purposes. Claude never has direct access to the database โ€” it only ever calls tools exposed by the MCP server. This is the architecture you want for any production deployment.

PostgreSQL MCP Server Setup

01

Install and configure the PostgreSQL MCP server

We'll use the official @anthropic/mcp-server-postgres package for the quickest path to a working setup.

# Install the official Postgres MCP server
npm install -g @anthropic/mcp-server-postgres

# Or for project-local install
npm install @anthropic/mcp-server-postgres

If you prefer to build your own PostgreSQL MCP server (recommended for enterprise โ€” you control the query validation logic), here's a minimal implementation:

// postgres-mcp-server.js
const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
const { Pool } = require('pg');
require('dotenv').config();

// Read-only database connection
const pool = new Pool({
  host: process.env.PG_HOST,
  port: parseInt(process.env.PG_PORT || '5432'),
  database: process.env.PG_DATABASE,
  user: process.env.PG_USER,          // Use a read-only DB user
  password: process.env.PG_PASSWORD,
  max: 10,
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
});

const server = new Server(
  { name: 'postgres-mcp', version: '1.0.0' },
  { capabilities: { tools: {} } }
);

// Define available tools
server.setRequestHandler('tools/list', async () => ({
  tools: [
    {
      name: 'query_database',
      description: 'Execute a SELECT query against the PostgreSQL database. Only SELECT statements are allowed.',
      inputSchema: {
        type: 'object',
        properties: {
          sql: { type: 'string', description: 'The SELECT SQL query to execute' },
          limit: { type: 'number', description: 'Maximum rows to return (default: 100, max: 1000)', default: 100 }
        },
        required: ['sql']
      }
    },
    {
      name: 'list_tables',
      description: 'List all available tables and their columns in the database',
      inputSchema: { type: 'object', properties: {} }
    },
    {
      name: 'describe_table',
      description: 'Get column names, types, and constraints for a specific table',
      inputSchema: {
        type: 'object',
        properties: { table_name: { type: 'string' } },
        required: ['table_name']
      }
    }
  ]
}));

// Handle tool calls
server.setRequestHandler('tools/call', async (request) => {
  const { name, arguments: args } = request.params;

  switch (name) {
    case 'query_database': {
      const sql = args.sql.trim();
      const limit = Math.min(args.limit || 100, 1000);

      // Security: only allow SELECT statements
      if (!sql.toUpperCase().startsWith('SELECT')) {
        return { content: [{ type: 'text', text: 'Error: Only SELECT statements are permitted.' }], isError: true };
      }

      // Security: block dangerous keywords
      const blocked = ['DROP', 'DELETE', 'UPDATE', 'INSERT', 'TRUNCATE', 'ALTER', 'CREATE'];
      if (blocked.some(kw => sql.toUpperCase().includes(kw))) {
        return { content: [{ type: 'text', text: 'Error: Query contains forbidden keywords.' }], isError: true };
      }

      try {
        const result = await pool.query(`${sql} LIMIT $1`, [limit]);
        return {
          content: [{
            type: 'text',
            text: JSON.stringify({ rows: result.rows, rowCount: result.rowCount, fields: result.fields.map(f => f.name) }, null, 2)
          }]
        };
      } catch (err) {
        return { content: [{ type: 'text', text: `Query error: ${err.message}` }], isError: true };
      }
    }

    case 'list_tables': {
      const result = await pool.query(`
        SELECT table_name, table_type
        FROM information_schema.tables
        WHERE table_schema = 'public'
        ORDER BY table_name
      `);
      return { content: [{ type: 'text', text: JSON.stringify(result.rows, null, 2) }] };
    }

    case 'describe_table': {
      const result = await pool.query(`
        SELECT column_name, data_type, is_nullable, column_default
        FROM information_schema.columns
        WHERE table_name = $1 AND table_schema = 'public'
        ORDER BY ordinal_position
      `, [args.table_name]);
      return { content: [{ type: 'text', text: JSON.stringify(result.rows, null, 2) }] };
    }
  }
});

const transport = new StdioServerTransport();
server.connect(transport).catch(console.error);
02

Configure Claude to use your MCP server

In Claude Code, add the server to your CLAUDE.md or .claude/settings.json.

// .claude/settings.json
{
  "mcpServers": {
    "postgres": {
      "command": "node",
      "args": ["/path/to/postgres-mcp-server.js"],
      "env": {
        "PG_HOST": "localhost",
        "PG_PORT": "5432",
        "PG_DATABASE": "your_database",
        "PG_USER": "claude_readonly",
        "PG_PASSWORD": "your-readonly-password"
      }
    }
  }
}

For Claude Cowork, MCP servers are configured as connectors in the admin console. For API deployments, pass MCP server configuration in the API call. See the Claude Code MCP servers guide for the full configuration reference.

MySQL MCP Server Setup

The MySQL MCP server follows the same pattern as PostgreSQL, with the mysql2 driver replacing pg. The security controls and query validation logic are identical โ€” the only differences are the connection configuration and a few MySQL-specific introspection queries.

// mysql-mcp-server.js (key differences from PostgreSQL version)
const mysql = require('mysql2/promise');

const pool = mysql.createPool({
  host: process.env.MYSQL_HOST,
  port: parseInt(process.env.MYSQL_PORT || '3306'),
  database: process.env.MYSQL_DATABASE,
  user: process.env.MYSQL_USER,
  password: process.env.MYSQL_PASSWORD,
  waitForConnections: true,
  connectionLimit: 10,
  queueLimit: 0
});

// Query execution (MySQL uses ? placeholder vs $1 in PostgreSQL)
const [rows] = await pool.execute(
  `${sql} LIMIT ?`,
  [limit]
);

// List tables (MySQL syntax)
const [tables] = await pool.execute(`
  SELECT TABLE_NAME, TABLE_TYPE
  FROM INFORMATION_SCHEMA.TABLES
  WHERE TABLE_SCHEMA = DATABASE()
  ORDER BY TABLE_NAME
`);

// Describe table (MySQL syntax)
const [columns] = await pool.execute(`
  SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT
  FROM INFORMATION_SCHEMA.COLUMNS
  WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?
  ORDER BY ORDINAL_POSITION
`, [tableName]);

For MySQL specifically, create a dedicated read-only user with explicit GRANT permissions rather than using a superuser account:

-- Create read-only MySQL user for Claude
CREATE USER 'claude_readonly'@'%' IDENTIFIED BY 'strong-password-here';

-- Grant SELECT only on specific database
GRANT SELECT ON your_database.* TO 'claude_readonly'@'%';

-- Remove unnecessary privileges
REVOKE ALL PRIVILEGES ON *.* FROM 'claude_readonly'@'%';
GRANT SELECT ON your_database.* TO 'claude_readonly'@'%';

FLUSH PRIVILEGES;

MongoDB MCP Server Setup

MongoDB's document model requires a different approach from SQL databases. Instead of SQL queries, Claude uses MongoDB's aggregation pipeline and find operations. The MCP server needs to expose these as tools with appropriate validation.

// mongodb-mcp-server.js
const { MongoClient } = require('mongodb');
require('dotenv').config();

let client;
let db;

async function connect() {
  client = new MongoClient(process.env.MONGODB_URI, {
    readPreference: 'secondary'  // Read from secondary to avoid load on primary
  });
  await client.connect();
  db = client.db(process.env.MONGODB_DATABASE);
}

// Tools exposed to Claude:
const mongoTools = [
  {
    name: 'find_documents',
    description: 'Query documents from a MongoDB collection using a filter',
    inputSchema: {
      type: 'object',
      properties: {
        collection: { type: 'string', description: 'Collection name' },
        filter: { type: 'object', description: 'MongoDB query filter (JSON)' },
        projection: { type: 'object', description: 'Fields to include/exclude' },
        limit: { type: 'number', default: 50, description: 'Max documents (max: 200)' },
        sort: { type: 'object', description: 'Sort criteria' }
      },
      required: ['collection']
    }
  },
  {
    name: 'aggregate',
    description: 'Run a MongoDB aggregation pipeline on a collection',
    inputSchema: {
      type: 'object',
      properties: {
        collection: { type: 'string' },
        pipeline: { type: 'array', description: 'Aggregation pipeline stages' }
      },
      required: ['collection', 'pipeline']
    }
  },
  {
    name: 'list_collections',
    description: 'List all collections in the database',
    inputSchema: { type: 'object', properties: {} }
  },
  {
    name: 'get_collection_stats',
    description: 'Get document count and sample schema for a collection',
    inputSchema: {
      type: 'object',
      properties: { collection: { type: 'string' } },
      required: ['collection']
    }
  }
];

// Handle find_documents
async function findDocuments(args) {
  const limit = Math.min(args.limit || 50, 200);
  const col = db.collection(args.collection);
  const docs = await col
    .find(args.filter || {}, { projection: args.projection || {} })
    .sort(args.sort || {})
    .limit(limit)
    .toArray();
  return { documents: docs, count: docs.length };
}

// Handle aggregate (validate pipeline stages)
async function aggregate(args) {
  const blocked = ['$out', '$merge'];  // Block write stages
  const hasBlockedStage = args.pipeline.some(stage =>
    blocked.some(b => Object.keys(stage)[0] === b)
  );
  if (hasBlockedStage) throw new Error('Write stages ($out, $merge) are not permitted.');

  const col = db.collection(args.collection);
  const results = await col.aggregate(args.pipeline).limit(500).toArray();
  return { results, count: results.length };
}

Need a production-grade MCP database server?

The code above gets you started, but enterprise deployments require connection pooling, query logging, row-level security, schema whitelisting, and monitoring. Our MCP server development service builds this for your stack โ€” with full documentation and handoff.

Get a Custom MCP Build โ†’

Security Considerations

Connecting an AI model to your database is a significant security decision. Claude is good at generating valid queries, but it can also be manipulated by malicious inputs (prompt injection) into generating queries that expose data it shouldn't access. The security model needs to be enforced at the MCP server level, not by trusting Claude's intentions.

The six security controls you must implement

First, use a dedicated read-only database account for Claude's MCP connection โ€” never your application account, never a superuser. Second, whitelist the tables and collections Claude is permitted to access. If Claude only needs access to the orders and customers tables, don't give it access to users or payments. Third, validate every query at the MCP server level before execution โ€” check for forbidden keywords, injection patterns, and overly broad selects. Fourth, enforce hard row limits on all queries. Fifth, log every query with timestamp, user context, and query text โ€” this is mandatory for compliance in financial services, healthcare, and government. Sixth, run the MCP server in an isolated process with minimal system permissions.

// Comprehensive query validator for SQL databases
function validateQuery(sql) {
  const normalised = sql.toUpperCase().trim();

  // Must start with SELECT
  if (!normalised.startsWith('SELECT')) {
    throw new Error('Only SELECT statements are permitted');
  }

  // Block write/admin operations
  const forbidden = ['DROP', 'DELETE', 'UPDATE', 'INSERT', 'TRUNCATE',
    'ALTER', 'CREATE', 'GRANT', 'REVOKE', 'EXEC', 'EXECUTE',
    'XP_', 'SP_', 'INFORMATION_SCHEMA.USER', '--', '/*'];
  for (const kw of forbidden) {
    if (normalised.includes(kw)) {
      throw new Error(`Forbidden keyword detected: ${kw}`);
    }
  }

  // Block multiple statements
  if (normalised.includes(';')) {
    throw new Error('Multiple statements are not permitted');
  }

  // Block subqueries to system tables (PostgreSQL example)
  if (normalised.includes('PG_') && normalised.includes('PG_SHADOW')) {
    throw new Error('Access to system tables is not permitted');
  }

  return true;
}

// Query audit logger
async function logQuery(query, user, success, rowCount) {
  await auditLog.insert({
    timestamp: new Date(),
    user,
    query,
    success,
    rowCount,
    ip: process.env.DEPLOYMENT_ENV
  });
}

For a deeper treatment of MCP security architecture, see our guide on MCP security for enterprise deployments and the Claude security and governance service.

Query Patterns and Examples

Once your Claude database MCP connection is working, here's what you can actually ask Claude. These are the patterns that work well across all three database types.

Business intelligence queries

Natural language: "Show me total revenue by product category for the last 3 months, sorted by highest to lowest." Claude will introspect the schema to find the relevant tables, construct the GROUP BY query, and return a formatted breakdown with column names and totals.

Data quality checks

Natural language: "How many records in the customers table are missing email addresses or phone numbers?" Claude handles the NULL checks and UNION logic โ€” questions that are tedious to write in SQL but trivial to express in English.

Cross-table analysis

Natural language: "Which customers have placed more than 5 orders but haven't purchased in the last 60 days?" Claude joins the customers and orders tables, applies the aggregate filter, and presents a list with customer names and last order dates.

Schema exploration

Natural language: "What tables do you have access to, and what information does the orders table contain?" Claude calls the list_tables and describe_table tools and gives a plain-English summary of the data model โ€” useful for onboarding new analysts who don't know the database schema.

Enterprise Deployment

Running a Claude database MCP connection in development is one thing. Running it for 500 analysts querying a production database is another. Enterprise deployments need several additional layers.

Connection pooling is critical โ€” don't create a new database connection for every MCP tool call. The examples above use a pool, but you need to tune pool size based on your database's max connection limit. For databases behind a VPN or in a private subnet, your MCP server needs to run inside the same network segment โ€” it cannot call across the public internet. This typically means deploying the MCP server as a container inside your Kubernetes cluster or VPC, not running it on a developer's laptop.

Schema versioning is another consideration. If your database schema changes โ€” a column is renamed, a table is dropped โ€” Claude will construct queries against the old schema and fail. Build a schema cache refresh mechanism into your MCP server, and consider exposing schema documentation as a tool Claude can call before constructing queries against unfamiliar tables.

Finally, cost management. Each natural language database query typically costs 2โ€“5 API calls (schema inspection + query execution + result formatting). At scale, this adds up. Route simple lookups to Claude Haiku and reserve Sonnet/Opus for complex multi-table analysis. See the Claude model selection guide for the routing logic, and our Claude API integration service for the full enterprise architecture.

Key Takeaways

  • MCP servers are the security boundary โ€” validate queries at the server, never rely on Claude's judgement alone
  • Always use a dedicated read-only database account for Claude's MCP connection
  • Whitelist tables and enforce row limits to control data exposure and cost
  • Log every query for audit compliance โ€” required in regulated industries
  • For enterprise deployments, run MCP servers inside your network perimeter, not on developer machines

Related Articles

CI

ClaudeImplementation Team

Claude Certified Architects building enterprise AI integrations โ€” from MCP servers to full agentic deployments. Meet the team โ†’