Introduction & Demo: MCP in Action
Hey, this is Lan from Lang chain. You've heard a lot about MCP lately. I want to carefully break down what it is. I want to build an MCP server from scratch. I'm going to connect it to your favorite tools like Claude, Cursor, and Windsurf.
Here I'm in Cursor, open up a chat, select the agent. I'll ask a question about LangGraph. I'm using Claude 3.5 Sonnet. You can see right here I have an MCP tool connected to Cursor called langgraph_query_tool. I hit run. Here's the query. Here are the docs I get back from my tool. Use the docs to answer the question.
Here I'm in Windsurf, open up Cascade. I can actually see my MCP server connected right here. What is LangGraph? Again, using Claude 3.5. There it is. I can see my MCP tool is called with this query, and it uses the retrieved docs to answer the question.
Here I'm in Claude desktop. You can see I have my LangGraph query tool as an MCP tool available. We ask what is LangGraph. We can see that Claude is now attempting to call our tool. We allow it, it formulates a query to our tool, and it uses the retrieved docs from our tool to answer the question.
We've seen MCP acting as a tool in three different popular applications: Cursor, Windsurf, Claude desktop. Now, let me explain exactly what I did. Let's build this all from scratch.
So first, what's the motivation here? Augmenting LLMs with context or tools is a well-known, popular, and important practice. Augmenting LLMs with context is common in approaches like RAG. Augmenting LLMs with tools is central for building agents.
Now, to make this concrete and before we get into MCP, I want to just build a tool for you from scratch and show how it works. So I'm in Cursor, I'm in a completely empty directory here. First, I'm going to create a virtual environment and I'll do a few pip installs. Now I'm in a fresh notebook, and we saw with Cursor, Windsurf and Claude this LangGraph query tool. I'm going to go ahead and build it from scratch so you can see exactly what's going on here.
So to build this tool, I'm going to do a few things. I'm going to load LangGraph docs. Here's the URLs I want to load from. I'm going to use this URL loader and grab all child links for each URL up to depth 5. It'll apply a little extractor to clean up the web pages. We'll take everything we loaded, we'll save it to a file called lms_full.text. We'll split all those documents. I'm going to use 8,000 tokens as my split size because that's the context size of the embedding model I'm going to use to embed all these documents. And I'm going to create a vector store. I'll use OpenAI embeddings and I'll save that Vector store to this directory. Great. So that all ran. We can see it loaded 122 documents, here's all the URLs, around 300,000 tokens, and it saves that all to a little Vector store here locally in our directory. Let's test it out. Nice. I can ask a question with LangGraph, I get back relevant documents based upon semantic similarity search. So this simple workflow to take a bunch of web pages and index them into a vector store is super common. It's the foundation for RAG and it's a very nice foundation for creating a tool which retrieves documents given an input.
Now if I'm just using LangChain to do this, all I have to do is from langchain_core import tool, add this little tool decorator to a function which defines my tool. Query in, returns a string of retrieved documents, accesses my vector store which is saved, and returns some documents. Now if I'm just using—I can use convenience methods like bind_tools that we have in LangChain, but many of the model SDKs have their own method to bind tools to the model as long as it supports tool calling. We can take this tool that we defined, we can bind it to our model and we have an augmented LLM. Let's test this out. Invoke my augmented LLM with some messages. This is the key point. I get a tool call back out. We can look at LangSmith at the trace. We can see here's our model and here's our tool. It was called, and again, we see our LangGraph query tool is called and forms this query which can then be passed to the tool.
Explaining the Model Context Protocol (MCP)
All right, that's just setting up the fundamentals. We have a tool called langgraph_query_tool. We've connected it to our chat model. Here's the bridge to MCP. What if I want to take this same tool and connect it to AI applications like Cursor, Claude Code, Claude Desktop, or Windsurf? How do I do that? That is where MCP comes in.
So it's kind of analogous. LangChain Chat Model is a convenient interface which exposes this bind_tools method, which allows you to bind tools directly to a whole bunch of different models. MCP is a protocol that allows you to bind tools you define to many different AI apps. So it's kind of analogous. Both provide interfaces for models to interact with external tools. Both support the concept of exposing tools so models can invoke them. That's really the key point.
Now MCP is just a client-server protocol, where the host or the application is your client, and then the server exposes a number of different things. It can expose tools, which is the most intuitive that we've just been talking about, but it can also expose resources, which is basically just like documents or files, and it can expose prompts. So just be aware that MCP servers can do more than just expose tools. And there's different modes of communication between servers and hosts, including standard I/O or server-sent events.
Now, we previously saw Cursor, Windsurf, and Claude Desktop all working with this langgraph_query_tool. So what was going on? Well, I'm going to show you how to build a server in a minute, but at a high level, what's happening is this MCP server is connected to the hosts in some way, so the hosts have awareness that this tool exists. That's the same as in bind_tools, right? Now in practice, what's happening is the server is using standard in and out and the communication mode is JSON. And when I ask a question about LangGraph in any of these hosts, like Windsurfer or Claude as you just saw, the host knows that this tool is available and it can make a call to the tool using a standard method and the tool output is returned to the host.
Now here's the thing I found a little bit confusing when I started. What is this server and where is it? So this server in my case is just running locally on my machine, and what's also kind of interesting is this server is actually launched by the MCP host application. So it's actually launched by Cursor, Windsurf, and Claude. So I don't have to worry about spinning up that server independently. And that kind of confused me in the beginning, but really what's happening is, as you're going to see shortly, we create this server and we just, in configuration, connect it to our various hosts. Those hosts know it exists, and the host will spin up this server automatically for us. And it'll make requests to the server, like initialize at the start, list_tools available at the start, and then call the tools accordingly based upon the interaction with the user. That's all that's going on here.
Building and Inspecting the MCP Server
All right, so now for the fun part, let's actually define a server. So it's just a script, I'm going to call this langgraph_mcp.py. And all that's happening here is a few really simple things. I'm using the Model Context Protocol Python SDK, and I'm importing FastMCP. I create my server here, and I go ahead and add a tool called langgraph_query_tool. By the way, this is exactly the same function that I defined here. Literally, it's the same. Here I create a tool, here I create a LangChain tool and I bind it to my chat model right here. Now I'm creating an MCP tool that'll be bound to Cursor, Windsurf, Claude desktop. Literally the same logic.
Now, this is another little interesting twist I wanted to add, and this one's a little bit less supported but I'll show it anyway. You can also define resources. I mentioned that before, and this can actually be documents. So when we ran our notebook, I actually created this lms_full.text. This is basically a dump of LangGraph docs, okay? And what's kind of neat is you can define a resource that will just grab that file and return it. I'll show where that can be kind of cool. And the final thing is just this last part, mcp.run. We're going to use standard I/O. That's literally it, super simple little Python script.
Now you can run what they call an inspector: mpx model-context-protocol-inspector. This spins up, and you can see it spins up. Specify my transport type. Now this is where I just specify my directory and this is my Python virtual environment. Hit connect and we can see, look at resources, list resources, we see that docs/langgraph.full resource. Again, that's what I defined right here. Click on this, there's that full doc. Pretty nice. Check tools, list tools. Here's that LangGraph query tool. We actually can run the tool from here. What is LangGraph? Run the tool. And we can see here's some retrieve documents. We can just test the server directly before we attempt to connect it to any of our hosts.
Connecting the MCP Server to Host Applications
All right, so we're basically done here. All you need now is this little configuration, MCP servers. So again, this is the command, this is the argument that we passed in the inspector to connect this server to our hosts. You're going to add this little snippet to a few different configuration files. It's that same command, same argument, as well as the OpenAI API key, and that's only needed because we use an embedding model in this particular tool to embed the incoming query.
Now, right here I show the configuration files that you modify with this snippet to connect this particular server to the different hosts. And it's just as one example, you can see just open that up for Cursor. And in this case, I actually don't have my OpenAPI key specified, of course, but you can just see path to Python, path to my server definition. And just do the following for the other hosts you're interested in. And closing the loop, that's why when we asked what is LangGraph with the Cursor agent, it was aware of the MCP tool and it called it with this input, receiving this output. Perfect. That's exactly like we saw with our inspector.
And again, if you go to Cursor settings up here, click MCP, and you can clearly see our server is right here. Very nice. And in Windsurf again, open up our chat. Let's create a new chat. What's pretty nice is it shows you directly any MCP servers. I'll zoom in a little bit. We can see one available MCP server, there it is. To configure MCP, just click on this button and it takes you to that configuration file. Claude desktop, open settings, go to developer, you can see that we can edit the configuration file and that it is indeed running.
Now one other cool trick I'll show you about using Claude desktop. If you click on a little icon here, this allows you to grab that resource. Pretty cool. Look at that, there it is. So it's a large context doc, but it pulls that doc directly into context, and I can ask detailed questions about LangGraph that would reference this full doc. Now I spoke with folks at Windsurf and resources are not yet supported, probably will be soon. I'm not sure about cursor, but I poked around and it seems that MCP resources are also not supported. So again, of course, like we just saw, you can use tools, but not the resource. And again, that resource was just that full document that we defined here.
Recap and Final Thoughts
So just to recap, we know we want to augment LLMs with context and tools. It's pretty easy to do this when you're working with the SDKs directly. You can just define tools as functions typically. You can use helpers like bind_tools with LangChain to just simply bind them to your model of choice. But of course the problem is, well, how do I take a tool and bind it to an IDE or Claude desktop app? That's where MCP comes in. It provides a standard protocol for doing so. It works with tools, but also works with resources like we saw, like raw docs, and with prompts, with customizable communication between a client, which is some application—Cursor, Windsurf, Claude—and a server which we can define and build from scratch. And we saw it just works pretty nicely with our various hosts.
So there's a lot of talk about this right now, and there's also a lot of confusion. Frankly, I was a bit confused myself. I actually had to sit down and implement my own server. I had to poke around a lot to find the right resources, even identifying, okay, here's the configuration files I need to modify, here's how to inspect it, here's how to define a resource versus a tool using FastMCP. All these things I had to kind of search around for a little bit. But I wanted to just put everything in one place, make it as simple as possible, and really just highlight what's the point of all this and why is it useful.
Long story short, it gives you a way to bind tools and context to different LLM applications like IDEs, like Claude desktop, and it provides a common interface for those applications to discover what tools exist and also call them. So hopefully this is helpful. Leave any comments or questions below if there's anything that's unclear. And just making this also clarified a lot for me and hopefully we're able to convey that also to you. Thanks.