Skip to content

MCP server ​

Marimohub exposes notebooks that a user can access through the Model Context Protocol (MCP). By default, the Hub issues a scoped personal access token after browser consent. Deployments can instead enable external authorization through their OIDC issuer.

Enable MCP ​

MCP is off by default and runs only on the Node server. Set these variables:

dotenv
MARIMOHUB_MCP=on
MARIMOHUB_APP_BASE_URL=https://hub.example.com

MARIMOHUB_APP_BASE_URL must include the public origin and any path prefix. MARIMOHUB_APP_BASE_URL must use HTTPS, because the OAuth issuer derives from it. The server refuses to start with a plain http:// URL unless the host is localhost or 127.0.0.1.

The MCP server URL adds /mcp to this value:

text
https://hub.example.com/mcp

OAuth discovery uses the base URL to publish stable, absolute URLs. The MCP dialog in the user menu shows the MCP URL and client setup instructions.

Connect a client ​

For Claude Code, run:

bash
claude mcp add --transport http marimohub https://hub.example.com/mcp

For Claude.ai, add a custom connector and enter the MCP server URL. For Cursor, add a remote HTTP MCP server. The client discovers the authorization server.

By default, the client registers with the Hub and opens the marimohub consent page. With external authorization, the client uses the configured issuer.

For the default Hub authorization flow, use the following consent checklist. Check the client name and redirect URL before approval. The default grant permits notebook editing and execution. Use the smallest practical set of actions and projects. The token lifetime defaults to 7 days and cannot exceed 90 days. Revoke a token from the API tokens dialog. Marimohub does not issue refresh tokens. Expiry or revocation requires a new authorization.

Work with notebooks ​

Use list_catalog to find accessible projects, notebooks, and active sessions. Project and notebook selectors accept IDs or exact names, case-insensitively. Use IDs when names are duplicated and for subsequent calls.

ToolPurpose
list_catalogDiscover notebooks. Filter by project, status, tag, or text.
get_notebookRead notebook metadata and stored source.
create_notebookCreate a local notebook. Optional launch starts an edit session.
update_notebookReplace supplied metadata fields or the complete local source.
delete_notebookSoft-delete a notebook, retire live apps, and cancel job runs.
start_sessionStart or reuse an edit or app session.
execute_codeRun Python in an edit session's live scratchpad.
stop_sessionStop a session and destroy its sandbox, with a save attempt for persistent edits.

When profile selection is enabled, both tools accept an optional compute_profile:

  • create_notebook saves the profile and uses it with launch: true. Omission uses the deployment default.
  • start_session overrides the profile for a new persistent edit session without changing the saved choice. Omission uses the saved profile.

Reused sessions and restored filesystem snapshots retain their profile. "default" selects the deployment default, except during creation when a configured profile is named default.

Edit stored source ​

Notebook reads, updates, and deletions work without a session. get_notebook returns stored source, which can differ from unsaved edits in a live session.

  1. Read the notebook with get_notebook.
  2. Pass the changed fields to update_notebook, using expected_updated_at from the read.

Omitted fields remain unchanged. Supplied fields replace their previous values. A code update creates a version. Remote source changes go through sync. delete_notebook also accepts expected_updated_at.

If the precondition fails, read the latest notebook before retrying. A persistent edit session blocks stored code replacement until its sandbox is cleaned up. Edit in the live session, or stop it and call get_notebook to include its saved changes before retrying. Metadata updates, app sessions, and temporary sessions do not have this restriction.

Source format ​

The code parameter for create_notebook and update_notebook contains a complete marimo Python notebook. For example:

python
import marimo

app = marimo.App()


@app.cell
def _():
    import marimo as mo
    mo.md("Hello from MCP")
    return


if __name__ == "__main__":
    app.run()

The Hub stores source verbatim, without syntax validation or script conversion.

Notebook dependencies ​

Prefix the code supplied to create_notebook or update_notebook with a PEP 723 header:

python
# /// script
# dependencies = ["cowsay==6.1"]
# ///

For local and synced notebooks, dependencies install before edit sessions, app sessions, and jobs start. Creation without launch: true only saves the source. Imports alone do not declare dependencies.

Workspace pyproject.toml dependencies install first, then inline dependencies. Keep requirements compatible: inline pins can replace project versions. The sandbox image supplies marimo. See the dependency contract for source-version behavior, Python requirements, and custom indexes.

