EspoCRM Assistant
Constrained EspoCRM access for issue #28. The assistant-visible MCP server can read records and prepare signed change sets; it cannot apply or delete anything. See the evaluation 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
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:
PYTHONPATH=src python -m unittest discover -s tests -v
Runtime Modes
The stdio MCP entrypoint is for local evaluation:
thekeep-espocrm-mcp
Run streamable HTTP MCP for an internal gateway:
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.
The container default command is the service HTTP entrypoint:
thekeep-espocrm-http
It exposes:
GET /healthz- authenticated
POST /crm/*read and prepare endpoints using
ESPOCRM_ASSISTANT_TOKEN
- authenticated
POST /approval/apply-changeusing
ESPOCRM_ASSISTANT_APPLY_TOKEN
Apply a reviewed change set outside the assistant, either locally:
thekeep-espocrm-apply change.json \
--approve-sha256 <sha256> \
--approved-by <human-identity>
or through the deployed approval endpoint:
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:
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 exampleregistry.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.











