# Asistn MCP Server

Asistn MCP Server is an internal Model Context Protocol wrapper for trusted external agents such as Hermes. It exposes selected Asistn workspace API capabilities as MCP tools.

Public docs URL:

```text
https://asistn.com/docs/mcp
```

Downloadable Markdown URL:

```text
https://asistn.com/docs/asistn-mcp.md
```

The docs can be public because they contain placeholders only. Do not publish the real MCP token, workspace API tokens, or the internal MCP endpoint publicly.

## Security Model

- `/mcp` requires `Authorization: Bearer <ASISTN_MCP_SERVER_TOKEN>`.
- `/healthz` is unauthenticated and returns only service-level health.
- Production deploy exposes the MCP container only on the Docker network.
- The MCP service is not routed through public Nginx in the MVP.
- Workspace API tokens stay in server environment variables.
- Read tools are enabled by default.
- Write tools are disabled by default.
- `send_message` is disabled by default.
- No delete tools exist in the MVP.

## Architecture

```text
Hermes / External Agent
  -> MCP client
  -> http://asistn-mcp:3010/mcp
  -> Asistn MCP Server
  -> http://app:3006/api/v1/*
  -> Asistn workspace API
```

Important MVP model:

- There is no true super-admin Asistn API yet.
- Multi-workspace access means every workspace configured in the MCP service.
- Each configured workspace still uses its own Asistn workspace API token.
- MCP does not access the database directly.

## MCP Server Token

`ASISTN_MCP_SERVER_TOKEN` is generated by the operator. It is not generated by the Asistn app UI and it is different from workspace API tokens.

Generate one on a server:

```bash
openssl rand -base64 48
```

Or use the browser generator on:

```text
https://asistn.com/docs/mcp#token
```

The browser generator uses browser crypto locally. The generated token is not sent to Asistn or stored by the docs page.

## Production Env Path

Current deploys run from:

```text
/opt/asistn/Agentic CRM
```

Production env file:

```text
/opt/asistn/Agentic CRM/.env
```

Install or rotate the MCP server token:

```bash
TOKEN='paste_generated_token_here'
ENV_FILE='/opt/asistn/Agentic CRM/.env'

if grep -q '^ASISTN_MCP_SERVER_TOKEN=' "$ENV_FILE"; then
  sed -i "s|^ASISTN_MCP_SERVER_TOKEN=.*|ASISTN_MCP_SERVER_TOKEN=$TOKEN|" "$ENV_FILE"
else
  printf '\nASISTN_MCP_SERVER_TOKEN=%s\n' "$TOKEN" >> "$ENV_FILE"
fi

cd '/opt/asistn/Agentic CRM'
docker compose -f docker-compose.prod.yml up -d mcp
```

If a trusted server agent needs the token, it can read `ASISTN_MCP_SERVER_TOKEN` from `/opt/asistn/Agentic CRM/.env`. Only allow this for agents that are already trusted with production infrastructure, because that file may contain other production secrets.

## Minimal Read-Only Config

```bash
ASISTN_MCP_SERVER_TOKEN=long_random_internal_bearer_token
ASISTN_MCP_WORKSPACES=[{"slug":"klaser","tokenEnv":"ASISTN_API_TOKEN_KLASER"},{"slug":"safencare","tokenEnv":"ASISTN_API_TOKEN_SAFENCARE"}]
ASISTN_API_TOKEN_KLASER=asistn_live_xxx
ASISTN_API_TOKEN_SAFENCARE=asistn_live_xxx

ASISTN_BASE_URL=http://app:3006
ASISTN_MCP_PORT=3010
ASISTN_MCP_DEFAULT_WORKSPACE=klaser
ASISTN_MCP_READ=true
ASISTN_MCP_WRITE=false
ASISTN_MCP_SEND_MESSAGES=false
ASISTN_MCP_DELETE=false
ASISTN_MCP_ALLOWED_ORIGINS=
```

## Permission Gates

Read-only monitoring:

```bash
ASISTN_MCP_READ=true
ASISTN_MCP_WRITE=false
ASISTN_MCP_SEND_MESSAGES=false
ASISTN_MCP_DELETE=false
```

Lead intake without WhatsApp sending:

```bash
ASISTN_MCP_READ=true
ASISTN_MCP_WRITE=true
ASISTN_MCP_SEND_MESSAGES=false
ASISTN_MCP_DELETE=false
```

Controlled WhatsApp messaging:

```bash
ASISTN_MCP_READ=true
ASISTN_MCP_WRITE=true
ASISTN_MCP_SEND_MESSAGES=true
ASISTN_MCP_DELETE=false
```

`send_message` is a real WhatsApp action. Enable it only for trusted agents and approved workspaces.

## Connect

MCP endpoint from the internal Docker network:

```text
POST http://asistn-mcp:3010/mcp
Authorization: Bearer $ASISTN_MCP_SERVER_TOKEN
```

Health endpoint:

```bash
curl http://asistn-mcp:3010/healthz
```

## Agent Instruction Template

Use this for a trusted infrastructure agent that has access to the droplet or the same Docker network:

