Complete Setup Guide

End-to-end guide for deploying suitecrm-mcp v4.x from scratch.

Overview

Auth0 / Azure AD Identity Provider MCP Clients Claude Desktop Claude Code OpenClaw suitecrm-mcp gateway OAuth2 · API keys · SSE SuiteCRM Instances CRM A CRM B CRM X confirms identity (OAuth2) issues API key Bearer token v4_1 REST API

What the gateway does:

  • Handles OAuth2 login (Authorization Code flow)
  • Issues personal, revocable API keys to authenticated users
  • Provisions CRM accounts via SSH on first login (if configured)
  • Proxies MCP tool calls to the appropriate SuiteCRM instance

Steps 1-4: Install

1
Identity Provider

Set up Auth0 or Azure AD before installing the gateway. The installer will prompt for the credentials you collect here.

See Identity Provider Setup for step-by-step instructions. Minimum required:

  • Auth0 domain
  • Auth0 client ID and client secret
  • Redirect URI registered: https://YOUR_GATEWAY_DOMAIN/auth/callback
2
Prepare the Gateway VM

Requirements: Ubuntu 20.04+ (or Debian 11+), public IP or domain, ports 80 and 443 open, SSH access to CRM VMs if using SSH provisioning.

bash
git clone https://github.com/Anirudhx7/suitecrm-mcp.git
cd suitecrm-mcp
3
Configure Entities
bash
cp entities.example.json entities.json

Edit entities.json:

json
{
  "crm1": {
    "label": "Main CRM",
    "endpoint": "https://crm.yourcompany.com/legacy/service/v4_1/rest.php",
    "port": 3101,
    "group": "CRM-Main"
  }
}
  • endpoint - full REST API URL. If unsure, leave as the base URL and the installer auto-detects the correct path.
  • group - the JWT claim value a user must have to access this entity. Must match a role/group in your identity provider.
  • port - each entity gets its own port (3101, 3102, ...). With nginx these are internal-only.

Finding your REST API path:

bash
for path in /service/v4_1/rest.php /legacy/service/v4_1/rest.php /crm/service/v4_1/rest.php; do
  curl -sf -X POST "https://crm.example.com$path" \
    --data-urlencode 'method=get_server_info' \
    --data-urlencode 'input_type=JSON' \
    --data-urlencode 'response_type=JSON' \
    --data-urlencode 'rest_data={}' | python3 -m json.tool && echo "FOUND: $path" && break
done
4
Install the Gateway
bash
sudo python3 install.py \
  --config entities.json \
  --domain mcp.yourcompany.com \
  --email you@yourcompany.com

The installer will: install Node.js, nginx, and certbot; prompt for OAuth2 configuration; generate a random API_KEY_SECRET; write env files to /etc/suitecrm-mcp/; create and start systemd services; configure nginx with entity routing; and obtain a Let's Encrypt certificate.

Non-interactive install (CI/automation):

bash
sudo python3 install.py \
  --config entities.json \
  --domain mcp.yourcompany.com \
  --email you@yourcompany.com \
  --oauth-issuer https://your-tenant.auth0.com \
  --oauth-client-id YOUR_CLIENT_ID \
  --oauth-client-secret YOUR_CLIENT_SECRET \
  --oauth-audience https://your-tenant.auth0.com/api/v2/ \
  --gateway-url https://mcp.yourcompany.com

Steps 5-6: CRM Prep

5
Prepare CRM Accounts (LDAP/SSO users only)

If users authenticate via LDAP or SSO, they have no local SuiteCRM password. The gateway needs a local password to call the REST API on their behalf.

Note: For local SuiteCRM users, ensure API access is enabled in SuiteCRM Admin > User Management > Edit User > Advanced tab > API Access = Yes.

Copy tools/crm-provision-user.sh to each CRM VM and run as root:

bash
# Single user
sudo crm-provision-user alice SecurePass123

# Bulk from CSV (username,password)
sudo crm-provision-user --csv users.csv
6
Configure SSH Provisioning (optional)

