环境搭建

1:本地环境搭建使用编辑器打开对应的项目目录

2:使用uv来进行管理环境

# 如果没有创建uv,可以直接通过以下命令来进行安装
pip install uv

# 进入对应目录下创建项目
uv init mcp-server-weather
cd mcp-server-weather

# 创建uv环境
uv venv mcp-server-weather  # 或者也可以直接使用 uv venv 也可以

# 安装对应安装包,缺什么依赖直接安装即可
uv pip install mcp
uv pip install numpy

3:编写MCP服务代码

# 直接在main方法中删除新环境中代码,使用自己的代码即可

# 下面是我给出的一个简单的示例,主要是用来获取天气情况,基于高德地图
import httpx
from mcp.server.fastmcp import FastMCP
import os
import argparse

# 初始化 MCP 服务器
mcp = FastMCP("mcp-gaode-weather-server-unique")

class GaodeWeatherTool:
    def __init__(self, api_key):
        """初始化高德天气工具"""
        self.api_key = api_key
        self.base_url = "https://restapi.amap.com/v3/weather/weatherInfo"
        self.headers = {"User-Agent": "weather-app/1.0"}
        
        # 城市名称到高德代码的映射表(中国主要城市)
        self.city_code_map = {
            # 直辖市
            "北京": "110000",
            "上海": "310000",
            "天津": "120000",
            "重庆": "500000",
            
            # 河北省
            "石家庄": "130100",
            "唐山": "130200",
            "秦皇岛": "130300",
            "邯郸": "130400",
            "邢台": "130500",
            "保定": "130600",
            "张家口": "130700",
            "承德": "130800",
            "沧州": "130900",
            "廊坊": "131000",
            "衡水": "131100",
            
            # 山西省
            "太原": "140100",
            "大同": "140200",
            "阳泉": "140300",
            "长治": "140400",
            "晋城": "140500",
            "朔州": "140600",
            "晋中": "140700",
            "运城": "140800",
            "忻州": "140900",
            "临汾": "141000",
            "吕梁": "141100",
            
            # 内蒙古自治区
            "呼和浩特": "150100",
            "包头": "150200",
            "乌海": "150300",
            "赤峰": "150400",
            "通辽": "150500",
            "鄂尔多斯": "150600",
            "呼伦贝尔": "150700",
            "巴彦淖尔": "150800",
            "乌兰察布": "150900",
            "兴安盟": "152200",
            "锡林郭勒盟": "152500",
            "阿拉善盟": "152900",
            
            # 辽宁省
            "沈阳": "210100",
            "大连": "210200",
            "鞍山": "210300",
            "抚顺": "210400",
            "本溪": "210500",
            "丹东": "210600",
            "锦州": "210700",
            "营口": "210800",
            "阜新": "210900",
            "辽阳": "211000",
            "盘锦": "211100",
            "铁岭": "211200",
            "朝阳": "211300",
            "葫芦岛": "211400",
            
            # 吉林省
            "长春": "220100",
            "吉林": "220200",
            "四平": "220300",
            "辽源": "220400",
            "通化": "220500",
            "白山": "220600",
            "松原": "220700",
            "白城": "220800",
            "延边朝鲜族自治州": "222400",
            
            # 黑龙江省
            "哈尔滨": "230100",
            "齐齐哈尔": "230200",
            "鸡西": "230300",
            "鹤岗": "230400",
            "双鸭山": "230500",
            "大庆": "230600",
            "伊春": "230700",
            "佳木斯": "230800",
            "七台河": "230900",
            "牡丹江": "231000",
            "黑河": "231100",
            "绥化": "231200",
            "大兴安岭地区": "232700",
            
            # 江苏省
            "南京": "320100",
            "无锡": "320200",
            "徐州": "320300",
            "常州": "320400",
            "苏州": "320500",
            "南通": "320600",
            "连云港": "320700",
            "淮安": "320800",
            "盐城": "320900",
            "扬州": "321000",
            "镇江": "321100",
            "泰州": "321200",
            "宿迁": "321300",
            
            # 浙江省
            "杭州": "330100",
            "宁波": "330200",
            "温州": "330300",
            "嘉兴": "330400",
            "湖州": "330500",
            "绍兴": "330600",
            "金华": "330700",
            "衢州": "330800",
            "舟山": "330900",
            "台州": "331000",
            "丽水": "331100",
            
            # 安徽省
            "合肥": "340100",
            "芜湖": "340200",
            "蚌埠": "340300",
            "淮南": "340400",
            "马鞍山": "340500",
            "淮北": "340600",
            "铜陵": "340700",
            "安庆": "340800",
            "黄山": "341000",
            "滁州": "341100",
            "阜阳": "341200",
            "宿州": "341300",
            "巢湖": "341400",
            "六安": "341500",
            "亳州": "341600",
            "池州": "341700",
            "宣城": "341800",
            
            # 福建省
            "福州": "350100",
            "厦门": "350200",
            "莆田": "350300",
            "三明": "350400",
            "泉州": "350500",
            "漳州": "350600",
            "南平": "350700",
            "龙岩": "350800",
            "宁德": "350900",
            
            # 江西省
            "南昌": "360100",
            "景德镇": "360200",
            "萍乡": "360300",
            "九江": "360400",
            "新余": "360500",
            "鹰潭": "360600",
            "赣州": "360700",
            "吉安": "360800",
            "宜春": "360900",
            "抚州": "361000",
            "上饶": "361100",
            
            # 山东省
            "济南": "370100",
            "青岛": "370200",
            "淄博": "370300",
            "枣庄": "370400",
            "东营": "370500",
            "烟台": "370600",
            "潍坊": "370700",
            "济宁": "370800",
            "泰安": "370900",
            "威海": "371000",
            "日照": "371100",
            "莱芜": "371200",
            "临沂": "371300",
            "德州": "371400",
            "聊城": "371500",
            "滨州": "371600",
            "菏泽": "371700",
            
            # 河南省
            "郑州": "410100",
            "开封": "410200",
            "洛阳": "410300",
            "平顶山": "410400",
            "安阳": "410500",
            "鹤壁": "410600",
            "新乡": "410700",
            "焦作": "410800",
            "濮阳": "410900",
            "许昌": "411000",
            "漯河": "411100",
            "三门峡": "411200",
            "南阳": "411300",
            "商丘": "411400",
            "信阳": "411500",
            "周口": "411600",
            "驻马店": "411700",
            "济源": "419001",
            
            # 湖北省
            "武汉": "420100",
            "黄石": "420200",
            "十堰": "420300",
            "宜昌": "420500",
            "襄阳": "420600",
            "鄂州": "420700",
            "荆门": "420800",
            "孝感": "420900",
            "荆州": "421000",
            "黄冈": "421100",
            "咸宁": "421200",
            "随州": "421300",
            "恩施土家族苗族自治州": "422800",
            "仙桃": "429004",
            "潜江": "429005",
            "天门": "429006",
            "神农架林区": "429021",
            
            # 湖南省
            "长沙": "430100",
            "株洲": "430200",
            "湘潭": "430300",
            "衡阳": "430400",
            "邵阳": "430500",
            "岳阳": "430600",
            "常德": "430700",
            "张家界": "430800",
            "益阳": "430900",
            "郴州": "431000",
            "永州": "431100",
            "怀化": "431200",
            "娄底": "431300",
            "湘西土家族苗族自治州": "433100",
            
            # 广东省
            "广州": "440100",
            "韶关": "440200",
            "深圳": "440300",
            "珠海": "440400",
            "汕头": "440500",
            "佛山": "440600",
            "江门": "440700",
            "湛江": "440800",
            "茂名": "440900",
            "肇庆": "441200",
            "惠州": "441300",
            "梅州": "441400",
            "汕尾": "441500",
            "河源": "441600",
            "阳江": "441700",
            "清远": "441800",
            "东莞": "441900",
            "中山": "442000",
            "潮州": "445100",
            "揭阳": "445200",
            "云浮": "445300",
            
            # 广西壮族自治区
            "南宁": "450100",
            "柳州": "450200",
            "桂林": "450300",
            "梧州": "450400",
            "北海": "450500",
            "防城港": "450600",
            "钦州": "450700",
            "贵港": "450800",
            "玉林": "450900",
            "百色": "451000",
            "贺州": "451100",
            "河池": "451200",
            "来宾": "451300",
            "崇左": "451400",
            
            # 海南省
            "海口": "460100",
            "三亚": "460200",
            "三沙": "460300",
            "儋州": "460400",
            
            # 四川省
            "成都": "510100",
            "自贡": "510300",
            "攀枝花": "510400",
            "泸州": "510500",
            "德阳": "510600",
            "绵阳": "510700",
            "广元": "510800",
            "遂宁": "510900",
            "内江": "511000",
            "乐山": "511100",
            "南充": "511300",
            "眉山": "511400",
            "宜宾": "511500",
            "广安": "511600",
            "达州": "511700",
            "雅安": "511800",
            "巴中": "511900",
            "资阳": "512000",
            "阿坝藏族羌族自治州": "513200",
            "甘孜藏族自治州": "513300",
            "凉山彝族自治州": "513400",
            
            # 贵州省
            "贵阳": "520100",
            "六盘水": "520200",
            "遵义": "520300",
            "安顺": "520400",
            "毕节": "520500",
            "铜仁": "520600",
            "黔西南布依族苗族自治州": "522300",
            "黔东南苗族侗族自治州": "522600",
            "黔南布依族苗族自治州": "522700",
            
            # 云南省
            "昆明": "530100",
            "曲靖": "530300",
            "玉溪": "530400",
            "保山": "530500",
            "昭通": "530600",
            "丽江": "530700",
            "普洱": "530800",
            "临沧": "530900",
            "楚雄彝族自治州": "532300",
            "红河哈尼族彝族自治州": "532500",
            "文山壮族苗族自治州": "532600",
            "西双版纳傣族自治州": "532800",
            "大理白族自治州": "532900",
            "德宏傣族景颇族自治州": "533100",
            "怒江傈僳族自治州": "533300",
            "迪庆藏族自治州": "533400",
            
            # 西藏自治区
            "拉萨": "540100",
            "日喀则": "540200",
            "昌都": "540300",
            "林芝": "540400",
            "山南": "540500",
            "那曲": "540600",
            "阿里地区": "542500",
            
            # 陕西省
            "西安": "610100",
            "铜川": "610200",
            "宝鸡": "610300",
            "咸阳": "610400",
            "渭南": "610500",
            "延安": "610600",
            "汉中": "610700",
            "榆林": "610800",
            "安康": "610900",
            "商洛": "611000",
            
            # 甘肃省
            "兰州": "620100",
            "嘉峪关": "620200",
            "金昌": "620300",
            "白银": "620400",
            "天水": "620500",
            "武威": "620600",
            "张掖": "620700",
            "平凉": "620800",
            "酒泉": "620900",
            "庆阳": "621000",
            "定西": "621100",
            "陇南": "621200",
            "临夏回族自治州": "622900",
            "甘南藏族自治州": "623000",
            
            # 青海省
            "西宁": "630100",
            "海东": "630200",
            "海北藏族自治州": "632200",
            "黄南藏族自治州": "632300",
            "海南藏族自治州": "632500",
            "果洛藏族自治州": "632600",
            "玉树藏族自治州": "632700",
            "海西蒙古族藏族自治州": "632800",
            
            # 宁夏回族自治区
            "银川": "640100",
            "石嘴山": "640200",
            "吴忠": "640300",
            "固原": "640400",
            "中卫": "640500",
            
            # 新疆维吾尔自治区
            "乌鲁木齐": "650100",
            "克拉玛依": "650200",
            "吐鲁番": "650400",
            "哈密": "650500",
            "昌吉回族自治州": "652300",
            "博尔塔拉蒙古自治州": "652700",
            "巴音郭楞蒙古自治州": "652800",
            "阿克苏地区": "652900",
            "克孜勒苏柯尔克孜自治州": "653000",
            "喀什地区": "653100",
            "和田地区": "653200",
            "伊犁哈萨克自治州": "654000",
            "塔城地区": "654200",
            "阿勒泰地区": "654300",
            "石河子": "659001",
            "阿拉尔": "659002",
            "图木舒克": "659003",
            "五家渠": "659004",
            "北屯": "659005",
            "铁门关": "659006",
            "双河": "659007",
            "可克达拉": "659008",
            "昆玉": "659009",
        }

    async def query_weather(self, city: str, extensions: str = "base") -> dict:
        """
        从高德地图 API 查询天气信息。
        :param city: 城市名称或高德地图城市代码
        :param extensions: 'base' 为实时天气,'all' 为预报天气
        :return: 天气数据字典,若出错则包含 error 字段
        """
        # 优先使用映射表中的代码,否则直接使用传入的值(可能是用户提供的代码)
        city_code = self.city_code_map.get(city, city)
        
        params = {
            "key": self.api_key,
            "city": city_code,
            "extensions": extensions,
            "output": "json"
        }
        print(f"DEBUG: Querying weather for city: {city} (code: {city_code}), params: {params}")
        
        async with httpx.AsyncClient() as client:
            try:
                response = await client.get(self.base_url, params=params, headers=self.headers, timeout=10.0)
                print(f"DEBUG: Response status: {response.status_code}, content: {response.text[:500]}...")
                response.raise_for_status()
                data = response.json()
                
                if data.get("status") != "1":
                    # 检查是否是城市代码无效导致的错误
                    if data.get("info") == "INVALID_PARAMS" and city != city_code:
                        return {"error": f"未找到城市 '{city}' 的代码,请尝试其他城市"}
                    return {"error": f"API error: {data.get('info', 'Unknown error')}"}
                
                lives = data.get("lives", [])
                if not lives:
                    return {"message": "未找到该城市的天气数据"}
                
                weather_info = lives[0]
                result = {
                    "city": weather_info.get("city", city),
                    "weather": weather_info.get("weather", "Unknown"),
                    "temperature": weather_info.get("temperature", "Unknown"),
                    "winddirection": weather_info.get("winddirection", "Unknown"),
                    "windpower": weather_info.get("windpower", "Unknown"),
                    "humidity": weather_info.get("humidity", "Unknown"),
                    "reporttime": weather_info.get("reporttime", "Unknown")
                }
                return result
            except httpx.RequestException as e:
                return {"error": f"Weather query error: {str(e)}"}

    def format_weather(self, weather_data: dict) -> str:
        """将天气数据格式化为易读文本"""
        if "error" in weather_data:
            return f"⚠️ {weather_data['error']}"
        if "message" in weather_data:
            return f"⚠️ {weather_data['message']}"
        
        return (
            f"🌍 {weather_data['city']}\n"
            f"🌡 温度: {weather_data['temperature']}°C\n"
            f"💧 湿度: {weather_data['humidity']}%\n"
            f"🌬 风向: {weather_data['winddirection']} 风力: {weather_data['windpower']} 级\n"
            f"🌤 天气: {weather_data['weather']}\n"
            f"⏰ 更新时间: {weather_data['reporttime']}\n"
        )