```text
Read https://asistn.com/docs/asistn-mcp.md.

Connect to Asistn MCP at:
http://asistn-mcp:3010/mcp

Use Authorization: Bearer <token>.

If you are running on the production droplet and have explicit permission to read production env files, read ASISTN_MCP_SERVER_TOKEN from:
/opt/asistn/Agentic CRM/.env

Never print the token. Never copy workspace API tokens into prompts or logs.
Call list_tools first and confirm the exposed tools match the expected permission mode.
```

## Read Tools

Registered when `ASISTN_MCP_READ=true`.

| Tool | Input | Description |
| --- | --- | --- |
| `list_workspaces` | none | Lists every workspace configured in this MCP service and fetches workspace profiles. |
| `get_workspace` | `{ workspace? }` | Reads one workspace profile. Uses the default workspace when omitted. |
| `list_contacts` | `{ workspace?, status?, q?, updated_since?, tag?, limit? }` | Lists contacts using current API-supported filters. |
| `get_contact` | `{ workspace?, contact_id }` | Reads one contact by id. |
| `list_conversations` | `{ workspace?, limit? }` | Lists recent conversations without full message history. |
| `get_conversation` | `{ workspace?, contact_id, limit? }` | Reads message history for one contact. |
| `get_analytics` | `{ workspace? }` | Reads compact workspace analytics summary. |
| `get_agent_memory` | `{ workspace?, contact_id?, type?, limit? }` | Reads AI memory. Type filtering is applied client-side by MCP. |
| `get_content_insights` | `{ workspace?, days?, limit? }` | Reads questions, objections, content signals, and learning signals. |
| `get_audiences` | `{ workspace?, segment?, tag?, limit? }` | Reads audience segments: ghosted, engaged, qualified, recent, or all. |

## Write Tools

Registered when `ASISTN_MCP_WRITE=true`.

| Tool | Input | Description |
| --- | --- | --- |
| `upsert_contact` | `{ workspace?, phone, name?, status?, source?, intent?, profile_data?, ad_source?, meta_data? }` | Creates or updates a contact by phone. |
| `push_lead` | `{ workspace?, phone, name?, source?, external_id?, status?, intent?, payload?, ad_source?, meta_data? }` | Pushes an external lead into Asistn. |
| `fire_conversion_event` | `{ workspace?, contact_id?, phone?, event_name, platform?, payload?, response? }` | Records a conversion event. |

## Message Send Tool

Registered only when both `ASISTN_MCP_WRITE=true` and `ASISTN_MCP_SEND_MESSAGES=true`.

| Tool | Input | Description |
| --- | --- | --- |
| `send_message` | `{ workspace?, contact_id?, phone?, content }` | Sends a WhatsApp text from the workspace number. |

## Health Tools

Always registered.

| Tool | Input | Description |
| --- | --- | --- |
| `check_health` | none | Pings the default workspace API and returns status, latency, base URL, and API version. |
| `check_all_workspaces_health` | none | Checks workspace and analytics API health across all configured workspaces. |

## Common Flows

Monitor every configured workspace:

```text
1. list_workspaces {}
2. check_all_workspaces_health {}
3. For each workspace:
   - get_analytics { "workspace": "klaser" }
   - list_contacts { "workspace": "klaser", "status": "open", "limit": 50 }
   - get_content_insights { "workspace": "klaser", "days": 30, "limit": 50 }
```

Investigate one customer conversation:

```text
1. list_contacts { "workspace": "klaser", "q": "6281234567890", "limit": 5 }
2. get_contact { "workspace": "klaser", "contact_id": "CONTACT_ID" }
3. get_conversation { "workspace": "klaser", "contact_id": "CONTACT_ID", "limit": 100 }
4. get_agent_memory { "workspace": "klaser", "contact_id": "CONTACT_ID", "limit": 50 }
```

Push a lead:

```json
{
  "workspace": "klaser",
  "phone": "6281234567890",
  "name": "Albert",
  "source": "agentic_marketing",
  "external_id": "meta_lead_123",
  "intent": "consultation",
  "ad_source": {
    "platform": "meta",
    "campaign": "k-laser-leadgen"
  },
  "meta_data": {
    "tags": ["meta-lead", "k-laser"]
  }
}
```

## Troubleshooting

| Symptom | Meaning |
| --- | --- |
| 401 from `/mcp` | Missing or wrong `Authorization: Bearer <ASISTN_MCP_SERVER_TOKEN>`. |
| 403 origin rejected | Browser `Origin` is not listed in `ASISTN_MCP_ALLOWED_ORIGINS`. |
| Startup fails with token env missing | A workspace entry references `tokenEnv`, but that env var is not set. |
| Unknown workspace error | Tool input used a slug not present in `ASISTN_MCP_WORKSPACES`. |
| `send_message` not visible | Both `ASISTN_MCP_WRITE=true` and `ASISTN_MCP_SEND_MESSAGES=true` are required, then restart MCP. |
| Health is degraded | A workspace API request failed. Check token, app container reachability, and analytics endpoint. |