Lets the gateway automatically provision CRM accounts when users first log in via OAuth.

bash
# On the gateway VM, generate a key if you don't have one
ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N ""

# Copy to each CRM VM
ssh-copy-id -i /root/.ssh/id_ed25519.pub root@crm1.internal

Create /etc/suitecrm-mcp/crm-hosts.json:

json
{
  "crm1": {
    "ssh_host": "crm1.internal",
    "ssh_user": "ubuntu",
    "ssh_key": "/etc/suitecrm-mcp/crm-ssh-key"
  }
}

Add to the entity env file (/etc/suitecrm-mcp/crm1.env):

env
CRM_HOSTS_FILE=/etc/suitecrm-mcp/crm-hosts.json

Restart: sudo systemctl restart suitecrm-mcp-crm1


Steps 7-8: Test & Connect

7
Test the Auth Flow
bash
# Check services are running
sudo python3 install.py --status

# Test gateway health
curl https://mcp.yourcompany.com/health

# Test auth redirect (should return 302 Location: /auth/login)
curl -I https://mcp.yourcompany.com/

# Test OIDC discovery
curl https://YOUR_DOMAIN/.well-known/openid-configuration

Visit https://mcp.yourcompany.com in a browser and complete the login flow. You should see the success page with your API key.

8
Connect Clients

Admin Operations

Periodic session cleanup

sessions.json accumulates expired entries over time. Safe to run at any time:

bash
# Purge expired sessions
python3 tools/mcp-admin sessions --purge-expired

# Example cron: daily at 2am
echo "0 2 * * * suitecrm-mcp python3 /opt/suitecrm-mcp/tools/mcp-admin sessions --purge-expired" | sudo crontab -

Manage user profiles and API keys

bash
python3 tools/mcp-admin list
python3 tools/mcp-admin whoami --sub <sub>
python3 tools/mcp-admin revoke --sub <sub>

Add a user manually (bypass OAuth)

bash
python3 tools/mcp-admin add --sub <sub> --entity <entity_code> --user <crm_username> --pass <crm_password>

Update server code without reinstalling

bash
git pull
sudo python3 install.py --update --config entities.json --skip-oauth

Add a new entity

bash
# Edit entities.json to add the new entry, then:
sudo python3 install.py --add --config entities.json --skip-oauth

Remove an entity

bash
sudo python3 install.py --remove crm2

Enable HTTPS on an existing plain HTTP install

bash
sudo python3 install.py --config entities.json --domain mcp.yourcompany.com --email you@example.com --skip-oauth

File Layout After Install

filesystem
/opt/suitecrm-mcp/           gateway server code
  index.mjs
  auth.mjs
  package.json
  node_modules/

/etc/suitecrm-mcp/           config and runtime state (mode 700, owned by suitecrm-mcp)
  entities.json              entity list (written by installer)
  auth.env                   env vars for auth service (mode 600)
  crm1.env                   env vars for entity crm1 (mode 600)
  user-profiles.json         per-user API keys and CRM creds (mode 600, written at runtime)
  sessions.json              active gateway sessions (mode 600, written at runtime)
  crm-hosts.json             SSH host map for provisioning (if configured)
  domain                     saved domain for nginx rebuild

/etc/systemd/system/
  suitecrm-mcp-auth.service
  suitecrm-mcp-crm1.service

/etc/nginx/sites-available/
  suitecrm-mcp               nginx config with entity and /auth/ routing

Identity Provider Setup

The gateway supports any OIDC-compliant provider. This guide covers Auth0 and Azure AD, the two most common configurations.

Auth0

1. Create a Regular Web Application

  1. Log in to the Auth0 Dashboard
  2. Go to Applications > Applications > Create Application
  3. Name it (e.g. SuiteCRM MCP Gateway)
  4. Choose Regular Web Application - required for Authorization Code flow
  5. Click Create

2. Configure the application

On the Settings tab:

FieldValue
Allowed Callback URLshttps://YOUR_GATEWAY/auth/callback
Allowed Logout URLshttps://YOUR_GATEWAY
Allowed Web Originshttps://YOUR_GATEWAY

