# Agentled/mcp-server [Health: Active]

**Category:** 🏢 Workplace & Productivity  
**Repository:** https://github.com/Agentled/mcp-server  
**GitHub Stars:** 3  
**npm Downloads (last month):** 1329  
**Views:** 5  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/agentled-mcp-server

## Description
AI-native workflow orchestration with long-term memory, 100+ integrations, and unified credits. 32 MCP tools for building and running intelligent business workflows — lead enrichment, content publishing, company research, media production, and more. Knowledge Graph that learns across executions.

## Tools
Capabilities this server exposes over MCP:

- **list_workflows** — List all workflows in the workspace. Returns compact workflow summaries plus small metadataSummary fields (analytics, workflowGraph, revision, imports, builder preflight state). Use get_workflow for full steps, context, raw metadata, full builder contract/compiler preview, and update history.
- **get_workflow** — Get full details of a workflow including all steps, context, metadata, and configuration.
Also returns hasDraftSnapshot (boolean) and draftSnapshot summary if a draft exists for a live workflow.
When available, useCaseContext links the workflow to its WorkspaceUseCase and operating-guide README commands. Read the linked use case and guide before making workflow-specific claims or edits; missing-guide warnings mean context is incomplete.
- **get_workflow_credits** — Get ledger-derived, period-labelled credit usage for a single workflow.

Defaults to period=rolling-30-days. Set includeCostDrivers=true for bounded step/model/app cost drivers. Always show the returned period.label/display/start/end with any credit total. Use all-time intentionally because it can scan more ledger rows.
- **create_workflow** — Create a new workflow.

## KG-First — before you write any prompt content

If the workflow you're building contains workspace-specific content (investment thesis, ICP criteria, scoring rubrics, target sectors, brand voice, geo focus, seed lists), run this preflight before generating AI-step prompt strings:

1. **Inspect** — call `list_memories`, `list_knowledge_lists`, `get_knowledge_text` to see what already exists in the workspace KG.
2. **Seed** — if the content isn't in the KG yet, write it there first (`store_memory`, `upsert_knowledge_text`, `upsert_knowledge_rows`). Confirm with the user before seeding new content.
3. **Reference** — pull KG content at runtime in the step (`kg.read-text`, `kg.read-list`, `recall_memory`). Never paste workspace-specific paragraphs into prompt template strings.

**Boundary: strategy → KG; execution wiring → workflow context; workflow structure → workflow.**

## Recommended flow for agent-authored workflows

`create_workflow({ name, goal })` → `add_step` per step → `validate_workflow` → `publish_workflow`.

Call this with **only** `name` + `goal` — leave `steps` empty. Then `add_step` one step at a time. The incremental path catches errors immediately and surfaces `{{input.X}}` / `{{steps.Y.Z}}` bindings per-step. Internal testing: 0 errors incremental vs 13 errors bulk.

`pipeline.steps` is supported for imports/templates/round-trips only. Agents authoring from scratch should not use it.

## Pipeline object fields

- `name` (required), `goal`, `description`, `context`, `metadata`, `style`
- `steps`: Closed `type` values (others silently stripped): `trigger`, `appAction`, `aiAction`, `aiActionWithTools`, `toolAction`, `code`, `knowledgeSync`, `return`, `milestone`, `share`, `wait`, `branch`, `parallel`, `loop`, `end_if`, `agentOrchestrator`.

Prefer `schedule` triggers for email intake and non-real-time use — idempotent, no webhook infra. Use `app_event`/`webhook` only for "as soon as" / "real-time" requirements.

For child / sub-workflows that end in a `return` step and are only invoked via `agentled.call-workflow`, set `context.executionInputConfig.internal: true` to hide the Run button in the UI (see `update_workflow` for details).

## Step shape reference

Call `get_step_schema({ stepType, shape? })` for the authoritative field schema AND minimal JSON examples of every common step shape. The highest-friction shapes are:

- **Report step** (aiAction with Config renderer) — `get_step_schema({ stepType: "aiAction", shape: "report" })`.
- **Composed email with approval** — `get_step_schema({ stepType: "aiAction", shape: "email" })`. Use this with `schedule-email`; do not add Gmail/Outlook send appActions unless explicitly requested.
- **Agentic research** (web_search + workspace_memory) — `get_step_schema({ stepType: "aiActionWithTools", shape: "agentic-search" })`.
- **Agent Team** (agentOrchestrator) — `get_step_schema({ stepType: "agentOrchestrator", shape: "supervisor" })`.
- **Public share URL for a report** — `get_step_schema({ stepType: "share", shape: "public" })`.
- **KG field mapping** — `get_step_schema({ stepType: "knowledgeSync", shape: "standard" })`.
- **Code step** (JavaScript transformation) — `get_step_schema({ stepType: "code", shape: "standard" })`. JavaScript only — Python is not supported.

For app actions, call `get_app_actions({ appId })` for input/output schemas. For models, call `list_models`.

## Composable Step Blocks

**Search & Extract**: `aiAction (queries) → appAction (search) → aiAction (extract)`
⚠️ NEVER pass raw user input (job titles, topics) directly to a search API — always generate optimized boolean/keyword queries first.
**Enrich & Score**: `appAction (fetch) → aiAction (score)`
**Draft & Send**: `aiAction email → approval + schedule-email`. Use HTML bodies for open/click tracking.
**Write app action with approval**: put the approval directly on the write `appAction` (`preExecuteApproval: true`, `onApproval.action: "execute-approved-action"`, `onApproval.target.type: "current-step"`). Do not add a separate "Approve X" milestone/wait step before the write action, and do not add a separate "Mark X sent/published" step unless it records the real post-send result.
**Report & Notify**: `aiAction report with Config renderer → share step → aiAction notification email with concise HTML overview + shareUrl`
**Loop Enrich & Filter**: `loopConfig on first step → appAction (enrich) → aiAction (score)`

## Available Apps & Data Sources

**Before proposing data sources or sourcing channels to the user, you MUST know what is actually reachable.** The catalog has two billing models — don't conflate them, and don't invent connectors that don't exist. Always call `list_apps` and `get_app_actions({ appId })` to verify before finalizing a plan.

### Billed via Agentled credits (no user setup needed)

- **agentled native LinkedIn / email**: `get-linkedin-profile-from-url`, `get-linkedin-company-from-url`, `find-email-person-domain`, `get-emails-from-company-domain`.
- **agentled native LinkedIn search & content**: `linkedin-post-search` (keyword post search — primary LinkedIn discovery surface), `linkedin-jobs`, `linkedin-profile-posts`, `linkedin-company-posts`.
- **email finder**: `hunter`.
- **web fetch / scrape**: `web-scraping.scrape` (any URL → markdown), `http-request.request`, `page-index`.
- **browser automation**: `browser-use.run-task` / `extract-data`, `anthropic-computer-use`, `openai-computer-use`.
- **AI / image gen**: `openai`, `google-gemini`, `mistral`, `bytedance`, `kling`.
- **public data feeds**: `french-gouv`, `google-maps`, `realtor`, `seloger`, `amazon`, `ad-intelligence`, `upwork`, `instagram`, `facebook`.
- **knowledge graph**: `kg.*` (read-list, upsert-rows, update-rows, traverse-edges, etc.) — 1 credit per call.
- **comms**: `gmail`, `google-calendar`, `webhook` (Slack/Discord), `notion`.

### Bring-your-own-key (NOT billed via Agentled credits)

