mcpField
M
MAVLink MCP Server

MAVLink MCP Server

本地服务
github.com/ion-g-ion其他258 周前更新

关于 MAVLink MCP Server

MAVLink MCP Server 是一个基于 Python 的 MCP 服务,供 LLM Agent 通过 MAVLink 与支持 MAVLink 的无人机通信,典型对象为经 MAVSDK 连接的 PX4。项目默认建议先在 PX4 SITL 中测试,并提供围绕航线规划、校验与上传的审核流程。

主要功能

  1. 01通过 MAVLink 与飞行器建立连接
  2. 02连接状态以非阻塞方式返回
  3. 03基于卫星影像生成测绘航线
  4. 04航线校验、预检与上传后核对
  5. 05支持 stdio 与 streamable-http 传输

适用场景

  1. 01在 PX4 SITL 中测试 LLM 控制无人机
  2. 02为测绘任务生成并审核蛇形扫描航线
  3. 03通过本地或远程 MCP 客户端远程调用飞行工具

MCP 相关

该项目是 MCP 服务,向 MCP 客户端暴露无人机连接、航线规划与任务执行相关的工具、资源和提示词。HTTP 传输未提供认证,需自行做好网络访问控制。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

暂未收录安装方式

我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。

代码仓库

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

MAVLink MCP 服务

面向与支持 MAVLink 的载具(通常是通过 MAVSDK 连接 PX4)通信的大模型 Agent 的 Python Model Context Protocol (MCP) 服务。

前置条件

  • Python 3.10 或更高版本
  • 一个 MAVLink 端点(推荐首选 PX4 SITL;不要在无人监督的情况下让不受信任的 Agent 连接真实飞行器)

安装

  1. 克隆仓库:
git clone https://github.com/ion-g-ion/MAVLinkMCP.git
cd MAVLinkMCP
  1. 安装项目(本仓库自带 pyproject.toml 和 uv.lock;没有根目录下的 requirements.txt):
# with pip (editable)
pip install -e .

# or with uv
uv sync

配置(SITL / 连接)

该服务会读取:

环境变量默认值含义
MAVLINK_ADDRESS空字符串要发送到的对端主机。留空表示改为监听。
MAVLINK_PORT14540UDP 端口(PX4 SITL 通常使用 14540)
MAVLINK_CONNECT_TIMEOUT60在判定载具不可达之前持续拨号连接的秒数

MAVLINK_ADDRESS 决定链路方向:

  • 留空(默认) → udpin://0.0.0.0:$MAVLINK_PORT。服务绑定该 端口并等待。这正是 PX4 SITL 所需的:SITL 发送到 14540,并 期望另一端有监听者。
  • 设置为某个主机 → udpout://$MAVLINK_ADDRESS:$MAVLINK_PORT。服务 发送到已经在那里监听的某个对端,例如真实硬件上的伴飞链路。

本地 PX4 的示例:

export MAVLINK_PORT=14540
# MAVLINK_ADDRESS can stay empty: SITL talks to us, so we listen
链路建立是非阻塞的

MCP 握手从不等待载具。会话立即启动, 链路在后台建立,因此工具会报告链路状态,而不是 挂起:

{"status": "failed", "error": "MAVLink link is still coming up; retry shortly", "connected": false, "link_state": "connecting"}

一旦自动驾驶仪响应,工具就会切换到真实结果。如果在 MAVLINK_CONNECT_TIMEOUT 内没有任何响应, 该失败将是确定性的,并会指明尝试过的地址:

{"status": "failed", "error": "no MAVLink vehicle reachable on udpin://0.0.0.0:14540 after 60s", "connected": false, "link_state": "failed"}

等待 GPS/home 位置估计被有意地排除在这一门槛之外 ——它会被记录,但正在收敛的位置定位绝不会阻塞命令。

每个会话一条载具链路。 在 HTTP 传输下,每个 MCP 会话 都会打开自己的 MAVSDK 连接,因此第二个并发客户端无法绑定 同一个 UDP 端口,并会看到上面的失败关闭负载。

用法

安装项目后,你的环境中会有一个 mavlinkmcp 控制台脚本。 它通过 stdio 提供 MCP:

mavlinkmcp
# equivalently
python -m mavlinkmcp
从另一个工作目录启动

用绝对路径将你的 MCP 客户端指向该控制台脚本。它的 shebang 指定了项目解释器,因此它自带自己的环境,不关心 从哪里启动:

/abs/path/to/MAVLinkMCP/.venv/bin/mavlinkmcp

这是填入聊天应用的 stdio MCP 服务配置的形式。

请注意,uv run 从你的当前目录解析项目,而不是从 你传给它的路径 —— 因此从别处执行 uv run /abs/path/to/src/mavlinkmcp/server.py 会构建一个不含本项目依赖的环境,并在导入时失败。如果你想通过 uv 运行, 请显式指定项目:

uv run --project /abs/path/to/MAVLinkMCP mavlinkmcp
uv --directory /abs/path/to/MAVLinkMCP run mavlinkmcp
通过 HTTP 运行

同样的工具可以通过网络套接字而不是 stdio 来提供:

# current MCP HTTP transport, on http://127.0.0.1:8000/mcp
mavlinkmcp --transport streamable-http

# pick the socket and endpoint path
mavlinkmcp --transport streamable-http --host 127.0.0.1 --port 9000 --path /drone
选项适用于默认值含义
--transport全部stdiostdio、streamable-http 或 sse
--hostHTTP127.0.0.1绑定地址
--portHTTP8000绑定端口
--pathstreamable-http/mcp端点路径
--allowed-hostHTTP—要接受的 Host 头;可重复,允许通配符
--allow-any-hostHTTP关闭完全关闭 Host/Origin 检查

sse 是较旧的传输方式,已在 MCP 规范中弃用。除非你有需要 sse 的客户端, 否则优先使用 streamable-http。在 --transport stdio 下,HTTP 选项 会被拒绝,而不是被静默忽略。

绑定到 localhost 之外

在 localhost 绑定时,DNS 重绑定防护(Host/Origin 校验)会自动启用。在 localhost 之外无法推断,因此你必须明确说明接受什么——否则服务会拒绝启动:

# declare the Host headers clients will send
mavlinkmcp --transport streamable-http --host 0.0.0.0 --port 8000 \
    --allowed-host drone.lan:8000

# or turn the check off deliberately
mavlinkmcp --transport streamable-http --host 0.0.0.0 --allow-any-host

Host 不在列表中的请求会收到 421 Misdirected Request。

在暴露它之前

HTTP 传输上没有任何身份验证。 这里一个开放的端口,就是一个可以武装、起飞和移动载具的端口——这比只有本地客户端能通信的 stdio 管道暴露面大得多。请保持在 localhost,或将其置于你控制的网络以及你自己的认证层之后。

另请注意,MAVSDK 连接是按 MCP 会话建立的,而不是按进程。通过 stdio 时,每个客户端进程一个连接。通过 HTTP 时,每个连接的会话都会打开一条通往同一载具的独立链路,且它们之间没有任何仲裁——两个客户端就意味着对一架飞行器有两个独立的指令源。无论哪种方式,服务都会立即绑定;它不会在启动时等待载具。

Agent 使用示例

参见 examples/README.md 并运行:

python examples/example_agent.py
# or
uv run examples/example_agent.py

按 examples/ 下的说明导出你的大模型提供商密钥(切勿提交密钥)。该示例使用 fast-agent-mcp / FastAgent 以及 mavlink_mcp 服务条目。

飞行计划

该服务可以生成、存储、检查并执行覆盖任务。这种拆分的关键在于,路线在到达载具之前就变得可审查:

get_map_view -> create_survey_plan -> render_plan_view
  -> validate_plan -> preflight_check -> upload_plan
  -> verify_uploaded_plan -> start_mission
看清地面

get_map_view 返回一张带地理配准的卫星视图作为图像,并附带其精确变换。在图片中识别一个地物,将其角点以像素形式报告,让服务进行转换——视觉模型擅长指向一块田地,而不擅长凭空编造纬度,因此它永远不必这么做:

get_map_view()                       # centred on the vehicle
  -> [image, {view_id, center, meters_per_pixel, drone: {pixel, heading_deg}, ...}]

create_survey_plan(name="Front Field", view_id=..., polygon_pixels=[[430,180], ...],
                   altitude_m=40, camera={...})

map_transform 会针对已存储的视图进行双向转换。传入 orientation="heading_up" 可旋转视图,使载具的前进方向朝上,这样“无人机前方的那块田地”就变成了一个关于图片的问题,而不是关于罗盘运算的问题。

改为提供 latitude_deg/longitude_deg 则可以查看任意位置,且完全不需要载具——计划可以在办公桌前编写和估算成本,之后再飞行。

地图影像

