Skip to main content

Building thunks with the Builder MCP: a guide for AI agents

For AI agents connected to the Thunk.AI Builder MCP: the order to build a thunk in, which help articles to read for each task, and how to show the user the thunk while you work.

This guide is for AI agents (such as Claude, Codex, Cursor, or Copilot) that build and analyze thunks through the Thunk.AI Builder MCP. People who want to set one up should start with Build thunks from Claude Code or Codex. If this is the user's first time on Thunk.AI, follow first-run-with-the-builder-mcp instead.

It covers what every thunk task has in common (sessions, tenants, showing the
thunk to the user), building or changing a thunk, explaining a thunk, and
which help articles to read for each task. Troubleshooting a work item or
step, a health check, and a cleanup or upgrade of an existing thunk are in
​analyze-a-thunk-with-the-builder-mcp.

The Builder MCP and its tools

The help center (info.thunk.ai) is the source of truth for how to build good
thunks.

The Thunk.AI Builder MCP (/api/builder/mcp on your Thunk.AI tenant)
is how you read and change thunks. Its name depends on how it was
connected: thunk-builder as a local MCP server, a claude.ai connector such
as Thunk.AI, a Claude Code plugin per tenant, whose tools appear as
​mcp__plugin_thunk-<tenant>_thunk-builder__*, or a Codex plugin per tenant,
whose server is named thunk-<tenant>. Discover the tools this session
lists and use those; this guide names the product task, not a particular
tool. This guide tells you which articles to read for
the task in front of you. Read them before you act; do not work from memory.

Start a session first

If the Builder MCP offers a way to start a Builder session, do that before
anything else with a short title, a description of what you are doing, and
​purpose: build_thunk when you are creating or substantially changing a
thunk, analyze_performance when you are measuring time or cost,
​debug_ai_control when a step or work item did the wrong thing, or other
when none of those fit. Also say who you are: agent is claude, codex,
​cursor or other (with a few words in agentDescription when it is
​other, such as the product name), and agentSurface is the app you run in
(desktop_app, terminal, web, ide or other). The instructions then
include the guide for that purpose and the app you named. Start the session
with those arguments, as the only Builder call in its turn, and wait for its
result: you do not have its sessionId until it finishes. Read the
instructions it returns and pass that sessionId to every other Builder call.
Where the server requires sessions, a call without a sessionId is refused
with session_required. A sessionId that is not yours, or not a real one,
is always refused with session_not_found. Either way, start a session
again and carry on. Over HTTP the id travels in the X-Builder-Session header
instead. Sessions are yours alone and show up under Account → Builder →
Sessions.

More than one tenant

Each Thunk.AI tenant (such as the public one and a dedicated customer tenant)
is a separate server with its own thunks and accounts, and a thunk id exists
on only one of them. If this session has the Builder MCP for more than one
tenant, find out which tenant the user means before the first call, ask if it
isn't clear, and keep every call for that thunk on that server. Never copy a
design or data from one tenant to another unless the user asks for exactly
that.

The Builder MCP informs; the browser pane shows

Treat the browser pane (the in-app browser) as a presentation vehicle for the
user, not as an information source for your work or your explanation. Get
thunk facts and configuration from the Builder MCP — the thunk's summary and
its full design. Use the pane to navigate to and display the
relevant part of the app for the user; do not rely on its page text,
screenshots or controls to learn how the thunk is configured. If the UI
appears to differ from what the Builder MCP returns, tell the user about the
difference and verify it through the Builder MCP.

Offer numbered choices when you stop

Whenever you pause for the user's input (a question, a decision, a proposal
to approve, the end of a stage), end your message with a few sensible next
steps as a numbered list, the one you recommend first, so the user can reply
with just a number. They can still answer in their own words.

Keep the user in the loop while you build

Most users are new to Thunk.AI and can't see your tool calls. They see only
your messages, the task list and the browser pane. A few minutes of silence
looks to them like the build has frozen, and a result that arrives after
they've run out of time is a result they never see. Show the first useful
result fast, and keep them informed until then.

  1. Answer their questions first. If the user asked something direct
    (where the data goes, whether the AI makes the decision, who approves,
    what it costs, how it connects to their system), answer it in your first
    reply, before you build. If an answer depends on the build, say what you
    know now and when you'll know the rest. Don't make them wait for the
    build to learn whether the tool is acceptable to them.

  2. Say the plan, then build. Before a build that will take more than a
    minute, give a plan of two or three lines in plain words: what you'll
    make, what you'll test it on, and roughly how long it will take ("about 5
    minutes"). Keep the task list in step with the work: mark each item done
    as you finish it, so the list moves at least every couple of minutes.

  3. First result on one sample, before anything else. The first goal is
    one realistic sample through the main path, with its output shown to the
    user. Do these only after that first result is shown: mock thunks for
    systems the main path can do without (give it the sample data directly
    instead), extra test cases, consistency reruns, probe tools and other
    hardening. Then offer them as next steps. On a user's first build this
    rule takes priority over steps 2 and 5 of Build order: a mock of
    the user's ERP, ticketing system or HR system waits until the first
    result is shown.

  4. Show the output itself. When a run produces what the user came for
    (a memo, an email, a list of findings, a score with its evidence, a
    coaching note), paste it into your reply as text. Don't just report that
    it exists or that its checks passed. Lead with the output, then the pass
    or fail summary.

  5. Never go quiet for long. Post a one-line status in plain words at
    each milestone (thunk created, steps written, test running, result in),
    and at least every 60–90 seconds during long work. Write status lines for
    a business user: say "the test deal is running", not "rerunning the work
    item", "indexing" or "the contaminated item".

  6. Don't end your turn on a wait. "Still running", "I'll check back in a
    minute" or "the test has started" is not a result. While a run is in
    progress, keep the turn going and check the run as Wait for a run
    says. End the turn on a result, a question, or a real blocker, said
    plainly.

  7. Time-box each turn to about five minutes. If a turn has run about
    five minutes without a result for the user, stop adding to it. Show what
    exists now (the steps, any partial output), say in a line what's left and
    how long it will take, and end the turn with a choice: carry on, or
    change course. The user can't reply while your turn is running, so a long
    turn full of status lines still keeps them waiting.

