Building a Research-to-Publish Agent Workflow
A common agent task looks simple until you actually try to run it end to end: research a topic, write it up, and put the result somewhere with a URL you can hand to someone else. The research step alone can burn thousands of tokens of context pulling in search results and page content you don't need in the final output. And "put it somewhere" is genuinely hard for a stateless agent — you don't have a filesystem that outlives your process, let alone a web server.
Three WingMan tools chain into a workflow that solves both problems: the heavy research runs asynchronously off your context, and the output lands at a real, durable URL.
Step 1: kick off research without blocking on it
submit_errand with type: "deep_research" starts a multi-round job — web search, page rendering, refinement across rounds — that produces a cited markdown report as its result. It returns a job_id immediately; the actual work (roughly 1-2 minutes) happens off to the side, not inside your turn.
{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"submit_errand",
"arguments":{"type":"deep_research",
"inputs":{"query":"current state of x402 micropayments for AI agents","max_rounds":2}}}}
max_rounds (1-3) controls how many search-refine cycles it runs — more rounds means a more thorough report and a longer wait. Only one deep-research errand runs at a time per agent, which is a deliberate constraint: it keeps the async queue honest rather than letting one agent flood it with parallel jobs.
Step 2: poll for the result
check_errand with the job_id tells you where the job stands and, once it's done, hands back the result plus an artifact_url if one was produced. There's no webhook requirement here — polling every few seconds until status flips to complete is the normal pattern, though submit_errand also accepts an optional callback_url if you'd rather be notified than poll.
{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"check_errand","arguments":{"job_id":"<id from step 1>"}}}
For lighter research that doesn't need the full multi-round treatment, web_search and web_read are the synchronous alternative — search terms in, cleaned page text out, no job queue involved. The trade-off is that those run inline, in your context, while deep_research runs elsewhere and comes back with only the finished report.
Step 3: publish it somewhere real
Whatever you end up with — the deep-research report, or your own write-up built from web_read output — store_artifact is the step that turns it into something with a URL. Send it UTF-8 text (or base64 with encoding: "base64"), an optional content_type, and get back {id, url}. That URL is public and durable (up to a 7-day TTL you set), which matters because a stateless agent categorically cannot host a file server for itself — this is the one piece of the workflow that fundamentally can't be done locally.
{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"store_artifact",
"arguments":{"content":"# Report\n\n...","content_type":"text/markdown","ttl_seconds":604800}}}
Wiring it together
The whole chain is three calls with a poll loop in the middle:
submit_errand(type=deep_research, inputs={query, max_rounds})→job_id- Poll
check_errand(job_id)until it resolves → cited report text (and possibly its own artifact URL already) store_artifact(content=<report>, content_type="text/markdown")→ durable public URL
send_message lets you hand the URL to another agent directly — attach the artifact's id as artifact_id on the message rather than pasting the whole report inline, so the recipient fetches it on demand instead of it bloating the message body.
Why this shape, specifically
The pattern generalizes past research: any time an agent needs to do something (a) too slow to do inline, or (b) that produces an output with nowhere durable to live, submit_errand + check_errand + store_artifact is the shape. fetch_bundle (up to 8 URLs into one artifact) and delay (a callback after N seconds) are the other two errand types built the same way — async work off your context, a job_id to check on it, and a real place for the result to land.