Clinia
Tutorials

Connect an AI Agent

Configure an MCP client, give it access to a patient via the four VFS tools, and walk through a real clinical question end-to-end.

Connect an AI Agent

In this tutorial you will connect a language model to Patient Memory using the Model Context Protocol (MCP). By the end, the agent will be able to browse a patient's conditions, read a condition story, and answer a clinical question that requires traversing relationships across the graph.

Time: ~20 minutes.

Prerequisites

  • A patient already ingested. Complete Ingest and Query a Patient first.
  • Node.js 18+.
  • An Anthropic API key and a Patient Memory access token set in your environment:
export ANTHROPIC_API_KEY="your-anthropic-key"
export CLINIA_ACCESS_TOKEN="your-access-token"   # obtained via OAuth client credentials

See Manage Credentials to obtain a Patient Memory access token.

Replace <workspace-id> with your Patient Memory workspace ID in the code below.

Steps


Install dependencies

npm install @modelcontextprotocol/sdk @anthropic-ai/sdk

Connect the MCP client

Patient Memory exposes an MCP endpoint at /mcp over HTTP with SSE streaming. Connect to it and confirm the four tools are available:

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";

const transport = new SSEClientTransport(new URL("https://<workspace-id>.w.clinia.cloud/mcp"), {
  requestInit: {
    headers: { Authorization: `Bearer ${process.env.CLINIA_ACCESS_TOKEN}` },
  },
});

const mcp = new Client({ name: "clinical-agent", version: "1.0.0" });
await mcp.connect(transport);

const { tools } = await mcp.listTools();
console.log(tools.map((t) => t.name));
// → ["browse_patient", "read_patient", "search_patient", "get_patient_info"]

The {id} segment in VFS paths is the VFS patient ID — the patient.id extracted from the ingested record. It may differ from the registry key used at ingest time. Use get_patient_info to look it up if needed.

Wire the tools to Claude

Pass the MCP tools directly to the Anthropic SDK and let Claude decide when to call them:

import Anthropic from "@anthropic-ai/sdk";

const anthropic = new Anthropic();

const mcpTools = tools.map((t) => ({
  name: t.name,
  description: t.description ?? "",
  input_schema: t.inputSchema,
}));

async function runAgent(question: string): Promise<string> {
  const messages: Anthropic.MessageParam[] = [{ role: "user", content: question }];

  while (true) {
    const response = await anthropic.messages.create({
      model: "claude-sonnet-4-6",
      max_tokens: 4096,
      tools: mcpTools,
      messages,
    });

    if (response.stop_reason === "end_turn") {
      return response.content
        .filter((b) => b.type === "text")
        .map((b) => b.text)
        .join("");
    }

    const toolUses = response.content.filter((b) => b.type === "tool_use");
    if (toolUses.length === 0) break;

    const toolResults = await Promise.all(
      toolUses.map(async (use) => {
        if (use.type !== "tool_use") return null;
        const result = await mcp.callTool({
          name: use.name,
          arguments: use.input as Record<string, unknown>,
        });
        return {
          type: "tool_result" as const,
          tool_use_id: use.id,
          content: result.content
            .filter((c) => c.type === "text")
            .map((c) => c.text)
            .join("\n"),
        };
      }),
    );

    messages.push({ role: "assistant", content: response.content });
    messages.push({
      role: "user",
      content: toolResults.filter(Boolean) as Anthropic.ToolResultBlockParam[],
    });
  }

  return "";
}

Run a clinical question

const answer = await runAgent(
  "For patient jeanne-tremblay: given the recent acute COPD exacerbation, " +
    "is the current inhaler regimen appropriate and is bone health being monitored?",
);

console.log(answer);
await mcp.close();

The agent typically calls tools in this sequence:

  1. browse_patient on /patient/jeanne-72f-copd/conditions/active to discover available conditions
  2. read_patient on chronic_obstructive_lung_disease/_story.md — the COPD story includes the acute exacerbation, current inhaler regimen, and spirometry trends
  3. Optionally read_patient on osteopenia/_story.md — surfaces the ICS exposure as a contributing factor and the pending DEXA follow-up
  4. Synthesises the answer from the graph data

The agent reaches this conclusion without any application code that knows about ICS-related bone loss. It discovers the relationship from the condition stories, which were assembled by the pipeline from the clinical knowledge base.


Next steps

On this page