- : Why I Wrote This
- The Evolution of Tool Integration with LLMs
- What Is Model Context Protocol (MCP), Really?
- Wait, MCP sounds like RAG… but is it?
- Quick recap!
- Core Capabilities of an MCP Server
- Real-World Example: Claude Desktop + MCP (Pre-built Servers)
- Build Your Own: Custom MCP Server from Scratch
- 🎉 Congrats, You’ve Mastered MCP!
- References
: Why I Wrote This
I will likely be trustworthy. After I first noticed the time period “Mannequin Context Protocol (mcp),” I did what most builders do when confronted with yet one more new acronym: I skimmed a tutorial, noticed some JSON, and quietly moved on. “Too summary,” I assumed. Quick-forward to after I really tried to combine some customized instruments with Claude Desktop— one thing that wanted reminiscence or entry to exterior instruments — and all of a sudden, MCP wasn’t simply related. It was important.
The issue? Not one of the tutorials I got here throughout felt beginner-friendly. Most jumped straight into constructing a customized MCP server with out explaining in particulars why you’d want a server within the first place — not to mention mentioning that prebuilt MCP servers exist already and work out of the field. So, I made a decision to study it from the bottom up.
I learn all the pieces I might, experimented with each prebuilt and customized servers, built-in it with Claude Desktop and examined whether or not I might clarify it to my mates —folks with zero prior context. After I lastly received the nod from them, I knew I might break it down for anybody, even in case you’ve by no means heard of MCP till 5 minutes in the past.
This text breaks down what MCP is, why it issues, and the way it compares to different well-liked architectures like RAG. We’ll go from “what even is that this?” to spinning up your individual working Claude integration — no prior MCP information required. In case you’ve ever struggled to get your AI mannequin to really feel rather less like a goldfish, that is for you.
The Evolution of Software Integration with LLMs
Earlier than diving into MCP, let’s perceive the development of how we join Massive Language Fashions (LLMs) to exterior instruments and knowledge:
- Standalone LLMs: Initially, fashions like GPT and Claude operated in isolation, relying solely on their coaching knowledge. They couldn’t entry real-time data or work together with exterior techniques.
- Software Binding: As LLMs superior, builders created strategies to “bind” instruments on to fashions. For instance, with LangChain or related frameworks, you may do one thing like:
llm = ChatAnthropic()
augmented_llm = llm.bind_tools([search_tool, calculator_tool])
This works properly for particular person scripts however doesn’t scale simply throughout functions. Why? As a result of instrument binding in frameworks like LangChain is often designed round single-session, stateless interactions, which means each time you spin up a brand new agent or operate name, you’re usually re-defining which instruments it will probably entry. There’s no centralized method to handle instruments throughout a number of interfaces or consumer contexts.
3. Software Integration Problem: The actual complexity arises while you wish to combine instruments with AI-powered functions like IDEs (Cursor, VS Code), chat interfaces (Claude Desktop), or different productiveness instruments. Every software would want customized connectors for each attainable instrument or knowledge supply, making a tangled net of integrations.
That is the place MCP enters the image — offering a standardized layer of abstraction for connecting AI functions to exterior instruments and knowledge sources.
What Is Mannequin Context Protocol (MCP), Actually?
Let’s break it down:
- Mannequin: The LLM on the coronary heart of your software — GPT, Claude, no matter. It’s a robust reasoning engine however restricted by what it was educated on and the way a lot context it will probably maintain.
- Context: The additional data your mannequin must do its job — paperwork, search outcomes, consumer preferences, latest historical past. Context extends the mannequin’s capabilities past its coaching set.
- Protocol: A standardized method of speaking between elements. Consider it as a typical language that lets your mannequin work together with instruments and knowledge sources in a predictable method.
Put these three collectively, and MCP turns into a framework that connects fashions to contextual data and instruments by means of a constant, modular, and interoperable interface.
Very similar to HTTP enabled the online by standardizing how browsers speak to servers, MCP standardizes how AI functions work together with exterior knowledge and capabilities.
Professional tip! A simple method to visualize MCP is to think about it like instrument binding for your entire AI stack, not only a single agent. That’s why Anthropic describes MCP as “a USB-C port for AI functions.”