Wait for a run

Running or re-running a work item through the Builder MCP (or creating one
that starts on its own) returns at once while the work item keeps running. Don't
guess how long it will take and wait for that long. Fixed waits of one to
two minutes usually overshoot a run that finished in 30 seconds, and leave
the user with nothing to read while you wait.

  • Any wait of a set length is a fixed wait, however it is written: a sleep,
    a loop that runs until a clock time, or a background timer that wakes you
    later. Don't use any of them for longer than about 20 seconds.

  • Read the work item's state through the Builder MCP after every pause of
    up to about 20 seconds. Stop as soon as the work item finishes, fails, waits for a
    person (stepCompletionApprovalRequired, waitingForConversationReply),
    or reports blocked.

  • Between checks, post a short status line when the step has changed ("Step
    2 of 3: checking against the policy").

  • When several work items are running, check them all in each round instead
    of waiting on them one at a time.

  • Give up after the step time limit (about five minutes of working time) and
    read blocked and the step history to see why.

Build order

Build skeleton first, then test it right away. Lay out the whole thunk
early (its steps, and an outline of its tools), breadth first, then fill it
in one step at a time. Stand in mocks for external systems, and as soon as
it is built, create realistic test data for the main path and run it. Edge
cases, failure cases and the design review come later, when you harden the
nearly finished design.

Design in modules. Read use-modularity-effectively-when-building-a-thunk
before you lay out the steps. In short: make each step a meaningful part of
the business process, small enough to finish within the step time limit
(by default, about five minutes of active working time per run,
counting the model's work and its tool calls); give each step
only the tools it needs, and package a self-contained piece of AI work as a
custom AI tool; put each external system behind its own MCP-server thunk
with a business-level interface, reached through a plug-in connection; and
when the same workflow serves several kinds of work items whose policies,
data or actions differ, keep the steps generic and select a module per work
item with plug-in binding cases, chosen by a column an earlier step fills
in. Present these as recommendations to the user, not requirements.

The user is watching the thunk while you build, in the Thunk.AI app (in the
side-panel browser, or from the link you gave). Build skeleton first (breadth first):
make the shape of the whole thunk appear, then fill it in one step at a time.
Don't build it layer by layer (every property, then every tool in full
detail, then finally the steps): that leaves an empty Workflow Plan on screen
for most of the build, and the user can't tell what you are making.

Keep each project's thunks in one folder. The user's home page in the
Thunk.AI app sorts thunks into folders, and a session that makes several
thunks (the thunk, its mock services, copies for testing) otherwise leaves
them scattered among everything else the user owns. At the start of a
project, choose one folder path for it, such as
​/invoice-intake or /claims/fraud-review, and tell the user which one.
Use a short name for the project, not for the session or the date. Before
choosing, list the user's thunks through the Builder MCP with folderPath
set to / and includeSubfolders true to see the folders they already have,
and reuse one that fits (each summary's folderPath shows where a thunk is
filed). Then file every thunk you create for the project there: pass
​folderPath when you create or copy a thunk (a copy of a thunk the user owns
otherwise stays in the source's folder, and a copy of someone else's lands
at the top level). Mock services and test copies go in the same folder, or
in a subfolder such as /invoice-intake/mocks. To file a thunk you didn't
create there, or move one, set the thunk's folder path (folderPath); only
the thunk's owner or an admin can move it, and the move is refused in
Production. List the project's thunks with folderPath set to that path. A
folder needs no creating: filing a thunk in it is enough.

Work log for a unit of edits. Before you change a thunk's definition
(properties, steps, tools, connections, options, export, reports), begin a
work-log note that states that unit of work. End it when you finish.
End is optional: if you forget, further edits keep attaching to that note,
and a later begin closes it. Use a closed note only when you are not
starting a burst of edits. Do not pass a work-log id to mutation tools;
the builder joins the edits. If those operations are not listed, skip this
and keep building.

For a new workflow or chat-app thunk:

  1. Create it with every step laid out, then open it. If
    creating a thunk accepts steps (check that call's input schema), pass
    them all in the create call, in order and chained by status (New → … →
    the last step's final status), plus any properties the design already
    fixes. Otherwise create the thunk, then add the same
    steps. Give each step its real title and a short paragraph of directions
    saying what the step is for (step 4 replaces it with full directions).
    Leave out toolConfig, and any binding to a property that doesn't exist
    yet. Check the results the response gives
    for its steps and properties (or the batch's results) and fix any
    ​error entry. Then
    open the thunk in the browser pane (see Open the thunk in the browser
    pane
    )
    and give its link.

  2. Decide real or mock for each external system. On a user's first
    build of a thunk, skip this step until the first result is shown (see
    ​Keep the user in the loop while you build). Put the sample record's
    data from those systems straight into the test work item, or into a small
    content folder, and run the main path on it. Build mocks only after that
    first result, and offer them to the user as the next step. For every outside
    system the workflow reads or changes (a CRM, email, a ticketing system, a
    database), read the thunk's design for an account connection that
    already reaches it. If there is a suitable one, ask the user whether
    to use it or a mock; never wire a live connection into a new thunk
    without asking. If the user wants a real system that has no connection
    yet, create one on their account through the Builder MCP: a REST API
    (Generic API), an MCP server (MCP service) or an OAuth connection
    that uses the client-credentials grant (Generic OAuth). Do not ask
    the user to paste a client secret or API key into the chat, for any
    connection type: OAuth and MCP connections take no secret, and a REST
    connection can be created without its key. Create the connection; if
    it comes back with secretPending true, ask the user to enter the
    secret on that connection on their Thunk.AI Account page, then refresh
    an MCP connection's tools. Add the connection
    to the thunk. These are the user's personal
    connections; a thunk assigned to a resource environment uses that
    environment's connections instead. An OAuth connection that needs
    a person to sign in is created on the Account page, not through the
    Builder MCP. Otherwise, and whenever the user prefers, build a
    ​mock of that system, shaped so that moving to the real system later
    is a rebind, not a rebuild:

    • One MCP-server thunk per system, with a business-level interface.
      Create a thunk with kind:"mcpServer" and an interface whose tools are the
      operations the workflow needs, named in business terms (find_customer,
      ​issue_refund), not a copy of the vendor's endpoints. Base their ids,
      fields, required arguments and error answers (an unknown id, a refund
      larger than the charge, a duplicate request) on the real system's API,
      so the mock behaves like the real system and the adapter that connects
      the real system later stays thin. Give every tool its output as well as its inputs
      (outputSchema, or outputProperties in property-type form): the
      fields of a successful response, with each field the caller relies on
      marked required and a field that can be empty typed to allow null. The
      output is half of the contract: without it a replacement for the mock
      can match the interface and still return different fields. Read the
      vendor's API documentation first; for an internal system with no public
      documentation, model the calls such a system would plausibly offer.
      File it with the project, for example in /invoice-intake/mocks.

    • Its records in a spreadsheet, never in code. Put the mock's data in
      a Spreadsheets content folder on the mock thunk
      (readOnly true), one tab per record type with
      a header row, so the user can open the workbook and see and change what
      the mock returns. Build the workbook yourself and upload it
      through an upload link from the Builder MCP, or link a Google Sheet the
      user owns. Each tab
      gets list and lookup tools; read their names from the design
      section contentFolders.

    • Thin code tools over the tabs. Implement each interface tool as a
      short code tool that reads the tabs through those tools (in code, a tab's
      list tool is tools.<workbook>_<tab>_list, with dots and other
      characters turned into underscores) and returns the real system's
      response shape. A write tool checks the request the way the real system
      would, returns what it would have done with an id derived from the
      request's idempotency key, and changes nothing. Never write records,
      ids or amounts into the code. A request the real system would refuse
      fails the call (throw an error naming the status and the reason, such
      as 404 trip_not_found); it is not a successful response with an
      error inside, which would not match the output. Each code tool gets its
      output from the interface: pin it with signature (the
      interface and the tool's name), which copies the contract's name,
      description, inputs and output onto the tool. Test each tool through
      the Builder MCP, including a rejection ($status is rejected),
      then publish the export with exactly the interface's tools enabled; the tabs'
      own list and lookup tools stay unpublished.

    • Reach it through a plug-in connection. On the thunk you are
      building, declare a plug-in connection with the same interface, bound to
      the mock: kind: "plugIn", an alias such
      as CRM, the interface, and bindTo set to the mock thunk's id. Steps
      and code tools then call the tools under the plug-in's name
      (get_account_crm), and swapping in the real system is binding the same
      alias to a connection that matches the interface, or to an adapter
      (reuse-thunk-interfaces-and-plug-in-connections).

    • Keep the write tools away from the agent. A plug-in's tools are on
      for every step when it is added. On each step, turn off with
      ​toolConfig every write tool its agent must not call directly, and turn
      the plug-in off on steps that do not use it. A step's toolConfig does
      not limit a code tool the step calls: the code tool can call any tool
      that is on for the thunk, unless its own Tools settings turn it off
      (toolConfig with disable_tool or disable_library).
      So a code tool is the place for a write that must follow a rule (for
      example, a refund whose amount the code computes). When a code tool
      calls a plug-in tool and the call fails, the code tool can receive
      the failure as text rather than as an object or a thrown error: treat
      any result that is not the declared object as a refused request.

    Data the thunk itself owns, such as a policy table, a rate card or
    thresholds, goes the same way: a Spreadsheets folder on this thunk, read by
    its tools, never constants in code. The rules for good stand-ins are in
    ​improve-a-live-thunk-safely and in analyze-a-thunk-with-the-builder-mcp (C3).
    Tell the user which systems are mocked.

  3. Outline the tools. Add each custom tool
    the design needs, with its final name, description, intent, input
    schema and output, and a stub implementation: a short code body that
    returns a "not implemented yet" message. Give the output as
    ​outputProperties (property types, with constraints.required on each
    field the tool always returns) or outputSchema. A code tool that
    returns one value can declare a bare value, such as
    ​{"type":"string","enum":["Red","Amber","Green"]}. An ai tool's output
    is an object of fields. A classifier needs at least one YES_OR_NO
    field, or a TEXT field with an enum constraint, before it can be used.
    Leave the output out of a later update to keep it; {} or [] clears
    it. A documentTemplate or spreadsheetLookup tool's output is set by
    its type, so you cannot declare one; the design still returns it, and an
    update that sends it back unchanged is accepted. The design returns each tool's outputSchema, batchInvoke and
    ​batchConcurrency, and with includeToolImpl, for an ai or code
    tool, the toolConfig of what it may call. Stub an ai tool
    as an ai tool (with one-line userGuidance), because a new AI tool
    picks up the thunk's enabled tools only when it is added, not when a
    stub is changed into one. To change what an ai or code tool may
    call later, send toolConfig on its update, with the
    same ops a step's toolConfig takes.

  4. Fill in each step, depth first, in plan order. For one step at a time:

    • before you add any property, audit its type. List every fact this
      step, a later step or a tool needs, one property per fact, and give
      each the most specific type that fits ("Choosing a property type" in
      ​schematized-state): NUMBER for an amount or count,
      ​CALENDAR_DATE or DATE for a date, YES_OR_NO for a flag, TEXT
      with an enum constraint for a fixed set of values, FILE or IMAGE
      for a document or picture (file-and-image-property-types),
      ​isListType for several values, and — for a value with several
      parts (a record such as a contact, an address or a line item) — a
      ​Structure Type: a named, reusable shape added to the Type Library,
      with the column given the STRUCTURE_TYPE_REF type pointing at it
      (structure-types). A Structure Type is preferred over JSON_OBJECT
      for any shape you know, and over several loose columns when the parts
      belong together. Reach for JSON_OBJECT (with an exampleObject)
      only for a value whose shape you genuinely cannot name. A Conversation
      property is the thread for email or chat
      (conversation-properties-for-email). If a step should send a
      message and wait for the reply, set
      ​constraints.convoSendMessageMode to sendAndWaitForReply and
      list that conversation under the step's outputProperties; leaving
      the mode unset does not wait. Use plain TEXT
      only when no more specific type fits, and be able to say why. Don't pack several facts into one text
      property for a later step or tool to pick apart;

    • add the properties it reads and writes and bind them
      on the step (inputProperties / outputProperties);

    • align each tool's input schema with the properties it reads: one
      argument per property, with the same meaning and a matching type (a
      ​NUMBER property is a number argument, an enum is the same set of
      allowed values). The surest way to match is to type the argument
      as a reference to the property (x-thunk-ref), so it takes the
      property's type and allowed values and stays in step when they
      change; prefer it for every argument a property holds, above all
      an enum, number, date or object. If an argument carries a fact about the work item
      that no property holds, the work item never records it: add the
      property. The step's agent passes every argument; nothing fills one
      in from the work item. That holds for an argument typed as a
      reference to a property (x-thunk-ref) too: it gets only that
      property's type, so a call without it fails. A Conversation input
      is the one exception (how-to-build-a-tool-that-uses-a-conversation);

    • mark the outputs the step must produce with requiredOutputProperties
      on the same update (and inputs it cannot start without with
      ​requiredInputProperties). This, not the property's own
      ​constraints.required, is what stops the step finishing without the
      value: constraints.required is checked only when a work item is
      created. For a value needed only in some cases, use finishCondition
      instead (schematized-state, "Choosing which outputs are required");

    • write its full directions, in the shape Write readable directions
      and code
      gives, replacing the skeleton paragraph;

    • replace the stubs of the tools it uses with real implementations
      (the full definition, written as Write
      readable directions and code
      says), try each tool through the
      Builder MCP, and enable them on the step (toolConfig);

    • set the plan's input bindings while doing the first step and its output
      bindings while doing the last;

    • check the result in the design section interface. It shows, for
      each property, whether it is a workflow input, a workflow output and
      required when a work item is created. For each step, it lists what the
      step needs to start and must record to finish. Its changeWith names
      the tool and field that changes each one. These are four separate
      settings: making a property a workflow input does not make it required.

    Finish a step before starting the next. When a step is done, you can run a
    test work item through it before moving on.

  5. Test the main path as soon as it is built, without waiting to be
    asked. Create test data: a few work items for the typical cases the
    thunk will see most, with tests so each is a
    test work item with assertions on its expected outputs. If the thunk
    holds a conversation (a chat app, or a step that emails someone and waits
    for the reply), give each test work item a simulated user
    (set_simulated_user) so the run does not stop to
    wait for a person; read test-a-conversation-with-a-simulated-user.
    For a conversation the end user opens, give the simulated user an
    ​initialMessage: running the new test work item starts its
    conversation with it.
    To turn a finished work item into a test, set
    makes that work item a test work item and gives each conversation it
    holds a simulated user that replays the conversation. Re-running it
    replays the conversation in the same thread and replaces the recorded
    messages and outputs; to keep the original, use copy_as_test instead.
    Leave edge cases
    and failure cases for hardening (step 6): while the design is still
    changing, they cost runs and fixes on behavior that may not last. Run
    them, read the work item's state and the step history, and
    fix what fails. Where the user chose a real connection over a mock, a
    test run reads and changes that real system: say what it will do and get
    their go-ahead first. Then tell the user what you tested and what passed.

    Use realistic inputs. Test data should look like what the thunk will
    get in production: realistic names, amounts and wording, in the real
    formats. For an IMAGE or FILE input, use a real document or image, not a
    placeholder: fetch one from a public URL through the Builder MCP, or build
    one locally for the test (for example a PDF invoice laid out the way real
    ones are, or a scanned-looking image of a receipt) and upload it with
    an upload link (see Files). When you make several, do not
    fill one template with different values: vary the layout from issuer to
    issuer, vary the content (line items, taxes, currencies, date formats),
    and make some of them look captured rather than exported (photographed
    at an angle, cropped, creased, faded, blurred, shadowed). Keep the values
    the tests assert on readable. Tidy inputs
    pass tests that real ones fail (building-a-test-plan).

  6. Harden it when the design is nearly complete. Once the main path
    passes and the steps and tools have stopped changing, add test work items
    for the edge and failure cases: the edge of the range, missing data, bad
    input, a required input left out, each branch, and the things the thunk
    must not do (building-a-test-plan). Run them; the go-ahead rule for a
    real connection in step 5 applies here too. Then run the design review
    (start a design review, then poll it; workflow-review). It is slow and
    costly, so run it once here rather than during the build, and again only
    after a set of fixes is finished. Tell the user what the hardening tests
    and the review found, and what you fixed.

Don't run work items through a step whose tools are still stubs.

For an MCP-server thunk, the interface is the outline: create it with its
tool signatures (kind: "mcpServer" and an interface,
whose tools can give their inputs as inputProperties with name,
​description, type, and constraints, or a batch of stubs), open the thunk and give its link, then
implement and try one tool at a time, and publish the export
when they work.

When you change an existing thunk, apply the same idea: add any new steps and
tool outlines first, then fill them in one at a time.

Write readable directions and code

The thunk's owners read and maintain what you write: step directions, an AI
tool's userGuidance and a code tool's script. Write them so a person can
review them, and so they look like what the rest of the platform produces.

Directions and AI tool instructions. Write a step's directions,
​scheduledDirections and a custom AI tool's userGuidance by the
guidelines in how-to-write-effective-directions. These are the guidelines
the Rewrite button in the app applies, so text written by them survives
a Rewrite with few changes. Give every full set of instructions the same
shape:

  • An Objective: one or two sentences on what the step or tool is for.

  • A Process: a numbered list in the order the work happens, one action
    per item, with any detail as indented bullets under its item.

  • Markdown with real line breaks between the objective, the list and each
    item. Never write the whole text as one paragraph, and never send \n
    as two literal characters in place of a line break.

Two rules are broken most often when the earlier steps of a workflow are
already built:

  • Don't restate or guard the inputs. Never recap what an earlier step
    recorded, and never write "Do not change X". The agent already sees the
    step's input values. A step can change only its output properties, so
    to keep it from changing a property, bind the property as an input and
    not as an output.

  • Put what a value means in the property's description. A definition
    such as "Red means escalate, Amber means monitor" goes in the property's
    ​description, where every step that reads or
    records the property sees it. The directions keep only the actions that
    depend on a value: "If the Disposition is Red, draft the report."

A third rule is broken in every kind of step: don't name tools or
describe how they are called.
Three things are easy to mix up here:

  • A step's input properties are the values its agent sees when the step
    starts.

  • A tool argument typed as a reference to a property (x-thunk-ref)
    takes only that property's type and allowed values, never its value.

  • The agent fills in every argument of a tool call itself, from what it
    sees, when it decides to call the tool.

So the directions say what to do with the data: "look up the supplier's
open orders". They never say which tool to call, which property goes into
which argument, or which field of a result to read. The tools enabled on
the step decide which tool does the work (the "Say what should happen, not
which tool to call" and "Describe the intent, not the mechanics" guidelines
in how-to-write-effective-directions).

The design review reports instructions that name a tool, that break the
first two rules, or that lack the Objective and Process shape.

After you save them, read the instructions back from the design and
check them against the article's quick checklist, as an author would before
pressing Rewrite. Fix what fails.

Code tools. Write a code tool's code (and any responseParser) as
readable source, by the Write code people can read section of
​how-custom-code-tools-work: one statement per line with two-space
indents, a short opening comment on what the tool does and returns (the
first line inside the function when the script is one async function,
which must start with async), fixed values as named constants at the top, descriptive names, small helper
functions, comments on business rules, and named fields for a result of
more than one value. Never send minified code or a script on one line.
When the saved code has a line over 160 characters, or more than three
lines and no comments, the save result carries a warning; the tool
is saved, so reformat the code and send it again as an update.
Code that is not valid JavaScript, for example a script cut off part
way, is refused: nothing is saved for that operation, and the error
names the line where the code breaks. Fix it and send it again.

Files

For a FILE or IMAGE input, or a content folder, never send a file's
contents as base64 in a tool argument. If the file is at a public URL, upload it through the Builder MCP. If it is on your machine (one you made, or one the
user gave you), ask the Builder MCP for an upload link and send the file from your shell.
It returns an uploadUrl and an uploadToken. PUT each file's raw bytes to
​uploadUrl, one file per request, with the token in the X-Upload-Token
header and the file name in the name query parameter:

curl -T invoice.pdf -H "X-Upload-Token: " \
  -H "Content-Type: application/pdf" "?name=invoice.pdf"

You need no other sign-in. Each response lists the files stored so far; use
their files[].url when you create a work item or add files to a
content folder, or read them again later through the same upload link. A
link lasts 30 minutes and takes up to 5 files of up to 16 MB each. The token
is returned only once: keep it to yourself and don't show it in your
replies. If you can't run commands that reach the network, ask the user to
put the file at a public URL and upload it through the Builder MCP.

How to read an article

  • Read the article by its slug through the Builder MCP (the slugs for
    each task are under Which articles for which task).

  • Not sure which slug? Search the help center through the Builder MCP with
    a short query.

  • When a Builder tool or help page names articles for a task, those are the
    articles for that task. Read them the first time you do that work in a
    session.

  • Read 1–3 articles per task, not all of them. Where articles disagree,
    the more specific one wins.

Which articles for which task

Start here (first thunk in a session)

Slug

Why

thunk-application-model

The parts of a thunk and how they fit together

tutorial-how-to-build-your-first-thunk

End-to-end build walk-through

building-thunks-from-reusable-parts

Steps, tools and connections as reusable parts

use-modularity-effectively-when-building-a-thunk

When to split work into steps, scoped tools and modules

Design the workflow

Task

Slugs

Lay out steps / statuses

workflow-plan, workflow-orchestration

Define work-item schema

schematized-state, structure-types, file-and-image-property-types, conversation-properties-for-email

Email or chat thread that waits for a reply

conversation-properties-for-email

Workflow inputs/outputs

workflow-plan, schematized-state

Run a step only when / until a condition holds

run-a-step-only-when-a-condition-matches

Error handling and when a step saves results

choose-how-a-step-handles-errors-and-when-it-saves-results

Human approval / review gates

set-human-approval-gates-for-your-workflow, ai-governance

Scheduled / recurring steps

scheduled-work-on-workflow-steps

How an agent finishes (conclude, ask, error)

how-an-ai-agent-ends-a-run

Recurring patterns

work-tracker-pattern

Write step AI instructions

Read how-to-write-effective-directions before writing or rewriting any
step instructions. It says what the platform already supplies (so you don't
restate it) and gives the guidelines for the logic that is left. Then:

Slug

Why

ai-instructions

The parts of a step's AI instructions

files-in-instructions-and-content-folders

Referencing files and content folders

ai-reliability-concepts-and-principles

Why instructions fail and how the platform guards them

Tools, connections and content

Task

Slugs

Pick / enable tools on a step (toolConfig)

ai-tools, ai-tool-libraries, conditional-tool-availability

Build custom tools

custom-tools, how-custom-code-tools-work, how-to-build-a-tool-wrapper

Specific tool recipes

how-to-build-a-custom-database-tool, how-to-build-a-spreadsheet-lookup-tool, how-to-build-a-tool-that-uses-a-conversation

Test a tool

testing-custom-tools

Give a tool unit tests, run them, read graded results — test one tool without a work item; iterate on selected cases (not saved), then run the whole suite once (saved as its test status)

testing-custom-tools, custom-tools

Save reports, set up the report automation (scheduled report emails / summaries), run a report (the design section reports) — saving reports is refused on Production; running a report works there

how-to-save-a-report-from-your-work-items, send-reports-on-a-schedule-with-the-report-automation

Connections

connections-to-business-applications, connecting-to-external-systems-and-tools, reuse-thunk-interfaces-and-plug-in-connections

Content folders

content-folders, spreadsheet-content-folders

Documents, PDFs, spreadsheets

working-with-documents-and-pdfs, working-with-spreadsheets, generate-documents-from-templates

Kinds of thunk and how they are reached

Task

Slugs

Chat App (kind:"chatApp")

set-up-the-hosted-chat-ui-for-your-thunk, conversational-interface-via-a-rest-api

MCP server (kind:"mcpServer")

publish-your-thunk-as-an-mcp-server-for-ai-agents, modular-reuse-of-tools

Inbound requests: add, change or remove webhooks; change the email channel and who may send email to it; let a channel update existing work items instead of adding new ones

inbound-requests, receive-requests-through-a-webhook-or-a-shared-form, send-requests-to-your-thunk-by-email

Copy a thunk

how-to-copy-a-thunk

Clean up or upgrade a live thunk in a copy, with stand-in tools

improve-a-live-thunk-safely

Run and test

Task

Slugs

Create / run / rerun work items

workflow-orchestration, manual-testing

Automated tests

evals-automated-tests, building-a-test-plan

Test a conversation: a test work item whose end user is a simulated user, with assertions (set_simulated_user then rerun, or copy_as_test then run)

test-a-conversation-with-a-simulated-user, evals-automated-tests

Mock external systems for testing

improve-a-live-thunk-safely, evals-automated-tests

Production readiness

shipping-enterprise-thunks-testing-and-quality-principles

Design review

workflow-review

Switch AI model (thunk options, model definitions)

move-a-thunk-to-a-new-ai-model, supported-ai-models-and-what-happens-when-they-change

Pause / resume

pause-and-restart-a-thunk

Delete work items

how-to-work-with-your-work-items-list, data-retention

Add people to a thunk (people outside your organization: the user adds them at shareUrl)

users-and-access-controls, users-organizations-and-governance

Read or update a thunk's work log (Owner or Admin only; Production does not block this). For a unit of definition edits, begin a work-log note first and end it when you are done (end is optional). create is a closed note that does not attach later edits.

thunk-work-log, users-and-access-controls

Diagnose a run

Symptom

Slugs

General health check / cleanup pass, or no specific symptom yet — read first, it sets the order (stuck → errors → wrong results → design review → time → cost)

check-a-thunks-health-where-to-start

A run failed / errored — find which workitems

reading-a-failed-run, troubleshooting-errors

Agent did the wrong thing / unreliable

troubleshooting-ai-reliability-issues, ai-reliability-mechanisms, then how-to-write-effective-directions to fix the instructions

Inspect what happened

step-diagnostics, tool-call-history-and-performance

What a tool really received and returned, across work items

tool-call-history-and-performance, testing-custom-tools

Too slow

time-analysis-how-long-your-thunk-takes, what-working-waiting-and-waiting-for-tool-mean

Too expensive

token-costs-what-your-thunk-spends-on-ai, understanding-llm-costs-and-latency

Work item stuck waiting

find-work-that-is-waiting-on-you, how-an-ai-agent-ends-a-run

Each of the bold articles opens with a Concepts section (how to think
about it) followed by Details (specific rules and cases). Read Concepts
first; go to Details for the specific case you're dealing with.

Open the thunk in the browser pane

This is mandatory, not optional. As soon as creating or copying a thunk
returns a url (or, when working on an existing thunk, as soon as the
thunk's summary or the thunk list returns one), open that URL beside the
conversation so the user can watch the thunk while you work on it. Do this for every thunk you
build, copy, edit, inspect, analyze, troubleshoot, review or report on,
before continuing with other work, and always put the clickable link in your
message too. Reuse the current browser tab when possible. If opening fails or
no browser pane is available, say so and give the link anyway. Do not treat
this as optional because the user did not explicitly ask to see the UI.
Never let it hold up the build. The panel is mainly for the
user to watch. Make changes through the Builder MCP tools by default: they are
faster and more reliable than the UI, and their results can be checked.

How you open and move the pane depends on the app you run in. Before you
open it, read the guide for your app through the Builder MCP:

  • The Claude desktop app, or a cloud session linked to the user's computer
    with the Claude browser-pane tools: show-a-thunk-in-the-claude-desktop-app.
    It also says what to do at the start of a session, so the user approves the
    site and signs in once, up front.

  • The Codex desktop app: show-a-thunk-in-the-codex-desktop-app.

The instructions a Builder session returns name the guide for your app when you
pass agent and agentSurface. With no side-panel browser, follow
​With no side-panel browser.

When the user asks to see it done in the UI. If the user explicitly asks to
see how to do something in the Thunk.AI app (for example "show me how to add a
step in the UI" or "walk me through connecting Gmail"), you may drive the app in
the side-panel browser: navigate, click and type there, and say what you are
doing at each step so they can follow along and do it themselves next time.
Where the session has no tools that can drive that browser, describe the clicks
for them to follow instead.

  • Do only what they asked to see, then go back to the Builder MCP for the rest
    of the work, and check the result in the design.

  • A general request such as "build it" or "fix it" is not a request to use the
    UI. Neither is an MCP call that failed or seems slow.

  • Never sign in for the user, and ask before anything in the UI that deletes,
    publishes, shares or touches a live external system.

Open every app URL in agent mode

Add agentMode=true to every Thunk.AI app URL you open in the pane, navigate
to, or give the user as a link: the url values the Builder MCP returns and
the URLs you build from the table in Show each change in the pane. Add it
as a query parameter: after ? when the URL has no query yet, otherwise after
​&, and always before any #. For example, <host>/thunk/<thunkId> becomes
​<host>/thunk/<thunkId>?agentMode=true, and
​<host>/thunk/<thunkId>/testing?testingTab=rows becomes
​<host>/thunk/<thunkId>/testing?testingTab=rows&agentMode=true.

Agent mode is the app as it is shown to a user who is watching you work. You
are editing the thunk, so the planning agent chat and the Tool Builder AI
chat start hidden, which leaves more room for the thunk itself. The user can
bring either back with Show AI. Agent mode lasts for that browser tab,
including after they click around in the app; agentMode=false turns it off.

Add it only to links into the Thunk.AI app. Leave help-center articles,
upload links and short.thunk.ai links as they are.

Show each change in the pane

Once the pane is open, keep it on what you're working on. Whenever you change
the thunk through the Builder MCP, or fetch specific information from it,
navigate the pane to the view that shows it: before the call, so the user
watches the change land, or after it, to show what you changed or found.
If the page doesn't show the change after the call, navigate to the same URL
again to reload it.

Do this by changing the URL only, in the same tab, as the guide for your
app describes (show-a-thunk-in-the-claude-desktop-app or
​show-a-thunk-in-the-codex-desktop-app). Never click through the app to get
there. Navigate once per view, not once per call, and skip it if the pane is
already there. Prefer a url the tool returned; otherwise build one on the
thunk URL's host (<host> in the table). Either way, add agentMode=true
(see Open every app URL in agent mode):

What you changed or fetched

URL to show

The thunk as a whole

the thunk's url

A step

that step's url from the design

A work item

the work item's url

One step's run on a work item

that step run's url

Test work items / test results

<host>/thunk/<thunkId>/testing?testingTab=rows / ?testingTab=results

Connections and their tools

<host>/thunk/<thunkId>/tools

Errors

an error's runUrl, or <host>/thunk/<thunkId>/monitor?tab=errors

Review

the review's url

Reports

<host>/thunk/<thunkId>/monitor/reporting

Files and folders

<host>/thunk/<thunkId>/files

MCP export

<host>/thunk/<thunkId>/deployment?deployTab=export

Thunk options

<host>/thunk/<thunkId>/settings

Members

<host>/thunk/<thunkId>/team

For anything not listed, show the thunk's url. If the pane isn't open (no
side-panel browser, or it couldn't be reached), give the URL as a link
instead. This doesn't apply to work you do on a thunk that is not the one in
the pane, such as reading a source thunk while building a copy.

With no side-panel browser

In the Claude Code terminal, Cursor, Copilot, Claude chat without the desktop app, or
any agent with no side-panel browser, give the thunk's link (url in the
thunk summary) and carry on. Don't build a page of the thunk or write one
to a file for the user to open.

In every case, give step links (each step's url in the design) when
you discuss a specific step.

Analyzing a thunk

To analyze a thunk — often a customer's production thunk you must not edit —
read the analyze-a-thunk-with-the-builder-mcp article through the Builder MCP before you start,
and follow it. It covers three scenarios: (A) troubleshooting one work item
or step, (B) a health check, and (C) a cleanup / upgrade done in a mocked
copy with tests from real data, a hand-over to the owner and a .docx report.
It works only through this Builder MCP; when the cause is the platform, it
files feedback and has the user raise a support ticket.

Explaining a thunk

When the user asks for an explanation, first tell which of two asks it is:

  1. Explain this thunk: the thunk's purpose, workflow, data, tools and
    features. Assume the user knows the platform, and explain how this thunk
    uses it.

  2. Explain the platform through this thunk: use the thunk as a teaching
    example and guide the user through the Thunk.AI app, explaining each
    platform concept as well as how this thunk uses it.

Both follow the same flow; the only difference is whether you also explain
the platform. If you can't tell which one the user wants, ask, with the two as
numbered choices.

  1. Read the thunk through the Builder MCP first, its summary and its
    design, so that you explain its actual design. Read the help
    articles for the parts you will explain (see Which articles for which
    task
    ), especially for the second ask.

  2. Open the thunk in the browser pane and give its link (see Open the
    thunk in the browser pane
    ). Tell the user the sections of the tour
    in a short list. A usual order is: the thunk's purpose, the Workflow Plan
    and its steps, the work item's data (properties), tools and connections,
    files and content folders, tests, and options such as approval gates.
    Leave out the sections this thunk doesn't use.

  3. Show and explain one section at a time. Navigate the pane to the view
    for that section (the URLs in Show each change in the pane), then
    explain it, mapping the platform concept to this thunk's configuration.
    Keep the UI read-only: navigate and show, but never save, run, delete,
    publish or share anything there. With no browser pane, give the link to
    each section's view instead.

  4. Stop after each section and wait for the user to choose what to see
    next. Don't advance the tour until they choose to continue. End each
    section with numbered choices: 1. Continue the tour (naming the next
    section), then two or three deeper dives into the section just shown. The
    user can reply with a number.

  5. After a deep dive, stay at the same place in the tour and offer
    numbered choices again, with 1. Continue the tour first.

Explaining a sample. Every sample already has test work items that
have run. Show how a sample works through one of those runs: list its work
items, pick one that finished, and show it and its step runs in the pane.
That is quicker and clearer than copying the sample and running a new work
item, so don't do that just to explain it. Offer a copy only when the user
wants to change the sample or try it on their own data. If no run can be
read, say so and offer the copy. When the sample's summary has a videoUrl,
give that link once as a short video about the sample, for the user to watch
if they want.

Don't claim that a feature works just because a description or a step's
directions mention it. Check its configuration through the Builder MCP (for
example, that the tool is enabled on the step, that the connection exists,
that the property is bound), and say plainly when you couldn't verify
something.

An explanation changes nothing and runs nothing. If the user asks for a
change along the way, say so, and make it through the Builder MCP as for any
other edit.

Rules

  • Never guess a slug. If one this guide names returns not-found, search the
    help center for its title instead. A new article becomes readable only after
    the next Thunk.AI release.

  • Don't paste article text back into a thunk's instructions. Apply the
    guidance in your own words for that thunk.

  • If an article is wrong or missing something you needed, report it as
    feedback through the Builder MCP.

Before finishing

  • Did I open the relevant thunk in the in-app browser and include its link?

  • Is every thunk I created filed in the project's folder?

  • Did every Thunk.AI app URL I opened or gave carry agentMode=true?

  • Did every fact I gave about the thunk come from the Builder MCP, not from
    the browser pane?

  • Is every fact a later step or tool needs its own property, with the most
    specific type that fits, and in requiredOutputProperties of the step
    that produces it when the work cannot go on without it?

  • Do the directions and AI tool instructions I wrote open with an objective
    and a numbered process, and is every code tool I wrote readable source?

  • If I'm waiting for the user, did I end with numbered choices?

  • Did I answer the user's direct questions, and paste the output they came
    for as text rather than summarizing it?

  • Am I ending on a result, a question or a plain blocker, and not on "still
    running"? If this turn has run past about five minutes without a result,
    did I show what exists and hand back?

  • If the pane shows the Thunk.AI sign-in screen, did my reply say so in plain
    words, without telling the user to look at the pane?

Did this answer your question?