Skip to content

pyardley/DemoMCPforAPI

Repository files navigation

DemoMCPforAPI

A demonstration project that exposes a SQL Server database to Claude via a FastAPI REST layer and the Model Context Protocol (MCP). Claude can call a tool named get_customer_details in natural language and receive live data from the database.


How It Works

Claude (chat / Claude Code)
        │  natural language
        ▼
  Anthropic Messages API
        │  tool_use → get_customer_details(customer_id)
        ▼
  mcp_tool.py  OR  mcp_server.py (stdio MCP)
        │  HTTP GET /customer/{id}
        ▼
  FastAPI  (main.py)
        │  parameterised SQL query
        ▼
  SQL Server (CustomerDemo / dbo.Customer)

There are two ways to wire Claude to the API:

Mode File Use when
Direct Python script mcp_tool.py Testing from the terminal; runs a full Claude conversation loop
MCP stdio server mcp_server.py Connecting to Claude Desktop or Claude Code via .mcp.json

Tech Stack

Layer Technology
API framework FastAPI + Uvicorn
Database driver pyodbc + ODBC Driver 18 for SQL Server
Data validation Pydantic v2
HTTP client httpx
AI SDK Anthropic Python SDK
Config python-dotenv
Protocol Model Context Protocol — JSON-RPC 2.0 over stdio

Project Structure

DemoMCPforAPI/
├── .env                  ← your local config (git-ignored)
├── .env.example          ← template — copy to .env
├── .mcp.json             ← MCP server config for Claude Code / Claude Desktop
├── main.py               ← FastAPI app (routes, middleware)
├── models.py             ← Pydantic request/response models
├── database.py           ← SQL Server connection + queries
├── mcp_tool.py           ← MCP tool schema + Claude API demo script
├── mcp_server.py         ← Stdio MCP server for Claude Desktop / Claude Code
├── requirements.txt      ← Python dependencies
├── setup_database.sql    ← creates DB, table, and sample data
└── README.md

Database Setup

The script setup_database.sql creates the CustomerDemo database, the dbo.Customer table, and populates it with 10 sample rows. It is safe to run against an existing database — each step checks before acting.

Table schema

CREATE TABLE [dbo].[Customer] (
    [CustomerID]      INT           IDENTITY(1,1) NOT NULL PRIMARY KEY,
    [Forename]        NVARCHAR(50)  NOT NULL,
    [Surname]         NVARCHAR(50)  NOT NULL,
    [UpdatedDateTime] DATETIME2(7)  NOT NULL DEFAULT (GETDATE()),
    [AddressLine1]    NVARCHAR(100) NOT NULL,
    [AddressLine2]    NVARCHAR(100) NULL
);

Sample data

CustomerID Forename Surname AddressLine1 AddressLine2
1 Alice Johnson 1 Castle Lane Winchester
2 Jane Smith-London 45 High Street Top Floor Flat
3 Alex Jones 88 Baker Street Marylebone
4 Sam Taylor 12 Rose Cottage Oaklands
5 Robert Tables 10 Downing Street SW1A 2AA
6 Emma Wilson 7 Elm Road (null)
7 Liam Brown 22 Victoria Terrace Edinburgh
8 Sophia Davis 99 Harbour View Bristol
9 Noah Evans 3 Quarry Hill Leeds
10 Olivia Harris 51 Kingsway Cardiff

Note: CustomerID 5 (Robert Tables) is a nod to Little Bobby Tables — it confirms that parameterised queries handle special surnames without any SQL injection risk.

Running the script

sqlcmd -S "YOUR_SERVER\YOUR_INSTANCE" -E -i setup_database.sql

The -E flag uses Windows Authentication (no username/password required). On success you will see the 10 inserted rows printed as a verification query at the end.

If sqlcmd is not on your PATH, find it at:

C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\sqlcmd.exe

Or run the script directly from SQL Server Management Studio (SSMS) by opening setup_database.sql and pressing F5.


Prerequisites

  • Python 3.11–3.13 — Python 3.14+ is not yet supported by pydantic-core (see Troubleshooting)
  • ODBC Driver 18 for SQL Serverdownload here
  • SQL Server running and accessible (run setup_database.sql if the CustomerDemo database has not yet been created)
  • Anthropic API keyget one here

Setup

1. Create the virtual environment

cd C:\path\to\DemoMCPforAPI
py -3.13 -m venv .venv

