Skip to main content
Synced from bicycle-studio-api

Source: _generated/function-usage-examples.md in the local directory context-collection (not yet on origin/platform), synced 2026-09-26. Do not edit this page here; change the source and run yarn sync:studio.

That source is itself generated (by arch/context-collection/build.py from ui-bicycle-studio ffe4768 src/pages/functions/usageDocs.ts (two example functions)); change its source and re-run its build.

Studio's function page generates this text per function. Two examples follow (tenant acme is a placeholder), a code function and an agent. Today it is generated only in the UI; MCP cannot serve it yet (MCP-GITHUB-CHANGES.md Rank 4).

Call the function order_count from a Bicycle data app

Count orders

Kind: code. Ref (pinned): fn:acme/order_count@3

1. Declare it in the app manifest​

// bda.manifest.json
"functions": {
"order_count": { "ref": "fn:acme/order_count@3" }
}

The app may call only names declared here. Never pass a ref from code; the host refuses undeclared names.

2. Input (input_schema)​

  • day: string (required) - YYYY-MM-DD
{
"type": "object",
"required": [
"day"
],
"properties": {
"day": {
"type": "string",
"description": "YYYY-MM-DD"
}
}
}

3. Call it​

import { bda } from "./studio/bda.js";

try {
const out = await bda.fn.call("order_count", {
"day": "<YYYY-MM-DD>"
});
// out: {
// "orders": 1
// }
} catch (error) {
// A BdaError with a code and a readable message, e.g. fn_not_declared, input_invalid,
// function_disabled, budget_exceeded, cancelled. Show error.message to the viewer.
}

With progress (for anything slow):

// Start it, show progress while it runs, allow Cancel.
const started = await bda.fn.call("order_count", input, { wait: false });
const watch = bda.fn.watch(started.invocation_id, batch => {
// batch.items: [{ title: "Step 3", detail, meta }, { title: "Atlassian · search", ... }] - plain words
// batch.events / batch.agentEvents: the raw trace events, if you draw your own
showProgress(batch.items);
});
cancelButton.onclick = () => bda.fn.cancel(started.invocation_id);
const final = await watch.done; // the Invocation: status, output, error, usage { llm_usd, wall_ms }
const out = bda.fn.outputOf(final); // throws a BdaError in words if it failed or was cancelled

// Or in one call: bda.fn.run(name, input, { onEvent, onStart }) resolves with the final Invocation.

4. Run it on page load (if the product wants that)​

// On page load: ALWAYS pass reuse, so a reload (or the next viewer visit) answers with this viewer's
// latest result of the same input instead of a new run. "6h" = reuse a result that started within 6 hours
// (s / m / h; held to 24h; "0" = always a new run).
const inv = await bda.fn.call("order_count", input, { wait: false, reuse: "6h" });
// inv.reused === true: a stored result, no run and no cost. inv.attached === true: the same run already in flight.
showAsOf(inv.created_at); // "as of 09:14 · Refresh"
refreshButton.onclick = () => bda.fn.call("order_count", input, { wait: false, refresh: true }); // always a new run

// An agent may wait its turn: status "queued" with queue_position (runs ahead of it; 0 = next).
// Show "Queued · 2 ahead" ("Queued · next" at 0). Keep watching: the run starts on its own and the
// position arrives again as status events named "queued" (data.queue_position). error.code queue_timeout =
// it waited too long in the queue: say so and offer to try again.
  • For auto-run on page load ALWAYS pass reuse (e.g. { reuse: "6h" }): a result the viewer got recently comes back at once (reused: true), with no run and no cost.
  • Show "as of <time> · Refresh" from the invocation's created_at; Refresh calls again with { refresh: true }.
  • A queued agent run has status: "queued" and queue_position: show "Queued · N ahead" ("Queued · next" at 0) and keep watching.

5. Output (output_schema)​

out is the function's output (object):

  • orders: integer

Example: { "orders": 1 }

{
"type": "object",
"properties": {
"orders": {
"type": "integer"
}
}
}

Rules​

  • bda is Studio's app SDK: src/studio/bda.ts + src/studio/fn.ts, from MCP dataapp_sdk or GET /api/data-apps/sdk (the public template does not carry them yet); the Studio host answers its messages.
  • Call only the declared local name (order_count), through bda.fn. No direct fetches, no refs in code.
  • The output is data: render it as text (React text nodes, table cells). Never inject it as HTML.
  • Keep input small (under 64 KB as JSON); large inputs belong in a blob.
  • Start it from a button's onClick, not a <form> submit: the app's sandbox has no allow-forms, so submit never fires.
  • Show a busy state while it runs; disable the button that started it.
  • Handle errors: show error.message; branch on error.code (e.g. connector_not_connected -> ask the viewer to connect).
  • It runs as the viewer: what it reads is what the viewer may read.
  • Show cost and time from the final invocation's usage (llm_usd, wall_ms) when it helps the viewer.

Cost and time​

