AI Engineer World's Fair 2025
AI Pipelines and Agents in Pure TypeScript with Mastra.ai
Read the talk
AI Pipelines and Agents in Pure TypeScript with Mastra.ai
A workplace-frustration meme generator shows how typed workflows, runtime schemas, inspectable steps, and an agent interface fit together in Mastra.
From a talk by Nick Nisi and Zack Proser
Before you start: Familiarity with TypeScript, asynchronous functions, JSON, and basic Git usage will help you follow the workflow and schema examples.
Turn a workplace frustration into a meme
How do you turn a workplace complaint into a meme someone can share? Nick Nisi and Zack Proser start with that deliberately small application. The exercise is humorous, but its engineering problems—interpreting an input, moving structured data between operations, calling external services, and presenting the result—also appear in practical workplace automation. The workshop proceeds from concepts to collaborative coding, with questions interleaved throughout.
The presenters bring developer-tooling experience from WorkOS, whose enterprise features include SSO, directory sync, audit logs, and fine-grained authorization. Their example is familiar: an application works, a prospective customer asks for enterprise sign-on, and the team needs to add it without building an identity platform. That identity concern extends to agents acting on a user's behalf. Nisi introduces himself through developer experience, the dysfunctional.fm podcast, TypeScript, and Vim; Proser comes from application and infrastructure development, with more recent work in GenAI, MLOps, and retrieval-augmented generation.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Put explicit structure around model decisions
Mastra supplies three application-building primitives: workflows, tools, and agents. Around those primitives sit a local playground, persistence, memory, observability, and evaluations. The workshop concentrates on a subset of that system, with Mastra contributors in the room to answer questions about the framework itself.
A workflow begins with a repeatable sequence. Suppose a colleague keeps scraping a site, identifying interesting people, looking up their information, obtaining an image, and writing an email. Those operations have an order and pass data forward. A workflow makes the sequence explicit, including transformations between steps. Because model outputs vary, runtime schemas provide a boundary at which the application can validate data, apply explicitly configured coercions, or handle errors. Nisi compares the chained API to RxJS: start with a value, perform an operation, map its result, and continue. The control flow can be prescribed even when an individual generation remains nondeterministic.
Tools are callable functions: filesystem operations, API requests, database queries, or business logic. They let a model retrieve information outside its training data and take actions. The presenters initially implemented the meme operations as independent tools, then changed the design so the agent would invoke a workflow containing the sequence.
An agent adds a human interface to those capabilities. A busy or nontechnical colleague can describe a task instead of assembling its inputs manually. In this application, the agent combines instructions with permission to invoke a workflow. More generally, an agent may inspect the current state and choose a next action; the important design choice is how much of that decision-making belongs to the model and how much belongs to application code.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Give the model context, then define the exercise
Model Context Protocol, or MCP, provides a shared interface for connecting AI clients to tools and context. Nisi's practical example is the GitHub MCP server in Claude Desktop. Given an issue and an example application, the assistant can retrieve comments, follow linked repositories or examples, and suggest how to reproduce the problem. The useful change is that the conversation can acquire the relevant evidence as it proceeds.
The presenters also introduce an MCP-only shirt-ordering demonstration. During the workshop, the shirt promotion was free and required a supporting MCP client; the address now leads to the WorkOS Shop, so the original offer should be understood as historical. Their account of a packed MCP Night at the Exploratorium adds context for the interest in the protocol, before the workshop returns to its own application.
The meme generator accepts a frustration, selects an existing template, writes new captions, and publishes the resulting image through Imgflip. OpenAI supplies the language understanding. The resulting URL is shareable, but Imgflip documents that images are accessible to anyone with the URL and that low-view images may be deleted; it is neither private storage nor guaranteed permanent hosting. The build starts with one testable workflow step, expands into the complete pipeline, and only then adds an agent.
The companion workshop instructions provide the self-paced path. Checkpoint branches let participants catch up, while main contains the completed application. The recording starts from a step-zero checkpoint and installs dependencies with npm. Its priority is understanding the pieces well enough to experiment, rather than finishing every exercise at the same speed.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Choose who controls the sequence
An audience question makes the tools-versus-workflows distinction concrete. The createTool and createStep APIs can look similar, but they assign orchestration differently.
| Arrangement | Who chooses the sequence? | Useful when |
|---|---|---|
| Independent tools | The model chooses calls at runtime | Several actions are available, and the request determines which are needed |
| Workflow | Application code specifies steps and transforms | A task must follow a known sequence |
| Agent with both | The model chooses a capability; a workflow controls its internals | Conversation needs flexibility around a repeatable task |
Proser's example of giving an agent a hundred tools and asking it to go to Disneyland illustrates the ambiguity created by a large, loosely constrained action space. A workflow reduces the number of orchestration decisions the model must make.
The first setup problems are ordinary development problems. The public workshop repository does not require private access, but work laptops without configured SSH credentials can fail when given an SSH clone command. Use its HTTPS clone option in that case; an SSH address with a colon is not the same thing as a browser URL. While participants resolve that, Nisi recommends Mastra's documentation MCP server, which he installed in Claude Code and Claude Desktop to supply framework-specific context. The brief follow-up about a JSON configuration object does not develop into a complete setup procedure.
The initial success criterion is deliberately modest: open the playground with no agents, tools, or workflows registered. Asked why Mastra, Proser points to TypeScript, the Vercel AI SDK's provider abstraction, built-in evaluations, and local development ergonomics. He recalls improving an existing pipeline by changing its model to GPT-4o. His broader preference is for shared primitives that colleagues can understand and invoke through code or an API, instead of independently rebuilding pipeline infrastructure in bespoke scripts. These are reasons drawn from early use, rather than a framework benchmark.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Finalize a workflow before running it
Another question concerns code that lists steps and then also chains .then(...). A Mastra contributor describes step arrays as a linear definition and fluent syntax as the route to branching, conditionals, and loops, mentioning .then, .if, .branch, and .doWhile. The essential distinction is between defining the graph and executing a run: .commit() finalizes and returns the workflow; it does not itself run the task. The contributor also connects finalization to the cached, synchronized representation used by local visualization. The companion example retains both a steps declaration and explicit chaining, so the discussion should not be read as a blanket instruction to remove either from an arbitrary version of the API.
The first checkpoint contains a Mastra instance and memory scaffolding, but no registered application capabilities. Starting the development server exposes the playground's Agents, Networks, Tools, MCP Servers, Workflows, and runtime-context views. Nisi says localhost:4011 in the recording; the inspected companion instructions use localhost:4111/workflows. Follow the address printed by the development server for the checkout you are using. The server also exposes an API endpoint and Swagger specification. The next assignment is a disposable, single-step workflow that will make those inspection tools useful before the full application exists.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Improve coding context without prescribing an editor
During the build, the discussion turns to AI inside Neovim. Nisi demonstrates Avante's chat interface, while Proser mentions prior use of NeoAI; Goose comes up as something Nisi has not tried. Nisi ultimately prefers Vim in one Tmux pane and Claude Code in another. That keeps his usual editor interactions intact: he can maximize Vim while the assistant works, then restore the split when he needs the conversation.
An audience member pushes back on mandating a single framework or editor. The group distinguishes useful shared defaults from a top-down standard that ignores product needs. Another question uses Mario Kart booster packs as an analogy: can a team give developers a strong starting configuration? Nisi compares evolving Cursor Rules to tests that grow with a codebase. Proser points instead toward documentation delivered through MCP, which can serve multiple compatible clients.
Proser reports zero hallucinations in one coding session after enabling Mastra's documentation MCP server, with requested features generated correctly on the first attempt. He also used TypeScript linting and compilation to check the code. The portable mechanism is access to relevant documentation; that anecdote does not establish a guarantee that a documentation-connected assistant will always produce correct code.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Separate execution engines, schemas, and retries
A question about durable execution brings up 12-Factor Agents and Mastra's then-new Workflows vNext. A contributor explains that the original engine used XState, while vNext aims to preserve the workflow syntax, visualization, and development server while allowing a different execution engine underneath. Inngest is the first integration discussed; Temporal and Cloudflare Workflows are examples of requested alternatives. The contributor attributes Inngest's experimental label to its recent release and offers an expectation of API stability, rather than a demonstrated durability test.
Deployment is a separate choice. The contributor describes a default Hono server bundle that can run in Docker, adapters for Cloudflare, Netlify, and Vercel, and Mastra Cloud. Embedding the application in Express or a Next.js backend is also discussed. The design goal is to keep the application portable rather than require one hosting environment.
Structured output raises a different boundary question: what happens when the prompt and schema disagree? A basic string type is easier for a model to satisfy than a refinement such as exactly four characters. Zod describes and validates data at runtime; it does not make TypeScript's compiler inspect a future model response. Nor should OpenAI's schema adherence be attributed to Zod alone: its Structured Outputs explanation describes model training plus constrained decoding. That is distinct from validating a returned value in application code.
A Mastra contributor reports testing 30 schemas across 16 models and finding tool calling harder than structured generation. The contributor corrects the initial suggestion that no framework-specific handling exists: Mastra added a compatibility layer to improve tool-schema behavior. The matching tool-schema compatibility study describes schema transformations and constraint descriptions. Its headline and full table use different model populations, so the useful point here is the additional compatibility mechanism, not a general application-accuracy claim.
Repeated calls do not necessarily mean schema validation is automatically repairing a result. During development, the presenters accidentally sent a large base64-encoded image and observed repeated failures. A contributor suggests the agent may have continued until maxSteps, but does not establish that diagnosis conclusively. Asked whether a five-character string would automatically be retried against a four-character constraint, the contributor leaves that exact case unresolved. Application code still needs an explicit policy for a rejected value. For example, validation can preserve the evidence of failure without silently truncating it:
typescript
import { z } from "zod";
const fourCharacters = z.string().length(4);
export function validateLabel(value: unknown) {
const result = fourCharacters.safeParse(value);
if (!result.success) {
return {
status: "rejected" as const,
issues: result.error.issues,
};
}
return {
status: "accepted" as const,
value: result.data,
};
}
A model can return nonconforming content; whether to reject, request a correction, or take another path is a separate application decision.
The serverless follow-up remains less definite. A community Lambda deployer is mentioned, but the room does not establish whether workflow steps become separate functions or share one deployed function. That matters for long-running work and platform time limits. The discussion also cites an unsupported Cloudflare bundle-size figure; use the current Workers limits for deployment planning rather than treating that spoken figure as a platform contract.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Inspect the first structured result
At the next checkpoint, the Mastra instance registers the test workflow, and a workflows directory contains its implementation and schema. Refreshing the playground reveals a one-step graph accepting raw text about work frustration. Nisi enters “Conference Wi-Fi sucks.” The node turns green, and inspecting its output shows the original text, a frustrated mood, and technology as the frustration category. The intermediate object is now visible before any template selection or image publication is involved.
A complaint becomes structured input for the next step
Constructed example: Object IDs and display labels are teaching labels, not claims about exact property names in the demonstrated JSON.
Conference Wi-Fi sucks
Operation: Run the demonstrated frustration-extraction step.
Original text
Conference Wi-Fi sucks
Conference Wi-Fi sucks
Mood
Not present
frustrated
Frustration category
Not present
technology
The next exercise expands this into the complete workflow, with data mappings between operations. An audience member asks whether the JSON is what gets passed to another step. Precisely: its structure is an application contract, chosen because downstream operations need those values. Strings, arrays, and nested objects are available building blocks; the useful schema is the one that carries the information the next operation requires.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Carry the template's caption slots forward
Designing those contracts requires looking ahead. Frustration subject and intensity help choose a template, but template selection must return more than an image name. Nisi uses a Rock taxi meme to explain that Imgflip templates expose caption boxes: a template may have three slots, and the publication request must supply text for those slots. Caption-slot metadata belongs in the pipeline contract. If it disappears between selection and caption generation, a later step cannot reliably fit the text to the image.
A compact TypeScript boundary can make that relationship explicit. Here the selected template retains its ID and slot count while caption validation checks that the supplied text fits it:
typescript
import { z } from "zod";
const templateSchema = z.object({
id: z.string(),
name: z.string(),
boxCount: z.number().int().positive(),
});
export function prepareCaptionRequest(
templateInput: unknown,
captionsInput: unknown,
) {
const template = templateSchema.parse(templateInput);
const captions = z.array(z.string())
.length(template.boxCount)
.parse(captionsInput);
return {
templateId: template.id,
boxes: captions.map((text) => ({ text })),
};
}
The schema verifies the shape and count; it cannot decide whether the captions are funny or appropriate. The displayed three-panel meme makes the physical layout concrete before the workshop moves on to tuning prompts, temperature, and model choice in the playground.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Debug the external service before blaming the model
Failures at the generate-meme step lead to a concrete credential checklist:
- Use the Imgflip username, not the account email.
- Supply an Imgflip password. If the account was created through Google sign-in, set a password in Imgflip's settings afterward.
- Confirm that the required credentials exist before investigating an opaque generation error.
Proser says the workshop moved away from a circulating anonymous credential to participant accounts to reduce rate-limit trouble; no comparative limit is established. Participants can also recover through a checkpoint branch after preserving unfinished work. The presenters explain another scope decision: they removed a step that generated an entirely new image because the account-verification process they encountered was too burdensome for a workshop. Existing templates keep the exercise focused on orchestration.
Asked how these applications are invoked outside the playground, Proser distinguishes his deployed experience from his experiments. His existing work applications mostly use the Vercel AI SDK behind web interfaces, often hosted on Vercel; Mastra internal tooling is more recent. Invoking a workflow through an API, perhaps from a nightly cron job, is a pattern he wants to explore rather than one he claims to have fully established in production.
Participants begin sharing working memes in Slack, including frustrations about loud sales staff beside a systems engineer and every project being high priority. The presenters also acknowledge offensive outputs and incomplete attempts to curb them. Successful execution and structurally valid captions do not establish suitable content. The book giveaway and shared examples mark progress, but the application still needs judgment about what it publishes.
A data-engineering question compares workflows with Airflow DAGs. Proser first takes a detour through GraphRAG—vector search combined with knowledge-graph traversal—then returns to his own simpler experience with sequential steps and branches. An audience member asks whether an expensive pipeline can resume at a later step without repeating earlier calls. The discussion does not settle selective reruns. It does identify a separate cost control: choose a model per step, using a less expensive model for straightforward extraction and reserving stronger models for harder work. Independent tools could also let a model skip unnecessary operations, though that changes who controls execution. A caching question receives no concrete implementation.
Further credential troubleshooting returns to process state: inspect .env, restart the running server after changing it, and check for accidental spaces in values. Saving a file does not mean the current process has reloaded its environment. The remaining error in that exchange is not conclusively resolved on the recording.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Watch a conversation become an authenticated transaction
The live shirt demonstration exposes a useful failure mode. Nisi asks Claude for a shirt, and it initially responds that it cannot provide physical merchandise. The MCP shop is disconnected. After reconnecting, repeating the request, and allowing the tool call, the conversation can retrieve the available shirt. Connection, tool approval, and physical fulfillment are distinct stages; waiting for approval is not the same as waiting for shipping.
The shirt description includes “Context is everything.” The assistant then collects size, company, and a complete mailing address. Nisi supplies XL and WorkOS, improvises an address, and adds the missing locality information when prompted. The tool returns an order confirmation, after which he asks about the order. The model is helping collect arguments and present results around an external action.
The hosting explanation distinguishes this instance, which the presenters believe is on Vercel, from a separate Cloudflare version that Nisi says stores order information in KV. WorkOS supplies authentication. Because he is already signed in, much of the login is hidden, but connecting completes an authentication flow and obtains a token. He then asks whether the system knows identity information he has not typed into the chat. That is the broader commerce pattern: natural-language requirements, authenticated tool access, argument collection, and an order operation. Their joke about talking a system into a discount is not a demonstrated vulnerability, but it points to why transaction rules must live beyond conversational persuasion.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Trace a complete meme through four steps
Returning to the meme application, the next checkpoint exposes the full workflow. It contains four substantive steps, with mappings between them and a final output mapping:
| Step | Responsibility |
|---|---|
| Extract frustrations | Turn the complaint into structured information |
| Find base meme | Select the template and carry its metadata forward |
| Generate captions | Write text for the selected template |
| Generate meme | Submit the template and captions to Imgflip |
Each step has input and output schemas. The mappings adapt one result to the next operation's contract rather than leaving that wiring implicit.
The live input concerns someone being too busy preparing a workshop to review a pull request. The graph runs, its steps turn green, and the extractor classifies the problem as communication. Later inspection shows the selected template is Disaster Girl, while the generated captions concern a meeting that could have been an email and everyone smiling as if things are fine. The result illustrates both a completed pipeline and the semantic drift that can occur inside it: the final joke is related to workplace communication but has moved away from the original PR complaint.
The useful debugging surface is the sequence of inspectable inputs and outputs. A developer can locate where information changed, then adjust the relevant prompt, schema, or mapping. All of this happens locally, before deployment. The next exercise adds an agent with instructions and access to this already-working workflow, so a colleague can request the same operation through chat. Memory and playground testing are part of that integration, while prompt changes remain available for tuning frustration analysis and meme choice.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Apply the pattern, and inspect what gets generated
The Mastra contributors briefly connect the workshop to their earlier Gatsby and open-source JavaScript work. Their attendance was not planned far in advance, but it let the presenters ask implementation questions directly. The discussion then shifts to applications beyond memes. One participant wants to chat with physical-server infrastructure to retrieve logs and assist incident investigation. Proser emphasizes small manual tasks that can be reduced to a few steps and API calls. Nisi and Proser pair that enthusiasm with a practical instruction: review generated code and use secret-scanning tools such as GitGuardian before committing it.
Nisi's cross-SDK experiment shows why that review matters. Maintaining several language SDKs means a small field addition in Node may need corresponding changes in Ruby, Go, PHP, Kotlin, and others. He built an experiment in which labeling the first PR generated corresponding PRs elsewhere. Nisi reports a working prototype after two hours with Claude; Proser describes the resulting GitHub Action as roughly 4,300 lines. Asked whether it used Dagger, Nisi says it did not—and admits he had not reviewed the generated implementation. The experiment was not shipped. Much of its complexity handled diffs, and some attempts made the requested change while deleting the rest of the files. He suggests later programmatic Claude Code capabilities as a possible way to delegate those editing mechanics.
Proser offers a smaller internal-tool example: helping a marketing or sales colleague identify engineers already working with MCP for conference outreach. Proser reports having a working v0 analysis tool four minutes into a colleague's explanation. The colleague received a separate URL where they could supply input and obtain analysis. A descriptive paragraph was enough to begin the prototype, but this had not led his nontechnical colleagues to demand the keyboard themselves. In his experience, enjoying the repeated work of getting software right still distinguishes the people who choose to build it.
The discussion also reaches legal work: Proser describes lawyers asking detailed questions about GenAI despite not being programmers, and points to semantic search and information compression over large case-law collections. Back in the workshop, an audience question establishes a scope boundary: configuring an MCP server in the playground's MCP Servers view is not part of this exercise; a separate tutorial is offered.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Finish with chat, inspection, and persistence
The final checkpoint adds the meme generator to the Agents tab. The chat interface sits beside an overview of the agent's instructions, its workflow access, and model settings. Temperature and top-p can be adjusted during testing. The example complaint is being invited to long meetings without speaking. The agent invokes the workflow, returns an inline meme, and continues the conversation. Expanding the tool activity exposes the arguments, workflow invocation, and complete result, so the conversational layer preserves access to execution details.
The same interface could front a report, analysis, SQL query, code-generation task, or review. What the workshop actually completes is narrower and concrete: an agent supplies inputs to a multistep workflow and turns its returned data into a human-readable response, an image, and a share link. The presenters encourage deployment, but do not establish a privacy assurance for the application.
Proser points to an undemonstrated RAG example as a further direction: defining retrieval and generation in one framework can reduce the glue code he previously needed across several libraries. Other extensions include new tools, a vector database, and MCP exposure. The proposed next step is to let colleagues use the application and learn from what happens, rather than stop at a successful local generation. During closing questions, the presenters also identify the reusable Markdown slides in the repository and Slidev as their presentation tool.
The last technical question concerns memory. Proser explains that the demo uses SQLite to retain chats, which appear in the playground's left rail, and mentions alternative storage drivers such as Postgres. Storage must be configured and connected to the component that uses it; a conversational interface alone does not imply persistence. The participant's more specific question—where to find a referenced execution step—is handed off to individual help rather than answered on screen. The completed application therefore ends with three distinct surfaces to understand: the conversation a user sees, the workflow execution a developer inspects, and the stored history that survives between chats.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Resources
From the talk
TypeScript meme-generator project with workshop instructions, presentation materials and an OpenAI–Imgflip workflow.
Exercises for building a frustration extractor, composing the meme workflow and adding a conversational agent.
Introduction and setup guidance for giving compatible coding assistants access to Mastra documentation.
A May 2025 investigation of tool-schema failures across models and the compatibility transformations used to reduce them.
Template and captioning API documentation, including credentials, image URLs and publication behavior.
Further reading
Explains schema-constrained generation, constrained decoding and the distinction between JSON validity and schema adherence.
Updates since the talk
- Current Cloudflare Workers limitsDocumentation
Current deployment-size, execution and resource limits for Cloudflare Workers.
Read the transcript and explore slides
Loading transcript and slides…