LocalAI Assistant 管理员 MCP 服务器:REST 端点、MCP 工具与 Skill 提示词的三层契约设计与接入指南
【免费下载链接】LocalAILocalAI is the open-source AI engine. Run any model - LLMs, vision, voice, image, video - on any hardware. No GPU required.项目地址: https://gitcode.com/GitHub_Trending/lo/LocalAI
LocalAI 的localai_assistant管理面把「安装模型、管理后端、编辑模型配置、查看系统状态」这类管理员日常操作封装为 MCP(Model Context Protocol)工具,让管理员可以通过自然语言聊天或标准 stdio MCP 客户端驱动本地 LocalAI 实例完成运维任务。本文以仓库内契约文档 .agents/localai-assistant-mcp.md 为主体,结合pkg/mcp/localaitools/的源码实现,完整讲解该功能的双运行模式、REST / MCP / Skill 三层同步架构、新增端点与技能配方的操作清单,以及防止魔法字符串漂移的编码规范,读者可据此独立扩展该管理面。
功能总览:把 LocalAI 管理面变成可对话的 MCP 工具集
pkg/mcp/localaitools/是一个公开的 Go 包,它把 LocalAI 的管理/运维能力(admin surface)封装成一个 MCP Server。它在项目中有两种截然不同的使用方式:
- 进程内模式(in-process):当管理员在聊天会话中携带
metadata.localai_assistant=true元数据发起请求时,聊天处理器会向 LLM 注入一个驻留内存的 MCP Server。它通过net.Pipe()配对的进程内传输通道通信,不经过任何 HTTP 回环(loopback)。LLM 因此可以在对话中直接安装模型、管理后端、编辑配置。 - 独立模式(standalone):通过
local-ai mcp-server --target=…子命令启动,同一个 MCP Server 以stdio方式对外提供服务,内部通过 HTTP 与一个远程 LocalAI 实例通信,可被 Claude Desktop、Cursor、mcphost 等宿主接入。
两种模式共享全部工具定义与 Skill 提示词,唯一差别是LocalAIClient接口的实现:进程内使用 inproc/client.go(直接调用服务),独立模式使用 httpapi/client.go(调用 REST 接口)。
从源码看,二者的组装点是一致的:NewServer(client, opts)接收任意LocalAIClient实现与选项,注册 12 组工具后返回统一类型,见 server.go:
func NewServer(client LocalAIClient, opts Options) *mcp.Server { // name 缺省为 localai-admin,version 缺省为 internal.PrintableVersion() srv := mcp.NewServer(&mcp.Implementation{Name: name, Version: version}, &mcp.ServerOptions{ Instructions: SystemPrompt(opts), // 把嵌入的 markdown 提示词组装成系统提示 }) registerModelTools(srv, client, opts) registerAliasTools(srv, client, opts) registerBackendTools(srv, client, opts) registerConfigTools(srv, client, opts) // …registerSystemTools / registerSchedulingTools / registerStateTools … return srv }Options中DisableMutating会跳过所有改变服务状态的工具(供--read-only形态的 CLI 使用);ServerName/ServerVersion可覆盖 MCP 对外通告的Implementation.Name与版本号。
三条必须保持同步的层级
当开发者改动 LocalAI 的管理面时,文档强调有三层必须始终对齐,缺一不可:
- REST 端点:位于 core/http/endpoints/localai/(目录下按功能拆分
gallery.go、import_model.go、edit_model.go、toggle_model.go、pin_model.go、nodes.go、branding.go等),并在 core/http/routes/ 的localai.go中由auth.RequireAdmin()统一做管理员鉴权保护。 - MCP 工具注册:位于
pkg/mcp/localaitools/tools_*.go,同时需要在 client.go 的LocalAIClient接口上增加对应方法,并在inproc 与 httpapi 两个 client中分别实现。 - Skill 提示词:位于 pkg/mcp/localaitools/prompts/skills/ 的 markdown,它教会 LLM 在什么场景下调用新工具、先向用户询问什么、出错时如何处理。
若只发布 REST 端点而遗漏第 2、3 层,对话式管理员将永远看不到该功能——这正是该契约文档存在的根本原因。仓库根目录的 AGENTS.md 也明确要求:每个适合对话管理的管理端点都必须同步暴露为 MCP 工具,并通过TestToolHTTPRouteMappingComplete这类路由映射测试来防止 REST 与 MCP 之间漂移。
新增管理端点的完整核对清单
契约文档为「新增一个管理端点」给出了逐项清单,下面按层展开说明,并结合源码标注落点:
1. REST 端点
- 在
core/http/endpoints/localai/*.go中实现端点,并在core/http/routes/localai.go中登记,确保挂上auth.RequireAdmin()守卫。项目在 core/http/auth/permissions.go 中定义了FeatureLocalAIAssistant = "localai_assistant"特性开关,说明该管理面与鉴权体系深度绑定。
2.LocalAIClient接口方法
- 在 pkg/mcp/localaitools/client.go 的接口上新增覆盖该操作的方法。该接口定义了全部 40+ 方法,按领域分组并带注释,例如模型/画廊域(
GallerySearch、ListInstalledModels、InstallModel、ImportModelURI…)、后端域(ListBackends、InstallBackend、UpgradeBackend…)、调度域(ListScheduling、SetScheduling…)。接口注释同时揭示了实现约定:凡是仓库其它地方已有同型(如config.Gallery、gallery.Metadata、schema.KnownBackend、vram.EstimateResult、modeladmin.Action/Capability),直接复用而非另建平行 DTO,从而让 LLM 可见的线上格式与 LocalAI 其余部分天然一致。
3. DTO 定义
- 在 dto.go 中增补带 JSON tag 的 DTO,绝不直接暴露底层 service 的原始类型(例如后端列表刻意使用轻量的
localaitools.Backend而不是带RunFile、Metadata等文件系统路径的gallery.SystemBackend,避免 LLM 看到不应看到的路径信息)。
4. 双客户端实现
- inproc/client.go 直接调用服务层(如
galleryop.GalleryService、config.ModelConfigLoader、modeladmin),不走 HTTP 回环。 - httpapi/client.go 通过 REST 端点调用。
- 两者的对齐由 parity_test.go 保证输出等价性。
5. 工具注册与安全联动
- 在对应的
pkg/mcp/localaitools/tools_*.go中注册工具;变更类工具的描述里必须引用安全规则 1。 - 若工具会修改状态,确保
Options{DisableMutating: true}能跳过它(参照tools_models.go中的模式)。mutatingToolNames是覆盖安全提示词的工具名单,位于 tools.go。
6. Skill 提示词
- 在
pkg/mcp/localaitools/prompts/skills/下新增或更新配方文件。提示词必须指导 LLM:何时调用该工具、先向用户确认什么、出错时如何处理。
7. 测试
- server_test.go 把工具名加入
expectedFullCatalog;只读工具还要加入expectedReadOnlyCatalog。 - 把工具分发加入
TestEachToolDispatchesToClient。 - httpapi/client_test.go 覆盖新的 HTTP 路径。
新增 Skill 配方(不涉及新工具)
有时只是想教 LLM 用既有工具组合出新的行为模式,此时无需任何 Go 改动:直接在 pkg/mcp/localaitools/prompts/skills/ 放一个 markdown 文件即可。该目录通过//go:embed prompts/*.md prompts/skills/*.md在编译期嵌入(见 prompts.go),并由 SystemPrompt 以字典序确定性遍历拼装进系统提示词,每个文件前自动追加<!-- file: … -->与# section: <basename>头,便于追溯 LLM 引用了哪份配方。
配方的既定约定:
- 文件名形如
<动词>_<名词>.md,例如install_chat_model.md、upgrade_backend.md、manage_router_corpus.md。 - 首行必须是
# Skill: <Title Case 描述>。 - 步骤必须编号,并用反引号引用精确的工具名。
- 若配方会修改状态,提醒 LLM 先与用户确认。
以 install_chat_model.md 为例,它完整示范了标准工作流:先gallery_search→ 编号列出候选(含名称、画廊、简介、许可证)→ 等用户挑选 → 复述安装动作并等待确认 → 确认后调用install_model(variant留空则自动按机器可用引擎与内存选择最大可运行版本,仅当用户点名某个具体构建时才传值)→ 用返回的 job id 轮询get_job_status→ 成功后依次调用reload_models与list_installed_models验证 → 告知用户模型名即 chat completions 的model字段取值。
仓库当前内置的配方还包括edit_model_config.md、import_model_from_uri.md、configure_branding.md、manage_distributed_scheduling.md、system_status.md、upgrade_backend.md等,覆盖配置编辑、URI 导入、品牌配置、分布式调度、系统体检与后端升级。
工具全貌:只读与变更两类
pkg/mcp/localaitools/tools.go是全部Tool*名称常量的唯一事实来源。当前注册工具涵盖模型/画廊、别名、后端、配置、系统、调度、状态(启用/固定)、品牌、音色库、用量统计、PII 过滤与中间件/router 等 12 类。按是否修改服务器状态可分为两组:
只读工具:gallery_search、list_installed_models、list_galleries、get_job_status、get_model_config、list_backends、list_known_backends、system_info、list_nodes、list_scheduling、get_scheduling、vram_estimate、get_branding、get_usage_stats、get_pii_events、get_middleware_status、get_router_decisions、get_router_corpus_stats、list_aliases、list_voice_profiles。
变更类工具(均需按安全规则 1 先确认):install_model、import_model_uri、delete_model、edit_model_config、reload_models、load_model、install_backend、upgrade_backend、toggle_model_state、toggle_model_pinned、set_branding、set_alias、seed_router_corpus、clear_router_corpus、create_voice_profile、delete_voice_profile、set_node_vram_budget、set_scheduling、delete_scheduling。
这些工具各自的用途描述可在 prompts/20_tools.md 中查看——它是面向 LLM 的精选工具目录(tools/list仍会暴露每项完整输入 schema)。例如import_model_uri支持 HuggingFace / OCI / http(s) / file:// 等任意 URI,若多个后端适用会返回ambiguous_backend,需带上backend_preference再次调用消歧;load_model可预载模型消除冷启动,对实时流水线模型会一次性载入 VAD、转写、LLM、TTS 等全部子模型。
其中list_installed_models的过滤标签使用强类型 capability.go 的Capability:chat、completion、embeddings、image、tts、transcript、rerank、vad,空值表示不过滤。这些常量会进入工具 DTO 的 jsonschema enum,是公开 API 变更——新增取值会直接改变 LLM 在tools/list时看到的合法列表。
编码规范:对抗魔法字符串漂移
文档明确指出,这些规范源自第一次审计中暴露的魔法字面量漂移问题,目的是防止回归:
- 工具名一律引用 tools.go 的
Tool*常量。注册、测试目录(server_test.go的expectedFullCatalog/expectedReadOnlyCatalog)、分发表都引用常量;唯一例外是prompts/下嵌入的 markdown 无法引用 Go 常量而保留裸字符串,并由TestPromptsContainSafetyAnchors校验其与确认规则的持续对齐。 - 启用/固定类操作使用
modeladmin.Action类型(位于core/services/modeladmin):一律ActionEnable/ActionDisable/ActionPin/ActionUnpin,禁止裸写"enable"/"pin"。 - 能力标签使用
localaitools.Capability:ListInstalledModels接收强类型参数,inproc的 switch 只接受规范值——"embed"与"embedding"不是别名,只有CapabilityEmbeddings合法。 - HTTP 错误判断用
errors.Is(err, ErrHTTPNotFound),不做err.Error()子串匹配。*HTTPError类型携带StatusCode与Body;新增哨兵错误应扩展类型体系而非重拾字符串匹配。 - inproc client 向
GalleryService.ModelGalleryChannel/BackendGalleryChannel发送时,必须在select中监听ctx.Done()(见inproc.sendModelOp/sendBackendOp),否则被取消的聊天补全会让 goroutine 泄漏。 - 模型配置 YAML 落盘必须走
modeladmin.writeFileAtomic(临时文件 +os.Rename)。裸os.WriteFile在进程崩溃时截断文件,会损坏模型配置。 - MCP Server 生命周期:每个完成初始化的持有者(holder)必须用
signals.RegisterGracefulTerminationHandler注册Close();独立的mcp-serverCLI 则用signal.NotifyContext响应 SIGINT/SIGTERM,给在途调用排空的机会(见下文 CLI 源码)。
文件地图:去哪看、改什么
契约文档给出了逐文件的导航图,结合仓库实际布局梳理如下:
pkg/mcp/localaitools/ client.go # LocalAIClient 接口 + DTO 注册 dto.go # 双实现共享的 JSON-tagged DTO server.go # NewServer(client, opts) —— 注册全部工具 tools.go # Tool* 名称常量(唯一事实来源)+ mutatingToolNames capability.go # Capability 类型与常量 tools_models.go # gallery_search、install_model、import_model_uri … tools_backends.go # 后端安装/升级/列举 tools_config.go # get_model_config、edit_model_config tools_system.go # system_info、list_nodes 等系统域 tools_state.go # toggle_model_state / toggle_model_pinned tools_aliases.go # set_alias / list_aliases tools_branding.go # get_branding / set_branding tools_scheduling.go # 调度配置读写 tools_voice_profiles.go# 音色库管理 tools_usage.go # get_usage_stats tools_pii.go # get_pii_events tools_middleware.go # get_middleware_status / router 相关 prompts.go # //go:embed 加载器 + SystemPrompt(opts) prompts/00_role.md # LLM 角色设定 prompts/10_safety.md # 安全规则(改动需极其谨慎) prompts/20_tools.md # 精选工具目录(一行式描述) prompts/skills/*.md # 技能配方 inproc/client.go # 进程内 LocalAIClient(直连服务) httpapi/client.go # REST LocalAIClient(standalone CLI / 远程) parity_test.go # inproc 与 httpapi 输出等价性 server_test.go # 工具目录 / 分发断言 prompts_test.go # 系统提示词与安全锚点一致性 core/http/endpoints/mcp/ localai_assistant.go # 进程级持有者 LocalAIAssistantHolder + LocalToolExecutor core/cli/mcp_server.go # local-ai mcp-server 子命令关于进程内持有者,localai_assistant.go 的注释给出了设计取舍:采用进程级单例持有者而非每请求临时接线,是因为 MCP Server 本身跨请求无状态——每次请求重建net.Pipe()配对并重列工具纯属浪费;同一个进程内LocalToolExecutor可为所有 assistant 会话服务,无需 NATS、子进程或合成的管理员凭据。持有者在 Application 启动时初始化一次,之后可并发使用;若初始化失败或DisableLocalAIAssistant生效,Executor()返回空执行器且HasTools()为 false,聊天处理器将其视为「功能不可用」。此外,core/config/runtime_settings.go 暴露localai_assistant_enabled运行时设置,可在 UI 上开关该能力。
为什么是两套 client 而非一种
契约文档解释得很直白:进程内 MCP Server 跑在承载聊天的同一 LocalAI 二进制内部,如果走 HTTP 回环,会有三重代价——
- 服务端需要为自己铸造一个合成的 admin API key才能完成自认证;
- 每次工具分发都要双重序列化(marshal / unmarshal);
- 会丢失进程内通道——例如
GalleryService.ModelGalleryChannel上流式的安装进度,HTTP 形态拿不到。
因此进程内模式用inproc.Client(服务直连);独立 stdio CLI 面对的是远程 LocalAI,HTTP 是唯一选项,所以用httpapi.Client。二者实现同一个LocalAIClient接口,等价性由 parity_test.go 守护。inproc.Client还刻意保持「薄适配」:分发与持久化交给底层服务(GalleryService本身具备分布式感知、ModelConfigLoader管理磁盘 YAML),该层只做 MCP DTO 与服务签名的翻译,其依赖注入式字段(如可选的StatsRecorder、PIIRedactor、RouterDecisions)允许工具在相应能力未启用时优雅返回「unavailable」错误而非崩溃。
为什么用提示词强制确认,而非代码闸门
设计上选择了 KISS:每类变更工具都由一条安全规则(prompts/10_safety.md 规则 1)约束——LLM 在调用前必须先以自然语言复述「用哪个工具、改哪个目标、带什么参数」,并等待用户下一轮显式确认(Yes/do it/go ahead/proceed都算确认,其余一律不算)。代码里不存在plan_*/apply_*两段式设计。
因此,新增变更工具时不要在 Go 里追加逐工具的确认逻辑,而应把新工具名登记进10_safety.md,让 LLM 知道它受确认规则约束。tools.go中的mutatingToolNames名单与该 markdown 由prompts_test.go机械地保持同步。
该安全提示词还包含另外四条硬规则,共同约束 LLM 行为:规则 2 要求变更前先消歧(画廊候选多个、同名多版本、后端多变体时,编号列出请用户挑选);规则 3 要求工具报错时逐字转述错误(放进围栏代码块,不重试不转述);规则 4 禁止凭空捏造标识符——模型名、画廊名、后端名、job id 必须来自本会话之前的工具结果;规则 5 规定轮询get_job_status的终止条件为processed: true、cancelled: true或轮询满 30 次,三者先到为准,并始终向用户总结最终结果。
独立部署:local-ai mcp-server子命令
core/cli/mcp_server.go 实现了独立运行形态,其命令行参数与环境变量如下:
| 参数 | 环境变量 | 默认值 | 说明 |
|---|---|---|---|
--target | LOCALAI_MCP_TARGET | http://localhost:8080 | 目标 LocalAI 的 base URL |
--api-key | LOCALAI_API_KEY | — | 访问目标 LocalAI 的 Bearer API key |
--read-only | — | false | 跳过全部变更工具(install/delete/edit/upgrade 等),只读浏览远程状态 |
该命令用httpapi.New(target, apiKey)构造 client,ReadOnly映射为Options.DisableMutating,随后以mcp.StdioTransport运行:宿主导管(如 Claude Desktop、Cursor、mcphost)通过 JSON-RPC 与进程的 stdin/stdout 通信,因此 stdout 被明确定义为「神圣通道」——除协议数据外的任何输出都会污染传输,其余日志必须走 stderr。进程用signal.NotifyContext监听 SIGINT/SIGTERM,保证 Ctrl-C 或kill -TERM时srv.Run有机会排空在途调用后再退出。
分布式模式下的边界
契约文档明确划定了分布式模式的边界:内存态 MCP Server 只运行在主节点(head node)上——因为聊天处理器就在主节点。inproc.Client包装的服务本身已具备分布式感知:GalleryService会与 worker 协调安装,ListNodes读取的是 NATS 填充的节点注册表。MCP 工具不做 NATS 路由,管理面整体驻留主节点,没有例外。这也意味着list_nodes、list_scheduling、set_scheduling、set_node_vram_budget等联邦工具在单进程部署下会被 inproc 客户端报告为不可用或仅主节点有意义。
小结
LocalAI Assistant 管理面是一条「REST 端点 → MCP 工具 → Skill 提示词」三层咬合的生产级链路:以pkg/mcp/localaitools/单一公开包承载全部工具与提示词,以LocalAIClient接口隔离进程内直连与远程 HTTP 两种实现,以常量、强类型与机械性测试对抗三层之间的漂移,并以「提示词内确认规则」替代繁琐的代码闸门。无论是作为人类开发者扩展新管理能力,还是作为 Agent 理解该 MCP Server 的接入方式,.agents/localai-assistant-mcp.md 与上述源码文件共同构成了完整、可验证的权威参考。
【免费下载链接】LocalAILocalAI is the open-source AI engine. Run any model - LLMs, vision, voice, image, video - on any hardware. No GPU required.项目地址: https://gitcode.com/GitHub_Trending/lo/LocalAI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考