Generative UI... in Python? — Jeremiah Lowin, Prefect
Read the talk
Generative UI in Python: How Prefab Connects MCP Tools to Human Interfaces
Jeremiah Lowin explains how Prefab turns Python component composition into interactive MCP apps, why a serializable UI matters for agents, and how direct uploads and compact Python streams change the flow of information.
From a talk by Jeremiah Lowin
At a glance
Ideas worth remembering
MCP apps let an agent initiate a human interface whose interactions can reach a backend directly. File upload makes the benefit concrete: transferring a file through the app avoids having the agent reproduce its contents in a tool call.
Prefab makes Python UI authoring manageable by focusing on enterprise tables, forms, and charts. Nested context managers compose components, and reactive variables generate client-side bindings without requiring handwritten JavaScript.
The serializable UI representation is the architectural center. Python authors it, React renders it, and agents can generate or modify it; returning a Prefab component lets FastMCP arrange the MCP app delivery machinery.
A serializable rendering contract need not be the wire format. Lowin reports Python UI descriptions about 70% smaller than JSON and describes streaming Python into a sandbox before converting it to JSON. The talk leaves measurement coverage and sandbox guarantees unspecified.
From agent-mediated results to interfaces people can use
After a brief introduction and a show of hands about MCP apps, Lowin starts with the ordinary MCP request cycle. A user asks an agent for something; the agent chooses a tool on an MCP server; the result enters the agent’s context; and the agent forms a response for the user. This makes the server a place to expose functions and business logic, but it leaves the agent between the user and the server. Information passes through the agent’s context window rather than through a direct human interface.
MCP apps extend that arrangement with a user-facing interface. The agent still initiates the tool call, but Lowin describes an app response that reaches the user as HTML, CSS, and JavaScript. The user can then interact with the application and send information to its backend. The distinction is practical: the agent helps bring up the experience, while the person can complete actions through controls instead of asking the agent to mediate every step. Lowin’s examples include booking a restaurant table, changing an airplane seat, and interacting with an AI Engineer schedule.
Lowin also anticipates an extension that would let the agent interact with the app. He illustrates the possibility with a visual chess game: the human makes a move, and the agent makes its own move through the application. He presents this as an upcoming capability rather than demonstrating it here. His reference to a July MCP release is a timing expectation within the talk, not evidence that the capability has shipped.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Choosing a UI problem that Python engineers actually have
As the author of FastMCP, Lowin wants to make new MCP capabilities available to its users. The difficulty is that those users are mostly Python engineers. Giving them MCP apps means finding a way to produce useful, attractive, interactive interfaces without attempting to squeeze the entire frontend ecosystem into Python. His objection is to the scope of that undertaking: reproducing all the machinery of frontend development would create a compromised system rather than a clear path for his audience.
The team narrows the problem by considering what these developers do inside enterprises. Lowin characterizes their main need as sharing and collecting organizational information, rather than designing fully branded consumer products. That points to three recurring interface forms: tables to present information, forms to collect it, and charts to explain it. Prefab emerges from this constraint as a scoped framework for delivering those kinds of interfaces through an agent. The constraint reduces how much frontend freedom the framework needs to expose.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Composing components with Python structure
The first example is a familiar hello-world card: enter a name and the interface updates live. What makes it unusual is that the code defining it is entirely Python. Lowin argues that this becomes reasonable when the job is to compose a frontend from well-designed components. The developer arranges an existing set of building blocks into a structured interface, which gives the framework guardrails and avoids requiring every user to build the frontend from scratch.
Prefab’s domain-specific language uses Python context managers to express containment. Nesting components as context managers builds the corresponding nesting in the UI, so the structure of the code reflects the structure of the interface. Lowin compares this with FastMCP’s use of decorators to define MCP servers: each framework centers a familiar Python construct. Each UI component is a class that the developer instantiates and parameterizes; options can include CSS classes to adjust its appearance. The Python API therefore describes which components exist, how they fit together, and how they are configured.
Reactive variables provide another part of the interface definition: data bindings between components. Lowin says these bindings support client-side interactivity while allowing the developer to stay in Python. This separates authoring from runtime behavior. The developer describes the connection in the Python ecosystem, and the resulting interface supplies the client-side interaction. He postpones the detailed example until later in the talk.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
The JSON representation is the architectural center
Lowin lays out the pipeline explicitly: the Python DSL builds a declarative representation of the UI; that representation is serialized into a JSON protocol; and a React app renders the protocol as the actual MCP app. Python is the authoring layer, JSON carries the interface description, and React supplies the rendered frontend. The system does not require Python itself to become a browser UI runtime.
For Lowin, the essential design is the serializable representation in the middle. Once a UI can be represented as data, an agent can generate it, receive it, or modify an interface initially authored by a human. Those uses depend on having a transferable description of the UI rather than only a finished visual result. He describes the Python DSL as something that fell out of this design and proved pleasant to use, while the JSON protocol was the original point.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Using Prefab to document and explore Prefab
The documentation becomes a concrete exercise of the same rendering system. Lowin says the Prefab docs are 100% rendered in Prefab, and uses the data-table documentation as an example. Its basic-usage table is a live rendering of the accompanying Python code. Examples can be opened in a playground, where editing the Python updates the UI. He estimates that the library ships 130 or 140 components; the talk does not establish an exact count.
Lowin returns to the scope that makes this approach manageable: users compose a UI from components. His team has also used Prefab for small interactive data apps, exploration, and presentations, including a dark theme for slides. Those uses show that the composition model can extend beyond tool responses, but MCP servers remain the reason the team built it. He introduces three increasingly sophisticated applications of the architecture.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Turning a tool result into a table, then a composed view
The first level is an interactive tool. Lowin starts with a decorated Python function that returns information to the agent. The change is to return a Prefab component instead of a Python dictionary—in this example, a data table. FastMCP detects the component, infers that the response should be an MCP app, and arranges the HTML, JavaScript, CSS, and renderer. The developer expresses the result as a component rather than assembling the app delivery machinery separately.
In the Goose client example, a request for the team directory produces a data table. Lowin describes searching, filtering, sorting, and pagination as built-in interactions supplied by the component. The useful change is that the user receives a view they can explore, rather than only the agent’s response about the returned information. This portion of the presentation uses a static example, so it conveys the component’s stated behavior without walking through each interaction live.
The next change adds a pie chart beside the directory. The developer imports a grid and a pie chart, then uses a context manager to compose the chart and table inside the grid. The returned interface now includes both a directory and a chart breaking it down. Lowin connects this to Prefect’s design principle that a small code change should produce a large, noticeable change in behavior. Complexity grows incrementally: returning one component gives a table, and composing components adds a richer layout.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Linking client-side controls without writing JavaScript
A brief recorded example demonstrates linked controls whose text and values update together on the client. Lowin says the components share the same assigned attribute, allowing them to work together automatically, and that he wrote no JavaScript for the example. The connection is part of the component definition rather than a set of handwritten event handlers.
He identifies the reactive class as RX. Its variables can be referenced in the interface code, formatted, or used as a name, and those uses compile into a JavaScript implementation. This explains how Python-authored bindings can still produce browser-side behavior: JavaScript exists in the generated implementation even when the developer does not write it. The talk gives the binding model and a short example, but does not explore its full semantics.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Adding a backend and removing agent-mediated file transfer
The second level is a full FastMCP app. Lowin distinguishes an interactive tool’s one-shot interface from an application with a backend, with the MCP server serving that backend. An app class has a UI entry point that returns the Prefab components forming the base interface, plus optional decorated backend methods that the UI can call. A button could take values entered into a form and invoke a method that sends them to a database. He sketches the ergonomics rather than providing a full worked application.
File upload exposes why a direct interface matters. In the ordinary agent-mediated arrangement Lowin describes, adding an upload tool to an MCP server does not give the user a direct upload path: the agent must call the tool. His example gives the agent a megabyte of text, then requires it to reproduce that text character by character in the tool call. The content reaches the server, but the transfer consumes the agent’s generation process for what is fundamentally copying data.
The built-in upload component changes the transfer path. The user asks the agent to bring up the app, drags a file into its interface, and the file goes directly to the server, bypassing the agent. Lowin says FastMCP makes this a one-line addition and that it works in clients supporting MCP apps. He subsequently describes the agent interacting with a file he uploaded by drag and drop. The key benefit is separating file transfer from model-generated text; the talk does not specify the upload component’s security controls or quantify the savings.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Generating interfaces and choosing a smaller wire format
The final level is generative UI. Lowin describes asking Claude to stream an interesting interface for his presentation. The demo exposes a tool that accepts Prefab’s JSON serialization of a UI. As the agent streams that representation, the system renders the information available so far, repairing incomplete JSON as it goes. The interface definition can therefore come from the agent rather than being written in advance by the developer. The talk describes this incremental rendering but does not detail the JSON-repair algorithm.
Lowin says the team ships a skill that teaches an agent how to write these interfaces. He also acknowledges that some clients have built-in generative UI features, which users may prefer. Prefab offers a way to define a custom approach or restrict generation to a useful set of components. The freedom here remains tied to the component vocabulary: the agent generates an interface description for the renderer, while the developer can choose the building blocks available to it.
The recorded demo uses JSON on the wire, but Lowin explains that the team subsequently changed the transport representation. They found that the Python representation of a UI was about 70% smaller than its JSON representation. Their newer path streams Python, executes it in a sandbox, converts it to JSON on the server, and renders that JSON. The rendering contract stays centered on JSON even though the agent sends a more compact authoring representation.
Lowin attributes token-efficiency, cost, and latency benefits to that compact representation. The reported 70% reduction is his observation, without a stated measurement method or benchmark coverage; it does not establish a universal reduction for every interface or a matching percentage decrease in cost or latency. The Python path also introduces execution in a sandbox before rendering, but he does not explain the sandbox’s isolation guarantees. The substantive result is a transport choice: generate a compact Python description, then recover the declarative JSON representation needed by the frontend.
Lowin closes by pointing to the documentation and library and describing Prefab as integrated into FastMCP. For a recent FastMCP installation, his stated entry path is to install the optional addition, import components, and return them from tools to begin using MCP apps. He gives no exact minimum version in this closing guidance. The talk ends with thanks and applause after presenting a progression from component results to backend applications and generated interfaces.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
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]