WEBVTT

NOTE Sentence-level transcript of https://www.youtube.com/watch?v=ZIYYsAzaLlA

NOTE One cue per sentence. Cue ids are the line anchors on /transcripts/ZIYYsAzaLlA.html. A cue ends where the next begins, or 2 s after its last word.

s1
00:00:01.309 --> 00:00:03.309
[music]

s2
00:00:12.960 --> 00:00:14.240
All right, get started.

s3
00:00:14.240 --> 00:00:23.359
Hi, I'm Jesse and I've been a software engineer at Figma for about three years and I'm going to talk to you about how we built Figma's first MCP server in about 3 months.

s4
00:00:23.359 --> 00:00:31.599
Um, and in case you don't know what it is, the Figma MCP server is a way for you to send context between production uh code and design and vice versa.

s5
00:00:31.599 --> 00:00:34.320
AI tools don't need to build a dedicated integration.

s6
00:00:34.320 --> 00:00:39.280
They can just use our Figma MCP and they kind of get started.

s7
00:00:39.280 --> 00:00:50.559
So, taking you back to November 2024, long long time ago, Anthropic released the MCP server spec and uh everyone in the world of AI was sort of starting to use it and experiment with it.

s8
00:00:50.559 --> 00:00:55.120
Um but outside of anthropic none of the other AI agents or labs were really using it.

s9
00:00:55.120 --> 00:00:58.079
So OpenAI cursor VS Code they didn't support it yet.

s10
00:00:58.079 --> 00:01:08.240
Um once we got access to the feature in cursor however uh we were able to kind of ideate and understand what it was capable of and we got something a little bit closer to an actual product

s11
00:01:08.240 --> 00:01:10.000
and that's where my story begins.

s12
00:01:10.000 --> 00:01:15.360
Um I was working on growth initiatives at the time and I saw our internal demo and I really wanted to use it.

s13
00:01:15.360 --> 00:01:19.040
I thought it'd be great for non-designers uh to kind of be able to use Figma.

s14
00:01:19.040 --> 00:01:24.479
Um, I started creating actually a Figma plug-in based MCP server and I started doing it one day a week.

s15
00:01:24.479 --> 00:01:30.000
It was kind of my 20% project that we didn't we didn't really have 20% projects, but I really wanted to work on it, so I did.

s16
00:01:30.000 --> 00:01:33.280
Um, and so I got staffed with some other folks um on the team.

s17
00:01:33.280 --> 00:01:34.400
We were the MC Peeps.

s18
00:01:34.400 --> 00:01:35.200
That's a peep.

s19
00:01:35.200 --> 00:01:36.720
It's a candy if you don't know.

s20
00:01:36.720 --> 00:01:37.920
Um, they're delicious.

s21
00:01:37.920 --> 00:01:45.200
And I I just want to be very clear um I'm going to say I a lot and we there was a big team behind this so it's not just me and they're all fantastic.

s22
00:01:45.200 --> 00:01:56.399
Um a few weeks later after we started getting our initial architecture sorted a new version of the spec dropped uh deprecating the support type that we were going to use which was server events

s23
00:01:56.399 --> 00:02:02.079
and while the MCP spec was sort of chugging along clients were adding features and support in different paces.

s24
00:02:02.079 --> 00:02:09.280
Uh cloud had early support cloud desktop but cloud code you was wasn't really supported uh with all the complete set of features.

s25
00:02:09.280 --> 00:02:18.720
OpenAI and VS Code didn't h have support until that spec update and then it wasn't even then uh VS Code didn't go out of uh didn't get to G until July.

s26
00:02:18.720 --> 00:02:20.959
It didn't mean that all the features were implemented either.

s27
00:02:20.959 --> 00:02:26.480
There are lots of different pieces um and in many cases only tools were supported.

s28
00:02:26.480 --> 00:02:29.040
VS Code was truly like the golden client.

s29
00:02:29.040 --> 00:02:37.201
Uh they eventually supported kind of all pieces of the spec, but it was it was hard to kind of understand what you were building towards because clients supported so many different things.

