Generative UI... in Python? — Jeremiah Lowin, Prefect
Read the talk
Generative UI… in Python?
Jeremiah Lowin explains how Prefab turns Python component trees into interactive MCP apps, then uses the same serializable representation to let agents generate interfaces. The useful constraint is a familiar enterprise job: sharing and collecting information through tables, forms, and charts.
From a talk by Jeremiah Lowin
At a glance
Ideas worth remembering
MCP Apps let the agent initiate an experience while the person interacts with a real interface and its backend.
Prefab’s scope makes Python UI composition practical: component trees, parameters, and reactive bindings cover structured information work without recreating the frontend ecosystem.
The JSON protocol is the central architectural mechanism. A serializable UI can be created by a human, generated by an agent, or passed between them for modification.
Changing a tool’s return value to a Prefab component produces an interactive result. Adding a grid and chart extends the team directory without replacing that working table.
Direct upload separates capability selection from payload transfer: the agent opens the app, and the file reaches the server without being reproduced in model-generated tool arguments.
Prefab changed generated-UI transport from JSON to a smaller Python representation, then converts it back to JSON after sandbox execution. Compact authoring and a serializable rendering protocol serve different jobs.
MCP Apps open a human interaction path
An ordinary MCP tool call puts the agent between the user and the server. The user makes a request, the agent chooses a tool, and the server returns its result into the agent’s context window. The agent then decides what to tell the user. Jeremiah Lowin, Prefect’s founder and CEO and the creator of FastMCP, begins with this routing problem: adding business logic to an agent does not by itself give a person an interface to that logic.
MCP Apps add a user-facing path. A tool can deliver an interface made of HTML, CSS, and JavaScript, which the person can click and use. The agent still initiates the experience, but subsequent interactions can communicate with the application’s backend. Booking a restaurant table, choosing an airline seat, or exploring a conference schedule can become an ordinary human interaction rather than a sequence of instructions the model must translate into tool calls.
Lowin also anticipates an extension allowing the agent to interact with the app alongside the user. His hypothetical example is visual chess: the person makes a move, then the agent acts through the same application. That is a proposed direction in this recording; the interfaces developed here focus on giving the human a direct way to act.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Scope the problem to tables, forms, and charts
FastMCP serves a largely Python engineering audience. MCP Apps, meanwhile, run conventional frontend technology. Bridging those worlds creates a tempting but sprawling assignment: reproduce frontend development inside Python. Lowin’s refusal is blunt: “I can’t pretend we’re gonna ship React in Python.”
The way forward begins with what these developers actually do. Enterprise Python engineers often need to share information across an organization or collect it from colleagues. That job supplies a smaller vocabulary:
- Tables: Present records that people can inspect.
- Forms: Collect structured information.
- Charts: Communicate patterns in data.
Fully branded consumer experiences require a different degree of freedom. Restricting the initial problem makes it possible to offer useful interfaces without importing the entire frontend ecosystem into Python.
Prefab is the resulting framework. Its introductory card behaves like a familiar frontend example: type a name and the displayed text updates live. The strange part is that Python declares the interface. The nesting makes its structure recognizable even while the language feels out of place.
Prefab makes this manageable by composing existing, well-designed components. The Python author arranges and configures them rather than implementing every browser primitive. The component vocabulary supplies both the capability and the guardrails: it suits structured information work, while arbitrary frontend design remains outside the problem Lowin set out to solve.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Python describes the tree; JSON makes it portable
The Python domain-specific language uses context managers to express containment. Nesting components inside a context manager builds the corresponding nesting in the interface. Each component is a class that the author instantiates and parameterizes, including passing CSS classes for styling. Reading the Python therefore reveals the layout hierarchy.
Reactive variables describe relationships between components. A value can be bound to controls and displayed elsewhere, allowing changes to propagate inside the client without the author writing JavaScript. The framework supplies the browser implementation for those bindings; Python supplies their declaration.
What actually crosses from Python into the browser? The pipeline below separates the authoring language from the representation and renderer. Python builds a declarative UI, that UI is serialized into a JSON protocol, and a React application renders the protocol as the MCP App. React still does the frontend work.
The consequential step is serialization. Once an interface has a portable representation, an agent can generate it, receive it, or modify something a human created. The renderer can consume the same protocol regardless of who authored the interface. Lowin calls the Python DSL an accident discovered afterward: the original question was whether a UI could become serializable data, and the pleasant Python syntax emerged from that design.
Nested context managers and component instances describe the UI.
The JSON protocol separates interface authorship from React rendering. Humans and agents can work with the representation without building a new frontend for each interface.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Documentation that runs its own examples
The Prefab documentation applies another useful constraint: it is entirely rendered in Prefab. The data-table page contains a live table generated from its Python example. An example can be opened in the playground, edited as Python, and watched as the rendered interface changes.
That creates a tight learning loop. The example is an interface the reader can use; changing its declaration changes the thing being explained. It also exercises the composition model on the documentation itself, beyond a single greeting card.
The team has used Prefab for small interactive data apps and presentations, including a dark theme intended for slides. Those experiments broaden its uses, but MCP servers remain the reason it was built. The next examples move through three increasingly involved integrations: interactive tool results, applications with backends, and agent-generated interfaces.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
A team directory becomes an interactive tool
Start with a decorated FastMCP function that returns a Python dictionary. Its information goes to the agent. The first integration changes the function’s return value to a Prefab data-table component. FastMCP detects the component and prepares the MCP App machinery: the renderer, HTML, JavaScript, and CSS needed to display it.
The observable result is a team directory in the Goose client. A request to show the directory brings up a table with searching, filtering, sorting, and pagination. Those behaviors come from the component and renderer. The author’s change is small, but the person receiving the result gains a way to inspect the directory directly rather than asking the agent to restate each view.
The same example then grows. Import a grid and a pie chart, place the chart and table inside the grid’s context manager, and the directory now appears beside a chart showing its breakdown. The progression is causal and visible: returning a component opens the interactive result; nesting two components adds a coordinated layout. The table provides individual records while the chart provides an aggregate view.
Lowin’s framework preference is “one line of code, one big noticeable change.” That is a design goal for incremental complexity, rather than a literal promise that every feature takes one line. The directory example illustrates it: each added component contributes recognizable behavior without requiring the author to construct the frontend infrastructure.
A separate demonstration connects several controls through shared reactive attributes. Text and values update together entirely on the client. The Rx class supplies reactive variables that can be referenced and formatted throughout the Python declaration; Prefab compiles those references into JavaScript. This is a distinct capability from placing the chart beside the table: shared bindings describe how components change together.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
A backend makes direct uploads possible
The second integration adds a backend to the interface. A FastMCP app class has a UI entry point, decorated with app.ui, that returns its initial Prefab components. Optional decorated backend tools can then be called from the UI. A form button, for example, can submit the person’s input to a method that writes it to a database.
File upload exposes why this matters. In the ordinary agent-mediated setup, adding an upload tool to the MCP server does not give the user a file-transfer channel. The agent must call the tool with the contents. Lowin’s example gives it a megabyte of text to reproduce character by character—the “world’s most expensive copy-paste operation.” The file arrives, but its transfer consumes model work unnecessarily.
FastMCP’s upload component changes the route. Ask the agent to open the upload interface, drag a file into it, and the app sends the file directly to the server. In the demonstration, the agent subsequently works with the uploaded file. Its involvement is still useful; reproducing the payload during transfer is the avoidable part. The client must support MCP Apps for this user-facing channel to work.
Which bytes need to pass through the agent? The comparison below separates the request to open an upload capability from the file payload. In the app path, the agent initiates the experience while the file takes a direct route to the server. That routing change explains the saving.
The example supplies a megabyte of text to the agent.
The naive upload passes file contents through an agent-generated tool call. The MCP App lets the person transfer the file directly, after the agent opens the interface.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Generative UI—and Python beating JSON on the wire
The third integration lets Claude generate the interface itself. In the recorded demo, a tool accepts Prefab’s serialized JSON protocol. As the agent streams the representation, the renderer displays what has arrived, attempting to repair incomplete JSON along the way. The UI grows progressively instead of waiting for a complete response.
Prefab ships a skill that teaches the agent how to write these interfaces. This makes the component vocabulary available to another author: the model. Builders can choose a custom approach or restrict the available components to their needs. Lowin also leaves room for client-native implementations; when a client already offers the desired generative UI, using that may be preferable.
Then the team discovered a surprising representation tradeoff. Lowin reports that the Python form of a UI was about 70% smaller than its JSON form in their comparison. The recording does not specify the size measure or comparison workload, so this is an implementation observation rather than a universal compression ratio. It was enough to change what Prefab sends over the wire.
The newer path streams Python, executes it in a sandbox, converts it to JSON on the server, and renders that JSON. The intermediate protocol remains intact; only the agent’s transmitted representation changes. Lowin reports token, cost, and latency benefits from the more compact input. The tradeoff is that this path now requires sandboxed code execution, whose isolation mechanisms are not developed in the talk.
Python therefore ends up doing two jobs: giving humans a readable composition language and giving agents a compact way to transmit the same interface. JSON retains the architectural job that motivated the project—a serializable representation consumed by the React renderer. To try the integration described in the recording, Lowin points to the Prefab documentation and the optional FastMCP installation: import components, return them from tools, and let the framework deliver the app.
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
The MCP framework that detects returned Prefab components and delivers interactive apps; useful for framework installation and integration examples.
Related talks
- MCP Apps: Extending the Frontier — Ido Salomon & Liad Yosef
The MCP Apps talk Lowin mentions at the opening provides a companion to the protocol-level part of this recording.
- Building Interactive UIs in VS Code with MCP Apps — Marlene Mhangami & Liam Hampton, GitHub
Follow the same subject into a specific host environment: interactive MCP interfaces in VS Code.
- Your MCP Server is Bad and You Should Feel Bad
Lowin’s longer treatment of workflow design and context costs explains why agent-facing tools also need deliberate interface design.
Read the complete timestamped transcript
- 0:01
[music]
- 0:12
Good. Um, thank you all for for coming
- 0:15
out. Um, I'm going to talk today about
- 0:16
one of the weirdest pieces of software
- 0:18
I've ever written. It's sort of on the
- 0:20
edge of a whole lot of stuff I've been
- 0:21
putting putting forward into the world.
- 0:23
Um, so join me if you will. We're going
- 0:25
to try and have the most reasoned
- 0:27
approach to to a very strange thing that
- 0:29
agents and MCP and other things have
- 0:31
enabled. And so to begin, I want to talk
- 0:35
about MCP apps. I don't know if any of
- 0:36
you were able to join any of the other
- 0:38
talks earlier today. Maybe the one that
- 0:40
um Edo and Lead just gave maybe an hour
- 0:43
ago. Just a show of hands. MCP apps
- 0:46
familiarity.
- 0:49
Okay, this is probably the best crowd
- 0:51
I've ever given this talk to actually.
- 0:52
So that's that's fantastic. Um, for
- 0:54
those that didn't put their hands up,
- 0:56
MCP apps is an extension of the MCP
- 0:58
protocol that was introduced I think in
- 0:59
January of this year. And the idea is
- 1:02
this is a typical request response cycle
- 1:05
for an MCP um tool. The user makes a
- 1:08
request to the agent. The agent in turn
- 1:11
decides to use an MCP tool that's hosted
- 1:14
on an MCP server. A tool result comes
- 1:16
back into the agent's context and the
- 1:18
agent chooses to form some response and
- 1:21
send it out to the user. And so
- 1:23
fundamentally MCP servers are these
- 1:25
fantastic ways of adding uh functions
- 1:27
and business logic to your agents but
- 1:29
never a direct connection between a user
- 1:32
and the MCP server. It always goes
- 1:33
through the brain of the agent and more
- 1:35
importantly through the context window
- 1:36
of the agent. So MCP apps are an
- 1:39
extension of this which allow us
- 1:41
actually to bypass the agent and instead
- 1:43
what happens is the following. The user
- 1:45
requests something from the agent. The
- 1:47
agent uses a tool, but instead of that
- 1:49
tool request going back to the agent, it
- 1:50
is sent out to the user and it's sent
- 1:52
out as HTML, CSS, JavaScript. It's a
- 1:55
full UI and it can be whatever you want
- 1:57
it to be. And so the idea is you have
- 1:59
this way to make to basically put the
- 2:02
internet into your agent, so to speak.
- 2:03
You can ship any custom branded useful
- 2:06
UI that you want. You can let the user
- 2:08
have any interactive experience that
- 2:10
they want. And then the user, as you can
- 2:12
see in the diagram, the user now can
- 2:14
interact with the application. they can
- 2:16
use the tools, they can send information
- 2:18
back into a backend host on that app and
- 2:20
really get a full experience. You can
- 2:21
imagine booking a table at a restaurant
- 2:23
or um changing your seat on a plane or
- 2:27
interacting with a schedule for AI
- 2:29
engineer. There's a lot of things that
- 2:30
you can do as a user now where the agent
- 2:32
facilitated it, but you are going to
- 2:34
interact as a human. And there's an
- 2:35
extension coming now. This is going to
- 2:37
come out in um in the July MCP release
- 2:40
where the agent can actually interact
- 2:42
with the app as well. And this will tee
- 2:44
up some really interesting use cases
- 2:45
we're not going to talk about today, but
- 2:47
you could hypothetically play a game of
- 2:48
chess against the agent now in a visual
- 2:51
app where you make a move and then the
- 2:52
agent interacts with the app as well.
- 2:54
And so I think that's going to open up a
- 2:55
whole new world of possibilities.
- 2:59
Now um some of you may know a framework
- 3:02
that that I'm the author of and my
- 3:03
company maintains called FastMPP. Um
- 3:05
fast MCP is one of the most popular ways
- 3:07
of building MCP servers. And so whenever
- 3:09
new cool things come to the world of
- 3:11
MCP, the first thing I wonder is how can
- 3:14
I deliver this to our users? And one of
- 3:17
the most important things I have to
- 3:18
share with you about our user base is
- 3:20
that they're mostly Python engineers.
- 3:23
And so and so this is a little bit of a
- 3:26
problem when we want to deliver
- 3:27
frontends and UIs because how are we
- 3:30
actually going to do that? And this is
- 3:31
the point in the talk where I reveal
- 3:32
that I don't remember what the next
- 3:33
slide exactly is. So we're going to take
- 3:34
a peek at it. Nope, we're going to come
- 3:36
back. Um, we we have we have a challenge
- 3:39
now. Uh, how are we going to have have
- 3:41
Python engineers build UIs that are best
- 3:45
practice, that are interactive, that are
- 3:48
beautiful, that are useful without
- 3:50
pretending that we're going to do
- 3:51
something silly, something that's been
- 3:53
tried, and jam all of the front end, all
- 3:55
of the ecosystem, everything into Python
- 3:57
in some sort of like weird compromised
- 4:00
haphazard Frankenstein of a system. And
- 4:02
so I really struggled with this. I need
- 4:04
I really need I feel an obligation to
- 4:06
find a way to deliver this, but I I
- 4:09
can't I can't pretend we're going to
- 4:10
ship React and Python. It's not going to
- 4:12
work. And so we thought pretty hard
- 4:14
about who are our users in the fastmcp
- 4:16
ecosystem. Who are these Python
- 4:18
developers who tend to be in
- 4:19
enterprises? What are they doing and
- 4:21
what do they need these UIs for? What do
- 4:23
they need these MCP apps for? Um what
- 4:25
they don't need is consumer-grade custom
- 4:28
UIs that are fully branded. That that's
- 4:31
not what these folks are doing. what
- 4:33
they are uh primarily charged with is
- 4:34
sharing information throughout their
- 4:36
throughout their organization for
- 4:38
collecting information throughout their
- 4:39
organization and so it changed the
- 4:41
nature of what we expect them to do
- 4:43
within MCP apps framework and that
- 4:45
constraint became really useful so
- 4:47
fundamentally we expect that they're
- 4:48
going to do things like build tables
- 4:51
they're going to collect information
- 4:53
through forms and they're going to want
- 4:54
to share charts and so fundamentally
- 4:57
with this constraint we can introduce a
- 5:00
piece of software that we open sourced a
- 5:02
few months ago and has been surprisingly
- 5:04
popular among this crowd called prefab
- 5:06
and it's a scoped UI building framework
- 5:10
for the purpose of delivering UIs
- 5:12
through an agent for the set of purposes
- 5:14
that I mentioned a moment ago. So this
- 5:16
is a hello world card. You might see
- 5:19
this in any front-end framework,
- 5:21
literally anyone. It'll have something
- 5:22
that looks like this and it's on their
- 5:23
website and you you type your name in
- 5:25
and it it updates live. But of course,
- 5:28
the weird thing about this one is that
- 5:29
the code that generated it is entirely
- 5:31
written in Python. And so I hope that
- 5:35
you're feeling what I feel when I look
- 5:37
at this, which is a really weird
- 5:39
combination of like, yes, that's cool,
- 5:40
and this really freaks me out. The yes,
- 5:43
that's cool, comes from the fact that I
- 5:45
think there's something about this code,
- 5:46
even if you can't see it up close, I can
- 5:48
make it a little bigger. There's
- 5:49
something about this that like kind of
- 5:51
makes sense. You can see the structure
- 5:53
of the of the of the UI in the code, but
- 5:55
there's also something about it that's
- 5:57
obviously alien and a little bit a
- 5:59
little bit odd. And we come to this
- 6:01
conclusion when you when you feel that
- 6:03
when you look at it, which is that when
- 6:04
you compose a front end in Python, it's
- 6:06
actually starts to feel good as long as
- 6:09
we scope the challenge right. We are not
- 6:11
trying to build a front end from
- 6:12
scratch. We are trying to compose a
- 6:14
front end from a bunch of worldclass
- 6:17
well-designed components. And that's how
- 6:19
we keep the guardrails and that's how we
- 6:21
keep the user in mind. The user here is
- 6:23
not trying to do something arbitrary.
- 6:24
They're trying to take a well structured
- 6:27
front end and put it in front of um
- 6:28
whomever they're delivering it to. And
- 6:30
so here's a little quick tour of that
- 6:31
DSL. Um primarily we're using context
- 6:33
managers. For those of you who do know
- 6:35
fastmcp, you know that arguably you
- 6:37
could reduce fastmc down and say the
- 6:39
core innovation of fastmcp is that we
- 6:41
used a python decorator to build an
- 6:43
entire MCP server. So if you want to
- 6:45
take the same reductive approach to
- 6:46
prefab, you could say we use a context
- 6:47
manager to build an entire UI. And by
- 6:50
nesting components with as context
- 6:52
managers as you see here, we are
- 6:54
building up the exact same structure in
- 6:57
the UI. It feels very natural when you
- 7:00
read it. You can see how things are
- 7:01
structured. Um each element of the UI,
- 7:04
each component, which is a beautiful
- 7:05
shaden component when it's rendered, as
- 7:07
you can see here, is a class that you
- 7:09
instantiate. You can parameterize it.
- 7:11
you can pass it stuff like CSS classes
- 7:13
and and make it look however you want.
- 7:16
And then the last thing which we're not
- 7:17
going to have enough time to really
- 7:18
explore today is these reactive
- 7:20
variables. Um I'll show you a demo of
- 7:21
those in a moment, but essentially we
- 7:23
have a full way to um create client side
- 7:26
interactivity and bind data between
- 7:28
components that allows you to build
- 7:29
these really rich experiences again
- 7:31
without having to go fully into the
- 7:33
JavaScript world and leave an ecosystem
- 7:35
that my user base at least is extremely
- 7:37
comfortable with. Um, and this is the
- 7:40
pipeline that prefab is essentially
- 7:41
exposing. We use a Python DSL that I
- 7:43
just shared with you. We use that to
- 7:45
build a declarative representation of a
- 7:47
UI that then gets serialized into a JSON
- 7:51
protocol. And that JSON protocol is
- 7:53
ultimately rendered by a React app which
- 7:56
is hosted as the actual MCP app. And so
- 7:58
this is going to open up a whole lot of
- 7:59
possibilities for us that again I'm
- 8:01
going to show you in just a second. But
- 8:02
the key to this whole thing is the JSON
- 8:04
in the middle. The Python is actually an
- 8:06
accident that I discovered after the
- 8:07
fact because it was a weird
- 8:08
idiosyncratic thing that I wanted. The
- 8:10
point of this was can we create a
- 8:12
serializable representation of a UI and
- 8:15
that's the JSON protocol again. And
- 8:17
because it's serializable, I can
- 8:19
generate it from an agent. I can send it
- 8:21
to an agent. I can generate it as a
- 8:22
human and ask an agent to modify.
- 8:24
There's all this cool stuff that happens
- 8:25
because of that intermediate
- 8:27
representation in JSON. And then when
- 8:29
the when the Python DSL just fell out of
- 8:31
this and was really beautiful and easy
- 8:32
to use, I kind of felt like we had
- 8:33
something
- 8:34
So uh we have these docs and sort of to
- 8:38
prove the point this was another
- 8:39
constraint we took on this is I don't
- 8:41
even know what this is this is a doc
- 8:42
these are the docs for the data table
- 8:43
component in prefab I think there's 130
- 8:46
or 140 components that we ship that you
- 8:48
can compose into an arbitrary form the
- 8:50
docs for prefab are 100% rendered in
- 8:54
prefab so the data table that's here in
- 8:56
the basic usage it is live rendered in
- 8:58
prefab the Python code you can see it
- 9:00
sneaking in at the bottom of the screen
- 9:02
that Python code is being rendered live
- 9:03
by the renderer to generate that. If you
- 9:06
want, you can take any example in the
- 9:07
prefab docs, you can click a link, pop
- 9:09
them into the playground, and you can
- 9:10
edit the code live, the Python code
- 9:12
live, and you will see the UI update.
- 9:15
And again, this is super weird. If
- 9:17
you're feeling a little uncomfortable
- 9:18
about this, that is that is okay. It
- 9:20
makes a lot more sense when we constrain
- 9:21
the problem. And remember that we're
- 9:22
composing a UI rather than building it.
- 9:25
So, I want to bring this back to the
- 9:27
thing I opened with now, which is MCP
- 9:28
servers and more specifically MCP apps.
- 9:31
You are welcome to use prefab for any
- 9:33
kind of front-end problem you have. My
- 9:35
team has started using it for small
- 9:36
interactive data apps and things to
- 9:38
explore. They've been building
- 9:39
presentations with it. We ship a a dark
- 9:41
mode theme that honestly looks kind of
- 9:43
like the one I'm showing you right now
- 9:44
to make slides and presentations. You
- 9:47
can do a lot of stuff with it. But the
- 9:48
reason we built it, the use case that it
- 9:50
is satisfying is for MCP servers. And so
- 9:53
um I want to give you a quick tour of
- 9:55
three ways that you can use it, three
- 9:56
increasingly sophisticated ways that you
- 9:58
can use it in your MCP server. The first
- 10:00
is to build an interactive tool. As I
- 10:03
showed you at the beginning of the talk,
- 10:04
typically an MCP tool is something your
- 10:06
agent calls and the agent gets the
- 10:08
result and you don't get to interact
- 10:09
with it at all. So what's the easiest
- 10:11
way that we can advance that that
- 10:13
interactive functionality?
- 10:15
I'm going to show you here. This is a
- 10:17
fastm tool. It's been decorated with a
- 10:19
tool decorator as you can see and it's
- 10:21
just a Python function that returns some
- 10:22
information. Bearing in mind this
- 10:24
information will go to the agent, not
- 10:25
the user. If we want to turn this into a
- 10:27
fully interactive tool with prefab,
- 10:31
we're going to make one change. Instead
- 10:33
of returning a Python dictionary at the
- 10:34
end, which will go to the agent, we're
- 10:36
going to return a prefab component. In
- 10:38
this case, it's the data table. These
- 10:39
are the this is what I just showed you
- 10:40
the docs for a moment ago. And when we
- 10:42
return this prefab component, FastMPP
- 10:46
will automatically detect that. It will
- 10:47
automatically infer that you in fact
- 10:49
want to return an MCP app. and it will
- 10:51
spin up all the machinery to get the
- 10:52
HTML, the JavaScript, the CSS, the
- 10:54
render, everything in place so that your
- 10:55
user will see a data table. Here's what
- 10:58
this looks like in practice. This is
- 10:59
using the goose client, which is an
- 11:01
excellent one. Um, I asked a server that
- 11:04
had the function I just uh showed you,
- 11:05
show me the team directory. And what
- 11:07
pops up uh this would be better as a
- 11:09
GIF. I apologize, but what pops up is a
- 11:10
fully interactive data table component.
- 11:12
It supports um searching and filtering
- 11:15
and sorting and pagionation and all this
- 11:17
stuff. And all it is is what I showed
- 11:19
you a moment ago. Just return the data
- 11:21
table class and all this will be taken
- 11:22
care of. We can go a step further. What
- 11:25
if in addition to the data table, we
- 11:26
want to show a pie chart right next to
- 11:28
the data table that breaks down this
- 11:30
team directory. As you might imagine,
- 11:32
very, very, very similar code. Instead
- 11:34
of the data table alone, we're now going
- 11:35
to import a grid and a pie chart. And if
- 11:38
you look at the bottom, you'll see that
- 11:39
we compose both the pie chart and the
- 11:41
data table into a grid very naturally
- 11:43
with a context manager. And this is the
- 11:45
result. we now get a pie chart next to
- 11:48
our data table. So this follows a
- 11:50
principle that we really try to hold in
- 11:51
a lot of our software at Prefect, which
- 11:53
is one line of code, one big noticeable
- 11:55
change. We try to keep that complexity
- 11:57
incremental. And so this satisfies a lot
- 11:58
of things that I think are really
- 11:59
important about frameworks and DSLs. Um,
- 12:03
this is very quickly because we won't
- 12:04
have time to go into it. This is just an
- 12:05
example I threw together and recorded of
- 12:08
fully client side interactivity where
- 12:10
all of these controls are linked. Uh,
- 12:12
stuff's updating, text is updating,
- 12:14
values are updating. No JavaScript was
- 12:16
written. This is just a couple of
- 12:18
classes composed that all have the same
- 12:19
attribute assigned. So they all work
- 12:21
together automatically.
- 12:23
Um, oh, and I did throw in a quick code
- 12:26
example of what that looks like. We have
- 12:27
a class called RX, which as you may
- 12:29
guess stands for reactive. If you use
- 12:31
these reactive variables, you can just
- 12:33
reference them anywhere in your code.
- 12:34
You can format them. You can you can
- 12:36
make them the name of something and it
- 12:38
will automatically compile into the
- 12:39
correct uh JavaScript implementation.
- 12:42
The second thing that we can do is a
- 12:43
fastmcp app. So if an interactive tool
- 12:46
is sort of a oneshot here's a user
- 12:48
interface and you can interact with it
- 12:49
in the client a fastmcp app is a full
- 12:52
application with a backend and in this
- 12:54
case the MCP server is going to be the
- 12:56
back end. We don't have time to go
- 12:58
through a full worked example in this
- 13:00
session but here's what the code looks
- 13:01
like just to give you a sense of the
- 13:02
ergonomics. We're going to write a class
- 13:04
which is our fastmcp app and then we're
- 13:06
going to decorate at least two uh
- 13:09
functions with app.ui UI. That's the
- 13:12
entry point that's going to return the
- 13:13
prefab components that form the base UI
- 13:16
of that application. And then at least
- 13:18
one, I guess this is optional, so zero
- 13:19
or more um app.tools. And these are
- 13:23
essentially backend methods that you can
- 13:25
now reference in the UI. So you could
- 13:27
have a button that takes data that the
- 13:29
user has entered into a form and sends
- 13:31
it to a database using a decorated tool
- 13:34
like this. One thing that we use and we
- 13:37
ship as a built-in component now in
- 13:39
fastmcp
- 13:41
is an upload component. So as you can
- 13:43
see because only the agent has access to
- 13:46
an MCP server you can't simply upload a
- 13:49
file to an MCP server. It has to go
- 13:51
through the brain of the agent. And so
- 13:52
what ends up happening is a lot of
- 13:53
people create basically an upload tool
- 13:55
on their MCP server forget that the
- 13:57
agent has to actually call it. And what
- 13:59
you end up doing is the world's most
- 14:00
expensive copy paste operation. You give
- 14:02
the agent a megabyte of text. the agent
- 14:05
retypes it character by character into
- 14:07
the MCP and now yes in fact you have
- 14:10
uploaded it but it's extremely extremely
- 14:12
inefficient. So this is a really good
- 14:14
use case for an MCP app where you ask
- 14:17
the agent to bring up the app interface
- 14:19
you drag a file into it and now the file
- 14:21
bypasses the agent and goes right into
- 14:23
the server. And we've made that a
- 14:24
oneliner like this along with a handful
- 14:26
of other um useful tools. And is this a
- 14:30
gift? this is not a GIF or if it is it's
- 14:32
not rendering but it would look like
- 14:33
this in your client in any client that
- 14:35
supports MCP apps if you ask the agent I
- 14:37
need to upload something it can now show
- 14:38
you this and you can upload safely and
- 14:41
most importantly cheaply um oh I do have
- 14:44
a gift mind know your own slides is a
- 14:47
good lesson from this talk so here's the
- 14:49
agent is now interacting with a file
- 14:51
that I just uploaded that I dragged and
- 14:53
dropped um I'll make these slides
- 14:54
available later if you'd like to see
- 14:55
this or of course it's a oneliner you
- 14:57
could try it in your servers uh this
- 14:59
afternoon. The last thing that I want to
- 15:02
talk about which is sort of enabled by
- 15:03
this architecture is a fully generative
- 15:05
UI. Um, we're just going to skip and let
- 15:08
this play while I talk. So, this is a
- 15:10
very simple demo where I asked Claude,
- 15:12
"Hey, just I'm giving a talk on this.
- 15:14
Just start streaming the most
- 15:15
interesting UI you can come up with."
- 15:17
And so, it just went. And what it's
- 15:19
doing here is we exposed a tool that
- 15:22
accepts the JSON uh the protocol
- 15:25
serialization of a UI that prefab is
- 15:28
based on. And so now as the agent is
- 15:30
streaming that information over the
- 15:32
wire, we are in real time rendering
- 15:34
whatever we've got, healing that JSON
- 15:36
and rendering it. And so this was a
- 15:37
really cool demo and it was really
- 15:38
effective and people like this because
- 15:40
now you don't even have to define the UI
- 15:41
yourself. All you have to do is use the
- 15:44
skill we already ship, share it with
- 15:45
your agent so it knows how to write a UI
- 15:47
and off it goes. It can make you
- 15:49
whatever you want. There are some
- 15:50
clients that have built-in versions of
- 15:51
this. if they have a built-in version,
- 15:53
you may prefer to use it by all means,
- 15:54
but this may be a way for you to build
- 15:56
your own custom uh approach or limited
- 15:58
set of components that are useful to
- 16:00
you. Now, a really interesting thing
- 16:02
happened when we spun this up. So, as I
- 16:04
mentioned, originally the plan was for
- 16:06
the agent to send JSON over the wire and
- 16:08
have it be rendered into this full React
- 16:10
application.
- 16:12
What we ended up discovering is that the
- 16:13
Python representation of a UI is about
- 16:16
70% smaller than the JSON
- 16:18
representation.
- 16:21
So we don't do this anymore. When I
- 16:23
recorded this demo was streaming JSON.
- 16:25
What we now do is we actually stream the
- 16:26
Python over the wire. It's executed in a
- 16:29
sandbox. It's turned into JSON on the
- 16:32
server and then that's rendered. And so
- 16:34
this has a dramatic dramatic token
- 16:36
efficiency, cost, and latency uh
- 16:38
benefit. So it would work exactly the
- 16:41
same as when I recorded this demo, but
- 16:42
this is just one of those things that
- 16:44
we've learned on the fly. And it's
- 16:45
really fascinating that the Python
- 16:46
representation is just that much more
- 16:48
compact and ergonomic than the full um
- 16:51
JSON one. So um that's prefab. If you'd
- 16:54
like to check it out, if you're curious,
- 16:56
if you want to see the weirdest thing
- 16:57
I've ever built um along with however
- 16:59
other many people, you can see the docs
- 17:01
at prefab.pref.io.
- 17:02
You can see the full library uh which is
- 17:04
on our GitHub here. And this is already
- 17:07
fully baked into FastmcP. So, if you're
- 17:09
using a recent version of FastMPP, you
- 17:11
should be able to install this optional
- 17:12
addition, uh, import the components,
- 17:14
return them, and start playing with
- 17:16
these MCP apps. Thank you all for
- 17:18
coming. [applause]