Avo MCP tools reference
Every tool except list_workspaces and describe_tool operates on a workspace. Pass workspaceId as a parameter; stdio clients can also set the WORKSPACE_ID environment variable.
Each tool lists the OAuth scope it requires. Write tools (workflow, save_items) require the write scope, which is requested as a separate consent step on first use.
The MCP exposes a small set of tools mapped to agent intents:
| Intent | Tool | Scope |
|---|---|---|
| Entry point — find your workspace IDs | list_workspaces | read |
| Learn the shape — fetch the exact contract for the call you are about to make | describe_tool | none |
| Discover — find items by meaning or by structural filter | search | read |
| Understand — full details for an event, property, branch, source, etc. | get | read |
| Change — create, update, archive, or unarchive items on a branch | save_items | write |
| Progress — create a branch, update its description, pull main, set a source language, import | workflow | write |
Two more tools round out the surface: list_branches (transitional, stays available while branch enumeration is folded into search) and give_feedback (send product feedback about the MCP to the Avo team — call it when the MCP could not do what you set out to do).
How discovery works
The advertised tool list is deliberately small: the save_items input schema is an index, not the full contract. Many MCP clients silently drop tools whose schema is too large, so the per-type field contracts are served on demand by describe_tool instead of being advertised up front. The flow an agent should follow:
- Connect. The server’s
instructions(what your client receives oninitialize) are the capability map — every tool, its item types and actions, and what to call next. describe_tool()— the same capability map plus a short “Designing tracking” guide.describe_tool(tool:"save_items", type:"<type>", op:"<op>")— the exact fields for the item type and operation you are about to write.- Write with
save_items.
If a save_items item carries an unknown field or an out-of-domain value, the error echoes that type’s contract, so an agent can self-correct without a second describe_tool call.
Item type vocabulary
save_items, search, and get share one snake_case entity vocabulary:
| Canonical type | Used by |
|---|---|
event, property, metric, category | search, get, save_items |
event_variant, property_bundle | search, get, save_items |
source, destination, group_type, gateway | search, get, save_items |
journey | search, get (read-only) |
workspace_config | get (takes no id) |
branch | get |
Deprecated aliases. The camelCase spellings eventVariant, propertyBundle, groupType, and workspaceConfig are still accepted for one release as aliases of the snake_case types above. They will be removed — use the snake_case spellings in anything you write today.
list_workspaces
Scope: read
List the Avo workspaces the authenticated user has access to. Call this first to discover workspace IDs before invoking any workspace-scoped tool.
Parameters
None.
Returns
One row per workspace: name, workspace ID, and the user’s role.
Examples
Discover the workspaces you can access
Prompt: “What Avo workspaces do I have access to?”
Claude calls list_workspaces with no parameters and uses the returned workspaceId to scope every other tool call in the session.
describe_tool
Scope: none — read-only, rendered locally by the MCP server. No workspace data, no branch, no authentication, no side effects.
The on-demand schema and contract index. Call it to get the exact shape of the call you are about to make: the capability map, the save_items item envelope, the per-type field contract for a given operation, the workflow action parameters, and the search / get vocabularies.
Parameters
All parameters are optional strings. There are no nested objects or unions.
| Parameter | Values | Description |
|---|---|---|
tool | list_workspaces, describe_tool, search, get, save_items, workflow, give_feedback, list_branches | Which tool to describe. Omit for the capability map. |
type | An item type (see vocabulary) | For save_items: render that type’s field contract. |
action | create_branch, update_branch_description, pull_main, set_source_language, import | For workflow: render that action’s parameters. |
op | create, update, archive, unarchive | For save_items: narrow the fields table to one operation. |
format | full (default), concise | concise returns the fields table only (no placement note, example, or limits line). |
Returns
Plain text, shaped by what you asked for:
describe_tool()— the capability map (the same text as the serverinstructions) followed by a “Designing tracking” guide and the link to the avo-mcp plugin. The guide only lives in this response, not in the advertised instructions.describe_tool(tool:"save_items")— the item envelope explanation and a pointer to call again with atype.describe_tool(tool:"save_items", type:"property")— the fields table for that type: one line per field with its name, JSON type, whether it is required, which ops accept it, and a one-line doc. Fields with a closed value set list the accepted values (One of: …); thesetobject lists its accepted nested keys. Addop:"update"to narrow to one operation.describe_tool(tool:"workflow")— the list of actions. Addaction:"<action>"for that action’s parameters and an example call.describe_tool(tool:"search")/describe_tool(tool:"get")— each read tool’s item-type vocabulary, and forget(type:"branch")the acceptedincludevalues.- Unknown
typeoraction— an error that lists the valid values, so an agent can self-correct from the message alone.
Examples
Learn what a property update accepts before writing one
Prompt: “Rename the product_id property and add two allowed values.”
Before its first property write of the session, Claude fetches the update contract for properties and reads that renames go in set.name while allowed-value changes go in the top-level addAllowedValues.
{
"tool": "save_items",
"type": "property",
"op": "update"
}The response (abridged):
Fields for property (op: update):
- propertyId (string, required; update/archive/unarchive) — The property's id. Required for update/archive/unarchive.
- addAllowedValues (array; create/update) — Create/update (string property): allowed values to add.
- removeAllowedValues (array; update) — Update only: allowed values to remove …
- isList (boolean; update) — Update: toggle whether the property holds a list of values.
- addCategories (array; update) — Update: category names to attach …
- set (object; update) — Update only: scalar field changes … Nested keys (set.<key>): nameSuffix, description, propertyType, sendAs, name, platform, programmingLanguage, libraryName, libraryDestination, analyticsTool, triggers.
Placement: scalar renames go in set.*; collection deltas at the item top level.
Limits: up to 50 items per call; $tmp: refs resolve within one call.Get the capability map
Prompt: “What can the Avo MCP do?”
Claude calls describe_tool with no parameters and summarizes the capability map for the user.
search
Scope: read
Find tracking plan items in one of two modes — the mode is selected automatically by which parameters you pass. Combining query with structural filters returns 400. An empty call (no query, no filter, no branch scope) is rejected. For ID-based lookups use get.
- Semantic search — pass
queryto find items by meaning across events, properties, metrics, categories, property bundles, and event variants. Avo embeds each item with OpenAI embeddings and runs a vector-similarity search at query time, so"user signed up"matchesAccount CreatedorRegistration Completedeven when no keyword overlaps. - Structured listing — omit
queryand pass filters to enumerate exact matches with keyset pagination. PassitemTypealone (no query, no filter) to enumerate sources, destinations, group types, gateways, or journeys.
Semantic search requires Avo Intelligence Smart Search to be enabled in your workspace. Workspace admins can enable it in Workspace Settings. If you don’t have admin access, ask a workspace admin to enable it. Filter-mode listing does not require Smart Search.
Parameters
Shared across modes
| Parameter | Required | Description |
|---|---|---|
itemType | No | Filter by type: event (default), property, metric, event_variant, category, property_bundle, source, destination, group_type, gateway, or journey. camelCase aliases are accepted but deprecated. |
maxResults | No | Semantic mode: 1–20, default 10. Filter mode: 1–500, default 10. |
workspaceId | No | Workspace ID. Repeat it on every page when paginating. |
Semantic mode (pass query)
| Parameter | Required | Description |
|---|---|---|
query | Yes | Natural language search query. |
Semantic search is performed against the main branch only. The semantic index may lag slightly for very recently created or updated items.
Filter mode (omit query, pass any of the filter fields)
| Parameter | Required | Description |
|---|---|---|
tags | No | Filter by tag. |
categories | No | Filter by category name. |
sources | No | Filter by source name. Does not apply to metrics. |
eventNames | No | Filter by event name. With itemType: "property", returns properties on those events. |
variantNames | No | Filter by event variant name. |
properties | No | With itemType: "event", returns events referencing any of these properties. |
includeVariants | No | With itemType: "event", interleaves each event’s variants in the result set. |
stakeholders | No | Filter by stakeholder. |
owners | No | Filter by owner. |
destinations | No | Filter by destination name. |
type | No | With itemType: "event", filter by event type. |
customField | No | Filter by custom-field name. Resolve valid names from get with type: "workspace_config". |
pii | No | Filter by PII type. Resolve valid types from get with type: "workspace_config". |
nameMapping | No | Filter by destination-name-mapping. Tagged object: { kind: "any" } (items with any mapping) or { kind: "matchesAny", names: [...], includeNoMapping?: bool } (items whose mapped name is in names; with includeNoMapping: true items without a mapping rule also pass). Omitting nameMapping means no filter on mapping. |
checkpoint | No | Event and property listings only. Narrow to items whose data reaches one pipeline location. Tagged object by kind: { kind: "source", sourceId }, { kind: "gatewayInput", gatewayId, inputId }, { kind: "gateway", gatewayId }, { kind: "gatewayOutput", gatewayId, outputId }, or { kind: "destination", destinationId }. An unwired or dangling checkpoint returns no items plus a warning, never an error. |
branchId | No | Branch to enumerate on. Defaults to main. |
branchName | No | Alternative to branchId. |
pageToken | No | Pagination token from a previous response. |
Multiple values inside one array are OR’d; values across different filter keys are AND’d.
Pagination
Filter-mode responses include a nextPageToken when more results are available, and end with a “Next page” note. To fetch the next page, call search again with the same parameters — workspaceId, itemType, every filter, the branch, maxResults, and checkpoint when you used it — plus pageToken. The token alone is not sufficient: omitting maxResults reverts the page size to the default, and omitting workspaceId can resolve the next page against a different workspace.
Returns
Ranked results with rank, name, type, item ID, relevance percentage, and a truncated description (80 characters). Filter-mode responses include a nextPageToken when more results are available.
{
"results": [
{
"rank": 1,
"name": "Account Created",
"itemType": "event",
"itemId": "evt-9f2b…",
"relevance": 0.91,
"description": "Sent when a new account is successfully created."
},
{
"rank": 2,
"name": "Signup Started",
"itemType": "event",
"itemId": "evt-3c11…",
"relevance": 0.87,
"description": "Sent when the user opens the signup screen."
}
]
}Examples
Find events by meaning (semantic)
Prompt: “What events do we have for signup?”
Claude passes the user’s phrasing directly to query. Semantic mode returns events whose meaning matches the query, even when the exact words differ — "user signed up" will match Account Created or Registration Completed.
{
"query": "user signed up",
"itemType": "event",
"maxResults": 5
}List events using a specific property (filter)
Prompt: “Which events on iOS use the product_id property?”
Filter mode is selected by omitting query. Multiple filter keys are AND’d, so this returns only events that reference product_id and are tracked from the iOS source.
{
"itemType": "event",
"properties": ["product_id"],
"sources": ["iOS"],
"maxResults": 50
}Enumerate the workspace’s sources
Prompt: “Which sources do we have?”
{
"itemType": "source"
}Common errors
queryand structural filters combined — returns 400. Choose one mode.- Empty call (no
query, no filter, no branch scope) — rejected. - Smart Search not enabled in the workspace — semantic mode fails; fall back to filter mode or
get. - Workspace access denied.
get
Scope: read
Get item details for any of four type families:
- Tracking-plan items —
event,property,metric,category,property_bundle,event_variant. Look up byidor exactname. For events,includePropertyDetails: truereturns each property’s type, constraints, and allowed values inline.event_varianttakes the base event’sidplusvariantId. - Workspace metadata —
source,destination,group_type,gateway. Passidornamefor one item; to enumerate the list, usesearchwith thatitemTypeand no query. - Workspace config and journeys —
workspace_config(takes noid) returns naming/casing rules, custom-field definitions, and the PII type list; custom-field and PII-type names plug straight intosearch’scustomFieldandpiifilters.journeyrequires anid(enumerate withsearch(itemType:"journey")). - Branches —
branch. Identify withbranchIdorbranchName. Useincludeto pick content.includedefaults to["overview"].
Defaults to the main branch when no branch is specified.
Parameters
| Parameter | Required | Description |
|---|---|---|
type | Yes | Item type. One of: event, property, metric, category, property_bundle, source, destination, group_type, gateway, event_variant, workspace_config, journey, branch. camelCase aliases are accepted but deprecated. |
id | Varies by type | The item’s unique ID. Required for tracking-plan items unless name is provided; required for journey. Not used by workspace_config. |
name | Varies by type | Exact name match. Alternative to id for most types. May return multiple matches for ambiguous names (especially properties) — use search for fuzzy lookup. |
variantId | For event_variant | The variant ID. Combined with id (the base event ID). |
include | For branch | Array of branch facets to return: overview, all_changes, event_changes, property_changes, code_snippets, implementation_guide. Defaults to ["overview"]. Values are unioned. |
sourceId | When include contains code_snippets | Source ID to scope the code snippets to. |
checkpoint | No | Events and properties only. Annotates whether the item’s data reaches one pipeline location — same tagged-object shape as the search checkpoint filter. Never filters the item out, never errors. |
branchId | No | Branch to look up on. Defaults to main. branchId takes precedence over branchName. |
branchName | No | Alternative to branchId. |
includePropertyDetails | No | Events only. When true, includes full property definitions (type, constraints, allowed values). Defaults to false, which returns only property ID + name references. |
includeArchived | No | When true (default), includes archived items in results. When false, only active items. |
workspaceId | No | Workspace ID |
Returns
Full details for the item, shaped per item type.
type: "event" \| "property" \| "metric" \| "category" \| "property_bundle" \| "event_variant"— the item’s full definition. Events, properties, and event variants include owner and stakeholders.type: "source" \| "destination" \| "group_type" \| "gateway"— a single entity.type: "branch"— the facets requested viainclude.overviewreturns resolved emails for the creator, reviewers, and collaborators, branch status, impacted source IDs, and description.all_changes(or the narrowerevent_changes/property_changes) returns the structured diff (new, modified, and deleted items with their properties and descriptions).implementation_guidereturns the diff organized for an implementer.code_snippetsreturns per-event code diffs for the source named insourceId— exact unified diffs for Avo Codegen sources and illustrative pseudocode for manually-instrumented sources.type: "workspace_config"— the workspace’s event/property naming conventions and casing rules, plus custom field definitions and the list of recognized PII types. Use this before proposing new events or properties so names match the workspace’s audit rules.type: "journey"— the journey’s definition.
Examples
Look up an event by exact name
Prompt: “How is the Account Created event defined?”
Claude calls get with the exact name and includePropertyDetails: true so the response includes each attached property’s type, constraints, and allowed values. Useful when the agent already knows the canonical name and wants the full schema in one call.
{
"type": "event",
"name": "Account Created",
"includePropertyDetails": true
}Read what changed on a branch
Prompt: “What’s on the checkout-v2 branch — and can you show me the iOS code diff?”
Claude calls get with type: "branch" and combines all_changes and code_snippets in include. sourceId scopes the diff to a single source.
{
"type": "branch",
"branchName": "checkout-v2",
"include": ["all_changes", "code_snippets"],
"sourceId": "src-ios"
}Common errors
- Item not found.
- Ambiguous name (returns multiple matches — narrow by ID).
sourceIdmissing whenincludecontainscode_snippets.- Workspace access denied.
save_items
Scope: write · Destructive: archive ops
Write access is in general beta — enabled for every workspace, no need to request access. Email support@avo.app if you hit anything unexpected.
Destructive operations. op: "archive" archives the target item on the branch. Property archives cascade — references on every event that uses the property are also removed. Archives are reversible (op: "unarchive", or from the Avo web app), but the cascade means a single call can touch many events.
Batch create, update, archive, and unarchive tracking-plan items on a branch: events, properties, event variants, property bundles, metrics, categories, sources, destinations, group types, and gateways. A single call can mix item types and operations, and can cross-reference new items via temporary IDs.
Before your first write of a type in a session, call describe_tool with tool:"save_items", the type, and the op. The advertised save_items schema only describes the item envelope; the per-type fields live in the describe_tool response, and the tables below are a snapshot of it.
Parameters
Top-level
| Parameter | Required | Description |
|---|---|---|
branchId | Yes | The branch to write to. Get it from workflow (action: "create_branch"). Writes on main are rejected. |
items | Yes | Array of item envelopes to apply (see below). |
onReviewedBranch | No | Acknowledgement for writing to a branch that is already in review. Omitted → the write is rejected so approvals aren’t silently invalidated. { kind: "revertToDraft" } reverts the branch to Draft and applies the edits; { kind: "reject" } is the default. |
workspaceId | No | Workspace ID |
The request is capped at 50 items per call.
Item envelope
Every item has the same six-key shape. The type-specific content goes inside fields:
{ "op": "create", "type": "event", "id": "…", "name": "…", "tempId": "…", "fields": { } }| Key | Type | Notes |
|---|---|---|
op | "create" | "update" | "archive" | "unarchive" | Defaults to "create". Every op applies to every type. |
type | string | Required. One of event, property, event_variant, property_bundle, metric, destination, source, group_type, category, gateway. camelCase aliases are accepted but deprecated. |
id | string | The identity ID for update / archive / unarchive on single-ID types — see the table below. You may pass it here or as the type’s ID field inside fields; passing both with different values is rejected. event_variant has a compound identity and passes baseEventId + variantId inside fields instead. |
name | string | Required on every create. Cosmetic on other ops. |
tempId | string | Create only. A temporary handle (letters, digits, _, -; max 64 characters) for cross-referencing inside the same call. Reference it elsewhere as "$tmp:<tempId>". |
fields | object | The type-specific fields for this (type, op) — exactly what describe_tool renders. Omit or pass null when the op needs nothing beyond the identity. |
Identity ID per type (for update / archive / unarchive):
| Type | ID field |
|---|---|
event | eventId |
property | propertyId |
metric | metricId |
category | categoryId |
property_bundle | propertyBundleId |
source | sourceId |
destination | destinationId |
group_type | groupTypeId |
gateway | gatewayId |
event_variant | baseEventId + variantId (both in fields, on every op) |
Inside fields, three placement rules apply to every type:
- Scalar edits go in
set(update only).setis a nested object with a closed key set:name,description,nameSuffix,propertyType,sendAs,platform,programmingLanguage,libraryName,libraryDestination,analyticsTool,triggers. Not every key applies to every type; unknown keys are rejected.set.sendAsis accepted only on create — a property’ssendAsis immutable after creation. - Collection changes stay at the top level of
fields—addProperties/removeProperties,addCategories/removeCategories,addAllowedValues/removeAllowedValues, and every otheradd*/remove*/clear*field. descriptionis create-only, and only for types whose create reads it:event,property,event_variant,property_bundle,metric,category. Acreatefor asource,destination,group_type, orgatewaythat carriesdescriptionis rejected. On update, useset.description.
Validation is strict. An unknown key in fields, a field the current op doesn’t accept (set on a create, description or tempId on an update, removeAllowedValues on a create), or a value outside a field’s domain is rejected up front. The error names the (type, op) and echoes the type’s field contract.
Field contracts by type
The tables below are the describe_tool(tool:"save_items", type:"<type>") output at the time of writing. The live response is authoritative. Fields marked create/update are accepted on both ops; the identity ID field is required on update / archive / unarchive. tempId (create only) and set (update only) apply to every type and are omitted from the tables.
event
| Field | Ops | Notes |
|---|---|---|
eventId | update, archive, unarchive | The event’s ID (or $tmp: of a same-batch create). |
description | create | Event description. |
sources | create | Source IDs to include the event in. ID or $tmp: only. |
properties | create | Property IDs to attach. ID or $tmp: only. |
propertyBundles | create | Property bundle IDs to attach. ID or $tmp: only. |
actions | create | Initial action types (identify, page, revenue, …). Omit for logEvent only. |
nameComponents | create | Advanced-naming workspaces: per-building-block name values. |
tags | create | Tag names to attach (literal strings). |
addProperties / removeProperties | update | Property IDs (or $tmp:) to add / remove. |
addSources / removeSources | update | Source IDs to include / exclude. |
setSourceCodegen | update | Set the per-source include-in-codegen flag. |
addSourceDestinations / removeSourceDestinations | update | Link / unlink (source, destination) pairs on the event. |
setActions | update | Replace the event’s action types. |
customFieldValues | update | Set or clear per-custom-field values. |
addCategories / removeCategories | update | Category names (or $tmp: to a same-batch category). The event must already exist — attaching a category to an event created in the same batch is rejected; do it in a follow-up call. |
addTags / removeTags | update | Tag names to attach / detach. |
addGroupTypes / removeGroupTypes | update | Group type names (name or $tmp:, not raw IDs). |
addPropertyBundles / removePropertyBundles | update | Property bundle IDs to attach / detach. |
nameMappings | create/update | Per-destination name mapping entries — see Name mappings. |
owner, stakeholders | create/update | See Owner and stakeholder fields. |
set.name, set.description | update | Rename (renaming to a name already held by another live event is rejected) / new description. |
property
| Field | Ops | Notes |
|---|---|---|
propertyId | update, archive, unarchive | The property’s ID. |
description | create | Property description. |
propertyType | create | string, int, long, float, bool, object, any (aliases like integer, boolean, double accepted). Change later with set.propertyType. |
sendAs | create | event, user, or system. Immutable after create. |
nestedProperties | create | Object property: child-property slots { propertyId, … }. |
tags | create | Tag names to attach. |
addAllowedValues | create/update | String property: allowed values to add. |
removeAllowedValues | update | Allowed values to remove. Rejected on a create. |
eventConfigs | create/update | Per-event property settings — see Per-event property settings. Max 50 entries. |
customFieldValues | create/update | Set or clear per-custom-field values. |
pii | create/update | The property’s PII state: { kind: "declared" | "notPii" | "unset" }. |
nameMappings | create/update | See Name mappings. |
isList | update | Boolean — toggle between scalar (false) and list (true). |
addNestedProperties / removeNestedProperties | update | Object property: child-property slots to add / child-property IDs to detach (live IDs only, no $tmp:). |
addPropertyRegex / removePropertyRegex | update | Set the global regex rule { regex, testValue } / pass true to remove it. |
addEventRegexOverride / removeEventRegexOverride | update | Set an event-specific regex override { eventId, regex, testValue } / the event ID whose override to remove. |
addCategories / removeCategories | update | Category names (or $tmp:). The property must already exist. |
addTags / removeTags | update | Tag names to attach / detach. |
owner, stakeholders | create/update | See Owner and stakeholder fields. |
set.name, set.description, set.propertyType | update | Rename / new description / new type. |
event_variant
| Field | Ops | Notes |
|---|---|---|
baseEventId | all | The parent event’s ID (or $tmp:). Required on every op. |
variantId | all | The variant’s ID — you supply it on create (must not contain .). Required on every op. |
description | create | Variant description. |
nameSuffix | create | Suffix appended to the parent event name (e.g. buy_now produces click / buy_now). Change later with set.nameSuffix. |
attachProperties | create/update | Property IDs (or $tmp:) to attach to this variant. |
overrides | create | Component-level overrides { propertyId, pinned } or { propertyId, allowed }. |
bundleOverrides | create | Bundle IDs to attach to this variant. |
addComponentOverrides / removeComponentOverrides | update | Component override specs to add or replace / property IDs whose overrides to clear. |
removeProperties | update | Property IDs to set as explicit not-on-variant overrides. |
clearAttachedProperties | update | Property IDs whose attachment override is cleared (inherit from base). |
addSourceOverrides / removeSourceOverrides / clearSourceOverrides | update | Source-level overrides to force-add / force-remove / reset to inherit. |
addBundleOverrides / removeBundleOverrides / clearBundleOverrides | update | Bundle IDs to force-add / force-remove / reset to inherit. |
addVariantPropertyRegex | update | Set a variant regex override { propertyId, regex, testValue }. |
removeVariantPropertyRegex | update | Property ID set to explicit no-regex on the variant. |
clearVariantPropertyRegexOverride | update | Property ID whose variant regex override is cleared (inherit). |
owner, stakeholders | create/update | See Owner and stakeholder fields. |
set.nameSuffix, set.description, set.triggers | update | New suffix / description / replace the trigger list. |
The override surface is a three-state lattice — add* / remove* / clear* — for attached properties, source overrides, bundle overrides, and regex overrides. Use add* / remove* for explicit overrides; use clear* to fall back to the base event.
property_bundle
| Field | Ops | Notes |
|---|---|---|
propertyBundleId | update, archive, unarchive | The bundle’s ID. |
description | create | Bundle description. |
addProperties | create/update | Property IDs (or $tmp:) to add to the bundle. |
attachToEvents | create | Event IDs (or $tmp:) to attach the new bundle to. |
removeProperties | update | Property IDs to remove from the bundle. |
set.name, set.description | update | Rename / new description. |
Bundle-to-event attachment after creation is managed from the event side via addPropertyBundles / removePropertyBundles on an event update.
metric
| Field | Ops | Notes |
|---|---|---|
metricId | update, archive, unarchive | The metric’s ID. |
description | create | Metric description. |
metricType | create | One of Funnel, EventSegmentation, Proportion, Retention, CustomEvent, Cohort. Immutable after create — archive and recreate to change. |
items | create | Non-cohort metrics: array of metric items. Each is { kind: "Event", id, eventId, where?, groupBy? }, { kind: "EventVariant", id, baseEventId, variantId, where?, groupBy? }, or { kind: "Metric", id, metricId }. id is a caller-supplied local key used to address the item in later updates; eventId / baseEventId / metricId accept $tmp:. where entries are { propertyId, operator, values } with a non-empty values. |
cohortConditions | create | Cohort metrics: array of conditions — { kind: "Action", id?, eventId, performed, frequency, timeWindow } or { kind: "Variable", id?, propertyId, binOp, literals }. |
setName / setDescription | update | Rename / new description (metrics use these top-level fields). |
addItems / updateItems / removeItems | update | Non-cohort metrics: add items / replace an item’s where and groupBy by ID / remove item IDs. |
addCohortConditions / updateCohortConditions / removeCohortConditions | update | Cohort metrics: manage the condition list. |
addCategories / removeCategories | update | Category names to attach / detach. |
category
| Field | Ops | Notes |
|---|---|---|
categoryId | update, archive, unarchive | The category’s ID. |
description | create | Category description. |
set.name, set.description | update | Rename / new description. |
Category membership is managed from the member side: addCategories / removeCategories on event, property, and metric updates.
source
| Field | Ops | Notes |
|---|---|---|
sourceId | all | Required on update / archive / unarchive. On create, supply sourceId or a tempId. |
platform | create (required) | The development platform this source runs on. Change later with set.platform. |
programmingLanguage | create | The source’s programming language. Change later with set.programmingLanguage (or workflow set_source_language). |
libraryName / libraryDestination | create | Codegen library name / output path. Change later via set.*. |
connectDestinations | update | Destination IDs (or $tmp:) to connect for codegen routing on the source. |
disconnectDestinations | update | Destination IDs to disconnect. Destructive — cascades to every event routing it on this source (and inheriting variants). |
set.name, set.platform, set.programmingLanguage, set.libraryName, set.libraryDestination | update | Scalar edits. |
A create for a source does not accept description.
destination
| Field | Ops | Notes |
|---|---|---|
destinationId | all | Required on update / archive / unarchive. On create, supply destinationId or a tempId. |
analyticsTool | create (required) | The analytics platform this destination connects to. Change later with set.analyticsTool. |
includeUserPropsWithEventProps | create/update | Boolean. Defaults to false on create. |
disabledByDefault | create/update | Boolean — new events are disabled for this destination by default. Defaults to false on create. |
apiKey | update | Set or remove an API key for an environment { kind, env, value? }. |
set.name, set.analyticsTool | update | Scalar edits. |
A create for a destination does not accept description.
group_type
| Field | Ops | Notes |
|---|---|---|
groupTypeId | all | Required on update / archive / unarchive. On create, supply groupTypeId or a tempId. |
set.name | update | Rename. |
A create for a group type does not accept description. Attach group types to events with addGroupTypes on an event update.
gateway
Gateways are gated by a per-workspace feature. A gateway write returns 403 when the workspace does not have gateways enabled.
| Field | Ops | Notes |
|---|---|---|
gatewayId | update, archive, unarchive | The gateway’s ID. A create is server-minted, so no gatewayId is supplied then. |
inputs | create | Input checkpoints, each { sourceId? } (ID or $tmp:; omit or null for an unwired input). |
outputs | create | Output checkpoints, each { destinationId? } (ID or $tmp:; omit or null for an unwired output). |
addInputs / removeInputs | update | Input checkpoints to add, each { sourceId? } / input-checkpoint IDs to remove. |
addOutputs / removeOutputs | update | Output checkpoints to add, each { destinationId? } / output-checkpoint IDs to remove. |
setInputOrigins / setOutputTargets | update | Re-point existing checkpoints: { inputId, sourceId? } / { outputId, destinationId? }. |
outputId | update | Apply this item’s transformation keys at one output checkpoint (an output ID from get, or $tmp: of a same-batch outputs / addOutputs entry) instead of at the gateway itself. Has no meaning without transformation keys. |
addTransformationProperties | update | Upsert per-property overrides at the gateway (or at outputId). Each { propertyId, pinned?, absence?, disallowedValues?, regex?, nameMapping? }; a bare { propertyId } inherits every dimension. pinned / regex null = explicitly none; nameMapping is { kind: "set" | "clear" }. disallowedValues are add-only and must already be allowed values of the property. |
removeTransformationProperties | update | Property IDs to stop sending from this checkpoint on. |
clearTransformationProperties | update | Property IDs whose overrides are removed at this checkpoint (back to inherit). The only way to drop a disallowed value: clear, then re-add the overrides you still want. |
addTransformationPropertyBundles / removeTransformationPropertyBundles / clearTransformationPropertyBundles | update | Property-bundle IDs to include / stop sending / reset to inherit at this checkpoint. $tmp: works for a bundle created in the same batch. |
setTransformationEventNames | update | Rename events as sent from this checkpoint on. Each { eventId, sendAs }. |
removeTransformationEventNames | update | Event IDs whose send-as rename is removed (back to the event’s own name). |
set.name | update | Rename. |
A property ID may appear in only one of add / remove / clear transformation fields per item. A create for a gateway does not accept description.
Name mappings
nameMappings is accepted on event and property items, on create and update. Each entry is { destination, name }, where destination is { kind: "allDestinations" } or { kind: "destination", destinationId: "…" }. name must be a non-empty string when present; pass name: null (or omit it) to remove the mapping for that destination. On a create there is nothing to remove yet, so a name: null entry is a harmless no-op. Multiple entries are allowed, one per destination scope.
Owner and stakeholder fields
Branch-independent. Owner and stakeholder assignments take effect immediately workspace-wide, even if the branch is later discarded. Discarding the branch will not roll back these changes. Treat these fields as out-of-branch mutations, not draft edits.
owner and stakeholders are accepted inside fields on event, property, and event variant items (both create and update):
| Field | Notes |
|---|---|
owner | { action: "set", stakeholder } sets the owning stakeholder; { action: "clear" } removes it. Stakeholders are created in the Avo web app — the MCP does not create them. |
stakeholders | { add?: [...], remove?: [...] } — stakeholder references to add or remove as collaborators. |
Merging categories
The MCP has no dedicated “merge categories” op. To merge category A into category B, send a single save_items batch containing:
- An
updateitem for every event, property, and metric inAcarryingfields: { addCategories: ["B"], removeCategories: ["A"] }. - A
{ op: "archive", type: "category", id: "<id of A>" }item.
Both must travel in the same batch — archiving a category does not cascade to its members, so step 1 has to move the members first.
Per-event property settings (eventConfigs)
eventConfigs is an array inside fields on a property item (create or update; max 50 entries). Each entry adjusts how the property behaves on a specific event, on several events, or across all events:
setPresence— change presence toalwaysSent,sometimesSent, orneverSent. Can be scoped per source.sometimesSentonallEventsrequires a migrated workspace.setPinnedValue— pin a value for the property. Withevents: { kind: "onEvent" }pins per event; withevents: { kind: "allEvents" }pins property-wide.restrictAllowedValues— change the property’s allowed value list for an event. Carries avaluesChangedelta:addValues(non-empty),removeValues, orclear(no payload).
Per-event scopes (events.kind = "onEvent" / "onEvents") name events by persisted ID or $tmp: reference, on update and create. On a create, the batch must also attach the property to the referenced event (via $tmp:), otherwise the reference is unresolvable. allEvents entries apply to the freshly created property directly.
Temporary IDs (tempId / $tmp:)
To reference a newly created item from another item in the same call, declare a tempId on the create envelope and reference it elsewhere as "$tmp:<name>":
tempIdis create-only — setting it on anupdate,archive, orunarchivereturns an error.tempIdnames must be unique within a singlesave_itemscall and match^[\w-]+$(max 64 characters).$tmp:references resolve within one call only. Across calls, use the real ID returned from the previous call.- ID fields that accept
$tmp:are ID-or-$tmp:only — a literal name is rejected. Fields that take names (addCategories/removeCategories,addGroupTypes/removeGroupTypes) rewrite a$tmp:ref to the sibling create’s name instead. - Sources and destinations created in the same batch can be referenced by
$tmp:too, sincesave_itemscreates those types. - If a
$tmp:ref names a tempId that wasn’t declared on any item, the server returns a validation error.
Returns
A structured result with:
createdEntities,updatedEntities,removedEntities,unarchivedEntities— each entry hasname,entityId, andentityType. Created entries also echo thetempIdyou supplied, so you can map temporary handles to real IDs.errors— per-item validation or audit errors, each with the item index, name, and a message.warnings— non-fatal notices, same shape as errors.success— overall boolean.
{
"success": true,
"createdEntities": [
{ "name": "Checkout Method", "entityId": "prop-9d44…", "entityType": "property", "tempId": "checkout_method" }
],
"updatedEntities": [
{ "name": "Checkout Completed", "entityId": "evt-3f01…", "entityType": "event" }
],
"removedEntities": [],
"unarchivedEntities": [],
"errors": [],
"warnings": []
}Examples
Create a new event with a new property in one call
Prompt: “Add a Checkout Completed event with a Checkout Method property for Web and iOS.”
Claude declares a tempId on the new property so the new event can attach it before the server has allocated a real ID. The server resolves the $tmp: reference, allocates the real propertyId, attaches the property to the event, and includes the event in both sources — all atomically.
{
"branchId": "br-abc123",
"items": [
{
"op": "create",
"type": "property",
"tempId": "checkout_method",
"name": "Checkout Method",
"fields": {
"description": "How the user completed checkout",
"propertyType": "string",
"sendAs": "event",
"addAllowedValues": ["Card", "Apple Pay", "PayPal"]
}
},
{
"op": "create",
"type": "event",
"name": "Checkout Completed",
"fields": {
"description": "Sent when a user successfully pays and their order is placed.",
"properties": ["$tmp:checkout_method"],
"sources": ["src-web", "src-ios"]
}
}
]
}Rename a property and add an allowed value
Prompt: “Rename user_email to email and allow the value Unknown on Checkout Method.”
Scalar edits go in set; collection changes stay at the top level of fields.
{
"branchId": "br-abc123",
"items": [
{
"op": "update",
"type": "property",
"id": "prop-1a2b…",
"fields": { "set": { "name": "email" } }
},
{
"op": "update",
"type": "property",
"id": "prop-9d44…",
"fields": { "addAllowedValues": ["Unknown"] }
}
]
}Define a checkout funnel metric
Prompt: “Add a funnel metric on this branch that tracks the share of users who start checkout and complete it.”
Claude creates a Funnel metric whose items reference two existing events in order. The same call could chain in new events with $tmp: references if the funnel needed events that don’t exist yet.
{
"branchId": "br-abc123",
"items": [
{
"op": "create",
"type": "metric",
"name": "Checkout Funnel",
"fields": {
"description": "Share of users who start checkout and complete it.",
"metricType": "Funnel",
"items": [
{ "kind": "Event", "id": "step1", "eventId": "evt-checkout-started" },
{ "kind": "Event", "id": "step2", "eventId": "evt-checkout-completed" }
]
}
}
]
}Archive an event
{
"branchId": "br-abc123",
"items": [
{ "op": "archive", "type": "event", "id": "evt-3f01…" }
]
}Common errors
- Missing
writescope — the client must re-authorize withwrite. branchId is required/items is required— malformed request.- Unknown field, wrong-op field, or out-of-domain value in
fields— the error names the(type, op)and echoes the type’s contract. Calldescribe_toolwith that type and op. namemissing on acreate.tempId is only valid on create items— don’t settempIdonupdate,archive, orunarchive.Duplicate tempId "<name>"— eachtempIdmust be unique across items in the batch.Unknown $tmp: reference— a$tmp:ref names a tempId that wasn’t declared.<idField> is required for <op> <entity> items— missing identity ID.idandfields.<idField>both present with different values.too many items (got N, max 50)— batch is over the 50-item cap.403on a gateway write — the workspace does not have gateways enabled.- Per-item audit-pipeline validation failures (e.g. illegal name, duplicate property) — returned inside the
errorsarray rather than failing the whole call.
workflow
Scope: write
Write access is in general beta — enabled for every workspace, no need to request access. Email support@avo.app if you hit anything unexpected.
Branch-lifecycle write operations, dispatched by action:
| Action | What it does |
|---|---|
create_branch | Open a new branch off main. |
update_branch_description | Set the description on an existing open branch. |
pull_main | Merge the latest main into a branch. |
set_source_language | Set a source’s programming language on a branch. |
import | Import a tracking plan document (CSV or JSON Schema) into a branch. |
Call describe_tool with tool:"workflow" and an action for that action’s parameters and an example call.
A branch is a draft workspace for tracking-plan changes, analogous to a git branch. All write operations via the MCP happen on a branch — save_items requires a branchId that exists. The MCP never merges to main; open the branch in the Avo app to review and merge.
Parameters
| Parameter | Required | Description |
|---|---|---|
action | Yes | One of the actions above. |
branchName | create_branch (required); alternative to branchId for update_branch_description and set_source_language | The new branch’s name, or an existing branch’s name. |
branchId | pull_main, import (required); alternative to branchName elsewhere | The existing branch’s ID. Where both are accepted, provide exactly one. Never main. |
description | create_branch (optional); update_branch_description (required) | The branch description. Blank is a no-op on update. |
sourceId | set_source_language | The source to change. Enumerate sources with search(itemType:"source"). |
language | set_source_language | One of JavaScript_V2, Reason_V2, Java, Swift, JSON, Python, Python3, PHP, Kotlin, C#, TypeScript, Objective-C, Ruby, Dart, Go. Validated against the source’s platform. |
format | import | "csv" or "json_schema". |
importMethod | import | "add_only", "add_and_update", or "add_update_and_remove". |
payload | import | The CSV or JSON Schema document, as a string. |
workspaceId | No | Workspace ID |
Returns
For create_branch: the new branch’s branchId, branchName, and a branchUrl that opens it in the Avo web app. For update_branch_description: confirmation with the resolved branchId and the updated description. The other actions return a confirmation of what changed on the branch.
Examples
Create a branch for a new feature
Prompt: “Start an Avo branch for the new checkout flow we’re shipping next sprint.”
Claude calls workflow with action: "create_branch" and a descriptive branchName. The returned branchId is required for the follow-up save_items calls that write the new events and properties.
{
"action": "create_branch",
"branchName": "add-checkout-tracking"
}Update a branch’s description
Prompt: “Update the description on the add-checkout-tracking branch to mention that we’re now also tracking abandonment.”
Claude looks up the branch by name (no separate branchId lookup needed for this action) and replaces the description in one call.
{
"action": "update_branch_description",
"branchName": "add-checkout-tracking",
"description": "Adds the Checkout Completed and Checkout Abandoned events with the Checkout Method property."
}Import a JSON Schema tracking plan into a branch
{
"action": "import",
"branchId": "br-abc123",
"format": "json_schema",
"importMethod": "add_and_update",
"payload": "{ \"$schema\": … }"
}Common errors
- Missing
writescope — re-authorize withwrite. - Workspace access denied.
- Unsupported action value — the error lists the valid actions.
- Both
branchIdandbranchNameprovided where exactly one is expected. languagenot valid for the source’s platform.
list_branches
Scope: read
Transitional. This tool stays available while branch enumeration is being folded into search with type: "branch". Until that ships, use list_branches to enumerate branches.
Browse branches in a workspace with filtering and pagination. Results are paginated newest-first.
Parameters
| Parameter | Required | Description |
|---|---|---|
workspaceId | No | Workspace ID |
branchStatuses | No | Filter by status. Valid values: Draft, ReadyForReview, ChangesRequested, Approved, Merged, Closed, Open. Defaults to open/active branches only. |
pageSize | No | Results per page, default 25. Clamped to 1–50 (an out-of-range value is coerced, not rejected). |
pageToken | No | Pagination token from a previous response |
branchName | No | Substring match on branch name (case-insensitive) |
creatorEmail | No | Filter by creator email |
creatorUserId | No | Filter by creator user ID |
reviewerEmail | No | Filter by reviewer email |
reviewerUserId | No | Filter by reviewer user ID |
collaboratorEmail | No | Filter by collaborator email |
collaboratorUserId | No | Filter by collaborator user ID |
createdAfter | No | ISO 8601 date — only branches created after |
createdBefore | No | ISO 8601 date — only branches created before |
impactedSourceId | No | Filter to branches affecting a specific source |
By default Merged and Closed branches are excluded. Pass branchStatuses: ["Merged"] (or any other value) to include them.
To find “my branches,” pass your own email as creatorEmail or reviewerEmail. The tool does not auto-inject your identity into the filter.
Returns
Compact per-branch summary — name, status, ID, creator email — plus a nextPageToken when more results are available. Call get with type: "branch" and include: ["overview"] for full resolved data.
Examples
Find branches I’m reviewing
Prompt: “What branches am I assigned to review?”
Claude passes the user’s email as reviewerEmail and filters status to ReadyForReview. The tool does not auto-inject the caller’s identity, so the email has to be supplied explicitly.
{
"reviewerEmail": "thora@avo.sh",
"branchStatuses": ["ReadyForReview"]
}Common errors
- Workspace access denied.
- Invalid date format on
createdAfter/createdBefore.
Troubleshooting
Tool-specific behavior issues. Authentication and workspace access issues are covered in Troubleshooting on the overview page.
search returns nothing for a clearly relevant query. Semantic search requires Avo Intelligence Smart Search to be enabled. Workspace admins can turn it on in Workspace Settings. Without it, fall back to get with an exact name or search in filter mode.
The wrong branch is returned by name. branchName resolves to a best match and prioritizes open branches, so an ambiguous name can pick the wrong one. Resolve the name to a branchId with list_branches first and pass branchId to the follow-up call.
save_items rejects an item with a contract in the error. The item carried a field that type or op does not accept, or a top-level field that belongs inside fields. Read the echoed contract, or call describe_tool with the same type and op, and retry. Older examples that put fields like propertyType directly on the item (outside fields) no longer work.
My client does not show save_items at all. Some MCP clients drop tools whose advertised schema exceeds their size limits. The Avo MCP keeps every advertised schema small precisely so this does not happen; if it still does, make sure the client is talking to https://mcp.avo.app/mcp and report it to support@avo.app.