使用说明
花园mcp服务器
MCP服务器为园艺问题和规划提供工具。
当前工具集(fastMCP):
- 天气预报:获取某个地点的每日天气预报(Open‑Meteo)。
- 天气档案:通过Open‑Meteo ERA5档案提供的历史每日数据。
- 气象站历史:具有公共字段(tavg/tmin/tmax/prcp)的台站观测(每日)。
- 识别植物:使用Plant.id从图像中识别植物(带细节的首选)。
- 丰富植物:获取候选学名的额外信息(iNaturalist+维基百科)。
- 获取URL文本:获取网页并提取可读文本(用于客户端RAG)。
- 花园搜索:在网上搜索园艺主题(DuckDuckGo)。
- 床布局规划器:计算植物数量和简单的网格布局。
快速启动(使用紫外线)
- 要求:Python 3.11+和
uv已安装(macOS:brew install uv). - 创建虚拟环境并安装项目(使用开发工具):
uv venv --python 3.11uv pip install -e .[dev]
- 通过stdio运行MCP服务器:
uv run garden-mcp-server
测试和起毛(紫外线)
- 运行测试:
uv run pytest - 棉绒:
uv run ruff check .
配置
- 天气预报使用Open‑Meteo的免费API(无需密钥)。
- 天气档案使用Open‑Meteo ERA5档案(无需密钥)。
- Meteostat工具使用免费的Meteostat客户端(不需要密钥)。
- 花园搜索使用DuckDuckGo通过
duckduckgo-search.
整合
该服务器使用fastMCP框架,通过stdio传输MCP。将支持MCP的客户端(例如IDE或代理运行时)指向 garden-mcp-server 可执行(或调用 uv run garden-mcp-server).有关配置MCP服务器的信息,请参阅客户端的文档。
身份验证和机密
- 请勿将API密钥或机密提交到此存储库。它是公共的设计。
- 所有需要身份验证的工具都必须从环境变量中读取凭据。建议命名:
GARDEN_<SERVICE>_API_KEY(例如。,GARDEN_WEATHER_API_KEY). - 服务器自动加载本地
.env为了方便起见,请在启动时打开文件。.env已在.gitignore. - 示例用法:
- One‑off:
GARDEN_WEATHER_API_KEY=sk_live_... uv run garden-mcp-server - 使用.env:
- 创建
.env在项目根目录中(未提交):GARDEN_WEATHER_API_KEY=sk_live_...
- 运行:
uv run garden-mcp-server
- 创建
- One‑off:
- 我们的默认天气工具使用Open‑Meteo(无键)。如果切换到基于密钥的提供程序,请遵循上面的env-var模式。
- 植物标识使用Plant.id,并且需要API密钥。BYOT/K政策适用:
- 公众:呼叫者通行证
plant_id_api_key根据请求。 - 私人/内部:设置
GARDEN_PLANT_ID_API_KEY在env中;具有作用域的经过身份验证的调用者GARDEN_INTERNAL_SCOPE(默认值internal)可以省略plant_id_api_key. - 不要记录或存储用户提供的密钥。
- 公众:呼叫者通行证
客户示例
-
Python(通过uv进行stdio):
pip install fastmcp- 例子:
-
import asyncio from fastmcp.client import Client from fastmcp.client.transports import UvStdioTransport async def main(): # Launch the server in a subprocess via uv run transport = UvStdioTransport( command="garden-mcp-server", project_directory=".", # repo root with pyproject.toml ) client = Client(transport) async with client: # List available tools tools = await client.list_tools() print([t.name for t in tools]) # Call forecast tool res = await client.call_tool( "tool_get_weather_forecast", {"location": "Portland, OR", "days": 3}, ) print(res) # Call Open-Meteo archive tool res = await client.call_tool( "tool_open_meteo_archive", { "location": "Portland, OR", "start_date": "2024-01-01", "end_date": "2024-01-07", "daily_vars": [ "temperature_2m_max", "temperature_2m_min", "precipitation_sum", ], }, ) print(res) # Call Meteostat history tool res = await client.call_tool( "tool_meteostat_history", { "location": "Portland, OR", "start_date": "2024-01-01", "end_date": "2024-01-07", "fields": ["tavg", "tmin", "tmax", "prcp"], }, ) print(res) # Identify a plant by image URL (BYOK or env-based) res = await client.call_tool( "tool_identify_plant", { "image_url": "https://example.com/photo.jpg", "top_n": 3, # Public deployment (BYOK): pass your Plant.id key here # "plant_id_api_key": "pk_live_...", }, ) print(res) # Enrich the first two scientific names via iNaturalist/Wikipedia names = [c["scientific_name"] for c in res["candidates"][:2]] enriched = await client.call_tool( "tool_enrich_plants", {"scientific_names": names, "max_results": 2}, ) print(enriched) # Fetch page text for RAG (LLM summarization stays client-side) page = await client.call_tool( "tool_fetch_url_text", {"url": "https://example.com/garden-guide", "max_chars": 12000}, ) print(page["title"], len(page["text"])) asyncio.run(main())
-
-
错误处理和验证:
- 工具验证ISO日期格式(YYYY-MM-DD)并强制执行
start_date <= end_date. - Open‑Meteo存档检查变量名的简单标识符格式。
- Meteostat历史根据已知列验证所请求的字段(例如。,
tavg,tmin,tmax,prcp,snow,wdir,wspd,wpgt,pres,tsun).
- 工具验证ISO日期格式(YYYY-MM-DD)并强制执行
云运行部署
- 在本地构建容器:
docker build -t gcr.io/PROJECT_ID/garden-mcp-server:latest .docker push gcr.io/PROJECT_ID/garden-mcp-server:latest
- 或者使用gcloud(推荐):
gcloud builds submit --tag gcr.io/PROJECT_ID/garden-mcp-server:latest
- 部署到云运行:
gcloud run deploy garden-mcp-server --image gcr.io/PROJECT_ID/garden-mcp-server:latest --region REGION --platform managed --allow-unauthenticated- 根据需要设置环境变量:
FASTMCP_HOST=0.0.0.0(默认)GARDEN_MCP_HTTP_TRANSPORT=streamable-http或sse(默认:可流式传输http)- 可选路径:
FASTMCP_STREAMABLE_HTTP_PATH=/mcp,FASTMCP_SSE_PATH=/sse,FASTMCP_MESSAGE_PATH=/messages/
- 从客户端连接:
- 流式HTTP:
Client("https://SERVICE.run.app/mcp") - SSE:
Client("https://SERVICE.run.app/sse") - 如果启用身份验证(JWT或OAuth),请包含相应的
auth在客户端(例如。,Client(url, auth="<bearer-token>")).
- 流式HTTP:
移动上传(地面军事系统预签名网址)
- HTTP服务器公开了一个助手路由,用于在不公开凭据的情况下从移动应用程序上传图像:
POST /upload-url使用JSON:{ "file_name": "uploads/uuid.jpg", "content_type": "image/jpeg" }- 回复:已签名
upload_url(PUT)和签名get_url(GET)在短时间内有效。 - 需要环境:
GARDEN_GCS_BUCKET和Cloud Run服务帐户storage.objects.create许可。
- 流量:
- 移动请求
/upload-url以获取URL。 - 移动
PUT图像字节数upload_url带标题Content-Type集。 - 客户电话
tool_identify_plant随着image_url=get_url(临时访问)。 - (可选)稍后根据保留策略删除或旋转对象。
- 移动请求
上传URL策略(默认情况下使用您自己的存储)
- 控制访问
/upload-url通过GARDEN_UPLOAD_URL_POLICY:disabled:不提供上传URL(仅限公共BYOS)。internal(默认):仅允许具有以下条件的呼叫者GARDEN_INTERNAL_SCOPE(默认值internal).public:允许任何人(不建议用于共享bucket)。
- BYOS(自带存储):客户端可以跳过
/upload-url完全通过:- 发送
image_base64(小/中图像),或 - 通过A
image_url其指向其存储器(GCS/S3/等)中自己的签名URL。 - 这确保了第三方永远不会使用你的水桶。
- 发送
Cloud Run的推荐身份验证(JWT)
- 具有共享密钥的对称JWT(HS256)(如果通过密钥管理器管理,则简单安全):
- 创建一个秘密:
echo -n 'a-strong-shared-secret' | gcloud secrets create garden-jwt-secret --data-file=- - 启用身份验证进行部署:
gcloud run deploy garden-mcp-server \ --image gcr.io/PROJECT_ID/garden-mcp-server:latest \ --region REGION --platform managed --allow-unauthenticated \ --set-env-vars=FASTMCP_HOST=0.0.0.0,GARDEN_MCP_HTTP_TRANSPORT=streamable-http,FASTMCP_SERVER_AUTH=JWT,FASTMCP_SERVER_AUTH_JWT_ALGORITHM=HS256 \ --update-secrets=FASTMCP_SERVER_AUTH_JWT_PUBLIC_KEY=garden-jwt-secret:latest
- 客户端使用:使用共享密钥传递用HS256签名的Bearer令牌。
- 创建一个秘密:
默认值和端点
- Cloud Run的默认传输方式为
streamable-http(健壮,与Cloud Run请求模型配合良好)。使用sse如果您的客户端明确要求SSE,并且您的服务并发/超时已针对长期连接进行了调优。 - 在HTTP模式下运行时提供健康终结点:
GET /healthz→ JSON{"status":"ok"}GET /readyz→ok
公共MCP+BYOK(自带密钥)
- 为了保持MCP的公开性,同时确保其他人为自己的Plant.id使用付费:
- 公共实例:不设置
GARDEN_PLANT_ID_API_KEY所以呼叫者必须通过plant_id_api_key根据请求(BYOK)。 - 私有/内部实例:set
GARDEN_PLANT_ID_API_KEY对于您自己的应用程序。 - 具有策略的单个实例:set
GARDEN_PLANT_ID_API_KEY并且需要一个身份验证范围(默认值internal)使用它。其他人必须通过plant_id_api_key.- 通过配置所需的作用域
GARDEN_INTERNAL_SCOPE(默认值:internal).
- 通过配置所需的作用域
- 公共实例:不设置
- 适用于此仓库中的所有付费提供商工具。当用户传递密钥(例如。,
plant_id_api_key),服务器仅将其用于上游API调用,而从不记录/存储它。