Skip to main content
Glama

Fused MCP Agents

Official
by fusedio
{ "cells": [ { "cell_type": "markdown", "id": "2cb2d191", "metadata": {}, "source": [ "### Exploring Tourism information worldwide\n", "\n", "This is a simple notebook to set up a Agent that fetches information for countries around the world across years" ] }, { "cell_type": "code", "execution_count": 6, "id": "22a99aea-7b52-4a62-b084-f5e0bdddfd06", "metadata": {}, "outputs": [], "source": [ "import fused\n", "import json\n", "import os\n", "import time\n", "from pathlib import Path\n", "\n", "# Handling the OS file paths\n", "from enum import Enum\n", "LOCAL_OS = Enum(\"LOCAL_OS\", [\"mac\", \"windows\"])" ] }, { "cell_type": "code", "execution_count": 7, "id": "784b0fb2", "metadata": {}, "outputs": [], "source": [ "# Set your LOCAL_OS to the correct one\n", "LOCAL_OS = LOCAL_OS.mac\n", "\n", "if LOCAL_OS == LOCAL_OS.mac:\n", " PATH_TO_CLAUDE_CONFIG = (f\"{str(Path.home())}/Library/Application Support/Claude/claude_desktop_config.json\")\n", " CLAUDE_APP_PATH = \"/Applications/Claude.app\"\n", "elif LOCAL_OS == LOCAL_OS.windows:\n", " PATH_TO_CLAUDE_CONFIG = (f\"{str(Path.home())}\\AppData\\Roaming\\Claude/claude_desktop_config.json\")\n", " CLAUDE_APP_PATH = r\"C:\\Users\\user\\AppData\\Local\\AnthropicClaude\\claude.exe\"\n", " \n", "else:\n", " raise ValueError(\"Unsupported OS\")\n" ] }, { "cell_type": "code", "execution_count": 8, "id": "7190247e-8f0b-47de-8953-8b8c77717d81", "metadata": {}, "outputs": [], "source": [ "# We still need your local paths\n", "\n", "if not os.path.exists(PATH_TO_CLAUDE_CONFIG):\n", " # Creating the config file\n", " os.makedirs(os.path.dirname(PATH_TO_CLAUDE_CONFIG), exist_ok=True)\n", " with open(PATH_TO_CLAUDE_CONFIG, \"w\") as f:\n", " json.dump({}, f)\n", "\n", "assert os.path.exists(PATH_TO_CLAUDE_CONFIG), (\n", " \"Please update the PATH_TO_CLAUDE_CONFIG variable with the correct path to your Claude config file\"\n", ")\n", "\n", "assert os.path.exists(CLAUDE_APP_PATH), (\"Please update the CLAUDE_APP_PATH variable with the correct path to your Claude app\")" ] }, { "cell_type": "code", "execution_count": 9, "id": "4675bb8e-5e7b-4cfc-9209-810bf52831a7", "metadata": {}, "outputs": [], "source": [ "# Local path to the Claude app\n", "assert os.path.exists(CLAUDE_APP_PATH), (\n", " \"Please update the CLAUDE_APP_PATH variable with the correct path to your Claude app\"\n", ")" ] }, { "cell_type": "code", "execution_count": 13, "id": "7807a52f-f669-47c4-ab6c-1f542562421a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/Users/maximelenormand/Library/CloudStorage/Dropbox/Mac/Documents/repos/fused-mcp\n" ] } ], "source": [ "# Change this path if you're not running this from the repo root\n", "WORKING_DIR = Path.cwd().parent\n", "print(WORKING_DIR)" ] }, { "cell_type": "code", "execution_count": 14, "id": "092b706b-ac1e-4215-9884-f0435e2235ff", "metadata": {}, "outputs": [], "source": [ "# We'll load the commons folder once again to have our helper functions\n", "commit = \"5dda36c\"\n", "common = fused.load(\n", " f\"https://github.com/fusedio/udfs/tree/{commit}/public/common\"\n", ").utils" ] }, { "cell_type": "code", "execution_count": 15, "id": "5b43b873-cb47-4fdf-b73d-aceda9a69b87", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'agents': [{'name': 'get_current_time', 'udfs': ['current_utc_time']},\n", " {'name': 'fused_docs', 'udfs': ['list_public_udfs', 'reading_fused_docs']},\n", " {'name': 'vancouver_open_data',\n", " 'udfs': ['hundred_parks_in_vancouver', 'internet_speeds_for_lat_lon']},\n", " {'name': 'elevation_stats_for_lat_lon_area', 'udfs': ['elevation_stats']},\n", " {'name': 'dynamic_output_vector', 'udfs': ['dynamic_output_vector_udf']},\n", " {'name': 'vancouver_open_data_demo',\n", " 'udfs': ['hundred_parks_in_vancouver',\n", " 'electric_vehicle_chargers_in_vancouver',\n", " 'yearly_crime_amount',\n", " 'internet_speeds_for_lat_lon',\n", " 'community_gardens_vancouver']},\n", " {'name': 'world_countries_info', 'udfs': ['get_country_data']},\n", " {'name': 'routing_agent', 'udfs': ['single_routing_agent']},\n", " {'name': 'tourism_info', 'udfs': ['visitors_arrival', 'visitors_purpose']},\n", " {'name': 'stock_information', 'udfs': ['get_stock_details']}]}" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# And see which agents we have available\n", "json.load(open(os.path.join(WORKING_DIR, \"agents.json\")))" ] }, { "cell_type": "code", "execution_count": 16, "id": "2a8b21b6-8a80-4637-bcae-64fe0ecd3afb", "metadata": {}, "outputs": [], "source": [ "AGENT_NAME = \"tourism_info\"" ] }, { "cell_type": "code", "execution_count": 17, "id": "00c45942-9794-48ac-9100-695d9cc5744c", "metadata": {}, "outputs": [], "source": [ "@fused.udf\n", "def get_visitor_data(year: int = None, country: str = None):\n", " \"\"\"\n", " UDF to retrieve visitor arrival data with optional filters.\n", " We've simply hosted a CSV file on Google Drive for the sake of simplicity for this example.\n", "\n", " Parameters:\n", " - year (int): Filter by specific year (e.g., 2023)\n", " - country (str): Filter by specific country name (e.g., 'Thailand')\n", "\n", " Returns:\n", " A DataFrame with the following columns:\n", " - Country\n", " - Purpose_of_visit_to_Japan\n", " - Year\n", " - Growth Rate(%)\n", " - Visitor Arrivals\n", " \"\"\"\n", " import pandas as pd\n", " import fused\n", " from io import BytesIO\n", " import requests\n", "\n", " drive_url = \"https://drive.google.com/uc?export=download&id=1OQkx2fLA6oQ3fzNL-aiCFqDr65bp-nAL\"\n", "\n", " @fused.cache\n", " def load_csv():\n", " response = requests.get(drive_url)\n", " response.raise_for_status()\n", " return pd.read_csv(BytesIO(response.content), encoding='utf-8')\n", "\n", " df = load_csv()\n", " df.rename(columns={\"Country/Area\": \"Country\"}, inplace=True)\n", "\n", " # Cleaning up\n", " df['Year'] = pd.to_numeric(df['Year'], errors='coerce').astype('Int64')\n", " if year is not None:\n", " df = df[df['Year'] == year]\n", " if country:\n", " df = df[df['Country'] == country]\n", "\n", " # Columns to return\n", " columns = [\n", " 'Country',\n", " 'Purpose_of_visit_to_Japan',\n", " 'Year',\n", " 'Growth Rate(%)',\n", " 'Visitor Arrivals'\n", " ]\n", "\n", " return df[columns] if not df.empty else pd.DataFrame(columns=columns)\n" ] }, { "cell_type": "code", "execution_count": 18, "id": "fdb56b9b-683d-4813-8056-f30349b304df", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "UDF result was cached." ] }, { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>Country</th>\n", " <th>Purpose_of_visit_to_Japan</th>\n", " <th>Year</th>\n", " <th>Growth Rate(%)</th>\n", " <th>Visitor Arrivals</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>China</td>\n", " <td>Tourism</td>\n", " <td>1990</td>\n", " <td>None</td>\n", " <td>None</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>China</td>\n", " <td>Tourism</td>\n", " <td>1991</td>\n", " <td>None</td>\n", " <td>None</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>China</td>\n", " <td>Tourism</td>\n", " <td>1992</td>\n", " <td>None</td>\n", " <td>29,147</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>China</td>\n", " <td>Tourism</td>\n", " <td>1993</td>\n", " <td>-9.239372834</td>\n", " <td>26,454</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>China</td>\n", " <td>Tourism</td>\n", " <td>1994</td>\n", " <td>-7.197399259</td>\n", " <td>24,550</td>\n", " </tr>\n", " <tr>\n", " <th>...</th>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " <td>...</td>\n", " </tr>\n", " <tr>\n", " <th>1045</th>\n", " <td>United States</td>\n", " <td>Business</td>\n", " <td>2020</td>\n", " <td>-86.664479278</td>\n", " <td>28,857</td>\n", " </tr>\n", " <tr>\n", " <th>1046</th>\n", " <td>United States</td>\n", " <td>Business</td>\n", " <td>2021</td>\n", " <td>-95.515819385</td>\n", " <td>1,294</td>\n", " </tr>\n", " <tr>\n", " <th>1047</th>\n", " <td>United States</td>\n", " <td>Business</td>\n", " <td>2022</td>\n", " <td>4,408.191653787</td>\n", " <td>58,336</td>\n", " </tr>\n", " <tr>\n", " <th>1048</th>\n", " <td>United States</td>\n", " <td>Business</td>\n", " <td>2023</td>\n", " <td>104.588933077</td>\n", " <td>119,349</td>\n", " </tr>\n", " <tr>\n", " <th>1049</th>\n", " <td>United States</td>\n", " <td>Business</td>\n", " <td>2024</td>\n", " <td>3.499819856</td>\n", " <td>123,526</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "<p>1050 rows × 5 columns</p>\n", "</div>" ], "text/plain": [ " Country Purpose_of_visit_to_Japan Year Growth Rate(%) \\\n", "0 China Tourism 1990 None \n", "1 China Tourism 1991 None \n", "2 China Tourism 1992 None \n", "3 China Tourism 1993 -9.239372834 \n", "4 China Tourism 1994 -7.197399259 \n", "... ... ... ... ... \n", "1045 United States Business 2020 -86.664479278 \n", "1046 United States Business 2021 -95.515819385 \n", "1047 United States Business 2022 4,408.191653787 \n", "1048 United States Business 2023 104.588933077 \n", "1049 United States Business 2024 3.499819856 \n", "\n", " Visitor Arrivals \n", "0 None \n", "1 None \n", "2 29,147 \n", "3 26,454 \n", "4 24,550 \n", "... ... \n", "1045 28,857 \n", "1046 1,294 \n", "1047 58,336 \n", "1048 119,349 \n", "1049 123,526 \n", "\n", "[1050 rows x 5 columns]" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fused.run(get_visitor_data)" ] }, { "cell_type": "code", "execution_count": 19, "id": "a4eeb93c-2b8a-4f4c-8c25-e74245e41d2a", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'description': \"\\nName: get_visitor_data\\nPurpose and Functionality:\\nThe UDF 'get_visitor_data' provides detailed visitor arrival statistics from a structured dataset sourced from a CSV file hosted on Google Drive. It allows optional filtering by year and country, and returns comprehensive information including purpose of visit, growth rate, and number of visitor arrivals.\\n\\nInput Parameters:\\n- year (int, optional): The year for which detailed visitor data is requested. If not specified, data for all available years will be returned.\\n- country (str, optional): The country for which visitor data is requested. If not specified, data for all countries will be returned.\\n\\nOutput:\\nReturns a Pandas DataFrame with the following columns:\\n- Country/Area\\n- Purpose_of_visit_to_Japan\\n- Year\\n- Growth Rate(%)\\n- Visitor Arrivals\\n\\nIf no matching records are found, an empty DataFrame with the above column structure is returned.\\n\\nTechnical Details and Limitations:\\n- The CSV is fetched from a public Google Drive link using HTTP requests. Internet access is required.\\n- The 'Year' column is cast to integer for filtering.\\n- The function gracefully handles missing or empty fields, such as blank growth rates or visitor counts.\\n- If the source format of the CSV changes, the behavior may be affected.\\n \",\n", " 'parameters': [{'name': 'year',\n", " 'type': 'integer',\n", " 'default': None,\n", " 'description': 'The year to filter visitor data. Optional.'},\n", " {'name': 'country',\n", " 'type': 'string',\n", " 'default': None,\n", " 'description': 'The country to filter visitor data. Optional.'}]}" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "get_visitor_data_metadata = {\n", " \"description\": \"\"\"\n", "Name: get_visitor_data\n", "Purpose and Functionality:\n", "The UDF 'get_visitor_data' provides detailed visitor arrival statistics from a structured dataset sourced from a CSV file hosted on Google Drive. It allows optional filtering by year and country, and returns comprehensive information including purpose of visit, growth rate, and number of visitor arrivals.\n", "\n", "Input Parameters:\n", "- year (int, optional): The year for which detailed visitor data is requested. If not specified, data for all available years will be returned.\n", "- country (str, optional): The country for which visitor data is requested. If not specified, data for all countries will be returned.\n", "\n", "Output:\n", "Returns a Pandas DataFrame with the following columns:\n", "- Country/Area\n", "- Purpose_of_visit_to_Japan\n", "- Year\n", "- Growth Rate(%)\n", "- Visitor Arrivals\n", "\n", "If no matching records are found, an empty DataFrame with the above column structure is returned.\n", "\n", "Technical Details and Limitations:\n", "- The CSV is fetched from a public Google Drive link using HTTP requests. Internet access is required.\n", "- The 'Year' column is cast to integer for filtering.\n", "- The function gracefully handles missing or empty fields, such as blank growth rates or visitor counts.\n", "- If the source format of the CSV changes, the behavior may be affected.\n", " \"\"\",\n", " \"parameters\": [\n", " {\n", " \"name\": \"year\",\n", " \"type\": \"integer\",\n", " \"default\": None,\n", " \"description\": \"The year to filter visitor data. Optional.\"\n", " },\n", " {\n", " \"name\": \"country\",\n", " \"type\": \"string\",\n", " \"default\": None,\n", " \"description\": \"The country to filter visitor data. Optional.\"\n", " }\n", " ]\n", "}\n", "get_visitor_data_metadata" ] }, { "cell_type": "code", "execution_count": 24, "id": "f2eaa769-5bf3-43ad-93de-e1b0bf101004", "metadata": {}, "outputs": [], "source": [ "common.save_to_agent(\n", " agent_json_path=os.path.join(WORKING_DIR, \"agents.json\"),\n", " udf=get_visitor_data,\n", " agent_name=AGENT_NAME,\n", " udf_name=\"retrieve_visitor_data\",\n", " mcp_metadata=get_visitor_data_metadata,\n", ")" ] }, { "cell_type": "code", "execution_count": 25, "id": "bb5ec84d-2d35-46ed-87db-340ec830d528", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{\n", " \"agents\": [\n", " {\n", " \"name\": \"get_current_time\",\n", " \"udfs\": [\n", " \"current_utc_time\"\n", " ]\n", " },\n", " {\n", " \"name\": \"fused_docs\",\n", " \"udfs\": [\n", " \"list_public_udfs\",\n", " \"reading_fused_docs\"\n", " ]\n", " },\n", " {\n", " \"name\": \"vancouver_open_data\",\n", " \"udfs\": [\n", " \"hundred_parks_in_vancouver\",\n", " \"internet_speeds_for_lat_lon\"\n", " ]\n", " },\n", " {\n", " \"name\": \"elevation_stats_for_lat_lon_area\",\n", " \"udfs\": [\n", " \"elevation_stats\"\n", " ]\n", " },\n", " {\n", " \"name\": \"dynamic_output_vector\",\n", " \"udfs\": [\n", " \"dynamic_output_vector_udf\"\n", " ]\n", " },\n", " {\n", " \"name\": \"vancouver_open_data_demo\",\n", " \"udfs\": [\n", " \"hundred_parks_in_vancouver\",\n", " \"electric_vehicle_chargers_in_vancouver\",\n", " \"yearly_crime_amount\",\n", " \"internet_speeds_for_lat_lon\",\n", " \"community_gardens_vancouver\"\n", " ]\n", " },\n", " {\n", " \"name\": \"world_countries_info\",\n", " \"udfs\": [\n", " \"get_country_data\"\n", " ]\n", " },\n", " {\n", " \"name\": \"routing_agent\",\n", " \"udfs\": [\n", " \"single_routing_agent\"\n", " ]\n", " },\n", " {\n", " \"name\": \"tourism_info\",\n", " \"udfs\": [\n", " \"visitors_arrival\",\n", " \"visitors_purpose\",\n", " \"retrieve_visitor_data\"\n", " ]\n", " },\n", " {\n", " \"name\": \"stock_information\",\n", " \"udfs\": [\n", " \"get_stock_details\"\n", " ]\n", " }\n", " ]\n", "}\n" ] } ], "source": [ "# Let's make sure we created our agent properly, with all our UDFs\n", "agents = json.load(open(os.path.join(WORKING_DIR, \"agents.json\")))\n", "print(json.dumps(agents, indent=4, sort_keys=True))" ] }, { "cell_type": "code", "execution_count": 22, "id": "a70f7f47-8919-434e-a38d-c5f432e925d8", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'tourism_info'" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "AGENT_NAME" ] }, { "cell_type": "code", "execution_count": 23, "id": "f46667e9-d383-4af3-abb8-2888a227ca5c", "metadata": {}, "outputs": [], "source": [ "# Finally, we can select which Agent we want to pass to Claude in our MCP server config\n", "common.generate_local_mcp_config(\n", " config_path=PATH_TO_CLAUDE_CONFIG,\n", " agents_list=[AGENT_NAME],\n", " repo_path=WORKING_DIR,\n", ")" ] }, { "cell_type": "code", "execution_count": 87, "id": "ba666eb9-05cc-4084-b5be-86dc75cac123", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{\n", " \"mcpServers\": {\n", " \"tourism_info\": {\n", " \"args\": [\n", " \"run\",\n", " \"--directory\",\n", " \"C:\\\\Users\\\\user\\\\fused-mcp\",\n", " \"main.py\",\n", " \"--runtime=local\",\n", " \"--udf-names=visitors_arrival,visitors_purpose\",\n", " \"--name=tourism_info\"\n", " ],\n", " \"command\": \"uv\"\n", " }\n", " }\n", "}\n" ] } ], "source": [ "# Let's read this Claude Desktop config to see what we're passing\n", "claude_config = json.load(open(PATH_TO_CLAUDE_CONFIG))\n", "print(json.dumps(claude_config, indent=4, sort_keys=True))" ] }, { "cell_type": "code", "execution_count": null, "id": "2e592c67-558e-41db-9055-60ac3b25afa6", "metadata": {}, "outputs": [], "source": [ "# Restarting Claude\n", "def restart_claude(claude_path: str = CLAUDE_APP_PATH):\n", " app_name = claude_path.split(\"/\")[-1]\n", "\n", " try:\n", " os.system(f\"pkill -f '{app_name}'\")\n", " print(f\"Killed {app_name}\")\n", " time.sleep(2) # Wait for shutdown\n", " except Exception:\n", " print(\"Claude wasn't running, so no need to kill it\")\n", "\n", " print(f\"Restarting {app_name}\")\n", " os.system(f\"open -a '{claude_path}'\") # Restart Claude\n", "\n", "restart_claude()" ] } ], "metadata": { "kernelspec": { "display_name": "fused-alpha", "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.11.11" } }, "nbformat": 4, "nbformat_minor": 5 }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/fusedio/fused-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server