MCP_Setup.ipynb•10.7 kB
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Standardizing LLM Interaction with MCP Servers\n",
"\n",
"Model Context Protocol, or MCP, is [an open protocol that standardizes how applications provide context to LLMs](https://modelcontextprotocol.io/introduction). In other words it provides a unified framework for LLM based applications to connect to data sources, get context, use tools, and execute standard prompts.\n",
"\n",
"<img src=\"./media/mcp_arch.png\" width=600>\n",
"\n",
"The MCP ecosystem outlines three specific components:\n",
"\n",
"- **MCP Servers** handle: tool availability (exposing what functions are available), tool execution (running those functions when requested), static content as resources (providing data that can be referenced), preset prompts (standardized templates for common tasks)\n",
"\n",
"- **Clients** manage: Connections to servers, LLM integration, message passing between components\n",
"\n",
"- **Hosts** provide: Frontend interfaces, surfacing of MCP functionality to users, integration points for the overall ecosystem\n",
"\n",
"This architecture creates a modular system where different components can be developed independently while maintaining interoperability. This let's users make MCP servers for different LLM related functionalities then plug and play across a variety of supported applications. Commonly used to integrate services APIs and tools, or connect to local datasources on your own machine.\n",
"\n",
"## MCP Server Components\n",
"\n",
"MCP servers form the foundation of the protocol by exposing standardized capabilities through well-defined interfaces. Hosts and clients can then connect to these servers using the protocol standard, but how these capabilities are presented to users remains flexible and open to developers. That means that the actual implementation and user experience is entirely up to the developer - whether through command line interfaces, graphical applications, or embedded within larger systems.\n",
"\n",
"In this guide, we'll focus on building an example MCP server with core capabilities, along with a simple client implementation to demonstrate the interaction patterns. To start, let's go over the main components of an MCP Server:\n",
"\n",
"<img src=\"./media/core_comp.png\" width=600>\n",
"\n",
"### Tools\n",
"\n",
"Tools are functions that the LLM can invoke to perform actions or retrieve information. Each tool is defined with:\n",
"\n",
"```python\n",
"{\n",
" name: string; // Unique identifier for the tool\n",
" description?: string; // Human-readable description\n",
" inputSchema: { // JSON Schema for the tool's parameters\n",
" type: \"object\",\n",
" properties: { ... } // Tool-specific parameters\n",
" }\n",
"}\n",
"```\n",
"\n",
"Tools allow LLMs to interact with external systems, execute code, query databases, or perform calculations. They represent actions that have effects or compute new information.\n",
"\n",
"### Resources\n",
"\n",
"Resources represent data sources that can be accessed by the client application. They are identified by URIs and can include:\n",
"\n",
"```python\n",
"{\n",
" uri: string; // Unique identifier for the resource\n",
" name: string; // Human-readable name\n",
" description?: string; // Optional description\n",
" mimeType?: string; // Optional MIME type\n",
"}\n",
"```\n",
"\n",
"Resources can be static (like configuration files) or dynamic (like database records or API responses). They provide context to the LLM without requiring function calls.\n",
"\n",
"### Prompts\n",
"\n",
"Prompts are reusable templates that define specific interaction patterns. They allow servers to expose standardized conversation flows:\n",
"\n",
"```python\n",
"{\n",
" name: string; // Unique identifier for the prompt\n",
" description?: string; // Human-readable description\n",
" arguments?: [ // Optional list of arguments\n",
" {\n",
" name: string; // Argument identifier\n",
" description?: string; // Argument description\n",
" required?: boolean; // Whether argument is required\n",
" }\n",
" ]\n",
"}\n",
"```\n",
"\n",
"Prompts help create consistent, purpose-built interactions for common tasks, allowing users to invoke them through UI elements like slash commands.\n",
"\n",
"*Note: While tools are designed specifically for LLM interaction (similar to function calling), prompts and resources serve different purposes in the MCP ecosystem. Prompts are typically user-controlled templates that can be invoked directly through UI elements like slash commands, and resources are application-controlled data sources that may be presented to users for selection before being included in the LLM context.*\n",
"\n",
"More details and additional functionality can be found in the [MCP Official Documentation](https://modelcontextprotocol.io/introduction)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"# Setting Up Our Example\n",
"\n",
"Our MCP Server will highlight tools, resources, and prompts. The core concept is to create a simple knowledgebase chatbot flow that will be have the functionality to:\n",
"1. Let the LLM use tools to query a vector database for RAG responses\n",
"2. Let the user choose existing resources to provide context\n",
"3. Let the user execute standard prompts for more complex analytical workflows\n",
"\n",
"<img src=\"./media/mcp_plan.png\" width=600>\n",
"\n",
"The above diagram is what's implemented in [mcp_server.py](./mcp_server.py) with a corresponding simple CLI client in [client.py](./client.py).\n",
"\n",
"As a useful resource, check out [MCP's Server List](https://github.com/modelcontextprotocol/servers) for official integrations and community-made servers."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"### ChromaDB Setup\n",
"\n",
"The below code outlines the setup for our persistent vector database using [ChromaDB](Https://trychroma.com), our candidate document is the lengthy paper [*The Ultimate Guide to Fine-Tuning LLMs from Basics to Breakthroughs: An Exhaustive Review of Technologies, Research, Best Practices, Applied Research Challenges and Opportunities*](https://arxiv.org/abs/2408.13296)"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import chromadb\n",
"\n",
"# Create a persistent client\n",
"client = chromadb.PersistentClient(path=\"./chroma_db\")"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# Convert PDF to Text\n",
"from langchain_community.document_loaders import PyPDFLoader\n",
"from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
"\n",
"def load_pdf(pdf_path):\n",
" \"\"\"\n",
" Load a PDF file and convert to text documents\n",
" \n",
" Args:\n",
" pdf_path (str): Path to PDF file\n",
" \n",
" Returns:\n",
" list: List of document pages\n",
" \"\"\"\n",
" loader = PyPDFLoader(pdf_path)\n",
" pages = loader.load()\n",
" return pages\n",
"\n",
"def create_chunks(documents, chunk_size=800, chunk_overlap=200):\n",
" \"\"\"\n",
" Split documents into overlapping chunks\n",
" \n",
" Args:\n",
" documents (list): List of documents to split\n",
" chunk_size (int): Size of each chunk in characters\n",
" chunk_overlap (int): Number of characters to overlap between chunks\n",
" \n",
" Returns:\n",
" list: List of text chunks\n",
" \"\"\"\n",
" text_splitter = RecursiveCharacterTextSplitter(\n",
" chunk_size=chunk_size,\n",
" chunk_overlap=chunk_overlap,\n",
" length_function=len,\n",
" is_separator_regex=False,\n",
" )\n",
" \n",
" chunks = text_splitter.split_documents(documents)\n",
" return chunks"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"# Load and Chunk the PDF\n",
"pdf_path = \"./testing/ft_guide.pdf\"\n",
"documents = load_pdf(pdf_path)\n",
"chunks = create_chunks(documents)\n",
"\n",
"# Create a collection with OpenAI embeddings\n",
"from chromadb.utils.embedding_functions import OpenAIEmbeddingFunction\n",
"\n",
"# API Key from Env\n",
"import os\n",
"from dotenv import load_dotenv\n",
"\n",
"load_dotenv()\n",
"OPENAI_API_KEY = os.getenv(\"OPENAI_API_KEY\")\n",
"\n",
"\n",
"embedding_function = OpenAIEmbeddingFunction(\n",
" api_key=OPENAI_API_KEY,\n",
" model_name=\"text-embedding-3-small\" # Latest OpenAI embedding model\n",
")\n",
"\n",
"collection = client.create_collection(\n",
" name=\"pdf_collection\",\n",
" embedding_function=embedding_function\n",
")\n",
"\n",
"# Add documents to collection\n",
"documents = [chunk.page_content for chunk in chunks]\n",
"metadatas = [chunk.metadata for chunk in chunks]\n",
"ids = [str(i) for i in range(len(chunks))]\n",
"\n",
"# Add to collection\n",
"collection.add(\n",
" documents=documents,\n",
" metadatas=metadatas,\n",
" ids=ids\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"555"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Get Statistics about the collection size\n",
"collection.count()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.2"
}
},
"nbformat": 4,
"nbformat_minor": 4
}