Automat Workflows MCP Server

Automat Workflows MCP Server

A remote MCP server that lets AI agents build, run, and manage Automat RPA workflows.

Category
访问服务器

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_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.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选