s30
00:02:37.201 --> 00:02:37.840
[snorts]

s31
00:02:37.840 --> 00:02:45.440
But even though we didn't know exactly what the MCP server spec would be supported, we we knew that it would be extremely powerful and a great product uh for us to to utilize.

s32
00:02:45.440 --> 00:02:54.959
And so we started building um and so taking a a more highle re recap about a year ago uh we launched our local MCP server.

s33
00:02:54.959 --> 00:02:58.560
And what local means it was heavily designed for developer use cases.

s34
00:02:58.560 --> 00:03:05.680
You kind of had to know what you were doing a little bit and we targeted developers because they were the first to adopt AI workflows.

s35
00:03:05.680 --> 00:03:14.400
they would use a single plop prompt like help me implement this and a developer could pull everything that they would normally get from Figma's dev mode into their coding agent.

s36
00:03:14.400 --> 00:03:29.920
This include things like component data, spacing, variables um and from there we just kept adding more and more re read tools like for fig jam for make um etc. But they all shared this kind of uh mutual goal to make Figma context available for develop develop

s37
00:03:29.920 --> 00:03:32.959
developers wherever they are.

s38
00:03:32.959 --> 00:03:38.080
So, Figma, if you don't know about Figma, um Figma is a canvas.

s39
00:03:38.080 --> 00:03:41.120
Um and it's represented as a scene graph in C++.

s40
00:03:41.120 --> 00:03:44.239
It's a graph of connected nodes, not unlike the HTML DOM.

s41
00:03:44.239 --> 00:03:47.360
And we had a number of different ways we could represent the scene graph.

s42
00:03:47.360 --> 00:03:57.280
We had this internal representation which was kind of akin to JSX or XML, effectively converting the scene graph into JSS tags and XML tags and passing those to the agent.

s43
00:03:57.280 --> 00:04:01.439
It was abstract and sparse, but it didn't have super rigorous fidelity.

s44
00:04:01.439 --> 00:04:10.400
Another option that we had internally was um D2R which is our like way of saying a react tailwind representation.

s45
00:04:10.400 --> 00:04:18.160
Uh and the reason we had this is Figma has a sites product and so we already had a way of basically converting the scene graph into HTML.

s46
00:04:18.160 --> 00:04:26.240
If you actually copy the output of the Figma MCP today and you paste into like a simple MCP or simple HTTP server, it should be pixel perfect.

s47
00:04:26.240 --> 00:04:28.160
Um and if it's not, file a bug.

s48
00:04:28.160 --> 00:04:38.160
Uh but um we had a hunch that this representation would be the best one because lots of the models were sort of rldled on this React Tailwind type of code.

s49
00:04:38.160 --> 00:04:41.040
Um and we we had a suspicion that it would work really well.

s50
00:04:41.040 --> 00:04:43.840
The last one that we kind of considered was just a plain image.

s51
00:04:43.840 --> 00:04:51.520
But back in early 2025, 2025 agents weren't great at converting images directly to HTML or CSS or sort of other languages.

s52
00:04:51.520 --> 00:04:55.520
And so we kind of use that as an additional piece of context, not as the sole one.

s53
00:04:55.520 --> 00:05:02.560
And to give you what what this kind of looks like in practice, on the left here we have a a Figma frame and on the right we have the

s54
00:05:02.560 --> 00:05:04.080
React Tailwind code.

s55
00:05:04.080 --> 00:05:09.600
Um you can also see at the very top the image can crew meetup uh link.

s56
00:05:09.600 --> 00:05:16.000
Uh we also basically abstract out the images within the code or sorry within the scene graph and put them at the top level.

s57
00:05:16.000 --> 00:05:21.039
Our first attempt was just passing B 64 data into the code and that was just a terrible idea.

s58
00:05:21.039 --> 00:05:24.320
It it just blew up the context window and was bad all around.

s59
00:05:24.320 --> 00:05:25.600
um don't do that.

