Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Querying PostgreSQL with Claude Code

Hello! Welcome back to our course on advanced Claude Code.

In our last lesson, we accomplished a major step by architecting and building a secure, production-grade MCP server for PostgreSQL. You created a FastAPI application that uses asyncpg for high-performance database access, manages connections with a pool, and exposes a secure, parameterized query tool defined with Pydantic models.

Today, we'll switch our focus from the server to the client. The learning outcome for this lesson is to use the custom PostgreSQL MCP server to perform natural language queries from within Claude Code. We will connect the server you just built to your Claude Code environment and, most importantly, learn how to prompt Claude to translate your plain-English questions into the structured API calls our tool expects.

The Full Picture: Connecting Claude to Your Database

Let's start by visualizing the complete architecture we're working with. The MCP server you built acts as the crucial bridge between the Claude Code client and your database.

MCP: Connect Claude to Your Database Architecture
This diagram illustrates the flow of information: your natural language query in Claude Code is sent to the MCP server, which translates it into a safe SQL query, executes it on your database, and returns the result.

Our main tasks today are to establish the connection between the Claude Desktop Client and the MCP Server, and then to master the art of asking questions.

Step 1: Connecting Your Local MCP Server

Before you can talk to your server, Claude Code needs to know it exists. Since our server is a local process (the FastAPI app you built), we'll register it as a local stdio server. This tells Claude Code to start your server process and communicate with it over standard input/output.

The recommended way to do this is with the claude mcp add command.

Connect Claude Code to tools via MCP

The official documentation, 'Connect Claude Code to tools via MCP', explains how to register different types of servers. We'll focus on the command for adding a local stdio server.

Please review the documentation, focusing on two parts: Option 3: Add a local stdio server: Understand the basic syntax claude mcp add <name> -- <command> [args...]. This is the command we will use. Example: Query your PostgreSQL database: Notice how they use npx to run a pre-packaged server. In our case, our <command> will be the uvicorn command to run the server you built in the previous lesson.

Assuming your FastAPI server file from the last lesson is named server.py and is located in your project's root directory, you can add it to Claude Code with the following command. Run this in your terminal from your project directory:

claude mcp add --scope local pg_server -- uvicorn server:app --host 127.0.0.1 --port 8000

Let's break this down:

  • claude mcp add: The command to add a new server.
  • --scope local: Makes this server available only to you within this specific project.
  • pg_server: The name we're giving our server. We'll use this to refer to it.
  • --: A separator. Everything after this is the command to start the server.
  • uvicorn server:app ...: The familiar command to run your FastAPI application.

Important: For this to work, the environment variables for your database connection (DB_USER, DB_PASSWORD, etc.) must be available in the shell where Claude Code runs. Ensure your .env file from the last lesson is present in the project directory.

What If It Doesn't Work?

Configuration issues are common. If Claude Code shows an error after you add the server, the log files are your best friend. This video provides a great walkthrough of the debugging process.

Set Up PostgreSQL MCP Server with Claude Desktop

The video 'Set Up PostgreSQL MCP Server with Claude Desktop' demonstrates how to connect a server and, more importantly, how to troubleshoot a faulty connection by inspecting the log files.

Watch the section from 13:52 to 15:58. The presenter intentionally makes an error in the server configuration and then uses the 'Open log folder' option in Claude Code to diagnose and fix the 'Failed to spawn' error. This is a critical skill for managing custom MCP servers.

Step 2: The Core Mechanism — How Claude Understands Your Request

Now for the most interesting part. When you ask Claude, "How many users do I have?", how does it know to use your pg_server and construct a call to the execute_parameterized_sql tool?

The answer is that Claude reasons about your request using the information it has about the available tools. It does not simply pass your question as a string to the tool.

This next video segment explains this translation process perfectly.

Set Up PostgreSQL MCP Server with Claude Desktop

This video clip explains the crucial distinction between the user's natural language query and the structured input the AI tool actually receives.

Watch from 10:53 to 13:15. The key insight here is that the AI agent acts as a translator. It takes your natural language question, interprets your intent, and then constructs a valid SQL query (in our case, a SQLQueryRequest object) to pass to the tool function. The JSON result from the tool is then processed by the AI and presented back to you in natural language.

