Skip to main content
Glama
yasserkh2

Andalusia AI MCP Server

by yasserkh2

Andalusia AI MCP Server

This MCP server exposes six Andalusia AI hospital metric tools to Claude:

  1. resolve_entities

  2. search_members

  3. query_cube

  4. get_measure_definition

  5. resolve_why_question

  6. execute_investigation

It is a thin wrapper around two FastAPI services:

  • Librarian: http://197.164.100.18:18080

  • Tools: http://197.164.100.18:28081

  • Investigator: http://197.164.100.18:28082

This repository itself is not Dockerized. In production, this MCP service runs as a Node.js process under systemd. The Andalusia backend services it calls may run separately in Docker on the same server. For example, on the current host:

MCP service                 systemd: andalusia-ai-mcp.service
Librarian backend           Docker: librarian-resolver-1
Tools backend               Docker: claude_project-tools-1
Librarian vector database   Docker: librarian-qdrant-1

When debugging a tool failure, check both MCP logs and the relevant backend container logs.

Backend calls use the shared X-Librarian-Secret header. Keep the real secret in .env; do not commit it. Non-secret app settings live in config.yml. Tool descriptions live in tool-descriptions.yml.

Project Layout

config.yml                  Non-secret runtime settings
.env                        Secrets only
tool-descriptions.yml       Editable Claude-facing tool guidance
src/config.ts               Loads config.yml plus env overrides
src/toolDescriptions.ts     Loads tool-descriptions.yml
src/andalusiaClient.ts      Backend API client
src/mcpServer.ts            Creates the MCP server and registers tools
src/tools/index.ts          Tool registry: controls which tools are enabled
src/tools/*.ts              One file per MCP tool

Related MCP server: FHIR MCP Server

Setup

npm install
cp config.example.yml config.yml
cp .env.example .env
npm run build

Edit config.yml for non-secret settings:

andalusiaLibrarianBaseUrl: http://197.164.100.18:18080
andalusiaToolsBaseUrl: http://197.164.100.18:28081
andalusiaInvestigatorBaseUrl: http://197.164.100.18:28082
andalusiaRequestTimeoutMs: 100000
andalusiaInvestigatorRequestTimeoutMs: 500000
monitoringEventLogFile: /var/log/andalusia-ai-mcp/monitoring-events.jsonl
monitoringEventLogMaxBytes: 52428800
monitoringEventLogReplayMaxBytes: 10485760
monitoringEventLogReplayMaxEvents: 10000
monitoringEventLogReplayMaxAgeMs: 86400000
monitoringMaxMetricBuckets: 200
oauthStateFile: /var/lib/andalusia-ai-mcp/oauth-state.json
oauthMaxClients: 1000
oauthMaxAuthorizationCodes: 1000
oauthMaxAccessTokens: 5000
oauthMaxRefreshTokens: 5000

Edit .env for secrets:

ANDALUSIA_LIBRARIAN_SECRET=your-shared-secret

Environment variables still override config.yml, which is useful for one-off local runs and deployment systems.

Adding Tools

Each MCP tool lives in its own file under src/tools/ and implements the AndalusiaTool interface from src/tools/types.ts.

To add a tool:

  1. Add the backend method to src/andalusiaClient.ts.

  2. Add a new src/tools/<toolName>.ts file with its name, title, descriptionKey, inputSchema, and handler.

  3. Add the tool factory to src/tools/index.ts. This is the registry that decides which tools the MCP server exposes.

  4. Add matching editable guidance to tool-descriptions.yml.

Example registry entry:

export function createAndalusiaTools(context: ToolContext): AnyAndalusiaTool[] {
  return [
    createResolveEntitiesTool(context),
    createSearchMembersTool(context),
    createQueryCubeTool(context),
    createGetMeasureDefinitionTool(context),
    createResolveWhyQuestionTool(context),
    createExecuteInvestigationTool(context)
  ];
}

If a tool is not added to src/tools/index.ts, it will not be registered with the MCP server even if its file exists.

Guides

HTTP Mode

MCP_TRANSPORT=http MCP_HTTP_PORT=3000 npm start

Then connect Claude with Streamable HTTP:

claude mcp add --transport http andalusia-ai http://127.0.0.1:3000/mcp

For hosted Claude connector usage, set MCP_PUBLIC_URL to the public /mcp URL and set MCP_HTTP_BEARER_TOKEN. Claude discovers OAuth metadata, registers dynamically, and prompts for that token as the connector code during sign-in.

Production Monitoring Dashboard

HTTP mode exposes built-in production monitoring endpoints:

  • Production dashboard: https://ai-mcp.andalusiagroup.net/dashboard

  • /dashboard - human-facing browser UI for production monitoring. Open this page to inspect MCP usage, tool calls, latency, failures, uptime, memory, backend configuration, JSON-RPC methods, and recent errors.

  • /dashboard/metrics - JSON API used by the dashboard. Use this endpoint for debugging, scripts, smoke tests, or custom internal tools that need the full structured monitoring snapshot.

  • /metrics - Prometheus-compatible text endpoint. Use this endpoint as the scrape target for Prometheus, Grafana, Datadog agents, or other monitoring systems.

  • /ready - readiness check for required Andalusia backend configuration.

Quick reference:

/dashboard          Browser UI for humans
/dashboard/metrics  JSON monitoring snapshot for the dashboard and scripts
/metrics            Prometheus-compatible metrics for monitoring platforms
/ready              Readiness check for deploy/load-balancer health gates

Application logs are written to stdout/stderr and captured by systemd. Enable persistent journald storage on the production host so journalctl logs survive reboots:

sudo install -d -m 2755 -o root -g systemd-journal /var/log/journal
sudo systemctl restart systemd-journald

When MCP_HTTP_BEARER_TOKEN is configured, /dashboard/metrics and /metrics require the same bearer token:

curl -H "Authorization: Bearer $MCP_HTTP_BEARER_TOKEN" \
  https://ai-mcp.andalusiagroup.net/dashboard/metrics

curl -H "Authorization: Bearer $MCP_HTTP_BEARER_TOKEN" \
  https://ai-mcp.andalusiagroup.net/metrics

The dashboard keeps bounded working counters in memory. On startup, it replays recent bounded history from the JSONL event log, then continues with live updates. When monitoringEventLogFile is configured, each HTTP request, tool call, and server error is also appended to a bounded JSONL event log. The file rotates to .1 when it reaches monitoringEventLogMaxBytes.

The dashboard does not depend only on the current process runtime state. Runtime counters are used while the service is running, but after a restart the dashboard rebuilds recent monitoring results from the persistent JSONL event log on disk before continuing with live updates.

Read the live monitoring event log:

sudo tail -f /var/log/andalusia-ai-mcp/monitoring-events.jsonl

Read recent monitoring events:

sudo tail -n 100 /var/log/andalusia-ai-mcp/monitoring-events.jsonl

Pretty-print recent JSONL events:

sudo tail -n 50 /var/log/andalusia-ai-mcp/monitoring-events.jsonl | jq

Read the previous rotated monitoring log:

sudo tail -n 100 /var/log/andalusia-ai-mcp/monitoring-events.jsonl.1

Read application stdout/stderr logs captured by systemd:

sudo journalctl -u andalusia-ai-mcp -n 200
sudo journalctl -u andalusia-ai-mcp -f
sudo journalctl -u andalusia-ai-mcp --since today

For long-term retention and alerting, scrape /metrics with your monitoring platform and alert on server errors, tool failures, high P95 latency, missing readiness checks, and process restarts. Use the JSONL event log or journald for forensics and recent dashboard replay after process restarts.

Update needed: add an explicit retention policy for filesystem logs. The MCP monitoring event log currently rotates by size, not by age. For normal operations, keep raw monitoring and journald logs for about 30 days unless a compliance requirement says otherwise. A production logrotate policy can be used for the JSONL file, for example:

/var/log/andalusia-ai-mcp/monitoring-events.jsonl {
  daily
  rotate 30
  compress
  missingok
  notifempty
  copytruncate
  create 0640 ai ai
}

Also configure journald retention on the host, for example with MaxRetentionSec=30day in journald.conf if that matches the server policy.

OAuth clients, access tokens, and refresh tokens can be persisted with oauthStateFile. The service still keeps bounded working maps in memory while running, but the configured caps prevent unbounded growth and the state file allows Claude connector sessions to survive process restarts.

Tool usage rows are driven by the MCP tool registry in src/tools/index.ts. When a new tool is added to createAndalusiaTools, the dashboard automatically includes it and starts its call count at 0 after the service restarts.

Troubleshooting Backend Failures

The MCP durable monitoring log shows MCP-level failures:

sudo jq -r 'select(.type=="tool_call" and .success==false) | [.ts, .toolName, .durationMs, .error] | @tsv' \
  /var/log/andalusia-ai-mcp/monitoring-events.jsonl

Example:

resolve_entities  librarian POST /resolve_entities failed with HTTP 500

That means the MCP service called the Librarian backend and the backend returned HTTP 500. To find the root cause, check the Librarian Docker logs:

docker logs librarian-resolver-1 2>&1 | rg -C 5 '402 Payment Required|openrouter.ai/api/v1/embeddings|HTTPStatusError'

If Docker requires sudo:

sudo docker logs librarian-resolver-1 2>&1 | grep -C 5 "402 Payment Required"

A known failure is:

httpx.HTTPStatusError: Client error '402 Payment Required'
for url 'https://openrouter.ai/api/v1/embeddings'

This is not a Claude quota error. It means the Librarian backend's OpenRouter embedding provider returned 402 Payment Required, usually because OpenRouter billing/credits/API-key access needs attention.

Publish To Production

Run from the project directory on the production host:

cd /home/ai/Workspace/yasser/Claude_Mcp
npm run typecheck
npm run build

Install the non-secret runtime files:

sudo install -d -m 750 -o root -g ai /etc/andalusia-ai-mcp
sudo install -d -m 750 -o ai -g ai /var/lib/andalusia-ai-mcp
sudo install -d -m 750 -o ai -g ai /var/log/andalusia-ai-mcp
sudo install -m 640 -o root -g ai config.yml /etc/andalusia-ai-mcp/config.yml
sudo install -m 640 -o root -g ai tool-descriptions.yml /etc/andalusia-ai-mcp/tool-descriptions.yml

Do not overwrite the production env file unless secrets changed. It should already contain:

MCP_HTTP_BEARER_TOKEN=your-connector-code
ANDALUSIA_LIBRARIAN_SECRET=your-shared-secret

Install service/proxy config and restart:

sudo cp deploy/andalusia-ai-mcp.service /etc/systemd/system/andalusia-ai-mcp.service
sudo cp deploy/andalusia-ai-mcp.nginx.conf /etc/nginx/conf.d/andalusia-ai-mcp.conf
sudo systemctl daemon-reload
sudo nginx -t
sudo systemctl reload nginx
sudo systemctl reset-failed andalusia-ai-mcp
sudo systemctl restart andalusia-ai-mcp
sudo systemctl status andalusia-ai-mcp --no-pager -l

Verify:

curl -sS http://127.0.0.1:3001/health
curl -i https://ai-mcp.andalusiagroup.net/health
curl -i https://ai-mcp.andalusiagroup.net/.well-known/oauth-authorization-server

Expected health response:

{"ok":true,"name":"andalusia-ai-mcp"}

Public HTTPS

For https://ai-mcp.andalusiagroup.net/mcp, run the Node server privately on 127.0.0.1:3001 and let Nginx terminate TLS on port 443.

This repo is configured to use the existing *.andalusiagroup.net wildcard certificate:

/etc/nginx/certs/fullchain.crt
/etc/nginx/certs/private.key

Install the wildcard certificate and Nginx config on the server. On this host, Docker already owns port 80, so the systemd Nginx config is 443-only and does not provide an HTTP-to-HTTPS redirect.

sudo dnf install -y nginx
command -v nginx
sudo systemctl enable --now nginx

sudo install -d -m 700 -o root -g root /etc/nginx/certs
sudo install -m 644 /path/to/wildcard/fullchain.crt /etc/nginx/certs/fullchain.crt
sudo install -m 600 /path/to/wildcard/private.key /etc/nginx/certs/private.key

sudo cp deploy/andalusia-ai-mcp.nginx.conf /etc/nginx/conf.d/andalusia-ai-mcp.conf
sudo nginx -t
sudo systemctl reload nginx

If command -v nginx prints nothing or nginx.service does not exist, Nginx is not installed on the host. Install it first, then rerun the Nginx commands. If install: cannot stat ... appears, replace /path/to/wildcard/... with the real certificate and private key paths. Files under /etc/pki/ca-trust/... are system CA bundles, not the *.andalusiagroup.net server certificate or its private key. If nginx fails with bind() to 0.0.0.0:80 failed and ss shows docker-proxy, port 80 is already handled by a Docker container. In that case, remove the package default listen 80 server from /etc/nginx/nginx.conf and run systemd Nginx as a 443-only TLS proxy, or stop the container before using systemd Nginx on both ports.

Verify locally and over HTTPS:

curl http://127.0.0.1:3001/health
curl -vk https://ai-mcp.andalusiagroup.net/mcp

For the full production checklist, including systemd and bearer-token setup, see Deploy on HTTPS 443.

Maintenance

ActivityMaintained
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables interaction with a comprehensive healthcare management system through FastAPI, supporting operations for patients, doctors, appointments, medical records, telemedicine, lab orders, prescriptions, insurance claims, and more with JWT authentication.
    5
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    Provides seamless integration with FHIR APIs, enabling AI/LLM tools to search, retrieve, and analyze clinical healthcare data with support for SMART-on-FHIR authentication and multiple transport protocols.
    7
    134
    Apache 2.0
  • F
    license
    A
    quality
    C
    maintenance
    Enables interaction with synthetic NIH-style clinical research data through tools for searching publications, querying patient metadata, analyzing AAA measurements, and retrieving protocol guidance.
    5

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/yasserkh2/Claude_Mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server