# 服务发现

> LMSP 客户端如何通过手动配置、探测本机地址和可选的 mDNS/DNS-SD 找到本地服务，以及如何读取 Well-Known 服务描述文档。

Source: https://madeforagent.com/zh-cn/lmsp/1.0/discovery/
Language: zh-CN
Specification: LMSP 1.0 (draft)
Updated: 2026-11-08

## 3.1 发现方式 {#methods}

客户端按以下顺序尝试各种方式来发现服务：

1. **手动配置。** 用户直接输入服务地址。
2. **探测本机约定地址。** 客户端向候选地址请求 `/.well-known/lmsp.json`。约定的候选地址是 `http://127.0.0.1:17832` 和 `http://[::1]:17832`。
3. **局域网发现。** 客户端通过 mDNS 与 DNS-SD 浏览 `_lmsp._tcp.local.`。

某种方式只要得到一份有效的服务描述（[第 3.3 节](https://madeforagent.com/zh-cn/lmsp/1.0/discovery.md#service-description)）即告成功。用户手动输入的地址始终优先于自动发现的地址。

## 3.2 局域网广播 {#mdns}

DNS-SD 使用 PTR 记录查找服务实例，通过 SRV 记录获取主机与端口，通过 TXT 记录携带少量附加信息；与 mDNS 结合可实现零配置的局域网发现（[RFC 6763](https://www.rfc-editor.org/rfc/rfc6763)、[RFC 6762](https://www.rfc-editor.org/rfc/rfc6762)）。

示例广播：

```text
Service type:   _lmsp._tcp.local.
Instance name:  Workstation LMSP._lmsp._tcp.local.

TXT record:
  v=1
  id=svc_01J8M4Y8K6M5E6K6Z7Z0Y8W3M2
  path=/
  auth=pairing
```

| TXT 键 | 含义 |
| --- | --- |
| `v` | 广播格式的版本。LMSP 1.0 为 `1`。 |
| `id` | 服务 id，与服务描述中的 `service.id` 相同。 |
| `path` | 服务根路径的 URL 路径。客户端请求 `{path}.well-known/lmsp.json`。 |
| `auth` | 认证提示。`pairing` 表示客户端必须先配对才能使用服务。 |

规则：

- 服务**不得**在 TXT 记录中放置访问令牌、模型清单、文件路径或其他敏感信息。该记录只负责找到服务，真实能力**必须**从服务描述中读取。
- 服务**必须**默认关闭 mDNS 广播，仅在用户明确开启“局域网发现”时才启用。仅在本机使用的服务无需广播。
- 在局域网中广播的服务需要监听回环地址之外的地址，而这只有在局域网模式下才被允许（[第 10.5 节](https://madeforagent.com/zh-cn/lmsp/1.0/security.md#lan)）。
- 客户端**必须**把 TXT 记录视为未经验证的提示，并通过读取服务描述来确认服务。

## 3.3 服务描述文档 {#service-description}

```http
GET /.well-known/lmsp.json
```

服务**必须**以 `application/json` 提供该文档，且不要求认证。

```json
{
  "protocol": "lmsp",
  "versions": ["1.0"],
  "service": {
    "id": "svc_01J8M4Y8K6M5E6K6Z7Z0Y8W3M2",
    "name": "Workstation Local Models",
    "version": "1.0.0"
  },
  "interfaces": [
    {
      "kind": "lmsp",
      "version": "1.0",
      "url": "http://127.0.0.1:17832/lmsp/v1",
      "transport": "http+json"
    },
    {
      "kind": "openai",
      "version": "responses+chat",
      "url": "http://127.0.0.1:17832/v1",
      "transport": "http+json"
    }
  ],
  "capabilities": {
    "streaming": true,
    "tasks": true,
    "event_resume": true,
    "conversations": true,
    "model_management": true
  },
  "authentication": {
    "type": "bearer",
    "pairing_url": "http://127.0.0.1:17832/lmsp/v1/pair"
  },
  "openapi": "http://127.0.0.1:17832/lmsp/v1/openapi.json"
}
```

| 字段 | 含义 |
| --- | --- |
| `protocol` | 始终为 `"lmsp"`。 |
| `versions` | 服务支持的 LMSP 版本，格式为 `major.minor` 字符串。 |
| `service` | `id` 是稳定的服务标识符，`name` 是显示名称，`version` 是服务软件的版本。 |
| `interfaces` | 所提供的接口。每项包含 `kind`（`lmsp`、`openai` 或 `ollama`）、`version`、基础 `url` 和 `transport`。对 `openai` 而言，`version` 表示所提供的 API 系列，例如 `responses+chat`。 |
| `capabilities` | 布尔型功能标志，见下表。 |
| `authentication` | `type` 为 `bearer`。`pairing_url` 是客户端用配对码换取令牌的地址（[第 10.2 节](https://madeforagent.com/zh-cn/lmsp/1.0/security.md#pairing)）。 |
| `openapi` | 描述 LMSP 接口的 OpenAPI 文档的 URL。 |

能力标志：

| 标志 | 含义 |
| --- | --- |
| `streaming` | 响应可以通过服务器发送事件流式返回。在 1.0 中**必须**为 `true`。 |
| `tasks` | 提供异步任务。在 1.0 中**必须**为 `true`。 |
| `event_resume` | 任务事件流可以订阅和恢复（`GET /lmsp/v1/tasks/{task_id}/events`）。 |
| `conversations` | 服务保存响应，因此 `store` 和 `previous_response_id` 可用，`GET /lmsp/v1/responses/{response_id}` 也可用。 |
| `model_management` | 可以显式加载和卸载模型。 |

## 3.4 客户端行为 {#client-behavior}

客户端：

- **应**从 `versions` 中选择自己也支持的最高版本。
- **应**根据自己所能使用的协议，从 `interfaces` 中选择 LMSP 接口或 OpenAI 接口。
- **必须**只使用服务描述中声明的能力，缺失的标志视为 `false`。
- **必须**忽略不认识的扩展字段。
- **应**定期或在请求失败时重新读取服务描述，而不是永久缓存。
- **应**用 `service.id` 而不是地址来记住所选的服务，因为地址可能变化。
