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.
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.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.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.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.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".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.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 reportsblocked.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
readblockedand 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:
Create it with every step laid out, then open it. If
creating a thunk acceptssteps(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 anypropertiesthe 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 outtoolConfig, and any binding to a property that doesn't exist
yet. Check theresultsthe response gives
for itsstepsandproperties(or the batch'sresults) and fix any
errorentry. Then
open the thunk in the browser pane (see Open the thunk in the browser
pane)
and give its link.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 withsecretPendingtrue, 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 withkind:"mcpServer"and aninterfacewhose 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, oroutputPropertiesin 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
(readOnlytrue), 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
sectioncontentFolders.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 istools.<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 (throwan error naming the status and the reason, such
as404 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 withsignature(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 ($statusisrejected),
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", analiassuch
asCRM, theinterface, andbindToset 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
toolConfigevery write tool its agent must not call directly, and turn
the plug-in off on steps that do not use it. A step'stoolConfigdoes
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
(toolConfigwithdisable_toolordisable_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-safelyand inanalyze-a-thunk-with-the-builder-mcp(C3).
Tell the user which systems are mocked.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 shortcodebody that
returns a "not implemented yet" message. Give the output as
outputProperties(property types, withconstraints.requiredon each
field the tool always returns) oroutputSchema. A code tool that
returns one value can declare a bare value, such as
{"type":"string","enum":["Red","Amber","Green"]}. Anaitool's output
is an object of fields. Aclassifierneeds at least oneYES_OR_NO
field, or aTEXTfield with anenumconstraint, before it can be used.
Leave the output out of a later update to keep it;{}or[]clears
it. AdocumentTemplateorspreadsheetLookuptool'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'soutputSchema,batchInvokeand
batchConcurrency, and withincludeToolImpl, for anaiorcode
tool, thetoolConfigof what it may call. Stub anaitool
as anaitool (with one-lineuserGuidance), 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 anaiorcodetool may
call later, sendtoolConfigon its update, with the
same ops a step'stoolConfigtakes.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):NUMBERfor an amount or count,
CALENDAR_DATEorDATEfor a date,YES_OR_NOfor a flag,TEXT
with anenumconstraint for a fixed set of values,FILEorIMAGE
for a document or picture (file-and-image-property-types),
isListTypefor 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 theSTRUCTURE_TYPE_REFtype pointing at it
(structure-types). A Structure Type is preferred overJSON_OBJECT
for any shape you know, and over several loose columns when the parts
belong together. Reach forJSON_OBJECT(with anexampleObject)
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.convoSendMessageModetosendAndWaitForReplyand
list that conversation under the step'soutputProperties; leaving
the mode unset does not wait. Use plainTEXT
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
NUMBERproperty is a number argument, anenumis 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
anenum, 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.requiredis checked only when a work item is
created. For a value needed only in some cases, usefinishCondition
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. ItschangeWithnames
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.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, withtestsso 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; readtest-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, usecopy_as_testinstead.
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).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 |
| The parts of a thunk and how they fit together |
| End-to-end build walk-through |
| Steps, tools and connections as reusable parts |
| When to split work into steps, scoped tools and modules |
Design the workflow
Task | Slugs |
Lay out steps / statuses |
|
Define work-item schema |
|
Email or chat thread that waits for a reply |
|
Workflow inputs/outputs |
|
Run a step only when / until a condition holds |
|
Error handling and when a step saves results |
|
Human approval / review gates |
|
Scheduled / recurring steps |
|
How an agent finishes (conclude, ask, error) |
|
Recurring patterns |
|
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 |
| The parts of a step's AI instructions |
| Referencing files and content folders |
| Why instructions fail and how the platform guards them |
Tools, connections and content
Task | Slugs |
Pick / enable tools on a step ( |
|
Build custom tools |
|
Specific tool recipes |
|
Test a tool |
|
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) |
|
Save reports, set up the report automation (scheduled report emails / summaries), run a report (the design section |
|
Connections |
|
Content folders |
|
Documents, PDFs, spreadsheets |
|
Kinds of thunk and how they are reached
Task | Slugs |
Chat App ( |
|
MCP server ( |
|
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 |
|
Copy a thunk |
|
Clean up or upgrade a live thunk in a copy, with stand-in tools |
|
Run and test
Task | Slugs |
Create / run / rerun work items |
|
Automated tests |
|
Test a conversation: a test work item whose end user is a simulated user, with assertions ( |
|
Mock external systems for testing |
|
Production readiness |
|
Design review |
|
Switch AI model (thunk options, model definitions) |
|
Pause / resume |
|
Delete work items |
|
Add people to a thunk (people outside your organization: the user adds them at |
|
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. |
|
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) |
|
A run failed / errored — find which workitems |
|
Agent did the wrong thing / unreliable |
|
Inspect what happened |
|
What a tool really received and returned, across work items |
|
Too slow |
|
Too expensive |
|
Work item stuck waiting |
|
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 |
A step | that step's |
A work item | the work item's |
One step's run on a work item | that step run's |
Test work items / test results |
|
Connections and their tools |
|
Errors | an error's |
Review | the review's |
Reports |
|
Files and folders |
|
MCP export |
|
Thunk options |
|
Members |
|
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:
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.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.
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.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.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.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.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 inrequiredOutputPropertiesof 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?
