Automat Workflows Server
БесплатноНе проверенA remote MCP server that lets AI agents build, run, and manage Automat RPA workflows.
Описание
A remote MCP server that lets AI agents build, run, and manage Automat RPA workflows.
README
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 35 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 Studio personal access token (pat_…, minted in Studio → Settings → Personal access tokens); the server forwards it as a Bearer to the studio public v1 API per request. No tokens are stored or committed. The token is read three ways (checked in order):
| Source | Use |
|---|---|
?api_key=pat_… query param |
Claude web/desktop connector (its UI has no header field) |
x-api-key: pat_… header |
generic clients |
Authorization: Bearer pat_… header |
Claude Code CLI |
Configuration (Vercel env)
| Var | Purpose |
|---|---|
STUDIO_API_BASE_URL |
Origin of the studio API. Production: https://studio.runautomat.com (stable). For a studio preview deploy, use that preview's URL (it changes per deploy). No trailing slash needed. |
VERCEL_AUTOMATION_BYPASS_SECRET |
Leave UNSET for production (the studio.runautomat.com custom domain is public). Set it only when STUDIO_API_BASE_URL points at a protection-enabled preview deploy. |
Auth note: a PAT is scoped to the studio environment it was minted in. Use a token minted at https://studio.runautomat.com/settings for production — a preview/staging token will 401 against prod (different database).
Connect a client
Replace pat_… with your personal access token. A PAT spans every project you can access, so a target project must be selected — the primary way is the set_project tool (the agent calls it once with the project UUID; the server injects it into every subsequent API call, best-effort warm-instance memory that self-heals via a "Missing project id" error → re-call set_project). Alternatively pin it on the connection: &project_id=<uuid> on the URL, an x-project-id header, or STUDIO_DEFAULT_PROJECT_ID on the server (set_project overrides all three). Secrets tools additionally need the Doppler identifiers (dopplerProject/dopplerConfig tool inputs, or STUDIO_DOPPLER_PROJECT/STUDIO_DOPPLER_CONFIG).
Token tiers: read tokens list/inspect (no definition JSON — read_workflow full/node need an authorship-tier PAT; graph degrades gracefully); write tokens also run workflows / stop sessions / complete HITL tasks; workflow, schedule, secret, and resource mutations need authorship (author role + write token). Tier ledger: studio docs/PROGRAMMATIC_ACCESS.md.
Claude web / desktop — Settings → Connectors → Add custom connector → URL:
https://workflows.runautomat.com/api/mcp?api_key=pat_…&project_id=<uuid>
Claude Code
claude mcp add --transport http automat \
"https://workflows.runautomat.com/api/mcp?api_key=pat_…&project_id=<uuid>"
MCP Inspector
npx @modelcontextprotocol/inspector
# Streamable HTTP → https://workflows.runautomat.com/api/mcp?api_key=pat_…&project_id=<uuid>
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 35 tools. Each forwards to the studio public v1 API (STUDIO_API_BASE_URL + /api/v1/projects/{projectId}/*), passing the caller's PAT; the project comes from the connection (project_id). 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 call (shown in legacy /api/agent/* form; the client rewrites it onto /api/v1/projects/{projectId}/* at request time).
Context & schema
list_projects
Discovery — the projects this token can access (allowlist-scoped tokens see only their allowlist). Use it to pick a set_project target.
- Input:
{ limit?, cursor? } - Output:
{ items: [{ projectId, name }], nextCursor } - →
GET /api/v1/projects(the one project-agnostic list)
set_project
Selects the target Studio project for every subsequent call — call first when the connection has no ?project_id=. Validates the id against the list_projects discovery listing (a project-scoped probe can't tell a typo from an empty project on an all-projects token). Best-effort warm-instance memory: a "Missing project id" error later just means "call set_project again".
- Input:
{ projectId }(UUID — discover vialist_projects) - Output:
{ projectId, validated: true } - → validates via
GET /api/v1/projects
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 code bodies, but per-nodecodeChars, decisionbranches/elseLabel, and ahelpersindex[{name, description, codeChars}]),node(one node incl. code),full(entire definition incl. helper code). Pass_meta.versionIdtoedit_workflow/edit_node_code. - Tiers:
full/nodereturn definition JSON → authorship-tier PAT required (403forbiddenotherwise);graphworks with any token, degrading to the server's lean names/types+edges view without authorship. - →
GET /api/agent/workflows/{id}?view=…; the richgraphprojection derives fromview=full, falling back to the server'sview=graphon a tier 403.
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} - Best for structural edits;
nodes.updatereplaces each patched field wholesale — for partial code changes useedit_node_code.
edit_node_code
- Input:
{ workflowId, nodeName, oldString, newString, field?: 'code' | 'instructions' | 'expression', replaceAll?, expectedActiveVersionId? } - Output:
{ ok: true, versionId, versionNumber, deduped, replacements, fieldChars }or{ error: { code, message } } - Surgical find/replace inside one node — the agent sends only the changed snippet instead of resending a multi-KB
codestring.oldStringmust match exactly and occur once (or passreplaceAll); an ambiguous match returns the occurrence count, a miss returns the field sizes.field: 'expression'spans the legacyexpressionplus every decisionbranches[i].expression. Same read-modify-PUT + optimistic-concurrency path asedit_workflow. →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?, previewBranch? } - Output:
{ sessionId, status: 'queued' }·inputvalidated againstinputSchema;lifecycle_gatedif disabled / no active version. →POST /api/agent/workflows/{id}/run - Defaults to the stable production Trigger runtime — studio resolves its deployment's default deploy tier (production on a prod studio). This is the path for all normal runs.
previewBranchis an explicit opt-in — set it ONLY to run against a specific deployed preview-branch runtime (sendsenvironment=preview+ that branch). The branch must have a running preview worker or the run stalls inqueued; that's why it's off by default and never inferred.
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.
Установка Automat Workflows Server
У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.
▸ github.com/AutomatAI/robotic-workflows-mcp-serverFAQ
Automat Workflows Server MCP бесплатный?
Да, Automat Workflows Server MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для Automat Workflows Server?
Нет, Automat Workflows Server работает без API-ключей и переменных окружения.
Automat Workflows Server — hosted или self-hosted?
Доступен hosted-вариант: Unyly запускает сервер в облаке, локальная установка не обязательна.
Как установить Automat Workflows Server в Claude Desktop, Claude Code или Cursor?
Открой Automat Workflows Server на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
Fetch
Web content fetching and conversion for efficient LLM usage.
AWS KB Retrieval
Retrieval from AWS Knowledge Base using Bedrock Agent Runtime.
автор: modelcontextprotocolSpring AI MCP Server
Provides auto-configuration for setting up an MCP server in Spring Boot applications.
llm-analysis-assistant
A very streamlined mcp client that supports calling and monitoring stdio/sse/streamableHttp, and can also view request responses through the /logs page. It also
автор: xuzexin-hzCompare Automat Workflows Server with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории ai