Under Advanced Settings > Grant Types, ensure Authorization Code and Refresh Token are checked. Under Advanced Settings > OAuth, set JWT Signature Algorithm to RS256. Click Save Changes.

3. Note your credentials

From the Settings tab, copy:

  • Domain (e.g. your-tenant.auth0.com) - this is your AUTH0_DOMAIN
  • Client ID - this is your AUTH0_CLIENT_ID
  • Client Secret - this is your AUTH0_CLIENT_SECRET

For AUTH0_AUDIENCE, use the Auth0 Management API identifier (https://your-tenant.auth0.com/api/v2/) or create a custom API in Auth0 > APIs.

4. Configure groups claim

The gateway reads group membership from the JWT to decide which CRM entities a user can access. Auth0 does not include custom claims by default - add an Action:

  1. Go to Actions > Flows > Login
  2. Click + to add a custom action
  3. Paste this code:
javascript
exports.onExecutePostLogin = async (event, api) => {
  const namespace = 'https://suitecrm-mcp/';
  api.idToken.setCustomClaim(namespace + 'groups', event.authorization?.roles ?? []);
  api.accessToken.setCustomClaim(namespace + 'groups', event.authorization?.roles ?? []);
};
  1. Deploy the action and add it to the Login flow

Then set OAUTH_GROUPS_CLAIM=https://suitecrm-mcp/groups in the gateway env file.

In Auth0 > User Management > Roles, create roles matching the group field in your entities.json (e.g. CRM-MyCompany). Assign users to roles.

5. Connect Azure AD (optional - corporate SSO)

  1. In Auth0, go to Authentication > Enterprise > Microsoft Azure AD
  2. Create a new connection using your Azure AD tenant ID, client ID, and secret
  3. Enable the connection on your SuiteCRM MCP Gateway application
  4. Users will see "Log in with Microsoft" on the Auth0 login page

Azure AD (direct, without Auth0)

Use this if you want to skip Auth0 and authenticate directly against Azure AD.

1. Register an application

  1. Go to Azure Portal > App registrations > New registration
  2. Name: SuiteCRM MCP Gateway
  3. Supported account types: Accounts in this organizational directory only
  4. Redirect URI: Web - https://YOUR_GATEWAY/auth/callback
  5. Click Register

2. Add a client secret

Certificates & secrets > New client secret - set an expiry and copy the value immediately.

3. Configure token claims

Token configuration > Add optional claim > ID token: add email, preferred_username.

For groups: Token configuration > Add groups claim. Select Security groups (or All groups). Under each token type, choose Group ID (object ID) or sAMAccountName if synced from on-prem AD.

Note: Azure AD sends group object IDs by default, not names. Set REQUIRED_GROUP in the entity env to the group's object ID, or configure optional claims to emit onpremisessecurityidentifier.

4. Note your credentials

Gateway env varAzure AD value
AUTH0_DOMAINlogin.microsoftonline.com/YOUR_TENANT_ID/v2.0
AUTH0_CLIENT_IDApplication (client) ID
AUTH0_CLIENT_SECRETClient secret value
AUTH0_AUDIENCEApplication (client) ID (same as client ID)
OAUTH_GROUPS_CLAIMgroups

Installer Prompts Reference

When you run sudo python3 install.py, the OAuth section asks:

PromptWhat to enter
Auth0 domainAuth0: your-tenant.auth0.com / Azure: login.microsoftonline.com/TENANT_ID/v2.0
Auth0 client IDFrom your app registration
Auth0 client secretFrom your app registration (keep this secret)
Auth0 audienceAuth0: your API identifier / Azure AD: your client ID
Gateway public URLhttps://mcp.yourcompany.com - must match the registered callback origin
JWT groups claimAuth0 with custom action: https://suitecrm-mcp/groups / Azure AD: groups / default: AUTH0_AUDIENCE + '/groups'

Verification

After installation, visit https://YOUR_GATEWAY/auth/login. You should be redirected to your identity provider's login page. After logging in, you should see the success page with your API key.

If you see an error, check:

  • The callback URL registered in your identity provider matches exactly
  • AUTH0_DOMAIN has no trailing slash
  • The /.well-known/openid-configuration endpoint is reachable from the gateway VM: curl https://YOUR_DOMAIN/.well-known/openid-configuration

Connecting Claude Desktop

Claude Desktop connects directly to the gateway via SSE using a gateway-issued API key. No CRM credentials are stored on your machine.

How authentication works

  1. Your admin installs the gateway and configures an identity provider
  2. You visit the gateway URL in your browser and log in with your corporate account
  3. The success page shows your personal API key - copy it
  4. Paste the key into the Claude Desktop config below

Your API key is tied to your identity. The gateway uses it to look up your CRM account and connect on your behalf. Keys expire after 30 days (configurable by your admin).

Prerequisites

  • Gateway v4.x installed and running
  • Claude Desktop installed
  • Your API key from https://YOUR_GATEWAY/auth/login

Get your API key

  1. Visit https://YOUR_GATEWAY/auth/login (or just https://YOUR_GATEWAY - it redirects)
  2. Log in with your corporate account
  3. On the success page, expand Claude Desktop and copy the config block shown
The success page shows the exact JSON block ready to paste - you do not need to construct it manually.

Config file location

OSPath
macOS~/Library/Application Support/Claude/claude_desktop_config.json
Windows%APPDATA%\Claude\claude_desktop_config.json

Single entity

json
{
  "mcpServers": {
    "suitecrm": {
      "type": "sse",
      "url": "https://mcp.yourcompany.com/sse",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY_HERE"
      }
    }
  }
}