To change dependencies:

  1. Stop the persistent edit session.
  2. Read its saved source with get_notebook.
  3. Call update_notebook with the complete updated source.
  4. Start a new session.

Running kernels do not reinstall dependencies after header changes. Invalid metadata or unresolved dependencies fail startup with PYTHON_ENV_SETUP_FAILED. If the MCP client times out during installation, retry start_session to inspect startup. Preinstall large packages to avoid repeated cold installs.

Work in a live session ​

  1. Call start_session with mode: "edit".
  2. Read execution.ready. If it is false, follow execution.next_step.
  3. Call execute_code with the returned project and session IDs.
  4. When finished, call stop_session for sessions you no longer need.

Edit sessions initialize kernels without a browser and respect the notebook's automatic-execution settings. create_notebook with launch: true does the same. Repeated starts reuse the kernel without rerunning cells. The first start can take about two minutes.

Session status describes the sandbox lifecycle. execution.status reports kernel readiness:

StatusNext step
readyCall execute_code. Execution queues behind any running cells.
startingRetry start_session to check sandbox startup. If authorization is required, follow execution.next_step.
initializingRetry start_session with a positive wait_seconds.
awaiting_clientOpen notebook_url in a browser. This runtime requires browser initialization.
unavailableRetry start_session. If it fails again, check the session logs or open the notebook in a browser.
forbiddenObtain session.attach access before executing code.
app_modeCall start_session with mode: "edit" to execute code.
terminating, terminated, failed, expiredCheck the session status and error before retrying start_session.

wait_seconds accepts integers from 0 to 120 (default 60). It bounds kernel initialization retries after sandbox startup while readiness remains initializing. If the wait expires, call start_session again. Zero inspects existing kernels, including browser sessions, without creating a kernel or running cells.

Custom images need compatible marimo and WebSocket support. execute_code does not install dependencies automatically.

A browser can attach later without losing notebook variables or cell edits. If the kernel disappears, execute_code directs you to start_session. It does not recreate the kernel or replay code after ambiguous failures.

execute_code reads live notebook variables, but scratchpad assignments are temporary. For persistent variables and cell edits, use marimo's code-mode API: import marimo._code_mode as cm; help(cm). App sessions do not support scratchpad execution.

Authorized MCP requests keep sessions active until completion, authorization expiry, or session termination. When MCP requests and browser activity stop, idle cleanup applies.

Cancellation or disconnection stops the call's wait and heartbeats. It does not stop an existing session or undo work already sent to the runtime. A sandbox already provisioning can finish startup and remains subject to normal idle cleanup. Use stop_session to stop a session explicitly.

Run and schedule jobs ​

Set MARIMOHUB_JOBS=on and restart the deployment to register these tools. When disabled, they are absent from tools/list and direct calls return an unknown-tool error.

ToolPurpose
list_jobsList saved jobs with cursor pagination.
create_jobSave a manual or scheduled job without starting a run.
schedule_jobChange or remove a schedule, or pause and resume scheduled runs.
run_jobQueue a saved job for headless execution.
get_job_runRead or wait for an existing run attempt.

All tools require project and notebook. These selectors and job accept IDs or exact, case-insensitive names. Duplicate names require an ID.

With jobs enabled, get_notebook returns a jobs array with IDs, schedules, enabled state, parameters, and updated_at. The array is empty for notebooks without jobs. Disabled deployments omit it.

create_job accepts the job API fields: name, parameters, schedule, timeout, retry policy, concurrency policy, notifications, and enabled state. Omitting the schedule creates a manual-only job. Parameters are strings available through mo.cli_args() and visible to project readers.

schedule_job requires expected_updated_at from list_jobs, get_notebook, or a previous job mutation. At least one change is required:

ArgumentEffect
schedule: { cron, timezone }Set a cron schedule with an IANA timezone.
schedule: nullRemove the schedule.
enabled: false / enabled: truePause / resume scheduled runs.

Omitted fields remain unchanged.

Start and poll a run ​

Call run_job with a saved job:

json
{
	"project": "Analytics",
	"notebook": "Report",
	"job": "Nightly",
	"parameters": { "region": "eu" },
	"idempotency_key": "report-eu-2026-09-15",
	"wait": false
}

