Skip to main content
Glama
AutomatAI

Automat Workflows MCP Server

Official
by AutomatAI

Robotic Workflows MCP Server

Claude Build Day submission. A remote MCP server that lets a Claude agent build, deploy, run, and debug real RPA workflows on Automat — browser/API automations that then run on their own schedule, deterministically, with zero LLM tokens per run.

The agent writes the automation once (using tokens); the workflow then runs forever on a cron with no tokens per run. Token-cheap to create, token-free to operate.

The brief

  • Problem. Back-office/RPA automations take days to build and stay locked inside builder UIs. An AI agent can do a task once, but re-doing it every run burns tokens and isn't repeatable or schedulable.

  • Who it's for. Anyone with a recurring browser/API task — ops, back-office, founders — and the agents acting on their behalf.

  • Done looks like. From a chat/agent: "build a workflow that does X on a schedule." The agent authors it through this MCP server, deploys it live, runs it, and returns a recording — and it keeps running on its schedule with no tokens.

Related MCP server: mmc-mcp

What we built at Build Day

This repo is the agent-facing layer: one Vercel Function (api/mcp.ts) exposing 32 MCP tools that forward to Automat studio's project-scoped agent API. Built during the event:

  • Full tool surface — discover (get_docs, get_workflow_schema, list_*), build (create_workflow, edit_workflow composite-patch model, read_workflow), manage (versions, lifecycle, schedules), run & debug (run_workflow, get_run with timeline/io/recording, cancel_run), plus secrets, resources, extractors, and HITL.

  • get_docs — serves the runtime authoring model (code-node globals, $('NodeName'), fetch, worked examples) so an agent writes working code nodes with no source access.

  • Pass-through auth — the caller's project key is forwarded per request; no secrets stored in this public repo.

  • (The backend it forwards to — studio's /api/agent/* — was built in parallel in our private studio repo.)

Demo — "Sauce Demo Shopper"

A Claude agent built this through the MCP server: a deterministic Playwright code node that logs into saucedemo.com, adds an item, and checks out — recorded, ~9s/run, 0 tokens per run, deployed active and schedulable.

  • Authored via create_workflow + edit_workflow, executed via run_workflow, recording fetched via get_run(include:["recording"]).

  • When the live run hit a native Chrome "breached-password" dialog that swallowed clicks, the agent reproduced it with Chrome DevTools and rewrote the clicks as page.evaluate(() => el.click())self-corrected, then re-ran green.

Try it / verify

Live, and "done" is verifiable by the model with no human in the loop:

  • Responding URLhttps://workflows.runautomat.com/api/mcp answers tools/list and tools/call over Streamable HTTP.

  • Connect any MCP client with a project key (see Connect a client) and run the loop: get_docscreate_workflowrun_workflowget_run.

  • Acceptance checklist (rubric). (1) endpoint lists 32 tools; (2) create_workflow + edit_workflow(patch) each save a new version; (3) run_workflowget_run returns status:"completed" with structured output; (4) a browser workflow returns a recordingUrl.

How Claude built it (Opus 4.8)

Opus 4.8 drove the whole build: it explored the studio + runtime repos to design the tool schemas, grounded the descriptions in MCP best practices, and self-verified — running a full stress test across every tool and real workflow runs through its own tools, then fixing a live browser failure with Chrome DevTools. It's repeatable: push to main auto-deploys, and get_docs + the tool surface let any agent rerun the build loop on a brand-new task.

Endpoint

https://workflows.runautomat.com/api/mcp

Streamable HTTP, stateless. The Vercel default URL (https://robotic-workflows-mcp-server.vercel.app/api/mcp) also resolves.

Authentication

Pass-through. The caller supplies a project-scoped studio key (ak_…); the server forwards it to the studio API per request. No keys are stored or committed. The key is read three ways (checked in order):

Source

Use

?api_key=ak_… query param

Claude web/desktop connector (its UI has no header field)

x-api-key: ak_… header

generic clients

Authorization: Bearer ak_… header

Claude Code CLI

Configuration (Vercel env)

Var

Purpose

STUDIO_API_BASE_URL

Origin of the studio agent API. Studio preview URLs change per deploy — update this each studio redeploy (or point at a stable alias once preview protection is lifted).

VERCEL_AUTOMATION_BYPASS_SECRET

Optional — only if the studio deployment is protected.

Connect a client

Replace ak_… with your project-scoped studio key.

Claude web / desktop — Settings → Connectors → Add custom connector → URL:

https://workflows.runautomat.com/api/mcp?api_key=ak_…

Claude Code

claude mcp add --transport http automat \
  "https://workflows.runautomat.com/api/mcp?api_key=ak_…"

MCP Inspector

npx @modelcontextprotocol/inspector
# Streamable HTTP → https://workflows.runautomat.com/api/mcp?api_key=ak_…

Development

npm install
npm run dev          # vercel dev → http://localhost:3000/api/mcp
npm run inspector    # MCP Inspector
vercel --prod        # deploy (requires vercel login)

Stack

mcp-handler (wrapping @modelcontextprotocol/sdk) as a single Vercel Function — no framework. The whole server is api/mcp.ts. It serves /api/mcp; a /mcp rewrite is not used because it collides with Vercel's built-in /api routing guard.


Tools

Live reference for the 32 tools. Each forwards to the studio agent API (STUDIO_API_BASE_URL + /api/agent/*), passing the caller's project-scoped key; the project is resolved from the key. The build/edit flow mirrors studio's own builder agent: read_workflowedit_workflow(patch) with server-side validation.

Conventions

  • Transport. Streamable HTTP (stateless), at https://workflows.runautomat.com/api/mcp.

  • Scope. One key = one project. No tool takes a projectId.

  • Workflow definition. The @automat/runtime WorkflowSchema: { name, description?, instructions?, notes?, settings, nodes[], edges[], sessionFields?, inputSchema?, outputSchema?, helpers?, files?, runtimeVersion? }. Nodes are a discriminated union on type (start, end, block, decision, document, hitl). Edges are { from, to, handle? }. Call get_workflow_schema for the exact shape.

  • Errors. On failure a tool returns result text { "error": { "code", "message", "issues"? } }. Codes: not_found, validation_failed, version_conflict, conflict, lifecycle_gated, forbidden, bad_request, rate_limited, unauthorized, internal_error. issues[] accompanies validation_failed.

  • Pagination. List tools take limit (default 25, max 100) and cursor, and return { items, nextCursor } (the cursor wraps the API's page number).

Each tool lists its input, output, and the backing /api/agent call.

Context & schema

get_docs

Authoring guide — call first. How to write code/decision nodes: globals ($('NodeName'), fetch, secrets, page/context, logger), async/return semantics, node types, browser/recording, schedules, and worked examples.

  • Input: { topic?: 'overview'|'codeNodes'|'nodeTypes'|'browser'|'secrets'|'schedules'|'examples' }

  • Output: the docs (all sections, or one topic)

list_runtime_versions

  • Input: none

  • Output: { versions: [{ version, isLatest }], note }

  • Runtime-version selection isn't exposed by the API; returns latest.

get_workflow_schema

  • Input: { runtimeVersion?: string } (default latest)

  • Output: { runtimeVersion, jsonSchema }

  • GET /api/agent/schema

Workflows

list_workflows

  • Input: { status?, search?, limit?, cursor? } (status: development | preview | active | disabled)

  • Output: { items: [{ workflowId, name, description, status, activeVersionId, apiEnabled, apiUrlSlug, sessionCount, lastRunAt, updatedAt }], nextCursor }

  • GET /api/agent/workflows (status/search filtered client-side)

create_workflow

  • Input: { name, description?, definition?, runtimeVersion? } — omit definition for a minimal start → end scaffold

  • Output: { workflowId, versionId, versionNumber, status }

  • POST /api/agent/workflows

copy_workflow

  • Input: { workflowId, name? }

  • Output: { workflowId, name }

  • Client-side: reads the source's active definition, then create_workflow with it. Schedules/runs not copied.

read_workflow

  • Input: { workflowId, view: 'graph' | 'node' | 'full', nodeName? } (nodeName required for node)

  • Output: { _meta: { workflowId, versionId, versionNumber, status, apiEnabled, apiUrlSlug }, ... }graph (nodes/edges + metadata, no node code), node (one node), full (entire definition). Pass _meta.versionId to edit_workflow.

  • GET /api/agent/workflows/{id}; graph/node views derived client-side.

update_workflow

  • Input: { workflowId, name?, description?, status?, apiEnabled?, apiUrlSlug? }

  • Output: the updated workflow

  • status: active needs a published version; disabled auto-pauses schedules. → PATCH /api/agent/workflows/{id}

delete_workflow

  • Input: { workflowId } · Output: { success: true } · soft delete → DELETE /api/agent/workflows/{id}

Editing

edit_workflow

  • Input: { workflowId, patch, expectedActiveVersionId? }

    patch = {
      nodes?: { add?: Node[], update?: [{ name, patch }], remove?: string[] },
      edges?: { add?: Edge[], remove?: Edge[] },
      // any top-level WorkflowSchema field: settings deep-merges, others replace
    }
  • Output: { ok: true, versionId, versionNumber, deduped } or { error: { code, message, issues? } }

  • Client reads the active definition, applies the patch (order: nodes.removenodes.addnodes.update [rename rewrites edges] → edges.removeedges.add → top-level), then PUTs the full definition. The server validates → a new version (one edit, one version). expectedActiveVersionId (from read_workflow's _meta) gives optimistic concurrency. → GET + PUT /api/agent/workflows/{id}

Versions

list_versions

  • Input: { workflowId, limit?, cursor?, named?, source? }

  • Output: { items: [{ versionId, versionNumber, name, source, createdAt }], nextCursor, activeVersionId }GET /api/agent/workflows/{id}/versions

get_version

  • Input: { workflowId, versionId } · Output: { versionId, versionNumber, name, source, createdAt, definition }GET …/versions/{versionId}

revert_to_version

  • Input: { workflowId, versionId, expectedActiveVersionId? } · Output: { versionId, versionNumber, revertedFromVersionNumber } · non-destructive (appends a new version) → POST …/versions/{versionId}/revert

Schedules

All schedules run in UTC. A workflow may have many; run input comes from a linked project resource (inputResourceName), gated against the workflow's inputSchema.

list_schedules

  • Input: { workflowId } · Output: { items: [{ scheduleId, name, recurrenceRule, startAt, status, nextFireAt, inputResourceName }] }

create_schedule

  • Input: { workflowId, recurrenceRule (RFC 5545 RRULE, UTC), name?, startAt? (UTC), enabled?, inputResourceName? }enabled: false creates it paused

  • Output: { scheduleId }POST /api/agent/workflows/{id}/schedules

update_schedule

  • Input: { workflowId, scheduleId, recurrenceRule?, name?, startAt?, enabled?, inputResourceName? }enabled maps to status active/paused

  • Output: { scheduleId }PATCH …/schedules/{scheduleId}

delete_schedule

  • Input: { workflowId, scheduleId } · Output: { success: true }DELETE …/schedules/{scheduleId}

Runs

run_workflow

  • Input: { workflowId, input?, environment? } (environment: development | staging | preview | production, default production)

  • Output: { sessionId, status: 'queued' } · input validated against inputSchema; lifecycle_gated if disabled / no active version. environment is sent as a query param. → POST /api/agent/workflows/{id}/run

list_runs

  • Input: { workflowId?, status?, limit?, cursor? } · Output: { items: [{ sessionId, workflowId, status, source, startedAt, endedAt, durationMs }], nextCursor }GET /api/agent/sessions

get_run

  • Input: { sessionId, include?: ('timeline' | 'io' | 'logs' | 'recording')[], logsCursor? }

  • Output: { sessionId, workflowId, versionId, status, source, input, output, startedAt, endedAt, durationMs } plus, when requested: timeline [{ name, type, status, startedAt, endedAt, durationMs }], nodeIO [{ name, input, output }], recordingUrl, logs { entries, nextCursor }.

  • timeline/io ← GET /sessions/{id}/nodes; recording ← the session; logs ← /sessions/{id}/logs (best-effort — returns null + logsNote until that endpoint is deployed).

cancel_run

  • Input: { sessionId } · Output: { success: true, status: 'canceled' }POST /sessions/{id}/stop

Human-in-the-loop

list_hitl_tasks

  • Input: { sessionId?, status?, limit?, cursor? } (status: pending | completed | expired)

  • Output: { items: [{ taskId, sessionId, workflowId, nodeName, prompt, actions, isApproval, fields, status, createdAt, expiresAt }], nextCursor }GET /api/agent/hitl/tasks

complete_hitl_task

  • Input: { taskId, action, fields? } · Output: { success: true }POST /api/agent/hitl/tasks/{taskId}/complete

Secrets

Project-scoped. Values are never returned.

list_secrets

  • Input: { lifecycle?, limit?, cursor? } · Output: { items: [{ key, last4, lifecycle, updatedAt }], nextCursor }

set_secrets

  • Input: { secrets: [{ key, value, description?, lifecycle? }] } · Output: { updated: [keys] } · upsert by key (resolves key→id, then PUT or POST)

delete_secret

  • Input: { key } · Output: { success: true } · resolves key→id

Resources

Data resources, referenced by name from block/document nodes and schedule inputs. Each has a lifecycle (development | preview | active).

list_resources

  • Input: { kind?, lifecycle?, search?, limit?, cursor? } · Output: { items: [{ name, kind, description, lifecycle, updatedAt }], nextCursor }

get_resource

  • Input: { name, lifecycle? } · Output: { name, kind: 'data', value, description, lifecycle, updatedAt }

set_resource

  • Input: { name, value, description?, lifecycle? } · Output: { name } · upsert by name; omitting lifecycle seeds all stages. File-resource uploads not supported.

delete_resource

  • Input: { name, lifecycle? } · Output: { success: true }

Extractors

Read-only. document nodes reference an extractorId; authoring is not exposed.

list_extractors

  • Input: { search?, limit?, cursor? } · Output: { items: [{ extractorId, name, activeVersionId, description }], nextCursor }

get_extractor

  • Input: { extractorId, view?: 'summary' | 'full' } · Output: { extractor }

Known gaps

  • Session logs: studio /api/agent/sessions/{id}/logs is pending; get_run logs return null + a note until it's deployed.

  • Schedules are UTC-only.

  • File-resource uploads and extractor authoring are not yet available.

Roadmap

  • Per-project API keys (today the caller's project-scoped studio key is forwarded as-is).

  • Session logs + recording surfaced once the studio endpoint lands.

  • Extractor authoring and file-resource uploads.

F
license - not found
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/AutomatAI/robotic-workflows-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server