Madhav Arora's Blog

How to Get Started with Model Context Protocol

TL;DR
Spin up a local SQLite MCP server, point your favourite LLM at it, and start talking SQL—in under five minutes.

alt text Source: Generated by Dall-E 3

Introduction

Large language models shine when they can reason over real data, but handing them unfettered access to your production database is... well, terrifying. Enter Model Context Protocol (MCP): a lightweight, tool‑based contract that lets you expose just the right capabilities (read‑only queries, schema discovery, feedback collection, etc.) in a way that models can understand and invoke safely.

In this guide we’ll walk through a minimal proof‑of‑concept (POC) MCP server built with FastMCP and SQLite. We’ll cover:

The code is public on GitHub — clone it, follow along, and drop questions in the comments if anything feels off.

What is the Model Context Protocol?

On November 25, 2024 Anthropic open‑sourced MCP, positioning it as a universal bridge between AI assistants and the systems where real data lives—content repositories, business apps, dev environments, you name it. Instead of writing a bespoke connector for every new data source, you expose a small set of JSON‑described tools on an MCP server and let any MCP client (Claude Desktop, an agent framework, your own script) call them.

Why the fuss?

With that backdrop, let’s zoom in on our SQLite demo and see where MCP really shines.

General architecture

MCP follows a simple client ↔ server pattern that scales from your laptop to the cloud: alt text Source: https://modelcontextprotocol.io/introduction

|Component|Role| |---|---| |MCP Host|The UI or agent (Claude Desktop, your IDE) that wants context| |MCP Client|Maintains a 1‑to‑1 connection with a server, handling auth & retries| |MCP Server|Lightweight process that exposes domain‑specific tools via JSON| |Local Data Source|Files, DBs, or services the server can access on your machine| |Remote Service|External API the server can proxy (e.g., Stripe, Jira)|

A single host can juggle multiple servers at once—imagine Claude Desktop chatting with your local SQLite DB, a Git repo, and a SaaS ticketing API simultaneously.

What we’ll build in this article: we’re going to implement the MCP Server block—highlighted above—using Python, FastMCP, and SQLite. By the end you’ll have a fully‑functioning server that exposes database tools the host (Claude Desktop) can call in real time.


In a Hurry?

If you just want to run the demo locally, the quick‑start is:

# 1. Clone the repo
git clone https://github.com/madhavarora1988/mcp_sqlite_poc.git
cd mcp_sqlite_poc

# 2. Install dependencies
pip install -r requirements.txt

# 3. (Optional) generate a fresh SQLite DB with sample tables & rows
python generate_sample_db.py

# 4. Fire up the MCP server (read‑only by default)
python server.py

By default the server listens on stdio, which is exactly what Claude Desktop expects when it auto‑detects local MCP servers. If you’d rather expose an HTTP port, simply change the transport parameter in app.run_async().

Understanding the POC

Project layout

.
├── generate_sample_db.py  # creates sample.db with metrics, users, orders…
├── requirements.txt       # FastMCP, aiosqlite, pydantic…
├── server.py              # the SQLite MCP server
└── README.md              # extended docs & Env vars

generatesampledb.py bootstraps a realistic e‑commerce schema with products, orders, and an order_analytics view so you have something to query straight away.
server.py exposes six MCP tools and handles validation, logging, and optional read‑only mode.

Key safety features

Available tools

|Tool|Purpose|Typical Payload| |---|---|---| |execute_query|Run a SELECT (or safe write) statement|{"query": "SELECT * FROM products LIMIT 3;"}| |list_tables|See what objects are available|{}| |describe_table|Get column names and types|{"table_name": "orders"}| |count_rows|Quick row count|{"table_name": "order_items"}| |insert_sample_data|Populate demo rows (write‑mode only)|{"table_name": "customers", "count": 10}| |add_feedback|Collect user feedback|{"user": "Alice", "email": "...", "feedback": "Great!"}|

Feel free to extend this list with your own domain‑specific tools—just decorate an async function with @app.tool("my_tool") and FastMCP will handle the schema for you.

Testing in Claude Desktop

Claude Desktop automatically scans its configuration file for MCP servers and makes them available in any chat. On macOS you’ll find (or create) that file here:

~/Library/Application Support/Claude/claude_desktop_config.json

Append (or merge) the following block so Claude knows how to start our SQLite server:

{
  "mcpServers": {
    "sqlite": {
      "command": "<absolute uv path>",
      "args": [
        "--directory",
        "<Path to workspace>",
        "run",
        "server.py"
      ]
    }
  }
}

Save the file—if the path didn’t exist, macOS will create the Claude directory chain automatically—then restart Claude Desktop, and you should see sqlite listed in the left‑hand Context panel. You’ll also notice a small hammer icon with a count (e.g. 6) that reads “MCP tools available” when you hover—proof that the server registered correctly (see Screenshot 1 below).

alt text Source: Image by Author

Click that icon and Claude pops up a modal detailing every tool your server exposed (Screenshot 2). From here you can double‑check descriptions—or skip ahead to the Example interactive query section below to watch a full conversation in action.

alt text Available Tools(Source: Image by Author)

Example interactive query

Once the tools show up you can chat naturally, and Claude will chain the right calls for you. In the screenshots below I asked three follow‑up questions:

  1. "Can you check how many orders were placed?" – Claude inspected the schema with list_tables, used count_rows, and reported 7 orders.

alt text Source:Image by Author

  1. "Yes, who placed them?" – Claude peeked at table schemas (describe_table), executed a join via execute_query, and returned a customer‑by‑customer breakdown. alt text Source:Image by Author

  2. "User John says "This product is awesome" — please update the associated table." – Claude validated the schema again and invoked add_feedback to insert the comment into the feedback table, confirming success with the returned ID.

alt text Source: Image by Author

💡 Notice how the assistant narrates each tool invocation, so you can audit exactly what was run.

Feel free to riff on this flow—ask for the most expensive order, daily sales totals, add new rows (with read‑only off), or fetch inactive users and watch the model stitch together the correct SQL on the fly.


Putting It All Together

  1. Spin up the server – local, container, or cloud function.

  2. Add it to your model config – Claude Desktop, Anthropic SDK, LangChain, etc.

  3. Let the model explore the schema – start with list_tables() and describe_table().

  4. Iterate – add domain‑specific tools (e.g., forecast_sales, flag_anomaly) as your use‑case grows.

  5. Lock it down – once happy, switch the DB to read‑only or point the server at a replica.

Suggestions

Conclusion

Model Context Protocol turns the vague idea of “let the LLM hit the database” into a concrete, enforceable contract. With less than 200 lines of Python we’ve wired up authentication‑free SQLite access, safe query validation, and a friendly tool catalogue the model can browse. Give the POC a whirl, customise it for your own datasets, and let me know in the comments what you build!