s60
00:05:25.600 --> 00:05:30.160
Um we'd also pass an image of the current node to the agent as well.

s61
00:05:30.160 --> 00:05:40.479
While the image by itself did not do a good job of converting to uh code, having the code context plus the image actually had better agentic output.

s62
00:05:40.479 --> 00:05:42.000
So what do I mean by better?

s63
00:05:42.000 --> 00:05:43.600
How did we know what was better?

s64
00:05:43.600 --> 00:05:45.120
We tried to do evals.

s65
00:05:45.120 --> 00:05:51.199
Um and so we did some sort of very simple eval to start with a mix of quantitative and qualitative data.

s66
00:05:51.199 --> 00:05:55.440
Uh, from a quantitative standpoint, we we looked at did it use variables?

s67
00:05:55.440 --> 00:05:57.199
Uh, did it use the theming we expected?

s68
00:05:57.199 --> 00:05:58.720
Did it use the right spot?

s69
00:05:58.720 --> 00:06:01.840
And from the qualitative side of things, does it look good?

s70
00:06:01.840 --> 00:06:04.880
Did it make good decisions with incomplete information?

s71
00:06:04.880 --> 00:06:09.280
And we spent like two hours grading an eval into an Excel spreadsheet.

s72
00:06:09.280 --> 00:06:10.880
And we said, we're never we're never doing that again.

s73
00:06:10.880 --> 00:06:11.600
It was awful.

s74
00:06:11.600 --> 00:06:13.919
Don't do eval by hand if you can help it.

s75
00:06:13.919 --> 00:06:19.280
Um, we had a bunch of toy repos that we kind of created or kind of had folks create for us.

s76
00:06:19.280 --> 00:06:28.479
Um and we eventually ended up coding up a web app to sort of help us with the eval which made things a lot easier at least from like a um process perspective.

s77
00:06:28.479 --> 00:06:44.639
One interesting thing is Figma um there are Figma files which we are converting there's a lot of open source code out there but there's not a lot of uh open- source code that also has fig files attached and so we had to either create our own or sort of find different ways to make automated systems.

s78
00:06:44.639 --> 00:06:50.240
Um, and now we have a um, eval that sort of runs like hundreds of times a week.

s79
00:06:50.240 --> 00:06:55.440
Engineers can kick this off and sort of grade against prompt changes um, with LLM judges.

s80
00:06:55.440 --> 00:06:58.319
So, we kind of remove the human from the loop where we don't need it.

s81
00:06:58.319 --> 00:07:04.160
Um, but having an agent translate a pixel uh, perfect version of code isn't enough.

s82
00:07:04.160 --> 00:07:09.199
Um, I mentioned that the React Tailwind version of our output was pixel perfect.

s83
00:07:09.199 --> 00:07:10.800
That's really only half the story.

s84
00:07:10.800 --> 00:07:17.120
An enterprise doesn't care if it's pixel perfect if it's not using its like battle tested accessible and internationalized components.

s85
00:07:17.120 --> 00:07:23.280
At Figma, we already had this concept of code connect which allows you to link design components to components in your codebase.

s86
00:07:23.280 --> 00:07:28.800
We needed a way to use this with our MCP server so that an a an agent used the correct components.

s87
00:07:28.800 --> 00:07:31.599
For example, this beautiful button here.

s88
00:07:31.599 --> 00:07:36.960
Uh this would be a perfect representation if you were to throw that into an HTML server.

s89
00:07:36.960 --> 00:07:38.960
But you kind of see two problems.

s90
00:07:38.960 --> 00:07:42.800
um if you had a primary button in your codebase, you wouldn't be referencing it.

s91
00:07:42.800 --> 00:07:47.599
And that's not ideal if it has accessibil accessibility properties or internationalization properties.

s92
00:07:47.599 --> 00:07:50.720
And then second, um you'd eat up the context window.

s93
00:07:50.720 --> 00:07:53.759
We we use Oh, this happened last time.