Wait, MCP appears like RAG… however is it?
Lots of people ask, “How is that this completely different from RAG?” Nice query.
At a look, each MCP and RAG intention to resolve the identical drawback: give language fashions entry to related, exterior data. However how they do it — and the way maintainable they’re — differs considerably.
In an MCP-based setup
- Your AI app (host/shopper) connects to an MCP doc server
- You work together with context utilizing a standardized protocol
- You may add new paperwork or instruments with out modifying the app
- All the pieces works by way of the identical interface, persistently

In a conventional RAG system
- Your app manually builds and queries a vector database
- You usually want customized embedding logic, retrievers, and loaders
- Including new sources means rewriting a part of your app code
- Each integration is bespoke, tightly coupled to your app logic
The important thing distinction is abstraction: The Protocol in Model Context Protocol is nothing however a standardized abstraction layer that defines bidirectional communication between MCP Shopper/Host and MCP Servers.

MCP provides your app the flexibility to ask, “Give me details about X,” with out understanding how that information is saved or retrieved. RAG techniques require your app to handle all of that.
With MCP, your software logic stays the identical, at the same time as your doc sources evolve.
Let’s have a look at some high-level codes to see how these approaches differ:
Conventional RAG Implementation
In a conventional RAG implementation, your software code instantly manages connections to doc sources:
# Hardcoded vector retailer logic
vectorstore = FAISS.load_local("retailer/embeddings")
retriever = vectorstore.as_retriever()
response = retriever.invoke("question about LangGraph")
With instrument binding, you outline instruments and bind them to an LLM, however nonetheless want to change the instrument implementation to include new knowledge sources. You continue to must replace the instrument implementation when your backend modifications.
@instrument
def search_docs(question: str):
return search_vector_store(question)
MCP Implementation
With MCP, your software connects to a standardized interface, and the server handles the specifics of doc sources:
# MCP Shopper/Host: Shopper/Host stays the identical
# MCP Server: Outline your MCP server
# Import crucial libraries
from typing import Any
from mcp.server.fastmcp import FastMCP
# Initialize FastMCP server
mcp = FastMCP("your-server")
# Implement your server's instruments
@mcp.instrument()
async def example_tool(param1: str, param2: int) -> str:
"""An instance instrument that demonstrates MCP performance.
Args:
param1: First parameter description
param2: Second parameter description
Returns:
A string end result from the instrument execution
"""
# Software implementation
end result = f"Processed {param1} with worth {param2}"
return end result
# Instance of including a useful resource (non-obligatory)
@mcp.useful resource()
async def get_example_resource() -> bytes:
"""Supplies instance knowledge as a useful resource.
Returns:
Binary knowledge that may be learn by purchasers
"""
return b"Instance useful resource knowledge"
# Instance of including a immediate template (non-obligatory)
mcp.add_prompt(
"example-prompt",
"It is a template for {{function}}. You need to use it to {{motion}}."
)
# Run the server
if __name__ == "__main__":
mcp.run(transport="stdio")
Then, you configure the host or shopper (like Claude Desktop) to make use of the server by updating its configuration file.
{
"mcpServers": {
"your-server": {
"command": "uv",
"args": [
"--directory",
"/ABSOLUTE/PATH/TO/PARENT/FOLDER/your-server",
"run",
"your-server.py"
]
}
}
}
In case you change the place or how the assets/paperwork are saved, you replace the server — not the shopper.
That’s the magic of abstraction.
And for a lot of use circumstances — particularly in manufacturing environments like IDE extensions or industrial functions — you can’t contact the shopper code in any respect. MCP’s decoupling is greater than only a nice-to-have: it’s a necessity. It isolates the applying code in order that solely the server-side logic (instruments, knowledge sources, or embeddings) must evolve. The host software stays untouched. This permits fast iteration and experimentation with out risking regression or violating software constraints.
Fast recap!
Hopefully, by now, it’s clear why MCP really issues.
Think about you’re constructing an AI assistant that should:
- Faucet right into a information base
- Execute code or scripts
- Preserve observe of previous consumer conversations
With out MCP, you’re caught writing customized glue code for each single integration. Positive, it really works — till it doesn’t. It’s fragile, messy, and a nightmare to keep up at scale.
MCP fixes this by performing as a common adapter between your mannequin and the skin world. You may plug in new instruments or knowledge sources with out rewriting your mannequin logic. Which means sooner iteration, cleaner code, fewer bugs, and AI functions which can be really modular and maintainable.
And I hope you have been paying consideration after I mentioned MCP allows bidirectional communication between the host (shopper) and the server — as a result of this unlocks one in all MCP’s strongest use circumstances: persistent reminiscence.
Out of the field, LLMs are goldfish. They overlook all the pieces until you manually stuff your entire historical past into the context window. However with MCP, you’ll be able to:
- Retailer and retrieve previous interactions
- Preserve observe of long-term consumer preferences
- Construct assistants that truly “bear in mind” full tasks or ongoing periods
No extra clunky prompt-chaining hacks or fragile reminiscence workarounds. MCP provides your mannequin a mind that lasts longer than a single chat.
Core Capabilities of an MCP Server
With all that in thoughts, it’s fairly clear: the MCP server is the MVP of the entire protocol.
It’s the central hub that defines the capabilities your mannequin can really use. There are three foremost varieties:
- Assets: Consider these as exterior knowledge sources — PDFs, APIs, databases. The mannequin can pull them in for context, however it will probably’t change them. Learn-only.
- Instruments: These are the precise features the mannequin can name — run code, search the online, generate summaries, you identify it.
- Prompts: Predefined templates that information the mannequin’s conduct or construction its responses. Like giving it a playbook.
What makes MCP highly effective is that each one of those are uncovered by means of a single, constant protocol. Which means the mannequin can request, invoke, and incorporate them while not having customized logic for each. Simply plug into the MCP server, and all the pieces’s able to go.
Actual-World Instance: Claude Desktop + MCP (Pre-built Servers)
Out of the field, Anthropic provides a bunch of pre-built MCP servers you’ll be able to plug into your AI apps — issues like Claude Desktop, Cursor, and extra. Setup is tremendous fast and painless.
For the complete listing of accessible servers, head over to the MCP Servers Repository. It’s your buffet of ready-to-use integrations.
On this part, I’ll stroll you thru a sensible instance: extending Claude Desktop so it will probably learn out of your pc’s file system, write new information, transfer them round, and even search by means of them.
This walkthrough relies on the Quickstart information from the official docs, however truthfully, that information skips a couple of key particulars — particularly in case you’ve by no means touched these settings earlier than. So I’m filling within the gaps and sharing the additional suggestions I picked up alongside the best way to save lots of you the headache.
1. Obtain Claude Desktop
First issues first — seize Claude Desktop. Select the model for macOS or Home windows (sorry Linux of us, no assist simply but).
Comply with the set up steps as prompted.
Have already got it put in? Ensure you’re on the newest model by clicking the Claude menu in your pc and deciding on “Verify for Updates…”
2. Verify the Conditions
You’ll want Node.js put in in your machine to get this working easily.
To test if you have already got Node put in:
- On macOS: Open the Terminal out of your Purposes folder.
- On Home windows: Press
Home windows + R
, kindcmd
, and hit Enter. - Then run the next command in your terminal:
node --version
In case you see a model quantity, you’re good to go. If not, head over to nodejs.org and set up the newest LTS model.
3. Allow Developer Mode
Open Claude Desktop and click on on the “Claude” menu within the top-left nook of your display. From there, choose Assist.
On macOS, it ought to look one thing like this:

From the drop-down menu, choose “Allow Developer Mode.”
In case you’ve already enabled it earlier than, it gained’t present up once more — but when that is your first time, it ought to be proper there within the listing.
As soon as Developer Mode is turned on:
- Click on on “Claude” within the top-left menu once more.
- Choose “Settings.”
- A brand new pop-up window will seem — search for the “Developer” tab within the left-hand navigation bar. That’s the place all the great things lives.

4. Set Up the Configuration File
Nonetheless within the Developer settings, click on on “Edit Config.”
It will create a configuration file if one doesn’t exist already and open it instantly in your file system.
The file location is determined by your OS:
- macOS:
~/Library/Software Help/Claude/claude_desktop_config.json
- Home windows:
%APPDATApercentClaudeclaude_desktop_config.json
That is the place you’ll outline the servers and capabilities you need Claude to make use of — so preserve this file open, we’ll be enhancing it subsequent.

Open the config file (claude_desktop_config.json
) in any textual content editor. Substitute its contents with the next, relying in your OS:
For macOS:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/username/Desktop",
"/Users/username/Downloads"
]
}
}
}
For Home windows:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"C:UsersusernameDesktop",
"C:UsersusernameDownloads"
]
}
}
}
Be sure to exchange "username"
together with your precise system username. The paths listed right here ought to level to legitimate folders in your machine—this setup provides Claude entry to your Desktop and Downloads, however you’ll be able to add extra paths if wanted.
What This Does
This config tells Claude Desktop to robotically begin an MCP server referred to as "filesystem"
each time the app launches. That server runs utilizing npx
and spins up @modelcontextprotocol/server-filesystem
, which is what lets Claude work together together with your file system—learn, write, transfer information, search directories, and so on.
⚠️ Command Privileges
Only a heads-up: Claude will run these instructions together with your consumer account’s permissions, which means it will probably entry and modify native information. Solely add instructions to the config file in case you perceive and belief the server you’re hooking up — no random packages from the web!
5. Restart Claude
When you’ve up to date and saved your configuration file, restart Claude Desktop to use the modifications.
After it boots up, you must see a hammer icon within the bottom-left nook of the enter field. That’s your sign that the developer instruments — and your customized MCP server — are up and working.