Use py -3.13 (or whichever 3.11–3.13 version you have installed) to avoid Python 3.14 compatibility issues.

2. Install dependencies

.venv\Scripts\python.exe -m pip install -r requirements.txt

If you see an execution policy error when trying to run .venv\Scripts\Activate.ps1, use the full path to the venv's Python/uvicorn executables directly instead — activation is not required.

3. Configure environment variables

Copy-Item .env.example .env

Then edit .env and fill in your Anthropic API key:

DATABASE_URL=DRIVER={ODBC Driver 18 for SQL Server};SERVER=[YOUR_DB_SERVER];DATABASE=CustomerDemo;Trusted_Connection=yes;TrustServerCertificate=yes
APP_PORT=8000
APP_ENV=development
ANTHROPIC_API_KEY=sk-ant-...

4. Start the FastAPI server

.venv\Scripts\uvicorn.exe main:app --host 127.0.0.1 --port 8000 --reload

Verify it is running:

Invoke-RestMethod http://localhost:8000/health

Expected output:

{
  "status": "ok",
  "database": "CustomerDemo",
  "version": "Microsoft SQL Server..."
}

Running Queries

Option A — Direct API call

Invoke-RestMethod http://localhost:8000/customer/2

Option B — Claude demo script (terminal)

Requires the FastAPI server to be running in a separate terminal first.

# Test the Python client directly (no Claude involved)
.venv\Scripts\python.exe mcp_tool.py 2

# Run a full Claude conversation with tool use
.venv\Scripts\python.exe mcp_tool.py "Show me details for customer 2"

The second form sends your message to Claude via the Anthropic API. Claude decides to call get_customer_details, the script executes the call, returns the result to Claude, and Claude formats the final answer.

Option C — Claude Code / Claude Desktop (MCP)

Once .mcp.json is present and Claude Code has been restarted, just ask in the chat:

"Show me details for customer 2"

Claude Code will call the tool natively.

Option D — Interactive API docs

Open http://localhost:8000/docs in a browser while the server is running. You can test all endpoints interactively from the Swagger UI.


API Endpoints

GET /health

Checks that the API is up and that SQL Server is reachable.

Response 200:

{
  "status": "ok",
  "database": "CustomerDemo",
  "version": "Microsoft SQL Server 2025..."
}

Response 503: Database is unreachable.


GET /customer/{customer_id}

Returns the full customer record for the given integer ID.

Response 200:

{
  "CustomerID": 2,
  "Forename": "Jane",
  "Surname": "Smith-London",
  "UpdatedDateTime": "2026-03-31T14:16:34.683333",
  "AddressLine1": "45 High Street",
  "AddressLine2": "Top Floor Flat"
}

Response 404: No customer exists with that ID.

Response 500: Unexpected database error (check server logs).


Code Walkthrough

models.py

Defines two Pydantic models used for request/response validation:

  • Customer — mirrors the dbo.Customer table columns; all fields except CustomerID are optional to handle NULLs gracefully.
  • HealthResponse — returned by the /health endpoint.

database.py

Handles all SQL Server interaction:

  • Opens a new connection per request using the DATABASE_URL env var. Per-request connections are used (rather than a pool) because Windows Integrated Authentication tokens can expire in pooled connections.
  • db_cursor() is a context manager that commits on success and rolls back on any exception, always closing the connection in finally.
  • fetch_customer() executes a parameterised query using ? placeholders — no string concatenation, so SQL injection is not possible.
  • check_db_health() runs SELECT @@VERSION as a lightweight connectivity probe.

main.py

The FastAPI application:

  • CORS is set to allow all origins (*) for local demo use — restrict this before any network exposure.
  • /health calls check_db_health() and returns 503 if it throws.
  • /customer/{customer_id} calls fetch_customer(), returns 404 if the result is None, and wraps any other exception as a generic 500.
  • Swagger docs (/docs) are disabled automatically when APP_ENV=production.

mcp_tool.py

Two things in one file:

  1. TOOL_SCHEMA — the JSON object Claude expects in the tools array of a Messages API call. Paste this into a Claude Project's custom tools section or pass it to the API.
  2. get_customer_details(customer_id) — a Python function that calls GET /customer/{customer_id} using httpx and returns the parsed JSON.
  3. run_claude_demo(user_message) — a full agentic loop: sends the message → handles tool_use stop reason → executes the tool → sends tool_result → repeats until end_turn.

mcp_server.py

