{ "cells": [ { "cell_type": "code", "execution_count": 6, "id": "b9ee0b72-791d-4281-9f12-933f7972a619", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Requirement already satisfied: openai in /usr/local/python/3.12.1/lib/python3.12/site-packages (1.88.0)\n", "Collecting fastmcp\n", " Downloading fastmcp-2.10.5-py3-none-any.whl.metadata (17 kB)\n", "Requirement already satisfied: anyio<5,>=3.5.0 in /home/codespace/.local/lib/python3.12/site-packages (from openai) (4.9.0)\n", "Requirement already satisfied: distro<2,>=1.7.0 in /usr/local/python/3.12.1/lib/python3.12/site-packages (from openai) (1.9.0)\n", "Requirement already satisfied: httpx<1,>=0.23.0 in /home/codespace/.local/lib/python3.12/site-packages (from openai) (0.28.1)\n", "Requirement already satisfied: jiter<1,>=0.4.0 in /usr/local/python/3.12.1/lib/python3.12/site-packages (from openai) (0.10.0)\n", "Requirement already satisfied: pydantic<3,>=1.9.0 in /usr/local/python/3.12.1/lib/python3.12/site-packages (from openai) (2.11.7)\n", "Requirement already satisfied: sniffio in /home/codespace/.local/lib/python3.12/site-packages (from openai) (1.3.1)\n", "Requirement already satisfied: tqdm>4 in /usr/local/python/3.12.1/lib/python3.12/site-packages (from openai) (4.67.1)\n", "Requirement already satisfied: typing-extensions<5,>=4.11 in /home/codespace/.local/lib/python3.12/site-packages (from openai) (4.13.2)\n", "Requirement already satisfied: idna>=2.8 in /home/codespace/.local/lib/python3.12/site-packages (from anyio<5,>=3.5.0->openai) (3.10)\n", "Requirement already satisfied: certifi in /home/codespace/.local/lib/python3.12/site-packages (from httpx<1,>=0.23.0->openai) (2025.4.26)\n", "Requirement already satisfied: httpcore==1.* in /home/codespace/.local/lib/python3.12/site-packages (from httpx<1,>=0.23.0->openai) (1.0.9)\n", "Requirement already satisfied: h11>=0.16 in /home/codespace/.local/lib/python3.12/site-packages (from httpcore==1.*->httpx<1,>=0.23.0->openai) (0.16.0)\n", "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/python/3.12.1/lib/python3.12/site-packages (from pydantic<3,>=1.9.0->openai) (0.7.0)\n", "Requirement already satisfied: pydantic-core==2.33.2 in /usr/local/python/3.12.1/lib/python3.12/site-packages (from pydantic<3,>=1.9.0->openai) (2.33.2)\n", "Requirement already satisfied: typing-inspection>=0.4.0 in /usr/local/python/3.12.1/lib/python3.12/site-packages (from pydantic<3,>=1.9.0->openai) (0.4.1)\n", "Collecting authlib>=1.5.2 (from fastmcp)\n", " Downloading authlib-1.6.0-py2.py3-none-any.whl.metadata (4.1 kB)\n", "Collecting cyclopts>=3.0.0 (from fastmcp)\n", " Downloading cyclopts-3.22.2-py3-none-any.whl.metadata (11 kB)\n", "Collecting exceptiongroup>=1.2.2 (from fastmcp)\n", " Downloading exceptiongroup-1.3.0-py3-none-any.whl.metadata (6.7 kB)\n", "Collecting mcp>=1.10.0 (from fastmcp)\n", " Downloading mcp-1.11.0-py3-none-any.whl.metadata (44 kB)\n", "Collecting openapi-pydantic>=0.5.1 (from fastmcp)\n", " Downloading openapi_pydantic-0.5.1-py3-none-any.whl.metadata (10 kB)\n", "Collecting pyperclip>=1.9.0 (from fastmcp)\n", " Downloading pyperclip-1.9.0.tar.gz (20 kB)\n", " Installing build dependencies ... \u001b[?25ldone\n", "\u001b[?25h Getting requirements to build wheel ... \u001b[?25ldone\n", "\u001b[?25h Preparing metadata (pyproject.toml) ... \u001b[?25ldone\n", "\u001b[?25hRequirement already satisfied: python-dotenv>=1.1.0 in /usr/local/python/3.12.1/lib/python3.12/site-packages (from fastmcp) (1.1.0)\n", "Requirement already satisfied: rich>=13.9.4 in /usr/local/python/3.12.1/lib/python3.12/site-packages (from fastmcp) (14.0.0)\n", "Collecting cryptography (from authlib>=1.5.2->fastmcp)\n", " Downloading cryptography-45.0.5-cp311-abi3-manylinux_2_34_x86_64.whl.metadata (5.7 kB)\n", "Requirement already satisfied: attrs>=23.1.0 in /home/codespace/.local/lib/python3.12/site-packages (from cyclopts>=3.0.0->fastmcp) (25.3.0)\n", "Collecting docstring-parser>=0.15 (from cyclopts>=3.0.0->fastmcp)\n", " Downloading docstring_parser-0.16-py3-none-any.whl.metadata (3.0 kB)\n", "Collecting rich-rst<2.0.0,>=1.3.1 (from cyclopts>=3.0.0->fastmcp)\n", " Downloading rich_rst-1.3.1-py3-none-any.whl.metadata (6.0 kB)\n", "Collecting docutils (from rich-rst<2.0.0,>=1.3.1->cyclopts>=3.0.0->fastmcp)\n", " Downloading docutils-0.21.2-py3-none-any.whl.metadata (2.8 kB)\n", "Collecting httpx-sse>=0.4 (from mcp>=1.10.0->fastmcp)\n", " Downloading httpx_sse-0.4.1-py3-none-any.whl.metadata (9.4 kB)\n", "Requirement already satisfied: jsonschema>=4.20.0 in /home/codespace/.local/lib/python3.12/site-packages (from mcp>=1.10.0->fastmcp) (4.24.0)\n", "Collecting pydantic-settings>=2.5.2 (from mcp>=1.10.0->fastmcp)\n", " Downloading pydantic_settings-2.10.1-py3-none-any.whl.metadata (3.4 kB)\n", "Requirement already satisfied: python-multipart>=0.0.9 in /usr/local/python/3.12.1/lib/python3.12/site-packages (from mcp>=1.10.0->fastmcp) (0.0.20)\n", "Collecting sse-starlette>=1.6.1 (from mcp>=1.10.0->fastmcp)\n", " Downloading sse_starlette-2.4.1-py3-none-any.whl.metadata (10 kB)\n", "Requirement already satisfied: starlette>=0.27 in /usr/local/python/3.12.1/lib/python3.12/site-packages (from mcp>=1.10.0->fastmcp) (0.46.2)\n", "Requirement already satisfied: uvicorn>=0.23.1 in /usr/local/python/3.12.1/lib/python3.12/site-packages (from mcp>=1.10.0->fastmcp) (0.23.1)\n", "Requirement already satisfied: jsonschema-specifications>=2023.03.6 in /home/codespace/.local/lib/python3.12/site-packages (from jsonschema>=4.20.0->mcp>=1.10.0->fastmcp) (2025.4.1)\n", "Requirement already satisfied: referencing>=0.28.4 in /home/codespace/.local/lib/python3.12/site-packages (from jsonschema>=4.20.0->mcp>=1.10.0->fastmcp) (0.36.2)\n", "Requirement already satisfied: rpds-py>=0.7.1 in /home/codespace/.local/lib/python3.12/site-packages (from jsonschema>=4.20.0->mcp>=1.10.0->fastmcp) (0.25.1)\n", "Collecting email-validator>=2.0.0 (from pydantic[email]>=2.11.7->fastmcp)\n", " Downloading email_validator-2.2.0-py3-none-any.whl.metadata (25 kB)\n", "Collecting dnspython>=2.0.0 (from email-validator>=2.0.0->pydantic[email]>=2.11.7->fastmcp)\n", " Downloading dnspython-2.7.0-py3-none-any.whl.metadata (5.8 kB)\n", "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/python/3.12.1/lib/python3.12/site-packages (from rich>=13.9.4->fastmcp) (3.0.0)\n", "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /home/codespace/.local/lib/python3.12/site-packages (from rich>=13.9.4->fastmcp) (2.19.1)\n", "Requirement already satisfied: mdurl~=0.1 in /usr/local/python/3.12.1/lib/python3.12/site-packages (from markdown-it-py>=2.2.0->rich>=13.9.4->fastmcp) (0.1.2)\n", "Requirement already satisfied: click>=7.0 in /usr/local/python/3.12.1/lib/python3.12/site-packages (from uvicorn>=0.23.1->mcp>=1.10.0->fastmcp) (8.2.1)\n", "Requirement already satisfied: cffi>=1.14 in /home/codespace/.local/lib/python3.12/site-packages (from cryptography->authlib>=1.5.2->fastmcp) (1.17.1)\n", "Requirement already satisfied: pycparser in /home/codespace/.local/lib/python3.12/site-packages (from cffi>=1.14->cryptography->authlib>=1.5.2->fastmcp) (2.22)\n", "Downloading fastmcp-2.10.5-py3-none-any.whl (201 kB)\n", "Downloading authlib-1.6.0-py2.py3-none-any.whl (239 kB)\n", "Downloading cyclopts-3.22.2-py3-none-any.whl (84 kB)\n", "Downloading rich_rst-1.3.1-py3-none-any.whl (11 kB)\n", "Downloading docstring_parser-0.16-py3-none-any.whl (36 kB)\n", "Downloading exceptiongroup-1.3.0-py3-none-any.whl (16 kB)\n", "Downloading mcp-1.11.0-py3-none-any.whl (155 kB)\n", "Downloading httpx_sse-0.4.1-py3-none-any.whl (8.1 kB)\n", "Downloading openapi_pydantic-0.5.1-py3-none-any.whl (96 kB)\n", "Downloading pydantic_settings-2.10.1-py3-none-any.whl (45 kB)\n", "Downloading email_validator-2.2.0-py3-none-any.whl (33 kB)\n", "Downloading dnspython-2.7.0-py3-none-any.whl (313 kB)\n", "Downloading sse_starlette-2.4.1-py3-none-any.whl (10 kB)\n", "Downloading cryptography-45.0.5-cp311-abi3-manylinux_2_34_x86_64.whl (4.5 MB)\n", "\u001b[2K \u001b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m4.5/4.5 MB\u001b[0m \u001b[31m61.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[?25hDownloading docutils-0.21.2-py3-none-any.whl (587 kB)\n", "\u001b[2K \u001b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m587.4/587.4 kB\u001b[0m \u001b[31m29.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[?25hBuilding wheels for collected packages: pyperclip\n", " Building wheel for pyperclip (pyproject.toml) ... \u001b[?25ldone\n", "\u001b[?25h Created wheel for pyperclip: filename=pyperclip-1.9.0-py3-none-any.whl size=11103 sha256=169534c63b1993ca775f6d89585f6c34c78e3aec849438646f2d88d242246814\n", " Stored in directory: /home/codespace/.cache/pip/wheels/e0/e8/fc/8ab8aa326e33bc066ccd5f3ca9646eab4299881af933f94f09\n", "Successfully built pyperclip\n", "Installing collected packages: pyperclip, httpx-sse, exceptiongroup, docutils, docstring-parser, dnspython, sse-starlette, email-validator, cryptography, rich-rst, pydantic-settings, openapi-pydantic, authlib, mcp, cyclopts, fastmcp\n", "\u001b[2K \u001b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m16/16\u001b[0m [fastmcp]5;237m━━\u001b[0m \u001b[32m15/16\u001b[0m [fastmcp]n]\n", "\u001b[1A\u001b[2KSuccessfully installed authlib-1.6.0 cryptography-45.0.5 cyclopts-3.22.2 dnspython-2.7.0 docstring-parser-0.16 docutils-0.21.2 email-validator-2.2.0 exceptiongroup-1.3.0 fastmcp-2.10.5 httpx-sse-0.4.1 mcp-1.11.0 openapi-pydantic-0.5.1 pydantic-settings-2.10.1 pyperclip-1.9.0 rich-rst-1.3.1 sse-starlette-2.4.1\n" ] } ], "source": [ "!pip install openai fastmcp" ] }, { "cell_type": "code", "execution_count": 7, "id": "402ff22c-4e3a-4895-acc5-9d2d79b01acc", "metadata": {}, "outputs": [], "source": [ "import os" ] }, { "cell_type": "code", "execution_count": 16, "id": "a25eb6fa-3716-43e5-9f4e-71c69e3d0bf0", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dict_keys(['city'])" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import random\n", "\n", "known_weather_data = {\n", " 'berlin': 20.0\n", "}\n", "\n", "def get_weather(city: str) -> float:\n", " \"\"\"Retrieves the temperature for a specified city.\"\"\"\n", " city = city.strip().lower()\n", "\n", " if city in known_weather_data:\n", " return known_weather_data[city]\n", "\n", " return round(random.uniform(-5, 35), 1)\n", "\n", "get_weather_tool = {\n", " \"type\": \"function\",\n", " \"name\": \"get_weather\", # TODO1: The function's name\n", " \"description\": \"Retrieves the temperature for a specified city.\", # TODO2: A description for the LLM\n", " \"parameters\": {\n", " \"type\": \"object\",\n", " \"properties\": {\n", " \"city\": { # TODO3: The function's parameter name\n", " \"type\": \"string\",\n", " \"description\": \"The name of the city for which to retrieve weather data.\" # TODO4: A description of the parameter\n", " }\n", " },\n", " \"required\": [\"city\"], # TODO5: A list of required parameters\n", " \"additionalProperties\": False\n", " }\n", "}\n", "\n", "todo3 = get_weather_tool['parameters']['properties'].keys()\n", "todo3" ] }, { "cell_type": "code", "execution_count": 17, "id": "4dd36d40-462d-4a62-88b6-4c4793802eef", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{\n", " \"type\": \"function\",\n", " \"name\": \"set_weather\",\n", " \"description\": \"Sets the temperature for a specified city.\",\n", " \"parameters\": {\n", " \"type\": \"object\",\n", " \"properties\": {\n", " \"city\": {\n", " \"type\": \"string\",\n", " \"description\": \"The name of the city for which to set the weather data.\"\n", " },\n", " \"temp\": {\n", " \"type\": \"number\",\n", " \"description\": \"The temperature to associate with the city.\"\n", " }\n", " },\n", " \"required\": [\n", " \"city\",\n", " \"temp\"\n", " ],\n", " \"additionalProperties\": false\n", " }\n", "}\n" ] } ], "source": [ "# Define the set_weather function\n", "def set_weather(city: str, temp: float) -> str:\n", " \"\"\"Sets the temperature for a specified city.\"\"\"\n", " city = city.strip().lower()\n", " known_weather_data[city] = temp\n", " return 'OK'\n", "\n", "set_weather_tool = {\n", " \"type\": \"function\",\n", " \"name\": \"set_weather\",\n", " \"description\": \"Sets the temperature for a specified city.\",\n", " \"parameters\": {\n", " \"type\": \"object\",\n", " \"properties\": {\n", " \"city\": {\n", " \"type\": \"string\",\n", " \"description\": \"The name of the city for which to set the weather data.\"\n", " },\n", " \"temp\": {\n", " \"type\": \"number\",\n", " \"description\": \"The temperature to associate with the city.\"\n", " }\n", " },\n", " \"required\": [\"city\", \"temp\"],\n", " \"additionalProperties\": False\n", " }\n", "}\n", "\n", "import json\n", "print(json.dumps(set_weather_tool, indent=4))" ] }, { "cell_type": "code", "execution_count": 10, "id": "a77847ed-96fe-4dec-babb-1343b178d1be", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Name: fastmcp\n", "Version: 2.10.5\n", "Summary: The fast, Pythonic way to build MCP servers and clients.\n", "Home-page: https://gofastmcp.com\n", "Author: Jeremiah Lowin\n", "Author-email: \n", "License-Expression: Apache-2.0\n", "Location: /usr/local/python/3.12.1/lib/python3.12/site-packages\n", "Requires: authlib, cyclopts, exceptiongroup, httpx, mcp, openapi-pydantic, pydantic, pyperclip, python-dotenv, rich\n", "Required-by: \n" ] } ], "source": [ "!pip show fastmcp" ] }, { "cell_type": "code", "execution_count": 11, "id": "01fe5562-4ae8-435f-a836-e7778fc92468", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing weather_server.py\n" ] } ], "source": [ "%%writefile weather_server.py\n", "\n", "from fastmcp import FastMCP\n", "import random\n", "\n", "known_weather_data = {'berlin': 20.0}\n", "\n", "mcp = FastMCP(\"Weather Service ☁️\")\n", "\n", "@mcp.tool\n", "def get_weather(city: str) -> float:\n", " \"\"\"\n", " Retrieves the temperature for a specified city.\n", " \"\"\"\n", " city = city.strip().lower()\n", " if city in known_weather_data:\n", " return known_weather_data[city]\n", " return round(random.uniform(-5, 35), 1)\n", "\n", "@mcp.tool\n", "def set_weather(city: str, temp: float) -> str:\n", " \"\"\"\n", " Sets the temperature for a specified city.\n", " \"\"\"\n", " city = city.strip().lower()\n", " known_weather_data[city] = temp\n", " return 'OK'\n", "\n", "if __name__ == \"__main__\":\n", " mcp.run()" ] }, { "cell_type": "code", "execution_count": 18, "id": "ca98649d-223a-407f-bd01-18aae6a539bb", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Started server with command: python weather_server.py\n", "Sending initialize request...\n", "Initialize response: {'protocolVersion': '2024-11-05', 'capabilities': {'experimental': {}, 'prompts': {'listChanged': False}, 'resources': {'subscribe': False, 'listChanged': False}, 'tools': {'listChanged': True}}, 'serverInfo': {'name': 'Weather Service ☁️', 'version': '1.11.0'}}\n", "Sending initialized notification...\n", "Handshake completed successfully\n", "Retrieving available tools...\n", "Available tools: ['get_weather', 'set_weather']\n", "Server stopped\n" ] } ], "source": [ "import mcp_client\n", "\n", "our_mcp_client = mcp_client.MCPClient([\"python\", \"weather_server.py\"])\n", "\n", "our_mcp_client.start_server()\n", "our_mcp_client.initialize()\n", "our_mcp_client.initialized()\n", "\n", "available_tools = our_mcp_client.get_tools()\n", "\n", "our_mcp_client.stop_server()\n", "\n", "# print(json.dumps(available_tools, indent=4))" ] } ], "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.1" } }, "nbformat": 4, "nbformat_minor": 5 }