Platform MCP
Streamable HTTP MCP for a Confluye workspace (JSON-RPC 2.0): read tools plus scope-gated typed write and Command tools.
Table images use tables.imageTarget (read a stable row ID and revision) and tables.setImage
(set or clear one image cell). tables.setImage takes tableId, zero-based rowIndex, column,
fileId (or null), imageTarget, confirm: true, and idempotencyKey. Inspect the target before
confirmation, then keep it for the mutation. A stale row returns a conflict. Replay the same confirmed
request and key to recover a committed result. Upload bytes through Files first.
tables.get, image targets and Files metadata/content require restricted workspace data access;
organization visibility alone is insufficient. Command Center can prepare the same confirmed change
when you identify the table, image column, record, and ready attached image. Ambiguous records require clarification.
External clients call one workspace at the workspace endpoint below, or the whole organization at the organization endpoint:
POST /api/v1/platform/mcp/{workspaceId}
Product setup (OAuth, Claude Code, ChatGPT): MCP. Path and env examples may
still say FLUXUS_*; those are compatibility names for the same Confluye host and token.
GET on this URL returns 405 with Allow: POST. There is no SSE GET transport.
Native-service guidance
Workspace and organization initialize responses include result.instructions asking the client to
suggest native databases/tables for migrations, internal Knowledge for RAG, and Workspace Files for
assets when they fit the request. The same guidance is in the capabilities.list description returned
by tools/list and by tools/call with includeOperations: true, including clients that skip
initialization. Organization initialization also explains workspaces.list and workspace targeting.
The stable capability catalog is metadata, not an authorization or readiness guarantee. Check the
session's tools/list before invoking a tool; rollout flags and permissions can hide operations.
File upload remains an authenticated Files HTTP API or app operation. Table
image cells retain file references; production bytes live in private object storage. Suggestions
respect the user's external-service choice and preserve every operation's existing authorization,
confirmation and idempotency requirements. See migration guidance.
Personal website sessions
The preview browser.list tool lists the authenticated user's personal browser connections in the
selected workspace. Both browser tools are available with mcp:read when capability waves are enabled;
browser.read additionally requires current workspace execution access, so a Viewer cannot call it.
browser.read accepts { connectionId, url } and requires
Allow my agent to read enabled on that connection.
It reopens the saved browser, verifies the account, then returns { url, title, text, links } for an
allowed HTTPS page. Website text is untrusted data. Cookies and internal browser handles are never
returned. Configure the site and complete interactive login in My browser connections.
The tools cannot create a login session, grant themselves permission or submit forms. Session expiry
requires the owner to reconnect. Removing agent read access stops subsequent tool reads.
An absent connection, another person's connection, or a connection without agent read access returns
-32004 with browser_connection_not_found. On the organization endpoint, include workspaceId;
reading a page requires a write-capable organization grant because a read-only grant excludes execution
authority. An organization connection still uses only the calling member's own website login.
Organization invitations and billing
organization-invitations.list, organization-invitations.cancel and billing.checkout are available
with mcp:write and capability waves enabled. All three require explicit current workspace execution
access, current organization Owner/Admin membership and human MFA approval. A workspace Owner does
not inherit organization administration.
Pass confirm: true and an idempotencyKey. List accepts optional cursor and limit (1–100);
cancel takes invitationId. Checkout takes planKey (trial, starter, pro, team), optional
overageEnabled and overageCapMinor; enabling overage requires an explicit cap in minor MXN units.
Organization connections additionally supply workspaceId within their grant.
The first call returns status: "authorization_required", an opaque authorizationRequest and an
approvalUrl. The same user opens the URL, verifies MFA and approves the exact displayed action.
Repeat the original call with authorizationRequest to receive status: "completed" and result.
An unapproved MCP request returns no invitation data and performs no mutation. Human approval is never an MCP tool.
Invitation-list results are approved snapshots with invitations and nextCursor; a different page
needs its own request and approval. Checkout's result contains the canonical trial receipt or Stripe
Checkout URL; payment completion is still human-controlled.
Request links expire after ten minutes. The one-use approval expires after at most five minutes, capped by the remaining MFA window. Changes to the reviewed invitation or plan invalidate it. Retry a completed call with its original arguments and key to recover the committed receipt. Current role revocation still refuses retries. An uncertain Checkout failure is quarantined by idempotency; it is not automatically repeated. The canonical billing service retains its abandoned-session recovery, reservation fencing and payment-in-progress protections.
Auth
Bearer required. Missing token: 401 JSON-RPC -32001 Bearer token is required. plus
WWW-Authenticate: Bearer resource_metadata="<origin>/.well-known/oauth-protected-resource/api/v1/platform/mcp/{workspaceId}".
API key. Must verify for {workspaceId} and have a userId. A workspace key must belong to
that workspace. An organization key is also accepted when {workspaceId} is inside its workspace
selection and organization; its authority is capped by the key's permission and the user's current role
there, and it never carries AIMS service-token assurance, so workflows.deploy, aims.governance.get
and credentials.create stay unavailable to it. Queued workflow execution rechecks the key’s current write permission, organization and workspace selection before running or resuming; revoking or narrowing the key prevents further execution. Wrong workspace, or a workspace outside an organization
key's selection: 403 -32003 API key cannot access this workspace.; personal or copilot key: 403
-32003 A workspace API key is required.
For workflows.deploy, the key supplies AIMS service_token assurance. Its acting user must also
hold a current AIMS assignment for this workspace (or its organization) with role aims_manager or
system_owner and requiredAssurance: service_token.
OAuth. If the bearer is not an API key, Confluye verifies an access token whose audience
is the public resource URL (BETTER_AUTH_URL origin when set, else the request URL). Token
workspaceId must match the path and scopes must include mcp:read. User must still be an
active workspace member. Failures: 401 (unverified) or 403 -32003
(OAuth token cannot access this workspace. / Active workspace membership is required.).
OAuth sessions never advertise or authorize workflows.deploy; browser MFA does not turn an OAuth
grant into AIMS service-token authority. A workspace OAuth token's granted scopes are narrowed at
authorize and refresh time to the resource row's allowedScopes, so a token never carries a scope
outside the resource's policy — a read-only resource (mcp:read without mcp:write) yields only
read-only tokens. API-key auth is unaffected: a workspace API key's authority still comes from its
own scope and the acting user's workspace role.
Protected-resource metadata (GET well-known, Cache-Control: public, max-age=300):
{
"resource": "https://host/api/v1/platform/mcp/{workspaceId}",
"authorization_servers": ["<issuer>"],
"scopes_supported": ["mcp:read", "offline_access"],
"bearer_methods_supported": ["header"],
"resource_name": "Confluye Command Center MCP"
}
Protocol
JSON-RPC 2.0. Header MCP-Protocol-Version: 2025-06-18 on responses. Server info
confluye-platform 0.1.0. Max body 1 048 576 bytes (413 -32010).
method | Result |
|---|---|
initialize | protocolVersion, capabilities.tools.listChanged: false, serverInfo |
tools/list | { tools } — stable reads plus read-only authoring tools for every session; write tools when the session holds mcp:write and the corresponding rollout flag is enabled; workflows.deploy only for an AIMS-authorized workspace API key |
tools/call | { content: [{ type: "text", text }], structuredContent } |
Notification (no id): HTTP 202 empty body. Unknown method: -32601. Invalid JSON-RPC:
400 -32600. Parse error: 400 -32700. Invalid tool params: -32602. Capability
availability: -32020. Expected missing resources use -32004; stale proposals and other expected
conflicts use -32009.
Not found and pagination. A tool that reads one resource by id (workflows.get, runs.get,
human-tasks.get, documents.get, knowledge.get, tables.get, catalog.get, mcp-connections.get,
files.get, mailer.get, rss.get, aims.governance.get, command.status) answers -32004 with a
*_not_found error.data.code when the id does not exist in the workspace; it never returns a null
resource. A tool that acts on one resource by id answers the same way. List and search tools answer an
empty page instead. skills.load is the exception: an unknown skill and one the principal may not use
both answer -32003 skill_not_available, so existence is not disclosed. A cursor a list never issued
is refused with -32602 / invalid_cursor, never treated as the first page; omit it to start over.
billing-usage.costs also refuses an issued cursor when its filtered evidence changed between pages;
restart without it and discard the previous pages and totals.
workspaces.list for a workspace API key has a single page (the key's workspace), so any cursor is
refused. The REST API and the app keep their own not-found responses. Unexpected failures remain masked as -32603 Platform MCP request failed.
with only a correlationId in error.data; see Errors.
capabilities.list advertised input allows maturity: "stable" only. The runtime parse
accepts preview | stable then forces maturity: "stable" before execution.
Read tools
Workflow destinations are exposed through workflows.targets and workflows.preview-target-publication. The first lists Production and named preview identities and state, plus each destination's event sources: RSS feeds, mail routes and Jenkins sources with their interval, last poll, pause and error, and per-job baseline, last build seen and skipped builds. It never returns credential secrets or the Jenkins server address. Optional includeHistory: true adds the latest 30 production deployments for rollback selection. The second requires execute authority and checks an exact workflowId, targetId, versionId, expectedRevision and UUID requestId, with optional promotion or historical activation context. A readiness result does not activate a destination or authorize a later changed request.
workflows.update-target accepts workflowId, confirm: true, an idempotencyKey, and the same change actions described in Workflow destinations. It requires write and execution authority. The publish action also requires the existing AIMS service-token and deployment authorization; an agent cannot create a human approval. Promotion carries its exact source and stop-source choice, and rollback checks the historical production snapshot again. The configure-source action adds or updates an RSS or Jenkins event source of a destination with the same checks as Versions & previews. It carries the destination's expectedRevision, and a destination whose saved version changed since that revision is refused with -32009 / workflow_target_revision_conflict without storing the source: the Jenkins connection must be one the acting user may use, job, result and interval limits apply, and a source is refused when the workflow would start a job it watches. An RSS source needs a Start trigger or a
plain Webhook trigger in the destination's saved version; otherwise it is refused with event_source_trigger_not_found,
because Jenkins, Sentry, app and schedule triggers only start from their own source. It does not need AIMS deployment authority. Mail and Sentry sources return a one-time secret and stay in the destination editor. Publishing a destination whose version has a Jenkins trigger fails with jenkins_source_required until its source exists.
These carry mcp:read and are advertised to every authorized session, except workflows.get and
runs.get, which return full definitions and run payloads and so need secret-read or write authority:
Viewers, derived organization access, and read-only organization grants do not see them. The set is derived from the
runtime registry (platformMcpReadToolNames); the exact advertised inventory is verified by
pnpm docs:check, so this table cannot silently drift from the server.
| Name | Scope | Purpose |
|---|---|---|
workspaces.list | mcp:read | Visible workspace directory with bounded search and cursor pagination |
capabilities.list | mcp:read | Capabilities, operations, readiness |
billing-usage.get | mcp:read | Read the workspace compute-usage / billing summary (no card data) |
billing-usage.organization | mcp:read | Owner/Admin organization usage grouped by workspace, workflow, and actor |
billing-usage.costs | mcp:read | Owner/Admin organization compute, estimated AI costs and equivalent credits; workflow/user/workspace/node/invocation rankings, daily series and coverage |
workflows.list | mcp:read | Workflows |
workflows.get | mcp:read | One definition plus candidate/active/synchronized execution state |
runs.list | mcp:read | Runs |
runs.get | mcp:read | One run, paginated steps/logs, optional redacted payload windows |
runs.ask | mcp:read | Answer a question from bounded, safe execution evidence and validated run citations |
documentation.ask | mcp:read | Answer a question from public Confluye documentation with page citations |
themes.get | mcp:read | Read the workspace's validated shared light/dark palette, or null for default colors |
human-tasks.list | mcp:read | Monitor Human Tasks (read-only metadata; never drafts/form values) |
human-tasks.get | mcp:read | One Human Task's monitoring view; completion stays human-only |
documents.list | mcp:read | Documents |
documents.get | mcp:read | Details / optional text |
knowledge.list | mcp:read | Knowledge bases |
knowledge.get | mcp:read | Metadata |
knowledge.search | mcp:read | Indexed knowledge |
tables.list | mcp:read | Tables |
tables.get | mcp:read | Table + bounded rows |
Managed configuration read tools are advertised with the same workspace-scoped authorization as the Tables surface:
| Tool | Scope | Purpose |
|---|---|---|
configuration.catalog | mcp:read | List database and table metadata, contracts, and affected writers; undeclared tables expose metadata only, never rows |
configuration.readRows | mcp:read | Read bounded rows from one declared managed table |
configuration.export | mcp:read | Export selected declared tables as a bounded package |
configuration.validate | mcp:read | Validate a package without changing workspace data |
configuration.preview | mcp:read | Calculate actions and blockers without writing |
configuration.history | mcp:read | Read workspace-scoped application receipts |
Successful configuration.catalog responses include applyEnabled and applyDisabledReason
(null when enabled, writes_disabled when the deployment flag is off). These describe deployment
availability; each write still requires the caller's workspace authorization. Export reads the
selected tables from one consistent database snapshot.
Write-capable sessions may see these managed configuration operations:
| Tool | Scope | Purpose |
|---|---|---|
configuration.prepare | mcp:write | Persist a proposal and return a human-review panel link |
configuration.apply | mcp:write | Apply a confirmed plan with its confirmation and request identity |
configuration.declare | mcp:write | Declare one non-sensitive table as managed configuration |
configuration.writeRow | mcp:write | Create or update one row by stable key and expected revision |
configuration.apply, configuration.declare, and configuration.writeRow each require both
confirm: true and a nonempty idempotencyKey. An idempotency key does not count as confirmation.
For configuration.apply, this envelope is additional to the human-issued plan confirmation.
Repeating the same operation with the same user, workspace, key and arguments returns its stored
result while the idempotency record is retained. Reusing that key with different arguments is a
conflict, and current permissions are checked before a stored result is returned. If declaration or
a row write reports an unknown outcome, inspect the table before attempting another write. Apply
also retains its separate durable requestId receipt recovery. An authorized retry can recover a
stored result for apply, declaration or a row write while new writes are disabled. This replay
does not authorize a new mutation.
History receipts explicitly report status: "applied". configuration.prepare declares no
idempotency: each call persists a new expiring proposal, and an idempotencyKey argument is rejected
with -32602.
There is deliberately no configuration.confirm tool. The authenticated human Tables panel issues
the confirmation; an agent can prepare a proposal and then use the resulting confirmation only when
the same user and workspace authority are still valid. Managed configuration has no SQL override or
delete operation, and drift remains a blocker.
Inspecting large executions
runs.get returns run metadata, node errors, and the first ten steps and logs by default.
Input/output and log metadata are omitted by default (input is null), so a large execution
remains inspectable without downloading its full payload. Use nodeId to select a node's steps.
This filter does not filter the run-wide logs.
Use stepOffset and logOffset independently with page.steps.nextOffset and
page.logs.nextOffset. stepLimit and logLimit accept 1–10; null next offsets mean the
end of that collection. Page totals describe the selected collection; the run's stepCount
and logCount describe the entire execution. Offsets are best used after the execution finishes;
an active execution can add or update entries between requests.
{ "runId": "run_example", "nodeId": "codexCli_9" }
At the workspace endpoint, the URL binds the workspace for runs.get; do not add workspaceId to its tool arguments.
Set includePayloads: true to include input, output, and log metadata as redacted JSON text
windows, each with format: "json-text", text, offset, totalChars, and nextOffset.
Each window contains up to 2,048 JavaScript string characters. Request the same run/node/page
with payloadOffset set to that field's nextOffset, concatenate its text windows in order,
and JSON-parse only after reaching null. Offsets apply independently to every payload in the
response; follow each field's own cursor. Secrets are redacted before splitting into windows.
Messages and errors have bounded previews; messagePayload contains a log's full redacted
message when payloads are requested. Payload omission and pagination do not mean data was lost.
Agents may monitor Human Tasks through human-tasks.list / human-tasks.get, but claiming,
drafting, and completing a Human Task are human-only actions and are never advertised as tools.
Workflow authoring and debugging
Read-only clients can search the current block catalog, inspect a block's config template/schema, validate a
candidate, and obtain a secret-safe diff. Debug actions require mcp:write, a current Member-or-higher role,
confirm: true, and an idempotencyKey.
| Name | Scope | Purpose |
|---|---|---|
catalog.search | mcp:read | Search allowed blocks by label, aliases, category, description, and runtime type |
catalog.get | mcp:read | Read one block's config template, schema, and for integration blocks how to fill the node |
workflows.validate | mcp:read | Validate graph structure, credentials, integrations, schedule mode, and version |
workflows.diff | mcp:read | Separate topology, config-key, credential-reference, and trigger-schema changes |
workflows.prepare-test-data | mcp:write | Persist a redacted pin for one node against an exact workflow version and optional executionTargetId |
workflows.test | mcp:write | Run the candidate version in simulated dry-run mode |
workflows.file-test-start | mcp:write | Open a 120-second, single-submission candidate file listener; returns metadata without its upload token |
workflows.file-test-status | mcp:read | Read the owned listener's state and test run ID, with current execution permission |
workflows.file-test-cancel | mcp:write | Cancel the owned file listener |
workflows.execute-step | mcp:write | Execute one node or bounded path; live effects require an explicitly pinned version |
Integration blocks (ids starting with manifest.) carry a manifest object. In both tools it lists the
operation's requiredInputs. catalog.get adds inputSchema, the JSON Schema of config.manifestInput with
types, required fields, defaults and limits; credentialBinding, which names the accepted credential types and
how to copy a credential's id, type and name from credentials.list into config.credentialBindings;
and exampleNode, a minimal node whose <...> placeholders you replace before workflows.validate or
workflows.propose. The example includes every required field: a list with a minimum length has that many
items, a nested object has its required fields, and a free-form object or list item is a <field> placeholder
named after its key. When the operation accepts one credential type, the example binding already has it; when it
accepts several, credentialType is a <credentialType> placeholder that you fill with the type of the
credential you chose in credentials.list. That binding is enough: validation, deploy checks and runs all resolve the node's credential
from it, so config.credential is optional. For Jenkins: Trigger build and wait for result, jobName is
required, timeoutMinutes defaults to 60 (1 to 10,080), parameters is an optional list of up to 100
{ name, value } pairs, and the credential type is jenkinsApi.
The input schema has an 8,000-character limit. If editor-only serialization hints would exceed it, the catalog omits those hints while keeping JSON Schema validation keywords and business property names. A schema that still exceeds the limit is omitted. Cross-field choices, such as an update field or custom report dates and metrics, remain deliberate caller input; the example does not choose them.
Dynamics account actions, Firestore document and collection actions, and GA4 report.run use this same
workflow path on both workspace and organization MCP endpoints. Discover their manifest. block with
catalog.search or catalog.get, fill manifestInput, select a credential, then validate, propose and apply
the workflow. On the organization endpoint, supply the selected workspaceId on each call. Applying a draft
requires confirmation and an idempotency key; it does not authorize a provider request. Prepare upstream test
data before executing a segment. With allowRealEffects: false, workflows.execute-step simulates effects;
a live segment also requires the current version and the native action's account, literal acknowledgments,
current actor permissions and exact-request Human approval. MCP does not bypass those controls or grant
product-gateway identity or end-user consent.
For a simulated single node (startNodeId and stopAfterNodeId equal), a saved expectedVersion
and compatible upstream pin do not require that node's provider transport to be enabled. The result
and same-key replay remain durable, and the transport setting stays unchanged. This exemption applies
only to the selected node. If missing pins or stale run data require upstream refresh, those upstream
nodes retain normal transport admission and runtime execution; this is not a whole-graph no-egress promise.
Structural, schema and selected-account scope checks still apply. The expected-version check and stale-pin
check are separate fences: a matching version does not make outdated pin data valid. Pins and simulated
results never authorize live effects.
Validation checks each enabled integration node's config.manifestInput against that inputSchema. A missing
required field or a value out of range is an error that names the field, such as
needs config.manifestInput.jobName, so the report has valid: false and the graph is not deployable. Integration
tools receive only config.manifestInput; fields placed next to it in config are not sent. A {{expression}} is
accepted in a field that takes text, because it is rendered to text at run time; it is refused in a number, boolean
or list field. Only a complete, non-empty expression counts: an unterminated {{ or an empty {{ }} is an error
that names the field, and the value is then checked like any other text. An exampleNode placeholder left in
manifestInput or credentialBindings (for example <credentialName> or <credentialType>) is also an error.
workflows.diff accepts the same optional expectedVersion as workflows.apply. When it is supplied and
no longer matches the workflow, the diff is refused with the same -32009 / workflow_version_conflict
(with details.expectedVersion / details.currentVersion) apply would return; when it is omitted, the diff
is computed against the current version. workflows.diff never returns credential values. Non-template credential fields are reported only as
<configured>. workflows.execute-step rejects stale pins when the source node config changed, rejects a
stop node that is not reachable from the start node, and reports the run id plus the node ids actually executed.
Typed publication lifecycle
For Claude Code and Codex nodes, call catalog.get with blockId: "ai.claude-code" or blockId: "ai.codex-cli" respectively to read the installed CLI's
available model IDs in configSchema.properties.model.enum. Select one explicitly; an agent must
not invent a model ID from its own model list or from another CLI version.
The server checks the scoped workspace catalog during proposal, validation, apply, partial edits,
destination candidate saves, and deployment. Apply checks again even if the earlier proposal passed.
workflow_cli_model_unavailable rejects absent or ambiguous IDs without saving the graph.
workflow_cli_model_catalog_unavailable is retryable after correcting the connection/runtime;
catalog discovery does not send a prompt. AIMS independently checks the saved version before review
and approval, so another editing surface cannot bypass the availability requirement.
workflows.propose is strictly non-mutating, carries the workflow read permission, and is advertised
to every authorized session (mcp:read). The mutating operations — workflows.apply,
workflows.update-partial, workflows.deploy, and workflows.execute — are advertised and callable
only when the session holds mcp:write; read-only sessions neither see nor can invoke them.
workflows.deploy is further restricted to an AIMS-authorized workspace API key and is hidden from
OAuth discovery. All five reuse the canonical capability executors, so the same validation,
confirmation, and idempotency rules apply on every surface.
| Name | Scope | Purpose |
|---|---|---|
workflows.propose | mcp:read | Read-only: validate a bounded graph + schedule mode + expected version; returns a semantic diff, validation report, blocked modes, missing prerequisites, and a planHash. For an existing workflow, an omitted expectedVersion is pinned to its current version. Adaptive-job recurrence is reported as a non-deployable blocked mode (it stays on the Command scheduling path) |
workflows.apply | mcp:write | Create/update a draft from the same hash-bound arguments sent to workflows.propose, plus planHash, idempotencyKey, and confirm: true |
workflows.update-partial | mcp:write | Apply a small ordered batch of typed surgical edits — mergeNodeConfig, renameNode, moveNode, addNode, removeNode, addEdge, removeEdge, setEdgeLabel — to the workflow's current candidate graph in one call, bound to an optional expectedVersion, idempotencyKey, and confirm: true; no separate propose step |
workflows.deploy | mcp:write | Deploy the pinned draft version, bound to planHash, expectedVersion, scheduleMode, idempotencyKey, and confirm: true; requires an AIMS-authorized workspace service-token key |
workflows.execute | mcp:write | Run active (default) or candidate, bound to idempotencyKey and confirm: true; poll the returned run id through runs.get |
For cable routing, include an ordered zipTies array on each affected edge in workflows.propose
and send the same graph through workflows.apply. Each guide is { "id": "tie_shared", "x": 300, "y": 200 }.
Use the same guide ID and coordinates on the cables it joins. Change its coordinates on every
member cable to move it; remove it from their arrays to release it. Empty arrays remove all guides
from an edge while keeping the connection. Guide changes are included in the confirmation hash,
so moving a guide after review requires a fresh proposal. They appear in the proposal's
diff.routedEdges and in workflows.diff's routing list, with previous/current guide points and
layoutOnly: true; routing alone does not add or remove a connection. This uses the existing write permission,
confirmation, idempotency and audit path on both workspace and organization MCP endpoints; the
organization endpoint also requires the authorized target workspaceId.
For an existing target, callers may omit expectedVersion from workflows.propose: the server pins the
current version while constructing the proposal. workflows.apply may omit it again or pass the exact
expectedVersion returned by workflows.propose; both produce the reviewed normalized intent. A changed
target, argument (including node position), schedule mode, or expected version reshapes the
server-normalized planHash, so apply returns the actionable conflict -32009 /
workflow_proposal_stale and requires a fresh workflows.propose. expectedVersion is also rechecked
atomically under the workflow's advisory lock. An invalid confirmed graph returns -32602 /
workflow_invalid; unexpected implementation or storage failures remain masked as -32603.
validation.valid checks the graph and explicitly selected MCP tools. Proposal and validation check
that each selected tool belongs to this workspace, is enabled in the current discovery snapshot,
and can be attached through its runtime server. A changed pinned schema is also invalid. Unknown,
disabled, retired, or incompatible tool references make the proposal invalid; apply and
update-partial check again before saving and reject them without creating a workflow or advancing
its version. These checks read the stored discovery state; they do not contact the remote server or
guarantee that it will stay available later.
For Agent and CLI attachments, use <serverId>:<toolName> from the runtime server. When an MCP
connection has a legacyMcpServerId, that is the server ID used by these attachments; the governed
connection ID remains its discovery identity. AIMS resolves the compatible server reference to the
same current, workspace-owned tool material during approval. Checking that material neither creates
nor grants an AIMS approval.
A valid graph can still be saved as a draft while other workspace prerequisites such as a
credential or a connected integration are missing. deployable is true only when the graph is valid,
missingPrerequisites is empty, the schedule mode is supported and the version matches, because deployment refuses
those same prerequisites. Every tool input refuses properties its advertised schema does not list
(additionalProperties: false) with -32602, so a misspelled field is reported instead of ignored. Only the
mechanical workflow mode publishes through this path. Adaptive-job recurrence remains a Command
scheduling feature, but command.submit is not a governed deployment path.
workflows.deploy has an additional AIMS boundary: it is advertised and callable only for a workspace
API key, which supplies service_token assurance. The key's acting user must have a current
aims_manager or system_owner assignment whose required assurance is also service_token. OAuth,
including an MFA-authenticated browser session, cannot deploy through MCP.
The same authorized service-token principal can call the read-only aims.governance.get tool with a
workflowId. It returns only the current stage, next action, active/candidate version status, bounded
deployment-readiness blockers, and a URL that hands the human decision back to Settings → AI
Governance. It does not expose evidence, rationales, approval records, manifests, profiles, or hashes. A saved workflow
change the active governance version does not cover yet is reported as the review_required blocker; the same
next action on an already-current workflow is only the standing governance-only change opportunity and is not
reported as a blocker.
If the AIMS assignment lookup itself fails, only the AIMS capabilities fail closed: workflows.deploy
and aims.governance.get are neither advertised nor callable and return -32003, while every unrelated
MCP tool in the session keeps working. The server emits a sanitized operational signal naming the surface,
workspace, and client; no database or error detail reaches the client or the log line.
MCP does not create or approve AIMS assessments, choose a renewal policy, or activate an AIMS system
version. A human completes those controlled steps in Settings → AI Governance, including independent
approval and either a dated renewal or No scheduled renewal. Once that version is active and its
authoritative production manifest is current, the service-token principal may call workflows.deploy.
Do not submit a natural-language deploy request through command.submit; it does not replace the typed,
governed deploy operation.
workflows.update-partial
Unlike apply, update-partial needs no separate propose call: it takes workflowId, an ordered
operations array, an optional expectedVersion (defaults to the workflow's current version when
omitted), idempotencyKey, and confirm: true, and applies the whole batch atomically against the
workflow's current candidate graph in one call. Either every operation in the array applies and the
resulting graph passes validation, or nothing is persisted. A schema-invalid or runtime-failing operation
returns workflow_update_partial_operation_failed and names the failing operation's index and declared
op; a final whole-batch validation failure returns workflow_invalid with the graph validation reason,
without attributing it to a single operation. addNode accepts an optional caller-supplied
nodeId, which lets later operations in the same batch address the new node; the ID must be unique in
the workflow. addNode connects the new node automatically unless it passes autoConnect: false: a
trigger is wired to the first step, and any other node is inserted before the Output node (replacing the
edge into it) or appended after the last step. Pass autoConnect: false when the batch wires the node with
its own addEdge operations. addEdge likewise accepts an optional caller-supplied edgeId (same format
as a node id, unique in the workflow; a duplicate fails that operation), so a later setEdgeLabel or
removeEdge in the same batch can address the new edge. The result carries version, planHash,
applied (one { op, summary, warnings } entry per operation, with the resolved nodeId on addNode
and the resolved edgeId on addEdge), and the same diff / validation shapes
workflows.propose / workflows.apply already publish. workflows.update-partial only ever writes
name, description, nodes, and edges on the workflow's draft — it never promotes a workflow to
production; workflows.deploy remains the only path that activates a version.
MCP tool discovery advertises the full typed operation union. At tools/call time, operation validation
is deferred to the canonical executor so malformed entries receive the same indexed domain error as
direct executor callers instead of a generic boundary-schema error.
update-partial rejects with the same actionable conflict -32009 /
workflow_collaboration_session_active when a live-collaboration session is already active on the
workflow, so a human editing the graph in the canvas and a typed caller never race each other's edits.
The mergeNodeConfig operation deep-merges its patch object into the target node's existing config
using JSON Merge Patch (RFC 7396) semantics, with one deliberate deviation: an array-valued field merges
by index, so the patch array's value at index i overwrites the existing array's value at index i,
preserving any existing tail beyond the patch's length, rather than replacing the array wholesale. A
null value at a key deletes that key. A patch that touches a managed integration key (credential
bindings or manifest fields) is rejected — use the workflow's existing integration-config path for those
instead. Before recursive schema parsing, the operation input is limited to 16 nesting levels, 1,000
object keys, 100 values in any array, 5,000 decoded values, and 65,536 decoded JSON bytes. Exceeding a
limit returns workflow_update_partial_operations_invalid without evaluating or persisting the patch.
workflows.get returns executionState with candidateVersionId, activeVersionId,
reviewStatus, synchronizedVersionId, and reviewReady. workflows.execute defaults to the active
production version. Pass target: "candidate" to test the candidate; it defaults to dry run. A live
candidate execution additionally requires dryRun: false and the matching expectedVersionId.
Without both, it returns -32602 with live_candidate_confirmation_required. A stale expectedVersionId
returns -32009 with workflow_version_changed and names the current version, and running active before
any deploy returns -32009 with workflow_not_deployed.
The result carries requestedTarget, versionId, resourceMode, and reviewIdentity, matching the
run evidence shown to humans. Candidate and active are not isolated runtimes: a live candidate uses
real workspace resources, and active v1 remains production until an explicit deploy promotes v2.
Knowledge Document lifecycle
documents.status reads an owned document's current indexing generation, indexing status, and
knowledge-base links; it is non-mutating and advertised to every authorized session (mcp:read).
The lifecycle mutations are advertised and callable only with mcp:write. Every operation reuses the
canonical capability executor, so document/knowledge-base ACLs, the generation-aware CAS, and idempotent
replay behave identically on MCP, the API, the Agent Runtime, and the knowledgeDocument workflow node.
| Name | Scope | Purpose |
|---|---|---|
documents.status | mcp:read | Read an owned document's indexing generation, status, and knowledge-base links |
documents.create | mcp:write | Create an owned text document in explicit knowledge bases and enqueue indexing |
documents.edit | mcp:write | Replace owned document metadata/content and enqueue re-indexing |
documents.trash | mcp:write | Reversibly trash a document and drop it from keyword and vector retrieval |
documents.restore | mcp:write | Restore a trashed document under a fresh indexing generation |
documents.link | mcp:write | Link an owned document to owned knowledge bases and re-index it |
documents.unlink | mcp:write | Remove knowledge-base links and their retrieval eligibility |
documents.reindex | mcp:write | Issue a fresh indexing generation so a stale job cannot publish stale content |
Each mutation carries the same envelope as the publication lifecycle: confirm: true plus an
idempotencyKey (missing either is -32602 with zero executor dispatch). Pass the document's current
expectedIndexingGeneration to bind the mutation to the state you read from documents.status; a losing
compare-and-set fails closed without mutating. Repeating a mutation with the same key and arguments
returns the original result without a second write or generation, marked deduplicated: true (top-level for
documents.create and documents.edit, in result for the lifecycle operations); reusing the key with different
arguments, including a different expectedIndexingGeneration, is an idempotency_key_mismatch
conflict (-32009). A missing document or knowledge base is -32004, and a generation or state conflict
is -32009. Claiming, drafting, and completing a Human Task remain human-only and are never advertised
here.
Workspace management
workspaces.rename changes only the workspace display name. The slug, route key, URLs, id,
membership, and visibility are unchanged, so every integration, API key, agent card, and bookmarked URL
keeps working. It reuses the canonical capability executor and the same audited domain service the
Settings UI, tRPC, and REST rename share, so authorization and audit are identical on every surface.
| Name | Scope | Purpose |
|---|---|---|
workspaces.rename | mcp:write | Rename the workspace display name only; slug, URLs, and integrations stay stable |
The mutation carries the same envelope as the other lifecycles: confirm: true plus an idempotencyKey
(missing either is -32602 with zero executor dispatch; a repeated key replays the recorded result
rather than renaming twice). On the organization endpoint it is a workspace-bound tool: the call must name
its workspaceId, which is re-authorized against the caller's live membership and the grant's workspace
ceiling before it runs. There is no organization-global rename.
Bug reports
bugs.report saves a report in the selected workspace's audit log and queues it for the Confluye team,
using the same executor as the in-app form. It requires mcp:write and an explicit Member, Admin or
Owner role. Supply title (1–200 characters),
description (1–4,000), optional steps and expected (up to 2,000 each), and optional pagePath
(a local path without a query string or fragment). Do not include credentials or personal data.
Send confirm: true and a unique idempotencyKey; retry the same report with the same key to receive its
original reference. The response is { report: { id, createdAt }, deduplicated } after persistence succeeds.
The organization MCP endpoint additionally requires workspaceId within the connection's grant.
Administrators can find the report under the audit action bug.reported. The response contains no
internal delivery configuration. Receipt persistence and delivery enqueueing commit together; an
unavailable delivery destination rejects the submission without saving a report. A later delivery
failure does not remove an accepted report.
Optional attachments is an array of up to three distinct { fileId } objects. Each file must be a
ready, scanned PNG, JPEG or WebP image of at most 5 MiB, uploaded by the caller into the selected
workspace. The server verifies the image content and checksum, then sends a copy to the team. Other
workspace readers can access uploaded originals through Files. Deleting an original does not retract
the team's copy. References follow audit retention (365 days by default).
Upload bytes through the authenticated Files HTTP API or the app, then pass the returned IDs to
bugs.report; this operation does not accept base64 content or image URLs. There is no MCP byte-upload
tool. A client with only MCP OAuth authority needs a separate authenticated Files upload before it can
attach a new image. Existing ready files owned by that caller work through both MCP endpoints.
The same idempotency key with different attachments is a conflict. Each workspace may start at most 10 new
report attempts per minute. An attempt counts even if it later fails, for example because of an invalid
screenshot or an unavailable destination, so the eleventh returns rate_limited until the minute passes.
Idempotent replays of an accepted report do not count. The team receives every field in full.
Workspace role authorization
An OAuth or API-key scope states what the client was granted; it says nothing about what the acting
user may do. Every mutating tool therefore also passes the shared workspace role/permission matrix
against the principal's current membership: workflows.apply, workflows.update-partial,
workflows.deploy, workflows.execute, and the documents.* lifecycle require Member, Admin, or
Owner; workspaces.rename requires Admin or Owner (an explicit workspace-admin action). A Viewer (or a principal with no
current membership) is refused with 403-class -32003
(MCP tool <name> requires a workspace role that can write. / … can admin. / … can execute.) before any executor call.
Read tools, workflows.propose, and documents.status need membership only, except the full-projection
reads (workflows.get, runs.get, workflows.preview-target-publication, credentials.list,
credentials.test), which need secret-read or write authority.
tools/list advertises only the tools the session can call: a tool whose role check the principal's current
capabilities cannot pass is hidden, as is credentials.create for any session other than a service-token
workspace API key. A role change after tools/list is still enforced by the call-time check.
That workspace role is separate from AIMS authorization. workflows.deploy additionally requires the
workspace API key posture and a current AIMS aims_manager or system_owner service-token assignment;
the deployment gate rechecks that assignment at execution time.
Apps
Apps tools use the canonical App schemas and executor shared with the editor and viewer. PostgreSQL is
required; an unavailable durable store reports apps_storage_required instead of returning a simulated
App. See Apps for the visual authoring and publishing flow.
| Tool | Scope | Arguments |
|---|---|---|
apps.list | mcp:read | Optional cursor, limit (1–100); returns apps and nextCursor |
apps.get | mcp:read | appId; returns draft, revision and published snapshot |
apps.backends | mcp:read | Optional cursor, limit (1–100); returns eligible backends and nextCursor |
apps.run | mcp:read | appId, invocationId; returns the caller's invocation state and result |
apps.invocations | mcp:read | appId, optional cursor and limit (1–100, default 50); returns the caller's invocation identifiers and creation times |
apps.create | mcp:write | draft, confirm: true, idempotencyKey |
apps.save | mcp:write | appId, expectedRevision, draft, confirm: true, idempotencyKey |
apps.publish | mcp:write | appId, expectedRevision, confirm: true, idempotencyKey |
apps.invoke | mcp:write | appId, versionId, widgetId, values, confirm: true, idempotencyKey |
apps.externalAccess | mcp:write | appId; returns mode, revision, username and current share URL, never the password |
apps.configureExternalAccess | mcp:write | appId, mode, expectedRevision, optional username, password, rotateLink, confirm: true, idempotencyKey |
Creation, saving and publication require workspace write authority; invocation requires execution authority.
Reading external access also requires write authority. Configuring public, password or basic mode
requires write and execute; internal requires write and revokes external access. Basic mode uses one
username/password pair. A password can be omitted only to retain the existing protected mode's credential
with the same username. The grant is bound to the current publication and must be saved again after
republishing. Every configuration increments its revision and invalidates previous sessions.
A Viewer cannot invoke a published App. Like runs.get, apps.run and apps.invocations require
workspace secret-read or write authority; a read-only organization OAuth grant cannot discover or
call them, even for an Owner.
apps.run is also limited to an invocation initiated by the current
caller in that workspace. A read grant does not confer the publisher's execution authority.
For both list operations, omit cursor for the first page, then pass each returned nextCursor
until it is null. Cursors are scoped to the selected workspace and operation. apps.backends
examines at most limit destinations per page before filtering unsupported contracts, so an empty
backends array can still have a continuation cursor. Do not treat that page as the end of the list.
App result text, structured values and errors have secret-pattern redaction before delivery.
apps.invocations lists only the caller's requests for the selected App, ordered by creation time and
then invocation ID, newest first. Its forward-only cursor is additionally scoped to that App and caller.
The response is { invocations: [{ invocationId, runId, versionId, createdAt }], nextCursor }, with an
ISO timestamp and no submitted values or execution output. Follow nextCursor until null, then use
apps.run to read a selected result. Neither reading history nor reading a result dispatches a workflow.
Transport pagination metadata such as direction is not part of the canonical MCP input.
External visitor MCP
An App's external link is /a/<share-token>. To obtain an agent session, send JSON to
POST /api/apps/<share-token>/session with transport: "bearer" and the mode's credentials:
password for shared-key mode, username and password for basic mode, or neither for public mode.
The response is {token, expiresAt}. Keep the token private and send it as `Authorization: Bearer
<token>toPOST /api/apps/<share-token>/mcp`. Do not use a workspace or organization token there.
| Tool | Arguments |
|---|---|
app-visitor.get | {}; returns the published form's title, version and fields |
app-visitor.invoke | versionId, values, confirm: true, idempotencyKey |
app-visitor.run | invocationId |
app-visitor.invocations | Optional forward cursor, limit (1–100, default 50) |
These operations use the same canonical executors as the external page and its HTTP API. Tool arguments
cannot select an App, workspace, member or workflow. Receipts and history omit runId; results omit
execution links and raw workflow errors. A cursor or invocation from another visitor session is rejected.
Idempotency keys are scoped to the visitor session. The session lasts up to 24 hours and is revoked by
access configuration changes. DELETE /api/apps/<share-token>/session with the bearer ends that session.
Browser sessions use an HttpOnly cookie instead; browser writes also require same-origin and CSRF checks.
An MCP client must explicitly request a bearer: the MCP endpoint does not accept the browser cookie.
draft contains name, one page and a binding. The page has title, form: { id, fields },
button: { id, label } and result: { id, title }. Every field declares name, label, type
(string, number or boolean) and required. The binding names workflowId, executionTargetId
and expectedVersionId; the server validates workspace ownership and the exact active backend. Publishing
also freezes its activation, start node and input/output contract. Unknown properties and executable
HTML, JavaScript or URL extensions are refused.
An invocation's versionId is the published App version. The server resolves the workflow version from
that snapshot, so an invocation must not supply workflowId, executionTargetId or expectedVersionId.
widgetId must name its published button. values must match the visible scalar contract exactly.
{
"appId": "app_example",
"versionId": "published_app_version",
"widgetId": "process",
"values": { "request": "Papel", "quantity": 2, "priority": "normal" },
"confirm": true,
"idempotencyKey": "request-2026-11-02-001"
}
The invocation receipt contains invocationId, runId, versionId and deduplicated. Follow
apps.run to observe pending, running, waiting, waiting-approval, error or succeeded.
output is null until success; a successful result contains the persisted workflow response and
redacted data. error carries a recorded backend failure when available. The optional errorCode
identifies two result conditions: app_execution_unavailable means no execution is currently available after
the five-minute database-clock polling period, even though it may still start or have run previously; app_output_limit means a successful result exceeded
100,000 serialized UTF-8 bytes. Both preserve the identifiers and return no output. The latter
preserves the execution's actual status and its usable execution reference. Reading either state
never restarts a workflow. An accepted request does not promise a successful execution.
Repeat the same mutation key and arguments to recover a completed receipt while retained. Reusing a key
with different inputs is idempotency_key_mismatch. Preparation failures before the queue is called release
the key, but a lost acknowledgement of the invocation's own commit can leave an entry in personal history
without an execution. A queue admission failure before any execution persistence attempt releases the key
only after compensation. After a persistence attempt, even an absent row can be a late commit;
the key stays quarantined. An uncertain dispatch returns idempotency_in_doubt and keeps
the key quarantined: inspect personal history and the recorded execution rather than retrying with a
new key. Stopped destinations, changed activations or versions,
revoked AIMS authority and incompatible contracts fail closed. Reload and review before republishing.
The workspace endpoint binds the workspace in its URL. Organization calls additionally require the selected
workspaceId, checked against current membership and the grant. These operations do not provide anonymous
publication or customer Product gateway identity/consent.
Workspace palettes
themes.get takes no arguments at a workspace endpoint and returns { workspaceId, palette }.
Palette is null for default colors or a versioned object with version: 1 and light/dark
objects. Each mode contains background, panel, panelSoft, foreground, muted, border,
accent, and accentStrong, each a six-digit hex color. Unknown properties and CSS expressions
are rejected. Invalid stored palettes fall back to default colors.
themes.update requires mcp:write, workspace write authority, confirm: true, and an
idempotencyKey. Pass the complete validated palette or palette: null to restore defaults.
It returns { workspaceId, palette, deduplicated } and records the change in the audit log.
It does not change the workspace's System/Light/Dark appearance selection. The palette is shared
by this workspace's members; it is not an organization-global or personal theme selection.
The workspace endpoint supplies workspaceId. At an organization endpoint, select an authorized
workspace with workspaceId; the same validation, authorization, confirmation and idempotency apply.
Questions about executions and documentation
runs.ask takes a required question (1–2,000 characters) and optional runId, workflowId,
status, trigger, dateFrom, dateTo, query, and sort. Dates use ISO date-time strings;
sort accepts newest, oldest, duration, cost, workflow, or status. With runId, only
that execution is inspected and the other filters are ignored. Without it, the operation inspects
up to 50 matching runs and selects up to eight source excerpts. Raw errors, payloads, and log
messages are withheld; recognized error categories and failed/waiting step metadata can be included.
documentation.ask takes only question and searches public navigation-listed documentation,
selecting up to four matching pages. It does not search private documents or knowledge bases.
Both tools return { answer, mode, sources, scope, truncated }. Each source has id, title,
href, and excerpt. Mode is synthesized, excerpts, or no-evidence. Source links are supplied
by the server; a source citation does not independently prove every generated claim.
At a workspace endpoint, the URL supplies the workspace; omit workspaceId from arguments.
At an organization endpoint, provide an authorized workspaceId as for other workspace-scoped tools.
Both operations require mcp:read, retain workspace authorization, and do not execute actions or
require confirmation. Generation uses the workspace's hosted provider and can consume AI credits;
local CLI providers are not selected. An unavailable provider, invalid generated response, or
30-second generation deadline falls back to labeled source excerpts. Questions are subject to a
best-effort per-process limit of 20 per minute per principal/workspace and 60 per workspace.
Governed capability waves
The following preview operations are selected from the canonical capability registry. They are visible only to
write-capable MCP sessions while FLUXUS_MCP_CAPABILITY_WAVES is enabled. Registry schemas, workspace roles,
confirmation, idempotency, egress controls, and the canonical executor remain authoritative.
| Area | Tools |
|---|---|
| Workflow/run control | workflows.create-from-template, runs.cancel, runs.rerun, schedules.run |
| MCP connections | mcp-connections.list, mcp-connections.get, mcp-connections.authorization-handoff, mcp-connections.create, mcp-connections.update, mcp-connections.refresh, mcp-connections.configure-tool, mcp-connections.test, mcp-connections.delete |
| Credentials | credentials.list, credentials.create, credentials.test |
| Files and knowledge | files.list, files.get, files.read, files.delete, knowledge.sync |
| Mail and feeds | mailer.list, mailer.get, mailer.reply, rss.list, rss.get |
| Web and research | web.search, web.read, web.crawl, research.run |
| Artifacts and skills | presentation.create, image.generate, skills.load |
runs.rerun takes runId, confirm: true, and an idempotencyKey, and repeats the original run with its
input, target, and pinned version. A live-candidate run is rerun live against the same candidate version, also
while the workflow has never been deployed; a dry run is rerun as a dry run. To test a newer candidate, call
workflows.execute with its expectedVersionId instead.
Connection administration returns endpoint metadata, credential-reference names, status, and discovered tool
schemas; it never returns resolved headers or secret values. The endpoint is shown in a readable redacted
form: only the scheme, host and port are kept. User info and the fragment are dropped, any non-empty path becomes a
single literal /<redacted> (a secret can equal any path word, api or mcp included), and a non-empty query,
parameter names included, becomes a single ?<redacted>, for example https://mcp.example.com/<redacted>?<redacted>.
The stored endpoint is unchanged. Creating a connection accepts only header templates such as
Bearer {{credential.remote_api_key}}. Consent and credential exchange remain human UI actions.
mcp-connections.create and mcp-connections.update apply the outbound network policy before they store an
endpoint, the same policy web.read and the MCP client enforce: a private, loopback, link-local, cloud metadata
(169.254.169.254), or internal host, or a name that resolves to one, is refused with -32602 /
egress_blocked and nothing is saved. A credential template in the user info, port, path or query does not skip
this check: the concrete host is still validated. Only a host chosen by a template is left to the runtime guard,
which checks it on every call; its scheme must still be http or https. An endpoint that is not a valid
absolute URL, including a relative path such as /api/v1/..., a value without a scheme, and an unterminated or
unsupported {{ ... }} in the host, user info or port, is refused with -32602 / mcp_endpoint_invalid; it is
never resolved against the platform's own origin. The REST and Settings paths share this check. An operator can opt a
legitimate private installation back in with FLUXUS_HTTP_EGRESS_ALLOWLIST.
mcp-connections.test, mcp-connections.refresh, mcp-connections.configure-tool, and
mcp-connections.delete answer an unknown connection with -32004 / mcp_connection_not_found. Repeating a
completed delete with the same idempotencyKey returns its original { deleted: true, deduplicated: true }
receipt; a new key for a connection that no longer exists is -32004. Testing or refreshing a disabled
connection is -32009 / mcp_connection_disabled, a header template whose credential is not stored is
-32009 / mcp_credential_missing, and configuring a tool outside the current discovery snapshot is
-32004 / mcp_tool_not_found.
runs.cancel cancels a queued, running, or waiting run. A run that already finished is -32009 /
run_not_cancellable; a replay of a completed cancel with the same key returns its original receipt.
Credential tools serve any integration provider whose manifest declares a credential type, such as Jenkins
(jenkinsApi). credentials.list returns metadata only (id, name, type, scope, creation time and the
providerIds each credential serves) and accepts an optional providerId filter. credentials.create takes
providerId, name, the secret value, optional credentialType (needed only when a provider declares several),
scope (workspace or personal) and replaceExisting, with confirm: true and an idempotencyKey. It needs
write authority and a workspace API key with service-token assurance; OAuth and organization sessions are refused
with -32003 and must use Settings → Credentials, which requires recent step-up. A credential with the same
name is replaced only with replaceExisting: true, and only when it has the same credential type; a same-named
credential of another type is refused with credential_type_conflict, so choose another name. The secret is
checked with the provider's own parser before it is stored: a Jenkins secret needs an https:// server URL, a user
and an API token, and a WhatsApp secret needs an access token. A secret the provider could not use is refused with
-32602 / credential_value_invalid and a message that names the missing part, never the secret. credentials.test runs the Settings connection test with a
credential the acting user may use. Listing and testing need secret-read or write authority. No tool returns a
secret value; creation is audited without it. Deleting a credential remains a Settings action. The request
identity binds the secret only through a keyed digest, so an identical retry replays its receipt across an
APP_ENCRYPTION_KEY rotation while the previous key stays in APP_ENCRYPTION_PREVIOUS_KEYS; once that key is
removed, the retry is refused as idempotency_key_mismatch.
End users
While the deployment runs with FLUXUS_END_USERS=true, the server advertises the owner's
end user operations (platformMcpEndUserReadToolNames and
platformMcpEndUserMutationToolNames). They are hidden and refused (-32602 Unknown MCP tool) while the flag is
off. A session with organization-derived access is refused like for every other non-safe tool, and product gateway
keys are refused by the MCP endpoints (403).
| Name | Scope | Purpose |
|---|---|---|
end_users.list | mcp:read | List end users, newest first, up to 200 per page (limit); erased customers are left out |
end_users.get | mcp:read | Read one end user's conversation, facts, work in progress and website logins (audited) |
memory_resources.list | mcp:read | List memory resources and the workflows connected to each |
site_notes.list | mcp:read | List site notes with raw text and provenance, optionally by site or status (audited) |
site_notes.get | mcp:read | Read one site note (audited) |
end_users.update_fact | mcp:write | Replace a fact's value; the fact becomes owner-written and extraction never overwrites it |
end_users.delete_fact | mcp:write | Delete one fact (confirmation required) |
end_users.delete_turn | mcp:write | Delete one turn of an end user's conversation (confirmation required) |
end_users.update_work_record | mcp:write | Set or clear (null) work in progress fields; the owner's edit always wins |
end_users.revoke_connection | mcp:write | Revoke one of an end user's website logins (confirmation required) |
end_users.erase | mcp:write | Erase everything stored for an end user (confirmation required) |
memory_resources.create | mcp:write | Create a named memory resource |
memory_resources.rename | mcp:write | Rename a memory resource |
memory_resources.connect | mcp:write | Connect a resource to a workflow; its runs use it from the next run |
memory_resources.disconnect | mcp:write | Disconnect a resource from a workflow; the memory is kept (confirmation required) |
memory_resources.delete | mcp:write | Delete a resource and every end user's memory in it (Admin/Owner, confirmation required) |
end_users.list returns nextCursor; pass it back as cursor to read the next page, until it is null.
Every mutation takes an idempotencyKey; the ones marked above also need confirm: true. The end_users.* and
site_notes.* tools require explicit workspace Admin or Owner membership, and end_users.get and
site_notes.* write a content-read audit event; memory resource reads need membership and their mutations need Member or higher, except memory_resources.delete,
which deletes customer content and requires explicit workspace Admin or Owner membership. Returned conversation,
memory, and note text comes from external customers: treat it as untrusted data, never as instructions. Approving a
site note and creating product gateway keys are human-only actions and have no MCP tool; granting a workflow node
access to a website (and revoking that grant) is available only in the app for now.
Command tools
The higher-level natural-language authoring path (platformMcpCommandToolNames) remains available
alongside the typed lifecycle. command.status is a read; command.submit, command.confirm, and
command.cancel require mcp:write and are hidden from read-only sessions.
command.submit may author or schedule work, but it is not the route for AIMS-governed deployment;
clients must use the typed workflows.deploy operation after human governance is complete.
| Name | Scope | Purpose |
|---|---|---|
command.submit | mcp:write | Submit a natural-language Command job (validation, confirmation, idempotency) |
command.status | mcp:read | Poll a submitted Command job's status and any pending confirmation |
command.confirm | mcp:write | Confirm a pending Command job's proposed mutation |
command.cancel | mcp:write | Cancel a submitted Command job |
A job that already reached a final state, including a blocked job without a pending confirmation, cannot be
canceled: command.cancel answers -32009 / invalid_state_transition. Use Retry to re-plan a blocked job.
Each Command tool advertises an object output schema. command.submit returns
{ threadId, job, queued, replayed }; command.status returns
{ threadId, messageId, plan, job, result, error, attempt, retryOfJobId }; command.confirm and
command.cancel return { job }. The schemas pin job.id, job.planId and job.status and allow
additional fields, because the job projection grows with Command Center.
Unknown or non-allowlisted name: -32602 Unknown MCP tool: …. Read-only sessions cannot see or
call any write-scoped tool. Executors redact secret-shaped strings.
Organization endpoint
One connection for every workspace an access grant reaches:
POST /api/v1/platform/mcp/org/{organizationId}
Auth. OAuth 2.1 tokens whose audience is the organization resource
(https://<origin>/api/v1/platform/mcp/org/{organizationId}, claim organization_id), or an
organization API key. Workspace tokens and workspace, personal, or copilot keys are refused
(401 / 403 -32003). An OAuth session also needs an access grant: the reach the user chose on
the consent page (all workspaces or selected ones; read only or read and write). Without one the
endpoint answers 403 -32003 and asks the client to authorize again.
Targeting. Every workspace-bound tool advertises a required workspaceId (the registry schema plus
that one property) and its description starts with Acts in the workspace identified by workspaceId.
workspaces.list and capabilities.list take none; workspaces.list returns only the workspaces the
grant reaches. billing-usage.organization and billing-usage.costs keep their own workspaceId,
which is both the target and the filter. Cost-report API-key/OAuth calls cannot widen that filter to
another workspace, even when the acting user is an organization administrator.
Per-call authorization. The target must be inside the grant and inside the organization, the user
must currently hold access there, and the effective authority is token scopes ∩ grant permission ∩ the
user's live role and capabilities. A workspace outside the grant, outside the organization, unknown, or
where membership was lost returns one message: Workspace is not available to this connection. Call workspaces.list to discover the workspace ids this connection may use. (-32003). A missing
workspaceId returns -32602 naming the tool. AIMS-gated tools (workflows.deploy,
aims.governance.get) are never advertised here.
tools/list is the union the scopes and grant permission allow; each call is still authorized for its
target. Read-only grants hide write tools and the full-projection reads (workflows.get, runs.get,
workflows.preview-target-publication, credentials.list), because a read grant never carries
secret-read authority in any workspace. credentials.create is never advertised here. Audit events carry the target workspace and
metadata.surface: "organization". The user rate-limit key is org:{organizationId}:{userId}. Rollout
flag: FLUXUS_MCP_ORGANIZATION_HUB.
Rate limits
Three sliding windows, 120 / 60s each:
| Dimension | Key |
|---|---|
| IP | x-real-ip or unknown |
| Client | OAuth clientId or api-key:{userId} |
| User | {workspaceId}:{userId} (organization endpoint: org:{organizationId}:{userId}) |
Exceeded: 429 -32029 MCP rate limit exceeded. IP is checked before auth.
Audit and idempotency
Each tools/call writes platform.mcp.call (no bearer stored). Audit failure does not fail the
RPC. Read tools, workflows.propose, and documents.status take no Idempotency-Key; the write-scoped
workflows.apply, workflows.update-partial, workflows.deploy, workflows.execute, and
documents.* lifecycle tools require an idempotencyKey argument, so one key retried returns the first
draft, partial-update version, deployment, run, or document mutation rather than creating a second.
Deploy audit events are attributed to the authenticated external principal.
Errors (HTTP + JSON-RPC)
| HTTP | error.code | Message / cause |
|---|---|---|
| 401 | -32001 | Missing/invalid bearer |
| 403 | -32003 | Workspace / scope / membership |
| 405 | — | GET |
| 413 | -32010 | Body > 1 MiB |
| 429 | -32029 | Rate limit |
| 400 | -32700 / -32600 | Parse / invalid request |
| 200 | -32601 / -32602 / -32004 / -32009 / -32020 | Method/params/not found/conflict/availability |
| 200 | -32603 | Masked unexpected implementation or storage error |
Expected workflow publication failures return -32004 for workflow_not_found or -32009 for a
blocked/conflicting publication. Their safe error.data contains code, retryable, and, for an AIMS
block, the bounded blockers list. Arbitrary diagnostic details are not returned.
Other expected failures return the same error.data shape (code, retryable, and sometimes details
or blockers):
error.code | error.data.code | Cause |
|---|---|---|
-32602 | egress_blocked | A URL or MCP endpoint resolves to a private, internal, or metadata address; the host is not echoed |
-32602 | billing_usage_query_invalid, file_offset_out_of_range, document_invalid_input, skill_request_invalid | Invalid period, file offset, or request |
-32602 | invalid_cursor | A cursor the list never issued, including billing-usage.organization; omit it to read the first page |
-32602 | workflow_step_trigger_node, workflow_step_stop_unreachable, workflow_expected_version_required, workflow_source_run_mismatch, mail_message_not_inbound, mail_reply_body_required | Step execution or mail reply precondition |
-32003 | skill_not_available | The skill is not available to this principal |
-32004 | workflow_node_not_found, workflow_version_not_found, workflow_target_not_found, workflow_source_run_not_found | Unknown node, version, destination, or source run |
-32004 | file_not_found, document_not_found, knowledge_base_not_found, knowledge_sync_not_found, mail_message_not_found, confirmation_not_found | Unknown resource in this workspace |
-32004 | workflow_not_found, run_not_found, human_task_not_found, table_not_found, catalog_block_not_found, mcp_connection_not_found, mail_thread_not_found, rss_trigger_not_found | A get-by-id tool's id does not exist in this workspace |
-32004 | mcp_tool_not_found | mcp-connections.configure-tool names a tool outside the current discovery snapshot |
-32009 | workflow_proposal_stale | workflows.deploy planHash no longer matches; request a fresh workflows.propose. The idempotencyKey is checked first, so a reused key with different arguments is idempotency_key_mismatch and a completed deploy replays |
-32009 | run_not_cancellable, invalid_state_transition | runs.cancel on a finished run, or command.cancel on a finished or blocked Command job |
-32009 | mcp_connection_disabled, mcp_credential_missing | Enable the MCP connection, or store the credential its header template names, then retry |
-32009 | workflow_version_conflict (with details.expectedVersion / details.currentVersion), workflow_not_deployed | Stale expectedVersion; api, schedule, or webhook execution of an undeployed workflow |
-32009 | aims_deployment_blocked (with blockers) | A destination publish blocked by AIMS; complete the named steps in Settings → AI Governance |
-32009 | workflow_target_revision_conflict, workflow_target_changed, workflow_publication_request_mismatch, workflow_target_stopped | The destination changed or is off; reload and review again |
-32009 | workflow_pin_data_stale, workflow_step_upstream_data_missing | Prepare test data again or run the workflow once before executing the step |
-32009 | idempotency_key_mismatch, document_generation_mismatch, document_invalid_state, file_state_conflict, knowledge_sync_conflict, skill_version_mismatch | Reused key with different arguments or a resource in another state |
-32009 | mcp_auth_required, mcp_timeout, mcp_transport_error, mcp_http_error, mcp_invalid_response, mcp_protocol_incompatible, mcp_response_too_large | The remote MCP connection failed; retryable says whether to retry |
-32009 | confirmation_mismatch, confirmation_consumed, confirmation_expired | The Command confirmation belongs to another job or was already used |
command.confirm answers confirmation_not_found when the job exists but has no pending confirmation with
that id, and Confluye Command job was not found. only when the job itself is unknown.
A masked -32603 carries error.data.correlationId. The server logs the same id with the tool name, the error
class, a fixed-length hash of the message for grouping repeats, and the file:line of the originating stack
frame. The message text itself is never logged, because it can quote credentials or provider responses. The hash
is keyed with a secret derived from the server's encryption key, so someone who can read the logs cannot confirm a
guessed message or credential against it; without a configured encryption key the hash is omitted.
Gaps
- GET/SSE not implemented (
405). tools/listdoes not emitlistChangednotifications.capabilities.listschema vs runtimematuritydiffer (preview is accepted then overwritten).- Rate limiters are in-process (not shared across instances).
- Read tools and the read-only
workflows.propose/documents.statusare available to every authorized session; the typedworkflows.apply,workflows.update-partial,workflows.execute, anddocuments.*lifecycle mutations requiremcp:writeand a Member-or-higher workspace role.workflows.deployadditionally requires a workspace API key and a current AIMS service-tokenaims_managerorsystem_ownerassignment. The per-workflow MCP servers remain a different surface (/api/v1/workflows/mcp/...).
