Skip to main content
Glama
The-Keep-Studios

EspoCRM Assistant

README.md
# EspoCRM Assistant

Constrained EspoCRM access for issue
[#28](https://github.com/The-Keep-Studios/thekeep-platform/issues/28).
The assistant-visible MCP server can read records and prepare signed change
sets; it cannot apply or delete anything. See the
[evaluation](docs/espocrm-mcp-evaluation.md) for the build-vs-wrap
decision and security boundary.

This service requires an existing EspoCRM deployment. It does not install,
enable, migrate, or administer EspoCRM. The Keep Platform or another GitOps
repository should consume a published image from this repository and provide
Kubernetes deployment configuration, secrets, probes, ingress, and operator
runbooks.

## Local Development

```bash
python -m venv .venv
. .venv/bin/activate
pip install -e .
export ESPOCRM_URL=https://crm.example.com
export ESPOCRM_READ_API_KEY=...
thekeep-espocrm-mcp
```

Run the dependency-free tests with:

```bash
PYTHONPATH=src python -m unittest discover -s tests -v
```

## Runtime Modes

The stdio MCP entrypoint is for local evaluation:

```bash
thekeep-espocrm-mcp
```

Run streamable HTTP MCP for an internal gateway:

```bash
export ESPOCRM_MCP_HOST=0.0.0.0
export ESPOCRM_MCP_PORT=8080
export ESPOCRM_MCP_PATH=/mcp
thekeep-espocrm-mcp-http
```

Do not expose the streamable HTTP endpoint directly to the internet. Production
should keep it internal and place OAuth/OIDC and per-user authorization in front
of it through the platform MCP gateway tracked by
[#54](https://github.com/The-Keep-Studios/thekeep-platform/issues/54).

The container default command is the service HTTP entrypoint:

```bash
thekeep-espocrm-http
```

It exposes:

- `GET /healthz`
- authenticated `POST /crm/*` read and prepare endpoints using
  `ESPOCRM_ASSISTANT_TOKEN`
- authenticated `POST /approval/apply-change` using
  `ESPOCRM_ASSISTANT_APPLY_TOKEN`

Apply a reviewed change set outside the assistant, either locally:

```bash
thekeep-espocrm-apply change.json \
  --approve-sha256 <sha256> \
  --approved-by <human-identity>
```

or through the deployed approval endpoint:

```bash
curl -fsS http://espocrm-assistant.espocrm.svc.cluster.local:8090/approval/apply-change \
  -H "Authorization: Bearer ${ESPOCRM_ASSISTANT_APPLY_TOKEN}" \
  -H "Content-Type: application/json" \
  --data @approved-change.json
```

`approved-change.json` must contain `change`, `approved_sha256`, and
`approved_by`. The approval endpoint uses separate write credentials and is not
registered as an MCP tool.

Use separate read-only and write-capable Espo API users. The executor reads
`ESPOCRM_WRITE_API_KEY`; optional HMAC secrets use the same `READ_`/`WRITE_`
prefix. The deployed approval endpoint additionally requires
`ESPOCRM_ASSISTANT_APPLY_TOKEN` and writes audit records to
`ESPOCRM_ASSISTANT_AUDIT_LOG`. Writes require source attribution; Opportunity
writes also require reciprocal signal evidence or an explicit human override.
The executor rejects stale updates, adds an Espo Note, and appends a mode-`0600`
audit record.

## Configuration

Use a least-privilege EspoCRM API user for reads and a separate write-capable
API user for the approval executor. Do not reuse admin credentials.

| Variable | Required | Purpose |
| --- | --- | --- |
| `ESPOCRM_URL` | yes | Existing EspoCRM base URL. HTTPS is required unless `ESPOCRM_ALLOW_HTTP=1`. |
| `ESPOCRM_READ_API_KEY` | yes | Read-only EspoCRM API key used by assistant-visible tools. |
| `ESPOCRM_READ_AUTH_METHOD` | no | `apikey` by default; set `hmac` when using HMAC auth. |
| `ESPOCRM_READ_SECRET_KEY` | for read HMAC | HMAC secret for the read API user. |
| `ESPOCRM_WRITE_API_KEY` | for apply | Write-capable EspoCRM API key used only by approval/apply paths. |
| `ESPOCRM_WRITE_AUTH_METHOD` | no | `apikey` by default; set `hmac` when using HMAC auth. |
| `ESPOCRM_WRITE_SECRET_KEY` | for write HMAC | HMAC secret for the write API user. |
| `ESPOCRM_ASSISTANT_TOKEN` | for HTTP `/crm/*` | Bearer token for the custom JSON HTTP API. |
| `ESPOCRM_ASSISTANT_APPLY_TOKEN` | for HTTP apply | Separate bearer token for `/approval/apply-change`. |
| `ESPOCRM_ASSISTANT_AUDIT_LOG` | no | JSONL audit path; defaults under `~/.local/state/thekeep/`. |
| `ESPOCRM_ASSISTANT_HOST` | no | HTTP service bind host; default `0.0.0.0`. |
| `ESPOCRM_ASSISTANT_PORT` | no | HTTP service port; default `8080`. |
| `ESPOCRM_MCP_HOST` | no | Streamable HTTP MCP bind host; default `127.0.0.1`. |
| `ESPOCRM_MCP_PORT` | no | Streamable HTTP MCP port; default `8080`. |
| `ESPOCRM_MCP_PATH` | no | Streamable HTTP MCP path; default `/mcp`. |
| `ESPOCRM_MCP_STATELESS_HTTP` | no | `1` by default. |
| `ESPOCRM_ALLOW_HTTP` | no | Set to `1` only for disposable local testing against HTTP EspoCRM. |

## Image Publishing

The CI workflow tests the package and builds the container image on every pull
request. Pushes to `main` and `v*` tags publish to GHCR by default:

```text
ghcr.io/the-keep-studios/espocrm-assistant:sha-<git-sha>
ghcr.io/the-keep-studios/espocrm-assistant:vX.Y.Z
```

The workflow also publishes `latest` on `main` for convenience, but downstream
GitOps deployments should pin a `sha-*` tag, release tag, or digest.

The OCI registry is replaceable. Set these repository variables/secrets before
running the workflow if publishing somewhere other than GHCR:

- variable `OCI_REGISTRY`: registry hostname, for example `registry.example.com`
- variable `OCI_IMAGE`: full image name, for example
  `registry.example.com/thekeep/espocrm-assistant`
- variable `OCI_USERNAME`: registry username when it differs from the GitHub actor
- secret `OCI_PASSWORD`: registry password or token

For non-GitHub installs, build or mirror the image with standard OCI tooling and
update the consuming platform manifest to that immutable tag or digest.

TDQS

B3/5.0

Scored across 7 tools

Disambiguation5/5

Each tool targets a distinct CRM operation: duplicate detection, CSV export, single record retrieval, metadata, change preparation, lead conversion preparation, and search. No overlap in functionality; an agent can easily distinguish them.

Naming Consistency4/5

All tools use the 'crm_' prefix with a verb-based name, e.g., 'crm_search', 'crm_get'. However, 'crm_get' lacks a noun (like 'record'), and 'crm_metadata' uses a noun as a verb, which is slightly inconsistent but still clear.

Tool Count5/5

Seven tools is well-scoped for a CRM assistant focused on read, search, duplicate detection, and preparation of change sets for human approval. No unnecessary tools, and the count supports a focused workflow.

Completeness4/5

The toolset covers core read, search, metadata, duplicate checking, change preparation, lead conversion preparation, and export. Missing direct mutation (create/update/delete) is intentional for a non-mutating, human-approval workflow. A minor gap is the lack of a list-all-entities tool, but search can compensate.

Maintenance

ActivityStale
ResponsivenessNo issues