After clicking the hammer icon, you must see the listing of instruments uncovered by the Filesystem MCP Server — issues like studying information, writing information, looking out directories, and so forth.

In case you don’t see your server listed or nothing exhibits up, don’t fear. Bounce over to the Troubleshooting part within the official documentation for some fast debugging tricks to get issues again on observe.
6. Strive It Out!
Now that all the pieces’s arrange, you can begin chatting with Claude about your file system — and it ought to know when to name the proper instruments.
Right here are some things you’ll be able to attempt asking:
- “Are you able to write a poem and put it aside to my Desktop?”
- “What are some work-related information in my Downloads folder?”
- “Can you’re taking all the pictures on my Desktop and transfer them to a brand new folder referred to as ‘Photos’?”
When wanted, Claude will robotically invoke the suitable instruments and ask to your approval earlier than doing something in your system. You keep in management, and Claude will get the job completed.
Construct Your Personal: Customized MCP Server from Scratch
Alright, able to stage up?
On this part, you’ll go from consumer to builder. We’re going to put in writing a customized MCP server that Claude can speak to — particularly, a instrument that lets it search the newest documentation from AI libraries like LangChain, OpenAI, MCP (sure, we’re utilizing MCP to study MCP), and LlamaIndex.
As a result of let’s be trustworthy — what number of occasions have you ever watched Claude confidently spit out deprecated code or reference libraries that haven’t been up to date since 2021?
This instrument makes use of real-time search, scrapes dwell content material, and offers your assistant contemporary information on demand. Sure, it’s as cool because it sounds.
The venture is constructed utilizing the official MCP SDK from Anthropic. In case you’re comfy with Python and the command line, you’ll be up and working very quickly. And even in case you’re not — don’t fear. We’ll stroll by means of all the pieces step-by-step, together with the elements most tutorials simply assume you already know.
Conditions
Earlier than we dive in, listed here are the belongings you want put in in your system:
- Python 3.10 or increased — that is the programming language we’ll use
- MCP SDK (v1.2.0 or increased) — this offers you all of the instruments to create a Claude-compatible server (which will likely be put in in upcoming elements)
- uv (package deal supervisor) — consider it like a contemporary model of pip, however a lot sooner and simpler to make use of for tasks (which will likely be put in in upcoming elements)
Step 1: Set up uv
(the Bundle Supervisor)
I
On macOS/Linux:
curl –LsSf https://astral.sh/uv/set up.sh | sh
On Home windows:
powershell –ExecutionPolicy ByPass -c "irm https://astral.sh/uv/set up.ps1 | iex"
It will obtain and set up uv
in your machine. As soon as it’s completed, shut and reopen your terminal to verify the uv
command is acknowledged. (In case you’re on Home windows, you should utilize WSL or observe their Home windows directions.)
To test that it’s working, run this command in your terminal:
uv --version
In case you see a model quantity, you’re good to go.
Step 2: Set Up Your Venture
Now we’re going to create a folder for our MCP server and get all of the items in place. In your terminal, run these instructions:
# Create and enter your venture folder
uv init mcp-server
cd mcp-server
# Create a digital setting
uv venv
# Activate the digital setting
supply .venv/bin/activate # Home windows: .venvScriptsactivate
Wait — what’s all this?
uv init mcp-server
units up a clean Python venture namedmcp-server
.uv venv
creates a digital setting (your non-public sandbox for this venture).supply .venv/bin/activate
activates that setting so all the pieces you put in stays inside it.
Step 3: Set up the Required Packages
Inside your digital setting, set up the instruments you’ll want:
uv add "mcp[cli]" httpx beautifulsoup4 python-dotenv
Right here’s what every package deal does:
mcp[cli]
: The core SDK that allows you to construct servers Claude can speak tohttpx
: Used to make HTTP requests (like fetching knowledge from web sites)beautifulsoup4
: Helps us extract readable textual content from messy HTMLpython-dotenv
: Lets us load API keys from a.env
file
Earlier than we begin writing code, it’s a good suggestion to open the venture folder in a textual content editor so you’ll be able to see all of your information in a single place and edit them simply.
In case you’re utilizing VS Code (which I extremely suggest in case you’re undecided what to make use of), simply run this from inside your mcp-server
folder:
code .
This command tells VS Code to open the present folder (.
simply means “proper right here”).
🛠️ If the
code
command doesn’t work, you most likely must allow it:1. Open VS Code
2. Press
Cmd+Shift+P
(orCtrl+Shift+P
on Home windows)3. Kind:
Shell Command: Set up 'code' command in PATH
4. Hit Enter, then restart your terminal
In case you’re utilizing one other editor like PyCharm or Chic Textual content, you’ll be able to simply open the
mcp-server
folder manually from throughout the app.
Step 3.5: Get Your Serper API Key (for Net Search)
To energy our real-time documentation search, we’ll use Serper — a easy and quick Google Search API that works nice for AI brokers.
Right here’s the best way to set it up:
- Head over to serper.dev and click on Signal Up:
It’s free for fundamental utilization and works completely for this venture. - As soon as signed in, go to your Dashboard:
You’ll see your API Key listed there. Copy it. - In your venture folder, create a file referred to as
.env:<br>
That is the place we’ll retailer the important thing securely (so we’re not hardcoding it). - Add this line to your
.env
file:
SERPER_API_KEY=your-api-key-here
Substitute your-api-key-here
with the precise key you copied
That’s it — now your server can speak to Google by way of Serper and pull in contemporary docs when Claude asks.
Step 4: Write the Server Code
Now that your venture is about up and your digital setting is working, it’s time to truly write the server.
This server goes to:
- Settle for a query like: “How do I exploit retrievers in LangChain?”
- Know which documentation website to go looking (e.g., LangChain, OpenAI, and so on.)
- Use an internet search API (Serper) to seek out one of the best hyperlinks from that website
- Go to these pages and scrape the precise content material
- Return that content material to Claude
That is what makes your Claude smarter — it will probably look issues up from actual docs as a substitute of creating issues up based mostly on outdated knowledge.
⚠️ Fast Reminder About Moral Scraping
All the time respect the location you’re scraping. Use this responsibly. Keep away from hitting pages too usually, don’t scrape behind login partitions, and test the location’s robots.txt
file to see what’s allowed. You may learn extra about it here.
Your instrument is just as helpful as it’s respectful. That’s how we construct AI techniques that aren’t simply sensible — however sustainable too.
1. Create Your Server File
First, run this from inside your mcp-server
folder to create a brand new file:
contact foremost.py
Then open that file in your editor (if it isn’t open already). Substitute the code there with the next:
from mcp.server.fastmcp import FastMCP
from dotenv import load_dotenv
import httpx
import json
import os
from bs4 import BeautifulSoup
load_dotenv()
mcp = FastMCP("docs")
USER_AGENT = "docs-app/1.0"
SERPER_URL = "https://google.serper.dev/search"
docs_urls = {
"langchain": "python.langchain.com/docs",
"llama-index": "docs.llamaindex.ai/en/steady",
"openai": "platform.openai.com/docs",
"mcp": "modelcontextprotocol.io"
}
async def search_web(question: str) -> dict | None:
payload = json.dumps({"q": question, "num": 2})
headers = {
"X-API-KEY": os.getenv("SERPER_API_KEY"),
"Content material-Kind": "software/json",
}
async with httpx.AsyncClient() as shopper:
attempt:
response = await shopper.put up(
SERPER_URL, headers=headers, knowledge=payload, timeout=30.0
)
response.raise_for_status()
return response.json()
besides httpx.TimeoutException:
return {"natural": []}
besides httpx.HTTPStatusError as e:
print(f"HTTP error occurred: {e}")
return {"natural": []}
async def fetch_url(url: str) -> str:
async with httpx.AsyncClient(headers={"Person-Agent": USER_AGENT}) as shopper:
attempt:
response = await shopper.get(url, timeout=30.0)
response.raise_for_status()
soup = BeautifulSoup(response.textual content, "html.parser")
# Attempt to extract foremost content material and take away navigation, sidebars, and so on.
main_content = soup.discover("foremost") or soup.discover("article") or soup.discover("div", class_="content material")
if main_content:
textual content = main_content.get_text(separator="n", strip=True)
else:
textual content = soup.get_text(separator="n", strip=True)
# Restrict content material size if it is too giant
if len(textual content) > 8000:
textual content = textual content[:8000] + "... [content truncated]"
return textual content
besides httpx.TimeoutException:
return "Timeout error when fetching the URL"
besides httpx.HTTPStatusError as e:
return f"HTTP error occurred: {e}"
@mcp.instrument()
async def get_docs(question: str, library: str) -> str:
"""
Search the newest docs for a given question and library.
Helps langchain, openai, mcp and llama-index.
Args:
question: The question to seek for (e.g. "Chroma DB")
library: The library to go looking in (e.g. "langchain")
Returns:
Textual content from the docs
"""
if library not in docs_urls:
increase ValueError(f"Library {library} not supported by this instrument. Supported libraries: {', '.be a part of(docs_urls.keys())}")
question = f"website:{docs_urls[library]} {question}"
outcomes = await search_web(question)
if not outcomes or len(outcomes.get("natural", [])) == 0:
return "No outcomes discovered"
combined_text = ""
for i, end in enumerate(outcomes["organic"]):
url = end result["link"]
title = end result.get("title", "No title")
# Add separator between outcomes
if i > 0:
combined_text += "nn" + "="*50 + "nn"
combined_text += f"Supply: {title}nURL: {url}nn"
page_content = await fetch_url(url)
combined_text += page_content
return combined_text
if __name__ == "__main__":
mcp.run(transport="stdio")
2. How The Code Works
First, we arrange the inspiration of our customized MCP server. It pulls in all of the libraries you’ll want — like instruments for making net requests, cleansing up webpages, and loading secret API keys. It additionally creates your server and names it "docs"
so Claude is aware of what to name. Then, it lists the documentation websites (like LangChain, OpenAI, MCP, and LlamaIndex) your instrument will search by means of. Lastly, it units the URL for the Serper API, which is what the instrument will use to ship Google search queries. Consider it as prepping your workspace earlier than really constructing the instrument.
Click on right here to see the revelant code snippet
from mcp.server.fastmcp import FastMCP
from dotenv import load_dotenv
import httpx
import json
import os
from bs4 import BeautifulSoup
load_dotenv()
mcp = FastMCP("docs")
USER_AGENT = "docs-app/1.0"
SERPER_URL = "https://google.serper.dev/search"
docs_urls = {
"langchain": "python.langchain.com/docs",
"llama-index": "docs.llamaindex.ai/en/steady",
"openai": "platform.openai.com/docs",
"mcp": "modelcontextprotocol.io"
}
Then, we outline a operate that lets our instrument speak to the Serper API, which we’ll use as a wrapper round Google Search.
This operate, search_web
, takes in a question string, builds a request, and sends it off to the search engine. It consists of your API key for authentication, tells Serper we’re sending JSON, and limits the variety of search outcomes to 2 for pace and focus. The operate returns a dictionary containing the structured outcomes, and it additionally gracefully handles timeouts or any errors which may come from the API. That is the half that helps Claude work out the place to look earlier than we even fetch the content material.
Click on right here to see the related code snippet
async def search_web(question: str) -> dict | None:
payload = json.dumps({"q": question, "num": 2})
headers = {
"X-API-KEY": os.getenv("SERPER_API_KEY"),
"Content material-Kind": "software/json",
}
async with httpx.AsyncClient() as shopper:
attempt:
response = await shopper.put up(
SERPER_URL, headers=headers, knowledge=payload, timeout=30.0
)
response.raise_for_status()
return response.json()
besides httpx.TimeoutException:
return {"natural": []}
besides httpx.HTTPStatusError as e:
print(f"HTTP error occurred: {e}")
return {"natural": []}
As soon as we’ve discovered a couple of promising hyperlinks, we’d like a method to extract simply the helpful content material from these net pages. That’s what fetch_url
does. It visits every URL, grabs the complete HTML of the web page, after which makes use of BeautifulSoup to filter out simply the readable elements—issues like paragraphs, headings, and examples. We attempt to prioritize sections like <foremost>
, <article>
, or containers with a .content material
class, which often maintain the great things. If the web page is tremendous lengthy, we additionally trim it all the way down to keep away from flooding the output. Consider this because the “reader mode” for Claude—it turns cluttered webpages into clear textual content it will probably perceive.
Click on right here to see the related code snippet
async def fetch_url(url: str) -> str:
async with httpx.AsyncClient(headers={"Person-Agent": USER_AGENT}) as shopper:
attempt:
response = await shopper.get(url, timeout=30.0)
response.raise_for_status()
soup = BeautifulSoup(response.textual content, "html.parser")
# Attempt to extract foremost content material and take away navigation, sidebars, and so on.
main_content = soup.discover("foremost") or soup.discover("article") or soup.discover("div", class_="content material")
if main_content:
textual content = main_content.get_text(separator="n", strip=True)
else:
textual content = soup.get_text(separator="n", strip=True)
# Restrict content material size if it is too giant
if len(textual content) > 8000:
textual content = textual content[:8000] + "... [content truncated]"
return textual content
besides httpx.TimeoutException:
return "Timeout error when fetching the URL"
besides httpx.HTTPStatusError as e:
return f"HTTP error occurred: {e}"
Now comes the principle act: the precise instrument operate that Claude will name.
The get_docs
operate is the place all the pieces comes collectively. Claude will go it a question and the identify of a library (like "llama-index"
), and this operate will:
- Verify if that library is supported
- Construct a site-specific search question (e.g.,
website:docs.llamaindex.ai "vector retailer"
) - Use
search_web()
to get the highest outcomes - Use
fetch_url()
to go to and extract the content material - Format all the pieces into a pleasant, readable string that Claude can perceive and return
We additionally embrace titles, URLs, and a few visible separators between every end result, so Claude can reference or cite them if wanted.
Click on right here to see the related code snippet
@mcp.instrument()
async def get_docs(question: str, library: str) -> str:
"""
Search the newest docs for a given question and library.
Helps langchain, openai, mcp and llama-index.
Args:
question: The question to seek for (e.g. "Chroma DB")
library: The library to go looking in (e.g. "langchain")
Returns:
Textual content from the docs
"""
if library not in docs_urls:
increase ValueError(f"Library {library} not supported by this instrument. Supported libraries: {', '.be a part of(docs_urls.keys())}")
question = f"website:{docs_urls[library]} {question}"
outcomes = await search_web(question)
if not outcomes or len(outcomes.get("natural", [])) == 0:
return "No outcomes discovered"
combined_text = ""
for i, end in enumerate(outcomes["organic"]):
url = end result["link"]
title = end result.get("title", "No title")
# Add separator between outcomes
if i > 0:
combined_text += "nn" + "="*50 + "nn"
combined_text += f"Supply: {title}nURL: {url}nn"
page_content = await fetch_url(url)
combined_text += page_content
return combined_text
Lastly, this line kicks all the pieces off. It tells the MCP server to begin listening for enter from Claude utilizing customary enter/output (which is how Claude Desktop talks to exterior instruments). This line at all times lives on the backside of your script.
if __name__ == "__main__":
mcp.run(transport="stdio")
Step 5: Check and Run Your Server
Alright, your server is coded and able to go — now let’s run it and see it in motion. There are two foremost methods to check your MCP server:
Improvement Mode (Beneficial for Constructing & Testing)
The simplest method to take a look at your server throughout growth is to make use of:
mcp dev foremost.py
This command launches the MCP Inspector, which opens up an area net interface in your browser. It’s like a management panel to your server.