Multi entity

Add one entry per entity. Each entity gets its own /{code}/sse path:

json
{
  "mcpServers": {
    "suitecrm_crm1": {
      "type": "sse",
      "url": "https://mcp.yourcompany.com/crm1/sse",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY_HERE"
      }
    },
    "suitecrm_crm2": {
      "type": "sse",
      "url": "https://mcp.yourcompany.com/crm2/sse",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY_HERE"
      }
    }
  }
}

The same API key works for all entities you have access to.

Apply changes

Fully quit and relaunch Claude Desktop (menu bar > Quit, then reopen).

Verify

Click the hammer icon in the bottom-left of the chat window. You should see 24 tools: suitecrm_search, suitecrm_get, etc. (or suitecrm_crm1_search for multi entity).

Test prompt: "List the first 5 accounts in the CRM" - Claude should call suitecrm_search automatically.

Rotating your API key

  1. Visit https://YOUR_GATEWAY/auth/login again - a new key is issued automatically
  2. Update Authorization in the config file
  3. Restart Claude Desktop

Your admin can also revoke a key immediately via mcp-admin revoke <sub>.

Troubleshooting

SymptomCauseFix
No hammer icon / tools missingConfig file path wrong or JSON syntax errorValidate JSON, check file path
HTTP 401 UnauthorizedAPI key invalid or expiredRe-authenticate at /auth/login and update the config
HTTP 403 ForbiddenNot in the required group for this entityAsk your admin to check your group membership
Connection refusedGateway not runningCheck with your admin: systemctl status suitecrm-mcp
429 Too Many RequestsRate limit hit on /sse (20 req/15 min)Wait 15 minutes

Connecting Claude Code (CLI)

Claude Code connects directly to the gateway via SSE using a gateway-issued API key. No CRM credentials are stored on your machine.

How authentication works

  1. Visit the gateway URL in your browser and log in with your corporate account
  2. The success page shows your personal API key
  3. Run the claude mcp add command shown on the success page - or paste the key into the command below

Prerequisites

  • Gateway v4.x installed and running
  • Claude Code CLI installed: npm install -g @anthropic-ai/claude-code
  • Your API key from https://YOUR_GATEWAY/auth/login

Get your API key

  1. Visit https://YOUR_GATEWAY/auth/login
  2. Log in with your corporate account
  3. On the success page, expand Claude Code and copy the ready-to-run command