Supplied parameters replace the stored map for this run. An empty map clears them. The maintenance scheduler must be active to start queued runs.

Results contain run, completed, wait_expired, and links to the browser run page and REST status endpoint. Available artifacts add HTML and log links, not their contents. All links require Hub authentication. Logs require editor access.

Active results include poll with tool: "get_job_run", its arguments, and interval_seconds: 2. Use those arguments to poll. Another run_job call can start another run.

create_job and run_job accept idempotency_key. After a lost response, reuse the key and inputs. Replay records last 24 hours. Concurrent run_job calls with the same key, caller, and job reuse one run. create_job and REST retain best-effort replay: concurrent first requests can create duplicates. A crash or storage failure between enqueueing and recording the replay can still cause a duplicate run on retry.

Wait with optional progress ​

Both run tools default to wait: false. With wait: true, they check status every two seconds for up to wait_seconds (default 60, range 1–120). Expiry returns the latest status, wait_expired: true, and polling details. The run continues under its separate execution timeout.

For progress, send _meta.progressToken in the MCP request parameters, outside the tool arguments. Streamable HTTP delivers an initial notification and observed status changes over SSE. The counter increases without a completion percentage. Without a token, the call returns only the final result.

Cancellation or disconnection stops observation. The run continues. Each poll checks current project membership and notebook visibility. To cancel execution, use the job UI or REST cancellation endpoint.

Each call tracks one attempt. Automatic retries have separate run IDs linked by retry_of. Failed, timed-out, cancelled, and skipped attempts return normal status data. Tool errors indicate invalid input, denied access, or another request failure.

External authorization ​

With external OIDC access tokens enabled, MCP discovery advertises the configured OIDC issuer. Clients use that issuer for authorization and consent. The issuer must support the discovery, client registration, and PKCE flow that each MCP client requires.

Configure the issuer to accept the exact public MCP URL as an OAuth resource, including any deployment path prefix.

Clients must request mcp:tools and at least one Hub grant scope, such as marimohub:read or marimohub:edit. The issuer controls which clients can request these scopes and what the user approves.

The initial authorization challenge requests mcp:tools marimohub:read. Discovery lists all supported scopes, but clients that honor the challenge start with read access. For execution or editing, authorize the client with the corresponding grant scope. The Hub does not automatically request broader scopes after a tool is denied.

External authorization does not use the Hub consent page or its project selector. Token scopes limit actions across all projects that the user can already access. They cannot increase user permissions or permit session-only administration.

Existing Hub-issued MCP tokens continue to work. Hub OAuth endpoints remain available, but protected-resource metadata advertises only the external issuer. External tokens expire within one hour, or sooner under the configured group-session limit. The Hub cannot revoke or refresh them.

A gateway needs an access token for the Hub with the required audience and scopes. A shared issuer alone does not guarantee one authorization step. Test discovery, client registration, resource requests, and scope requests with your gateway before deployment.

OAuth and security ​

The following OAuth rules apply to Hub-issued credentials. External credentials use the issuer requirements in external authorization.

Dynamic registration creates public clients that use authorization code and PKCE S256. Redirect URIs must use HTTPS, loopback HTTP, or a private-use application scheme. Marimohub supports cursor: and reverse-domain, single-slash application schemes. Authorization codes expire after ten minutes and can be used once. Authorization requests, token exchanges, and issued tokens must target the configured MCP URL. Each token also stores the registered client ID. Other marimohub PATs cannot access /mcp.

The mcp:tools OAuth scope permits MCP access. The consent grant restricts Hub actions and projects for each tool call.

Within the configured app base path, MCP reserves these paths:

  • /mcp
  • /authorize
  • /oauth/consent
  • /token
  • /register
  • /revoke
  • /.well-known/oauth-authorization-server
  • /.well-known/oauth-protected-resource
  • /.well-known/oauth-protected-resource/mcp

The grant does not restrict kernel code or injected credentials. Use a short token lifetime.

Hub dynamic registration remains available in both authorization modes. Registration is anonymous. Marimohub checks client metadata, enforces deployment-wide rate limits, and expires registrations after 90 days. Each successful registration emits an oauth_client_registered event without client-supplied names or URIs. Deployments that require client vetting must add trusted registration controls before enabling MCP.

Provider-agnostic. Deploy anywhere.