获取底图是本服务中唯一的出站网络使用。瓦片按需获取、缓存到磁盘,并限定于所配置提供商的主机;获取失败的瓦片会渲染为纯灰色,且响应中会说明这一点,因为地理配准仍然精确。

变量含义
MAVLINKMCP_MAP_PROVIDEResri(默认,全球航空影像,无需密钥)· osm(街道地图,不是影像)· mapbox / maptiler(需要密钥)· custom · none
MAVLINKMCP_MAP_API_KEY需要密钥的提供商所用的密钥
MAVLINKMCP_MAP_TILE_URL用于 custom 的 XYZ 模板,例如可自托管的瓦片服务器
MAVLINKMCP_MAP_IMAGE_MODEimage(默认)或 path,用于无法显示图像的客户端

none 会禁用所有网络访问,并渲染一个地理配准正确的空白画布——几何与叠加层仍然可用,只是没有任何东西可用于识别地面地物。prefetch_map_area 会在前往无网络连接的地方之前预热瓦片缓存。

署名不是可选项。 每个提供商的条款都要求署名;该声明会绘制到每个视图上,并在响应载荷中返回。

视图以 JPEG 渲染,默认 768 px,上限为 1024。这个上限关乎 token 而非字节:图像成本随像素面积增长,因此 768 px 的视图大约为 790 token,而 1024 px 的约为 1400。

计划存放的位置
$MAVLINKMCP_PLANS_DIR/          # default $XDG_DATA_HOME/mavlinkmcp
├── plans/north-field/
│   ├── meta.json               # name, head revision, timestamps
│   ├── rev-001.json
│   └── rev-002.json
├── views/                      # rendered map views + their geotransforms
└── tiles/                      # tile cache

计划是纯 JSON,并且比 MCP 会话存续得更久,因此无需运行本服务,人类也可以读取、做 diff 并编辑它们。

一个修订版的生成内容是固定不变的:revise_plan 会用新参数重新运行生成器并写入一个新修订版,而不是编辑航点,因此存储的参数和存储的路径永远不会互相矛盾。只有生命周期注解(status、checks、estimate、uploaded)会原地变化。

上传关卡

计划会经历 draft -> validated -> uploaded,并且 upload_plan 会拒绝任何未通过 validate_plan 的内容。任何修订版一开始都是草稿,因此一次改动总会让先前的检查失效。

validate_plan 会报告发现项;只有 error 会阻断。最有价值的一项是 FAR_FROM_HOME —— 在地图上画错的多边形会在世界的另一端生成一份格式完全正确的计划,而距家的距离正是能抓住它的东西。preflight_check 随后会基于健康状态、GPS 定位、电池和着陆状态给出实时的 go/no-go,而 verify_uploaded_plan 会把任务从飞行器上下载回来,并与审核过的内容做 diff。

请注意,计划的生命周期状态报告为 plan_status。status 键是本服务中每个工具都返回的调用信封(success / failed),并且它保持如此。

测绘几何

create_survey_plan 会生成 boustrophedon(割草机式)扫描。线间距来自 line_spacing_m 或 camera;两者都给会被拒绝,而不是静默地优先选择其中一个。相机还会设置拍照触发距离并报告地面采样距离:

camera = {"sensor_width_mm": 13.2, "focal_length_mm": 8.8,
          "image_width_px": 5472, "image_height_px": 3648,
          "front_overlap": 0.75, "side_overlap": 0.65}
# at 40 m: 1.10 cm/px, 60 m footprint, 21 m line spacing, 10 m trigger distance

省略 sweep_angle_deg 会沿区域的长轴扫描,从而最大限度地减少转弯 —— 测绘时间和电池实际上就消耗在这里。

安全说明

MCP 工具可以让飞行器解锁、起飞和移动。优先使用 SITL。让人类随时准备 kill switch / 降落。工具故障应由客户端按 fail-closed 处理。

飞行计划增加了一个审核步骤,而不是取消了审核的需要:validate_plan 和 preflight_check 能抓住的是机械性的错误(路线位置错误、无 GPS 定位、电池不足),而不是那些属于判断问题的错误。飞行前先看看 render_plan_view。

提示词

工具描述说明的是一次调用做什么。它们都没有说明为什么流水线要拆分,为什么 upload_plan 会拒绝草稿,或者为什么模型应该用像素而不是纬度来回答。这些上下文以 MCP 提示词的形式提供,大多数客户端会将其呈现为斜杠命令:

提示词涵盖内容
mavlink_overview服务如何组织,status / plan_status 信封,后台链路,安全态势
telemetry_guide有哪些读数,以及为什么一次失败的读取是未知值而不是零
map_view_guide地理配准影像:用像素而不是纬度、方向、token 成本、提供商、署名
plan_lifecycle_guide计划状态、不可变修订版,以及上传关卡的用途
survey_walkthrough(area?, altitude_m?)端到端,从查看地面到任务运行
preflight_briefing(plan_id)针对一个已存储计划运行的 go/no-go 序列

措辞位于 guides.py,并从执行这些限制的模块中插值出限制值,因此简报中引用的数字不会偏离实际检查的数字。

资源

对磁盘上已有内容的只读、可通过 URI 寻址的描述。工具调用是有 token 成本的操作;资源则是客户端可以自行完成的查询。以下这些都不会获取瓦片,也不会触碰载具。

URI内容
mavlinkmcp://map/providers所有已知的瓦片提供商及当前生效的配置,附署名信息
mavlinkmcp://map/limits地图调用受检所依据的边界:尺寸、半径、缩放级别、瓦片上限、存储限制
mavlinkmcp://map/cache瓦片缓存与视图存储中保存的内容,以及它们所在的位置
mavlinkmcp://map/views磁盘上仍然存在的每个已渲染视图,最新的排在最前
mavlinkmcp://map/views/{view_id}单个视图的完整地理参考:中心、比例尺、旋转、bbox、地面角点
mavlinkmcp://map/views/{view_id}/image渲染出的 JPEG,因此无需重新获取瓦片即可再次查看某个视图
mavlinkmcp://plans/{plan_id}/geojson以 GeoJSON 表示的方案最新修订 — 区域、路径和带编号的航点

API 密钥绝不会出现在资源中。 一个会话中的每个客户端都能读取每个资源,因此提供商区块只报告是否已配置密钥,而绝不报告密钥是什么;瓦片模板在输出时也会去掉其查询字符串。

索引类资源会在载荷中报告配置错误,而不是抛出异常 — “该提供商需要一个它没有的密钥”正是阅读 mavlinkmcp://map/providers 的人想要得到的答案。按 id 寻址的资源则会抛出异常:对于一个不存在的视图,没有部分答案可言。

测试

单元测试是离线的 — 它们只测试纯验证、几何和归一化辅助函数,绝不导入 mavsdk,绝不与载具通信,也绝不发起网络请求。瓦片获取通过注入一个假的获取器来测试,渲染路径则在 MAVLINKMCP_MAP_PROVIDER=none 下运行。请从仓库根目录运行它们:

python -m unittest discover

测试按其真实包路径导入辅助函数(from mavlinkmcp.endpoint import ...),因此项目必须安装在运行测试所用的环境中(uv sync 或 pip install -e .)。

SITL 集成测试

tests_sitl/ 覆盖了离线测试套件在结构上无法覆盖的内容:MAVSDK 握手、连接保护以及任务协议往返。这些需要真实的飞控,因此 sitl/ 会构建一个运行 SIH 动力学模型的 PX4 镜像 — 不需要 Gazebo,不需要 GPU,约 120 MB,约 7 MB 内存:

docker build -t px4-sih:v1.14.3 sitl/
docker run -d --rm --name px4 --network host \
  -e PX4_HOME_LAT=473977420 -e PX4_HOME_LON=85455940 \
  px4-sih:v1.14.3
python -m unittest discover

必须使用 network_mode: host:PX4 会发送到回环地址上的 14540,且在没有 MAV_2_BROADCAST=1 的情况下不会与非 localhost 通信,因此发布端口无济于事。这在 Linux 和 GitHub runner 上可行,但在 macOS 上的 Docker Desktop 不可行。

Home 坐标是 以 1e-7 度为单位的缩放 int32,而非十进制度 — px4-rc.simulator 会将 PX4_HOME_LAT 直接传入 SIH_LOC_LAT0。传入 47.397742 会被截断为 47,并悄无声息地让载具飞到 150 km 之外。

在没有载具监听时,这些测试会在约 1.5 秒的探测后跳过而非失败,因此在没有 Docker 的机器上 python -m unittest discover 仍然可用。如果漏检了较慢的容器,请调高 MAVLINKMCP_SITL_PROBE_TIMEOUT。

贡献

欢迎贡献!请 fork 仓库并提交 pull request。

许可证

本项目采用 MIT 许可证授权。详情请参阅 LICENSE 文件。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。