Commands

Single entity

bash
claude mcp add suitecrm \
  --transport sse \
  --header "Authorization:Bearer YOUR_API_KEY_HERE" \
  https://mcp.yourcompany.com/sse

Multi entity

Run once per entity. Each gets its own MCP server entry:

bash
claude mcp add suitecrm_crm1 \
  --transport sse \
  --header "Authorization:Bearer YOUR_API_KEY_HERE" \
  https://mcp.yourcompany.com/crm1/sse

claude mcp add suitecrm_crm2 \
  --transport sse \
  --header "Authorization:Bearer YOUR_API_KEY_HERE" \
  https://mcp.yourcompany.com/crm2/sse

Verify

bash
claude mcp list

Start a session and test:

bash
claude
> List the first 5 accounts in the CRM

Claude should call suitecrm_search automatically.

Manage entries

bash
# List all MCP servers
claude mcp list

# Remove an entry
claude mcp remove suitecrm

Rotating your API key

bash
# Remove the old entry
claude mcp remove suitecrm

# Re-authenticate, then re-add with the new key
claude mcp add suitecrm \
  --transport sse \
  --header "Authorization:Bearer YOUR_NEW_API_KEY" \
  https://mcp.yourcompany.com/sse

Troubleshooting

SymptomCauseFix
HTTP 401 UnauthorizedAPI key invalid or expiredRe-authenticate at /auth/login and re-add the entry
HTTP 403 ForbiddenNot in the required group for this entityAsk your admin to check your group membership
Connection refusedGateway not runningsystemctl status suitecrm-mcp on the gateway machine
429 Too Many RequestsRate limit hit on /sse (20 req/15 min)Wait 15 minutes
TLS error with self-signed certNODE_TLS_REJECT_UNAUTHORIZED not setAdd --tls-skip during gateway install

Connecting OpenClaw

OpenClaw uses a two-machine architecture: a remote gateway and a local bridge plugin.

Architecture

OpenClaw Machine OpenClaw runtime suitecrm-{code} plugin ~/.suitecrm-mcp/gateway.token SSE Gateway Machine suitecrm-mcp gateway SuiteCRM REST API

The bridge is a Node.js plugin that OpenClaw loads. It is silent at startup - no auth prompt, no polling. Authentication is triggered lazily the first time a SuiteCRM tool is actually called. The bridge requests a one-time login URL from the gateway, returns it as the tool response, and polls in the background. Once the user clicks the link and logs in, the token is saved and all subsequent tool calls work silently. No CLI needed on the OpenClaw machine.

Prerequisites

  • A dedicated Ubuntu VM or server for the gateway
  • OpenClaw installed on the client machine (Node.js required)
  • Access to the gateway auth URL in a browser

Installation

1
Install the gateway (gateway machine)

Single entity

bash
sudo python3 install.py --url https://crm.example.com --domain mcp.example.com --email you@example.com

Multi entity

bash
cp entities.example.json entities.json
# Edit entities.json: add your CRM codes, labels, ports, endpoints, and group names
sudo python3 install.py --config entities.json --domain mcp.example.com --email you@example.com
HTTPS strongly recommended. The --domain flag enables automatic TLS via Let's Encrypt. Without it, API keys travel unencrypted.
2
Install the bridge (OpenClaw machine)

Single entity

bash
sudo python3 install-bridge.py \
  --gateway https://mcp.example.com \
  --code mycrm \
  --label "My CRM"

Multi entity

bash
sudo python3 install-bridge.py \
  --gateway https://mcp.example.com \
  --entities entities.json

Specific users only

bash
sudo python3 install-bridge.py --gateway ... --entities entities.json alice bob

Agent scoping (optional)

bash
# All agents in OpenClaw get access
sudo python3 install-bridge.py --gateway ... --code mycrm --attach all

# Only specific agents (comma-separated names or IDs)
sudo python3 install-bridge.py --gateway ... --code mycrm --attach "Sales Bot,Support Agent"
3
Restart OpenClaw
bash
sudo systemctl restart openclaw-USERNAME