Right here’s what you are able to do with it:
- Interactively take a look at your instruments (like
get_docs
) - View detailed logs and error messages in actual time
- Monitor efficiency and response occasions
- Set or override setting variables briefly
Use this mode whereas constructing and debugging. You’ll have the ability to see precisely what Claude would see and rapidly repair any points earlier than integrating with the complete Claude Desktop app.
Claude Desktop Integration (For Common Use)
As soon as your server works and also you’re proud of it, you’ll be able to set up it into Claude Desktop:
mcp set up foremost.py
This command will:
- Add your server into Claude’s configuration file (the JSON file we fiddled with earlier) robotically
- Allow it to run each time you launch Claude Desktop
- Make it out there by means of the Developer Instruments (🔨 hammer icon)
However maintain on — there’s one small catch…
⚠️ Present Situation: uv
Command Is Hardcoded
Proper now, there’s an open challenge within the mcp
library: when it writes your server into Claude’s config file, it hardcodes the command as simply "uv"
. That works solely if uv
is globally out there in your PATH — which isn’t at all times the case, particularly in case you put in it domestically with pipx or a customized methodology.
So we have to repair it manually. Right here’s how:
Manually Replace Claude’s Config File
- Open your Claude config file:
On MacOS:
code ~/Library/Software Help/Claude/claude_desktop_config.json
On Home windows:
code $env:AppDataClaudeclaude_desktop_config.json
💡 In case you’re not utilizing VS Code, change code
together with your textual content editor of selection (like open
, nano
, or subl
).
2. Discover the part that appears like this:
"docs": {
"command": "uv",
"args": [
"run",
"--with",
"mcp[cli]",
"mcp",
"run",
"/PATH/TO/mcp-server/foremost.py"
]
}
3. Replace the "command"
worth to absolutely the path of uv
in your system.
- To seek out it, run this in your terminal:
which uv
- It’ll return one thing like:
/Customers/your_username/.native/bin/uv
- Now change
"uv"
within the config with that full path:
"docs": {
"command": "/Customers/your_username/.native/bin/uv",
"args": [
"run",
"--with",
"mcp[cli]",
"mcp",
"run",
"PATH/TO/mcp-server/foremost.py"
]
}
4. Save the file and restart Claude Desktop.
✅ That’s It!
Now Claude Desktop will acknowledge your customized docs
instrument, and anytime you open the Developer Instruments (🔨), it’ll present up. You may chat with Claude and ask issues like:
“Are you able to test the newest MCP docs for the best way to construct a customized server?”
And Claude will name your server, search the docs, pull the content material, and use it in its response — dwell. You may view a fast demo here.

