Asking an AI about medication can be terrifying. One moment itβs giving helpful advice, and the next, it's hallucinating a dangerous drug-drug interactionβor worse, missing one entirely. When it comes to Personal Health Assistants, "close enough" isn't good enough. We need a "Source of Truth."
In this tutorial, we are going to master the Model Context Protocol (MCP) to connect an LLM (Claude) to a structured Pharmacopeia database. By the end of this guide, youβll have a local MCP server that allows Claude to query a SQLite database for real, verified drug interactions, transforming a generic chatbot into a precision-grade health agent.
Using Model Context Protocol (MCP) with TypeScript and SQLite is the gold standard for building reliable LLM Agents. If you want to move beyond simple chat and into the world of functional, data-driven AI, this is the place to start.
The Architecture: How MCP Bridges the Gap
The Model Context Protocol (MCP) acts as a standardized "plug" between an LLM client (like Claude Desktop) and a data source. Instead of the LLM guessing, it calls "tools" provided by your server.
graph TD
A[User: Can I take Aspirin with Warfarin?] --> B[Claude Desktop / MCP Client]
B --> C{MCP Protocol}
C --> D[Our Custom MCP Server - TypeScript]
D --> E[(SQLite: Drug Interaction DB)]
E --> D
D --> C
C --> B
B --> F[Claude: 'No! That increases bleeding risk. Source: Pharmacopeia']
Prerequisites π οΈ
Before we dive in, ensure you have the following ready:
- Node.js (v18 or higher)
- TypeScript installed globally
- Claude Desktop (The most robust MCP client currently)
- A basic understanding of SQL
Step 1: Initialize the Project
First, let's set up our TypeScript environment and install the official MCP SDK.
mkdir mcp-health-server
cd mcp-health-server
npm init -y
npm install @modelcontextprotocol/sdk sqlite3 sqlite
npm install -D typescript @types/node @types/sqlite3
npx tsc --init
Step 2: The Database Schema ποΈ
We need a simple SQLite database to represent our "Professional Pharmacopeia." Let's create a file named database.ts to initialize our data.
import sqlite3 from 'sqlite3';
import { open } from 'sqlite';
export async function setupDatabase() {
const db = await open({
filename: './pharmacopeia.db',
driver: sqlite3.Database
});
await db.exec(`
CREATE TABLE IF NOT EXISTS interactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
drug_a TEXT,
drug_b TEXT,
severity TEXT,
description TEXT
)
`);
// Seed data: Only for demonstration!
await db.run(`INSERT INTO interactions (drug_a, drug_b, severity, description)
VALUES ('Aspirin', 'Warfarin', 'High', 'Increased risk of major bleeding.')`);
return db;
}
Step 3: Creating the MCP Server
This is where the magic happens. We will define a Tool called check_interaction that Claude can call whenever it needs to verify drug safety.
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 { setupDatabase } from "./database.js";
const server = new Server({
name: "health-assistant-server",
version: "1.0.0",
}, {
capabilities: {
tools: {},
},
});
const db = await setupDatabase();
// 1. List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [{
name: "check_interaction",
description: "Check for dangerous interactions between two drugs",
inputSchema: {
type: "object",
properties: {
drugA: { type: "string" },
drugB: { type: "string" },
},
required: ["drugA", "drugB"],
},
}],
}));
// 2. Handle the tool logic
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "check_interaction") {
const { drugA, drugB } = request.params.arguments as { drugA: string; drugB: string };
const interaction = await db.get(
"SELECT * FROM interactions WHERE (drug_a = ? AND drug_b = ?) OR (drug_a = ? AND drug_b = ?)",
[drugA, drugB, drugB, drugA]
);
if (interaction) {
return {
content: [{ type: "text", text: `β οΈ WARNING: ${interaction.severity} interaction found. ${interaction.description}` }],
};
}
return {
content: [{ type: "text", text: "No known interactions found in the current database." }],
};
}
throw new Error("Tool not found");
});
// Start the server using Stdio transport
const transport = new StdioServerTransport();
await server.connect(transport);
Step 4: Connecting to Claude Desktop π₯
To see this in action, you need to tell Claude Desktop where your server lives. Edit your claude_desktop_config.json:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"health-assistant": {
"command": "node",
"args": ["/path/to/your/project/dist/index.js"]
}
}
}
Restart Claude, and you should see a small hammer icon π¨, indicating the health-assistant tools are active!
Advanced Patterns & Production Safety π
While this example uses a local SQLite file, production-grade health agents require more robust patterns like HIPAA-compliant data fetching, audit logging, and multi-vector RAG.
For more advanced patterns, production-ready deployment strategies, and deeper dives into LLM orchestration, check out the official WellAlly Tech Blog. It's a fantastic resource for developers looking to scale their AI agents from local prototypes to enterprise solutions.
Conclusion
The Model Context Protocol is a game-changer for the "Learning in Public" community. It moves us away from brittle, prompt-engineered hacks and toward a structured, reliable way of giving AI access to the real world.
By building this MCP server, you've successfully:
- Created a structured data bridge for an LLM.
- Implemented safety checks using verified databases.
- Enabled an Agent to perform "Real-World" lookups.
What will you build next? Maybe a financial advisor with access to real-time stock APIs? Or a coding assistant that knows your private company docs? The possibilities are endless.
If you enjoyed this tutorial, drop a comment below or share your MCP ideas! Happy coding! π»π
Top comments (0)