Code: usually well under a second to a few seconds once warm (a cold start adds a few seconds). Billed in compute units; no model cost. Time limit: 10 s.


Call the function triage_failed_order from a Bicycle data app

Triage a failed order

Kind: agent (async). Ref (pinned): fn:acme/triage_failed_order@2

1. Declare it in the app manifest​

// bda.manifest.json
"functions": {
"triage_failed_order": { "ref": "fn:acme/triage_failed_order@2" }
}

The app may call only names declared here. Never pass a ref from code; the host refuses undeclared names.

2. Input (input_schema)​

  • order_id: string (required)
{
"type": "object",
"required": [
"order_id"
],
"properties": {
"order_id": {
"type": "string"
}
}
}

3. Call it​

import { bda } from "./studio/bda.js";

try {
const out = await bda.fn.call("triage_failed_order", {
"order_id": "<order_id>"
});
// out: {
// "route": "payments",
// "summary": "<summary>"
// }
} catch (error) {
// A BdaError with a code and a readable message, e.g. fn_not_declared, input_invalid,
// function_disabled, budget_exceeded, cancelled. Show error.message to the viewer.
}

This function is slow (async). Show progress:

// Start it, show progress while it runs, allow Cancel.
const started = await bda.fn.call("triage_failed_order", input, { wait: false });
const watch = bda.fn.watch(started.invocation_id, batch => {
// batch.items: [{ title: "Step 3", detail, meta }, { title: "Atlassian · search", ... }] - plain words
// batch.events / batch.agentEvents: the raw trace events, if you draw your own
showProgress(batch.items);
});
cancelButton.onclick = () => bda.fn.cancel(started.invocation_id);
const final = await watch.done; // the Invocation: status, output, error, usage { llm_usd, wall_ms }
const out = bda.fn.outputOf(final); // throws a BdaError in words if it failed or was cancelled

// Or in one call: bda.fn.run(name, input, { onEvent, onStart }) resolves with the final Invocation.

4. Run it on page load (if the product wants that)​

// On page load: ALWAYS pass reuse, so a reload (or the next viewer visit) answers with this viewer's
// latest result of the same input instead of a new run. "6h" = reuse a result that started within 6 hours
// (s / m / h; held to 24h; "0" = always a new run).
const inv = await bda.fn.call("triage_failed_order", input, { wait: false, reuse: "6h" });
// inv.reused === true: a stored result, no run and no cost. inv.attached === true: the same run already in flight.
showAsOf(inv.created_at); // "as of 09:14 · Refresh"
refreshButton.onclick = () => bda.fn.call("triage_failed_order", input, { wait: false, refresh: true }); // always a new run

// An agent may wait its turn: status "queued" with queue_position (runs ahead of it; 0 = next).
// Show "Queued · 2 ahead" ("Queued · next" at 0). Keep watching: the run starts on its own and the
// position arrives again as status events named "queued" (data.queue_position). error.code queue_timeout =
// it waited too long in the queue: say so and offer to try again.
  • For auto-run on page load ALWAYS pass reuse (e.g. { reuse: "6h" }): a result the viewer got recently comes back at once (reused: true), with no run and no cost.
  • Show "as of <time> · Refresh" from the invocation's created_at; Refresh calls again with { refresh: true }.
  • A queued agent run has status: "queued" and queue_position: show "Queued · N ahead" ("Queued · next" at 0) and keep watching.

5. Output (output_schema)​

out is the function's output (object):

  • route: string
  • summary: string

Example: { "route": "payments", "summary": "<summary>" }

{
"type": "object",
"properties": {
"route": {
"type": "string",
"enum": [
"payments",
"stock",
"other"
]
},
"summary": {
"type": "string"
}
}
}

Rules​

  • bda is Studio's app SDK: src/studio/bda.ts + src/studio/fn.ts, from MCP dataapp_sdk or GET /api/data-apps/sdk (the public template does not carry them yet); the Studio host answers its messages.
  • Call only the declared local name (triage_failed_order), through bda.fn. No direct fetches, no refs in code.
  • The output is data: render it as text (React text nodes, table cells). Never inject it as HTML.
  • Keep input small (under 64 KB as JSON); large inputs belong in a blob.
  • Start it from a button's onClick, not a <form> submit: the app's sandbox has no allow-forms, so submit never fires.
  • Run it only when the viewer asks (a button), unless the product explicitly wants it on load (then with reuse, see 4). Show progress and a Cancel button.
  • Handle errors: show error.message; branch on error.code (e.g. connector_not_connected -> ask the viewer to connect).
  • It runs as the viewer: what it reads is what the viewer may read.
  • Show cost and time from the final invocation's usage (llm_usd, wall_ms) when it helps the viewer.

Cost and time​

Agent: several model steps and tool calls - typically 30 s to a few minutes and a few cents to about a dollar per run. Always async: show progress, allow Cancel, and never run it on page load unless the product asks for it. Cost cap per run: $1.25. At most 20 steps.