piece-output-schema
GitHub为 Activepieces 组件生成 outputSchema,将原始 JSON 输出转化为结构化、带标签的树形数据,优化流程构建器中的数据显示与 LLM 字段检索。
Trigger Scenarios
Install
npx skills add activepieces/activepieces --skill piece-output-schema -g -y
SKILL.md
Frontmatter
{
"name": "piece-output-schema",
"description": "Generate `outputSchema` for an Activepieces piece's actions and triggers, so a step's output renders as a curated, labelled tree in the flow builder and data selector. Use when the user asks to add or improve outputSchema for a piece."
}
Piece Output Schema Generator
An outputSchema turns a step's raw JSON output into a friendly, typed, labelled tree in the flow builder's data selector and output viewer — and a path map that LLM/MCP consumers use to find the fields that matter. This skill takes a piece from "raw JSON dump" to curated schemas across all its actions and triggers.
Read a shipped example before starting: packages/pieces/community/clickup/src/lib/output-schemas.ts is the richest; google-docs and google-calendar are readable smaller ones.
The mental model (read this first)
An outputSchema is a curated tree: at every level you describe, only the fields you list appear — their siblings are dropped. That is exactly how you keep the output clean.
- Omitting a field hides it. At the top level, only the fields in
schema.fieldsrender; undescribed root siblings are gone. Inside a described object (children) or array item (listItems), only the children you list render — the resolved value's other keys are dropped. - Undescribed container drills, not hides. If you name a field but do not describe its inner shape (no
children/listItems), the renderer drills the whole value generically (matrices → Row/Cell, arrays → list, objects → every key). Describe a container's useful inner fields, or leave the field off entirely — there is no way to name a container and show only some of its contents without listing them. - What you're doing at each field: curate (drop config/headers/tokens/opaque bookkeeping), label for humans, attach a format (
datetime,url,email,boolean,image,filesize,html,number,date,currency,duration) where one fits, and record the path so data selector and AI/MCP consumers can find it.
Because the schema describes what the action's run() returns (not the raw third-party API response), you must know the return shape before you can map paths. See capture-recipes.md.
Prerequisites
- A running local dev instance (
npm start/npm run dev). Dev pieces load from each piece's builtdist/— see capture-recipes.md if a piece doesn't appear. - A real, active connection for the target piece — OAuth sign-in, API key, or whatever the piece's auth type requires. The user provides credentials.
- A piece not yet loaded as a dev piece gets its folder name appended to
AP_DEV_PIECES.
Ask the user for the piece(s) and the connection to use before starting.
Workflow
Step 1 — Scope the piece
List the piece's actions and triggers (packages/pieces/community/<piece>/src/lib/{actions,triggers}). For each, decide whether it gets a schema using the table below.
| Step kind | Schema? |
|---|---|
| Create / Update / Get / Read / List / Search / Find | Yes |
Delete / clear / archive that returns an empty body ({}, '', 204) |
No — nothing to describe |
custom_api_call (generic passthrough) |
No |
| Polymorphic trigger (payload is message OR poll OR callback, etc.) | No — a single shape would mislabel the others (e.g. Telegram "New Update") |
| Webhook / polling trigger with a stable payload | Yes — describes ONE item (the per-run payload) |
Step 2 — Learn each step's return shape
Open the action/trigger's run() (and test() for triggers). Note whether it returns response.body, response.data, the full HTTP/Gaxios wrapper ({status, headers, body, config}), or a hand-built/transformed object. Never surface config or headers — config.headers.Authorization leaks the bearer token. The schema's top-level value paths are relative to this returned object.
Step 3 — Capture the REAL output
Run each step against the live connection and capture the exact output JSON. Full recipes in capture-recipes.md.
- Preferred: builder Test Step (UI, or drive it with the browser MCP), or the
POST /v1/sample-data/test-stepAPI once a flow with the step exists. Running the piece's own code delivers faithful output and lets the engine refresh OAuth tokens for you. - Empty READ → WRITE first: if a list/search/get returns an empty payload because the account has no data, seed data by running the corresponding create/write action first, then chain the new id into the read's input and re-run. Never author a list schema from an empty
[].
Step 4 — Curate and author the schema
Write the schema in packages/pieces/community/<piece>/src/lib/output-schemas.ts (create the file if absent). Full field reference, formats, labels, and wiring in schema-reference.md. The essentials:
- Keep only useful fields; drop config/headers/tokens and opaque bookkeeping.
- A field's
value(the path) is optional and defaults tokey. For a plain field, setkeyto the real JSON property name and omitvalue(the dominant shipped style); setvalueonly to unwrap (body.*,data.*) or rename. See key vs value. - Apply a
formatto every field where one fits (datetime,url,email,boolean,image,filesize,html,number,date,currency,duration). children/listItemspaths are RELATIVE to the parent's value — this is the #1 correctness bug.owners[].displayNameis described as a top-level fieldownerswith alistItemschild{ key: 'displayName' }, NOT a child path ofowners.displayName.- Top-level array output → one wrapper field with
value: ''+listItems, plus a schema-levelitemLabeltemplate (e.g.'Row {row}'). - Maps with opaque/variable keys (e.g. per-calendar busy times) →
dynamicKey: true. - Add
labelKeyto lists/maps so items show a meaningful label;itemLabelfor top-level arrays. - Reuse shared field-sets — factor a repeated object shape (e.g.
taskFields, a DrivefileFields) into aconstand reference it from every action/trigger that returns it.
Step 5 — Validate every path
Resolve every field's value ?? key against the captured JSON at the correct scope — top-level against the root, children against the parent object, listItems against one array item. A path that doesn't resolve is a dead field; re-capture if the shape is ambiguous. For a piece with many schemas, verify each one adversarially — one sub-agent per schema, given only the schema and its captured payload, asked to find any path that fails to resolve.
Step 6 — Wire, version, build, lint
- Add
outputSchema: <name>to each action/trigger object (or populate the trigger registration map — see schema-reference.md). - Bump the piece's patch version in its
package.json(every touched piece) — this is what forces cloud/self-hosted registries to re-ingest the fresh metadata. - Rebuild the piece and reload the dev instance (capture-recipes.md); confirm the friendly tree renders in the builder.
- Run
npm run lint-dev(ornpx turbo run lint --filter=@activepieces/piece-<name>). Typecheck must be clean.
Verification checklist
- Every non-empty, non-generic action and every stable trigger has a schema (skips are deliberate per the table).
- Every schema was authored from real captured output, not documented/guessed shapes.
-
children/listItemspaths are relative; top-level array uses avalue: ''wrapper +itemLabel. - No
config,headers, tokens, or auth secrets appear in any schema. - Formats and
labelKey/itemLabelapplied where they help. - Each touched piece's patch version bumped; build +
lint-devgreen; friendly tree verified in the builder.
Note: how the schema reaches the builder
outputSchema ships as part of the served piece metadata (dev pieces from dist/, published pieces from the registry). If a schema doesn't appear in the builder, confirm the piece's patch version was bumped and the piece was rebuilt + reloaded — that's the served metadata refreshing. Legacy servers older than #13983 stripped outputSchema during registry ingestion; irrelevant for current builds.
Related
piece-builderskill — building pieces and theoutput-quality.mdreference (shapingrun()return values for table-readiness) complements this skill, which describes an existing return.- Files: schema-reference.md · capture-recipes.md
Version History
- 71dd175 Current 2026-08-20 18:26