def main():
    print("DEBUG: Starting MCP WeatherServer")
    parser = argparse.ArgumentParser(description="Run MCP WeatherServer")
    parser.add_argument("--api_key", type=str, required=True, help="高德地图 API 密钥")
    args = parser.parse_args()

    # 使用命令行提供的 API 密钥实例化天气工具
    weather_tool = GaodeWeatherTool(api_key=args.api_key)

    @mcp.tool()
    async def query_weather(city_name: str) -> str:
        """
        输入城市名称,返回今日天气查询结果。
        :param city_name: 城市名称(如"杭州")
        :return: 格式化后的天气信息
        """
        data = await weather_tool.query_weather(city_name, extensions="base")
        return weather_tool.format_weather(data)

    mcp.run(transport='stdio')

if __name__ == "__main__":
    main()

4:代码编写完成进行测试

# 下面是测试的命令
npx -y @modelcontextprotocol/inspector uv run main.py --api_key 高德地图的key

# 日志
Starting MCP inspector...
⚙️ Proxy server listening on port 6277
🔍 MCP Inspector is up and running at http://127.0.0.1:6274 🚀

# 上面的日志中清晰的显示对应的webui界面,通过端口6274
# 打开上面的界面,然后在左侧显示连接,连接完成会出现对应的界面,如下面展示的图片所示,点击Tools
# 工具,然后进行测试即可