🎉 Congrats, You’ve Mastered MCP!
You probably did it. You’ve gone from zero to constructing, testing, and integrating your very personal Claude-compatible MCP server — and that’s no small feat.
Take a second. Stretch. Sip some espresso. Pat your self on the again. You didn’t simply write some Python — you constructed an actual, production-grade instrument that extends an LLM’s capabilities in a modular, safe, and highly effective method.
Critically, most devs don’t get this far. You now perceive:
- How MCP works underneath the hood
- How one can construct and expose instruments Claude can use
- How one can wire up real-time net search and content material extraction
- How one can debug, take a look at, and combine the entire thing with Claude Desktop
You didn’t simply study it — you shipped it.
Wish to go even deeper? There’s a complete world of agentic workflows, customized instruments, and collaborative LLMs ready to be constructed. However for now?
Take the win. You earned it. 🏆
Now go ask Claude one thing enjoyable and let your new instrument flex.
References
[1] Anthropic, Mannequin Context Protocol: Introduction (2024), modelcontextprotocol.io
[2] LangChain, MCP From Scratch (2024), Notion
[3] A. Alejandro, MCP Server Instance (2024), GitHub Repository
[4] O. Santos, Integrating Agentic RAG with MCP Servers: Technical Implementation Information (2024), Medium