We raised a $50M Series B led by BenchmarkLearn more
Profound logo — MCP server on Gumloop

Profound

Track and optimize your brand visibility in AI answers with Profound.

Book a demo

Installation

Set up the Profound MCP server in Gumloop

Do this once to provision your hosted server URL.

1

Create a Gumloop account

To use this MCP, you need a Gumloop account. If you don't have one yet, sign up and start a 14-day free trial.

2

Add and authorize the Profound server

In Gumloop, open Connectors and add Profound. Depending on the app, you'll either sign in via OAuth or paste an API key. Either way, the credential is stored securely in Gumloop.

Then use it in your client

Use in GumloopUse in Gumloop
1

Use Profound in an agent

Once Profound is set up, just open any Gumloop agent, add Profound as a connector, and start chatting with the agent.

Tools (54)

  • Start Agent Build Session

    Open a build session — the FIRST step of building or revising an agent. Call this BEFORE anything else when the user asks you to build (or substantially revise) an agent — before `list_agent_definition_templates`, before `list_agent_node_types`, and before any `create`/`update`. It is the entry point to building: those discovery and write tools are steps that come AFTER you have opened a session here and are carrying its id. Call it once per build attempt. Pass `intent`: a short, plain-language description of what the user wants the agent to do (e.g. "weekly visibility summary for the Banking category"). You do not need every detail yet — that is what the returned `clarifying_questions` help you pin down with the user. Returns a freshly minted `agent_build_session_id` plus priming guidance: - `agent_build_session_id`: an opaque id for THIS build attempt. Pass it, UNCHANGED, on every subsequent build call (validate_agent_definition → create_agent_definition → update_agent_definition → publish_agent_definition) so the whole attempt is traced as one episode. Do not invent your own. - `clarifying_questions`: the things worth confirming with the user before building — inputs/outputs, what success looks like, which Profound data and category, linear vs. branching. - `good_prompt_examples`: what a buildable request looks like, to steer the user if their ask is vague. This call is safe and free: it persists nothing and validates nothing — it only mints the id and returns guidance. It does NOT create an agent. Next: gather any missing detail with the user, then browse `list_agent_definition_templates` for a proven starting point (or `list_agent_node_types` to assemble from scratch), and thread the returned `agent_build_session_id` on every build call.

  • Validate Agent Definition

    Check whether a saved draft agent definition is well-formed and publishable. Returns `{valid, issues[], input_schema, output_schema}` for the draft identified by `agent_id`. Each issue carries a `code` (`missing_field`, `invalid_graph`, `invalid_input`, or `schema_preview_failed`), a human `message`, and — when the fault can be pinpointed — `node_id`, `node_title`, `field`, and `field_title`, enough to fix the exact offending field yourself without asking the user. `invalid_input` flags a field that is present but unusable — a hub-backed node left with an empty `integration_id` reports it with `violation="required"`, and is fixed by resolving the id with `list_integrations` and setting it on the node. `schema_preview_failed` flags an output-wiring error (e.g. an end node referencing an output no upstream node produces) that the graph-shape check alone can't catch. When the graph is publishable, `input_schema`/`output_schema` carry the JSON Schemas the agent will expose once live (they are null when the graph isn't previewable). This call is safe and free: it only reads. This is your ONLY pre-publish safety net — there is no draft test-run, so validate carefully before publishing. You get a `agent_id` from `create_agent_definition(preview=False)` — which already returns the same report inline, so call this tool only when you need to RE-CHECK an existing draft (e.g. after fixing and re-creating it). On `valid=false`: read `issues`, call `get_agent_node_schema` for the node type whose field is flagged, fix that field and apply it with `update_agent_definition(preview=False)` (edits the same draft in place). Re-validate until `valid` is true. On `valid=true`: the draft is publishable. Next: `publish_agent_definition(preview=True)` to preview the go-live with the user. `agent_build_session_id`: REQUIRED. The opaque id that `start_agent_build_session` returned for THIS build — pass it unchanged on every build call (validate → create → update → publish) so the whole attempt is traced as one episode. If you have not opened a session yet, call `start_agent_build_session` first to get one; do not invent your own id. The report also carries `diagram`: a ready-to-render ```mermaid flowchart of the draft's graph (node labels only, no ids). Show it to the user so they can confirm the agent's shape at this gate before publishing. It is null when the graph has no drawable nodes. `graph_lint` carries advisory findings the backend `valid`/`issues` don't — most notably, for each hub-backed node (Google Search Console, WordPress, Google Docs, Gmail), whether its integration is already connected in this org: a connected one just needs its `integration_id` resolved with `list_integrations` and set on the node, an unconnected one must be connected first (the finding carries the connect-page link) or every run will fail. These never gate the call; read them to guide the user.

  • List Agent Node Types

    List the node types you may use to build an agent graph. Each entry has a `node_type` (the value you put in a node's `type` field), a `display_name`, and a one-line `description`. This is the complete set of types available in v1 — a graph using any other type will fail validation. Optional `kind` narrows the catalog by WHOSE CREDENTIAL the node runs on — the one axis that decides whether the user must go connect something first: - `third_party` — needs an account the ORG connects (Google Search Console, WordPress, Google Docs, Gmail). These author and publish freely, but every run fails until that integration is connected in Profound settings AND its `integration_id` is set on the node; `get_agent_node_schema` names which integration under `documentation.availability`. - `platform_key` — an outside vendor Profound pays for (`exa_*`, `firecrawl_*`, `perplexity_*`, `serpapi_*`, `dataforseo_*`, `youtube_*`, `parallel_*`). Nothing for the user to connect and no API key to supply — they just run. - `native` — Profound's own: the structural nodes (`llm`, `code`, `conditional`, `iteration`, …) and the `profound_*` data nodes, which read the org's own Profound account. - omit `kind` for the full catalog (default; unchanged for existing callers). The three are exhaustive and mutually exclusive, so `native` is NARROWER than "not third-party": a platform-key vendor needs nothing connected, yet it is not Profound's own data. `kind` is a static classification of the node type itself — it does NOT tell you whether *your* org has connected an integration (that is a separate, dynamic signal; `list_integrations` is the authority). Filtering is purely a convenience narrowing; the payload rows are identical whether or not you pass it. Important (beta API): being listed here is necessary but does NOT by itself guarantee a node of that type can be published. Some types are still stabilizing, so a graph that uses them may create as a draft yet be rejected at publish. Treat `validate_agent_definition` and the `publish_agent_definition` result as the authority on publishability — not this catalog — and follow their `issues` to correct or replace any node a publish rejects. Use this when assembling an agent so you know which building blocks exist — but only AFTER you have opened a build session with `start_agent_build_session` (the entry point to building); if you have not, call that first and carry the returned `agent_build_session_id` through the build. Next: call `get_agent_node_schema(node_type)` for the schema and worked examples of any type you intend to use.

  • Get Agent Node Schema

    Get the configuration schema for one node type. Returns `{node_type, input_schema, schema_version, description?, examples?, documentation?, docs_version?}`. The `input_schema` describes what goes in that node's `config`. `examples` may be null (the external API does not always ship worked examples) — rely on the `input_schema` and `description` to build the config. Get `node_type` from `list_agent_node_types`. When present, `documentation` carries the AUTHORITATIVE authoring shape for nodes whose configuration the `input_schema` does not fully spell out — in particular nested nodes like `iteration` (its `local_variables` and `sub_graph` envelope). Prefer `documentation` over the bare `input_schema` for those nodes; the schema alone is not enough to author them correctly. Read `input_schema` at runtime rather than hard-coding fields — the external API is in beta and the schema may change between calls. It is a real JSON Schema (typed fields, `required`, enums), not a placeholder to be ignored. For tool nodes (the `profound_*` data nodes and the third-party provider nodes) the `input_schema` is the node's full input parameter shape (typed `properties`, `required`, enums) relayed from upstream, and the response ALSO carries `output_schema` (the node's output-variable names/types) and `default_values` (the web builder's blank-form parameter defaults), taken from the static node-contract registry (upstream models no node outputs). Use `input_schema` for the parameter shape and `output_schema` for the outputs you can wire downstream. `default_values` is a blank form, not a working starting point: required literals arrive empty (`integration_id: ""`, `post_id: ""`), an empty required field fails validation, and a blank default reads `""` even where `input_schema` types the field as an integer — so fill every required field from `input_schema` rather than copying the defaults. Structural nodes carry neither `output_schema` nor `default_values`. `documentation.availability` tells you whether a node needs a connection: the platform-key research nodes (`exa_*`, `firecrawl_*`, …) run on Profound's own key and need none, while the hub-backed third-party nodes (Google Search Console, WordPress, Google Docs, Gmail) require that integration connected in the Profound app AND their `integration_id` set on the node — resolve it with `list_integrations`, set it, and publish here (an empty `integration_id` fails validation and every run). For hub-backed write nodes, `documentation.side_effect` states the run-time mutation and the non-destructive default to author (e.g. `wordpress_create_post` → `status: "draft"`). Use the schema to assemble or fix the node's `config` in your graph. Next: once the graph is assembled, call `create_agent_definition(preview=True)` to preview the plan — there is no validate-a-candidate step; validation happens on the saved draft after you apply.

  • List Agent Definition Templates

    Browse proven starting points before building an agent from scratch. First, make sure you have opened a build session: if you have not already called `start_agent_build_session` for this build, do that BEFORE this — it is the entry point to building — and carry the returned `agent_build_session_id` onto every build call. Then call this when a user wants something that resembles a common job — a visibility snapshot, a citation breakdown, a sentiment digest, a competitor comparison, a content brief — to find a proven starting point. Each template gives a plain-language `goal`, the `inputs` it needs, what it `produces`, and a `skeleton`: an ordered list of `{node_type, role, config_hints}` steps that is the workflow's proven topology. How to use a template: match the user's intent to a template's `goal` semantically — the user never picks one by name. NEVER show template ids, node types, or any other internal handle to the user; speak only in plain language. Then use the `skeleton` as your graph spine: for each step, read its `role` and `config_hints`, call `get_agent_node_schema(node_type)`, and fill that node's config. The template gives you the shape and what to put; the schema gives you the exact fields. Read each step's `config_hints` and the template's `tips`/ `portability` — they flag config details to get right (e.g. binding a Profound node's category, or terminating every conditional branch). Each template also lists `follow_ups`: sensible next nodes to extend the workflow after it works, each with a plain-language `reason` — offer them by reason, never by node type. If no template fits, build the graph yourself from `list_agent_node_types` — templates are a shortcut, not a requirement. Next: assemble the graph, then `create_agent_definition(preview=True)` to preview the plan with the user.

  • Get Agent Definition

    Read back an agent's full workflow graph so you can copy and edit it. Returns `{agent_id, version, graph}`, where `graph` is the `{nodes, edges}` object in the SAME canonical dialect that `create_agent_definition` and `update_agent_definition` accept — so you can submit it back verbatim (or after edits) without rebuilding the envelope. This is the reliable way to author a hard-to-assemble node (e.g. `iteration`): find an agent that already has one, read its graph, and copy that subgraph. `version` is `"published"` (the live, org-visible version — the default) or `"draft"` (the latest unpublished changes, visible only to its creator). Get `agent_id` from `list_agents`. Caveat — the read is NOT a verbatim mirror of the friendly authoring dialect: tool-backed nodes come back in their LOWERED `tool` form (e.g. a `profound_visibility` node reads back as `{"type": "tool", ...}`), not the friendly v1 `node_type`. The lowered shape is still re-submittable as-is, so a round-trip works; just don't expect the friendly node types you authored with. Treat `graph` as opaque data, not a typed contract: the external API is in beta and its shape may change. Next: edit the graph and call `update_agent_definition(preview=True)` to preview the change.

  • Create Agent Definition

    Create a draft agent definition — preview first, then apply on confirmation. Before calling: make sure you opened a build session with `start_agent_build_session` at the start of this build and are passing its `agent_build_session_id` here (see the param below); if you skipped it, call it now. Then assemble the `graph` from `list_agent_node_types` and `get_agent_node_schema`. There is no way to validate a graph before creating it — validation happens on the saved draft (see below). For a hard-to-assemble node (e.g. `iteration`), the reliable shortcut is to find an agent that already uses one with `list_agents`, read its graph with `get_agent_definition`, and copy that subgraph instead of building it blind. The graph envelope (REQUIRED — read this; the per-node schema does NOT describe it). `get_agent_node_schema` returns only that node's `data` payload (and for start/end just `{"type":"object"}`); it does NOT describe the wrapper every node needs. A graph is `{"nodes":[...], "edges":[...]}`. EVERY node, regardless of type, MUST carry all of: `id` (your own string handle), `type`, `data` (an object — the node title goes in `data.title`, NEVER at the top level), `input_variables` (array, may be empty), and `output_variables` (array, may be empty). Per-type rules the backend enforces: - start: `input_variables` MUST be empty; declare the workflow inputs as `output_variables[]`, each `{"variable": {"id": <id>, "name": <str>, "data_type": {"kind":"primitive","type":"string"}, "required": true}}`. - end: `output_variables` MUST be empty and `input_variables` non-empty; declare the agent outputs in `data.outputs[]` as `{"key": <name>, "variable_id": <id>}` — the field is `key`, not `name`. - llm: `data` MUST contain `provider`, `model`, and `model_parameters` (with `user_prompt`), and declare exactly one output slot whose `expected_output_id` is `"text"`. Not every `provider`/`model` string is accepted; if validation rejects the model, fall back to the known-good pair `provider: "openai"`, `model: "gpt-4o-mini"`. - edges: each needs a non-empty `id` plus `source`/`target` node ids. An edge LEAVING a conditional node selects its branch with a TOP-LEVEL `sourceHandle` equal to that case's `case_id` (e.g. `"sourceHandle": "case_false"` for the else branch) — NOT a `data.case_id`. - iteration body nodes are wired by `parentId` only — there is NO edge from the iteration node into its body (entry is implicit); chain multi-node bodies with `type:"iteration-inner"` edges; a single-node body has no edges. An edge into a body node ("crosses scopes") is rejected. The iteration COLLECTS its leaf body node's output (the body node with no outgoing `iteration-inner` edge), so the iteration's own output must mirror that leaf: its `data_type` is `array<leaf-type>` (item_type == the leaf's data_type) and its `expected_output_id` equals the leaf's (an LLM leaf is `"text"`, a code leaf is `"output"`). A mismatch is rejected. - list/array data types use `{"kind":"array","item_type":{...}}` where `item_type` is ITSELF a full type object, never a bare type name: a list of strings is `{"kind":"array","item_type":{"kind":"primitive","type":"string"}}` and a list of objects uses `item_type:{"kind":"primitive","type":"json"}`. A bare `item_type` (`{"kind":"array","item_type":"string"}`), a `{"kind":"list"}`, and a JSON-Schema `{"items":...}` wrapper are all rejected. - a multi-line free-text field uses `{"kind":"long-text"}` — `long-text` is its OWN kind, NOT a primitive type; `{"kind":"primitive","type":"long-text"}` is rejected. Single-line strings stay `{"kind":"primitive","type":"string"}`. - variable references in any field use `{{<variable-id>}}` (that variable's id only, never a node name) and that id must appear in the using node's `input_variables`. Variable ids: give each variable a plain readable id (`topic`, `article_text`) — the server assigns real UUIDs for you on create/update. Do NOT generate UUIDs or shell out to `uuidgen`. Declare the id in `output_variables[].variable.id`, then reference that SAME id everywhere it is used (`{{id}}`, `input_variables[]. variable_id`, `data.outputs[].variable_id`); matching ids are rewritten to one shared UUID. (An already-UUID id is left as-is; an id you reference but never declare stays flagged, so it still surfaces as an error.) `profound_*` data nodes (visibility, sentiment, citation_*) REQUIRE a `data.category_id` (a category UUID). The graph still validates and publishes without it, but the agent FAILS at RUN time on the first Profound node with `category_id is required` — scope is NOT resolved at the org level at run time. Set `data.category_id` on every Profound node. When your graph contains any Profound node, the result carries `category_snapshot` — the org's real `{id, name}` categories — so you can bind `data.category_id` to a literal UUID from it WITHOUT a separate `list_categories` call. The `graph_lint` runtime_warning flags any Profound node whose category_id is missing, and flags any whose category_id is set but is not one of the snapshot ids (an invented/stale UUID). The agent-builder guide resource (§2b, on the resources surface) has the full version. Date fields on Profound nodes are literal — never variables. Use a `date_range` enum (default `last_7_days`); only `answer_engine_insights`/`prompt_answer` take literal `start_date`/`end_date`. A templated date, or `date_range:"custom"` without literal `YYYY-MM-DD` dates, is rejected at create. Some tool nodes cap a text parameter's length only at RUN time (the node schema does NOT advertise the limit): notably `create_content_brief`'s `citations` (~2048 chars). Binding such a field to a large upstream output (e.g. a raw `article_research_report`) validates and publishes fine, then FAILS at run time — insert an `llm` node that condenses the text first and bind THAT node's output. Minimal valid graph — copy this shape, swapping in your own node types, ids, variable names, and prompts (start -> llm -> end). Variable ids here are plain readable names; the server assigns UUIDs on create/update: ```json { "nodes": [ { "id": "start", "type": "start", "data": {"title": "Start"}, "input_variables": [], "output_variables": [ {"variable": {"id": "topic", "name": "topic", "data_type": {"kind": "primitive", "type": "string"}, "required": true}} ] }, { "id": "summarize", "type": "llm", "data": { "title": "Summarize", "provider": "openai", "model": "gpt-4o-mini", "model_parameters": { "user_prompt": "Summarize {{topic}}" } }, "input_variables": [ {"variable_id": "topic", "required": true} ], "output_variables": [ {"variable": {"id": "summary", "name": "summary", "data_type": {"kind": "primitive", "type": "string"}, "required": true}, "expected_output_id": "text"} ] }, { "id": "end", "type": "end", "data": { "title": "End", "outputs": [ {"key": "result", "variable_id": "summary"} ] }, "input_variables": [ {"variable_id": "summary", "required": true} ], "output_variables": [] } ], "edges": [ {"id": "e1", "source": "start", "target": "summarize"}, {"id": "e2", "source": "summarize", "target": "end"} ] } ``` Flow shape — default to a SINGLE linear path (start -> ... -> end) unless the user asked for branching. More than one outgoing edge from a NON-conditional node (start/llm/tool/iteration) means the branches run in PARALLEL and each ends in its own end node — use that only when you deliberately want parallel work. For if/else routing use a conditional node, whose cases MUST be `case_true` (first, condition-bearing) and `case_false` (else), and give each terminal branch its own end node (never share one end across branches). Output variable labels must be specific, not generic (`text`/`result`/`output`/...). The result's `graph_lint` surfaces these (legal in the backend, but mishandled by the visual builder): unintended fan-out, a conditional case not named `case_true`/`case_false`, an end shared across branches, a generic output label, and a multi-node iteration body not chained with `iteration-inner` edges. Choosing the org (required, never guessed): `organization_id` MUST be the `id` returned by `list_organizations`. If the user has more than one organization, ask them which one BY NAME, then pass that org's `list_organizations` `id`. Never show or ask the user for a UUID. Preview/apply loop: - `preview=True` (the DEFAULT) persists NOTHING and validates nothing. It returns a plain-language `plan` describing what will be created. Relay the plan to the user — never show graph JSON, node types, or UUIDs — and get explicit confirmation. The result also carries `diagram`: a ready-to-render ```mermaid flowchart of the agent, in human labels (no ids/UUIDs). Show it verbatim so the user SEES the workflow before confirming — it is instant in preview, unlike the apply round-trip. - `preview=False` creates the draft, returns its `agent_id`, and includes the saved draft's `validation` report. If `validation.valid` is false, the draft exists but CANNOT be published: read `validation.issues`, call `get_agent_node_schema` to fix the offending fields and call `update_agent_definition(preview=False)` to apply the fix to the SAME draft in place (no need to re-create per fix). Call this ONLY after the user confirms. Always honor the returned `hint` for the next step — and when it lists connect URLs for hub-backed nodes, give the user each one verbatim as a full clickable link, never shortened to a settings breadcrumb. Name collision: if `list_agents` already shows an agent with the name the user asked for, do NOT silently create a duplicate — ask the user whether to pick a new name or edit the existing one via the Profound UI. Long conversations: if this chat has grown very long (roughly past 80K tokens), the graph you hold may have drifted — re-confirm the plan with the user before applying, and check `validation` on the result. Timeout/retry: if an apply call times out and you cannot tell whether the draft was created, call `list_agents` BEFORE retrying so you don't create a duplicate. Next: after `preview=False` succeeds with `validation.valid=true`, call `publish_agent_definition(preview=True)` to preview going live. There is no draft test-run — validation is the only pre-publish check, so if `validation.valid` is false, fix the issues with `update_agent_definition` before publishing. `agent_build_session_id`: REQUIRED. The opaque id that `start_agent_build_session` returned for THIS build — pass it unchanged on every build call (validate → create → update → publish) so the whole attempt is traced as one episode. If you have not opened a session yet, call `start_agent_build_session` first to get one; do not invent your own id.

  • Update Agent Definition

    Update a draft agent definition's graph — preview first, then apply on confirmation. Use this to FIX or revise an existing draft in place instead of creating a new one. After create_agent_definition(preview=False) (or publish) reports validation issues, correct the graph and call this — the draft keeps the same agent_id, no new draft is spawned. This REPLACES the whole draft graph — send the complete corrected graph, not a partial diff. The graph must follow the SAME envelope as create_agent_definition (see its description, or the agent-builder guide resource §2b on the resources surface): every node needs id/type/data (title in data.title)/input_variables/output_variables, start/end/ llm per-type rules, edges need id, variable refs are {{variable-id}} (use plain readable ids — the server assigns UUIDs; do NOT generate them). The same authoring gotchas apply: literal dates on Profound nodes (never variables), iteration body nodes wired by `parentId` only (no container->body edge; `iteration-inner` edges between siblings, and the iteration's output must mirror its leaf body node — `array<leaf-type>` item_type and the leaf's `expected_output_id`), conditional cases `case_true`/`case_false`, `long-text` as its own data-type kind (not a primitive type), run-time text caps on some tool params (e.g. `create_content_brief` `citations` ~2048 chars — condense large upstream output first), and specific (not generic) output labels. Inserting a node = REWIRE, not append. To put a new node N between existing nodes A and B, send the full graph with the A->B edge REMOVED and edges A->N and N->B ADDED. Do NOT keep A->B and also add a new branch off A (or off start) — that creates a second, parallel branch that runs independently and ends in its own end node, which is almost never what an edit intends. More than one outgoing edge from a NON-conditional node (start/llm/tool/iteration) means PARALLEL execution; only a conditional node should branch, and only for if/else routing. If the apply result carries `graph_lint` warnings, you likely appended instead of rewiring — fix the edges and re-apply. Preview/apply loop: - preview=True (the DEFAULT) persists NOTHING and validates nothing. It returns a plain-language plan. Relay it to the user — never show graph JSON, node types, or UUIDs — and get explicit confirmation. The result also carries `diagram`: a ready-to-render ```mermaid flowchart of the edited graph, in human labels (no ids/UUIDs). Show it verbatim so the user SEES the new shape before confirming. preview does not verify the agent_id exists; an unknown or stale id only fails when you apply (preview=False). - preview=False replaces the draft's graph and returns its re-validated `validation` report inline. If validation.valid is false, the draft still exists with the new graph but CANNOT be published: read validation.issues, call get_agent_node_schema to fix the offending fields, and call update_agent_definition(preview=False) again (same draft). Loop until valid. Always honor the returned `hint` — and when it lists connect URLs for hub-backed nodes, give the user each one verbatim as a full clickable link, never shortened to a settings breadcrumb. Next: once validation.valid is true, call publish_agent_definition(preview=True) to preview going live. `agent_build_session_id`: REQUIRED. The opaque id that `start_agent_build_session` returned for THIS build — pass it unchanged on every build call (validate → create → update → publish) so the whole attempt is traced as one episode. If you have not opened a session yet, call `start_agent_build_session` first to get one; do not invent your own id.

  • Publish Agent Definition

    Publish a draft agent definition so the agent goes live — preview first, then apply. This is the final step of building an agent. Publishing flips it from a private draft to a live, runnable state. Publishing is the authoritative STRUCTURAL validation gate. The backend validates the graph's structure and output wiring as part of publishing, BEFORE anything changes, so a structurally broken draft is REJECTED and stays a draft. A rejected `preview=False` does NOT raise: it returns a `validation` report with `valid=false` whose `issues` name the offending node and field. The result's `new_status` stays `draft`. Run prerequisites are NOT publish gates: a graph missing a hub-backed node's `integration_id` or a Profound node's `category_id` publishes fine and then fails at run — `validate_agent_definition` and the `graph_lint` advisories on create/update flag those, so clear them BEFORE publishing. On a rejected publish: read `validation.issues`, call `get_agent_node_schema` for the flagged node's type, fix that field and apply it with `update_agent_definition(preview=False)`, then publish again. Loop until the publish succeeds. Validate first to save a round-trip: `create_agent_definition(preview=False)` already returns a `validation` report, and `validate_agent_definition(agent_id)` re-checks an existing draft. But that draft-level check is LENIENT (topology only) and can report `valid=true` for a graph publish then rejects — so the publish result is the final word, not the draft validation. Preview/apply loop: - `preview=True` (the DEFAULT) changes NOTHING and does not validate. It returns the would-be transition (`previous_status` → `new_status`) so you can confirm with the user in plain language. Never show graph JSON, node types, or UUIDs — tell the user what publishing means (the agent becomes live and invocable), and get explicit confirmation. - `preview=False` performs the publish (and applies the validation gate). Call this only after the user confirms. If it returns `validation.valid=false`, fix the named node/field and retry per the loop above. Always honor the returned `hint` for the next step. Publishing is safe to repeat: if the agent is already published, this is a no-op transition (it does not create a duplicate or error). Next: after `preview=False` succeeds (no `validation` failure), the agent is live and can be invoked with `run_agent`. `agent_build_session_id`: REQUIRED. The opaque id that `start_agent_build_session` returned for THIS build — pass it unchanged on every build call (validate → create → update → publish) so the whole attempt is traced as one episode. If you have not opened a session yet, call `start_agent_build_session` first to get one; do not invent your own id. The result also carries `diagram`: a ready-to-render ```mermaid flowchart of the agent's graph (node labels only, no ids). Show it to the user — on preview as a final look before go-live, on a clean apply as a receipt of what shipped. It is null when the graph has no drawable nodes.

  • List Agents

    List agents defined for the authenticated organization. Agents take structured inputs and produce structured outputs. This list does NOT include each agent's input schema. To run one you must first call `get_agent` on it: run inputs are keyed by the schema's opaque UUID property keys (not the human titles), so `get_agent` → build inputs → `run_agent` is the required sequence — the `get_agent` step cannot be skipped. ``statuses`` filters by lifecycle state — typically ["published"] for the end-user surface, or ["draft"] for editing. Defaults to ["published"].

  • Get Agent

    Get details of an agent, including its `input_schema`. The `input_schema` field is a JSON Schema describing required and optional inputs. The LLM should construct an `inputs` dict matching this schema before calling `run_agent`. Property keys in `input_schema.properties` are opaque UUIDs, not human-readable names. The display name for each field lives in that property's `title`. When building `inputs` for `run_agent`, the dict keys must be the UUIDs (not the titles).

  • Run Agent

    Start an agent run. Triggers a run of the named agent. The `inputs` dict must match the agent's `input_schema` (fetch with `get_agent` first). The dict keys must be the UUID property keys from `input_schema.properties`, not the human-readable `title` strings. Agent runs consume Magi compute and will use agent credits, so only call this when the user intends to run the agent. Note: schema validation is performed by Magi at run time, not at request time. An `inputs` dict that omits required fields will be accepted here and the run will surface a terminal `failed` status via `get_agent_run` with an `error` describing the missing field. Returns immediately with a `run_id` and `status="queued"`. Poll `get_agent_run` to check progress; terminal statuses are `succeeded`, `failed`, `cancelled`, `skipped`. The result's `hint` names the next step (how to read the run back, including seeing each step's output).

Ship Profound agents in minutes

Connect any AI agent to 100+ MCP servers, zero setup.
Book a demo
Gradient