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.

0:120:42
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

0:12 · section reference included

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.

2:593:23
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

2:59 · section reference included

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.

How it fits togetherFrom Python composition to a browser interface

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.

6:226:52
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

6:22 · section reference included

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.

Recording frame at 551 seconds
Recording frame at 551 seconds

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.

8:228:52
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

8:22 · section reference included

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.

Recording frame at 701 seconds
Recording frame at 701 seconds

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.

9:5210:22
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

9:52 · section reference included

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.

Compare the ideasThe file takes a different route

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.

12:2212:52
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

12:22 · section reference included

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.

Recording frame at 1026 seconds
Recording frame at 1026 seconds

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.

14:5115:22
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

14:51 · section reference included

Resources

From the talk

  • The MCP framework that detects returned Prefab components and delivers interactive apps; useful for framework installation and integration examples.

Read the complete timestamped transcript
  1. 0:01

    [music]

  2. 0:12

    Good. Um, thank you all for for coming

  3. 0:15

    out. Um, I'm going to talk today about

  4. 0:16

    one of the weirdest pieces of software

  5. 0:18

    I've ever written. It's sort of on the

  6. 0:20

    edge of a whole lot of stuff I've been

  7. 0:21

    putting putting forward into the world.

  8. 0:23

    Um, so join me if you will. We're going

  9. 0:25

    to try and have the most reasoned

  10. 0:27

    approach to to a very strange thing that

  11. 0:29

    agents and MCP and other things have

  12. 0:31

    enabled. And so to begin, I want to talk

  13. 0:35

    about MCP apps. I don't know if any of

  14. 0:36

    you were able to join any of the other

  15. 0:38

    talks earlier today. Maybe the one that

  16. 0:40

    um Edo and Lead just gave maybe an hour

  17. 0:43

    ago. Just a show of hands. MCP apps

  18. 0:46

    familiarity.

  19. 0:49

    Okay, this is probably the best crowd

  20. 0:51

    I've ever given this talk to actually.

  21. 0:52

    So that's that's fantastic. Um, for

  22. 0:54

    those that didn't put their hands up,

  23. 0:56

    MCP apps is an extension of the MCP

  24. 0:58

    protocol that was introduced I think in

  25. 0:59

    January of this year. And the idea is

  26. 1:02

    this is a typical request response cycle

  27. 1:05

    for an MCP um tool. The user makes a

  28. 1:08

    request to the agent. The agent in turn

  29. 1:11

    decides to use an MCP tool that's hosted

  30. 1:14

    on an MCP server. A tool result comes

  31. 1:16

    back into the agent's context and the

  32. 1:18

    agent chooses to form some response and

  33. 1:21

    send it out to the user. And so

  34. 1:23

    fundamentally MCP servers are these

  35. 1:25

    fantastic ways of adding uh functions

  36. 1:27

    and business logic to your agents but

  37. 1:29

    never a direct connection between a user

  38. 1:32

    and the MCP server. It always goes

  39. 1:33

    through the brain of the agent and more

  40. 1:35

    importantly through the context window

  41. 1:36

    of the agent. So MCP apps are an

  42. 1:39

    extension of this which allow us

  43. 1:41

    actually to bypass the agent and instead

  44. 1:43

    what happens is the following. The user

  45. 1:45

    requests something from the agent. The

  46. 1:47

    agent uses a tool, but instead of that

  47. 1:49

    tool request going back to the agent, it

  48. 1:50

    is sent out to the user and it's sent

  49. 1:52

    out as HTML, CSS, JavaScript. It's a

  50. 1:55

    full UI and it can be whatever you want

  51. 1:57

    it to be. And so the idea is you have

  52. 1:59

    this way to make to basically put the

  53. 2:02

    internet into your agent, so to speak.

  54. 2:03

    You can ship any custom branded useful

  55. 2:06

    UI that you want. You can let the user

  56. 2:08

    have any interactive experience that

  57. 2:10

    they want. And then the user, as you can

  58. 2:12

    see in the diagram, the user now can

  59. 2:14

    interact with the application. they can

  60. 2:16

    use the tools, they can send information

  61. 2:18

    back into a backend host on that app and

  62. 2:20

    really get a full experience. You can

  63. 2:21

    imagine booking a table at a restaurant

  64. 2:23

    or um changing your seat on a plane or

  65. 2:27

    interacting with a schedule for AI

  66. 2:29

    engineer. There's a lot of things that

  67. 2:30

    you can do as a user now where the agent

  68. 2:32

    facilitated it, but you are going to

  69. 2:34

    interact as a human. And there's an

  70. 2:35

    extension coming now. This is going to

  71. 2:37

    come out in um in the July MCP release

  72. 2:40

    where the agent can actually interact

  73. 2:42

    with the app as well. And this will tee

  74. 2:44

    up some really interesting use cases

  75. 2:45

    we're not going to talk about today, but

  76. 2:47

    you could hypothetically play a game of

  77. 2:48

    chess against the agent now in a visual

  78. 2:51

    app where you make a move and then the

  79. 2:52

    agent interacts with the app as well.

  80. 2:54

    And so I think that's going to open up a

  81. 2:55

    whole new world of possibilities.

  82. 2:59

    Now um some of you may know a framework

  83. 3:02

    that that I'm the author of and my

  84. 3:03

    company maintains called FastMPP. Um

  85. 3:05

    fast MCP is one of the most popular ways

  86. 3:07

    of building MCP servers. And so whenever

  87. 3:09

    new cool things come to the world of

  88. 3:11

    MCP, the first thing I wonder is how can

  89. 3:14

    I deliver this to our users? And one of

  90. 3:17

    the most important things I have to

  91. 3:18

    share with you about our user base is

  92. 3:20

    that they're mostly Python engineers.

  93. 3:23

    And so and so this is a little bit of a

  94. 3:26

    problem when we want to deliver

  95. 3:27

    frontends and UIs because how are we

  96. 3:30

    actually going to do that? And this is

  97. 3:31

    the point in the talk where I reveal

  98. 3:32

    that I don't remember what the next

  99. 3:33

    slide exactly is. So we're going to take

  100. 3:34

    a peek at it. Nope, we're going to come

  101. 3:36

    back. Um, we we have we have a challenge

  102. 3:39

    now. Uh, how are we going to have have

  103. 3:41

    Python engineers build UIs that are best

  104. 3:45

    practice, that are interactive, that are

  105. 3:48

    beautiful, that are useful without

  106. 3:50

    pretending that we're going to do

  107. 3:51

    something silly, something that's been

  108. 3:53

    tried, and jam all of the front end, all

  109. 3:55

    of the ecosystem, everything into Python

  110. 3:57

    in some sort of like weird compromised

  111. 4:00

    haphazard Frankenstein of a system. And

  112. 4:02

    so I really struggled with this. I need

  113. 4:04

    I really need I feel an obligation to

  114. 4:06

    find a way to deliver this, but I I

  115. 4:09

    can't I can't pretend we're going to

  116. 4:10

    ship React and Python. It's not going to

  117. 4:12

    work. And so we thought pretty hard

  118. 4:14

    about who are our users in the fastmcp

  119. 4:16

    ecosystem. Who are these Python

  120. 4:18

    developers who tend to be in

  121. 4:19

    enterprises? What are they doing and

  122. 4:21

    what do they need these UIs for? What do

  123. 4:23

    they need these MCP apps for? Um what

  124. 4:25

    they don't need is consumer-grade custom

  125. 4:28

    UIs that are fully branded. That that's

  126. 4:31

    not what these folks are doing. what

  127. 4:33

    they are uh primarily charged with is

  128. 4:34

    sharing information throughout their

  129. 4:36

    throughout their organization for

  130. 4:38

    collecting information throughout their

  131. 4:39

    organization and so it changed the

  132. 4:41

    nature of what we expect them to do

  133. 4:43

    within MCP apps framework and that

  134. 4:45

    constraint became really useful so

  135. 4:47

    fundamentally we expect that they're

  136. 4:48

    going to do things like build tables

  137. 4:51

    they're going to collect information

  138. 4:53

    through forms and they're going to want

  139. 4:54

    to share charts and so fundamentally

  140. 4:57

    with this constraint we can introduce a

  141. 5:00

    piece of software that we open sourced a

  142. 5:02

    few months ago and has been surprisingly

  143. 5:04

    popular among this crowd called prefab

  144. 5:06

    and it's a scoped UI building framework

  145. 5:10

    for the purpose of delivering UIs

  146. 5:12

    through an agent for the set of purposes

  147. 5:14

    that I mentioned a moment ago. So this

  148. 5:16

    is a hello world card. You might see

  149. 5:19

    this in any front-end framework,

  150. 5:21

    literally anyone. It'll have something

  151. 5:22

    that looks like this and it's on their

  152. 5:23

    website and you you type your name in

  153. 5:25

    and it it updates live. But of course,

  154. 5:28

    the weird thing about this one is that

  155. 5:29

    the code that generated it is entirely

  156. 5:31

    written in Python. And so I hope that

  157. 5:35

    you're feeling what I feel when I look

  158. 5:37

    at this, which is a really weird

  159. 5:39

    combination of like, yes, that's cool,

  160. 5:40

    and this really freaks me out. The yes,

  161. 5:43

    that's cool, comes from the fact that I

  162. 5:45

    think there's something about this code,

  163. 5:46

    even if you can't see it up close, I can

  164. 5:48

    make it a little bigger. There's

  165. 5:49

    something about this that like kind of

  166. 5:51

    makes sense. You can see the structure

  167. 5:53

    of the of the of the UI in the code, but

  168. 5:55

    there's also something about it that's

  169. 5:57

    obviously alien and a little bit a

  170. 5:59

    little bit odd. And we come to this

  171. 6:01

    conclusion when you when you feel that

  172. 6:03

    when you look at it, which is that when

  173. 6:04

    you compose a front end in Python, it's

  174. 6:06

    actually starts to feel good as long as

  175. 6:09

    we scope the challenge right. We are not

  176. 6:11

    trying to build a front end from

  177. 6:12

    scratch. We are trying to compose a

  178. 6:14

    front end from a bunch of worldclass

  179. 6:17

    well-designed components. And that's how

  180. 6:19

    we keep the guardrails and that's how we

  181. 6:21

    keep the user in mind. The user here is

  182. 6:23

    not trying to do something arbitrary.

  183. 6:24

    They're trying to take a well structured

  184. 6:27

    front end and put it in front of um

  185. 6:28

    whomever they're delivering it to. And

  186. 6:30

    so here's a little quick tour of that

  187. 6:31

    DSL. Um primarily we're using context

  188. 6:33

    managers. For those of you who do know

  189. 6:35

    fastmcp, you know that arguably you

  190. 6:37

    could reduce fastmc down and say the

  191. 6:39

    core innovation of fastmcp is that we

  192. 6:41

    used a python decorator to build an

  193. 6:43

    entire MCP server. So if you want to

  194. 6:45

    take the same reductive approach to

  195. 6:46

    prefab, you could say we use a context

  196. 6:47

    manager to build an entire UI. And by

  197. 6:50

    nesting components with as context

  198. 6:52

    managers as you see here, we are

  199. 6:54

    building up the exact same structure in

  200. 6:57

    the UI. It feels very natural when you

  201. 7:00

    read it. You can see how things are

  202. 7:01

    structured. Um each element of the UI,

  203. 7:04

    each component, which is a beautiful

  204. 7:05

    shaden component when it's rendered, as

  205. 7:07

    you can see here, is a class that you

  206. 7:09

    instantiate. You can parameterize it.

  207. 7:11

    you can pass it stuff like CSS classes

  208. 7:13

    and and make it look however you want.

  209. 7:16

    And then the last thing which we're not

  210. 7:17

    going to have enough time to really

  211. 7:18

    explore today is these reactive

  212. 7:20

    variables. Um I'll show you a demo of

  213. 7:21

    those in a moment, but essentially we

  214. 7:23

    have a full way to um create client side

  215. 7:26

    interactivity and bind data between

  216. 7:28

    components that allows you to build

  217. 7:29

    these really rich experiences again

  218. 7:31

    without having to go fully into the

  219. 7:33

    JavaScript world and leave an ecosystem

  220. 7:35

    that my user base at least is extremely

  221. 7:37

    comfortable with. Um, and this is the

  222. 7:40

    pipeline that prefab is essentially

  223. 7:41

    exposing. We use a Python DSL that I

  224. 7:43

    just shared with you. We use that to

  225. 7:45

    build a declarative representation of a

  226. 7:47

    UI that then gets serialized into a JSON

  227. 7:51

    protocol. And that JSON protocol is

  228. 7:53

    ultimately rendered by a React app which

  229. 7:56

    is hosted as the actual MCP app. And so

  230. 7:58

    this is going to open up a whole lot of

  231. 7:59

    possibilities for us that again I'm

  232. 8:01

    going to show you in just a second. But

  233. 8:02

    the key to this whole thing is the JSON

  234. 8:04

    in the middle. The Python is actually an

  235. 8:06

    accident that I discovered after the

  236. 8:07

    fact because it was a weird

  237. 8:08

    idiosyncratic thing that I wanted. The

  238. 8:10

    point of this was can we create a

  239. 8:12

    serializable representation of a UI and

  240. 8:15

    that's the JSON protocol again. And

  241. 8:17

    because it's serializable, I can

  242. 8:19

    generate it from an agent. I can send it

  243. 8:21

    to an agent. I can generate it as a

  244. 8:22

    human and ask an agent to modify.

  245. 8:24

    There's all this cool stuff that happens

  246. 8:25

    because of that intermediate

  247. 8:27

    representation in JSON. And then when

  248. 8:29

    the when the Python DSL just fell out of

  249. 8:31

    this and was really beautiful and easy

  250. 8:32

    to use, I kind of felt like we had

  251. 8:33

    something

  252. 8:34

    So uh we have these docs and sort of to

  253. 8:38

    prove the point this was another

  254. 8:39

    constraint we took on this is I don't

  255. 8:41

    even know what this is this is a doc

  256. 8:42

    these are the docs for the data table

  257. 8:43

    component in prefab I think there's 130

  258. 8:46

    or 140 components that we ship that you

  259. 8:48

    can compose into an arbitrary form the

  260. 8:50

    docs for prefab are 100% rendered in

  261. 8:54

    prefab so the data table that's here in

  262. 8:56

    the basic usage it is live rendered in

  263. 8:58

    prefab the Python code you can see it

  264. 9:00

    sneaking in at the bottom of the screen

  265. 9:02

    that Python code is being rendered live

  266. 9:03

    by the renderer to generate that. If you

  267. 9:06

    want, you can take any example in the

  268. 9:07

    prefab docs, you can click a link, pop

  269. 9:09

    them into the playground, and you can

  270. 9:10

    edit the code live, the Python code

  271. 9:12

    live, and you will see the UI update.

  272. 9:15

    And again, this is super weird. If

  273. 9:17

    you're feeling a little uncomfortable

  274. 9:18

    about this, that is that is okay. It

  275. 9:20

    makes a lot more sense when we constrain

  276. 9:21

    the problem. And remember that we're

  277. 9:22

    composing a UI rather than building it.

  278. 9:25

    So, I want to bring this back to the

  279. 9:27

    thing I opened with now, which is MCP

  280. 9:28

    servers and more specifically MCP apps.

  281. 9:31

    You are welcome to use prefab for any

  282. 9:33

    kind of front-end problem you have. My

  283. 9:35

    team has started using it for small

  284. 9:36

    interactive data apps and things to

  285. 9:38

    explore. They've been building

  286. 9:39

    presentations with it. We ship a a dark

  287. 9:41

    mode theme that honestly looks kind of

  288. 9:43

    like the one I'm showing you right now

  289. 9:44

    to make slides and presentations. You

  290. 9:47

    can do a lot of stuff with it. But the

  291. 9:48

    reason we built it, the use case that it

  292. 9:50

    is satisfying is for MCP servers. And so

  293. 9:53

    um I want to give you a quick tour of

  294. 9:55

    three ways that you can use it, three

  295. 9:56

    increasingly sophisticated ways that you

  296. 9:58

    can use it in your MCP server. The first

  297. 10:00

    is to build an interactive tool. As I

  298. 10:03

    showed you at the beginning of the talk,

  299. 10:04

    typically an MCP tool is something your

  300. 10:06

    agent calls and the agent gets the

  301. 10:08

    result and you don't get to interact

  302. 10:09

    with it at all. So what's the easiest

  303. 10:11

    way that we can advance that that

  304. 10:13

    interactive functionality?

  305. 10:15

    I'm going to show you here. This is a

  306. 10:17

    fastm tool. It's been decorated with a

  307. 10:19

    tool decorator as you can see and it's

  308. 10:21

    just a Python function that returns some

  309. 10:22

    information. Bearing in mind this

  310. 10:24

    information will go to the agent, not

  311. 10:25

    the user. If we want to turn this into a

  312. 10:27

    fully interactive tool with prefab,

  313. 10:31

    we're going to make one change. Instead

  314. 10:33

    of returning a Python dictionary at the

  315. 10:34

    end, which will go to the agent, we're

  316. 10:36

    going to return a prefab component. In

  317. 10:38

    this case, it's the data table. These

  318. 10:39

    are the this is what I just showed you

  319. 10:40

    the docs for a moment ago. And when we

  320. 10:42

    return this prefab component, FastMPP

  321. 10:46

    will automatically detect that. It will

  322. 10:47

    automatically infer that you in fact

  323. 10:49

    want to return an MCP app. and it will

  324. 10:51

    spin up all the machinery to get the

  325. 10:52

    HTML, the JavaScript, the CSS, the

  326. 10:54

    render, everything in place so that your

  327. 10:55

    user will see a data table. Here's what

  328. 10:58

    this looks like in practice. This is

  329. 10:59

    using the goose client, which is an

  330. 11:01

    excellent one. Um, I asked a server that

  331. 11:04

    had the function I just uh showed you,

  332. 11:05

    show me the team directory. And what

  333. 11:07

    pops up uh this would be better as a

  334. 11:09

    GIF. I apologize, but what pops up is a

  335. 11:10

    fully interactive data table component.

  336. 11:12

    It supports um searching and filtering

  337. 11:15

    and sorting and pagionation and all this

  338. 11:17

    stuff. And all it is is what I showed

  339. 11:19

    you a moment ago. Just return the data

  340. 11:21

    table class and all this will be taken

  341. 11:22

    care of. We can go a step further. What

  342. 11:25

    if in addition to the data table, we

  343. 11:26

    want to show a pie chart right next to

  344. 11:28

    the data table that breaks down this

  345. 11:30

    team directory. As you might imagine,

  346. 11:32

    very, very, very similar code. Instead

  347. 11:34

    of the data table alone, we're now going

  348. 11:35

    to import a grid and a pie chart. And if

  349. 11:38

    you look at the bottom, you'll see that

  350. 11:39

    we compose both the pie chart and the

  351. 11:41

    data table into a grid very naturally

  352. 11:43

    with a context manager. And this is the

  353. 11:45

    result. we now get a pie chart next to

  354. 11:48

    our data table. So this follows a

  355. 11:50

    principle that we really try to hold in

  356. 11:51

    a lot of our software at Prefect, which

  357. 11:53

    is one line of code, one big noticeable

  358. 11:55

    change. We try to keep that complexity

  359. 11:57

    incremental. And so this satisfies a lot

  360. 11:58

    of things that I think are really

  361. 11:59

    important about frameworks and DSLs. Um,

  362. 12:03

    this is very quickly because we won't

  363. 12:04

    have time to go into it. This is just an

  364. 12:05

    example I threw together and recorded of

  365. 12:08

    fully client side interactivity where

  366. 12:10

    all of these controls are linked. Uh,

  367. 12:12

    stuff's updating, text is updating,

  368. 12:14

    values are updating. No JavaScript was

  369. 12:16

    written. This is just a couple of

  370. 12:18

    classes composed that all have the same

  371. 12:19

    attribute assigned. So they all work

  372. 12:21

    together automatically.

  373. 12:23

    Um, oh, and I did throw in a quick code

  374. 12:26

    example of what that looks like. We have

  375. 12:27

    a class called RX, which as you may

  376. 12:29

    guess stands for reactive. If you use

  377. 12:31

    these reactive variables, you can just

  378. 12:33

    reference them anywhere in your code.

  379. 12:34

    You can format them. You can you can

  380. 12:36

    make them the name of something and it

  381. 12:38

    will automatically compile into the

  382. 12:39

    correct uh JavaScript implementation.

  383. 12:42

    The second thing that we can do is a

  384. 12:43

    fastmcp app. So if an interactive tool

  385. 12:46

    is sort of a oneshot here's a user

  386. 12:48

    interface and you can interact with it

  387. 12:49

    in the client a fastmcp app is a full

  388. 12:52

    application with a backend and in this

  389. 12:54

    case the MCP server is going to be the

  390. 12:56

    back end. We don't have time to go

  391. 12:58

    through a full worked example in this

  392. 13:00

    session but here's what the code looks

  393. 13:01

    like just to give you a sense of the

  394. 13:02

    ergonomics. We're going to write a class

  395. 13:04

    which is our fastmcp app and then we're

  396. 13:06

    going to decorate at least two uh

  397. 13:09

    functions with app.ui UI. That's the

  398. 13:12

    entry point that's going to return the

  399. 13:13

    prefab components that form the base UI

  400. 13:16

    of that application. And then at least

  401. 13:18

    one, I guess this is optional, so zero

  402. 13:19

    or more um app.tools. And these are

  403. 13:23

    essentially backend methods that you can

  404. 13:25

    now reference in the UI. So you could

  405. 13:27

    have a button that takes data that the

  406. 13:29

    user has entered into a form and sends

  407. 13:31

    it to a database using a decorated tool

  408. 13:34

    like this. One thing that we use and we

  409. 13:37

    ship as a built-in component now in

  410. 13:39

    fastmcp

  411. 13:41

    is an upload component. So as you can

  412. 13:43

    see because only the agent has access to

  413. 13:46

    an MCP server you can't simply upload a

  414. 13:49

    file to an MCP server. It has to go

  415. 13:51

    through the brain of the agent. And so

  416. 13:52

    what ends up happening is a lot of

  417. 13:53

    people create basically an upload tool

  418. 13:55

    on their MCP server forget that the

  419. 13:57

    agent has to actually call it. And what

  420. 13:59

    you end up doing is the world's most

  421. 14:00

    expensive copy paste operation. You give

  422. 14:02

    the agent a megabyte of text. the agent

  423. 14:05

    retypes it character by character into

  424. 14:07

    the MCP and now yes in fact you have

  425. 14:10

    uploaded it but it's extremely extremely

  426. 14:12

    inefficient. So this is a really good

  427. 14:14

    use case for an MCP app where you ask

  428. 14:17

    the agent to bring up the app interface

  429. 14:19

    you drag a file into it and now the file

  430. 14:21

    bypasses the agent and goes right into

  431. 14:23

    the server. And we've made that a

  432. 14:24

    oneliner like this along with a handful

  433. 14:26

    of other um useful tools. And is this a

  434. 14:30

    gift? this is not a GIF or if it is it's

  435. 14:32

    not rendering but it would look like

  436. 14:33

    this in your client in any client that

  437. 14:35

    supports MCP apps if you ask the agent I

  438. 14:37

    need to upload something it can now show

  439. 14:38

    you this and you can upload safely and

  440. 14:41

    most importantly cheaply um oh I do have

  441. 14:44

    a gift mind know your own slides is a

  442. 14:47

    good lesson from this talk so here's the

  443. 14:49

    agent is now interacting with a file

  444. 14:51

    that I just uploaded that I dragged and

  445. 14:53

    dropped um I'll make these slides

  446. 14:54

    available later if you'd like to see

  447. 14:55

    this or of course it's a oneliner you

  448. 14:57

    could try it in your servers uh this

  449. 14:59

    afternoon. The last thing that I want to

  450. 15:02

    talk about which is sort of enabled by

  451. 15:03

    this architecture is a fully generative

  452. 15:05

    UI. Um, we're just going to skip and let

  453. 15:08

    this play while I talk. So, this is a

  454. 15:10

    very simple demo where I asked Claude,

  455. 15:12

    "Hey, just I'm giving a talk on this.

  456. 15:14

    Just start streaming the most

  457. 15:15

    interesting UI you can come up with."

  458. 15:17

    And so, it just went. And what it's

  459. 15:19

    doing here is we exposed a tool that

  460. 15:22

    accepts the JSON uh the protocol

  461. 15:25

    serialization of a UI that prefab is

  462. 15:28

    based on. And so now as the agent is

  463. 15:30

    streaming that information over the

  464. 15:32

    wire, we are in real time rendering

  465. 15:34

    whatever we've got, healing that JSON

  466. 15:36

    and rendering it. And so this was a

  467. 15:37

    really cool demo and it was really

  468. 15:38

    effective and people like this because

  469. 15:40

    now you don't even have to define the UI

  470. 15:41

    yourself. All you have to do is use the

  471. 15:44

    skill we already ship, share it with

  472. 15:45

    your agent so it knows how to write a UI

  473. 15:47

    and off it goes. It can make you

  474. 15:49

    whatever you want. There are some

  475. 15:50

    clients that have built-in versions of

  476. 15:51

    this. if they have a built-in version,

  477. 15:53

    you may prefer to use it by all means,

  478. 15:54

    but this may be a way for you to build

  479. 15:56

    your own custom uh approach or limited

  480. 15:58

    set of components that are useful to

  481. 16:00

    you. Now, a really interesting thing

  482. 16:02

    happened when we spun this up. So, as I

  483. 16:04

    mentioned, originally the plan was for

  484. 16:06

    the agent to send JSON over the wire and

  485. 16:08

    have it be rendered into this full React

  486. 16:10

    application.

  487. 16:12

    What we ended up discovering is that the

  488. 16:13

    Python representation of a UI is about

  489. 16:16

    70% smaller than the JSON

  490. 16:18

    representation.

  491. 16:21

    So we don't do this anymore. When I

  492. 16:23

    recorded this demo was streaming JSON.

  493. 16:25

    What we now do is we actually stream the

  494. 16:26

    Python over the wire. It's executed in a

  495. 16:29

    sandbox. It's turned into JSON on the

  496. 16:32

    server and then that's rendered. And so

  497. 16:34

    this has a dramatic dramatic token

  498. 16:36

    efficiency, cost, and latency uh

  499. 16:38

    benefit. So it would work exactly the

  500. 16:41

    same as when I recorded this demo, but

  501. 16:42

    this is just one of those things that

  502. 16:44

    we've learned on the fly. And it's

  503. 16:45

    really fascinating that the Python

  504. 16:46

    representation is just that much more

  505. 16:48

    compact and ergonomic than the full um

  506. 16:51

    JSON one. So um that's prefab. If you'd

  507. 16:54

    like to check it out, if you're curious,

  508. 16:56

    if you want to see the weirdest thing

  509. 16:57

    I've ever built um along with however

  510. 16:59

    other many people, you can see the docs

  511. 17:01

    at prefab.pref.io.

  512. 17:02

    You can see the full library uh which is

  513. 17:04

    on our GitHub here. And this is already

  514. 17:07

    fully baked into FastmcP. So, if you're

  515. 17:09

    using a recent version of FastMPP, you

  516. 17:11

    should be able to install this optional

  517. 17:12

    addition, uh, import the components,

  518. 17:14

    return them, and start playing with

  519. 17:16

    these MCP apps. Thank you all for

  520. 17:18

    coming. [applause]