s94
00:07:53.759 --> 00:07:58.479
Uh we use React Tailwind to um basically convert things over.

s95
00:07:58.479 --> 00:08:04.160
Um but we want to make sure we do it in the sparest way possible.

s96
00:08:04.160 --> 00:08:07.360
All right, just going to keep vamping a bit here.

s97
00:08:07.360 --> 00:08:09.680
And so picture the same thing on screen.

s98
00:08:09.680 --> 00:08:12.479
Uh but now we have like all this React Tailwind code.

s99
00:08:12.479 --> 00:08:18.879
It's going to then be converted into sending over uh basically a sparse representation of it via code connect.

s100
00:08:18.879 --> 00:08:29.120
Um and by connecting the user's code to the design, we're able to pass back effectively what is a pointer which allows the agent to use the code component leading to our higher fidelity

s101
00:08:29.120 --> 00:08:30.080
uh implementation.

s102
00:08:30.080 --> 00:08:38.880
So effectively you go from like this big old thing of uh react tailin to the small react component that just says use button component.

s103
00:08:40.080 --> 00:08:44.240
Um all right it's going to let you restart it.

s104
00:08:44.240 --> 00:08:49.440
Okay cool pause that.

s105
00:08:55.200 --> 00:08:57.360
All right I can start talking a little bit about the next bit.

s106
00:08:57.360 --> 00:08:57.839
Oh, wait.

s107
00:08:57.839 --> 00:08:59.600
You can see this cool thing.

s108
00:08:59.600 --> 00:09:05.200
You can ask yourself how I described it well, but yeah, it's basically like a React component that you're able to then bring into your code.

s109
00:09:05.200 --> 00:09:11.040
Um, once we felt good about the serialization syntax, we started to look at what an MCB server can be.

s110
00:09:11.040 --> 00:09:19.360
And the MCP spec had a lot of great pieces in it, but some features weren't quite fleshed out within clients, and other features we really wish existed.

s111
00:09:19.360 --> 00:09:24.399
Many clients only implemented a subset of the spec, and many features were very experimental.

s112
00:09:24.399 --> 00:09:28.720
This is the client compatibility matrix from March 2025.

s113
00:09:28.720 --> 00:09:37.760
Today, for example, we expose a host of resources to an agent so that it can figure out um how to use our server as well as different help articles within Figma.

s114
00:09:37.760 --> 00:09:47.760
Um whereas before we would send that information down with like an air for example, and the agent would have to call uh wasting inference and sort of reasoning to sort of figure out what is actually going wrong.

s115
00:09:47.760 --> 00:09:51.760
Um one small part of the spec uh that was missing was server instructions.

s116
00:09:51.760 --> 00:09:52.880
I shouldn't say missing from the spec.

s117
00:09:52.880 --> 00:09:55.279
it was in the spec, but no clients implemented it.

s118
00:09:55.279 --> 00:10:02.640
Um, and it wasn't really highlighted in the docs until Anthropic added a nice blog post uh to sort of talk about it and then some clients started adding it.

s119
00:10:02.640 --> 00:10:07.200
And therefore, we would add uh additional instructions into each tool call.

s120
00:10:07.200 --> 00:10:15.120
Basically instructing the LLM how to use our server um even though server descriptions weren't necessarily written out yet.

s121
00:10:15.120 --> 00:10:19.360
Some other features that we really really wanted were elicitation and sampling.

s122
00:10:19.360 --> 00:10:27.519
Um, elicitation, if you haven't heard of it, is a way for you to ask the user a question, take that input, and pass it back to your server.

s123
00:10:27.519 --> 00:10:31.920
So, here we have it's VS Code, uh, and basically just asking, you know, what's my name?

s124
00:10:31.920 --> 00:10:35.600
And you're able to take that input and pass it back, uh, to the server.

s125
00:10:35.600 --> 00:10:44.320
Um, which is interesting on its own, but we thought in combination with sampling, which is unfortunately deprecated, but it's fine because you're able to work around it.

