mcp-local-redes
用于车队遥测的本地 MCP 服务器
CC3067 网络课程项目 1,第 10 组,危地马拉山谷大学。 Fernando Hernández。
一个 MCP(模型上下文协议)服务器,运行在操作员的机器上,将车队的遥测查询作为工具暴露给语言模型调用。借助它,聊天机器人可以回答诸如“P-123BCD 在哪里?”或“这周哪辆车行驶里程最长?”这样的问题,而用户无需打开追踪平台。
该协议完全基于 Python 标准库在 stdio 上从零实现。我没有使用 MCP SDK 或任何处理 JSON-RPC 的库;这是项目的核心要求,我会在协议实现一节中解释。
什么是 MCP
MCP 是一个应用层协议,它标准化了语言模型如何发现和调用外部工具。MCP 服务器发布一个工具列表,每个工具都有名称、描述和参数 JSON 模式。客户端(例如 Claude Desktop)获取该列表,将其展示给模型,当模型决定使用某个工具时,客户端使用模型选择的参数调用它,并将结果返回给模型,让模型用自然语言解释。
从机制上讲,MCP 是基于传输层的 JSON-RPC 2.0。在本项目中,传输层是 stdio:客户端将服务器作为子进程启动,两者通过 stdin 和 stdout 交换以换行符分隔的 JSON 对象。会话以握手开始(initialize → 响应 → notifications/initialized),之后客户端可以调用 tools/list、tools/call 和 ping。
Related MCP server: NL-to-SQL MCP
为什么是车队,为什么是本地
拥有自有车队的公司已经在车辆上安装了 GPS,并拥有追踪平台;数据存在且完整。问题在于访问:如今需要浏览仪表盘、应用筛选器和生成报告,而最了解运营的人往往最不熟悉平台。
服务器按设计在本地运行,这不仅是课程要求:车队的位置会暴露商业路线、客户和作息时间。服务器运行在操作员的机器上,只有每次查询的聚合结果会发送给模型,位置历史永远不会传出。
前置要求
Python 3.11 或更高版本
git可选:用于 Google 地理编码和重新生成路线的 Google Maps 密钥(
GOOGLE_MAPS_API_KEY)。没有它一切照常工作。
安装
git clone https://github.com/FerAHMz/mcp-local-redes.git
cd mcp-local-redes
python3.11 -m venv .venv
source .venv/bin/activate # en Windows: .venv\Scripts\activate
pip install -r requirements.txt生成数据库
我没有使用任何公司的真实数据。生成器模拟 15 辆车在危地马拉大都会区的真实路线上行驶 7 天,每辆车在工作时间内每 15 秒报告一次,带有 σ ≈ 5 m 的 GPS 噪声,以及我预先知道的事件(长时间停车、超速、信号丢失、进出地理围栏)。
python datos/generador.py生成 datos/flota.db(SQLite,约 16 万条位置记录和约 1300 个事件),只需几秒钟。默认情况下,数据集在运行时刻结束,因此问题中的“今天”和“昨天”指的是真实日期。要获得可复现的数据集,可以固定结束时刻:
python datos/generador.py --ahora 2026-08-19T15:30最好在工作时间生成(或传入一个工作时间的 --ahora),这样 unidades_detenidas 中会有在途车辆,而不只是熄火的车辆。
路线:离线模式和 API 模式
基础路线以编码折线的形式保存在 datos/rutas/*.json 中(与 Google Directions API 返回的格式相同),以及每条路线的站点。生成器从那里读取它们,不需要网络或密钥。
要重新向 Directions API 请求路线(例如,通过编辑 JSON 来更改站点):
export GOOGLE_MAPS_API_KEY=...
python datos/generador.py --regenerar-rutas使用测试客户端运行服务器
服务器本身不是交互式的:它从 stdin 读取 JSON,向 stdout 写入 JSON。为了查看它的运行情况,我编写了 cliente_prueba.py,它作为子进程启动服务器,进行握手,列出工具,并允许调用它们,打印每个消息在双向传输中的原始内容。
python cliente_prueba.py # interactivo
python cliente_prueba.py --demo # las herramientas de texto y tres casos de error, de corrido在交互模式下,输入工具编号,回答其参数,然后查看请求、响应和结果。它还接受 ping 和 lista。
服务器也可以手动测试:
printf '{"jsonrpc":"2.0","id":1,"method":"ping"}\n' | python -m servidor.main服务器日志输出到 stderr;使用 --verbose 还会打印每个进出消息。
连接到 Claude Desktop
编辑 Claude Desktop 的配置文件:
macOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.json
并使用仓库的绝对路径添加服务器:
{
"mcpServers": {
"flota": {
"command": "/ruta/absoluta/mcp-local-redes/.venv/bin/python",
"args": ["/ruta/absoluta/mcp-local-redes/servidor/main.py"]
}
}
}在 Windows 上,command 是 C:\\ruta\\mcp-local-redes\\.venv\\Scripts\\python.exe。如果要使用 Google 地理编码,在 "flota" 中添加 "env": {"GOOGLE_MAPS_API_KEY": "..."}。
重启 Claude Desktop 后,七个工具就会出现,可以用自然语言提问。数据库在相对于仓库的 datos/flota.db 中查找;可以通过 MCP_FLOTA_DB 变量更改。
工具
工具 | 回答的问题 | 参数 | 返回 |
| P-123BCD 在哪里? |
| 地址、坐标、速度、航向、发动机状态和最后报告时间 |
| 哪些车辆已停止超过 30 分钟? |
| 每辆车的车牌、位置、停止时间以及发动机是否开启 |
| 给我 P-456DEF 昨天的行程 |
| 公里数、出发和返回时间、站点(数量、时长、最长的)、最高和平均速度、信号中断 |
| 在地图上显示 P-456DEF 昨天的行程 |
| 带有 OpenStreetMap 轨迹的 PNG 图像、起点、终点、带时长的站点和地理围栏 |
| 这周有超速吗? |
| 按类型和车辆计数,以及最严重事件的详细信息 |
| P-456DEF 今天进入 CEDIS 了吗? |
| 是否进入,进入和离开时间以及每次访问的停留分钟数 |
| 这个月哪辆车行驶里程最长? |
| 按里程排名的车辆,含运营天数和日均里程 |
日期格式为 AAAA-MM-DD。警报类型:exceso_velocidad、parada_prolongada、perdida_senal、geocerca_entrada、geocerca_salida。
合成数据集中定义的地理围栏:CEDIS Zona 12、Bodega Villa Nueva、Bodega Mixco、CD Zona 18、Bodega Carretera a El Salvador 和 Centro Histórico。verificar_geocerca 接受完整名称或部分名称(“cedis”、“mixco”)。
没有任何工具返回原始数据。 15 辆车每 15 秒报告一次,持续 7 天,就是数十万行;将它们发送给模型既不可行也没有必要。每个工具在 SQL 或 pandas 中聚合,并返回计算结果。每个响应的最大行数是 servidor/registro.py 中的常量 MAX_FILAS = 200,并且有一个测试为每个工具验证这一点。
行程地图
mapa_recorrido 是唯一返回文本以外内容的工具:其结果包含两个内容块,一个带有摘要的 text 和一个带有 base64 PNG 的 image,Claude Desktop 直接在聊天中显示。地图使用 matplotlib 绘制;背景瓦片使用 urllib 从 OpenStreetMap 下载,如果没有网络,则在纯色背景上绘制轨迹。

问题示例
P-123BCD 现在在哪里?
有没有停了超过一个小时的车?
给我 P-456DEF 昨天的行程摘要。
在地图上显示 P-789GHJ 昨天去过哪里。
P-234KLM 周一停了几次,最长的一次在哪里?
这周有超速吗?哪辆车最多?
过去七天哪辆车信号丢失了?
P-456DEF 昨天进入 CEDIS 了吗?什么时间,待了多久?
这周哪辆车行驶里程最长?
周一到周五整个车队跑了多少公里?
测试
python -m pytest tests -v两组:
tests/test_protocolo.py:正确的握手、在initialize之前拒绝方法、格式错误的 JSON →-32700、无效请求 →-32600、不存在的方法 →-32601、无效参数 →-32602、通知不产生响应、响应的id与请求的id匹配、响应永远不会同时带有result和error,以及通过 stdio 实际启动进程并在 EOF 时干净关闭。tests/test_herramientas.py:每个工具针对在临时目录中使用固定种子生成的数据集进行测试,对照生成器故意注入的事件(我留下的停止车辆、长时间停车、信号中断、超速)进行验证,以及业务错误,并确保没有响应超过MAX_FILAS。
协议实现
所有涉及协议的内容都是使用 sys、json 和 logging 手工编写的。pandas、shapely、geopy 和 matplotlib 是业务逻辑;requests 仅由数据生成器使用。
servidor/main.py,传输层。 逐行读取 stdin,将每个响应写入 stdout 并跟\n和flush()。所有日志都输出到 stderr,因为 stdout 是协议通道,多一个字节就会破坏它。在 EOF 时关闭数据库并以代码 0 退出。servidor/jsonrpc.py,JSON-RPC 2.0。 解析和验证每个消息,通过id键的存在来区分请求和通知(而不是通过其值,因为null是有效的id),并使用标准代码-32700、-32600、-32601、-32602和-32603构建响应和错误。servidor/protocolo.py,MCP。 使用状态机(NUEVA→INICIALIZANDO→LISTA)进行初始化握手:在收到notifications/initialized之前,除initialize或ping之外的任何方法都会被拒绝。版本协商:如果客户端请求的版本受支持,我返回该版本;否则返回我支持的最新版本。tools/list、tools/call和ping。我不处理的通知会被静默忽略,因为响应通知会破坏客户端。servidor/registro.py。 工具列表及其inputSchema,并针对它验证参数(类型、required、enum)。MAX_FILAS在这里定义。
我决定将 JSON-RPC 与 MCP 分开,因为它们是协议的两个不同层次:JSON-RPC 定义消息的形式,MCP 定义存在哪些方法以及它们的顺序。将它们分开让我能够测试没有会话时的消息验证,以及没有 stdin 时的状态机。
我最注意的区别在于 tools/call:如果工具不存在或参数不符合模式,那就是协议错误,并以 JSON-RPC error 返回,错误码为 -32602;如果工具存在并运行,但结果是业务失败(车牌不存在、日期没有数据),则以 result 返回,带有 isError: true 和一条可读的消息,以便模型能够向用户解释。
一次真实会话的完整跟踪,包含每条消息的确切 JSON,位于 docs/protocolo.md。
仓库结构
mcp-local-redes/
├── servidor/
│ ├── main.py # punto de entrada, bucle de stdio
│ ├── jsonrpc.py # construcción y validación de mensajes JSON-RPC 2.0
│ ├── protocolo.py # handshake, máquina de estados, despacho de métodos
│ ├── registro.py # registro de herramientas, validación de argumentos, MAX_FILAS
│ └── herramientas/
│ ├── comun.py # consultas compartidas
│ ├── geocodificacion.py
│ ├── posicion.py
│ ├── detenidas.py
│ ├── recorrido.py
│ ├── mapa.py
│ ├── alertas.py
│ ├── geocercas.py
│ └── kilometraje.py
├── datos/
│ ├── generador.py # set sintético
│ ├── esquema.sql
│ └── rutas/ # polilíneas guardadas para modo offline
├── cliente_prueba.py
├── tests/
│ ├── test_protocolo.py
│ └── test_herramientas.py
├── docs/
│ └── protocolo.md
└── requirements.txtThis server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceMCP tool server providing SQLite database access for AI agents.MIT
- FlicenseNot gradedqualityCmaintenanceMCP server enabling natural-language querying of SQLite databases via schema discovery, GraphRAG retrieval, and safely guarded read-only SQL execution.
- FlicenseNot gradedqualityBmaintenanceMCP server for AI-powered roadside assistance case management, exposing SQLite-backed tools for querying case counts, statuses, and summaries through natural language via Gemini function calling.
- AlicenseAqualityAmaintenanceMCP server for chatting with physical-world data from robotics, drones, automotive, and IoT sources using natural language. It generates auditable SQL queries over Apache Arrow/DuckDB to let you analyze, summarize, and build data pipelines.18393Apache 2.0
Related MCP Connectors
GibsonAI MCP server: manage your databases with natural language
MCP server exposing the Backtest360 engine API as tools for AI agents.
MCP server for managing Prisma Postgres.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/FerAHMz/mcp-local-redes'
If you have feedback or need assistance with the MCP directory API, please join our Discord server