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.

0:120:35
Suggest correction

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

0:01 · section reference included

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.

2:593:02
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

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.

5:165:23
Suggest correction

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

5:16 · section reference included

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.

7:407:41
Suggest correction

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

7:40 · section reference included

Using Prefab to document and explore Prefab

Selected presentation frame from Generative UI... in Python? — Jeremiah Lowin, Prefect at 550 seconds
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.

8:348:42
Suggest correction

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

8:34 · section reference included

Turning a tool result into a table, then a composed view

Selected presentation frame from Generative UI... in Python? — Jeremiah Lowin, Prefect at 701 seconds
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.

10:0010:04
Suggest correction

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

10:00 · section reference included

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.

12:0312:04
Suggest correction

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

12:03 · section reference included

Adding a backend and removing agent-mediated file transfer

Selected presentation frame from Generative UI... in Python? — Jeremiah Lowin, Prefect at 894 seconds
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.

12:4212:43
Suggest correction

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

12:42 · section reference included

Generating interfaces and choosing a smaller wire format

Selected presentation frame from Generative UI... in Python? — Jeremiah Lowin, Prefect at 1025 seconds
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.

15:0215:03
Suggest correction

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

15:02 · section reference included

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]