s126
00:10:44.320 --> 00:10:54.640
Sampling is a way of having a server query the client's LLM from from our server and in kind of the canonical case was for small queries.

s127
00:10:54.640 --> 00:10:59.519
We thought it'd be really useful to combine elicitation and sampling into a single workflow.

s128
00:10:59.519 --> 00:11:05.360
We talked about how code connect improves users workflows quite a bit and kind of makes outputs a lot better.

s129
00:11:05.360 --> 00:11:16.880
What we wanted to do was ask a user can we map out your code code base for code connections so that our MC MCP server can link them so that the output would be better and reduce the amount of context we send.

s130
00:11:16.880 --> 00:11:23.680
Unfortunately though most of the clients didn't implement these features and didn't allow you to properly query the the agent in the context of the codebase.

s131
00:11:23.680 --> 00:11:30.320
So for sampling even when VS code supported it you could only really query it as a general agent not specific to the codebase.

s132
00:11:30.320 --> 00:11:33.839
But we were able to kind of hack around it using tools.

s133
00:11:33.839 --> 00:11:41.760
When you got the context of a particular component or sorry of a particular design in Figma, if we noticed it was a component and that it wasn't code connected,

s134
00:11:41.760 --> 00:11:46.399
we'd send down a prompt to ask the user if they'd want to map the unlink component component.

s135
00:11:46.399 --> 00:11:49.279
Kind of mimicking elicitation.

s136
00:11:49.279 --> 00:11:56.320
If the user said yes, we'd send down another prompt to have the agent scan the code for potential matches, mimicking sampling.

s137
00:11:56.320 --> 00:12:03.760
We then service them in a specified format or ask the agent to do so and then have them send it back in bulk to make a bunch of code connections.

s138
00:12:03.760 --> 00:12:10.959
Um the screenshot on the right is the MCP inspector and if you haven't used it and you're developing an MCP server, you're doing yourself a disservice.

s139
00:12:10.959 --> 00:12:13.600
It's a really great tool and it's open source and great.

s140
00:12:13.600 --> 00:12:21.120
Um but the magic in our case was combining these two features because we could ask the user for for permission.

s141
00:12:21.120 --> 00:12:25.839
we can have the agent give us those suggestions and we can map them and in the end the users got a better experience.

s142
00:12:25.839 --> 00:12:27.440
Um that's pretty great.

s143
00:12:27.440 --> 00:12:40.320
Um the last sort of little thing that we did was we wanted to make our output the best it could be and we didn't necessarily know when we were starting you know we had our eval but we didn't know if the react tailwind code would be successful for other types of code bases.

s144
00:12:40.320 --> 00:12:45.760
Um and outside of the elicitation and sampling which didn't really work as we wanted there was no way of getting that information from the user.

s145
00:12:45.760 --> 00:12:54.160
So we added some optional query arguments to our tool calls for ones like get design context where they would send back what sort of language what sort of framework the user might be using.

s146
00:12:54.160 --> 00:13:03.040
This is imperfect uh agents lie but it was at least a signal for us to understand like oh this type of user this felt user may not have had a good experience.

s147
00:13:03.040 --> 00:13:06.079
Perhaps it's because our translation layer wasn't working as well.

s148
00:13:06.079 --> 00:13:10.399
We have found that that works pretty well but this was kind of our way of verifying that.

s149
00:13:10.399 --> 00:13:15.760
Um while we were working towards our first beta, we know we wanted four things.

s150
00:13:15.760 --> 00:13:17.760
We wanted to launch quickly.

s151
00:13:17.760 --> 00:13:21.279
Um we wanted to have the highest possible bar for our security.

s152
00:13:21.279 --> 00:13:23.279
We wanted to respect file permissions.

s153
00:13:23.279 --> 00:13:27.920
And we wanted to respect our pricing impact packaging so we didn't have abuse vectors.

s154
00:13:27.920 --> 00:13:40.079
And so after the spec changed and introduced OOTH in March 2025, we had to decide whether to keep our MCP server local or sort of switch to the new remote server using streamable HTTP and kind of like work on all the off problems.