These require the user to connect their own account / paste their own API key. Treat them as available only if the user has the integration connected.
- `crunchbase` (user's Crunchbase API key)
- `specter` (user's Specter API key)
- `affinity-crm` (user's Affinity API key)
- `phantombuster` (user's PhantomBuster account — runs LinkedIn search agents, Sales Navigator scrapers, etc.)

When proposing one of these, ask "do you already have a <service> account connected?" before assuming you can use it.

### Built-in tools (for `aiActionWithTools` steps, not standalone apps)

`web_search`, `file_search`, `code_interpreter`, `fetch_website_content`, `kg_search`, `kg_traverse`, `kg_nodes`, `kg_write`, `workspace_memory`. Attach via the step's `tools` array; the AI decides at runtime whether to call them.

### Common gotchas

- Want LinkedIn keyword/post search? Use the native `linkedin-post-search` (Agentled credits) — see `deal-sourcing-linkedin-founder-signals.ts` for a reference workflow.
- Want LinkedIn Sales Navigator search / lead lists / company employees? `phantombuster` (BYOK) — see `deal-sourcing-linkedin.ts`.
- No native ProductHunt / EU-Startups / X-Twitter connector — use `web-scraping.scrape` on a known URL or `web_search` via `aiActionWithTools`.
- LinkedIn profile / company *enrichment* is URL-only (`get-linkedin-profile-from-url`). For discovery, pair it with `linkedin-post-search` or `phantombuster`.

## Multi-Workflow Architecture (Source → KG → Process)

Before building a multi-workflow goal, call `list_use_cases` or `get_use_case`; reuse matching `workflowGraphId`, KG refs, agents, and workflows. Tag new bundles with `metadata.workflowGraph.id`.

For a new multi-source/shared-tail goal, call `preview_use_case_kit` first and review its dry-run operations with the user. If the ask fits Source -> KG -> Process, **do not hand-roll** N disconnected `create_workflow` calls from scratch.

When the user wants to "find leads", "source startups", "build a list to act on later", or run anything on a recurring cadence that produces entities to act on, **do not build one monolithic workflow**. Build several:

1. **One sourcing workflow per channel/theme** (e.g. "LinkedIn cybersecurity startups", "YC W25 batch", "ProductHunt this week"). Each runs on its own schedule and writes to a **shared KG list** via `kg.upsert-rows` with: `userKey` = stable id (URL/domain/LinkedIn URL) for O(1) dedup across runs; `status: "new"` to mark rows for downstream processing; `mergeStrategy: "merge"` so fields added later (scores, outreach status) survive re-upserts.
2. **One orchestrator/qualifier workflow** that runs on its own cadence (e.g. weekly), reads `kg.read-list({ filters: { status: "new" } })`, qualifies/scores each row against the current theme, then either dispatches to outreach or marks `status: "qualified" / "rejected"`.
3. **One outreach workflow** (often a child workflow called via `agentled.call-workflow`) that the orchestrator invokes for qualified rows.

Why split: sourcing cadences, qualification criteria, and approval-gated outreach evolve independently. Multiple sourcing workflows converging on one `listKey` is the canonical pattern.

Suggest the split explicitly ("N sourcing workflows + 1 qualifier + 1 outreach") instead of a mega-workflow.

## Build incrementally — two first, then refactor, then the rest

When the plan calls for many sourcing workflows (or any N near-identical workflows), **do not build all N upfront**. Build two first, ship them end-to-end, then extract what is actually shared — most often the *tail*: normalize → kg.upsert-rows (with userKey + status: "new" + mergeStrategy: "merge") → milestone.

Once the shared shape is clear:
1. Extract the common tail into a **child workflow** (terminal `return` step, `context.executionInputConfig.internal: true`) and have the two existing sourcing workflows call it via `agentled.call-workflow`.
2. Validate + run the two pilots end-to-end on the shared tail.
3. *Then* build the remaining sourcing workflows on top of that shared tail — they become small (just the source-specific search/scrape/extract head, then call the shared tail).

Why: two pilots surface the real shared shape; refactoring before scaling keeps workflows 3..N short and consistent. Don't pre-extract a child workflow before the second pilot exists.

## KG Status Lifecycle — multi-phase pipeline pattern

When a workflow acts on entities across phases (source → score → report → outreach), use KG row `status` as a DB-indexed state machine. Filtering by one status (e.g. `status: "new"`) is indexed — never scan the full list and filter in code.

**Status values are user-defined** — choose names that map to your pipeline phases (e.g. `new → scored → reported → email_sent → closed_*`, or `draft → review → approved → published`). Document the state machine in the workflow goal or as a KG text entry.

Key rules:
- Sourcing writes `status: "new"` via `kg.upsert-rows` with `mergeStrategy: "merge"` (preserves downstream-added fields across re-runs).
- Each phase reads only its input status tier and advances rows to the next.
- Mark the next status **before** side-effects (email, share, Slack). If delivery fails, the row stays in the new status — it won't be double-sent on retry.
- Every upserted row needs a `userKey` (URL, domain, LinkedIn URL, email) for O(1) cross-run dedup.
- Use `entryConditions.criteria[{ type: "loop_completion" }]` with `onCriteriaFail: "wait"` before cross-phase reads that depend on a loop finishing.

The `Loop Enrich & Filter` block above hints at the same fan-in mechanism: for post-loop convergence, use `entryConditions.criteria[{ type: "loop_completion" }]` with `onCriteriaFail: "wait"` — do not use `scope` as the runtime wait mechanism.
- **update_workflow** — Update an existing workflow.

## Recommended flow for agent-driven edits

For editing an existing workflow step-by-step, prefer the per-step tools — they catch errors incrementally and avoid the bulk-JSON vocabulary traps (`ai` / `integration` / `knowledge_graph_query`, silently-stripped root fields like `prompt` / `listKey` / `appId`):

- `update_step({ workflowId, stepId, updates })` — change one step (prompt, inputs, next, etc.). Safest and most common.
- `add_step({ workflowId, step, insertAfter? })` — append or insert a new step.
- `remove_step({ workflowId, stepId })` — delete a step and re-wire its neighbors.
- After a series of edits: `validate_workflow` → (if live) `promote_draft` / `discard_draft`.

The bulk `updates` param below is supported for **imports, templated rewrites, and programmatic round-trips** (export → edit JSON → re-import). Agents editing interactively should not use it for step changes — use `update_step` instead.

## Trigger type guidance

Prefer `schedule` (polling) for email intake, document processing, and any workflow where exact-millisecond latency is not required — it is idempotent, supports backfill, and needs no webhook infrastructure. Use `app_event` or `webhook` only when the user explicitly says "as soon as", "within X seconds", or "real-time". When in doubt, schedule wins.

## Draft routing (live workflows)

If the workflow is live, config edits (steps, context, name, etc.) are automatically routed to a draft snapshot instead of modifying the live pipeline. The response will include `editingDraft: true`. Use `get_draft` to view the draft, `promote_draft` to make it live, or `discard_draft` to throw away the changes. Non-live workflows are updated directly with an automatic pre-edit snapshot for rollback.

## Bulk updates param (imports / round-trips only)

⚠️ Avoid sending a full `steps` array for large workflows — use `update_step` instead.
Sending more than ~20 steps risks silent truncation at the MCP transport layer.
Full steps array replacement is only safe when doing a complete pipeline replacement from a known-good JSON source (import, template, export round-trip). For editing individual steps, always use `update_step`.

## `context` merge semantics (read before patching)

**Root level:** `updates.context` is **shallow-merged** with the stored workflow’s `context` (`{ ...existingContext, ...patchContext }`). Sibling keys at the root (`inputPages`, `outputPages`, `executionInputConfig`, etc.) do not clobber each other: **omitting a key preserves the stored value**; only keys present in the patch are overwritten. To clear a collection explicitly, send an empty value (e.g. `inputPages: []`). Silent deletion-by-omission no longer applies at the root — same spirit as `update_step`’s deep-merge for nested step fields.

**One level down:** Each **value** under `context` is still replaced **wholesale** when the patch includes that key. For example, `context: { executionInputConfig: { someKey: "x" } }` replaces the entire `executionInputConfig` object — any sibling fields under it (e.g. `defaults`, `fields`, `internal`) that are not in the payload are dropped. To partial-patch a nested object, **`get_workflow` first**, merge the current value with your changes client-side, then send the **full merged** object for that key in `update_workflow`.

**Surgical alternative (preferred for context/metadata):** `update_workflow_context` is the workflow-level analog of `update_step` — it accepts the same three explicit verbs (`updates` / `replace` / `unset`) on workflow-relative paths under `context.<anything>` (both page schemas like `context.inputPages` AND user-saved page values like `context.outreachProfile`) and `metadata`. Returns `diff` + `warnings`. Use it instead of bulk `update_workflow` for any context or metadata edit, including pre-filling configuration input pages programmatically (e.g. `updates: { context: { outreachProfile: { name: "Alberto", signature: "..." } } }`). To flip a single nested key like `executionInputConfig.internal`, fetch with `get_workflow` first, merge locally, then `updates: { context: { executionInputConfig: {...full merged...} } }, replace: ["context.executionInputConfig"]` — the same merge-order trap as `update_step` applies (deep-merge runs before replace[], so replace at the parent level).

## Internal-only workflows

Set `context.executionInputConfig.internal: true` to mark a workflow as a child / sub-workflow that runs only via `agentled.call-workflow`. The UI hides the Run button and replaces the manual run form with a banner; orchestrators still pass inputs via `executionInputData` (UI guard, not runtime restriction). Use for child workflows that end in a `return` step. To toggle on an existing workflow, fetch the current value with `get_workflow`, then call `update_workflow_context` with the explicit ops shape replacing at the parent level: `updates: { context: { executionInputConfig: {...merged...} } }, replace: ["context.executionInputConfig"]`.
- **add_step** — Add a new step to a workflow. **This is the recommended path for agent-authored workflows** — call `create_workflow({ name, goal })` first, then `add_step` one step at a time, then `validate_workflow` + `publish_workflow`.

Each call returns per-step validation errors immediately, so a bad step type / prompt template / missing required field is caught before the next step is built on top of it.

## KG-First — before writing prompt content into a step

Before writing an AI-step prompt that contains workspace-specific content (thesis, ICP criteria, scoring rubric, sector list, geo focus, brand voice, seed lists), check whether that content already lives in the workspace KG: call `list_memories` / `list_knowledge_lists` / `get_knowledge_text`. If it doesn't exist yet, seed it first (`store_memory` / `upsert_knowledge_text`) before adding this step. Then reference it at runtime in the prompt template via `{{steps.read-kg.content}}` rather than pasting the text inline.

**Boundary: strategy → KG; execution wiring → workflow context; workflow structure → workflow.**

## Required `step` fields (all types)

- `id`: stable string unique within the workflow.
- `type`: one of the closed list — `trigger`, `appAction`, `aiAction`, `aiActionWithTools`, `toolAction`, `code`, `knowledgeSync`, `return`, `milestone`, `share`, `wait`, `branch`, `parallel`, `loop`, `end_if`, `agentOrchestrator`. Any other string is silently stripped by the runtime.
- `name`: human-readable label.

Non-terminal steps also need `next: { stepId }` pointing to the next step. Terminal steps (`milestone`, `return`) omit `next`.

## Minimal shape by type

```json
// trigger (manual)
{ "id": "start", "type": "trigger", "name": "Manual Start", "pipelineStepStartConditions": { "trigger": { "type": "manual" } }, "next": { "stepId": "next-step" } }

// aiAction — LLM prompt → structured JSON
{ "id": "analyze", "type": "aiAction", "name": "Analyze",
  "pipelineStepPrompt": { "template": "Analyze {{input.company_url}}", "responseStructure": { "summary": "string", "score": "number (0-100)" } },
  "creditCost": 10, "next": { "stepId": "next-step" } }

// appAction — call an app/integration action
{ "id": "enrich", "type": "appAction", "name": "Enrich Company",
  "app": { "id": "agentled", "actionId": "agentled.get-linkedin-company-from-url", "source": "native" },
  "stepInputData": { "profileUrls": "{{input.company_url}}" },
  "next": { "stepId": "next-step" } }
// → call `get_app_actions({ appId })` FIRST to get valid actionId + input field names for this app.

// aiActionWithTools — LLM agent invoking runtime tools
{ "id": "research", "type": "aiActionWithTools", "name": "Research",
  "tools": [{ "type": "builtin", "name": "web_search", "builtinType": "web_search" }],
  "pipelineStepPrompt": { "template": "Research {{input.topic}}", "responseStructure": { "summary": "string" } },
  "creditCost": 10, "next": { "stepId": "next-step" } }
// → call `list_models` for valid builtinType values (web_search, workspace_memory, kg_search, …).

// knowledgeSync — persist prior step output to a KG list
{ "id": "save", "type": "knowledgeSync", "name": "Save to KG",
  "knowledgeSync": { "source": { "stepId": "analyze", "resultsPath": "items" }, "listKey": "scored_companies", "fieldMapping": { "name": "name", "score": "score" } },
  "next": { "stepId": "done" } }

// milestone — terminal step for top-level workflows
{ "id": "done", "type": "milestone", "name": "Done" }
```

## Variable references

- `{{input.fieldName}}` — input page field (defined in `context.executionInputConfig.fields` or `context.inputPages[].configuration.fields`).
- `{{steps.stepId.fieldName}}` — output of a prior step.
- `{{currentItem.field}}` — current item inside a `loopConfig` iteration.

Trigger step inputs are referenced as `{{input.X}}`, **not** `{{steps.trigger-id.X}}` — common agent mistake.

## Composable step blocks

When building multi-step workflows, apply these reusable patterns:
- **Search & Extract**: aiAction (generate queries) → appAction (search) → aiAction (extract). Never pass raw input to search APIs.
- **Enrich & Score**: appAction (fetch data) → aiAction (score). Always enrich before scoring.
- **Draft & Send**: aiAction email → approval with onApproval.action="schedule-email". Use HTML bodies for open/click tracking. Do not use Gmail/Outlook send appActions unless explicitly requested.
- **Report & Notify**: aiAction report with Config renderer → share step → aiAction notification email. Include `{{steps.<shareStepId>.shareUrl}}` in the email template and keep the body to an HTML overview + report link.
- **Scrape & Summarize**: appAction (scrape) → aiAction (summarize).
- **Loop Enrich & Filter**: loopConfig on first step only → appAction (enrich each) → aiAction (score/filter). Post-loop: aiAction to rank with `entryConditions.criteria[{ type: "loop_completion" }]` and `onCriteriaFail: "wait"`. Do not use `scope` as the runtime wait/fan-in mechanism; it only declares explicit container membership.
- **Multi-phase KG pipeline**: each phase reads its input status, processes rows, then advances them (kg.upsert-rows with initial status → kg.read-list by status → kg.update-rows to next status). Status values are user-defined per pipeline. Status is DB-indexed — always filter by a single equality value, never scan. Mark the next status BEFORE side-effects (email, share, Slack).
(Source of truth: COMPOSABLE_STEP_BLOCKS in workflowPatternExamples.ts)

## Positioning

Use `insertAfter` to place the step after an existing step ID. When `rewireNext` is true (default), the insertAfter step's `next` is updated to the new step, and the new step's `next` is set to what insertAfter previously pointed to — maintaining the chain. Validates step ID uniqueness. Respects draft snapshot routing for live workflows.
- **move_step** — Move a step to a new position in the workflow's steps array.

Provide exactly one target:
- `insertAfter`: place the step immediately after the given step ID.
- `position: "first"`: place the step at index 0. Use this to put a trigger
  back in first position after a remove + add cycle (the only way to recover
  trigger order via MCP — `add_step` always appends at the end).
- `position: "last"`: place the step at the end of the array.

Only the array order changes — NO next pointers or step config are modified.
This is a pure cosmetic reorder that fixes the "orchestrator-issue" validator warning
caused by steps being stored out of execution-chain order.

Use this when the validator reports:
  "Step X appears after Step Y but executes before it. Reorder the steps array..."

Works for both live workflows (via draft snapshot) and draft workflows.
- **remove_step** — Remove a step from a workflow with optional next-pointer rewiring.

When rewireNext is true (default): steps that pointed to the removed step are rewired to
the removed step's next target. Entry condition criteria referencing the removed step are
also cleaned up. Respects draft snapshot routing for live workflows.
- **update_step** — Update a single step in a workflow by step ID. **Preferred path for any single-step edit on an existing workflow** — only the fields in `updates` / `replace` / `unset` are touched, every other step and field is left as-is.

Use this instead of `update_workflow` for any one-step change (prompt, inputs, entry conditions, switching shape, swapping tools). `update_workflow` with a full `steps` array is for imports/round-trips only.

## KG-First — when editing a prompt template

If this edit introduces or changes workspace-specific content in a prompt template (thesis, ICP, rubric, sector list, geo focus, brand voice), check the KG first: call `list_memories` / `list_knowledge_lists` / `get_knowledge_text`. Seed the content there if it isn't already present, then reference it at runtime (`{{steps.read-kg.content}}`) instead of pasting text inline.

**Boundary: strategy → KG; execution wiring → workflow context; workflow structure → workflow.**

## Merge semantics

`update_step` accepts three independent operations on the same call. At least one must be non-empty.

- **`updates`** — partial step patch. Top-level fields are replaced; nested objects (`pipelineStepPrompt`, `stepInputData`, `entryConditions`, `renderer`, etc.) are deep-merged ONE LEVEL deep — keys nested two levels deep are overwritten as a unit, not merged. Arrays are replaced wholesale.
- **`replace: string[]`** — dot-paths (e.g. `"stepInputData.fieldUpdates"`) whose values from `updates` are assigned WHOLESALE onto the step, skipping the deep-merge. Use for dictionary-shaped fields where keys are user data.
- **`unset: string[]`** — dot-paths to DELETE. Each must exist on the original step.

## When to use which

| Situation | Verb | Example |
|---|---|---|
| Change one config key, keep siblings | `updates` | `updates: { pipelineStepPrompt: { template: "new..." } }` keeps `responseStructure` |
| Add a stepInputData entry | `updates` | `updates: { stepInputData: { profileUrls: "{{input.url}}" } }` |
| Replace a dictionary wholesale (keys = user data) | `replace` | `updates: { stepInputData: { fieldUpdates: {...} } }, replace: ["stepInputData.fieldUpdates"]` |
| Replace `responseStructure` / `knowledgeSync.fieldMapping` | `replace` | `replace: ["pipelineStepPrompt.responseStructure"]` |
| Remove a step input | `unset` | `unset: ["stepInputData.oldKey"]` |
| Swap full arrays (tools, integrations) | `updates` | `updates: { tools: [...] }` (arrays already replaced wholesale) |

**The trap.** Default deep-merge is one level deep — patching `stepInputData.fieldUpdates` with a partial dict silently wipes the others. Either send the FULL dict + `replace: ["stepInputData.fieldUpdates"]`, or call `get_step` first, edit locally, send back via `replace`.

## Read-before-write for dictionary fields

For dictionary fields where keys are user data (`stepInputData.fieldUpdates`, `responseStructure`, `fieldMapping`): `get_step` (~1KB), modify locally, send full object back under `replace[]`.

## Diff + warnings

Response includes `diff: { addedPaths, changedPaths, removedPaths }` and `warnings[]`. ≥6 fields removed without explicit `unset` triggers a warning — usually a "you wiped a dictionary" signal.

## Shape conversions

Fetch the canonical example before changing shape:
- email: `get_step_schema({ stepType: "aiAction", shape: "email" })`
- report: `get_step_schema({ stepType: "aiAction", shape: "report" })`
Send under `updates`, `replace[]` for dictionary children, `unset[]` for stale type-specific fields.

## Won't do

- **Cannot change `step.id`** — root id is immutable; API returns 400. Nested `*.id` is fine.
- **Does not enforce `step.type` immutability.** Stale type-specific fields (`pipelineStepPrompt`, `app`, `tools`) persist unless `unset`. For clean conversions, prefer `remove_step` + `add_step`.
- **Does not validate the merged result against shape rules** — call `validate_workflow` after edits.

## Draft routing (live workflows)

Edits are routed to a draft snapshot (`editingDraft: true` in response). Inspect via `get_draft`, ship via `promote_draft`, discard via `discard_draft`.

When a draft exists, the response carries a `draft` summary: `{ exists, draftCreatedAt, liveUpdatedAt, stale, modifiedStepIds, modifiedFields }`. **If `draft.stale === true`, the live workflow advanced past the draft** — promoting will land older values for fields you didn't touch. A staleness warning is pushed into `warnings[]`. Recovery: `discard_draft` + retry, or inspect via `get_draft` first.
- **get_step** — Read a single step from a workflow by step ID. Cheap alternative to `get_workflow` (typically ~1KB vs 50-200KB for a full workflow).

This returns the configured step definition only. To debug the actual prompt used in a specific execution, use `list_timelines` then `get_timeline` for that step invocation and inspect `metadata.computedPrompt`.

**Use this before editing dictionary-shaped fields** (`stepInputData.fieldUpdates`, `responseStructure`, `knowledgeSync.fieldMapping`, `agent.workers`) so you can fetch the current value, modify it locally, and send the full new object back via `update_step` with `replace: ["<path>"]`. Avoids the "patched one key, silently wiped the others" trap.

## Source resolution

- `source: "auto"` (default) — returns the draft step if a draft exists, else live. Matches `update_step`'s routing for live workflows.
- `source: "live"` — always reads from the live pipeline, ignoring any draft.
- `source: "draft"` — returns the draft step or 404 if no draft exists. Never creates a draft.

The response includes the resolved `source: "live" | "draft"` so you know which one you got.

## Response shape

```
{
  workflowId, stepId, source,
  step: <PipelineStep>,
  contextRefs: {
    inputPagesUsed: ["company_url", ...],   // {{input.X}} references found in the step
    stepRefs: ["fetch", "analyze", ...]      // {{steps.X.*}} references found in the step
  },
  draft?: {                                   // present when a draft snapshot exists
    exists: true,
    draftCreatedAt, liveUpdatedAt,
    stale: boolean,                           // live advanced past draft.createdAt
    modifiedStepIds: [...],                   // step IDs differing between draft and live
    modifiedFields: ["steps", "context", ...] // top-level keys differing
  }
}
```

`contextRefs` tells you which upstream fields the step depends on — useful when you're about to break a downstream chain by editing inputs.

`draft.stale === true` means the live workflow has been touched since the draft was created. Promoting will land older values for fields the agent didn't touch in this draft. Recovery: `discard_draft` and re-apply, or `get_draft` to inspect what's pending.
- **update_workflow_context** — Surgical edit of `workflow.context` (and `workflow.metadata`) — the workflow-level analog of `update_step`. Does NOT reach into `steps` (use `update_step` for that).

## Calling shape (preferred): three explicit verbs

`{ updates, replace, unset }` — same merge model as `update_step`, but on **workflow-relative paths**. At least one must be non-empty.

- **`updates`** — partial workflow patch. Top-level keys are limited to `context` and `metadata`. Each is shallow-merged (`{ ...stored, ...patch }`) so omitting a sibling preserves it. Direct sub-objects under `context` (e.g. `executionInputConfig`) are still replaced wholesale by default — use `replace[]` for explicit deep replacement, `unset[]` for deletion.
- **`replace: string[]`** — workflow-relative dot-paths whose values from `updates` are assigned WHOLESALE, skipping the deep-merge. Path examples: `"context.executionInputConfig.fields"`, `"context.inputPages"`, `"metadata.tags"`. The path's value MUST be present in `updates`.
- **`unset: string[]`** — workflow-relative dot-paths to delete. Each must currently exist on the workflow.

## What lives under `context`

`workflow.context` holds two things, side by side as siblings:

1. **Page schemas** — `context.inputPages`, `context.outputPages`, `context.executionInputConfig`. Field definitions, defaults, shortDescriptionFields, etc.
2. **User-saved page values** — `context.<contextKey>`, where `<contextKey>` mirrors a page's `contextKey`. e.g. `context.outreachProfile`, `context.cadence`, `context.introductionWorkflow`. These are the values a user persists when clicking "Save" on a configuration input page.

### Page entry shapes (read these BEFORE writing to `context.outputPages` or `context.inputPages`)

The workflow detail UI crashes on load if a page entry is missing required fields, and `validate_workflow` now rejects bad shapes with `MISSING_OUTPUT_PAGE_FIELD` / `INVALID_OUTPUT_STEPS_TYPE`.

- **`context.outputPages`** — `PipelineOutputPage[]`. Authoritative example: `get_step_schema({ stepType: "outputPage", shape: "standard" })`.
  - Required: `id` (string, unique), `title` (string), `pathname` (string, URL slug), `outputSteps` (string[] of step IDs that exist in `workflow.steps`).
  - Optional: `description`, `iconName`, `displayConfig.showExecutionsList` (boolean), `displayConfig.executionNameTemplate`, `displayConfig.filterStatuses`, `displayConfig.defaultFilterStatus`, `displayConfig.sortField`, `displayConfig.sortDirection`.
  - Product rule: choose only the 1-3 user-facing result surfaces that match the workflow, e.g. LinkedIn publish, X/Twitter publish, scheduled email/outreach, report, or canonical results list. Group related step outputs on one page. Do not create output pages for approval placeholders, status markers, or internal implementation details.
- **`context.inputPages`** — `PipelineInputPage[]`. Authoritative example: `get_step_schema({ stepType: "inputPage", shape: "standard" })`.
  - Required: `title`, `pathname`, `configuration.contextKey`, `configuration.fields[]`. Saved values land at `context.<contextKey>` (sibling).

Both shapes are dictionaries the workflow author owns, both are read at runtime via `{{context.<key>.<field>}}`, and both are edited through this tool with the same three-verb model. To pre-fill a config page programmatically:

```jsonc
update_workflow_context({
  workflowId,
  updates: { context: { outreachProfile: { name: "Alberto", signature: "<p>Best, Alberto</p>" } } }
})
```

Sibling context keys are preserved by the one-level deep-merge. Live workflows route to draft.

## Allowed path scope (both replace and unset)

Paths must begin with one of:

- `context.<anything>` — page schemas (`context.inputPages`, …) or user-saved page values (`context.outreachProfile`, `context.cadence`, …).
- `metadata` (exact, or any `metadata.*` sub-path).

Anything else (e.g. `steps.*`, `name`, `goal`, `status`) is rejected with `PATH_OUT_OF_SCOPE`. Use `update_step` for step-level edits and `update_workflow` for top-level scalars (`name`, `goal`, `description`, `style`).

## Diff and warnings

The ops shape returns `diff: { addedPaths, changedPaths, removedPaths }` and `warnings[]`. If ≥6 fields were silently removed without an explicit `unset`, a warning fires — that's usually a "you wiped a dictionary" signal. Read it.

## Errors (400)

| Code | When |
|---|---|
| `EMPTY_PAYLOAD` | All three of `updates`/`replace`/`unset` are missing or empty. |
| `INVALID_PATH` | Dot-path syntax violation (empty segment, leading/trailing dot, prototype-pollution segment). |
| `PATH_OUT_OF_SCOPE` | Path is not under `context.<anything>` or `metadata` (e.g. `steps.*`, `name`). |
| `REPLACE_VALUE_MISSING` | A `replace[]` path has no corresponding value in `updates`. |
| `UNSET_PATH_NOT_FOUND` | An `unset[]` path doesn't exist on the workflow. |

## Draft routing (live workflows)

Context edits are routed to a draft snapshot (`editingDraft: true`). Metadata is NOT part of the snapshot config — metadata edits write directly to the Pipeline row, **immediately and on the live workflow**.

⚠ **Mixed metadata + context in one call**: metadata is applied immediately while context goes to the pending draft. `discard_draft` reverts the pending context changes but **does NOT revert metadata**. If you need a single atomic checkpoint covering metadata too, call `create_snapshot` first, or split the call.

## Compatibility body shape

A legacy `{ contextKey, value }` shape is still accepted for one-shot wholesale replacement of a single root context key (`inputPages` / `outputPages` / `executionInputConfig` only — saved-values keys are not reachable through this shape). It does not return `diff` / `warnings` and cannot edit metadata. Prefer the three-verb shape above for new code.

## Recipes

```jsonc
// Add a single field to executionInputConfig.fields without rebuilding the array.
// Step 1: get_workflow → read context.executionInputConfig.fields
// Step 2:
{
  updates: { context: { executionInputConfig: {...full new value with the appended field...} } },
  replace: ["context.executionInputConfig"]
}

// Replace inputPages wholesale.
{
  updates: { context: { inputPages: [...new pages...] } },
  replace: ["context.inputPages"]
}

// Pre-fill a config page (user-saved values land at context.<contextKey>).
// Uses one-level deep-merge under updates.context — sibling saved-values
// dictionaries are preserved.
{
  updates: { context: { outreachProfile: { name: "Alberto", signature: "<p>Best, Alberto</p>" } } }
}

// Wholesale-replace a single saved-values dictionary.
{
  updates: { context: { cadence: { firstNudgeDays: 3, secondNudgeDays: 7 } } },
  replace: ["context.cadence"]
}

// Delete a saved-values dictionary.
{ unset: ["context.introductionWorkflow"] }

// Add a metadata tag.
{ updates: { metadata: { tags: ["beta"] } } }

// Save an operator-facing executive summary for a workflow or workflow group.
// Use this when a user asks to save a summary for the workflow, cluster, group,
// or home card. Store it in metadata, not as KG text, unless the user explicitly
// asks for a reusable knowledge note. For groups, write once to the owner
// pipeline: prefer workflowGraph.role === "orchestrator"; otherwise use the
// lowest workflowGraph.order pipeline. Keep body short, include concrete metrics
// and the reporting period when available, and set author to the active
// workspace agent name only (for example "AngelHive Assistant"), not the
// external coding/tool agent, and without a leading "by".
{
  updates: {
    metadata: {
      executiveSummary: {
        body: "Startup Outreach sent 46 founder emails for the reporting period, with 28 opens and 9 clicks: a 60.9% open rate, 19.6% click rate, and 32.1% click-to-open rate.",
        bullets: ["Clicks: 6 UTM Pitch Night, 2 plain Pitch Night, 1 calendar."],
        generatedAt: "2026-06-03T00:00:00.000Z",
        author: "AngelHive Assistant"
      }
    }
  }
}

// Delete an obsolete metadata key.
{ unset: ["metadata.legacyFlag"] }

// Toggle executionInputConfig.internal: fetch first (get_workflow), merge locally, replace at the
// PARENT level. The one-level deep-merge under updates.context wipes nested-object siblings BEFORE
// replace[] runs (same merge-order trap as update_step) — so replace at "context.executionInputConfig"
// (not ".internal") and pass the full object in updates.
{
  updates: { context: { executionInputConfig: {...full merged executionInputConfig with internal: true...} } },
  replace: ["context.executionInputConfig"]
}
```

Response: `{ editingDraft?, context, metadata?, diff?, warnings?, validation }`.
- **delete_workflow** — Permanently delete a workflow by ID. This cannot be undone.

**Two-phase flow (required):**
1. Call with just `workflowId` to get a deletion preview. Response:
   `{ pendingDelete: true, id, name, status, relatedEntities: { executions, leads, timelines }, relatedEntitiesTruncated, confirmToken, expiresIn: 300, expiresAt, message }`
   Nothing is deleted in phase 1. Token is valid for 5 minutes.
2. Call again with both `workflowId` AND `confirmToken` to perform the cascade delete.
   Response: `{ deleted: true, id }`.

**Server error responses (always structured JSON):**
- `404 { error: "Workflow not found" }` — wrong ID or wrong workspace.
- `403 { error: "Invalid or expired confirmation token. Request a new deletion preview first." }` — token typo, expired, or workspace/workflow mismatch.
- `500 { error: "Failed to delete workflow" }` — cascade failure (rare; safe to retry phase 2 with a fresh token).

**If you see a bare "Denied." with no detail, that is your MCP host (Claude Desktop / Cursor / etc.)
denying the destructive call at the approval-policy layer, not this server.** Agentled never returns
"Denied." — every error from this tool includes a JSON body. Check the host's tool-permission settings
and re-approve, then retry.
- **validate_workflow** — Validate a workflow's pipeline definition. Returns structured errors per step.
Use this after creating or updating a workflow to check for:
- Missing step connections (broken next.stepId references)
- Missing required fields (app action without inputs, AI step without prompt)
- Unreachable steps (not connected to the trigger chain)
- Invalid app/action IDs (not in the app registry)
- Missing trigger or milestone steps
- List field misconfigurations (missing itemFields, defaultValue format mismatches)
- Config page field validation (missing name/type on input page fields)

Each error/warning may include a "suggestedFix" with a concrete remediation.

You can also pass a pipeline object to validate a draft before saving.
Returns: { valid: boolean, errors: [...], warnings: [...], stepCount: number }
- **list_snapshots** — List config snapshots for a workflow, paginated across all pages (cap 500).
Snapshots are automatically captured before every external API update, allowing you to
restore a previous configuration. Includes both saved versions and unpublished drafts.
Use filter to narrow results: "all" (default, includes drafts), "saved" (no drafts), or
"draft" (drafts only). Returns snapshot ID, timestamp, source, and which fields changed.
- **get_snapshot_content** — Read the full content of a config snapshot WITHOUT restoring it. Returns the captured
`steps`, `context`, `name`, `description`, `goal`, `style`, and `analyticsConfig`
under a `config` field, plus snapshot metadata (createdAt, source, label, updatedFields).
Use this to inspect or compare an old snapshot against the live workflow without the
destructive create + restore + restore dance. No quota cost.
- **restore_snapshot** — Restore a workflow to a previous config snapshot. Use list_snapshots first to find the
snapshot ID. This will revert the workflow's steps, context, name, description, goal, and
style to the state captured in the snapshot.
- **create_snapshot** — Create a manual config snapshot of a workflow's current state. Use this to save a
checkpoint before making changes, so you can restore later if needed.
Enforces plan-based limits (Pro=2, Teams=10, Custom=50). Returns an error with limit
info if the snapshot limit is reached — delete old snapshots first to free up space.
- **delete_snapshot** — Delete a specific config snapshot. Use list_snapshots to find snapshot IDs.
Useful for freeing up space when the snapshot limit is reached.
- **get_draft** — Get the draft snapshot for a live workflow. When you update a live workflow, changes
go to a draft instead of modifying the live pipeline. Use this to inspect the current draft
state. Returns hasDraft: true/false and the draft config if it exists.
- **promote_draft** — Promote the draft snapshot to live. This overwrites the live pipeline config with
the draft contents, then deletes the draft snapshot. A pre-promote snapshot is saved
automatically so the previous live config can be restored if needed.
- **discard_draft** — Discard the draft snapshot for a live workflow. The live pipeline config stays
unchanged. Use this to abandon draft changes and go back to the current live version.
- **publish_workflow** — Change the status of a workflow (publish, pause, or archive).
Valid transitions: created/draft -> live, live -> paused, paused -> live, any -> archived.
Use "live" to publish a draft workflow so it can be executed.
- **export_workflow** — Export a workflow as portable JSON for cross-environment transfer.
Returns a self-contained WorkflowExport object with all steps, context, metadata, and pages.
Workspace-specific identifiers (workspaceId, agentIds) are stripped so the export can be imported into any workspace.

Use this together with import_workflow to move workflows between environments (e.g. sandbox → prod).
- **import_workflow** — Import a workflow from an export JSON into this workspace.
Accepts the full WorkflowExport object (from export_workflow) and creates a new workflow with fresh IDs.
Associated pages are recreated. Import provenance is recorded in the workflow metadata.

Use this together with export_workflow to move workflows between environments.
Tip: register separate MCP servers for sandbox and prod, export from one, import into the other.
- **preview_n8n_import** — Preview a deterministic n8n import from JSON. Returns:
- normalized import hash and IR
- mapped step graph
- unsupported nodes + remediation
- warnings/risks
- draft workflow build contract and compiler readiness summary

This is a read-only preview and does not create any workflow.
- **import_n8n_workflow** — Create a new Agentled workflow from an n8n JSON import.

Behavior:
- runs deterministic import preview
- creates workflow in preflight draft mode
- stores imported contract for review/approval
- does NOT auto-apply scaffold
- **get_step_schema** — Get allowed fields and minimal JSON examples for pipeline steps.

Unknown fields are automatically stripped on save — only fields listed here are persisted.

## Params

- `stepType` (optional): filter to one of `trigger`, `aiAction`, `aiActionWithTools`, `appAction`, `agentOrchestrator`, `code`, `knowledgeSync`, `share`, `return`, `milestone`, etc. Omit for the full schema.
- `shape` (optional): minimal JSON example for a specific shape. Requires `stepType`. Examples:
  - `aiAction` shapes: `standard`, `report` (with Config renderer), `email` (composed email with approval).
  - `aiActionWithTools` shapes: `standard`, `agentic-search` (web_search + workspace_memory).
  - `agentOrchestrator` shapes: `supervisor` (Agent Team preset).
  - `share` shapes: `public` (public URL for a report step).
  - `knowledgeSync` shapes: `standard` (deterministic KG field mapping).
  - `outputPage` shapes: `standard` — workflow-level output page schema for `context.outputPages` (NOT a pipeline step; edit via `update_workflow_context`).
  - `inputPage` shapes: `standard` — workflow-level configuration input page schema for `context.inputPages`.

## Response shape

```
{
  "description": "...",               // schema description
  "fieldCount": <number>,             // total fields (scoped to stepType when provided)
  "groups": [<field groups>],         // schema groups (scoped to stepType when provided)
  "shapes": [<shape examples>]        // only when stepType is set
}
```

Top-level keys are backward-compatible with pre-v0.11 callers that read `description`/`groups` directly.

## When to call

- **Before adding a report step**: `get_step_schema({ stepType: "aiAction", shape: "report" })`.
- **Before adding a composed-email step**: `get_step_schema({ stepType: "aiAction", shape: "email" })` — remember to also add an `outreachProfile` input page.
- **Before adding an Agent Team**: `get_step_schema({ stepType: "agentOrchestrator", shape: "supervisor" })`.
- **Before adding an aiActionWithTools step**: `get_step_schema({ stepType: "aiActionWithTools" })`, then `agentled tools builtins` for the closed `builtinType` list.
- **Before adding a code step**: `get_step_schema({ stepType: "code", shape: "standard" })`. Note: only JavaScript is supported — Python will fail at runtime.
- **Before writing to `context.outputPages`**: `get_step_schema({ stepType: "outputPage", shape: "standard" })`. Required fields: `id`, `title`, `pathname`, `outputSteps[]` — missing any crashes the workflow detail UI on load.
- **Output page selection rule**: keep workflow outputs compact. Select only the 1-3 user-facing result surfaces that match the workflow, such as LinkedIn publishing, X/Twitter publishing, scheduled email/outreach, a report, or a canonical results list. Do not create pages for approval placeholders, "mark sent/published" status steps, or internal plumbing.
- **Before writing to `context.inputPages`**: `get_step_schema({ stepType: "inputPage", shape: "standard" })`. Saved values land at sibling `context.<contextKey>`.

## Trigger type guidance

Prefer `schedule` (polling) for email intake, document processing, and any workflow where sub-minute latency is not required. Use `app_event` or `webhook` only when the user explicitly requires real-time delivery (e.g. "as soon as", "within 30 seconds"). When in doubt, schedule wins — it is idempotent, supports backfill, and needs no event infrastructure.
- **start_workflow** — Start a workflow execution. Optionally provide input data that maps to the workflow's input page fields.
For example, if the workflow expects "company_url", pass: { input: { company_url: "https://..." } }

Returns executionInputId for the submitted input/run record. It may also return executionId when the async PipelineExecution row is already available. Use only executionId with get_execution/list_timelines/get_timeline. If executionId is absent, call list_executions and match pipelineExecutionInputId to the returned executionInputId; the matching row's id is the executionId.

Mock control: by default, steps that have mock data configured (`step.mock.enabledByDefault`) will return that mock data and consume zero credits. Pass `useMocks: false` to force a real run that ignores mocks for every step. Pass `useMocks: true` (or omit) to keep the workflow's default mock behavior.
- **list_executions** — List recent executions for a workflow. Returns execution id, pipelineExecutionInputId, status, timestamps. Use pipelineExecutionInputId to match an executionInputId returned by start_workflow when executionId was not available yet.
- **get_execution** — Get full execution details including results from each completed step.
The executionContent field maps stepId -> step output data.
Use this to inspect what a workflow produced, debug failures, or check intermediate results.

executionId must be the PipelineExecution id, not executionInputId. If start_workflow returned only executionInputId, first call list_executions and match pipelineExecutionInputId to find the execution id.

When sharing a step-specific run with a human, use /runs?runId=<executionId>&step=<workflowStepId>. Do not send a run-only URL and ask the human to open an approval/output/failure/current step manually.
- **list_timelines** — List timelines (step execution records) for a specific execution. Each timeline represents a step that ran, with its status, output, and metadata. Use this to inspect individual step results, debug failures, or see the execution flow. When sharing a specific timeline/approval/output/failure with a human, use /runs?runId=<executionId>&step=<workflowStepId>; the step value is the workflow step id from these records, not the timeline id.

To debug the actual prompt used for a step in an execution, find that step's timeline here, then call get_timeline and inspect metadata.computedPrompt. get_step only shows the configured prompt template, not the resolved execution prompt.
- **get_timeline** — Get a single timeline (step execution record) by ID. Returns the full timeline including eventContent (step output), status, metadata, and context. Use this to inspect a specific step's result in detail.

To debug the actual prompt used for this step invocation, inspect metadata.computedPrompt. get_step only shows the configured prompt template, not the resolved execution prompt.
- **get_workflow_analytics** — Get aggregated workflow analytics from persisted snapshots. Use type="business" to retrieve business metrics configured in analyticsConfig, including trackingEvent metrics such as opened/clicked when configured. Use period="lifetime" for all-time totals or period="daily" for a time series.
- **list_tracking_events** — List raw tracking events for a workflow, such as email sent/opened/clicked events. Supports filtering by eventType, executionId, timelineId, channel, and createdAt range. Set aggregate=true to include simple counts by event type, channel, and URL for the returned page.
- **stop_execution** — Stop an active workflow execution. Works on executions with status "running", "pending", or "started"; requires a human-readable reason persisted in execution metadata.
- **delete_execution** — Delete a stopped workflow execution and its related timeline/chat records.

EXCEPTION-ONLY admin maintenance tool. Requires an API key with admin:patch scope.

Required operator flow:
  1. Call stop_execution with a human-readable reason.
  2. Wait until get_execution shows status "stopped".
  3. Call delete_execution with reason and confirmExecutionId exactly matching executionId.

The API rejects non-stopped executions and mismatched confirmation. This is destructive and should only be used for stopped test runs or incident cleanup.
- **read_step_output** — Read the full output from a previous workflow step.

Use this tool when a step output was deferred as a tool reference in a prompt — you will see a block like:

  [Tool Reference: stepName]
  Size: 24.3KB | Fields: name, emails, organizations, ...
  To read full data: call read_step_output(executionId="...", stepId="...")

Pass the executionId and stepId shown in that block. Optionally narrow the response with 'field' (dot-notation path) or 'select' (list of top-level fields to include).
- **rerun** — Rerun or retry any step in a workflow execution. Pass the timelineId — the backend derives everything else (workflowId, executionId, stepId) automatically from the timeline record.

Works for both retrying failed steps and rerunning any step regardless of status.
Bypasses cache by default (forceWithoutCache: true).

To find the timelineId: use list_timelines to browse step execution records for an execution, or get_execution which includes recent timeline IDs.
- **retry_execution** — [Deprecated — use rerun instead] Retry a failed step in a workflow execution. If no timelineId is provided, the most recent failed timeline is automatically detected and retried. This re-runs the failed step and continues the workflow from that point.
- **rerun_step** — [Deprecated — use rerun instead] Rerun a specific step in a workflow execution, regardless of step status (pending, completed, failed, skipped).
Works on any step and uses the current live pipeline config.
Bypasses cache by default (forceWithoutCache: true).
If no timelineId is provided, the most recent timeline for that step is automatically detected and rerun.
- **patch_timeline_fields** — Surgically edit a timeline's eventSummary, pending approval eventContent, or metadata fields without rerunning the upstream step. Terminal eventSummary-only relabels do not require confirmTimelineId; other terminal repairs require exact-ID confirmation.

EXCEPTION-ONLY tool. Use cases:
  - Fix a malformed email.to / subject / body in a pending email-draft step (no need to re-run the LLM)
  - Relabel a terminal timeline's eventSummary so the timeline row matches confirmed outcome, similar to metadata.executionName relabels on executions
  - Update metadata.pendingReasonTag for UI annotation
  - Recover a failed timeline back to pending (status transition: failed → pending)
  - Repair corrupted eventContent on a completed/approved/rejected terminal timeline after an incident, only when rerun/retry would duplicate side effects or lose canonical output

DO NOT use for day-to-day data fixes — most edits should happen by re-running the step or updating the workflow definition. This tool exists for incident response, not regular workflow operation.

Required:
  - API key with admin:patch scope (Stage 2 — without it returns 403 FORBIDDEN_SCOPE)
  - reason: non-empty string (≤500 chars), persisted in the audit row
  - expectedUpdatedAt: timeline.updatedAt from a fresh read — guards against lost-update races
  - confirmTimelineId: required for terminal records except eventSummary-only relabels; must be the exact timeline ID being patched

Allowed paths (any non-terminal status):
  - eventSummary
  - metadata.pendingReasonTag

Allowed paths (status === 'pending'):
  - eventSummary
  - eventContent  (wholesale replace; reserved keys _* rejected)
  - eventContent.email.subject | body | bodyType | to | cc | bcc
  - eventContent.<any>  (any AI-output field, except _*-prefixed reserved keys)
  - metadata.pendingReasonTag
  - status  (pending → completed)

Allowed paths (failed status):
  - status  (failed → pending)

Allowed paths (terminal without confirmTimelineId):
  - eventSummary only

Allowed paths (terminal + confirmTimelineId exact timeline id):
  - eventSummary
  - eventContent and eventContent.<any>, except reserved keys and provider send-result fields
  - eventContent.email.subject | body | bodyType | to | cc | bcc
  - metadata.pendingReasonTag

Forbidden:
  - Any path containing an underscore-prefixed segment (_timelineId, _metadata, _pointer, _continuation, etc. — runtime-internal markers)
  - Terminal timeline writes except eventSummary-only relabels, unless confirmTimelineId exactly matches the target timeline id
  - Identity fields, provider send results (sendResult/messageId/threadId/sentAt/postId/postUrl/commentId/mediaId/permalink), audit fields

Returns: { patched, dryRun, auditId, diff: [{path, before, after}], record }
On error: { error, code, path? }  — codes: FORBIDDEN_SCOPE | FORBIDDEN_PATH | FORBIDDEN_TRANSITION | INVALID_VALUE | NOT_FOUND | CONCURRENCY_CONFLICT | STATUS_MISMATCH | PRECONDITION_FAILED
- **patch_execution_fields** — Surgically edit a PipelineExecution's metadata, currentStepId, or status without re-running the workflow.

EXCEPTION-ONLY tool. Primary use case: relabeling a stuck, completed, or test run's executionName so it's distinguishable in the executions list, without spending credits on a rerun. Other use cases:
  - Update metadata.debugNote during incident investigation
  - Update metadata.pendingReasonTag for UI annotation
  - Advance currentStepId for stuck-state recovery (only when status is waiting or failed)
  - Force status transitions: waiting/failed/credits_missing → running, or started → stopped/canceled

DO NOT use for routine work. If you find yourself reaching for this tool repeatedly, the underlying workflow is misconfigured and the right fix is to update the workflow definition or the execution input.

Required:
  - API key with admin:patch scope (Stage 2)
  - reason, expectedUpdatedAt — same as patch_timeline_fields

Allowed paths:
  - metadata.debugNote
  - metadata.pendingReasonTag
  - metadata.executionName  (relabel run; orchestrator may recompute via executionNameTemplate)
  - currentStepId  (only when status is waiting or failed)
  - status  (only waiting → running, failed → running, credits_missing → running, started → stopped/canceled)

Terminal execution exception:
  - completed/canceled/approved/stopped executions may patch only metadata.executionName
  - terminal writes to metadata.debugNote, metadata.pendingReasonTag, currentStepId, status, or mixed batches are rejected

Forbidden:
  - Wholesale metadata replacement (must use sub-paths)
  - Analytics totals (totalCreditsUsed, creditsUsed, analyticsExtracted, etc. — anything not in the allowlist)
  - Identity fields, executionContent, completedAt, terminal-status writes outside the metadata.executionName exception

Audit: each patch appends an entry to metadata.adminPatchLog with { actor, apiKeyId, reason, diffs, timestamp }. This is the §8.1 short-term storage location — sufficient for the exception-only use case.

Returns: { patched, dryRun, auditId, diff, record }
- **list_apps** — List available apps/integrations in Agentled. Returns app names, descriptions, and action summaries.
Use this to discover what integrations are available before building a workflow.
Common apps: agentled (LinkedIn enrichment, email finder), hunter (email), web-scraping, affinity-crm, specter, http-request.

Pass `grep` to filter by keyword — much cheaper than fetching the full catalog when you only want to check a single platform (e.g. `grep: "producthunt"` to answer "is there a ProductHunt app?"). The keyword is matched (case-insensitive) against app id, name, description, and action ids/labels. Always run this before designing any "find / fetch / discover / source" step — falling back to aiActionWithTools + web_search without checking is how agents spend 25 credits per page when a 1-credit native call exists.
- **list_connections** — List all connected integrations for the workspace. Returns a unified list of connections from all sources:
- OAuth connections (via Composio)
- API key connections (e.g., Hunter, Affinity CRM) with masked key preview
- Native OAuth (social accounts)
Each connection shows appId, status, source, and for API keys a masked keyPreview (e.g., "c5a1...5532").
Use this to check which apps are configured before running workflows that depend on them.
- **get_app_actions** — Get detailed action schemas for a specific app. Returns input parameters, output fields, and credit costs.
Use this to understand exactly what inputs an action needs when building workflow steps.
- **test_app_action** — Test an app action in isolation without creating a workflow or execution.
Pass the appId and actionId (from list_apps / get_app_actions) plus input data to run the action directly and see results immediately.
Useful for verifying inputs before wiring a step into a workflow.
Example: test_app_action("web-scraping", "scrape", { url: "https://example.com" })
- **test_ai_action** — Test an AI prompt in isolation without creating a workflow or execution.
Pass a prompt template with {{variable}} syntax and variable values to run the AI and see the response.
Useful for tuning prompts and response structures before adding an AI step to a workflow.
Example: test_ai_action("Analyze this company: {{company}}", { company: "Stripe" }, { score: "number 0-100", summary: "string" })
- **test_code_action** — Test a code step in isolation without creating a workflow or execution.
Pass JavaScript code with optional {{variable}} template syntax and variable values to execute in the same sandboxed vm context as the production orchestrator.
Useful for verifying code logic and template variable resolution before adding a code step to a workflow.
No credits consumed.
Example: test_code_action("javascript", "const items = {{items}};\nreturn items.map(p => ({ name: p.fullName }));", { items: [{ fullName: "John" }, { fullName: "Jane" }] })
- **get_workspace** — Get workspace company info, current authenticated user, team visibility, and knowledge schema overview.
Returns currentUser for the API key owner, company details, active team members when available, pending team invitations when available, and a summary of all knowledge lists with their field definitions and row counts.
Use this as a first call to understand what data the workspace has.
- **get_workspace_company_profile** — Get the workspace company profile and company knowledge text.
Returns the company record used for workspace setup plus markdown from knowledge keys company.profile and company.products.
Use this when you need the editable company profile rather than the broader workspace context.
- **update_workspace_company_profile** — Update top-level workspace company profile fields.
Use this for company identity and summary information such as name, industry, size, logo, website URLs, and company description.
Products and services live in the company.products knowledge text, not in this structured profile.
- **update_workspace_executive_summary** — Write the workspace-wide executive summary shown on the Workspace Assistant card.
Uses the same summary signature as cluster executive summaries: body, optional bullets, and optional author.
The API writes only Workspace.metadata.executiveSummary and preserves other workspace metadata keys.
- **list_pinned_outputs** — List output pages currently pinned to the workspace home/sidebar.
Pinned outputs are workspace-level shortcuts to workflow output pages, stored on Workspace.metadata.pinnedOutputs.
Use this before changing pins so you can avoid duplicating or removing the wrong shortcut.
- **set_output_page_pin** — Pin or unpin a workflow output page on the workspace home/sidebar.
The API validates that the workflow belongs to this workspace and that the output page pathname exists on the workflow.
Pin sparingly: only use workspace-level pins for recurring reports, dashboards, canonical results lists, or other pages users should reach directly from the workspace home/sidebar.
- **list_knowledge_lists** — List all knowledge list schemas with field definitions, row counts, and metadata.
Returns detailed information about each list including fields, source type, category, entity config, and KG sync status.
Use this to discover what lists exist and understand their structure before querying rows.
When you mention knowledge lists or text entries back to a human, include both the human-readable name/title and stable key/id, not only the id. Example: VC Contacts (`gtm_vc_contacts`).

**KG-First:** Call this BEFORE generating any AI-step prompt that references business-specific personalization (ICP criteria, scoring rubrics, sector lists, seed lists, etc.) to check whether the content already lives in workspace knowledge. If it does, reference it at runtime via kg.read-list rather than hardcoding it in the prompt template.
- **get_knowledge_rows** — Fetch rows from a knowledge list (paginated, up to 200 per call).
Use this to inspect actual data or scan a small list end-to-end.
Returns rows with their full rowData, count, totalCount for unfiltered list reads, and nextToken. Pass nextToken from the previous response to fetch the next page. nextToken is null when there are no more rows.
When you mention this list back to a human, include the human-readable list name and stable list key when known, not only the key.

For targeted lookups by ID use `get_knowledge_rows_by_ids` — it fetches specific rows in a single call (max 200) without scanning the whole list.
For entity-relationship queries (e.g. "all deals scored by this investor") start with `query_kg_edges`, then pass the returned node IDs to `get_knowledge_rows_by_ids`.
- **get_knowledge_rows_by_ids** — Fetch specific knowledge rows by their IDs. Returns full row data for each requested ID (max 200 per call).

Use this after `query_kg_edges` to load full row data for node IDs returned by the Knowledge Graph.
The canonical chain for entity-scoped queries (e.g. "best deals for Teresa Abecasis") is:
  1. `query_kg_edges({ entityName: "Teresa Abecasis", relationshipType: "SCORED" })`
  2. `get_knowledge_rows_by_ids({ rowIds: <targetNodeIds from step 1> })`

This is O(edges for that entity) — independent of total list size — and scales to 10k+ rows without paginating.

Note: `source_node_id` and `target_node_id` values from `query_kg_edges` correspond directly to knowledge row IDs for investor/deal entity nodes. Rows not belonging to the workspace are silently excluded from results.
- **get_knowledge_text** — Fetch a text-type knowledge entry by key. Use this to access text-based knowledge like feedback files, notes, or configuration text stored in the workspace.
When you mention this text entry back to a human, include the human-readable title/name and stable text key. If no stored title exists, derive a short readable title from the key and still include the key.

**KG-First:** Call this BEFORE generating any AI-step prompt that references workspace-specific content (investment thesis, brand voice, ICP description, scoring rubric, etc.) to check whether it already exists. If found, pass it via a runtime read step rather than hardcoding the text inline in the prompt template.
- **create_knowledge_list** — Create a new knowledge list with a typed schema.
Idempotent on key collision — returns the existing list with a warning instead of erroring, so AI agents can safely re-run setup steps.
Use this before inserting rows to ensure the list schema exists.
Returns: { listKey, fieldCount, kgSyncEnabled, alreadyExisted? }.
- **update_knowledge_list_schema** — Add or remove fields on an existing knowledge list schema.
Use this to evolve a list's schema — add new columns or remove unused ones.
Existing rows are not modified; new fields will be missing from old rows until updated.
Returns: { listKey, fieldCount }.
- **delete_knowledge_list** — Permanently delete a knowledge list and ALL its rows. This action is irreversible.
Pass the listKey of the list to delete. The API requires confirm: true — this tool sends it automatically.
Returns: { success, listKey }.
- **upsert_knowledge_rows** — Create or update rows in a knowledge list. Maximum 500 rows per call — paginate for larger datasets.

Resolution order per row:
  - rows with `id` → update existing row by id
  - rows with `userKey` → O(1) upsert (same userKey in the same list always maps to the same row, across calls and runs — ideal for idempotent sourcing/enrichment workflows)
  - rows with neither → plain insert with a fresh UUID (no dedup)

Pick one stable key per row. `userKey` is caller-defined: a candidateId, a normalized URL, a domain, etc. Whatever string uniquely identifies the entity within the list for your use case.

mergeStrategy controls how updates are applied to existing rows:
  - "overwrite" (default): replace existing rowData entirely with the new values
  - "merge": shallow-merge new values into existing rowData (preserves downstream-added fields like scores or notes)

Returns: { inserted, updated, errors[] } — errors are per-row and do not abort the batch.
- **delete_knowledge_rows** — Delete specific rows from a knowledge list by their IDs. This action is irreversible.
For more than 10 rows the API requires confirm: true — this tool sends it automatically.
Returns: { deleted, errors[] } where each error includes a structured code field:
  - foreign_key_constraint: row is referenced by KG edges (scoring predictions, outcomes, relations) and cannot be hard-deleted
  - not_found: row ID does not exist
  - permission_denied: insufficient workspace permissions
  - delete_failed: unclassified failure

IMPORTANT: Rows referenced by KG edges cannot be hard-deleted. If you receive foreign_key_constraint errors,
use soft-delete instead: call upsert_knowledge_rows with mergeStrategy "merge" and rowData { _dropped: true, _dropReason: "..." }.
Workflow readers typically filter on _dropped or status != "noise", so soft-deleted rows are excluded from downstream reads.
- **upsert_knowledge_text** — Create or update a text-based knowledge entry. Use this to store free-form text (notes, prompts, ICP descriptions, etc.) that can be retrieved by key.
Returns: { key, upserted, created }.
- **delete_knowledge_text** — Delete a text-based knowledge entry by key. This action is irreversible.
Returns: { success, key }.
- **snapshot_knowledge_list** — Capture a full point-in-time backup of a Knowledge Data list.
Returns all rows with their IDs, rowData, status, createdAt, and updatedAt as inline JSON.
Also includes the list schema (fields, userKeyField) so the snapshot is self-contained.

Use before any risky migration, canonical-key reshuffle, or schema change.
The returned snapshot object can be passed directly to `restore_knowledge_list_snapshot` to roll back.

Supports lists up to 5 000 rows (inline JSON). Larger lists are not yet supported.
- **restore_knowledge_list_snapshot** — Restore rows from a snapshot into a Knowledge Data list without wiping computed fields.

Modes (computed fields like scores and enrichment are always preserved):
  - "merge-restore" (default) — for rows that already exist: shallow-merges snapshot fields back in
    (existing computed fields survive; snapshot fields win on conflict). New rows are inserted.
    Use this as the rollback path — it undoes a bad migration without destroying downstream work.
  - "append" — inserts only rows not already present; never touches existing rows.
    Use this when restoring into a new list or when you want to add missing rows without altering anything.

The target listKey may differ from the snapshot's source listKey — this enables cross-list cloning.
If the target list doesn't exist it will be auto-created using the snapshot's schema.

Returns: { restored (new rows inserted), merged (existing rows updated), skipped, errors[] }.
- **query_kg_edges** — Traverse Knowledge Graph edges by entity name and/or relationship type.
Returns edges with source/target node IDs, relations, scores, and metadata.
Use this to explore deal relationships, investor-startup connections, and scoring edges.
Gracefully returns empty results if the Knowledge Graph is not configured.

The `source_node_id` and `target_node_id` values correspond to knowledge row IDs.
Use `get_knowledge_rows_by_ids` as the follow-up call to fetch full row data for those IDs.
Example: `query_kg_edges({ entityName: "Teresa Abecasis", relationshipType: "SCORED" })` → collect `targetNodeIds` → `get_knowledge_rows_by_ids({ rowIds: targetNodeIds })`.
- **get_scoring_history** — Fetch global scoring history for entities from the Knowledge Graph.
Returns past scoring decisions (PROCEED_TO_IC, HOLD_FOR_REVIEW, REPOSITION, SCORED) with DMF scores and dates.
Use this for manual exploration and legacy calibration. Workflow scoring prompts should prefer row-level scoring_profile fields or the kg.retrieve-scoring-memory app action when bounded target IDs are available.
Returns both structured records and a compact text format for prompt injection.
- **chat** — Send a message to the AgentLed AI agent and get a response. The agent can reason, plan, and build workflows through natural language conversation — no need to construct pipeline JSON manually.

Use this tool when you want to:
- Build a workflow from a high-level description ("Create a lead enrichment workflow for SaaS companies")
- Get recommendations on how to structure a workflow
- Ask questions about available integrations or capabilities
- Iterate on workflow design through conversation

The agent has access to the same planning tools, workflow builder, and workspace context as the in-app chat.

For multi-turn conversations, pass the session_id returned from the first message to maintain context across messages.

Example: chat("Build me a workflow that takes a LinkedIn company URL, enriches the data, and scores it by ICP fit")
- **get_chat_turn_result** — Get the status or final result for a durable external chat turn returned by chat or chat_with_agent.

Use this after a chat tool response returns status "running" and a turn_id. Polling this tool is idempotent and does not rerun the original prompt, model call, app actions, approvals, credits, or external sends.
- **get_workspace_credits** — Get the workspace's current credit balance and usage statistics.

Returns:
- currentBalance: remaining credits on the subscription plan
- planType: subscription tier (e.g., "pro", "teams")
- period: exact labelled window for every total (label, display, start, end)
- periodDays: lookback window for usage stats when applicable
- usedThisPeriod: total credits consumed in the labelled period
- totalExecutions: number of unique workflow executions in the period
- averageCreditsPerExecution: average cost per run
- recentUsage: last 20 credit deductions with execution/step context unless includeRecentUsage=false
- costDrivers: optional bounded top workflows, steps, models, and apps when includeCostDrivers=true

Every usage total is ledger-derived and must be shown with its period label. Use this to check if the workspace has enough credits before starting expensive workflows,
or to report balance and burn rate to stakeholders.
- **get_workspace_credit_cost_drivers** — Get a concise, ledger-derived workspace credit cost-driver report.

Defaults to period=rolling-30-days and includeCostDrivers=true. Returned totals are period-labelled; always show the period.label/display/start/end alongside credit totals.

Cost drivers include bounded top workflows, steps, models, and apps. Use all-time intentionally because it can scan more ledger rows.
- **get_branding** — Get the workspace's whitelabel branding configuration.
Returns the current branding settings: displayName, logoUrl, tagline, primaryColor, primaryColorDark, faviconUrl, and hideBadge.
Use this to inspect the current client portal branding before making changes.
- **update_branding** — Update the workspace's whitelabel branding configuration.
Set any combination of branding fields. Pass null or empty string to clear a field.

Fields:
- displayName: Brand name shown to clients (e.g., "Acme Corp")
- logoUrl: URL to brand logo image
- tagline: Short tagline under the brand name
- primaryColor: Hex color for light mode (e.g., "#6366f1")
- primaryColorDark: Hex color for dark mode (e.g., "#818cf8")
- faviconUrl: URL to custom favicon
- hideBadge: Boolean — hide the "Built with Agentled" badge (requires teams/enterprise plan)
- **list_channels** — List the workspace's channel integrations and their configuration.

Returns each configured channel (email, slack, whatsapp, signal, telegram) with:
- enabled: whether the channel is active
- defaultAgentId: the agent handling inbound conversations on default-routed channels
- inboundAddress (email): workspace address pattern; each agent also exposes its own email in list_agents/get_agent/create_agent
- Other non-secret config (team names, channel IDs, phone numbers)

Secret credentials (bot tokens, access tokens, signing secrets) are REDACTED in the response.
Use this to discover which channels exist before assigning agents to default-routed channels.
- **set_channel_default_agent** — Assign the agent that handles inbound conversations on a default-routed channel.

Slack, WhatsApp, Signal, and Telegram use defaultAgentId routing. Email does not: email routes by
per-agent address and agent responses include the derived address in their channels array.

Use list_channels to inspect current assignments and list_agents to find valid agent IDs.
Use configure_channel for more granular updates (enable/disable, allowedSenders, outbound email approval, etc.).
- **configure_channel** — Update non-secret channel configuration — enable/disable, default agent, allowed senders, outbound policy, etc.

Allowed fields per channel:
- email: enabled, allowedSenders (string[]), inboundAddress, allowOutboundEmail, allowedOutboundRecipients, requireOutboundApproval. defaultAgentId is legacy and ignored by email routing.
- slack: enabled, defaultAgentId, defaultChannelId (Slack channel ID used to route inbound mentions when one Slack team is shared)
- whatsapp: enabled, defaultAgentId
- signal: enabled, defaultAgentId
- telegram: enabled, defaultAgentId, botUsername, allowedChatIds

Secret fields (botToken, signingSecret, accessToken, webhookSecret) are REJECTED by the
external API — connect those via the Settings → Channels UI (OAuth flows encrypt at rest).
- **set_channel_defaults** — Update workspace-wide channel defaults (rate limits, timeouts, tool mode).

These settings apply across all enabled channels:
- maxSessionsPerDay: Cap on inbound chat sessions per day (default 100)
- sessionTimeoutMinutes: Auto-close sessions after inactivity (default 60)
- toolMode: 'all' (all tools available) or 'mcp' (MCP-only tools)
- **do** — Semantic intent router — describe what you want to accomplish in plain English and
Agentled will find the best matching live workflow in your workspace. Optionally auto-executes the matched workflow.

Examples:
  - "find the CEO's email for stripe.com"
  - "research acme corp and score them against our ICP"
  - "scrape https://example.com and summarize it"

Returns the best match with confidence score, extracted inputs, and alternatives.
Set execute=true to also start the matched workflow immediately.
- **list_models** — List all supported AI models for workflow steps. Returns model ID, provider, display name,
tier (mini/standard/max), credit cost, and category.

Use the model ID in step.agent.model and the provider in step.agent.provider when configuring AI steps.
Example: agent: { model: "claude-5-opus", provider: "anthropic" }
- **list_agents** — List all agents in the workspace. Agents are workspace identities with
instructions, files, assigned workflows, chat, and optional attached routines for
scheduled/autonomous work.

Each agent includes a channels array for currently reachable inbound channels:
- email appears for every agent when the workspace email channel is enabled, with the derived per-agent address
- Slack, WhatsApp, Signal, and Telegram appear only on the agent assigned as that channel's defaultAgentId
- **get_agent** — Get full details of an agent including its config, files, attached routines, and assigned inbound channels.
- **create_agent** — Create a new agent with name, instructions, tools, workflows, and optional config files.

The response includes channels for currently reachable inbound channels. Email is automatic per agent when
the workspace email channel is enabled; use the returned email channel address rather than assigning a
defaultAgentId for email.

Agents are always 'chat-only' (conversational). For scheduled/autonomous work, create the agent
first, then attach routines to it via create_routine (e.g. daily deal-sourcer, weekly digest).

Key fields:
- name: Agent display name
- agentType: Preset template — 'personal-assistant', 'competitive-researcher', 'social-media-marketer',
  'customer-support', 'content-marketer', 'lead-qualifier', 'deal-sourcer', 'custom' (default)
- instructions: System prompt / core AGENTS.md content
- enabledApps: App IDs this agent can use — get IDs from list_apps (e.g. ['web-scraping', 'kg', 'gmail'])
- appPermissions: Optional per-app permissions keyed by app ID. Use { access: 'read' } for read-only or { access: 'write', writeApprovalRequired: true } for mutating access with approval.
  For per-action overrides, add actionApprovals with canonical action keys, e.g. { linkedin: { access: 'write', writeApprovalRequired: true, actionApprovals: { 'linkedin.create-comment': false } } }.
  Read access is implicit and never requires approval. The internal 'agentled' app is not configurable here.
- skillIds: Skill IDs from list_agent_skills. Stored internally as enabledSkills; user-facing label is Skills.
  Use list_agent_skills({ includeRuntime: true }) only when configuring advanced runtime bundles such as workflow-operator, template-runner, or inline-execution.
- assignedWorkflowIds: Workflow IDs this agent can trigger — get IDs from list_workflows
- goals: Natural-language description of what the agent should achieve
- configFiles: Override generated config files — keys are 'SOUL.md' (persona), 'TOOLS.md' (tool routing).
  If omitted, files are auto-generated from agentType template.
  Reflection context files ('JOURNAL.md', 'OBJECTIVES.md', 'PEOPLE.md') are linked AgentFiles, not configFiles.
  Active chat-only agents auto-seed placeholders for those files. The agent decides what durable signal belongs there;
  do not write raw transcript logs or update them just because a chat turn happened.
- avatar_icon_name: Lucide icon name for the agent avatar (e.g. 'Bot', 'Radar', 'Target', 'Sparkles')
- avatar_color: Hex color for the avatar (e.g. '#6366f1', '#7C3AED', '#EA580C')
- linkedFileIds: Workspace-level AgentFile IDs to attach as knowledge (from list_agent_files — workspace scope)
- chatModel: Override the chat model (e.g. 'anthropic:claude-4-6-sonnet', 'openai:gpt-4o-mini')
- activate: Set true to activate immediately (default false = draft)

To add scheduled routines after creating the agent, use create_routine.
- **update_agent** — Surgical update of one agent. **Preferred path for any single-field edit on an existing agent** — only the fields in `updates` / `replace` / `unset` are touched, every other field is left as-is. Same merge model as `update_step`.

## Merge semantics

`update_agent` accepts three independent operations on the same call. At least one must be non-empty.

- **`updates`** — partial agent patch. Top-level fields (`name`, `description`, `instructions`, `status`, `goals`, `chatModel`, `enabledApps`, etc.) are shallow-replaced. Nested objects (`configFiles`, `avatar`, `appPermissions`) are deep-merged ONE LEVEL — keys you don't mention are preserved. Arrays (`enabledApps`, `enabledActions`, `assignedWorkflowIds`, `linkedFileIds`) are replaced wholesale.
- **`replace: string[]`** — dot-paths whose values from `updates` are assigned WHOLESALE, skipping deep-merge. Use this when you genuinely want to wipe a dictionary (e.g. `replace: ["configFiles"]` swaps the whole configFiles dict instead of merging key-by-key).
- **`unset: string[]`** — dot-paths to DELETE (e.g. `["goals"]`, `["configFiles.SOUL.md"]`). Each must currently exist on the agent.
- **`null` in updates** — shortcut for unset (e.g. `updates: { goals: null }`).

## Common edit recipes

| Goal | Call |
|---|---|
| Update one config file (preserve others) | `updates: { configFiles: { "SOUL.md": "new persona…" } }` |
| Replace all instructions | `updates: { instructions: "new system prompt" }` |
| Rename agent slug/email address | `updates: { slug: "pitchnight" }` |
| Assign workflows (full replace) | `updates: { assignedWorkflowIds: ["wf-1", "wf-2"] }` |
| Add to assigned workflows | fetch via `get_agent`, modify locally, send full new array (or use `manage_agent_workflows`) |
| Allow an app to write with approval | `updates: { enabledApps: ["linkedin"], appPermissions: { linkedin: { access: "write", writeApprovalRequired: true } } }` |
| Override one action approval | `updates: { enabledApps: ["linkedin"], appPermissions: { linkedin: { access: "write", writeApprovalRequired: true, actionApprovals: { "linkedin.create-comment": false } } } }` |
| Keep an app read-only | `updates: { enabledApps: ["linkedin"], appPermissions: { linkedin: { access: "read" } } }` |
| Change avatar color only | `updates: { avatar: { color: "#7C3AED" } }` (iconName preserved) |
| Activate (fail-fast on missing fields) | `updates: { status: "active" }` |
| Deactivate / pause | `updates: { status: "paused" }` or `updates: { status: "draft" }` |
| Unset a scalar field | `updates: { goals: null }` or `unset: ["goals"]` |
| Wipe + reset configFiles wholesale | `updates: { configFiles: { "SOUL.md": "…", "TOOLS.md": "…" } }, replace: ["configFiles"]` |
| Remove just one config file | `unset: ["configFiles.SOUL.md"]` |

**The trap.** Default deep-merge for `configFiles` and `avatar` is one level — sending a partial dict preserves siblings. To force a full wipe, use `replace: ["configFiles"]`.

## Activation requirements (fail-fast on `status: "active"`)

When transitioning to `active`, the agent is validated:
- `instructions` must be non-empty
- `configFiles["SOUL.md"]` must be present, > 200 chars, and not contain the placeholder marker
- `configFiles["TOOLS.md"]` must be present, > 200 chars, and not contain the placeholder marker

If any check fails, the agent is NOT updated. The response is `{ ok: false, errors: ["…"] }` (HTTP 400). Fix the missing fields and retry.

For scheduled / autonomous behaviour, attach routines via `create_routine` AFTER activation. Routines are first-class entities — they are NOT a field on the agent.

## What update_agent will NOT do

- Cannot change `agent.id` (immutable, 400)
- Changing `agent.slug` moves the AgentEntity to a new `{slug}@{workspace}` id, rebinds routines/file links/channel sessions/chat sessions where available, and updates the agent email address derived from the slug.
- Slug convention: `slug` is the short role ID used in URLs/email. Keep "Agent" in the display name when useful, but do not append `-agent` to the slug just because the display name includes it; e.g. `Deal Sourcing Agent` should use `deal-sourcing`, not `deal-sourcing-agent`.
- Cannot create new agents (use `create_agent`)
- Cannot delete agents (use `delete_agent`)
- Does not edit routines (use `update_routine` / `create_routine` / `pause_routine`)
- Does not validate the merged result is internally consistent — only the activation guard runs. Other invariants are caller's responsibility.

## Diff + warnings

Response includes `diff: { addedPaths, changedPaths, removedPaths }` and `warnings[]`. ≥6 fields removed without explicit `unset` triggers a warning — usually a "you wiped a dictionary" signal.
- **delete_agent** — Permanently delete an agent and all its files. Attached routines are also removed by the API.
- **activate_agent** — Activate an agent, changing its status from draft or paused to active.
Active agents respond to chat messages; active agents with routines will run those routines on schedule.
- **pause_agent** — Pause an active agent. Routines attached to the agent stop running until the agent is resumed via activate_agent.
- **manage_agent_workflows** — Add, remove, or replace the workflows assigned to an agent without passing the full agent config.

- operation 'add': append workflowIds to the existing list (no-op for IDs already present)
- operation 'remove': remove specific workflowIds from the list
- operation 'set': replace the entire list with the given workflowIds

Use 'set' with an empty array to clear all assigned workflows.
- **chat_with_agent** — Send a message to an agent. Defaults to the active workspace assistant (assistant@...) when no id or agent_slug is provided.
Use list_agents first when you want to choose a different agent. The agent's instructions are used as the system prompt.
Supports multi-turn conversations via session_id.
- **list_agent_files** — List all files attached to an agent. These include reference documents, knowledge files,
and any other files uploaded to provide the agent with context.
- **get_agent_file** — Get the content of a specific file attached to an agent.
- **upload_agent_file** — Upload a file to an agent. Files provide the agent with additional context such as
reference documents, knowledge bases, or configuration data.

- name: Filename (e.g. 'thesis.md', 'company-context.txt')
- content: Raw text or markdown content (max 400KB)
- mime_type: MIME type (default: 'text/plain', use 'text/markdown' for .md files)
- role: Optional label describing the file's purpose (e.g. 'knowledge', 'context')
- **update_agent_file** — Update a file that is already attached to an agent.

Use this for durable reflection context such as JOURNAL.md, OBJECTIVES.md, and PEOPLE.md:
read the current file first with get_agent_file, then send the full updated content. Update
only when there is durable signal (decisions, corrections, objectives, people context, or
useful learning), not for every chat turn. The file must already be linked to the agent, so
this tool cannot accidentally edit an unrelated workspace file.
- **delete_agent_file** — Permanently delete a file attached to an agent.
- **submit_feedback_to_agentled** — Report a bug, request a feature, escalate an issue, or ask the Agentled team a question.
Use this when you encounter something broken, have a suggestion for improvement,
need human help from the Agentled team, or want to escalate a problem you cannot solve.
Include a clear title and detailed description. Provide the user's email if follow-up is needed.
- **recall_memory** — Recall a specific memory by key. Returns the stored value if found.
Use this to retrieve previously stored facts, insights, preferences, or outcomes.

**KG-First:** Call this BEFORE generating AI-step prompts that reference workspace-specific strategy (ICP, thesis, rubric). If the content exists as a memory, reference it at runtime via the workspace_memory builtin tool rather than pasting it into the prompt template.
- **search_memories** — Search memories by natural language query and/or category.
Returns matching memories sorted by confidence. Use to find relevant stored knowledge.

**KG-First:** Call this BEFORE generating AI-step prompts with business-specific content to check whether workspace strategy (ICP, thesis, rubric) is already stored. Reference found memories at runtime instead of inlining them in prompt strings.
- **store_memory** — Store a persistent memory. Memories survive across workflow executions and can be recalled later.
Categories: fact (known truth), insight (pattern/learning), preference (user preference), outcome (result to track).

**KG-First:** Use this to seed workspace-specific strategy content (ICP criteria, scoring rubric definitions, investment thesis, brand voice) BEFORE building the workflow steps that need it. Once stored, the AI step references it at runtime via the workspace_memory builtin tool — never hardcoded in the prompt template.
- **list_memories** — List all memories in a given scope. Returns memories sorted by confidence.
- **delete_memory** — Delete a specific memory by key.
- **list_routines** — List all routines for an agent. Routines are scheduled tasks that run
automatically on a fixed interval (e.g. daily, weekday-morning).

Pass the agent's slug (e.g. "dealflow") or ID as `agent_id`.
- **create_routine** — Create a new routine for an agent. Routines are paid autonomous features; free-tier workspaces cannot create or resume them.

Interval values:
  By schedule: weekday-morning (Mon–Fri 08:00 UTC), weekday-evening (Mon–Fri 18:00 UTC),
               weekly-monday (Mon 08:00 UTC), weekly-tuesday-evening (Tue 18:00 UTC),
               weekly-friday-evening (Fri 18:00 UTC),
               daily (every day 08:00 UTC), monthly (1st of month 08:00 UTC)
  By interval: 6h, 48h

Model format: "provider:modelId" e.g. "openai:gpt-5.4-mini" (default).
Use skillIds from list_agent_skills for routine-level skill overrides. Use list_agent_skills({ includeRuntime: true }) for advanced routine runtime bundles such as routine-core.
- **update_routine** — Update a routine's fields. Only provided fields are changed.
If interval is updated, nextRunAt is automatically recalculated.
- **pause_routine** — Pause a routine (status → paused). The routine will not run until resumed.
- **resume_routine** — Resume a paused routine (status → active). Routines are paid autonomous features; free-tier workspaces cannot resume them. nextRunAt is reset to the next
occurrence of the routine's interval from now.
- **trigger_routine** — Run a routine immediately without changing its scheduled cadence. Free-tier workspaces cannot trigger routines.

Use a short reason, for example "verify analytics monitor". Source must be one
of: codex, claude, ui, api, mcp.
- **delete_routine** — Permanently delete a routine. This cannot be undone.
- **list_public_form_links** — List public form links for a workflow. Public URLs use /en/forms/{formLinkId}.
- **create_public_form_link** — Create and enable a public form link for a workflow so anyone can submit the workflow input form without an account.
- **update_public_form_link** — Update a workflow public form link, including enabling/disabling it, limits, auto-share, expiry, and success message.
- **get_public_form_link** — Get one public form link by token/id.
- **delete_public_form_link** — Delete a public form link by token/id. Currently unsupported by External API (returns 501).
- **share_execution** — Create or get a share URL for an execution.
- **get_share** — Get share metadata by share id.
- **revoke_share** — Revoke/delete share by share id.
- **list_agent_skills** — List supported agent skill IDs for create_agent, update_agent, create_routine, and update_routine.

Use these IDs in skillIds. They are stored internally as enabledSkills, but user-facing copy should say Skills.
By default this returns only public user-facing skills. Pass includeRuntime: true only for advanced runtime bundle configuration such as workflow-operator, template-runner, inline-execution, routine-core, or channel-email.
The agent-manager and routine-manager skills are control-plane tools for draft goal groups, owner agents, reusable assets, and paused schedules; activation or scheduled autonomy still requires explicit approval and audit.
- **list_workspace_skills** — List workspace-created skills. These are draft/published/archived skill records backed by AgentFile content.

Use import_workspace_skill for a one-step Markdown/SKILL.md/JSON import. Use create_workspace_skill only when you already have the backing AgentFile ID. Workspace skill allowedApps/allowedActions are recommended tools only; they do not grant app/action scope, runtime bundles, workspace connections, write access, or approval bypasses.
- **import_workspace_skill** — Import a Markdown, SKILL.md, or JSON skill file into a draft workspace skill.

This creates one backing workspace AgentFile and then one draft WorkspaceSkill. Imported allowedApps/allowedActions are recommended apps/actions only. Import does not grant those tools, publish the skill, assign it to an agent, run chat/routines/workflows, write providers, send messages, bypass approvals, or spend credits. Use publish_workspace_skill and explicit agent assignment only after reviewing the draft.
- **create_workspace_skill** — Create a draft workspace skill backed by an existing AgentFile.

The AgentFile content should contain the skill instructions/body. The WorkspaceSkill record stores lifecycle, risk, relevance, recommended apps/actions, and approval metadata. The allowedApps/allowedActions fields are recommendations for runtime guidance; actual availability still comes from the assigned agent app/action scope, runtime bundles, workspace connections, and approval gates. Prefer import_workspace_skill for Markdown/SKILL.md/JSON imports; use this low-level tool when you already have a reviewed AgentFile ID.
- **get_workspace_skill** — Get a workspace-created skill by ID.
- **update_workspace_skill** — Update a draft workspace-created skill. Published and archived workspace skills are immutable through this tool; archive and create a new version instead.
- **publish_workspace_skill** — Publish a draft workspace-created skill.
- **archive_workspace_skill** — Archive a workspace-created skill without deleting its backing AgentFile content.
- **upsert_ai_builder_profile** — Create or update the external AI builder profile linked to this authenticated API key.
Clients should call this after receiving a scoped API key; workspaceId and apiKeyId are derived server-side.
Setup cadence for the builder: poll every 4h when idle/no active topic, every 1h when there is an active topic or recent assigned work, every 30m while work is in progress, and opportunistically call list_builder_work_items when already engaging with the user on another topic.
- **list_builder_work_items** — List work items eligible for this authenticated builder. Defaults to queued and needs_changes items assigned to the registered builder/API key when available.
- **claim_builder_work_item** — Claim a queued or needs_changes builder work item for this authenticated builder and append activity.
- **append_builder_work_activity** — Builder-only activity append. Use this when the authenticated external builder is reporting progress, links, test notes, or questions; the server records the registered builder identity.
- **submit_builder_work_item** — Submit completed builder output refs and a summary. The work item moves to submitted for business-agent review.
- **create_builder_work_item** — Create a durable work handoff from a business agent to an external AI builder. Use sourceAgentEntityId to preserve the business-agent owner for review/email/UI.
- **list_builder_work_items_for_agent** — List open/stale/submitted builder work items for a business agent by sourceAgentEntityId.
- **get_builder_work_item** — Get full builder work item context, outputs, activity, metadata, and review state.
- **review_builder_work_item** — Review submitted builder work as a business agent; set accepted, needs_changes, or closed and optionally attach thumbs-up/down quality signal.
- **append_builder_work_item_activity** — Business-agent-only activity append. Use this for source-agent follow-up, review, or routing notes; builders should use append_builder_work_activity instead.
- **list_use_cases** — List workspace use cases and their operating guides, linked workflows, agents, routines, AgentFiles, knowledge refs, data sources, config, validation, and metadata.

Use this before building or changing a multi-workflow business goal so you can reuse the existing WorkspaceUseCase instead of hand-rolling disconnected workflows. Read operatingGuides/agentGuidance first; missing operating-guide warnings mean the agent context is incomplete. This tool is read-only and does not run workflows, spend credits, trigger routines, or write to providers.
- **preview_use_case_kit** — Preview the atomic operation plan for a Source -> KG -> Process WorkspaceUseCase kit.

Use this before creating multiple related workflows for sourcing, intake, monitoring, business intros, or other shared-tail use cases. It returns the planned WorkspaceUseCase record, knowledge list, source workflows, shared tail, optional orchestrator or receiver, and link operations in deterministic order. A shared-assistant-sourcing-v1 profile can be atomically provisioned after this preview is reviewed. This is dry-run only: it does not create records, workflows, KG rows, sends, provider writes, routine runs, or spend credits.
- **provision_use_case_kit** — Provision a previously reviewed Source -> KG -> Process WorkspaceUseCase kit.

Use preview_use_case_kit first, review the deterministic plan, then call this only when the user explicitly approves the write. This creates the WorkspaceUseCase record, knowledge-list schema, and concrete workflows carried in payload.pipeline, then links them on the use-case record. shared-assistant-sourcing-v1 profiles atomically add their receiver, operating guide, capped configuration, and additive existing-assistant update. It does not run workflows, insert KG rows, send messages, write to external providers, trigger routines, make approval decisions, or spend credits. Requires confirmToken exactly "PROVISION_USE_CASE_KIT".
- **create_use_case** — Create a WorkspaceUseCase product record for a multi-workflow business goal.

Use this before creating or attaching workflows for use cases such as sourcing, intake, front desk, or business intros. For config.useCasePage.body.columns, arbitrary badges can use case-insensitive valueRoles and numeric scores can use ordered minimum thresholds. Allowed semantic roles are neutral, accent, warning, success, and danger; never store CSS classes in config. This creates the product-layer object and linked references only; it does not create workflows, write KG rows, run providers, spend credits, trigger routines, or send messages. After creating it, create/update workflows with the returned workflowGraphId and keep linked refs on the use case current.
- **get_use_case** — Get one workspace use case by stored id, key, or workflowGraphId.

Use this as the first inspection step for a specific business goal such as business-intro or front-desk. Read operatingGuides before answering workflow-specific questions or changing linked workflows. The normalized onboardingGoal field is stable desired configuration only; never infer live approval enforcement from it. If agentGuidance.warnings includes MISSING_USE_CASE_OPERATING_GUIDE, state that the operating README is missing and create/update the linked knowledge text before treating the context as complete. This tool is read-only; create/update/provisioning operations remain intentionally separate until the atomic use-case kit lifecycle lands.
- **get_use_case_goal** — Get the normalized onboarding-goal contract and freshly resolved approval enforcement for one WorkspaceUseCase.

Use this instead of reading raw config when deciding whether an operation is automatic, approval-gated, disabled, misaligned, or unresolved. This tool is read-only and does not run workflows, trigger routines, call providers, spend credits, make approval decisions, or perform external writes.
- **set_use_case_goal_policy** — Set one typed desired approval policy on an existing onboarding-goal requirement.

This changes desired policy only; it does not change live workflow, agent, channel, or routine enforcement and does not approve or execute any action. Weakening a send, write, or delete safeguard requires confirm=true. The server reuses the UI validator and writes an actor-attributed audit row.
- **bind_use_case_goal_requirement** — Add one typed live-enforcement pointer on an onboarding-goal approval requirement.

This updates the requirement binding only. It does not change the referenced workflow, agent, channel, or routine; run providers; spend credits; make approval decisions; or execute external writes. Removing a path from a send, write, or delete safeguard requires confirm=true. The server reuses the UI validator and writes an actor-attributed audit row.
- **unbind_use_case_goal_requirement** — Remove one typed live-enforcement pointer on an onboarding-goal approval requirement.

This updates the requirement binding only. It does not change the referenced workflow, agent, channel, or routine; run providers; spend credits; make approval decisions; or execute external writes. Removing a path from a send, write, or delete safeguard requires confirm=true. The server reuses the UI validator and writes an actor-attributed audit row.
- **set_use_case_goal_finish_line** — Set the default finish line to one milestone already declared by the onboarding-goal contract.

This changes desired goal configuration only. It cannot mutate runtime milestone evidence or mark work complete, and it does not run workflows, trigger routines, call providers, spend credits, make approval decisions, or perform external writes.
- **set_use_case_goal_brief** — Set the Knowledge Text key that contains the durable goal brief for one use case.

This changes only the typed pointer. It does not edit Knowledge Text content, run workflows or routines, call providers, spend credits, decide approvals, or perform external writes. The server reuses the UI validator and writes an actor-attributed audit row.
- **bind_use_case_goal_skill** — Bind one skill reference to a linked use-case agent and business role.

This changes goal configuration only. It does not assign the skill to the agent, grant tools or app permissions, run providers, spend credits, or perform external writes. The server reuses the UI validator and writes an actor-attributed audit row.
- **unbind_use_case_goal_skill** — Remove one exact skill and agent reference from an onboarding goal.

This changes goal configuration only. It does not remove the skill from the agent, revoke tools or app permissions, run providers, spend credits, or perform external writes. The server reuses the UI validator and writes an actor-attributed audit row.
- **set_use_case_goal_primary_crm** — Set the primary CRM reference used by one onboarding goal.

This changes only the typed CRM reference. It does not connect the app, change credentials, write CRM data, run workflows or routines, call providers, spend credits, or decide approvals. The server reuses the UI validator and writes an actor-attributed audit row.
- **get_use_case_record_feedback** — Get the current fit assessment for one record in a workspace use case.

The response keeps fit assessment separate from sourceOperationalStatus and explicitly reports zero workflow, routine, provider, approval, CRM, and credit side effects. This tool is read-only.
- **list_use_case_record_feedback** — List current fit assessments for records in one workspace use case.

Cleared/Unreviewed records are omitted. This tool is read-only and does not run workflows, trigger routines, call providers, change approvals, write CRM data, or spend credits.
- **set_use_case_record_feedback** — Set a Good fit, Not a fit, or Needs review assessment only after an explicit user instruction.

This stores structured use-case memory for the exact record. It does not run workflows, trigger routines, call AI/providers, approve or reject pending work, write CRM data, mutate the source record, or spend credits. Free-form comments are untrusted evidence and are never promoted into prompts or scoring policy by this tool.
- **clear_use_case_record_feedback** — Clear the current fit assessment only after an explicit user instruction.

This returns the record to Unreviewed while retaining bounded attributed change evidence. It does not run workflows, trigger routines, call providers, change approvals, write CRM data, mutate the source record, or spend credits.
- **update_use_case** — Update a WorkspaceUseCase product record by stored id, key, or workflowGraphId.

Use this to attach existing workflows, agents, routines, AgentFiles, KG refs, data sources, config, validation, owner, collaborators, or lifecycle status to the use-case hub. config.onboardingGoal.approvalRequirements is read-only through external tools and must be edited from Use case > Safeguards. For config.useCasePage.header, use source pointers rather than copied labels: workflow analytics require { source: "workflow-analytics", workflowId, metricId, window }, and channels require { channel, workflowId, stepId }. For config.useCasePage.body.columns, preserve every existing column, set integer order values for an explicit order, and set semanticRole: "company" when a company-like column such as Startup should render before Status. Badge columns can map any case-insensitive value to a fixed semantic color with valueRoles, for example { type: "badge", field: "qualification", valueRoles: { ready: "success", warm: "warning" }, defaultRole: "neutral" }. Numeric score columns use { type: "score", field, thresholds: [{ min, role }], defaultRole }; the highest matching minimum wins. Allowed color roles are neutral, accent, warning, success, and danger; never store CSS classes in config. The platform derives metric labels/formats and channel approval/provider modes from those sources; validate each referenced workflow after updating the use case. This updates the use-case record only; it does not run workflows, mutate providers, spend credits, or make approval decisions.
- **archive_use_case** — Archive a WorkspaceUseCase product record without deleting linked workflows, agents, routines, AgentFiles, or knowledge.
- **list_workspace_views** — List saved workspace views and their source/action guidance.

Use this before designing a new view so you can reuse or update an existing operating surface. This is read-only and does not run workflows, read provider data, mutate KG rows, make approval decisions, or spend credits.
- **create_workspace_view** — Create a saved WorkspaceView manifest over live workspace data.

Workspace Views are general operating surfaces, not KG-only reports. Data sources can include KG lists/text, workflow executions/output pages/timelines, pending approvals, agent approvals, action queues, routine runs, external APIs, and custom sources. Creating the view writes only the manifest; it does not read sources, run workflows, insert KG rows, send messages, call providers, make approval decisions, or spend credits.
- **get_workspace_view** — Get one saved WorkspaceView by stored id or key.

Read agentGuidance before rendering or acting from the view. View reads are safe; action execution must still use the normal approval and side-effect policies.
- **update_workspace_view** — Update a saved WorkspaceView by id or key.

This updates only the view manifest. It does not mutate source rows, execute view actions, start workflows, make approval decisions, call providers, or spend credits.
- **archive_workspace_view** — Archive a WorkspaceView manifest without deleting linked KG rows, workflows, executions, approvals, agents, routines, or external data.
- **resolve_workspace** — Check whether a workspace already exists for a firm, slug, domain, owner email, or idempotency key.

This is read-only. Agents should call this before create_workspace so existing firm workspaces are reused instead of accidentally creating duplicates.
- **create_workspace** — Create a workspace only when the caller has operator workspace-create scope; otherwise the request is rejected without mutation.

Call resolve_workspace first. This setup primitive does not provision use-case kits, run workflows, insert candidate rows, send messages to customers, write CRM/calendar, call SignalKit receive, trigger routines, or spend provider credits.

## Claude Desktop Quick Installation
Install path detected from listing signals. Uses `npx` (confidence: high):

```json
"mcpServers": {
  "mcp-server": {
    "command": "npx",
    "args": ["-y","@agentled/mcp-server"],
    "env": {
      "AGENTLED_API_KEY": ""
    }
  }
}
```

**Requires environment variables:** `AGENTLED_API_KEY` — the values above are empty placeholders; fill in real credentials before running (see the repository for what each one is for).

## Documentation

## What the Agentled/mcp-server MCP server does

The Agentled/mcp-server MCP server gives MCP-compatible clients access to Agentled workspaces and their workflow runtime. It supports creating, inspecting, editing, validating, publishing, starting, and monitoring workflows. Workflow definitions can include triggers, AI actions, AI actions with built-in tools, app actions, JavaScript code, Knowledge Graph synchronization, approvals, sharing, waits, branches, parallel paths, loops, returns, milestones, and agent orchestration.

The server also exposes workspace memory and Knowledge Graph operations. Agents can inspect memories, knowledge lists, and knowledge text before placing workspace-specific strategy or criteria into prompts. Stored information can then be read at runtime by workflow steps, keeping strategy data separate from workflow wiring.

## How it works

A typical authoring sequence is `create_workflow` with only a name and goal, followed by `add_step` calls, `validate_workflow`, and `publish_workflow`. Adding one step at a time returns validation feedback early and helps reveal variable bindings such as `{{input.X}}` and `{{steps.Y.Z}}`. Existing workflows can be changed with focused tools such as `update_step`, `add_step`, `remove_step`, and `update_workflow_context`.

For live workflows, configuration changes are routed to a draft snapshot. Agents can inspect that draft, promote it to live, or discard it. Snapshots can also be created, listed, inspected, restored, and deleted. Workflows can be exported as portable JSON and imported into another workspace. The server also supports previewing and importing n8n JSON, with the import entering a draft review state rather than being applied automatically.

Execution is asynchronous. `start_workflow` accepts input data and may return an execution ID or an input ID that must be matched through `list_executions`. `get_execution` returns completed step results, while timeline tools can help inspect a particular invocation. Mock-enabled steps use mock data by default; `useMocks: false` forces a real run.

## Setup and configuration

The Agentled/mcp-server MCP server is distributed as the npm package `@agentled/mcp-server`. A local stdio installation can be registered with:

```bash
claude mcp add --transport stdio --scope user agentled \
  -e AGENTLED_API_KEY=wsk_... \
  -- npx -y @agentled/mcp-server
```

The API key is generated in Agentled under Workspace Settings > Developer and starts with `wsk_`. The repository also documents a Claude Code plugin that installs the server and an Agentled skill together, but the plugin and direct MCP registration should not both be used for the same setup. Local development can point a built entrypoint at an `AGENTLED_URL`, such as a local application instance.

## Tools and capabilities

The Agentled/mcp-server MCP server includes tools for:

- Listing and reading workflows, steps, executions, timelines, credits, drafts, and snapshots.
- Creating, updating, moving, removing, validating, publishing, pausing, archiving, exporting, importing, and deleting workflows.
- Reading step schemas and app action schemas before authoring pipeline definitions.
- Running AI models, built-in tools, app integrations, browser automation, scraping, communications, media generation, and Knowledge Graph operations when available in the workspace catalog.
- Building multi-workflow pipelines that source entities, store them in shared Knowledge Graph lists, qualify them, and route approved results to later workflows.
- Applying approval gates to writes and using scheduled triggers for recurring, idempotent processing.

Before selecting an app or data source, agents should inspect the available app catalog and action schemas. Some connectors require the user’s own connected account or API key, while other services consume Agentled credits.

## Limitations and notes

JavaScript is supported for code steps; Python is not. Workflow step types and fields are schema-controlled, and unknown fields may be stripped when saved. Full step-array updates are intended for imports and round-trips; interactive edits should use per-step tools. Dictionary-shaped fields should be read before replacement to avoid unintentionally removing sibling values.

Deleting a workflow is a two-phase operation: the first call creates a confirmation token and the second performs the cascade delete. Snapshot limits depend on the Agentled plan. Credit totals should be reported with their returned period label and date range. The server requires an Agentled API key, and actual integrations, models, and data sources depend on the workspace catalog and connected accounts.

_Full upstream README: https://allmcps.com/mcp/agentled-mcp-server/readme_

