MCP App URL Tool Plan
Goal
Add an MCP tool that returns a deep-link URL the user can click to open the app at a specific state — workspace, project, process, version, dataset part, and sounding. The primary use case is an LLM agent that creates processes or configures workspaces and then hands the user a direct link to the result, rather than making them navigate there manually.
MCP Tool
| Tool | Description |
|---|---|
get_app_url |
Build a URL that opens the app with the specified state pre-selected. All parameters after workspace_id are optional — omit trailing ones to link at a coarser level (e.g. workspace only, or workspace + project + process with no sounding). Returns a URL string the user can click or open in a browser. |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
workspace_id |
string (UUID) | yes | ID of the workspace layout to load. Use list_workspaces to discover available workspaces. |
project_id |
string (UUID) | no | ID of the project to select within the workspace. |
process_id |
string (UUID) | no | ID of the process to open. Requires project_id. |
version |
integer | no | Process version number to select. Requires process_id. |
part |
string | no | Dataset part name (e.g. a flightline name) to load. Requires version. |
sounding |
integer | no | Sounding index within the selected part. Requires part. |
URL format
The frontend parses the following path structure (all segments optional after w):
{SERVER_URL}/app/w/{workspace_id}/p/{project_id}/pr/{process_id}/v/{version}/part/{part}/s/{sounding}
SERVER_URL is read from the server's environment config (e.g. https://ymerflow.earth).
Why this is useful
The LLM workflow this enables:
- Agent receives a user request ("plot the resistivity curtain for the latest inversion")
- Agent calls
list_processes/create_process, waits for completion - Agent calls
create_workspacewith a layout configured to show the result - Agent calls
get_app_urlwith the workspace, project, process, version, and optionally a starting sounding - Agent returns the URL to the user — one click to land in the exact view
Without this tool the agent would have to instruct the user to navigate manually through the UI.
Implementation
Backend
New endpoint in backend/routers/workspaces.py (or a new backend/routers/app_url.py):
@router.get("/app-url", tags=["Workspaces"])
def get_app_url(
workspace_id: str,
project_id: str | None = None,
process_id: str | None = None,
version: int | None = None,
part: str | None = None,
sounding: int | None = None,
settings: Settings = Depends(get_settings),
) -> dict:
"""
Build a URL that opens the app with the specified state pre-selected.
Returns {"url": "https://..."}.
"""
path = f"/app/w/{workspace_id}"
if project_id:
path += f"/p/{project_id}"
if process_id:
path += f"/pr/{process_id}"
if version is not None:
path += f"/v/{version}"
if part:
path += f"/part/{urllib.parse.quote(part, safe='')}"
if sounding is not None:
path += f"/s/{sounding}"
return {"url": f"{settings.app_base_url}{path}"}
Config: read SERVER_URL from the existing settings model — it is already defined in
config.env.example as the public URL clients use to reach the app. Default to
http://localhost:3000 for development (where SERVER_URL is typically unset).
No ID validation: the frontend handles missing or unknown IDs gracefully with auto-selection fallbacks; backend validation would add round-trips for no user benefit.
Frontend (no changes needed)
ProcessContext.js already implements parseUrlParams() (line 95) and buildUrlPath() (line
136) which handle the full URL structure including optional trailing segments. Deep-linking is
fully supported today.
File Checklist
| File | Change |
|---|---|
backend/routers/workspaces.py (or new app_url.py) |
Add GET /app-url endpoint tagged "Workspaces" |
backend/config.py (or equivalent settings file) |
Add SERVER_URL setting |
config.env.example |
SERVER_URL already documented — no change needed |
backend/main.py |
Import new router if in separate file |