Authentication Flow

Authentication is lazy - nothing happens at startup. The first time a user asks the agent to do something with SuiteCRM:

  1. User invokes a SuiteCRM tool - the bridge detects no token and calls POST /auth/bridge/start.
  2. Gateway returns a one-time login URL tied to a short-lived nonce. The bridge returns this URL as the tool response, visible in Teams chat or wherever the agent runs.
  3. User clicks the link and logs in with their corporate account.
  4. Bridge polls in the background every 3 seconds. Once the gateway resolves the nonce session, the bridge saves the token to ~/.suitecrm-mcp/gateway.token.
  5. Subsequent tool calls work silently. Token is stored per-Linux-user.
  6. On expiry or revocation, the bridge receives HTTP 401, clears the saved token, and sends a fresh login URL on the next tool call. No restart needed.

Verify

OpenClaw should now expose 24 tools per entity: suitecrm_mycrm_search, suitecrm_mycrm_get, suitecrm_mycrm_create, and 21 more.

Test prompt: "List the first 5 accounts in the CRM" - OpenClaw should call suitecrm_mycrm_search automatically.

Re-authenticating

If the token expires (default 30 days) or is revoked: the next tool call returns HTTP 401, the bridge clears the token and calls POST /auth/bridge/start, and returns a fresh login URL. Same flow as first-time auth. No agent restart needed.

Revoking a user's token (operators)

Using mcp-admin (recommended)

bash
mcp-admin revoke <sub>

Using the REST endpoint directly

bash
curl -X POST https://mcp.example.com/auth/revoke \
  -H "X-Admin-Key: your-admin-key" \
  -H "Content-Type: application/json" \
  -d '{"sub": "user@example.com"}'

Updating the bridge

bash
sudo python3 install-bridge.py --update \
  --gateway https://mcp.example.com \
  --entities entities.json

Removing the bridge

bash
sudo python3 install-bridge.py \
  --remove alice bob \
  --gateway https://mcp.example.com \
  --entities entities.json

Troubleshooting

SymptomCauseFix
Tool call returns a login URL instead of CRM dataExpected - first-time auth or token expiredClick the URL and log in; next tool call works normally
Login URL never appears as tool responseBridge plugin not loadedCheck openclaw.json plugins section; re-run installer
Login URL expired before user clicked itNonce TTL (15 min) elapsedCall any SuiteCRM tool again - a fresh URL is generated
Auth polling times out after 15 minUser did not complete loginRe-invoke any SuiteCRM tool to get a new login URL
Token rejected (HTTP 401) in logsToken expired or revokedBridge clears token and sends fresh login URL on next tool call
HTTP 403 ForbiddenUser not in required group for entityAdmin checks group membership in identity provider
Rate limited (HTTP 429) in logsToo many reconnectsWait 15 min; backoff is automatic
Gateway connect failedWrong gateway URL or gateway downCheck --gateway URL; systemctl status suitecrm-mcp-* on gateway
TLS errorSelf-signed cert on CRMAdd --tls-skip during gateway install

Observability

Prometheus metrics, Grafana dashboards, and Loki log aggregation - shipped in docker-compose.yml and ready to run alongside the gateway.

Starting the stack

The full observability stack (Prometheus + Grafana + Loki + Promtail) is bundled in docker-compose.yml. It starts automatically alongside the gateway:

bash
docker compose up -d

Set GRAFANA_PASSWORD in your environment before starting, then visit http://localhost:3000. Both dashboards are provisioned automatically - no manual import needed.

Systemd installs: the stack runs separately. Start it with docker compose up -d from the repo directory after the gateway is running. Point Prometheus at your gateway's metrics port using monitoring/prometheus.yml.

Prometheus metrics

Two components expose metrics on separate localhost ports. Both are scraped by the bundled Prometheus instance.

Gateway entity metrics (default port 9090)