A minimal JSON-RPC 2.0 server over stdio implementing the MCP protocol (version 2024-11-05). Claude Desktop and Claude Code launch this as a subprocess and communicate with it line-by-line on stdin/stdout.

Handles three MCP methods:

  • initialize — returns server capabilities
  • tools/list — returns [TOOL_SCHEMA]
  • tools/call — calls get_customer_details() and wraps the result in an MCP content block

Notifications (messages with no id) receive no response, as required by the spec.


Registering with Claude Desktop

Claude Desktop does not read .mcp.json, so full paths are required in its own config file. Edit %APPDATA%\Claude\claude_desktop_config.json, replacing C:\path\to with your actual project path:

{
  "mcpServers": {
    "customer-demo": {
      "command": "C:\\path\\to\\DemoMCPforAPI\\.venv\\Scripts\\python.exe",
      "args": ["C:\\path\\to\\DemoMCPforAPI\\mcp_server.py"],
      "env": {
        "API_BASE_URL": "http://localhost:8000"
      }
    }
  }
}

Note: If you prefer a relative path for the script ("args": ["mcp_server.py"]), add a "cwd" field set to the project directory so Claude Desktop knows where to look — e.g. "cwd": "C:\\path\\to\\DemoMCPforAPI". Without it, the relative path will be resolved from an unpredictable working directory and the server will fail to start.

Restart Claude Desktop. A hammer icon (🔨) in the chat bar confirms the tool is connected.


Registering with Claude Code

The .mcp.json file in this directory is already configured. Restart Claude Code (reload the VSCode window) with the FastAPI server running and the get_customer_details tool will be available in the chat.


Troubleshooting

pydantic-core build fails — "Python 3.14 is newer than PyO3's maximum supported version (3.13)"

pydantic-core uses a Rust extension that does not yet support Python 3.14. Install Python 3.13 from python.org, then recreate the venv:

Remove-Item -Recurse -Force .venv
py -3.13 -m venv .venv
.venv\Scripts\python.exe -m pip install -r requirements.txt

PowerShell execution policy error when running Activate.ps1

.venv\Scripts\Activate.ps1 : The module '.venv' could not be loaded.

Either allow user-scoped scripts:

Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

Or skip activation entirely and call venv executables by full path:

.venv\Scripts\uvicorn.exe main:app --host 127.0.0.1 --port 8000 --reload
.venv\Scripts\python.exe mcp_tool.py "Show me details for customer 2"

[WinError 10061] No connection could be made because the target machine actively refused it

The FastAPI server is not running. Start it in a separate terminal:

.venv\Scripts\uvicorn.exe main:app --host 127.0.0.1 --port 8000 --reload

404 Not Found for a customer ID

The customer does not exist in the database. Find valid IDs:

sqlcmd -S "YOUR_SERVER\YOUR_INSTANCE" -d CustomerDemo -Q "SELECT TOP 10 CustomerID, Forename, Surname FROM dbo.Customer ORDER BY CustomerID"

TypeError: Could not resolve authentication method

ANTHROPIC_API_KEY is not set. Add it to .env:

ANTHROPIC_API_KEY=sk-ant-...

Then ensure .env is being loaded — mcp_tool.py calls load_dotenv() at the top, so the file must exist in the working directory.


503 Service Unavailable from /health

SQL Server is unreachable. Check:

  1. The SQL Server service is running (services.msc → SQL Server (YOUR_INSTANCE))
  2. The DATABASE_URL in .env has the correct server name
  3. Your Windows account has access to the CustomerDemo database

MCP tool not appearing in Claude Code

  1. Confirm .mcp.json exists in the project root.
  2. Confirm the FastAPI server is running on port 8000.
  3. Reload the VSCode window (Ctrl+Shift+P → "Developer: Reload Window").
  4. Check the Claude Code output panel for MCP connection errors.

Security Notes

This project is configured for local development only. Before exposing it on a network:

Concern Current Production recommendation
Authentication None Add API key middleware or OAuth 2.0 / JWT
CORS Allow all origins Restrict to your specific front-end domain
Transport HTTP Terminate TLS at a reverse proxy (nginx, Caddy)
Rate limiting None Add slowapi or gateway-level rate limiting
SQL injection Parameterised queries ✓ Already safe
Error details Generic 500 messages ✓ Already safe — stack traces go to stderr only
Credentials .env file (git-ignored) Use a secrets manager in production
DB account Windows Auth (your user) Use a dedicated service account with SELECT-only access

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors