Emergency Medicare Planner MCP Server
by manolaz
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
Tool,
} from "@modelcontextprotocol/sdk/types.js";
import { Client as GoogleMapsClient } from "@googlemaps/google-maps-services-js";
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";
// Add chalk import for formatted output
import chalk from 'chalk';
// Initialize Google Maps client
const googleMapsClient = new GoogleMapsClient({});
// Define ThoughtData interface for sequential thinking
interface ThoughtData {
thought: string;
thoughtNumber: number;
totalThoughts: number;
isRevision?: boolean;
revisesThought?: number;
branchFromThought?: number;
branchId?: string;
needsMoreThoughts?: boolean;
nextThoughtNeeded: boolean;
}
// Sequential Thinking class implementation
class SequentialThinkingServer {
private thoughtHistory: ThoughtData[] = [];
private branches: Record<string, ThoughtData[]> = {};
private validateThoughtData(input: unknown): ThoughtData {
const data = input as Record<string, unknown>;
if (!data.thought || typeof data.thought !== 'string') {
throw new Error('Invalid thought: must be a string');
}
if (!data.thoughtNumber || typeof data.thoughtNumber !== 'number') {
throw new Error('Invalid thoughtNumber: must be a number');
}
if (!data.totalThoughts || typeof data.totalThoughts !== 'number') {
throw new Error('Invalid totalThoughts: must be a number');
}
if (typeof data.nextThoughtNeeded !== 'boolean') {
throw new Error('Invalid nextThoughtNeeded: must be a boolean');
}
return {
thought: data.thought,
thoughtNumber: data.thoughtNumber,
totalThoughts: data.totalThoughts,
nextThoughtNeeded: data.nextThoughtNeeded,
isRevision: data.isRevision as boolean | undefined,
revisesThought: data.revisesThought as number | undefined,
branchFromThought: data.branchFromThought as number | undefined,
branchId: data.branchId as string | undefined,
needsMoreThoughts: data.needsMoreThoughts as boolean | undefined,
};
}
private formatThought(thoughtData: ThoughtData): string {
const { thoughtNumber, totalThoughts, thought, isRevision, revisesThought, branchFromThought, branchId } = thoughtData;
let prefix = '';
let context = '';
if (isRevision) {
prefix = chalk.yellow('š Revision');
context = ` (revising thought ${revisesThought})`;
} else if (branchFromThought) {
prefix = chalk.green('šæ Branch');
context = ` (from thought ${branchFromThought}, ID: ${branchId})`;
} else {
prefix = chalk.blue('š Thought');
context = '';
}
const header = `${prefix} ${thoughtNumber}/${totalThoughts}${context}`;
const border = 'ā'.repeat(Math.max(header.length, thought.length) + 4);
return `
ā${border}ā
ā ${header} ā
ā${border}ā¤
ā ${thought.padEnd(border.length - 2)} ā
ā${border}ā`;
}
public processThought(input: unknown): { content: Array<{ type: string; text: string }>; isError?: boolean } {
try {
const validatedInput = this.validateThoughtData(input);
if (validatedInput.thoughtNumber > validatedInput.totalThoughts) {
validatedInput.totalThoughts = validatedInput.thoughtNumber;
}
this.thoughtHistory.push(validatedInput);
if (validatedInput.branchFromThought && validatedInput.branchId) {
if (!this.branches[validatedInput.branchId]) {
this.branches[validatedInput.branchId] = [];
}
this.branches[validatedInput.branchId].push(validatedInput);
}
const formattedThought = this.formatThought(validatedInput);
console.error(formattedThought);
return {
content: [{
type: "text",
text: JSON.stringify({
thoughtNumber: validatedInput.thoughtNumber,
totalThoughts: validatedInput.totalThoughts,
nextThoughtNeeded: validatedInput.nextThoughtNeeded,
branches: Object.keys(this.branches),
thoughtHistoryLength: this.thoughtHistory.length
}, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: JSON.stringify({
error: error instanceof Error ? error.message : String(error),
status: 'failed'
}, null, 2)
}],
isError: true
};
}
}
}
// Define the sequential thinking tool
const SEQUENTIAL_THINKING_TOOL: Tool = {
name: "sequentialthinking",
description: `A detailed tool for dynamic and reflective medical problem-solving through thoughts.
This tool helps analyze medical problems through a flexible thinking process that can adapt and evolve.
Each thought can build on, question, or revise previous insights as understanding of the medical situation deepens.
When to use this tool:
- Breaking down complex medical problems into steps
- Planning and designing treatment approaches with room for revision
- Clinical analysis that might need course correction
- Medical problems where the full scope might not be clear initially
- Healthcare decisions that require a multi-step solution
- Medical evaluations that need to maintain context over multiple steps
- Situations where irrelevant medical information needs to be filtered out
Key features:
- You can adjust total_thoughts up or down as the diagnosis progresses
- You can question or revise previous medical assessments
- You can add more diagnostic thoughts as new information emerges
- You can express clinical uncertainty and explore alternative approaches
- Not every medical assessment needs to build linearly - you can branch or backtrack
- Generates a clinical hypothesis
- Verifies the hypothesis based on the Chain of Thought steps
- Repeats the process until a satisfactory diagnosis or treatment plan is reached
- Provides a correct medical assessment or recommendation`,
inputSchema: {
type: "object",
properties: {
thought: {
type: "string",
description: "Your current clinical thinking step"
},
nextThoughtNeeded: {
type: "boolean",
description: "Whether another medical assessment step is needed"
},
thoughtNumber: {
type: "integer",
description: "Current thought number",
minimum: 1
},
totalThoughts: {
type: "integer",
description: "Estimated total thoughts needed for complete evaluation",
minimum: 1
},
isRevision: {
type: "boolean",
description: "Whether this revises previous medical thinking"
},
revisesThought: {
type: "integer",
description: "Which medical assessment is being reconsidered",
minimum: 1
},
branchFromThought: {
type: "integer",
description: "Branching point thought number for alternative diagnosis",
minimum: 1
},
branchId: {
type: "string",
description: "Branch identifier for the diagnostic path"
},
needsMoreThoughts: {
type: "boolean",
description: "If more clinical evaluation is needed"
}
},
required: ["thought", "nextThoughtNeeded", "thoughtNumber", "totalThoughts"]
}
};
// Initialize the thinking server
const thinkingServer = new SequentialThinkingServer();
// Schema definitions
const FindNearbyMedicalFacilitiesSchema = z.object({
userLocation: z.string().describe("User's current location (address or coordinates)"),
radius: z.number().default(10000).describe("Search radius in meters (default: 10000m = 10km)"),
treatmentNeeds: z.array(z.string()).optional().describe("Specific medical treatments or services needed"),
careQuality: z.enum(["high", "medium", "any"]).optional().describe("Expected quality of medical care"),
priceRange: z.enum(["low", "moderate", "high", "any"]).optional().describe("Price range preference"),
facilities: z.array(z.enum(["hospital", "clinic", "emergency", "pharmacy", "specialist"])).optional()
.describe("Types of medical facilities to search for"),
infrastructure: z.enum(["excellent", "good", "any"]).optional().describe("Quality of infrastructure and cleanliness"),
});
const CheckMedicareCoverageSchema = z.object({
treatmentCode: z.string().describe("Medicare treatment or procedure code"),
state: z.string().describe("US State code (e.g., CA, NY)"),
insuranceType: z.string().optional().describe("Type of Medicare insurance (e.g., Part A, Part B)"),
});
const GetEmergencyContactsSchema = z.object({
location: z.string().describe("Location to get emergency contacts for"),
serviceType: z.array(z.string()).optional().describe("Types of emergency services needed"),
});
const ScheduleEmergencyTransportSchema = z.object({
patientLocation: z.string().describe("Patient's current location"),
destination: z.string().optional().describe("Destination hospital or clinic"),
medicalCondition: z.string().describe("Brief description of medical condition"),
urgency: z.enum(["critical", "urgent", "standard"]).describe("Level of urgency"),
});
// Server implementation
const server = new Server(
{
name: "emergency-medicare-planner",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
},
);
// Tool handlers
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "find_nearby_medical_facilities",
description: "Finds hospitals and clinics nearby user location that match specific requirements",
inputSchema: zodToJsonSchema(FindNearbyMedicalFacilitiesSchema),
},
{
name: "check_medicare_coverage",
description: "Checks what treatments and procedures are covered by Medicare",
inputSchema: zodToJsonSchema(CheckMedicareCoverageSchema),
},
{
name: "get_emergency_contacts",
description: "Retrieves emergency contact information for a specific location",
inputSchema: zodToJsonSchema(GetEmergencyContactsSchema),
},
{
name: "schedule_emergency_transport",
description: "Arranges emergency medical transportation",
inputSchema: zodToJsonSchema(ScheduleEmergencyTransportSchema),
},
// Add the sequential thinking tool
SEQUENTIAL_THINKING_TOOL,
],
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
// Handle sequential thinking tool
if (name === "sequentialthinking") {
return thinkingServer.processThought(args);
}
switch (name) {
case "find_nearby_medical_facilities": {
const validatedArgs = FindNearbyMedicalFacilitiesSchema.parse(args);
// Mock implementation - in a real scenario this would call the Google Maps API
const mockFacilities = [
{
name: "General Hospital",
address: "123 Main St, Cityville",
distance: "2.3 km",
type: "hospital",
treatmentsAvailable: ["emergency", "surgery", "cardiology"],
careQuality: "high",
priceRange: "moderate",
infrastructure: "excellent"
},
{
name: "Community Clinic",
address: "456 Oak Ave, Cityville",
distance: "3.8 km",
type: "clinic",
treatmentsAvailable: ["general practice", "pediatrics"],
careQuality: "medium",
priceRange: "low",
infrastructure: "good"
},
{
name: "Specialized Medical Center",
address: "789 Pine St, Cityville",
distance: "5.1 km",
type: "specialist",
treatmentsAvailable: ["oncology", "neurology"],
careQuality: "high",
priceRange: "high",
infrastructure: "excellent"
}
];
// Filter facilities based on user criteria
let facilities = mockFacilities;
// Filter by treatment needs if specified
if (validatedArgs.treatmentNeeds && validatedArgs.treatmentNeeds.length > 0) {
facilities = facilities.filter(facility =>
validatedArgs.treatmentNeeds!.some(treatment =>
facility.treatmentsAvailable.includes(treatment)
)
);
}
// Filter by care quality if specified
if (validatedArgs.careQuality && validatedArgs.careQuality !== "any") {
facilities = facilities.filter(facility =>
facility.careQuality === validatedArgs.careQuality ||
(validatedArgs.careQuality === "high" && facility.careQuality === "high") ||
(validatedArgs.careQuality === "medium" && ["medium", "high"].includes(facility.careQuality))
);
}
// Filter by price range if specified
if (validatedArgs.priceRange && validatedArgs.priceRange !== "any") {
facilities = facilities.filter(facility => facility.priceRange === validatedArgs.priceRange);
}
// Filter by facility type if specified
if (validatedArgs.facilities && validatedArgs.facilities.length > 0) {
facilities = facilities.filter(facility =>
validatedArgs.facilities!.includes(facility.type as any)
);
}
// Filter by infrastructure quality if specified
if (validatedArgs.infrastructure && validatedArgs.infrastructure !== "any") {
facilities = facilities.filter(facility =>
facility.infrastructure === validatedArgs.infrastructure ||
(validatedArgs.infrastructure === "good" && ["good", "excellent"].includes(facility.infrastructure))
);
}
return {
content: [
{
type: "text",
text: `Found ${facilities.length} medical facilities near ${validatedArgs.userLocation} within ${validatedArgs.radius/1000} km:\n\n` +
facilities.map(f =>
`- ${f.name} (${f.type})\n Address: ${f.address}\n Distance: ${f.distance}\n` +
` Treatments: ${f.treatmentsAvailable.join(", ")}\n` +
` Quality: ${f.careQuality}, Price: ${f.priceRange}, Infrastructure: ${f.infrastructure}`
).join("\n\n")
},
],
};
}
case "check_medicare_coverage": {
const validatedArgs = CheckMedicareCoverageSchema.parse(args);
// Mock implementation - would connect to Medicare database in real implementation
return {
content: [
{
type: "text",
text: `Medicare coverage for ${validatedArgs.treatmentCode} in ${validatedArgs.state}:\n` +
`Coverage Type: ${validatedArgs.insuranceType || "Standard"}\n` +
`Coverage Status: Covered\n` +
`Co-pay: $25\n` +
`Deductible: Applies\n` +
`Special Requirements: Prior authorization needed`,
},
],
};
}
case "get_emergency_contacts": {
const validatedArgs = GetEmergencyContactsSchema.parse(args);
return {
content: [
{
type: "text",
text: `Emergency contacts for ${validatedArgs.location}:\n` +
`Emergency Services: 911\n` +
`Nearest Hospital: General Hospital - (555) 123-4567\n` +
`Poison Control: (800) 222-1222\n` +
`Medicare Hotline: 1-800-MEDICARE`,
},
],
};
}
case "schedule_emergency_transport": {
const validatedArgs = ScheduleEmergencyTransportSchema.parse(args);
return {
content: [
{
type: "text",
text: `Emergency transport scheduled from ${validatedArgs.patientLocation}\n` +
`${validatedArgs.destination ? `To: ${validatedArgs.destination}\n` : ''}` +
`Urgency: ${validatedArgs.urgency}\n` +
`Condition: ${validatedArgs.medicalCondition}\n` +
`ETA: 12 minutes\n` +
`Transport ID: EMT-${Math.floor(Math.random() * 10000)}\n` +
`Please stand by and keep patient stable.`,
},
],
};
}
default:
throw new Error(`Unknown tool: ${name}`);
}
} catch (error) {
return {
content: [
{
type: "text",
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
},
],
isError: true,
};
}
});
// Start server
async function runServer() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Emergency Medicare Planner MCP Server running on stdio");
}
runServer().catch((error) => {
console.error("Fatal error running server:", error);
process.exit(1);
});