MCP Tools Reference

YmerFlow exposes a subset of its REST API as MCP (Model Context Protocol) tools via fastapi-mcp, mounted at /mcp using the Streamable HTTP transport.

Only routes tagged Processes, Datasets, Environments, Uploads, or Workspaces are exposed as MCP tools (backend/main.py, FastApiMCP(..., include_tags=[...])). Auth, Projects, Publications, Admin, Systems, Tags, Plugins, and Internal routes are REST-only and not visible to MCP clients. No operation_id is set on any route, so tool names follow FastAPI's default {function_name}_{path}_{method} pattern (e.g. create_process_projects__project_id__process_post) — MCP tool names are auto-derived from the route path, so they follow automatically whenever a route moves; nothing to hand-update here.

Authentication

All tools require a project-scoped API key:

Authorization: Bearer apk_<key>

API keys are scoped to a single project, but every project-resource endpoint still takes that same project_id as an explicit path parameter (/projects/{project_id}/...) — pass it on every call. Under the hood, a single bearer credential is dispatched by prefix: apk_... (API key, hashed and looked up, scopes the request to its project — the project_id you pass must match the key's scope or you get 403), upt_... (short-lived 1h upload-only JWT from request_upload_token), or a full-session JWT (no project scope). Write endpoints require real project membership; read endpoints additionally accept a read-only publication id in place of project_id (see docs/plans/done/publication-readonly-projects.md) — not relevant to MCP clients, which always authenticate with a real project-scoped API key.

Typical Workflow

1. list_environments          — discover available environments and process type names
2. get_process_type_schema    — fetch the JSON Schema for the specific type you want to run
3. upload_file                — upload local input data (or use request_upload_token + curl for large files)
4. create_process              — submit the job; save the returned id and version
5. get_process                — poll until versions[-1].state is 'done' or 'failed'
6. get_dataset                — resolve output URLs from versions[-1].outputs
7. curl '{url}'                — download results; /files/ URLs need no authentication

Use get_dataset to inspect a dataset before downloading it in full: its files dict (root-level and per-part) may include a "application/vnd.ymerflow.stats+json" entry whose URL resolves to pre-computed statistics (count, min, max, mean, rms, percentiles, skewness, kurtosis), so you can check value ranges without downloading the binary content.

Which endpoints are NOT exposed

Binary data download endpoints are excluded from MCP (include_in_schema=False) because they overflow LLM context windows: - GET /projects/{project_id}/dataset/{id}/data and /geography — use the url field from get_dataset + curl instead - GET /files/{path} — auth-free, download directly with curl - GET /uploads/{id} — use the url returned by upload_file

There is no longer a describe_dataset endpoint — it was removed and replaced by the pre-computed stats files described above (see get_dataset).


Processes

create_process

POST /projects/{project_id}/process

Submit any type of job — data import, processing, inversion, forward modelling, etc. The job is queued and runs asynchronously in Kubernetes. Returns immediately with the process id and version number.

Retry vs. new process: If retrying a failed job or correcting parameters, pass the original id in the body to append a new version. Do NOT create a new process — that loses history. Omit id only when starting a genuinely new workflow.

Resource sizing for inversions: Never use defaults for inversions — the defaults (1 CPU, 2 Gi RAM, 1 h deadline) will cause OOM-kills or deadline failures with no output produced. Set resource_requests and deadline_seconds explicitly based on dataset size.

Path parameters:

Parameter Type Required Description
project_id string Yes Project ID the job belongs to.

Request body (ProcessCreate; accepts extra unknown fields, which are silently ignored):

Field Type Required Description
type string Yes Process type key, e.g. aem_inversion. Obtain from list_environments / get_process_type_schema.
environment object Yes Compute environment to run in — {id, [name]}. Obtain from list_environments (pass the object straight through, or just {"id": ...}).
params object No Process-type-specific parameters defined by the process type's JSON Schema. Default {}. Fields with x-format: dataset expect a file URL from search_datasets or get_dataset.
id string No Existing process ID. Provide to add a new version (retry/correction). Omit to create a new process.
name string No Human-readable display name. Defaults to <type>-process.
resource_requests object No Kubernetes resource requests. See below.
deadline_seconds integer No Max wall-clock time before the job is killed. Default: 3600. Always set explicitly for inversions.
cluster object No Cluster to run on — {id, [name]}. Obtain from available_clusters. Omit to auto-select the first allowed cluster (by sort_order).

A value obtained from list_environments, available_clusters, or a prior get_process can be passed straight through as the environment/cluster field — no extraction step needed. name, if present on environment/cluster, is ignored server-side (only .id is read).

resource_requests fields:

Field Type Default Description
cpu string "1000m" CPU request in Kubernetes notation, e.g. "500m" or "4".
memory string "2Gi" Memory request, e.g. "512Mi" or "16Gi".
ephemeral-storage string "10Gi" Temporary disk space for the job.

Returns: {"id": "<process_id>", "versions": [{"version": <n>}]}


list_processes

GET /projects/{project_id}/processes

List processes in a project, with their status and outputs.

Each process has a versions array sorted ascending by version number; versions[-1] is the most recent run. Each version includes state, outputs, and parameters. Logs are not included — use get_process_logs for those.

Important: The URLs in outputs are /projects/{project_id}/dataset/{id} metadata URLs, not directly usable as input_data. Call get_dataset to resolve the actual file URL.

Parameter Type Required Description
project_id string Yes Project to list processes in.

Returns: Array of process objects — {id, name, type, environment: {id, name, created_at}|null, project_id, flow_x, flow_y, versions: [...]}. Each version is {version, parameters, outputs: {name: "<url>/projects/{project_id}/dataset/{id}"}, state: "queued"|"running"|"done"|"failed", dependencies, resource_requests, deadline_seconds, cluster, tags}.


get_process

GET /projects/{project_id}/process/{process_id}

Get a single process by ID, including all versions with state, parameters, and outputs. Prefer this over list_processes when you already have the ID — it fetches only the one record.

After create_process returns an id, poll this endpoint until versions[-1].state is done or failed, then read versions[-1].outputs for dataset URLs.

Parameter Type Required Description
project_id string Yes Project the process belongs to.
process_id string Yes Process ID from create_process.

Returns: Single process object (same shape as list_processes entries). Returns 404 if not found in the given project.


get_process_logs

GET /projects/{project_id}/process/{process_id}/logs

Retrieve execution logs for a process job. Use this to diagnose why a job failed (state == 'failed').

Always pass version when diagnosing a specific run — omitting it returns logs from all versions interleaved.

Pagination examples: - offset=0, limit=100 → first 100 lines - offset=100, limit=100 → next 100 lines - offset=-50 → last 50 lines (tail) - offset=-100, limit=50 → 50 lines starting 100 from the end

Parameter Type Required Description
project_id string Yes Project the process belongs to.
process_id string Yes Process ID.
version integer No Version number. Omitting returns logs from all versions interleaved.
offset integer No Positive = from start; negative = from end. Default: 0.
limit integer No Maximum number of log entries to return. Omit for all entries from offset.

Returns: Array of log entry objects — {timestamp, message}.


clone_process_version

POST /projects/{project_id}/process/{process_id}/versions/{version}/clone

Create a new version of a process by copying parameters from an existing version with optional overrides. Enables iterative tuning: run → inspect results → adjust one parameter → re-run, without re-specifying everything.

Resource limits and deadline are inherited from the source version unless explicitly overridden. For inversions, always override resources — the source may have been created with small defaults.

Returns the same {"id", "versions": [{"version"}]} format as create_process. Poll get_process to track state.

Path parameters:

Parameter Type Required Description
project_id string Yes Project the process belongs to.
process_id string Yes Process ID.
version integer Yes Source version number to clone.

Request body (optional):

Field Type Description
parameter_overrides object Keys to change relative to the source version. All other parameters are copied unchanged.
resource_requests object Override resource limits (same fields as in create_process).
deadline_seconds integer Override the deadline (seconds). If omitted, inherits from source version.
cluster object Override the cluster for the cloned run — {id, [name]}. Obtain from available_clusters. If omitted, inherits the source version's cluster (falls back to the first allowed cluster if no longer allowed).

Returns: {"id": "<process_id>", "versions": [{"version": <n>}]}


cancel_process_version

POST /projects/{project_id}/process/{process_id}/versions/{version}/cancel

Cancel a process version that is currently queued or running. Deletes the Kubernetes job (if submitted) and marks the version as failed. Returns 409 if the version is already in a terminal state (done or failed).

Parameter Type Required Description
project_id string Yes Project the process belongs to.
process_id string Yes Process ID.
version integer Yes Version number to cancel.

Returns: {"status": "cancelled"}


update_process_position

PATCH /projects/{project_id}/process/{process_id}/position

Persist the FlowView canvas position for a process node, so the layout is remembered between sessions.

Path parameters:

Parameter Type Required Description
project_id string Yes Project the process belongs to.
process_id string Yes Process ID.

Request body:

Field Type Required Description
x number Yes X coordinate on the FlowView canvas.
y number Yes Y coordinate on the FlowView canvas.

Returns: No content (204).


available_clusters

GET /projects/{project_id}/utilities/available-clusters

Return the clusters the current user may run a process on, each with live CPU/memory limits and its max_runtime_seconds ceiling (null = unbounded). Two distinct limits are returned per cluster, because a pod is atomic (it must fit on one node): max_cpu_cores/max_memory_gb are the single-node capacity — the ceiling a single task's resource_requests must stay within, and what create_process enforces — while aggregate_max_cpu_cores/aggregate_max_memory_gb are the cluster-wide autoscaled ceiling (read from the Kueue ClusterQueue, informational only). Size resource_requests against max_cpu_cores/max_memory_gb, not the aggregate. Call this before create_process to discover valid cluster IDs. Sorted by sort_order, the same order the value should be presented in.

Parameter Type Required Description
project_id string Yes Restrict to clusters allowed for this project.
cpu string No CPU request to check allowance for, e.g. "4".
memory string No Memory request to check allowance for, e.g. "16Gi".
deadline_seconds integer No Accepted but currently unused server-side — only cpu/memory affect which clusters are returned.

Returns: Array of cluster objects — {id, name, namespace, created_at, sort_order, active, max_runtime_seconds, provisioning_status, max_cpu_cores, max_memory_gb, aggregate_max_cpu_cores, aggregate_max_memory_gb}. max_cpu_cores/max_memory_gb are single-node (per-task) capacity; aggregate_max_* are the cluster-wide autoscaled ceiling (informational).


Datasets

search_datasets

GET /projects/{project_id}/datasets

Search for datasets produced by completed processing jobs. Each result includes id, url (for use as input_data), dataset_name, process_name, and mime_type. The url can be downloaded directly with curl — no authentication required.

The search string is matched case-insensitively against <process_name> / v<version> / <dataset_name>.

Parameter Type Required Description
project_id string Yes Project to search datasets in.
search string No Name fragment to filter by. Default: "" (all datasets).
completed_only boolean No Default: true. Set false to include datasets from still-running or failed jobs.

Returns: Array of dataset metadata objects (same shape as get_dataset).


get_dataset

GET /projects/{project_id}/dataset/{dataset_id}

Return metadata for a specific dataset including its mime_type, parts structure, and the process version that produced it.

The url field in the response is the actual file URL — downloadable directly with curl (curl "{url}" -o /tmp/result.msgpack). Use this url as input_data when passing this dataset to create_process, not the /projects/{project_id}/dataset/{id} URL from list_processes outputs.

The files dict (both at the root and under parts.<name>.files) may contain a key "application/vnd.ymerflow.stats+json". Fetching that URL returns pre-computed statistics — count, min, max, mean, rms, geometric_mean, std, percentiles p5/p25/p50/p75/p95, skewness, kurtosis (constant columns/flightlines appear as {"constant": true, "value": X} instead). Structure varies by dataset type: - XYZ/AEM (application/x-aarhusxyz-msgpack): flightlines (per-column stats) and layer_data (per-channel, with .layers arrays indexed by layer number for varying channels). - MAG (application/x-magdata-msgpack): columns (per-column stats). - Grid/webxtile (application/x-webxtile): variables, each with all and (for 3-D variables) slices arrays indexed by z-slice.

Use this stats URL to inspect a dataset without downloading the full binary file.

Parameter Type Required Description
project_id string Yes Project the dataset belongs to.
dataset_id string Yes Dataset ID.

Returns: Dataset metadata object — {id, mime_type, process_id, process_name, process_version, dataset_name, project_id, parts, url}.


Environments

list_environments

GET /environments

List available compute environments. Returns each environment's id, name, and process_types. By default process_types is a list of type name strings only.

Parameter Type Required Description
include_schemas boolean No Include full JSON Schemas for each process type. Default: false. Use get_process_type_schema to fetch a single type's schema instead of embedding all schemas here.

Returns: Array of environment objects — {id, name, docker_image, process_id, process_types, created_at}.


get_process_types

GET /environments/{env_id}/process-types

Return all process types available in an environment, keyed by type name. Each entry is a JSON Schema describing the required and optional params for that process type. Fields with x-format: dataset expect a file URL from search_datasets.

Returns an empty dict if the environment has not finished registering its process types yet (environment setup is itself a process — check list_processes to see if it has completed).

Parameter Type Required Description
env_id string Yes Environment ID from list_environments.

Returns: Object mapping type name → JSON Schema.


get_process_type_schema

GET /environments/{env_id}/process-types/{type_name}

Return the JSON Schema for exactly one named process type. Even the largest schemas (~44 KB) fit in a single response.

Parameter Type Required Description
env_id string Yes Environment ID from list_environments.
type_name string Yes Process type key, e.g. import_skytem.

Returns: JSON Schema object. Returns 404 if the environment or type name is not found.


create_environment

POST /environments

Register a Docker image as a named compute environment. Typically called automatically by a build pipeline after pushing a new image. The environment is immediately available for create_process; its process_types are populated once the environment's setup job completes.

Request body:

Field Type Required Description
name string Yes Human-readable display name.
docker_image string Yes Fully-qualified Docker image reference, e.g. registry.example.com/myenv:latest.
process_id string No ID of the process that built this environment, if any. Links the environment back to its build job.

Returns: Environment object including the generated id.


Uploads

upload_file

POST /projects/{project_id}/upload

Upload a raw input file (e.g. AEM survey data, CSV) that is not the output of any process. The response url is a direct HTTP file URL ready to pass as input_data to create_process.

Supports two body formats, auto-detected from Content-Type:

Raw body (any file size; streamed straight to storage):

curl -X POST "https://host/projects/{project_id}/upload?filename=data.xyz" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @data.xyz

The filename travels as the URL-encoded filename query parameter and the content type as the Content-Type request header. The body is streamed to object storage in bounded chunks, so backend memory stays flat regardless of file size.

JSON + base64 (MCP-friendly, up to ~25 MB):

{
  "filename": "data.xyz",
  "content": "<base64-encoded bytes>",
  "content_type": "application/x-aarhusxyz-msgpack"
}

The MCP upload_file tool uses this JSON+base64 path and is unchanged; only the large-file curl path switched from multipart to a raw body.

For files larger than ~25 MB, use request_upload_token to get a short-lived token, then upload via curl (the token already encodes the project it was requested for, but project_id in the path must still match it):

curl -X POST "https://host/projects/{project_id}/upload?filename=survey.xyz" \
  -H "Authorization: Bearer upt_..." \
  -H "Content-Type: application/octet-stream" \
  --data-binary @survey.xyz
Parameter Type Required Description
project_id string Yes Project ID. Must match the API key's/upload token's scope.

Returns: {"id": "<upload_id>", "filename": "<name>", "url": "<http_url>"}


request_upload_token

POST /upload/request-token

Issue a short-lived Bearer token (prefix upt_) for uploading large files via curl, without passing full session credentials. The token is a signed JWT that expires after 1 hour and is scoped to the same project as the current session.

Requires a project-scoped API key session.

No parameters.

Returns: {"token": "upt_<jwt>", "expires_in": 3600}


Workspaces

Workspaces belong to a project and are versioned: each workspace is a parent record with a versions array (mirroring how Process/ProcessVersion work). Saving over a workspace never overwrites — it appends the next version, and "current" is always the highest version number. A workspace can be marked public (is_public), which lists it in the cross-project public gallery where any authenticated user can fork a specific version of it into their own project.

A workspace object has the shape: {id, title, project_id, is_public, forked_from_workspace_id, forked_from_version, created_at, versions: [{version, layout, created_at}], project_name?} (project_name is included only on public-gallery and single-workspace reads).

list_workspaces

GET /workspaces?project_id=<id>

List the workspaces belonging to a project, each with its full version history. Requires the caller to be a member of the project (the API key's scoped project).

Parameter Type Required Description
project_id string Yes Project whose workspaces to list.

Returns: Array of workspace objects (see shape above).


list_public_workspaces

GET /workspaces/public

List every public workspace across all projects — the "public gallery" used to discover workspaces worth forking. Any authenticated user can call this (no project-membership check). Each entry includes its home project's project_name and full version list.

No parameters.

Returns: Array of workspace objects, each including project_name.


get_workspace

GET /workspace/{workspace_id}

Get a workspace and its full version history. Returns 404 unless the workspace is public or the caller is a member of its home project. Each entry in versions holds a recursive JSON layout tree of nodes with id, widget, optional children, and widget-specific layoutConfig.

Call get_workspace_schema first to understand valid node structures and widget types.

Parameter Type Required Description
workspace_id string Yes Workspace ID from list_workspaces or list_public_workspaces.

Returns: Workspace object (see shape above). Returns 404 if missing or not accessible.


create_workspace

POST /workspace?project_id=<id>

Create a new workspace in a project, with an initial version-1 layout. Requires membership of project_id.

layout is a typed JSON object (a node tree) — not a JSON string. FastAPI returns 422 if it is a string or array, so the old footgun of storing a serialized-JSON string is impossible. Explore the layout format first with get_workspace_schemaget_widget_schema (and, for PlotView, list_plot_layer_types / get_plot_layer_schema).

Parameter Type Required Description
project_id string Yes Project the workspace belongs to.

Request body:

Field Type Required Description
title string No Display name. Default: "Untitled Workspace".
layout object Yes Recursive node tree for version 1. Must be an object (422 otherwise).

Returns: Created workspace object with its generated id and a single version 1.


create_workspace_version

POST /workspace/{workspace_id}/versions

Append a new version to an existing workspace's layout — never overwrites, so prior layouts stay browsable. Requires membership of the workspace's home project (whether or not it's public).

Parameter Type Required Description
workspace_id string Yes Workspace to add a version to.

Request body: {layout} — the new version's node tree. layout is a typed JSON object (422 if a string or array), same as create_workspace.

Returns: The workspace object with the new version appended.


update_workspace

PATCH /workspace/{workspace_id}

Rename a workspace and/or toggle whether it's public. Any member of the workspace's home project may do either (no creator-only or admin-only restriction).

Parameter Type Required Description
workspace_id string Yes Workspace to update.

Request body: {title?, is_public?} — supply either or both.

Returns: The updated workspace object.


fork_workspace

POST /workspace/{workspace_id}/fork?project_id=<id>

Copy a version of a public workspace into your own project as a new, independent workspace. The fork is a snapshot copy (not a live reference): it starts its own version-1 history and never changes when the source is edited afterward. Requires membership of the destination project_id; the source must be public (404 otherwise).

Parameter Type Required Description
workspace_id string Yes Public source workspace to fork.
project_id string Yes Destination project (the fork's new home).

Request body: {version?} — which source version to copy. Defaults to the source's latest.

Returns: The new forked workspace object (with forked_from_workspace_id/forked_from_version set).


get_workspace_schema

GET /workspace-schema

Return a terse index of the widget types available for a workspace layout — one short row per widget instead of one giant recursive schema (the full dump is 248 KB and overflows the tool-output limit). Each row is {widget, title, description?, container, has_params}; the response also carries a node_envelope describing the common node shape. Drill in from here:

  1. get_workspace_schema() → this index
  2. get_widget_schema(widget=...) → one widget's node/param schema
  3. list_plot_layer_types(widget="PlotView") → PlotView's layer types
  4. get_plot_layer_schema(widget="PlotView", layer_type=...) → one layer's params

Returns 503 if widget schemas have not been generated yet (reads backend/widget_schemas.json). To generate them:

cd frontend && npm run export-schemas

No parameters (MCP). REST/OpenAPI callers may pass a hidden verbose=true query param to get the historical full recursive $defs/Node-union dump unchanged; the MCP tool schema never exposes it.

Returns: {widgets: [...], node_envelope: {...}} (terse), or the full JSON Schema document when verbose=true.


get_widget_schema

GET /workspace-schema/widget/{widget}

Return the node schema for one widget type. Container widgets (VerticalSplit, HorizontalSplit, TabSet) give the {id, widget, children} shape; leaf widgets give {id, widget, layoutConfig} with layoutConfig being that widget's parameter schema. PlotView is special — its layers union is collapsed to a list of layer-type names (drill in via list_plot_layer_types / get_plot_layer_schema) so the payload stays within the tool-output limit.

Parameter Type Required Description
widget string Yes A widget value from get_workspace_schema. 404 if unknown.

Returns: A JSON Schema node object (with $defs at its root when the widget's params reference them).


list_plot_layer_types

GET /workspace-schema/widget/{widget}/layer

List the layer types available for a plotting widget (PlotView). Returns one short row per layer type ({layer_type, title, description}). Widgets with no layers return an empty list; unknown widgets return 404.

Parameter Type Required Description
widget string Yes The plotting widget, i.e. PlotView.

Returns: Array of {layer_type, title, description} rows.


get_plot_layer_schema

GET /workspace-schema/widget/{widget}/layer/{layer_type}

Return the parameter schema for one layer type of a plotting widget. A layer is an object with a single key naming its type — e.g. {"ResistivityCurtain": {...params...}} — and this returns that inner parameter schema (with any $defs it references).

Parameter Type Required Description
widget string Yes The plotting widget, i.e. PlotView. 404 if unknown.
layer_type string Yes A layer_type from list_plot_layer_types. 404 if unknown.

Returns: The layer type's parameter JSON Schema.


get_app_url

GET /workspace/app-url

Build a deep-link URL that opens the app with specific state pre-selected. All parameters after workspace_id are optional — omit trailing ones to link at a coarser level.

Parameter Type Required Description
workspace_id string Yes Workspace to open.
project_id string No Pre-select a project.
process_id string No Pre-select a process.
version integer No Pre-select a specific process version.
part string No Pre-select a dataset part path.
sounding integer No Pre-select a specific sounding index.

Returns: {"url": "https://..."}