Studio Backend
Integrations and External Services
This page covers two things: the integrations a project configures for its agents — webhooks around a conversation and MCP tools during it — and the external services the platform itself depends on.
Project Integrations
Integrations are stored in the project document and executed by the engine. The Studio, the AI
Assistant and the MCP server all manage them through ProjectRestController:
| Kind | Stored in | Create / edit | Executed |
|---|---|---|---|
|
|
|
At the start of a session; the result is given to the model (relational agents use it in their planning prompts). |
|
|
|
During the conversation, as tools of the agent. |
|
|
|
After the session ended, for example to deliver the transcript. |
|
|
|
During the conversation, as tools of the agent, with the end user’s own connection to the service. |
GET .../integrations returns the four lists, and every row carries its type. Deleting is one
endpoint for all kinds, DELETE .../integrations?type={pre|during|post|connector}&ref={ref}, so the
kind must be known to delete a row. Code that opens, edits or deletes an integration by ref has to
branch on its kind first — the pre/post and the MCP-server edit views each look the ref up only in their
own list, and a ref routed to the wrong one never resolves.
Webhooks
A webhook has a name, a method (GET or POST), a URL, headers, enabled, a timeout in
milliseconds (default 5000) and a number of retries (default 0). A pre-session webhook also has an
llmDescription, which tells the model what the returned data means, and a default value. Header
values typically hold credentials; the MCP server masks them in its results.
MCP Servers
An MCP server integration names a URL, a transport (StreamableHttp, the default, or SSE), an
optional authentication header and the tools the agent may use. When it is saved, McpServerService
connects to the server as an MCP client, initializes the session (timeout 20 seconds, adjustable with
the PROMETHIST_MCP_REQUEST_TIMEOUT_SECONDS environment variable), lists its tools and stores the
selected ones as full tool specifications. A Streamable HTTP URL without a path is called at /mcp.
GET /api/v1/integration/mcp/load-tools runs the same probe without saving anything; the Studio’s
Try connection button and the MCP tool load_mcp_tools use it.
The selection is intersected with what the server advertises. An empty selection therefore stores no tools, and an integration without tools gives the agent nothing at conversation time — the most common reason an agent "does not call" an integrated tool.
MCP Connectors
Connectors are MCP servers from a global catalog (McpConnector in promethist-common, collection
mcpConnector), each with a name, description, icon, the server URL and its tool list, and an OAuth
client registration. The catalog is maintained on the engine side, which also refreshes each
connector’s tool list and keeps the end users' connections to the connected service.
A project enables a connector by its ref with a subset of the catalog’s tools
(McpConnectorIntegrationService); an unknown tool name is rejected. GET .../integrations/connectors
merges the catalog with the project’s state: each entry says whether it is enabled and which tools
are selected, and a connector that is enabled but no longer in the catalog shows up with its ref as
its name and should be removed.
Referencing Integrations from Agents
Global integrations are available to every agent of the project. The tools of a non-global MCP server or
connector are served only to agents whose instructions name each tool, as @MCP:{ref}_{toolName}; (the
match is case-insensitive and the trailing semicolon is required). The whole-integration form
@MCP:{ref}; records a dependency but exposes no tools on its own.
The ref of an MCP-server integration is derived from its name when it is saved, so renaming one can change its ref and break the references to it; webhook and connector refs stay stable.
Identity Providers
Projects that admit end users through their own identity providers name Keycloak identity-provider
aliases (Identity Providers and External
Roles). KeycloakIdentityProviderProbe checks a new alias against the realm the backend trusts without
admin credentials: it reads the realm’s discovery document and then calls the broker endpoint
{realm}/broker/{alias}/endpoint, which answers 404 only for an unknown alias. An unreachable
Keycloak yields "unknown" rather than a verdict, and the save goes through. Probing a known OIDC alias
leaves a failed identity-provider login event in Keycloak.
Secrets
| Secret | Handling |
|---|---|
LiveAvatar API key |
Stored per account or per project in the |
Vault values |
|
Re-encoding of Uploaded Videos
A video uploaded to an interactive-content element — in the Studio (POST /api/v1/project/multimodality-video)
or through the MCP tool upload_interactive_content_file — is stored as uploaded at
platform/project/{id}/multimodalInteraction/{ref}.mp4 and served from there right away, whatever its
container. ProjectService then queues its re-encoding with VideoTranscoder: the original is downloaded
into a temporary directory, re-encoded with ffmpeg, and the copy stored next to it as
{ref}_encoded_v{n}.mp4, n being ENCODE_VERSION. The element’s url is then switched to the copy by a
compare-and-set that matches only while the element still serves that very upload, so an edit, a newer upload or
a removal made in the meantime wins. The copy is then deleted again, unless an element serves it after all — a
form saved with the address of the copy an earlier upload made. An element missing at that point is looked up
once more a moment later, as an edit replaces an element by removing it and adding its new version. The copy’s
address keeps the original’s q query: when a form opened before the switch sends the original’s address back,
the update recognizes the same upload and keeps the copy.
| Part | Encoding |
|---|---|
Video |
H.264 High, 8-bit 4:2:0 ( |
Audio |
AAC-LC, stereo, 128 kbit/s (less at a low sample rate). |
Container |
MP4 with the index first ( |
The original’s video bitrate is that of its video stream. A container that stores none per stream (WebM,
Matroska) gives the file’s bitrate less the audio’s, and a file without a duration — a browser’s recording —
gives its size over the time of its last frame, which a second ffprobe pass reads; when none of that is known,
the copy gets 3 Mbit/s. A browser’s recording also declares 1000 frames a second; the cap of 60 keeps it from
being encoded at that rate. The decoder buffer (-maxrate, -bufsize) is sized for at least 200 kbit/s, so that
a keyframe of a low-bitrate video still fits it. A copy can come out larger than its original, for one because the
audio is always stereo, and it replaces the original all the same.
These settings are constants of VideoTranscoder, and changing any of them means bumping ENCODE_VERSION, so
that copies made with other settings stay recognizable by name. The originals are kept for the same reason: a copy
can always be made again from its original. Only new uploads are re-encoded — an element keeps serving whatever
its url names, and a video given by URL is never re-encoded.
ffmpeg and ffprobe read only what an uploaded video needs: the containers MP4, MOV and 3GP, WebM and Matroska, and
Ogg; the decoders of the common video, audio, subtitle and cover-art codecs (DECODERS); and pictures of at most
4096 × 2304 pixels, any 4K video. A file with anything else — an AVI, a QuickTime Animation track, an 8K or a 5.7K
video — fails and keeps its original; a picture too large is refused before it is decoded.
ffmpeg runs as a child process in the backend’s container and shares the pod’s memory with the JVM, which shapes how it runs:
-
One job at a time, on a single worker thread where it may block, though every transfer and database call has a timeout; at most 20 more jobs wait, and a further one is dropped — its element keeps the original.
-
A newer upload to the same element, clearing its URL or removing it cancels the element’s job and kills its ffmpeg. Clearing and removing delete the original and every copy. Cloning copies the original and, when the element serves one, its copy; a clone left serving its copy of the original is re-encoded like an upload.
-
ffmpeg and ffprobe run with
nice -n 10and, whereprlimitexists (it does in the backend image), with their memory capped atpromethist.video-transcoding.memory-limit(768 MB): an encoding that needs more fails. ffmpeg has two decoder, scaler and encoder threads each, and is killed afterpromethist.video-transcoding.timeout(30 minutes). The startup log says which of these apply. -
One encoding peaks at about 280 MB for a 1080p source and about 450 MB for a 4K one, which is what the backend’s memory above its heap is for (Helm Charts). Storing the copy holds it on the JVM heap while it is written, as storing an upload of the same size does.
A failure — no ffmpeg, a file ffmpeg cannot read or does not take, an ffmpeg error or timeout, a storage error — is
logged as a warning and leaves the original served, as does a restart in the middle of a job. Without ffmpeg and
ffprobe on the PATH, or with promethist.video-transcoding.enabled=false — as in issue environments — nothing
is re-encoded (Video Re-encoding settings).
External Services of the Platform
| Service | How the platform uses it |
|---|---|
Keycloak |
Issuer of Studio users' access tokens ( |
Azure OpenAI, OpenAI |
The AI Assistant (AI Assistant), knowledge descriptions, the initial plans of relational agents, embeddings and transcription (Knowledge Processing, AI settings). |
Azure AI Document Intelligence |
Layout analysis of knowledge documents. |
Stripe |
Billing (Usage and Billing). |
Mailgun |
Invitation emails. |
Blob storage |
|
ffmpeg |
Re-encoding of uploaded videos (Re-encoding of Uploaded Videos), installed in the backend image from the Ubuntu packages. |
Promethist asset repository |
The avatar and environment catalog ( |
Bridge, engine |
Test suites and live tests (Test Suites), voice previews. |
Zendesk |
The Studio’s contact form ( |