Cantemo MCP

Cantemo MCP is an optional Model Context Protocol (MCP) server. It exposes a controlled subset of the Cantemo REST API as MCP tools for compatible AI clients.

Deployment and setup

Install MCP as an optional Cantemo component:

sudo ./setup --components=cantemo-mcp

The installer loads and starts the cantemo-mcp Docker container. It creates the following configuration files:

  • /etc/cantemo/portal/mcp-settings.env for container settings and credentials.

  • /etc/cantemo/portal/mcp-tool-allowlist.json for the allowed API tools.

An upgrade preserves these files and does not create missing MCP configuration. When adding MCP for the first time during a Portal upgrade, complete the Portal upgrade without cantemo-mcp first. With the updated Portal code and Python dependencies installed, create the files manually:

sudo /opt/cantemo/portal/manage.py mcp_settings --create-mcp-settings

Do this only when the settings file is absent. Edit the generated configuration and credentials before installing the component:

sudo ./setup --upgrade --components=cantemo-mcp

If MCP is already installed, keep the operator’s existing settings and allowlist; do not run --create-mcp-settings as an upgrade step. Docker and configuration errors now stop installation. Correct the reported error before retrying. The creation command can write files before reporting that no image is loaded; a Docker diagnostic failure also leaves the created files in place. Inspect them before retrying rather than overwriting edited configuration.

The command accepts root or the owning www-data user. Root-created credential files and backups use owner root, group www-data, and mode 0640. Files created by www-data use mode 0600. Use root to replace a root-owned settings file; www-data can read it but cannot overwrite it. Existing credential files and command-generated backups are restricted when the command reads the configuration. Do not make them world-readable. The non-secret allowlist uses 0644 so that www-data and the container’s unprivileged Bun user can read it. The command copies the allowlist into a stopped container before starting MCP.

For Compose development, ordinary setup.sh, up.sh, and restart.sh do not build or start MCP. Run compose/bin/mcp-up.sh explicitly after Portal is running. That script offers submodule initialization, obtains a development token, builds MCP, and starts the MCP/IdP stack. Keep existing local submodule changes when declining initialization.

The bundled Nginx configuration exposes MCP at /mcp and proxies the OAuth discovery route. The public endpoint is normally:

https://cantemo.example.com/mcp

Check the installed image, container status, and settings:

sudo /opt/cantemo/portal/manage.py mcp_settings

Normal and verbose diagnostics redact credential values. Only the explicit --obtain-api-token operation prints a complete token, on a single stdout line. Avoid capturing that output in shared logs.

The MCP health endpoint confirms that the process is running, but does not validate its Cantemo credentials or OAuth configuration:

curl --fail http://127.0.0.1:3000/health

To apply a change to mcp-settings.env, recreate the container:

sudo /opt/cantemo/portal/manage.py mcp_settings --recreate-container

This restarts MCP and ends active MCP sessions. To stop and remove the container without removing its configuration, use:

sudo /opt/cantemo/portal/manage.py mcp_settings --remove-container

Recreation requires exactly one installed cantemo-mcp image tag. Missing or multiple tags fail before stopping the current container; install or select the replacement image before retrying.

For an external or separate-host deployment, set CANTEMO_BASE_URL to the Cantemo URL that the MCP container can reach. Publish MCP through an HTTPS reverse proxy and block direct external access to port 3000. The proxy must route both /mcp and /.well-known/oauth-protected-resource/mcp. The installer binds Docker port 3000 to 127.0.0.1 only. Compose does the same for its configurable host port. A separate-host proxy needs an explicit, secured network arrangement rather than a public Docker port.

Tool allowlist

The allowlist limits the generated MCP tools that can be used. It complements, but does not replace, Cantemo roles assigned to the service user. Keep both the user permissions and allowlist as narrow as possible.

The allowlist uses HTTP methods as keys and regular expressions matching full OpenAPI operation IDs as values. This permits selected read operations and one write operation:

{
  "GET": ["items_.+", "collections_.+", "whoami_retrieve"],
  "PUT": ["search_update"]
}

An omitted method or an empty list permits no tools for that method. Use /APIdoc/ or /APIdoc/schema/ to find operation IDs. The file is reread before each tool call; an invalid or missing file blocks calls. Recreate the container after changing rules when clients must discover newly allowed tools.

To update the allowlist:

  1. Back up and edit /etc/cantemo/portal/mcp-tool-allowlist.json. Add the operation ID regular expression under its HTTP method, or remove the expression to revoke access. Do not grant write methods unless required.

  2. Validate that the edited file is valid JSON:

    sudo python3 -m json.tool /etc/cantemo/portal/mcp-tool-allowlist.json > /dev/null
    

    No output means the file is valid JSON. If the file is invalid, the command prints a parse error; correct it before continuing.

  3. Copy the updated file into a recreated MCP container:

    sudo /opt/cantemo/portal/manage.py mcp_settings --recreate-container
    
  4. Confirm that MCP is running:

    curl --fail http://127.0.0.1:3000/health
    

Recreating the container ends active MCP sessions. Clients may need to refresh their tool list before newly allowed tools appear.

Key management

MCP supports Cantemo API token and HTTP Basic credentials. The static service credential used with OAuth RunAs depends on the Cantemo version:

  • Cantemo 6.2 requires HTTP Basic credentials.

  • Cantemo 6.3 supports either HTTP Basic credentials or an API token.

For OAuth RunAs, the service account must have Portal administrator permission to impersonate users. A regular user’s API token does not grant that permission. Use a dedicated service account and restrict access to its credentials.

For Cantemo 6.2 OAuth deployments, configure HTTP Basic credentials in /etc/cantemo/portal/mcp-settings.env. This also works on Cantemo 6.3:

CANTEMO_BASE_URL=http://host.docker.internal
BASIC_USERNAME_BASICORLOGGEDINAUTHENTICATION=cantemo-mcp-service
BASIC_PASSWORD_BASICORLOGGEDINAUTHENTICATION=<service account password>
LOG_LEVEL=info

Leave API_KEY_APITOKENAUTHENTICATION unset when using Basic credentials. On Cantemo 6.3, an API token avoids storing the service account password. Create the token:

sudo /opt/cantemo/portal/manage.py mcp_settings --obtain-api-token cantemo-mcp-service

Without --duration, expiry is the configured Django SESSION_COOKIE_AGE in seconds from issuance. Set an explicit lifetime or future expiry when needed:

sudo /opt/cantemo/portal/manage.py mcp_settings --obtain-api-token cantemo-mcp-service --duration P1Y2M3D
sudo /opt/cantemo/portal/manage.py mcp_settings --obtain-api-token cantemo-mcp-service --duration 2027-09-30T14:30:00

The same option applies to --create-mcp-settings --set-admin-api-token. An ISO datetime without an offset uses Django TIME_ZONE; an explicit offset is normalized to UTC. Supported durations use ordered year/month/day/time units, such as P1Y2M3D and P1DT2H30M, or whole weeks alone, such as P2W. Do not combine weeks with other units. Years and months must be integers; day/time components may contain decimal fractions.

Years and months are calendar units applied first in the configured timezone: January 31 plus one month ends on the last day of February. After that calendar shift, days, hours, minutes, and seconds are added as elapsed time in UTC. A day is 24 hours and a week is seven elapsed days. For example, PT2H always adds two elapsed hours, including across daylight-saving transitions. Zero or omitted calendar components do not change the starting instant. Durations must be positive and all expiry dates must be in the future. Invalid or orphan --duration options fail before token creation, file changes, or container removal. The command still prints only the token, not its expiry.

For an API-token service credential, store the token in /etc/cantemo/portal/mcp-settings.env and restrict access to this file. OAuth RunAs requires Cantemo 6.3 for this configuration:

CANTEMO_BASE_URL=http://host.docker.internal
API_KEY_APITOKENAUTHENTICATION=<Cantemo API token>
LOG_LEVEL=info

Recreate the container after changing the credential. To rotate a token, create a replacement, update the settings file, verify MCP, then revoke the old token. Include the settings file in encrypted configuration backups.

For an evaluation installation, --create-mcp-settings --set-admin-api-token creates a token for the Cantemo admin user. With OAuth RunAs, this option requires Cantemo 6.3. On Cantemo 6.2, use --create-mcp-settings --set-admin-basic-auth instead. The Basic option also works on Cantemo 6.3. Do not use these built-in admin credentials in production. Use a dedicated service account with the required RunAs permission.

Client authentication with API tokens

As an alternative to OAuth, a client can send a Cantemo user’s API token as an Auth-Token header with every request to /mcp. The tools then run with that user’s Cantemo permissions. This pass-through works on both Cantemo 6.2 and Cantemo 6.3 and does not use RunAs.

Give users the MCP URL and a dedicated, least-privilege token. The client must not send an Authorization header at the same time; MCP returns HTTP 400 when both headers are present. A client Auth-Token takes precedence over API_KEY_APITOKENAUTHENTICATION and does not require OAuth configuration. Revoke tokens when no longer needed. SSO-only users need an administrator to create or assign a token.

OAuth and OIDC login

OAuth authenticates MCP clients. It is separate from the static Cantemo service credential: a validated client token supplies the Cantemo RunAs username. The username claim must match a Cantemo user. The static service credential authenticates the account that performs RunAs, which requires Portal administrator permission. Use HTTP Basic on Cantemo 6.2. Cantemo 6.3 also supports an API-token service credential; Basic remains valid.

Configure an HTTPS identity provider that issues signed JWT access tokens and supports authorization-code flow with PKCE S256. Register each MCP client’s exact callback URL. A public client commonly uses client ID cantemo-mcp with no client secret.

Add the public MCP URL and identity-provider settings to /etc/cantemo/portal/mcp-settings.env:

MCP_RESOURCE_URL=https://cantemo.example.com/mcp
MCP_BEARER_ISSUER=https://keycloak.example.com/realms/cantemo
MCP_BEARER_AUDIENCE=https://cantemo.example.com/mcp
MCP_BEARER_JWKS_URI=https://keycloak.example.com/realms/cantemo/protocol/openid-connect/certs
MCP_BEARER_USERNAME_CLAIM=preferred_username
MCP_BEARER_ALGORITHMS=RS256

Use either MCP_BEARER_JWKS_URI or MCP_BEARER_PUBLIC_KEY, not both. Recreate the container after changing OAuth settings. An unauthenticated MCP request should return 401 Unauthorized with a WWW-Authenticate header that points to the OAuth resource metadata:

curl -i https://cantemo.example.com/mcp

Do not send an Auth-Token together with an OAuth Authorization header.

Logs and auditing

MCP writes structured JSON logs to stderr, captured by Docker:

docker logs --since 1h cantemo-mcp
docker logs -f cantemo-mcp

Use LOG_LEVEL=info in production. Set debug only while investigating a problem, then recreate the container. MCP log entries identify the tool, HTTP method, API path, response status, duration, and errors. They do not log tool arguments, response bodies, API tokens, or bearer tokens.

The installer rotates Docker logs at 50 MB, which is not an audit-retention policy. Forward Docker, Nginx, and Cantemo logs to the organization’s central, access-controlled log system and retain them according to its security and compliance policy. Use Cantemo and Vidispine audit facilities to investigate the resulting API operation and user identity.