To be specific, Claude relies on three pieces of information you provided when you registered the tool in the previous lesson:

  1. Tool Name: execute_parameterized_sql
  2. Tool Description (Docstring): "Executes a parameterized SQL SELECT query against the PostgreSQL database. Only for read-only (SELECT) operations."
  3. Input Schema (SQLQueryRequest Pydantic model):
    • query: A string for the SQL with placeholders ($1, $2, etc.).
    • params: A list of values to safely substitute into the placeholders.

When you ask a question related to your database, Claude's reasoning process is:

  1. "The user is asking a database-related question."
  2. "The execute_parameterized_sql tool seems appropriate based on its description."
  3. "To use this tool, I must generate a JSON object with query and params keys."
  4. "Based on the user's question, the correct SQL query is SELECT COUNT(*) FROM users;."
  5. "This query has no parameters, so params will be an empty list."
  6. "Therefore, I will call the tool with {'query': 'SELECT COUNT(*) FROM users;', 'params': []}."

Step 3: Putting It Into Practice

Let's guide Claude to perform this translation. For the first time, it's helpful to provide Claude with explicit context about your tool and database schema.

First, ensure your MCP server is running (you should see it in the Claude Code tool panel). Then, start a new chat and use the following prompt.

Initial Context-Setting Prompt

I have a local MCP server running called 'pg_server' that exposes a tool named 'execute_parameterized_sql'.

Tool Schema:

  • Description: Executes a parameterized SQL SELECT query.
  • Input Model (SQLQueryRequest):
    • query (string): The SQL query with placeholders like $1.
    • params (list): A list of values for the placeholders.

Database Schema:
My database contains a users table with the following columns: id (integer), username (varchar), email (varchar), and created_at (timestamp).

Now, please act as an expert SQL writer. When I ask a question, your job is to translate it into the correct tool call for execute_parameterized_sql. Do not execute it immediately. First, show me the ToolUse block you would generate for my question.

My first question is: "How many users are there?"

Claude should respond by showing you its reasoning and the ToolUse block, which will contain the JSON payload, without executing it. It should look something like this:

{
  "tool": "pg_server:execute_parameterized_sql",
  "input": {
    "query": "SELECT COUNT(*) FROM users;",
    "params": []
  }
}

Once you see this and confirm it's correct, you can simply reply:

Go ahead.

Claude will then execute the tool call, receive the SQLQueryResult from your server, and present the answer in a human-readable format, like "Your users table has 217,605 rows."

Exercise: A Query with Parameters

Now, try a more complex query. Ask a new question in the same chat:

Find the user with the email 'some.user@example.com'.

Claude should again show you its plan. This time, notice how it uses a parameterized query to prevent SQL injection:

{
  "tool": "pg_server:execute_parameterized_sql",
  "input": {
    "query": "SELECT * FROM users WHERE email = $1;",
    "params": [
      "some.user@example.com"
    ]
  }
}

This demonstrates the power and security of the server you built. You are interacting with your database using natural language, while the MCP server ensures the interactions are safe and structured.

Conclusion

In this lesson, you successfully bridged the gap between your custom MCP server and the Claude Code client. You've moved from theory to practice, turning your database into a resource you can converse with.

Key Takeaways:

  • You can connect local servers to Claude Code using the claude mcp add --transport stdio command.
  • Troubleshooting MCP connections is best done by examining the log files provided by Claude Code.
  • Claude translates natural language into structured tool calls by using the tool's name, description, and input schema as a guide.
  • Providing explicit context about your tools and database schema in your initial prompts leads to more reliable and accurate results.

Next Up:
You have now built and used a custom, stateful MCP server from scratch. This is a powerful capability for your own projects. In the next lesson, we will broaden our scope and learn how to integrate a third-party MCP server (e.g., from GitHub or Sentry) into your Claude Code workflow. This will allow you to connect Claude to popular external services without having to build the server yourself.

Can't find a good explanation? Sign up and we'll make it for you

Sign up