
Main Goal: Become proficient in Anthropic's Model Context Protocol (MCP).
Learning Objectives: By the end, you will:
Understand core MCP principles/primitives.
Develop MCP servers.
Integrate MCP clients with servers (custom or pre-made).
Implement MCP clients.
Enable advanced agentic AI workflows using MCP.
Debug and monitor MCP connections.
Understand production-ready aspects (testing, logging, monitoring, security) in the MCP context.
Target Audience:
Primarily: Software Engineers, Data Scientists.
Also suitable for: Technical Product Managers, anyone comfortable with code (e.g., lawyers, doctors have taken it).
Required: GenAI experience (understanding LLMs, agents, RAG, ReAct) and general software development experience (coding, debugging).
Prerequisites (Not Covered In-Depth):
Python/TypeScript knowledge (writing functions, classes, running programs).
Basic Git usage (clone, commit).
Understanding Python virtual environments and environment variables.
Course Style: Technical, hands-on, project bootstrapped from zero, but assumes basic programming/tool knowledge. It's an MCP course, not a Python/Git basics course.
Guarantee: 30-day money-back guarantee offered (contact instructor if needed after 30 days).
Topic: The need for the Model Context Protocol (MCP).
Problem: AI agents often need to interact with various external tools and services (Slack, Gmail, Databases, etc.).
Each service has its own API.
Traditionally, developers need to write custom integration code (tools) for each service the agent needs to use.
Furthermore, this integration code often needs to be re-written or adapted for each different agent platform (like Cursor, Windsorf, GitHub Copilot, etc.) that wants to use those tools.
This leads to repetitive, time-consuming work ("writing a thousand integrations").
Solution (MCP): MCP acts as a standardized abstraction layer.
You integrate your tools/services (like Slack, Gmail access) once into an MCP-compliant server.
Any AI agent platform (MCP client) that understands the MCP protocol can then connect to this server and use the tools it exposes.
Benefits:
Write Once, Use Everywhere: Developers only need to build the integration for their tools/services once (the MCP server).
Interoperability: Any MCP-compliant agent can use any MCP-compliant server's tools without custom client-side integration code.
Reduced Development Effort: Avoids rewriting the same integrations for different agent platforms.
Analogy: Like social media, the value of MCP increases as more agents (clients) and tools (servers) adopt the standard protocol, creating a larger ecosystem.
Conclusion: MCP standardizes tool integration for AI agents, solving the problem of redundant custom integrations across different platforms and services.
Goal: Demonstrate the capabilities and user experience of the Model Context Protocol (MCP).
Analogy: MCP acts as an abstraction layer for AI agent tools, similar to how frameworks like LangChain provide tools, but MCP standardizes the connection between agents (clients) and tools (servers).
Problem: Without MCP, integrating tools (like Slack, Gmail, DB access) requires custom code for each tool and for each specific agent platform (Cursor, Windsorf, Claude, etc.). This is inefficient.
MCP Solution: Implement the tool logic once in an MCP server. Any MCP-compliant agent (client) can then connect and use those tools without needing custom integration code for that specific client.
Demo 1 (Claude):
Shows Claude initially cannot get weather (no built-in tool or MCP connection).
(Behind the scenes) Configure Claude to use a "weather" MCP server.
Ask Claude for SF weather. Claude's LLM deduces the required tool (get_forecast) and parameters (latitude, longitude) from the MCP server's description.
Claude makes an MCP call to the server, gets the weather data, and formats the answer.
Claude also intelligently decides to call another tool (get_alerts) from the same MCP server.
Demo 2 (Cursor):
Shows the same "weather" MCP server configured in Cursor settings (revealing its tools: get_alerts, get_forecast).
Ask Cursor's agent for SF weather.
Cursor successfully calls the same MCP server and get_forecast tool (prompting for permission is skipped due to "YOLO mode").
Cursor also calls the get_alerts tool.
Key Benefit Shown: The same MCP server provides weather functionality to both Claude and Cursor seamlessly, demonstrating interoperability and avoiding redundant integrations.
Conclusion: MCP allows developers to create tool logic once and make it available to any compatible AI agent, greatly simplifying integration and unlocking powerful, cross-platform capabilities.
The video demonstrates how to integrate a weather MCP server with Cursor to enable the Cursor Agent to fetch weather data using the configured MCP server.
The video includes instructions on:
Determining the path to the MCP server executable.
Opening a terminal window and using the “pwd” command to find the current directory.
Navigating to the correct directory and identifying the “index.js” file.
Adding a new MCP server in Cursor settings.
Naming the server (e.g., “weather”).
Specifying the server type as a command.
Entering the command to run the server (e.g., “node” followed by the path to “index.js”).
Verifying the server configuration and available tools (e.g., “get_alerts” and “get_forecast”).
The artifacts shown in the video include:
Cursor documentation.
A terminal window.
Cursor settings interface.
An MCP server configuration dialog.
A chat interface within Cursor.
MCP Recap: MCP (Model Context Protocol) standardizes how AI applications provide context (additional info, tools, prompts) to LLMs.
Analogy: MCP is like a USB-C port for AI applications, providing a standard way to connect AI models/apps to data sources and tools.
Benefits of MCP:
Provides a growing list of pre-built integrations/tools.
Offers flexibility to switch between LLM providers and vendors (less vendor lock-in).
Includes best practices for securing data within your infrastructure.
MCP Architecture: Follows a client-server model.
MCP Host: The main AI application (e.g., Claude Desktop, an IDE like Cursor or Windsorf, a custom AI tool/agent) that needs external context or tools.
MCP Server: The component exposing specific functionalities (tools, resources, prompts) to the host via the protocol. It acts as a gateway/proxy to underlying services (e.g., Google Drive, databases, web APIs like GitHub/Slack).
MCP Client: Resides within the MCP Host. It handles the communication with one specific MCP Server using the MCP Protocol. (Note: A host needs multiple clients to talk to multiple servers).
MCP Protocol: The standardized language/set of methods (e.g., list_tools, call_tool, list_prompts, get_prompt) used for communication between the client and server.
Core Components (Functionality): MCP servers expose:
Resources: External data sources (like Google Drive, databases).
Tools: Functions the AI can invoke (like calling web APIs, searching the internet, interacting with Slack).
Prompts: Pre-defined prompt templates or structures.
Key Advantage: Implement functionality (like accessing a tool or data) once in an MCP server, and any MCP-compatible host application can connect and use it via its MCP client. This promotes reusability and interoperability (plug-and-play).
Contrast with LangChain: Both MCP and LangChain address AI engineering problems, but in different ways (details in a future comparison). MCP focuses on the standardized connection, LangChain provides broader framework capabilities. Spoiler: They work together well :)
MCP Architecture and Transport Types
The MCP protocol facilitates communication between an MCP client and an MCP server.
The location of the MCP server (local machine or cloud) determines the transport type used.
The two primary transport types are STDIO (Standard Input/Output) and Streamable HTTP/SSE (Server-Sent Events).
Local MCP Server (STDIO Transport)
When an MCP server runs locally, it's on the same machine as the MCP client, often as a subprocess started by the client.
Communication between the local client and server happens through the STDIO channel.
Each MCP client gets its own subprocess instance of the MCP server.
This setup is popular and historically significant for MCP.
Security Concern: Running an MCP server locally poses a significant security risk, as it's equivalent to remote code execution.
Remote MCP Server (HTTP/SSE Transport)
When the MCP server is remote (e.g., in the cloud), it communicates with the client via HTTP.
The HTTP-based transport in MCP is called "Streamable HTTP." An older, though still used, version is SSE.
This architecture allows a single, centralized server to be shared by many clients, which is efficient for resource-intensive computations and ensures all clients use the latest version of the server code.
Connection is established by providing the client with the server's URL.
It's also possible to use HTTP transport for a locally running MCP server, not just a remote one.
Implementation
Switching between different transport types (STDIO, HTTP, SSE) is simple, typically requiring only a minor configuration change (e.g., changing a string parameter) when starting the MCP server.
The low-level details of how these transports work are not necessary for most users to understand for implementation.
Summary
Two Main Classes: When building MCP servers with the Python SDK, you can use the low-level Server class or the recommended high-level FastMCP class.
Server Class: This is the original, low-level implementation that requires extensive boilerplate code, making development repetitive and complex.
FastMCP Class: This is a high-level wrapper that simplifies development. It allows you to create servers and tools by writing standard Python functions and using decorators, automatically handling backend processes and schema inference.
FastMCP Versions:
Version 1.0: Integrated into the official MCP Python SDK (mcp.server.fastmcp).
Version 2.0: A separate, highly maintained, and popular open-source package (fastmcp) with more features for production use.
Recommendation:
Use FastMCP 2.0 in 99% of cases over the low-level Server class.
For new projects, it is highly recommended to start with the FastMCP 2.0 package.
Summary:
llms.txt is a simple, machine-readable manifest designed to help large language models (LLMs) and AI agents discover and navigate a site’s documentation by listing key URLs and short descriptions in plain text. By exposing a curated index of content, it streamlines AI-driven code generation, research, and tooling workflows without embedding the full documentation itself.
llms.txt is a proposed standard text file placed at the root of a website that provides a structured, AI-friendly index of important pages and resources .
Unlike robots.txt (which manages crawler permissions) or sitemap.xml (geared towards SEO), llms.txt focuses specifically on enhancing how LLMs read and understand web content at inference time .
Structure & Contents
The file is plain text (often Markdown-style) where each entry typically consists of a URL followed by a one-sentence blurb or title .
This minimal format avoids HTML, scripts, and markup, ensuring AI models can parse it without wasting context tokens on non-essential elements .
Purpose & Benefits
By offering a clean index, llms.txt helps LLMs avoid crawling through full HTML pages, which reduces context-window strain and speeds up retrieval of relevant documentation .
Developers can guide AI tools directly to authoritative references, improving accuracy in generated code snippets or research outputs .
Usage & Implementation
To implement, simply create a llms.txt file and host it at https://yourdomain.com/llms.txt, ensuring it’s publicly accessible .
Best practices include keeping it up to date, limiting the number of entries for clarity, and using a single H1 header if employing Markdown semantics .
Comparison with llms-full.txt
A sibling file, llms-full.txt, inlines all documentation content directly, eliminating the need for further fetches but often ballooning to hundreds of thousands of tokens .
While llms-full.txt can be useful in IDEs or RAG pipelines that support large contexts, llms.txt remains the lightweight default for tooling that prefers on-demand retrieval
In this section:
Build from Scratch: You will learn to create a custom Model Context Protocol (MCP) server entirely from the ground up, going beyond simply using pre-built examples.
Expose Tools & Resources: We will implement an MCP server capable of exposing both custom Tools (like executing shell commands) and Resources (making local file content accessible).
Integrate with Apps: You'll connect and utilize your custom-built MCP server within real-world applications such as Claude Desktop and Cursor.
Understand Security Risks: We will explore the potential security implications of MCP servers, especially those with powerful or permissive tools, demonstrating potential vulnerabilities and emphasizing safe practices.
Containerize with Docker: Discover how to package your MCP server into a Docker container, enhancing isolation, security, portability, and simplifying deployment processes.
Hands-On Code: All the code developed throughout this section will be made available in the course's GitHub repository for you to use and study.
In this video:
Project Setup with UV: You'll initialize a new Python project using the uv package manager, setting the stage for our custom MCP server.
Virtual Environment Creation: Create and activate an isolated virtual environment using uv to manage dependencies cleanly.
Install Dependencies: We will install the necessary mcp[cli] package using uv, which provides the core functionalities for building MCP servers in Python.
Leverage Cursor for Development: We'll set up the Cursor AI code editor for this project, preparing it to assist in writing the MCP server code.
Index Documentation: Index the official MCP documentation (both the main site and the Python SDK GitHub repository) directly within Cursor, providing crucial context for AI-assisted coding.
Configure Cursor Rules: We will create custom Cursor rules (.cursor/rules/python.mdc) to define a specific "persona" (e.g., a Python/FastAPI expert) for Cursor, ensuring the generated code is high-quality and follows best practices.
Prepare for AI Coding: With the boilerplate project, environment, dependencies, and Cursor setup complete, you'll be ready to "vibe code" the actual MCP server implementation using AI assistance in the following steps.
This video explores MCP (Model Context Protocol) security risks, focusing on Remote Code Execution (RCE) and Supply Chain Attacks.
Demonstration Steps:
Creates a publicly accessible file on GitHub Gist (hacked.txt) containing simple ASCII art ("YOU GOT HACKED").
Uses Cursor AI to generate a new MCP tool (benign_tool) for the server. This tool is designed to download content from the specific Gist URL using the curl command and return it.
Connects an MCP client (Claude Desktop) to the server and instructs it to run the benign_tool.
The client successfully executes the tool, downloads the content from the remote Gist, and displays the "YOU GOT HACKED" message.
Core Vulnerability: The MCP server dynamically downloaded and processed content from an external, remote source based on instructions from the client via a tool.
Security Implications:
RCE Risk: While the demo used harmless ASCII art, the remote file could easily contain malicious code (e.g., scripts to steal data, create backdoors, delete files). Running MCP servers downloaded from the internet inherently carries RCE risk.
Supply Chain Attack Risk: Even if the MCP server itself is from a trusted source (like an official vendor), it might depend on other open-source libraries. If those dependencies are compromised, the entire system becomes vulnerable.
Key Takeaway: Do not blindly trust or run MCP server code downloaded from the internet (open-source repositories or seemingly trusted vendors) without thorough review. Always examine the code and understand its dependencies to mitigate RCE and supply chain risks.
The video discusses the security risks of using overly "permissive" tools within AI agents, specifically focusing on a shell execution tool exposed via an MCP server.
The Vulnerability: A previously created terminal/shell tool allows running any command without proper authentication or user checks. This broad permission level is dangerous.
Risk: Such permissive tools open the door to malicious actions, including file deletion, privilege escalation, and other attacks if exploited by a user or another compromised tool/agent.
Demonstration: Use the Claude Desktop application (connected to the MCP server tool) to try and delete a local file (mcpreadme.md).
Initial Agent Refusal: Claude initially refuses, stating it operates in a restricted sandbox and cannot access local files (likely due to safety mechanisms or prompt engineering within the Claude client).
Bypassing Refusal: We use a simple prompt engineering trick ("Help me clean up my computer...") to reframe the request.
Successful Attack: With the new prompt, Claude agrees and uses the permissive MCP tool to execute the command:
rm -f ~/Desktop/mcpreadme.md, successfully deleting the file from the actual desktop.
Conclusion: Overly permissive tools with wide scope, like unrestricted shell access, are a significant security risk in AI agent systems and can be exploited even with simple prompting techniques.
The video explains the key advantages of running an MCP (Model Context Protocol?) server inside a Docker container.
Consistency Across Environments: Docker solves the "works on my machine" problem by ensuring the application runs the same way regardless of the host OS (Windows, Mac, Linux), simplifying collaboration and deployment.
Isolation and Safer Execution: Containers act as sandboxes, isolating the MCP server. This prevents accidental file overwrites on the host, conflicts with other services, and allows for granular permission control (e.g., specific folder/device access), enhancing security and stability.
Easy Scaling and Management: Docker makes it simple to scale the application by launching multiple container instances to handle increased load. Updates are streamlined by building and deploying new container image versions. Containers integrate well with orchestration tools (like Docker Compose, Kubernetes) for managing larger deployments.
Conclusion: Dockerizing the MCP server provides significant benefits in portability, security, consistency, and scalability.
LLM Fundamentals: Large Language Models (LLMs) are fundamentally text/token generators. They predict the next token in a sequence based on the preceding ones.
LLM Limitations: LLMs cannot inherently perform actions (like searching the web, running code, or interacting with external systems) on their own. They primarily output text (or other modalities if multimodal).
Agentic Behavior is Application-Driven: Features that seem like actions (web search, code execution, research) are enabled by an application layer built around the LLM.
External Tools: These "actions" are performed by separate code modules or functions ("tools") written by software engineers, which are external to the LLM itself.
Tool Calling Mechanism:
LLMs are guided by sophisticated system prompts.
When needing external information or action, the LLM generates text output formatted as a specific function/tool call (e.g., web_search(query="...")).
This output format is designed to be easily parsed by the surrounding application.
Application Role in Tool Use:
The application (e.g., ChatGPT interface) parses the LLM's text output.
If it detects a formatted tool call, the application executes the actual external tool/function with the specified arguments.
The result obtained from executing the tool is then fed back to the LLM.
The LLM uses this result (and potentially the original query) to generate the final response to the user.
Statistical Nature & Reliability: Since LLMs are statistical predictors, their ability to generate the correct tool call in the correct format is probabilistic and not 100% guaranteed, though often effective.
MCP Context: MCP relates to standardizing how these external tools are created and exposed, allowing developers to focus on tool logic and making them potentially reusable across different LLM applications (like ChatGPT, Claude, etc.).
Summary in Bullet Points:
Shared Concept: Both LangChain and MCP utilize the concept of "Tools," which are essentially external functions written by developers.
Tool Definition: When defining tools in either system, developers must specify:
Function arguments (inputs).
Function return values (outputs).
A description explaining when and why the tool should be called (crucial for the LLM).
Description's Role: The tool description is key, as it gets injected into the LLM's prompt, guiding the model on which tool to invoke.
LangChain Toolkits & MCP Servers: Both frameworks have ways to group tools:
LangChain uses "Toolkits."
MCP uses "MCPServers," which function similarly as collections of tools.
MCP Generalization: MCP expands beyond just "Tools" to also formally expose:
Resources: Data like documents, PDFs, images, database records, API responses.
Prompts: Pre-defined templates for the AI.
Key Difference - Binding/Exposure:
LangChain: Uses bind_tools to directly attach tools to the LLM (ChatModel).
MCP: Exposes Tools/Resources/Prompts to the AI Application (e.g., Cursor, Claude Desktop, LangGraph Agent) via the MCP Client, which then injects the necessary descriptions into the underlying LLM. MCP adds layers of abstraction (Client/Server communication).
LangChain MCP Adapter:
An open-source library released by the LangChain team.
Bridges the gap, allowing seamless integration of MCP-defined tools/servers into LangChain and LangGraph agents.
Provides tool compatibility: Converts MCP tools into a format LangChain agents can use.
Allows developers to leverage existing MCP servers without manual code changes.
Includes an MCP client implementation to connect to multiple MCP servers.
Goal: Explain the basic interaction flow of the MCP (Model Context Protocol).
Components:
User: Initiates queries.
App: The application layer (e.g., Cursor, Cloud Desktop, custom agent) that the user interacts with. Acts as the "Host" for the MCP Client.
LLM: The Large Language Model used for processing and responding.
MCPServer: A separate service that exposes Tools, Resources, and Prompts. Executes the actual tool logic.
MCP Client: Resides within the App. Handles communication with MCPServer(s). An app can have multiple clients connected to different servers.
Initialization Flow (App Startup):
The App (containing the MCP Client) establishes a connection with the relevant MCPServer(s).
The MCPServer responds, listing the available Tools, Resources, and Prompts it exposes. (Example: Weather server might expose 'alert' and 'forecast' tools).
This happens before any user interaction.
User Interaction Flow:
User sends a query to the App.
The App augments the user query with the list of available tools (obtained during initialization) and sends this combined information to the LLM.
The LLM, aware of the tools, responds either with a final answer or a structured "Tool Call" (specifying the tool name and arguments). MCP is designed for tool-calling LLMs.
The App receives the LLM's response. If it's a Tool Call, the App (via the MCP Client) sends this Tool Call to the MCPServer.
The MCPServer executes the requested tool with the provided arguments.
The MCPServer sends the result (tool response) back to the App (MCP Client).
The App sends the tool response (along with context like the original query) back to the LLM.
The LLM uses the tool's result to generate the final answer.
The App returns the final answer to the user.
Key Difference: MCP vs. LanCchain (Vanilla ReAct):
In vanilla LangChain ReAct, tool execution typically happens within the application/agent itself.
In MCP, tool execution is decoupled and happens on the MCPServer.
Advantages of MCP's Decoupling:
Separates tool execution logic (on MCPServer) from orchestration logic (in App/Agent).
Improves scalability, deployability (e.g., Kubernetes, serverless), monitoring, debugging, and potentially cost management of tool execution.
MCP provides a standardized interface for this interaction.
Enables dynamic tool calling: The App/Client can periodically re-initialize connection with the MCPServer to discover updated or new tools without needing to redeploy the entire agent.
Purpose: Setup the boilerplate project structure for implementing MCP LangChain adapters.
Initial Setup:
Clone the base course repository (mcp-crash-course) from GitHub.
Creates a new, clean, orphan Git branch named project/langchain-mcp-adapters using:
git checkout --orphan.
Remove all existing files from the new branch (git rm -rf .).
Project Environment (using UV):
Initializes a Python project structure using uv init (creates main.py, pyproject.toml, README.md).
Creates a virtual environment using: uv venv
Activate the virtual environment using source .venv/bin/activate (Cursor IDE may do this automatically on reopen).
Dependencies:
Installs core dependencies using uv add:
langchain-mcp-adapters (implicitly installs mcp)
langgraph
langchain-openai
Install python-dotenv using uv add for managing environment variables.
Environment Variables:
Create a .env file to store sensitive information like API keys (e.g., OPENAI_API_KEY).
Add .env to a .gitignore file to prevent committing secrets.
Uses dotenv.load_dotenv() in main.py to load variables into the environment.
Mentions LangSmith keys are for tracing (optional, set LANGCHAIN_TRACING_V2=false if not using).
Code Structure:
Modifies main.py to be an async function and uses asyncio.run(main()) to execute.
Prints the loaded API key as a sanity check.
Version Control:
Adds all new/modified files (git add .).
Commits the changes (uses Cursor AI to generate commit message).
Sets the upstream branch and pushes the new branch to the remote GitHub repository.
Command for cloning everything:
git clone -b project/langchain-mcp-adapters https://github.com/emarco177/mcp-crash-course.git
cd langchain-mcp-adapters
git checkout f3567e5babb9bc91e8406d41ee82f2331f5641fe
Result: A clean project structure on a dedicated branch, ready for implementing MCP clients and adapters, with necessary dependencies and environment setup handled.
This video demonstrates how to create two simple MCP (Model Context Protocol) servers using Python and the langchain-mcp-adapters library before building the client.
Server 1 (Math Server):
Created in servers/math_server.py.
Exposes two tools: add and multiply.
Uses stdio (standard input/output) for communication, suitable for local processes.
Code is adapted from the LangChain Adapters repository example.
Run using uv run servers/math_server.py.
Note: Avoid naming files like math.py to prevent conflicts with built-in Python modules.
Server 2 (Weather Server):
Created in servers/weather_server.py.
Exposes a simplified get_weather tool that returns a static string ("Hot as hell").
Uses SSE (Server-Sent Events) over HTTP for communication. This is the first demonstration of SSE transport in the series.
The MCP library handles the underlying HTTP POST (client-to-server) and SSE (server-to-client) communication.
Run using uv run servers/weather_server.py.
By default, runs as an HTTP server on localhost:8000.
Setup:
A servers Python package is created (directory containing __init__.py).
Development Workflow:
Code is copied and adapted from examples.
Servers are run and tested individually using uv run.
Cursor AI is used for debugging and generating Git commit messages.
Changes are committed and pushed to a Git repository.
The focus shifts to main.py to implement the MCP client.
Key imports for the MCP client are introduced:
ClientSession: Provides the core framework for a Python app to act as an MCP client, handling connections, message exchange, and server interactions via callbacks. Uses an initialize method.
StdioServerParameters: A Pydantic class defining how to run an MCP server using the stdio transport (specifies command and args).
stdio_client: The client implementation that spawns a server process and communicates via standard input/output.
Imports for LangChain/LangGraph integration:
ChatOpenAI: The language model to be used.
load_mcp_tools (from langchain_mcp_adapters.tools): A utility function to convert tools exposed by an MCP server into standard LangChain tools (maintaining name, description, arguments, etc., but adapting the format).
create_react_agent (from langgraph.prebuilt): A pre-built LangGraph agent implementation (ReAct style) that will act as the orchestrator, deciding when to call which tool using function calling.
The isort package is added using uv add isort and run (isort .) to automatically format and organize the import statements (standard library -> third-party -> local).
An llm instance of ChatOpenAI is created (using default settings).
An stdio_server_params object is created using StdioServerParameters to configure how the stdio math server (math_server.py) should be launched by the client.
command is set to "python".
args must contain the absolute path to the server script (servers/math_server.py), as the client runs it as a separate process without relative path context. The absolute path is obtained using pwd and manually added.
A quick test run (uv run main.py) initially fails due to an incorrect import path for load_mcp_tools.
The import error is fixed (changed from langchain_mcp_adapters to langchain_mcp_adapters.tools).
The code is successfully run after the fix.
Code changes (imports, LLM instantiation, server parameter setup) are committed to Git using Cursor's integrated features.
We will have a deeper understanding of the MCP objects will come in the next video through practical implementation.
Connecting to the Stdio Server:
An stdio_client context manager is used to establish communication with the math_server.py.
It requires stdio_server_params (defined in the previous video) which tells the client how to launch and communicate with the server process.
The context manager yields read and write streams for communication.
Client Session:
A ClientSession is created within an async with block, passing the read and write streams obtained from the stdio_client.
The ClientSession object handles the low-level MCP communication (initialization, message exchange, requests, notifications). The developer doesn't need to manage the protocol details directly.
Initializing the Connection:
await session.initialize() is called. This performs the initial handshake with the MCP server according to the protocol.
The MCP server responds with its available tools, resources, and prompts.
Listing Available Tools (MCP Format):
await session.list_tools() is called to retrieve the tools exposed by the connected MCP server.
The output shows a list of MCP tool objects (e.g., Tool(name='add', ...)), including name, description, input schema, required arguments, etc. This is the raw information from the server in MCP format.
Creating the LangGraph Agent:
The goal is to use these tools within a LangGraph agent.
The create_react_agent prebuilt function from LangGraph is used.
It requires an LLM (llm instance) and a list of LangChain tools.
The Mismatch Problem:
Passing the raw tools list (containing MCP tool objects) obtained from session.list_tools() directly to create_react_agent causes a ValueError or TypeError.
This is because the agent expects LangChain tool objects, not MCP tool objects, due to differences in format and structure, even though the underlying information is the same.
The Solution: load_mcp_tools:
The load_mcp_tools function (from langchain_mcp_adapters.tools) is introduced.
Its purpose is to translate the MCP tool objects (retrieved from the ClientSession) into compatible LangChain tool objects.
It takes the ClientSession object as input.
The line tools = await session.list_tools() is replaced with tools = await load_mcp_tools(session).
Corrected Agent Creation:
With tools now containing LangChain-compatible tools, agent = create_react_agent(llm, tools) works correctly.
Printing the converted tools shows they are now LangChain StructuredTool objects.
Testing the Agent:
The agent is tested by invoking it asynchronously: result = await agent.ainvoke({"messages": [HumanMessage(content="What is 2 + 2?")]}).
The HumanMessage class needs to be imported from LangChain core prompts.
Running the Agent (First Query: "What is 2 + 2?"):
The code in main.py is executed using uv run main.py.
Logs show: session initialized, ListToolsRequest processed, then CallToolRequest processed.
The CallToolRequest indicates the LangGraph ReAct agent (acting as the MCP host) used the MCP client to invoke the add tool on the remote MCP server.
The final printed output is "2 + 2 is equal to 4."
LangSmith Trace Analysis (2 + 2):
The LangSmith trace for this run is examined.
The initial LLM call shows the input query ("What is 2 + 2?") and the tools provided (add, multiply).
The LLM output indicates a call to the add tool with arguments a=2, b=2.
The trace shows the add tool execution step, confirming the input and showing the output 4.
The final LLM call receives the tool execution result and generates the final answer.
Key Point: The tool execution (add) happens remotely on the MCP server, decoupled from the LangGraph application. This decoupling isn't explicitly visible in the standard LangSmith trace (but might be improved).
Running the Agent (Second Query: "What is 54 + 2 * 3?"):
The HumanMessage content is changed to the more complex query.
The code is rerun (uv run main.py).
Logs show three CallToolRequest logs (initially surprising the speaker).
The final printed output is "54 + 2 * 3 = 60."
LangSmith Trace Analysis (54 + 2 * 3):
The trace for the second run is examined in LangSmith.
The initial LLM call correctly identifies the need for multiple tool calls.
The LLM output shows two concurrent tool calls requested: add(a=54, b=0) (the unexpected one) and multiply(a=2, b=3).
LangGraph executes these tool calls concurrently (verified by checking timestamps in the trace).
add(54, 0) returns 54.
multiply(2, 3) returns 6.
The results (54 and 6) are sent back to the LLM.
The LLM makes a final tool call: add(a=54, b=6).
This final add call executes, returning 60.
This result (60) is sent to the LLM, which then generates the final answer string.
Code Finalization:
The final code implementing the client logic and agent invocation is committed to Git using Cursor's auto-commit message generation.
The code is pushed to the GitHub repository.
The final state of main.py is shown on GitHub.
Summary The Model Context Protocol (MCP) servers has three fundamental primitives, though "Tools" are the most well-known.
1. Tools
Model-controlled functions that an LLM can invoke to perform actions.
Allow the LLM to interact with the outside world (e.g., API requests, CRUD operations, computations).
They are the most powerful and dominant primitive in the MCP ecosystem.
2. Prompts
User-controlled predefined templates that servers expose to clients.
Allow users to guide LLM interactions by selecting a template and filling in specific arguments (e.g., a "research_query" prompt where the user provides the "Topic" and "Goal").
This helps users get better, more consistent results for specific tasks without having to write the entire prompt from scratch.
3. Resources
Application-controlled structured data or content (e.g., file contents, user data, API responses).
The server makes this data available to the client, which can then use it to provide additional context to the LLM.
The client application decides if and how to use these resources.
Key Takeaways & Practical Reality
Tools are Foundational: Both Prompts and Resources can be implemented using the Tool primitive (e.g., a tool that fetches a list of prompts or a data resource).
Client Support is Essential: A server can define Prompts and Resources, but they are only usable if the client application you are using has the built-in functionality to display and handle them.
Current Focus: Many popular client applications currently focus primarily or exclusively on supporting the Tool primitive.
Prompts are reusable message templates that help guide LLM interactions.
They are one of the three core components that FastMCP servers can expose to clients (alongside tools and resources).
Key Characteristics:
Template-based: Prompts are parameterized templates that can take dynamic inputs to generate customized messages
Reusable: They provide consistent, standardized ways to interact with LLMs for specific tasks
Server-defined: The server defines the prompt templates, which clients can then request and use
Conversation starters: They act as pre-written instructions that show users exactly what to ask for
Common Use Cases for Prompts:
Dynamic Input Processing: Accept user parameters and incorporate them into prompts
Context Inclusion: Add local data or configuration to guide LLM responses
Workflow Guidance: Provide structured instructions for complex multi-step tasks
Conversation Templates: Standardize how users interact with specific tools or features
System Instructions: Define assistant behavior and response formatting
Common Use Cases for Prompts:
Dynamic Input Processing: Accept user parameters and incorporate them into prompts
Context Inclusion: Add local data or configuration to guide LLM responses
Workflow Guidance: Provide structured instructions for complex multi-step tasks
Conversation Templates: Standardize how users interact with specific tools or features
System Instructions: Define assistant behavior and response formatting
Introduction to MultiServerMCPClient: This class from langchain_mcp_adapters.client is used to manage connections to multiple MCP servers simultaneously.
Configuration:
It's used within an async with block: async with MultiServerMCPClient(...) as client:.
It takes a dictionary where keys are arbitrary names for the servers (e.g., "math", "weather") and values are dictionaries specifying connection details for each server.
Stdio Server ("math"): Configured using command: "python" and args: ["/absolute/path/to/servers/math_server.py"]. (Absolute path is crucial).
SSE Server ("weather"): Configured using url: "http://localhost:8000/sse" and transport: "sse". Note the absence of command/args and the use of url/transport.
Tool Discovery: The client.get_tools() method is used (instead of load_mcp_tools). This method connects to all configured servers, retrieves their tools, and returns a unified list of LangChain-compatible tools.
Agent Creation: A create_react_agent instance is created using the LLM and the aggregated tools obtained from client.get_tools().
Testing with Math Query:
The agent is invoked with await agent.ainvoke({"messages": ["What is 2 + 2?"]}).
The output "2 + 2 is equal to 4." is printed, confirming the interaction with the "math" stdio server.
Testing with Weather Query:
The agent is invoked with await agent.ainvoke({"messages": ["What is the weather in San Francisco?"]}).
The output "The weather in San Francisco is hot as hell." is printed, confirming interaction with the "weather" SSE server.
SSE Server Log Inspection:
The terminal running the weather_server.py (SSE server) is shown.
It displays incoming HTTP POST requests to the /sse endpoint for both ListToolsRequest (when client.get_tools runs) and CallToolRequest (when the agent invokes the get_weather tool).
A print() statement is added to the weather_server.py's get_weather function.
The weather server is restarted.
The agent is invoked again with the weather query.
The added print statement's output now appears in the weather server's logs, demonstrating that the tool code is indeed executed on the remote server process.
Benefits Highlighted: This demonstrates the decoupling – the agent orchestrates, but the tool logic runs remotely. This is beneficial for logging, monitoring, and deploying tools independently (e.g., to the cloud).
Code Management:
black is used to format the code.
A new Git branch named project/sse is created using git checkout --orphan.
All files are added, committed (using Cursor's auto-commit), and pushed to the new branch on GitHub.
The final code is shown available in the project/sse branch.
Video Summary:
Goal: This video introduces a project to build a secure, remote MCP (Model Context Protocol) server deployed in the cloud.
Functionality: The remote MCP server connects a local AI application (Claude Desktop) to a protected external API (a sample Todos API).
Core Technologies:
Auth0: Used for handling secure authentication and authorization (including access/refresh tokens).
Cloudflare Workers: Used as the serverless platform to host both the remote MCP server and the Todos API.
MCP-Remote: A lightweight local proxy used to bridge the gap between the local Claude Desktop client (which expects local connections) and the remote MCP server via HTTP requests.
Architecture: User interacts with Claude Desktop (local), which connects via mcp-remote (local proxy) to the Remote MCP Server (Cloudflare Worker). This server interacts with Auth0 for auth and then securely calls the Todos API (Cloudflare Worker).
Learning Approach:
The tutorial is based on an official Auth0 blog post.
It intentionally includes real-world debugging moments to provide practical learning insights.
Viewers are advised to watch the entire section first before attempting the walkthrough.
Benefits: Demonstrates a practical and powerful way to securely grant AI applications access to external tools and APIs using standard protocols and modern cloud infrastructure, without needing to manage underlying server infrastructure.
Cost: All tools used (Auth0, Cloudflare Workers) have free tiers suitable for following along.
Video Summary:
This video demonstrates the user experience after setting up the secure remote MCP server with Auth0 and Cloudflare.
Launching the Claude application immediately triggers a redirect to an Auth0 authorization screen.
The user is prompted to grant the "MCP CLI Proxy" permission to access the "Todo API" on their behalf.
The requested permissions (scopes like openid, email, read:todos, offline_access) are clearly listed.
This process follows the standard OAuth 2.0 authorization flow.
Upon clicking "Allow Access", Auth0 issues an access token (JWT) which the local MCP proxy receives and uses for subsequent requests.
Back in Claude, tools provided by the remote MCP server (hosted on Cloudflare) become available (e.g., whoami, list-todos).
Invoking the whoami tool demonstrates decoding the JWT access token to retrieve authenticated user details.
Invoking the list-todos tool uses the access token to make a secure, authenticated call to the protected Todos API, successfully retrieving the user's data.
A sequence diagram visually explains the interaction between the MCP Client, Remote MCP Server, Todos API, and Auth0.
Video Summary:
This video focuses on setting up and reviewing the Todos API, the backend service that the remote MCP server will interact with.
Plan: Review code -> Run locally -> Configure with Auth0 -> Test -> Deploy to Cloudflare Workers.
Initial Setup:
Clones the cloudflare/ai monorepo containing the sample code.
Navigates into the specific todos-api project directory (ai/demos/remote-mcp-auth0/todos-api).
Installs the necessary Node.js dependencies using npm install.
Code Review (Using Cursor editor, focusing on src/index.ts):
The API is built using the Hono web framework.
It imports Faker.js to generate placeholder data for API responses (like todo items).
Key security components are imported from ./middlewares/jwt:
jwt: Middleware function to validate incoming JWT access tokens.
requireScope: Middleware to check if the validated token has specific permissions (scopes).
Type definitions for JWT parts (JWTHeaderParameters, JWTPayload) are imported, likely from the jose library.
Application Structure & Middleware:
A Hono application instance is created.
An /api/health endpoint is defined for basic health checks.
The core jwt middleware is applied globally (app.use('*', jwt())) to intercept all incoming requests.
This middleware validates the JWT token found in the request.
If the token is valid, it decodes the payload (user claims) and header, storing them in the request context for downstream handlers.
If the token is invalid or missing, it rejects the request (likely with a 401 Unauthorized error).
Protected Endpoint Example (/api/me):
This endpoint retrieves user information.
Because the jwt middleware runs first, this handler can safely assume a valid token exists.
It accesses the previously stored JWTPayload (user claims) from the context.
It returns these claims as a JSON response.
Video Summary:
This clip examines the JWT middleware (middlewares/jwt.ts) used within the Todos API for security.
The middleware's primary role is handling authentication and authorization based on JWTs issued by Auth0.
Key Configuration: The middleware relies on two crucial environment variables:
AUTH0_DOMAIN: The Auth0 tenant domain. This tells the middleware where to fetch the public keys (JWKS) needed to verify the JWT signature and confirms the token's issuer.
AUTH0_AUDIENCE: The unique identifier for this specific API as registered in Auth0. This ensures the JWT was intended for this API, preventing misuse (like token confusion attacks).
Implementation: The core logic resides in the exported jwt function (for token validation/user authentication) and the requireScope function (for checking user permissions/authorization). The video doesn't detail the internal workings but highlights their purpose.
Local Execution & Debugging:
The speaker runs the API locally using npm run dev (which executes wrangler dev).
The server starts on http://localhost:8789.
Attempting to access the base URL immediately results in an "Internal Server Error".
Checking the console logs reveals the root cause: Error: JWT auth middleware requires options "auth0_domain".
Problem: The error occurs because the necessary AUTH0_DOMAIN and AUTH0_AUDIENCE environment variables haven't been set yet for the local development environment.
Next Step: The speaker refers back to the Auth0 blog post instructions to configure these Auth0 settings for the API.
Alternative
Video Summary:
This video details the configuration of the Todos API within the Auth0 dashboard and subsequent local testing.
Auth0 API Configuration:
Identifies the Auth0 tenant domain from the dashboard URL.
Navigates to Applications -> APIs in Auth0.
Creates a new API registration named "Todo API" with the identifier urn:todos-api. This identifier acts as the audience for JWT validation.
Accesses the API settings and enables "Allow Offline Access" under Access Settings. This permits applications to request refresh tokens for the API, useful for maintaining sessions when access tokens expire.
Navigates to the "Permissions" tab for the API.
Adds two custom permissions (scopes): read:todos (description: "Read ToDo List") and read:billing (description: "Read Billing Information"). These define specific actions applications can be authorized to perform.
Local Environment Setup:
Explains the need for AUTH0_DOMAIN and AUTH0_AUDIENCE environment variables for the local API server.
Creates a .dev.vars file in the project root to store these variables locally without committing them to version control.
Retrieves the Auth0 Domain from the "Test Application" settings (created automatically with the API) in the Auth0 dashboard under Applications -> Applications.
Pastes the Auth0 Domain and the previously defined Audience (urn:todos-api) into the .dev.vars file.
Local Testing & Verification:
Restarts the local API server (npm run dev).
Attempts to access the base URL (http://localhost:8789) in the browser again. This time, instead of an Internal Server Error, it correctly returns "Unauthorized", indicating the JWT middleware is now active but no token was provided.
Goes to the "Test" tab of the "Todo API" in the Auth0 dashboard.
Copies the provided curl command to obtain a Machine-to-Machine (M2M) access token using the Client Credentials flow (uses Client ID and Client Secret of the "Test Application").
Executes the curl command in the terminal to get a JWT access token.
Copies another curl example from the Auth0 "Test" tab, designed to send the obtained token to an API endpoint.
Modifies the target URL in the curl command to http://localhost:8789.
Pastes the obtained JWT access token into the Authorization: Bearer <token> header of the curl command.
First, attempts to access /api/todos endpoint with the token. This results in an "Unauthorized" response because the M2M token obtained doesn't inherently have the read:todos scope/permission required by the endpoint's requireScope middleware.
Then, attempts to access the /api/me endpoint with the same token. This request succeeds and returns the JWT claims (like iss, sub, aud) because this endpoint only requires a valid, authenticated token (verified by the base jwt middleware) and does not check for specific scopes
Video Summary:
Goal: Deploy the configured Todos API to Cloudflare Workers.
Secrets Configuration:
Explains that the AUTH0_DOMAIN and AUTH0_AUDIENCE values stored locally in .dev.vars need to be securely provided to the deployed Cloudflare Worker.
Uses the Cloudflare Wrangler CLI's secret put command for this.
Runs wrangler secret put AUTH0_DOMAIN (using npx to resolve the command-not-found issue from a previous error).
Pastes the domain value from .dev.vars when prompted. Wrangler automatically creates the todos-api worker service if it doesn't exist.
Runs npx wrangler secret put AUTH0_AUDIENCE and pastes the audience value.
Deployment:
Uses the command npx wrangler deploy to build and deploy the Todos API code to Cloudflare Workers.
Waits for the deployment to complete.
Verification:
Shows the Cloudflare dashboard (Workers & Pages).
Navigates to the newly deployed todos-api worker.
Confirms the AUTH0_DOMAIN and AUTH0_AUDIENCE secrets are listed under Settings -> Variables & Secrets (values are encrypted).
Observes that the deployed worker URL is now active (previously might show "Inactive" briefly after creation).
Testing Deployed API:
Accesses the root URL of the deployed worker in the browser, receives the expected "Unauthorized" response.
Uses curl with the previously obtained M2M JWT access token.
Targets the deployed worker's /api/me endpoint (e.g., https://todos-api.your-subdomain.workers.dev/api/me).
Successfully receives the JWT claims as a response, confirming the deployed API and its Auth0 integration are working.
Mentions that accessing /api/todos would still fail at this stage due to missing scopes in the M2M token.
Sanity Check: Points out that the steps are documented in the project's README file on GitHub.
Recap & Next Steps: Summarizes that the Todos API is now configured, tested locally, and deployed to Cloudflare. The next videos will focus on configuring and deploying the Remote MCP server.
Video Summary:
Focus Shift: Now configuring the Remote MCP Server, the component that acts as the bridge and authorization layer.
Remote MCP Server Roles:
Handles the OAuth 2.0 authentication flow with Auth0.
Acts as an OAuth server for the local MCP client (Claude Desktop via mcp-remote).
Exposes "tools" (like list-todos, read:billing, whoami) to the client, which correspond to actions on the backend Todos API.
Auth0 Configuration for Remote MCP Server:
Creates a new Regular Web Application in Auth0 named "Remote MCP Server".
Configures the Allowed Callback URL for this application. Initially set to http://localhost:8788/callback for local testing (port 8788 is planned for the local MCP server). This URL is where Auth0 redirects the user after successful authentication.
Notes that this callback URL will need updating with the Cloudflare deployment URL later.
Identifies the crucial credentials needed from this Auth0 application: Domain, Client ID, and Client Secret. These establish trust between the Remote MCP Server and Auth0.
Cloudflare KV Setup for OAuth State:
Explains the need for persistent storage for the OAuth flow because Cloudflare Workers are stateless.
Uses Cloudflare Workers KV (Key-Value storage) for this purpose.
Creates a KV namespace named OAUTH_KV (either via Wrangler CLI or the UI - UI shown).
The workers-oauth-provider library (used by the Remote MCP Server) will use this KV namespace to store temporary OAuth state (like authorization codes, tokens).
Local Configuration (wrangler.jsonc & .dev.vars):
Navigates to the mcp-auth0-oidc project directory.
Updates the wrangler.jsonc file to include a binding for the OAUTH_KV namespace, linking the code to the created KV store using its ID.
Creates a .dev.vars file for the Remote MCP Server project.
Populates this file with necessary environment variables identified from the blog/Auth0:
AUTH0_DOMAIN
AUTH0_CLIENT_ID (from the "Remote MCP Server" Auth0 app)
AUTH0_CLIENT_SECRET (from the "Remote MCP Server" Auth0 app)
AUTH0_AUDIENCE (Identifier of the Todos API, e.g., urn:todos-api)
AUTH0_SCOPE (Permissions the MCP server needs from the user for the Todos API, e.g., openid profile email offline_access read:todos)
NODE_ENV (Set to development)
API_BASE_URL (Points to the Todos API, initially http://localhost:8789 for local testing)
Next Steps: With the Auth0 application created and local files configured, the next step is to run the Remote MCP Server locally.
Video Summary:
Prerequisite: Ensure the Todos API server is running locally before starting the Remote MCP Server locally, as the latter depends on it.
Running Remote MCP Server Locally:
Uses npm run dev (invoking wrangler dev) to start the server.
The server runs on localhost:8788.
Testing with MCP Inspector:
Starts the @modelcontextprotocol/inspector tool using npx.
Connects the Inspector via SSE to http://localhost:8788/sse.
OAuth Flow Triggered:
Connecting the Inspector immediately redirects to the OAuth consent screen generated by the local Remote MCP server.
This screen asks the user (Eden) to authorize the "MCP Inspector" (the OAuth Client) to access the "Todo API" (the OAuth Resource) via the requested scopes (openid, email, profile, offline_access, read:todos).
OAuth Roles Explained:
User (Resource Owner): Eden, who owns the data in the Todos API.
OAuth Client: The MCP Inspector application requesting access.
OAuth Server (Authorization Server): The Remote MCP Server itself, acting as the intermediary.
OAuth Resource: The Todos API where the actual data resides.
Authorization Provider: Auth0, which handles the underlying identity verification.
OAuth Process Detailed:
User uses MCP Inspector (Client).
Inspector needs access to Todos API (Resource).
Inspector connects to Remote MCP Server (OAuth Server).
Remote MCP Server presents the consent screen to the User.
If User clicks "Allow Access":
The Remote MCP Server (OAuth Server) forwards the authorization request to Auth0 (Authorization Provider).
Auth0 verifies the User's identity (e.g., via login).
Auth0 redirects back to the Remote MCP Server's configured callback URL (http://localhost:8788/callback) with an authorization code.
The Remote MCP Server's /callback endpoint receives the code.
The Remote MCP Server then redirects again to the MCP Inspector's callback URL (http://127.0.0.1:6274/oauth/callback), forwarding the authorization code.
The MCP Inspector (Client) uses this code to request access tokens from the Remote MCP Server (acting as the token endpoint in this flow).
Key OAuth Benefits Highlighted:
Passwords are never shared with the client application (MCP Inspector).
User grants specific, limited permissions (scopes).
Access can be revoked later.
Callback URL Clarification: Addresses the potential confusion between the MCP Inspector's callback (:6274) and the Remote MCP Server's callback (:8788), explaining the multi-step redirect flow involving Auth0.
Next Steps: The next video will review the Remote MCP Server code, run it with debug logging to observe the flow, and provide a deeper understanding.
Video Summary:
Prerequisites: Ensures the local Todos API server (from previous steps) is running, as the local Remote MCP Server will call it.
Running Locally:
Starts the Remote MCP Server using sudo npm run dev (which runs wrangler dev).
Server listens on localhost:8788.
Testing Setup:
Uses the @modelcontextprotocol/inspector tool (npx command) as the MCP client for testing.
The Inspector tool itself runs on localhost:6274.
Initiating Connection & Auth Flow:
Configures the Inspector to connect to the local Remote MCP Server at http://localhost:8788/sse using the SSE transport type.
Clicking "Connect" in the Inspector triggers the process.
Observes logs in the Remote MCP Server terminal: an initial GET request to /sse receives a 401 Unauthorized error.
OAuth Discovery & Registration (Client Side - MCP Inspector):
The MCP Inspector client, upon receiving the 401, realizes authentication is needed.
It queries the Remote MCP Server's .well-known/oauth-authorization-server endpoint (a standard OAuth 2.0 discovery mechanism, RFC 8414) to find configuration details like the authorization and token endpoints.
It then makes a POST request to the Remote MCP Server's /register endpoint (OAuth 2.0 Dynamic Client Registration, RFC 7591) to register itself.
OAuth Authorization Request (Client Side - MCP Inspector):
After discovery and registration, the MCP Inspector makes a GET request to the Remote MCP Server's /authorize endpoint to start the user authorization flow.
OAuth Consent Screen (Server Side - Remote MCP Server):
The Remote MCP Server's /authorize handler receives the request.
It renders and displays the OAuth consent screen (shown in the previous demo), asking the user to grant the MCP Inspector permission to access the Todos API with specific scopes (openid, email, etc.).
OAuth Terminology Recap:
User (Resource Owner): The person owning the data (e.g., the To-Do list).
OAuth Client: The application requesting access (MCP Inspector).
OAuth Resource: The data/service being accessed (Todos API).
OAuth Server (Authorization Server): The system handling the authorization flow (Remote MCP Server).
Authorization Provider: The system verifying the user's identity (Auth0).
Full Flow Explanation (Triggered by User Consent): Explains the multi-step redirect sequence involving Auth0, the Remote MCP Server (/callback), and the MCP Inspector (/oauth/callback) to exchange the authorization code for tokens.
Code Preview: Briefly shows the src/index.ts file of the mcp-auth0-oidc project, pointing out the tool implementations (list-todos, whoami, list-billing) and the OAuth-related routes (/authorize, /authorize/consent, /callback, /token, /register) handled by the imported OAuthProvider.
Next Steps: The next video will delve into the code details, run with debug logging, and provide a deeper understanding of the implementation.
Video Summary:
Prerequisites: Both the Todos API (on port 8789) and the Remote MCP Server (on port 8788) must be running locally.
Testing Tool: The @modelcontextprotocol/inspector tool is used (npx command) to act as the MCP client. It runs on port 6274.
Connection & Authentication Trigger:
The Inspector connects to the local Remote MCP Server's SSE endpoint (http://localhost:8788/sse).
The initial connection receives a 401 Unauthorized error from the server logs.
This triggers the Inspector client to initiate the authentication process.
OAuth Discovery & Registration:
The Inspector client automatically queries the server's .well-known/oauth-authorization-server endpoint to get the server's OAuth configuration (using standard OAuth 2.0 metadata discovery - RFC 8414).
The client then registers itself with the server via a POST request to /register (OAuth 2.0 Dynamic Client Registration - RFC 7591). These standard endpoints (/.well-known, /register) are handled automatically by the OAuthProvider library used in the server.
Authorization Request & Consent:
The Inspector client makes a GET request to the server's /authorize endpoint.
The server's /authorize handler (implemented in the code) generates and displays the OAuth consent screen to the user in the browser, listing the client (MCP Inspector) and the requested scopes.
Code Preview (auth.ts):
Briefly shows the authorize function responsible for handling the /authorize route and initiating the flow.
Shows the renderConsentScreen helper function which takes client details and requested scopes to generate the HTML for the consent page.
OAuth Flow Explained (User Interaction):
When the user clicks "Allow Access" on the consent screen rendered by the Remote MCP Server:
The Remote MCP Server (acting as the OAuth server for the Inspector) forwards the request to Auth0 (the actual Authorization Provider) for user identity verification (e.g., login page shown).
Auth0, upon successful user authentication, redirects back to the Remote MCP Server's pre-configured /callback URL (http://localhost:8788/callback) with an Authorization Code.
The Remote MCP Server's /callback handler receives this code and then redirects again to the MCP Inspector's callback URL (http://127.0.0.1:6274/oauth/callback), passing the Authorization Code along.
The MCP Inspector (OAuth Client) receives the code.
Token Exchange (Client Side - MCP Inspector):
The MCP Inspector then makes a POST request to the Remote MCP Server's /token endpoint, exchanging the Authorization Code for an Access Token (and possibly a Refresh Token).
Final Test with Inspector:
After successful authentication (consent granted), the Inspector shows "Successfully authenticated with OAuth".
The available tools (whoami, list-todos, list-billing) are listed.
Running whoami works (shows JWT claims).
Running list-todos works (retrieves data from the Todos API using the obtained token).
Running list-billing fails with "Unauthorized" because the granted scopes did not include read:billing.
Summary
This optional video explains why accessing billing information failed previously: the required read:billing scope was missing.
It revisits the .dev.vars file, showing the AUTH0_SCOPE variable which defines the permissions the MCP server requests during authentication (e.g., openid, email, profile, offline_access, read:todos).
Scopes act like permission slips: the user grants them via the consent screen, and the API verifies them using the received token.
The /api/todos endpoint in the ToDo API code requires the read:todos scope, which was requested and granted, hence it worked.
The /api/billing endpoint requires the read:billing scope. Since this scope wasn't requested in AUTH0_SCOPE, the user never granted it, leading to the "Unauthorized" error when the API checked the token.
For demonstration purposes only, the video shows modifying the ToDo API code to temporarily require the read:todos scope (which the token does have) for the /api/billing endpoint.
After restarting the necessary services and re-authenticating, the list-billing tool now successfully returns data because the (temporarily altered) scope requirement matches the granted permissions.
The video concludes by reverting the API code change and assigning a homework task: add the read:billing scope to the AUTH0_SCOPE environment variable in the MCP server configuration (.dev.vars) and verify that the billing endpoint works correctly with the proper scope requested and granted.
Video Summary
Demonstrates deploying a remote MCP (Managed Component Platform) server as a Cloudflare Worker.
Explains the need to set environment variables (secrets) like Auth0 domain, client ID, client secret, and API base URL within the Cloudflare Worker environment.
Shows how to use the wrangler secret put <SECRET_NAME> command via npx (using sudo npx to overcome permissions/path issues) to add secrets interactively.
Illustrates copying secret values from a local .dev.vars file.
Handles the scenario where the worker doesn't exist yet, showing Wrangler's prompt to create it upon adding the first secret.
Verifies the successfully added secrets in the Cloudflare dashboard under the worker's settings.
Updates the API_BASE_URL secret to point to the deployed backend API's URL (retrieved from the Cloudflare dashboard for the todos-api worker) instead of localhost.
Uses the sudo npx wrangler deploy command to deploy the worker code to Cloudflare.
Troubleshoots a "Could not resolve" error during deployment by running sudo npm install to install missing dependencies.
Troubleshoots a permission error (EACCES) during deployment by using sudo with the npx wrangler deploy command.
Confirms the successful deployment and shows the resulting worker URL.
Shows the final configuration step: updating the "Allowed Callback URLs" in the associated Auth0 application settings to include the deployed worker's URL followed by /callback.
Summarizes the current state (deployed API and deployed MCP server) and sets the stage for the next video (testing the integration).
Summary
This section focuses on connecting the Claude Desktop application (an MCP host) to the remote MCP server previously deployed on Cloudflare.
A key challenge is that Claude Desktop currently only supports local connections via STDIO, while the remote server uses SSE (Server-Sent Events).
To bridge this gap, a local proxy is required. This proxy will communicate with Claude Desktop via STDIO and forward requests to the remote MCP server via SSE.
The video uses the mcp-remote NPM package (an official adapter from Cloudflare) as the proxy implementation.
The mcp-remote package needs to be installed globally (npm i -g mcp-remote).
Claude Desktop is configured by editing its claude_desktop_config.json file.
The configuration specifies an mcpServers entry (named todos here). The command is set to npx, and the args array contains two items:
The command to run the proxy (mcp-remote or its full path).
The full URL of the remote MCP server's SSE endpoint (e.g., https://mcp-auth0-oidc.<your-subdomain>.workers.dev/sse).
After saving the configuration and restarting Claude Desktop, it immediately triggers the Auth0 authentication flow via the proxy.
The Auth0 consent screen shows the "MCP CLI Proxy" is requesting permission to access the ToDo API using the defined scopes.
Once access is granted, Claude Desktop successfully connects and displays the available tools (list-billing, list-todos, whoami) exposed by the remote MCP server.
Testing within Claude Desktop confirms functionality:
whoami works and retrieves user details.
Asking "what are my todos?" correctly invokes the list-todos tool.
Asking "what are my billing details?" correctly attempts to use the list-billing tool but results in an "Unauthorized" response (as expected, since the read:billing scope wasn't granted earlier).
The setup successfully connects the local Claude Desktop to the remote, authenticated MCP server using the mcp-remote proxy.
Future support for direct remote connections (streamable HTTP) in Claude Desktop is mentioned.
This course contains the use of artificial intelligence :)
Please note that this is not a course for beginners. This course assumes that you have a background in software engineering and are proficient in Python. I will be using Cursor IDE but you can use any editor you'd like since we only use basic feature of the IDE like debugging and running scripts .
Ideal students are software developers / data scientists
What is the Model Context Protocol?
The Model Context Protocol (MCP) is an open-source standard, introduced by Anthropic in 2024, that allows AI models to seamlessly connect with external data sources, tools, and software systems
Architecture Components
MCP Hosts: Programs like Claude Desktop, Cursor, Windsurf, or AI tools that want to access data through MCP
MCP Clients: Protocol clients that maintain 1:1 connections with servers (Content ETA April)
MCP Servers: Lightweight programs that each expose specific capabilities through the standardized Model Context Protocol
Local Data Sources: Your computer's files, databases, and services that MCP servers can securely access (Content ETA End of March)
Remote Services: External systems available over the internet (e.g., through APIs) that MCP servers can connect to
(Content ETA End of March)
Authenticaiton
Key Capabilities
Resources: Components that expose data and content from your servers to LLMs
Prompts: Functionality to create reusable prompt templates and workflows
Tools: Features that enable LLMs to perform actions through your server
Sampling: Capability that lets your servers request completions from LLMs
Transports: MCP's communication mechanism between clients and servers
Topic Covered:
MCP + Agent Security best practices
Containerizing MCP Servers
Protocol Flow
MCP + Docker
MCP + LangChain
OAuth 2.0 with MCP featuring Auth0
MCP Deployment (featuring Cloudflare)
A2A - Agent 2 Agent Protocol (WIP)