Automat Workflows MCP Server
A remote MCP server that lets AI agents build, run, and manage Automat RPA workflows.
README
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.
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_workflowcomposite-patch model,read_workflow), manage (versions, lifecycle, schedules), run & debug (run_workflow,get_runwith 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 workingcodenodes 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 viarun_workflow, recording fetched viaget_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 URL —
https://workflows.runautomat.com/api/mcpanswerstools/listandtools/callover Streamable HTTP. - Connect any MCP client with a project key (see Connect a client) and run the loop:
get_docs→create_workflow→run_workflow→get_run. - Acceptance checklist (rubric). (1) endpoint lists 32 tools; (2)
create_workflow+edit_workflow(patch)each save a new version; (3)run_workflow→get_runreturnsstatus:"completed"with structuredoutput; (4) a browser workflow returns arecordingUrl.
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_workflow → edit_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/runtimeWorkflowSchema:{ name, description?, instructions?, notes?, settings, nodes[], edges[], sessionFields?, inputSchema?, outputSchema?, helpers?, files?, runtimeVersion? }. Nodes are a discriminated union ontype(start,end,block,decision,document,hitl). Edges are{ from, to, handle? }. Callget_workflow_schemafor 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[]accompaniesvalidation_failed. - Pagination. List tools take
limit(default 25, max 100) andcursor, 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 }(defaultlatest) - 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/searchfiltered client-side)
create_workflow
- Input:
{ name, description?, definition?, runtimeVersion? }— omitdefinitionfor a minimalstart → endscaffold - 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_workflowwith it. Schedules/runs not copied.
read_workflow
- Input:
{ workflowId, view: 'graph' | 'node' | 'full', nodeName? }(nodeNamerequired fornode) - Output:
{ _meta: { workflowId, versionId, versionNumber, status, apiEnabled, apiUrlSlug }, ... }—graph(nodes/edges + metadata, no node code),node(one node),full(entire definition). Pass_meta.versionIdtoedit_workflow. - →
GET /api/agent/workflows/{id};graph/nodeviews derived client-side.
update_workflow
- Input:
{ workflowId, name?, description?, status?, apiEnabled?, apiUrlSlug? } - Output: the updated workflow
status: activeneeds a published version;disabledauto-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.remove→nodes.add→nodes.update[rename rewrites edges] →edges.remove→edges.add→ top-level), then PUTs the full definition. The server validates → a new version (one edit, one version).expectedActiveVersionId(fromread_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: falsecreates it paused - Output:
{ scheduleId }→POST /api/agent/workflows/{id}/schedules
update_schedule
- Input:
{ workflowId, scheduleId, recurrenceRule?, name?, startAt?, enabled?, inputResourceName? }—enabledmaps 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' }·inputvalidated againstinputSchema;lifecycle_gatedif disabled / no active version.environmentis 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 — returnsnull+logsNoteuntil 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; omittinglifecycleseeds 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}/logsis pending;get_runlogs returnnull+ 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.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。