s155
00:13:40.079 --> 00:13:49.440
Um we punted so until HMR there wasn't this offspec to to build from and we could easily relay off from our web app to our desktop app.

s156
00:13:49.440 --> 00:13:58.720
So for folks who don't know the Figma desktop app is Electron and so the front end of it is a web app and we basically just run figma.com in that and then we have an

s157
00:13:58.720 --> 00:14:05.680
um IPC uh bridge between the two and that sends it to our node process that allows us to talk to the user's file system.

s158
00:14:05.680 --> 00:14:12.480
Um we then sort of expose a server events server in node and that way clients could talk directly locally.

s159
00:14:12.480 --> 00:14:18.000
The local story was also really great with enterprises because they kind of like the idea of our data not being sent anywhere.

s160
00:14:18.000 --> 00:14:27.600
Um, this architecture was our fastest path to getting something into the hands of users to understand product market fit and what kind of tools and use cases folks had.

s161
00:14:28.480 --> 00:14:34.800
Um, we launched the MCP server internally and the reception was extremely honest.

s162
00:14:34.800 --> 00:14:35.745
Um, we

s163
00:14:35.745 --> 00:14:36.800
[laughter]

s164
00:14:36.800 --> 00:14:43.839
uh but we we worked out a lot of the kinks and we started to get some really positive feedback in the community um from from a bunch of nice folks.

s165
00:14:43.839 --> 00:14:46.480
Um, but launching this part was just the beginning.

s166
00:14:46.480 --> 00:14:52.399
Um we had a lot of improvements that we wanted to make and we immediately started working on the remote server as soon as we launched.

s167
00:14:52.399 --> 00:15:00.639
Um clients were on different timelines and we were still trying to figure out you know where we're going but we knew we wanted to get the remote server out so that's what we worked on.

s168
00:15:00.639 --> 00:15:03.760
Um in September we launched the remote server.

s169
00:15:03.760 --> 00:15:07.199
We ged both servers in October 2025.

s170
00:15:07.199 --> 00:15:19.519
Um, and then we started adding read and write capabilities and kind of all these things combined ended up making for Figma one of the fastest growing products that they've ever had, which was not something we expected when we started working on this.

s171
00:15:19.519 --> 00:15:24.079
Um, so late last year, I started working on a slightly different thing.

s172
00:15:24.079 --> 00:15:31.199
Um, we started to see some research that designers really wanted to shift to writing production code in certain cases and we didn't really have a dedicated product for this.

s173
00:15:31.199 --> 00:15:45.120
So I started hacking with a bunch of MCP MCP folks at an offsite and this eventually became something called make in your local codebase uh which is kind of Figma's agent solution for for working on on git GitHub and and local code bases.

s174
00:15:45.120 --> 00:15:49.839
Um and the reason I bring this up is only slightly self- serving but it relates to this this next slide.

s175
00:15:49.839 --> 00:15:57.120
Um if there's one thing you want to take away from this talk it's that we're so early like this has not been a long time.

s176
00:15:57.120 --> 00:16:02.720
The MC MCP spec is only two years old and we're still figuring out the best way to do things.

s177
00:16:02.720 --> 00:16:10.880
And then second, um, Figma's done a great job of letting engineers build and figure out, uh, what's next and letting them run with it run with it.

s178
00:16:10.880 --> 00:16:12.639
I wasn't staffed on MCP.

s179
00:16:12.639 --> 00:16:21.759
I wasn't staffed on our make product, but I ended up helping them be built um, just because I was kind of given the leeway to do so and learning a ton along the way.

s180
00:16:21.759 --> 00:16:24.000
Um, that's all for my talk.

s181
00:16:24.000 --> 00:16:28.320
I'll be around today and tomorrow, but feel free to reach and happy to talk about MCP, Figma and all that.

s182
00:16:28.320 --> 00:16:31.040
Thanks so much for your time.
