{
  "slug": "io-github-wso2-fhir-mcp-server",
  "name": "io.github.wso2/fhir-mcp-server",
  "title": "FHIR MCP Server",
  "description": "MCP server providing seamless access to FHIR APIs for AI tools and healthcare applications",
  "version": "0.10.0",
  "publishedAt": "2026-03-31T07:11:21.613312Z",
  "updatedAt": "2026-03-31T07:11:21.613312Z",
  "repoUrl": "https://github.com/wso2/fhir-mcp-server",
  "websiteUrl": "https://github.com/wso2/fhir-mcp-server",
  "namespace": {
    "type": "github",
    "owner": "wso2"
  },
  "kind": "local",
  "packages": [
    {
      "registryType": "pypi",
      "identifier": "fhir-mcp-server",
      "version": "0.10.0",
      "transport": "stdio",
      "runtimeHint": "uvx"
    }
  ],
  "remotes": [],
  "envVars": [
    {
      "name": "FHIR_SERVER_BASE_URL",
      "isSecret": false,
      "isRequired": true,
      "description": "Base URL of the FHIR Server (eg: https://hapi.fhir.org/baseR5)"
    }
  ],
  "category": "health",
  "subcategory": "medical",
  "company": "",
  "categoryAuto": true,
  "checks": [
    {
      "id": "source-linked",
      "label": "Source repository linked",
      "result": "pass",
      "detail": "https://github.com/wso2/fhir-mcp-server"
    },
    {
      "id": "version-pinned",
      "label": "Version pinned",
      "result": "pass",
      "detail": "Every package has an explicit version"
    },
    {
      "id": "secrets-requested",
      "label": "Secrets requested",
      "result": "info",
      "detail": "Asks for no secrets"
    },
    {
      "id": "known-registry",
      "label": "Known package registry",
      "result": "pass",
      "detail": "All packages come from npm, PyPI, OCI, NuGet or MCPB"
    },
    {
      "id": "namespace",
      "label": "Namespace ownership",
      "result": "info",
      "detail": "Name verified via GitHub account wso2"
    },
    {
      "id": "meta-text",
      "label": "Registry text aimed at the AI",
      "result": "pass",
      "detail": "Description and settings contain no hidden characters or instructions to the AI"
    },
    {
      "id": "code-flags",
      "label": "Code red flags",
      "result": "info",
      "detail": "Package could not be read (too large)"
    }
  ],
  "scannedAt": "2026-10-29",
  "level": "Scanned",
  "install": [
    {
      "label": "Claude Desktop (pypi)",
      "kind": "claude_desktop_json",
      "text": "{\n  \"mcpServers\": {\n    \"fhir-mcp-server\": {\n      \"command\": \"uvx\",\n      \"args\": [\n        \"fhir-mcp-server==0.10.0\"\n      ],\n      \"env\": {\n        \"FHIR_SERVER_BASE_URL\": \"<your value>\"\n      }\n    }\n  }\n}"
    }
  ],
  "curated": true,
  "stars": 137,
  "archived": false,
  "pushedAt": "2026-10-12T09:41:28Z",
  "review": {
    "package": "fhir-mcp-server",
    "version": "0.10.0",
    "at": "2026-10-31",
    "files": 8,
    "lines": 1741,
    "verdict": "pass-with-notes",
    "summary": "Connects your AI to a FHIR health-records server you configure, so it can search, read, create, update and delete patient records. It sends data only to that FHIR server and its login service. Your OAuth tokens and client secret stay in memory and go nowhere else. Watch out: it can change and permanently delete medical records without a confirmation step. Debug logging writes tokens and patient data to the console, so leave the log level at INFO.",
    "hosts": [
      {
        "host": "FHIR_SERVER_BASE_URL (the user's FHIR server: /metadata, resource endpoints)",
        "when": "only when a tool is called",
        "sends": "FHIR searches, reads, creates, updates and deletes from tool arguments (health records), with the user's bearer token"
      },
      {
        "host": "FHIR_SERVER_BASE_URL/.well-known/smart-configuration and the authorization/token endpoints it lists",
        "when": "during the OAuth login flow and token exchange (when authorization is enabled, the default)",
        "sends": "authorization code, PKCE verifier, FHIR_SERVER_CLIENT_ID and FHIR_SERVER_CLIENT_SECRET, refresh token"
      },
      {
        "host": "localhost:8000 (listens; FHIR_MCP_HOST/FHIR_MCP_PORT; default transport streamable-http)",
        "when": "the whole time the server runs",
        "sends": "serves MCP plus OAuth endpoints and /oauth/callback; protected by OAuth unless FHIR_SERVER_DISABLE_AUTHORIZATION=true"
      }
    ],
    "env": [
      {
        "name": "FHIR_SERVER_BASE_URL",
        "sent_to": "target FHIR server"
      },
      {
        "name": "FHIR_SERVER_CLIENT_ID",
        "sent_to": "FHIR authorization server (authorize + token endpoints)"
      },
      {
        "name": "FHIR_SERVER_CLIENT_SECRET",
        "sent_to": "FHIR token endpoint only; not logged"
      },
      {
        "name": "FHIR_SERVER_ACCESS_TOKEN",
        "sent_to": "FHIR server as a Bearer token on every request (overrides per-user OAuth tokens)"
      },
      {
        "name": "FHIR_SERVER_SCOPES",
        "sent_to": "FHIR authorization server"
      },
      {
        "name": "FHIR_SERVER_DISABLE_AUTHORIZATION",
        "sent_to": "local: turns off MCP auth"
      },
      {
        "name": "FHIR_MCP_HOST / FHIR_MCP_PORT / FHIR_MCP_SERVER_URL / FHIR_MCP_REQUEST_TIMEOUT",
        "sent_to": "local listener config; server URL is used in the OAuth redirect_uri"
      },
      {
        "name": ".env file in the working directory",
        "sent_to": "read by pydantic-settings for all FHIR_* settings"
      }
    ],
    "local": "No file system reads or writes besides the optional .env file. OAuth clients, auth codes and tokens are held in memory only and lost on restart. Tool input builds request paths on the configured FHIR server only (type/id/operation), with no way to target another host.",
    "exec": "None. No subprocess, eval or shell.",
    "telemetry": "None.",
    "tools": [
      "get_capabilities",
      "search",
      "read",
      "create",
      "update",
      "delete",
      "get_user"
    ],
    "toolText": "clean. get_capabilities says it 'MUST always be invoked before' other operations. That is workflow guidance pointing to this server's own tool, not hidden or cross-server instructions.",
    "deps": [
      "mcp",
      "click",
      "fhirpy",
      "aiohttp",
      "httpx",
      "pydantic",
      "pydantic-settings",
      "starlette",
      "typing_extensions"
    ],
    "findings": [
      {
        "severity": "note",
        "at": "fhir_mcp_server/server.py:410-695",
        "what": "create, update and delete write to or remove live health records. delete supports conditional deletes by searchParam (may remove several resources) and arbitrary operations such as $expunge (permanent purge). There is no confirmation step and no read-only mode in the server."
      },
      {
        "severity": "note",
        "at": "fhir_mcp_server/oauth/common.py:125-127 + oauth/types.py:207-209",
        "what": "At --log-level DEBUG the full token-endpoint response (access, refresh and ID tokens) is logged, and a malformed ID token is logged verbatim. Tool payloads and FHIR results (patient data) are also logged at DEBUG (server.py:466-467, 561-562). The default is INFO, so this only happens if the user turns on DEBUG."
      },
      {
        "severity": "note",
        "at": "fhir_mcp_server/server.py:58-69,99-105,124-139",
        "what": "With FHIR_SERVER_DISABLE_AUTHORIZATION=true the HTTP endpoint on localhost:8000 has no authentication. Combined with FHIR_SERVER_ACCESS_TOKEN, any local process can read and modify records with that token."
      },
      {
        "severity": "info",
        "at": "fhir_mcp_server/oauth/server_provider.py:75-116,166-215",
        "what": "Proper OAuth proxy: PKCE S256, random state, and MCP-side tokens are opaque random values mapped to upstream tokens in memory. The upstream token is never returned to the MCP client."
      },
      {
        "severity": "info",
        "at": "fhir_mcp_server/oauth/server_provider.py:254-260",
        "what": "Quality bug: refresh exchange sends the RefreshToken object instead of its token string and uses 'scopes' instead of 'scope', so refreshing will likely fail and users must log in again."
      },
      {
        "severity": "info",
        "at": "fhir_mcp_server/server.py:292",
        "what": "Quality: a malformed debug log call passes results as a format argument; harmless."
      }
    ]
  },
  "health": {
    "score": 73,
    "parts": {
      "popularity": 0.49696338176010524,
      "activity": 0.9050927194098854,
      "issues": 0.6666666666666666,
      "security": 1
    },
    "fail": 0,
    "warn": 0,
    "raw": {
      "stars": 137,
      "forks": 59,
      "downloads": 427,
      "hn": {
        "n": 0,
        "pts": 0,
        "t": []
      },
      "issuesOpen": 4,
      "issuesClosed": 8,
      "prsMerged": 38,
      "commits90": 22,
      "release": "2026-10-29T06:42:09Z",
      "pushed": "2026-10-12T09:41:28Z",
      "growth": null,
      "growthDays": null,
      "archived": false
    },
    "at": "2026-10-31T07:42:38.200Z"
  }
}
