花园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.11 - uv 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__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
- 我们的默认天气工具使用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 - 例子: - ```python 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 ")`).
移动上传(地面军事系统预签名网址)
- 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` 许可。
- 流量:
1. 移动请求 `/upload-url` 以获取URL。
1. 移动 `PUT` 图像字节数 `upload_url` 带标题 `Content-Type` 集。
1. 客户电话 `tool_identify_plant` 随着 `image_url` = `get_url` (临时访问)。
1. (可选)稍后根据保留策略删除或旋转对象。
上传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调用,而从不记录/存储它。