在这里插入图片描述
在这里插入图片描述

5:打包部署

发布之前记得修改pyproject.toml文件
参考:

[project]
name = "mcp-gaode-weather-server-unique"
version = "0.1.0"
description = "输入Gaode api_key,获取天气信息"
readme = "README.md"
requires-python = ">=3.11"
dependencies = ["httpx>=0.28.1","mcp>=1.6.0", "numpy>=2.2.5"]


[project.scripts]
mcp-gaode-weather-server-unique = "main:main"

注意:name要唯一,不能重复,重复上传会报错

# 执行安装命令
uv pip install httpx
uv pip install build twine

# 打包编译环境
python -m build

# 编译完成后会显示两个目录,一个是dist,一个是mcp_gaode_weather_server_unique.egg-info

# 上传mcp工具到pypi官网,这个地方需要去这个官网进行注册,并且拿到对应的api token令牌
python -m twine upload dist/*
执行完后输入token,token为隐式输入

官网地址:https://pypi.org/
注册即可,注册完成后进行一个设备认证,双重认证完成之后才能创建这个API Token
在这里插入图片描述

6:调用MCP工具

使用CherryStudio工具进行测试
在这里插入图片描述

注意参数为三个,第一个是服务名称,第二个是apikey,第三个是具体的key值
结果显示:
在这里插入图片描述

结束语

MCP 协议为 AI 与外部世界的交互提供了一个优雅的解决方案,我的实现则展示了其在实际场景中的强大潜力。通过集成天气查询、谷歌检索、微表情分析和 ComfyUI 生图,我希望为开发者提供一个开箱即用的模板,激发更多创新灵感。

欢迎访问我的项目仓库,获取完整代码并尝试扩展功能!如果有任何问题或建议,随时在评论区或 GitHub 上与我交流。

项目地址:https://github.com/Dreamboat-Rachel/MCP-Server-For-Local

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