MetricTypeDescription
suitecrm_mcp_active_connectionsGaugeCurrently open SSE connections
suitecrm_mcp_connections_totalCounterTotal SSE connections established
suitecrm_mcp_tool_calls_totalCounterTool calls by name and status (success/error)
suitecrm_mcp_tool_duration_secondsHistogramTool call latency (p50/p95/p99)
suitecrm_mcp_crm_api_duration_secondsHistogramCRM REST API call latency
suitecrm_mcp_session_renewals_totalCounterCRM session re-authentications
suitecrm_mcp_auth_failures_totalCounterAuthentication failures
suitecrm_mcp_circuit_breaker_stateGauge0 = closed, 1 = half-open, 2 = open
suitecrm_mcp_circuit_breaker_openings_totalCounterCircuit breaker trip events

Auth service metrics (default port 9091)

MetricTypeDescription
suitecrm_auth_logins_totalCounterOAuth2 login completions by result (new/reused/error)
suitecrm_auth_bridge_sessions_totalCounterBridge session events (started/completed/expired)
suitecrm_auth_sessions_activeGaugeNon-expired gateway sessions currently stored

Scrape manually to verify metrics are flowing:

bash
# Gateway entity metrics
curl http://127.0.0.1:9090/metrics

# Auth service metrics
curl http://127.0.0.1:9091/metrics

For systemd (multi-entity), add each entity's metrics port to monitoring/prometheus.yml:

yaml
scrape_configs:
  - job_name: suitecrm-mcp-auth
    static_configs:
      - targets: ['127.0.0.1:9091']
  - job_name: suitecrm-mcp-crm1
    static_configs:
      - targets: ['127.0.0.1:9101']   # port 3101 + 6000
  - job_name: suitecrm-mcp-crm2
    static_configs:
      - targets: ['127.0.0.1:9102']   # port 3102 + 6000

Grafana dashboards

Two dashboards are auto-provisioned from monitoring/grafana/dashboards/:

  • suitecrm-mcp - per-entity view with 33 panels across 5 rows: system health, users/sessions table, CRM backend (latency, error codes, circuit breaker), security (auth failures, rate limits), and tool call breakdown
  • suitecrm-mcp-fleet - multi-entity overview showing all entities at a glance: circuit breaker state, active connections, error rates, and latency per entity

Open http://localhost:3000, log in with admin / $GRAFANA_PASSWORD, and both dashboards appear under Dashboards. No manual import or datasource setup needed.

Tip: use Grafana Explore to run ad-hoc queries against Prometheus and Loki side by side. Switch the datasource dropdown between Prometheus and Loki to correlate a latency spike with the log lines that caused it.

Loki & log queries

Promtail tails Docker JSON logs and ships them to Loki. All gateway log lines are structured JSON (via pino) - every line includes sub (user identity), entity, reqId, and tool where relevant. Sensitive fields (password, token, fields, search_string) are redacted before logging.

Query logs in Grafana Explore (select Loki datasource):

logql
# All logs for a specific user
{job="suitecrm-mcp"} | json | sub="auth0|abc123"

# All tool calls on a specific entity
{job="suitecrm-mcp"} | json | entity="crm1" | msg="tool call"

# Auth failures in the last hour
{job="suitecrm-mcp"} | json | level="warn" | msg=~"auth.*fail|invalid.*key"

# Trace a request by ID
{job="suitecrm-mcp"} | json | reqId="req-xyz"
Note: Loki requires Docker Compose - it does not run automatically on systemd installs. Run docker compose up -d loki promtail grafana separately and point Promtail at the journal or log files.

Alerting rules

Prometheus alerting rules are in monitoring/prometheus/rules.yml and loaded automatically by the bundled Prometheus. Four rules are configured:

AlertConditionSeverity
Circuit breaker opencircuit_breaker_state == 2 for 1 minCritical
High auth failure rate>10 failures/min for 5 minWarning
Latency SLO breachp99 tool latency > 5s for 5 minWarning
Session expiry storm>20 renewals/min for 3 minWarning

To wire up notifications (Slack, PagerDuty, email), add an Alertmanager config to docker-compose.yml and point Prometheus at it via alerting.alertmanagers in monitoring/prometheus.yml.