mcpField
D
Drone CI/CD MCP Server

Drone CI/CD MCP Server

本地服务
github.com/yusiwen云与基础设施16 周前更新

关于 Drone CI/CD MCP Server

Drone CI/CD MCP Server 为 Drone CI/CD 实例提供 MCP 接口,供客户端查询仓库、构建和其他 CI/CD 信息。默认仅启用只读工具,写入及破坏性操作需显式开启,并支持带认证的 Streamable HTTP。

主要功能

  1. 01提供仓库、构建、计划任务等工具
  2. 02默认只注册只读工具
  3. 03显式开启写入和破坏性操作
  4. 04支持带 Bearer 认证的 Streamable HTTP
  5. 05校验输入并提供构建详情资源

适用场景

  1. 01查询仓库构建状态与日志
  2. 02检查 Drone 仓库和计划任务
  3. 03在明确启用写入后管理构建或密钥

MCP 相关

该服务向 MCP 客户端提供 Drone 仓库、构建、计划任务、密钥、用户和模板相关工具,并提供构建详情资源。支持 stdio 和需 Bearer 认证的 Streamable HTTP 传输。

配置

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

mcp.json · 通用配置
{
"mcpServers": {
"yusiwen-drone": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"ghcr.io/yusiwen/drone-mcp-server:2.0.0"
]
}
}
}
启动命令docker run -i --rm ghcr.io/yusiwen/drone-mcp-server:2.0.0

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
ocighcr.io/yusiwen/drone-mcp-server:2.0.0

工具

还没有获取到工具列表

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

详细介绍

Drone MCP 服务

用于与 Drone CI/CD 交互的模型上下文协议(MCP)服务。此服务提供工具和资源,可从你的 Drone 实例查询构建信息、代码仓库等内容。

功能

  • 45 个工具,涵盖代码仓库、构建、定时任务、密钥、用户和模板
  • 默认只读:仅在使用 --enable-write-tools 时才会注册 27 个写入和破坏性工具
  • 可流式传输的 HTTP 传输,强制使用 bearer 身份验证,并提供 DNS 重绑定防护和跨域防护
  • 已验证的输入:在将代码仓库标识符、名称、分支和构建编号传递给 Drone API 之前进行检查
  • 资源访问:通过 drone://builds/{owner}/{repo}/{build} 获取构建详情

安装

go mod tidy
go build -o drone-mcp-server .

配置

环境变量
变量必需说明
DRONE_SERVER是Drone 实例的基础 URL,例如 https://drone.example.com
DRONE_TOKEN是Drone 个人访问令牌,每次 Drone API 调用时都会通过 Authorization: Bearer <token> 发送
MCP_AUTH_TOKENHTTP 模式客户端必须提供的 bearer 令牌。未设置此令牌时,HTTP 模式将拒绝启动
MCP_AUTH_TOKEN_FILE否包含 bearer 令牌的文件(Docker/Kubernetes 密钥)。其优先级高于 MCP_AUTH_TOKEN
MCP_ENABLE_WRITE_TOOLS否设为 true 可注册写入和破坏性工具,等同于 --enable-write-tools
export DRONE_SERVER=https://drone.example.com
export DRONE_TOKEN=your_drone_token
export MCP_AUTH_TOKEN="$(openssl rand -base64 32)"   # HTTP mode only

授予最小范围的 Drone 权限,以满足所公开工具的需求。只读工具集只需要对代码仓库和构建拥有读取权限,而 delete_user、create_org_secret 或 chown_repo 等写入工具则需要管理员权限。

安全

此服务是一个特权代理:它能够访问的任何内容,与它通信的任何人也都能访问。默认设置正是基于这一点。

  • HTTP 模式默认拒绝启动。 如果未设置 MCP_AUTH_TOKEN(或 MCP_AUTH_TOKEN_FILE),服务将在 HTTP 模式下拒绝启动。--insecure-allow-anonymous 可绕过此限制,并会记录警告。
  • 默认只读。 除非明确请求写入权限,否则只会注册 18 个只读工具。每个工具都带有 MCP 提示 readOnlyHint、destructiveHint 和 idempotentHint 注释,以便客户端自行控制写入操作。
  • 可流式传输的 HTTP 传输。 MCP 规范已移除传统的 SSE 传输,且该传输在发布时没有 DNS 重绑定或跨域防护。此服务使用 Go SDK 的 StreamableHTTPHandler,会拒绝来自环回地址、但带有非环回 Host 请求头的请求,并拒绝跨站浏览器请求。
  • 可选允许列表。 --allowed-hosts 和 --allowed-origins 可在传输默认设置的基础上,添加明确的 Host 和 Origin 允许列表。
  • Bearer 令牌处理。 令牌通过 SHA-256 摘要以恒定时间进行比较,并会对来自同一地址的重复失败请求进行限流(每分钟 10 次,之后返回 429)。
  • 输入验证。 在将代码仓库标识符、密钥和模板名称、分支及构建编号传递给 Drone API 客户端之前进行验证,因为 drone-go 会通过字符串格式化来组装请求路径。
  • 日志卫生。 除非设置了 --trust-proxy,否则会忽略 X-Forwarded-For / X-Real-IP;同时会从记录的值中剔除控制字符,防止请求数据伪造日志行。
  • 有界请求。 请求正文设有大小上限(默认为 1 MiB),空闲会话会过期,并通过请求头读取超时和静默连接超时限制慢速客户端滥用。
部署检查清单
  1. 在服务器前端终止 TLS(该二进制使用纯 HTTP),切勿通过不可信网络发送 bearer token。
  2. 保持默认的 loopback 绑定并在前端设置反向代理,或绑定到只有 MCP 客户端能够访问的接口。
  3. 生成高熵的 MCP_AUTH_TOKEN(32+ 个随机字节),并将其作为平台密钥注入,而不是放在命令行中。
  4. 除非工作流需要,否则禁用写入工具。
  5. 仅当由你控制的代理会重写 X-Forwarded-For / X-Real-IP 时才设置 --trust-proxy;否则,日志中的客户端地址可能由攻击者控制。
  6. 在将服务暴露到 localhost 之外之前,请阅读 SECURITY.md。

用法

作为 MCP 服务

将该服务添加到 MCP 客户端配置中(例如 Claude Desktop):

{
  "mcpServers": {
    "drone": {
      "command": "/path/to/drone-mcp-server",
      "env": {
        "DRONE_SERVER": "https://drone.example.com",
        "DRONE_TOKEN": "your_token"
      }
    }
  }
}
直接运行

你可以直接运行该服务进行测试:

# Stdio mode (default) - for local MCP clients
./drone-mcp-server

# Streamable HTTP mode - requires MCP_AUTH_TOKEN
export MCP_AUTH_TOKEN="$(openssl rand -base64 32)"
./drone-mcp-server --http --host localhost --port 8080
传输模式
  1. Stdio 模式(默认):使用 MCP 协议通过 stdin/stdout 通信。适用于与 MCP 客户端进行本地集成;不会暴露到网络,也不需要身份验证。

  2. Streamable HTTP 模式(--http):单个端点,接受 POST(客户端到服务端)、GET(服务端到客户端流)和 DELETE(拆除会话)。

    **从早期版本升级:**旧的 --sse 标志和 SSE 传输已移除。请使用 --http,并使用支持 Streamable HTTP 的客户端。

    # Local only, read-only tools
    MCP_AUTH_TOKEN=... ./drone-mcp-server --http
    
    # Behind a TLS terminating proxy, write tools enabled
    MCP_AUTH_TOKEN=... ./drone-mcp-server --http --host 127.0.0.1 --port 8080 \
        --enable-write-tools --allowed-hosts mcp.example.com
    

    客户端默认连接到 http://localhost:8080/,并且必须发送以下标头:

    Authorization: Bearer <MCP_AUTH_TOKEN>
    

    **在反向代理后运行:**作为 DNS 重绑定保护的一部分,传输层会对 loopback 连接上的公共 Host 标头返回 403。你可以保留 loopback Host 标头(proxy_set_header Host 127.0.0.1:8080;),也可以通过单个标志将 Host 验证交由允许列表处理:

    ./drone-mcp-server --http --allowed-hosts mcp.example.com
    

    设置 --allowed-hosts 会取代 loopback 启发式检查,因此无需设置 --localhost-protection=false。如果显式传入 --localhost-protection=true,则会同时应用两项检查,并再次拒绝公共 Host。

命令行标志
标志默认值说明
--httpfalse使用 Streamable HTTP 传输
--host / --portlocalhost / 8080监听地址(仅限 HTTP 模式)
--path/MCP 端点路径(仅限 HTTP 模式)
--enable-write-toolsfalse注册 27 个写入和破坏性工具
--insecure-allow-anonymousfalse允许 HTTP 模式不设置 MCP_AUTH_TOKEN
--trust-proxyfalse信任来自反向代理的 X-Forwarded-For / X-Real-IP
--allowed-hosts(空)以逗号分隔的 Host 允许列表;取代 loopback 启发式检查
--allowed-origins(空)以逗号分隔的浏览器 Origin 允许列表
--localhost-protectiontrue拒绝携带非 loopback Host 标头的 loopback 连接;与 --allowed-hosts 一起显式设置可同时启用两项检查
--session-timeout30mMCP 会话空闲超时时间(仅限 HTTP 模式)
--max-request-bytes1048576MCP 请求正文的最大大小
--version打印版本信息并退出

可用工具

此服务实现了 45 个工具。在默认的只读模式下,仅注册 18 个只读工具;其余 27 个写入或破坏性工具需要启用 --enable-write-tools(或设置 MCP_ENABLE_WRITE_TOOLS=true)。

写入和破坏性工具: enable_repo、disable_repo、repair_repo、chown_repo、sync_repos、create_cron、delete_cron、execute_cron、create_secret、update_secret、delete_secret、create_org_secret、update_org_secret、delete_org_secret、create_user、update_user、delete_user、create_template、update_template、delete_template、restart_build、cancel_build、promote_build、rollback_build、approve_build、decline_build、create_build。

破坏性工具(删除、禁用、取消、提升、回滚和所有权变更)标记为 destructiveHint: true;其余写入工具标记为 destructiveHint: false。全部 18 个只读工具均标记为 readOnlyHint: true。

仓库管理
list_repos

列出 Drone 实例中的所有仓库。

get_repo

获取仓库详情。

参数:

  • owner(string):仓库所有者
  • repo(string):仓库名称
enable_repo

启用仓库。

参数:

  • owner(string):仓库所有者
  • repo(string):仓库名称
disable_repo

禁用仓库。

参数:

  • owner(string):仓库所有者
  • repo(string):仓库名称
repair_repo

修复仓库。

参数:

  • owner(string):仓库所有者
  • repo(string):仓库名称
chown_repo

更改仓库所有权。

参数:

  • owner(string):仓库所有者
  • repo(string):仓库名称
sync_repos

同步仓库列表。

list_incomplete

列出构建未完成的仓库。

构建管理
list_builds

列出特定仓库的构建。

参数:

  • owner(string):仓库所有者
  • repo(string):仓库名称
get_build

获取特定构建的详细信息。

参数:

  • owner(string):仓库所有者
  • repo(string):仓库名称
  • build(number):构建编号
get_build_last

获取仓库的最新构建(可按分支筛选)。

参数:

  • owner(string):仓库所有者
  • repo(string):仓库名称
  • branch(string,可选):分支名称
get_build_logs

获取特定构建阶段和步骤的日志。

参数:

  • owner(string):仓库所有者
  • repo(string):仓库名称
  • build(number):构建编号
  • stage(number):阶段编号
  • step(number):步骤编号
restart_build

重新启动构建(可选携带参数)。

参数:

  • owner(string):仓库所有者
  • repo(string):仓库名称
  • build(number):构建编号
  • params(object,可选):构建参数
cancel_build

取消正在运行的构建。

参数:

  • owner(string):仓库所有者
  • repo(string):仓库名称
  • build(number):构建编号
promote_build

将构建提升到目标环境。

参数:

  • owner(string):仓库所有者
  • repo(string):仓库名称
  • build(number):构建编号
  • target(string):目标环境
  • params(object,可选):提升参数
rollback_build

将部署回滚到之前的构建。

参数:

  • owner(string):仓库所有者
  • repo(string):仓库名称
  • build(number):构建编号
  • target(string):目标环境
  • params(object,可选):回滚参数
approve_build

批准构建阶段(用于受控部署)。

参数:

  • owner(string):仓库所有者
  • repo(string):仓库名称
  • build(number):构建编号
  • stage(number):阶段编号
decline_build

拒绝一个构建阶段(用于受门控的部署)。

参数:

  • owner (string):仓库所有者
  • repo (string):仓库名称
  • build (number):构建编号
  • stage (number):阶段编号
create_build

从提交或分支创建新构建。

参数:

  • owner (string):仓库所有者
  • repo (string):仓库名称
  • commit (string, optional):提交 SHA
  • branch (string, optional):分支名称
  • params (object, optional):构建参数
Cron 作业管理
list_crons

列出仓库的 Cron 作业。

参数:

  • owner (string):仓库所有者
  • repo (string):仓库名称
get_cron

获取 Cron 作业详情。

参数:

  • owner (string):仓库所有者
  • repo (string):仓库名称
  • cron (string):Cron 作业名称
create_cron

创建新的 Cron 作业。

参数:

  • owner (string):仓库所有者
  • repo (string):仓库名称
  • name (string):Cron 作业名称
  • expr (string):Cron 表达式
  • branch (string):分支名称
  • disable (boolean, optional):禁用 Cron 作业
delete_cron

删除 Cron 作业。

参数:

  • owner (string):仓库所有者
  • repo (string):仓库名称
  • cron (string):Cron 作业名称
execute_cron

立即执行 Cron 作业。

参数:

  • owner (string):仓库所有者
  • repo (string):仓库名称
  • cron (string):Cron 作业名称
密钥管理
list_secrets

列出仓库密钥。

参数:

  • owner (string):仓库所有者
  • repo (string):仓库名称
get_secret

获取仓库密钥详情。

参数:

  • owner (string):仓库所有者
  • repo (string):仓库名称
  • name (string):密钥名称
create_secret

创建仓库密钥。

参数:

  • owner (string):仓库所有者
  • repo (string):仓库名称
  • name (string):密钥名称
  • value (string):密钥值
  • pull_request (boolean, optional):允许在拉取请求中使用
  • pull_request_push (boolean, optional):允许在拉取请求推送事件中使用
update_secret

更新仓库密钥。

参数:

  • owner (string):仓库所有者
  • repo (string):仓库名称
  • name (string):密钥名称
  • value (string):密钥值
  • pull_request (boolean, optional):允许在拉取请求中使用
  • pull_request_push (boolean, optional):允许在拉取请求推送事件中使用
delete_secret

删除仓库密钥。

参数:

  • owner (string):仓库所有者
  • repo (string):仓库名称
  • name (string):密钥名称
组织密钥
list_org_secrets

列出组织密钥。

参数:

  • namespace (string):组织命名空间
get_org_secret

获取组织密钥详情。

参数:

  • namespace (string):组织命名空间
  • name (string):密钥名称
create_org_secret

创建组织密钥。

参数:

  • namespace (string):组织命名空间
  • name (string):密钥名称
  • value (string):密钥值
  • pull_request (boolean, optional):允许在拉取请求中使用
  • pull_request_push (boolean, optional):允许在拉取请求推送事件中使用
update_org_secret

更新组织密钥。

参数:

  • namespace (string):组织命名空间
  • name (string):密钥名称
  • value (string):密钥值
  • pull_request (boolean, optional):允许在拉取请求中使用
  • pull_request_push (boolean, optional):允许在拉取请求推送事件中使用
delete_org_secret

删除组织密钥。

参数:

  • namespace (string):组织命名空间
  • name (string):密钥名称
用户管理
get_self

获取当前已验证身份的用户。

list_users

列出所有用户。

get_user

获取用户详情。

参数:

  • login (string):用户登录名
create_user

创建新用户。

参数:

  • login(string):用户登录名
  • email(string,可选):用户邮箱
  • admin(boolean,可选):管理员权限
  • active(boolean,可选):是否启用
  • token(string,可选):用户令牌
update_user

更新用户。

参数:

  • login(string):用户登录名
  • admin(boolean,可选):管理员权限
  • active(boolean,可选):是否启用
delete_user

删除用户。

参数:

  • login(string):用户登录名
模板管理
list_templates

列出模板(可按命名空间筛选)。

参数:

  • namespace(string,可选):模板命名空间
get_template

获取模板详情和数据。

参数:

  • namespace(string):模板命名空间
  • name(string):模板名称
create_template

创建新模板。

参数:

  • namespace(string):模板命名空间
  • name(string):模板名称
  • data(string):模板数据(YAML)
update_template

更新模板。

参数:

  • namespace(string):模板命名空间
  • name(string):模板名称
  • data(string):模板数据(YAML)
delete_template

删除模板。

参数:

  • namespace(string):模板命名空间
  • name(string):模板名称

资源

构建详情资源

通过资源 URI 访问构建详情:drone://builds/{owner}/{repo}/{build}

示例:

Read resource: drone://builds/owner1/repo1/123

Docker

GitHub Container Registry 上提供多架构 Docker 镜像:

# Pull the latest image
docker pull ghcr.io/yusiwen/drone-mcp-server:latest

# Run in stdio mode (for local MCP clients)
docker run --rm -i \
           -e DRONE_SERVER=https://drone.example.com \
           -e DRONE_TOKEN=your_token \
           ghcr.io/yusiwen/drone-mcp-server

# Run in Streamable HTTP mode: read-only, authenticated, published on loopback
docker run --rm \
           -e DRONE_SERVER=https://drone.example.com \
           -e DRONE_TOKEN=your_token \
           -e MCP_AUTH_TOKEN=your_mcp_token \
           -p 127.0.0.1:8080:8080 \
           ghcr.io/yusiwen/drone-mcp-server --http --host 0.0.0.0

# Add --enable-write-tools when the workflow must mutate Drone
docker run --rm \
           -e DRONE_SERVER=https://drone.example.com \
           -e DRONE_TOKEN=your_token \
           -e MCP_AUTH_TOKEN=your_mcp_token \
           -p 127.0.0.1:8080:8080 \
           ghcr.io/yusiwen/drone-mcp-server --http --host 0.0.0.0 --enable-write-tools

在容器中必须使用 --host 0.0.0.0,发布的端口才能正常工作;这也正是必须提供 bearer token 的原因。除非前置了 TLS 终止代理,否则请将端口发布到 127.0.0.1。

优先使用挂载的密钥,而非环境变量:

docker run --rm \
           -v /run/secrets/mcp_token:/run/secrets/mcp_token:ro \
           -e MCP_AUTH_TOKEN_FILE=/run/secrets/mcp_token \
           -e DRONE_SERVER=https://drone.example.com \
           -e DRONE_TOKEN=your_token \
           -p 127.0.0.1:8080:8080 \
           ghcr.io/yusiwen/drone-mcp-server --http --host 0.0.0.0
Docker Compose 示例
services:
  drone-mcp-server:
    image: ghcr.io/yusiwen/drone-mcp-server:latest
    environment:
      DRONE_SERVER: https://drone.example.com
      DRONE_TOKEN: ${DRONE_TOKEN:?DRONE_TOKEN is required}
      MCP_AUTH_TOKEN: ${MCP_AUTH_TOKEN:?MCP_AUTH_TOKEN is required}
      # MCP_ENABLE_WRITE_TOOLS: "true"   # only when the workflow needs writes
    ports:
      - "127.0.0.1:8080:8080"
    command: ["--http", "--host", "0.0.0.0"]
    restart: unless-stopped
    read_only: true
    security_opt:
      - no-new-privileges:true

发布版本

GitHub Releases 提供适用于 Linux(x64、arm64)、macOS(x64、arm64)和 Windows(x64)的预构建二进制文件。

版本信息

二进制文件包含版本信息:

./drone-mcp-server --version

输出示例:

drone-mcp-server
Version: v1.0.0
Commit: abc123
Build date: 2024-01-01T00:00:00Z
Go version: go1.25.1

项目结构

.
├── main.go              # Entry point: CLI flags, transports, auth, HTTP hardening, tool registration
├── main_test.go         # Auth middleware, allowlists, log hygiene and tool registration tests
├── tool/                # Tool handlers module
│   ├── build.go         # Build-related tools (list_builds, get_build, restart_build, etc.)
│   ├── repo.go          # Repository-related tools (list_repos, enable_repo, disable_repo, etc.)
│   ├── resource.go      # Resource handling (drone://builds/...)
│   ├── cron.go          # Cron job management tools
│   ├── secret.go        # Secret management tools
│   ├── user.go          # User management tools
│   ├── template.go      # Template management tools
│   ├── validate.go      # Input validation helpers used by every tool
│   ├── validate_args.go # Validate implementation for each tool argument struct
│   └── validate_test.go # Validation tests, including injection attempts
├── SECURITY.md          # Threat model, deployment guidance and reporting process
├── server.json          # MCP registry metadata
├── test_env.sh          # Manual smoke test script (uses environment variables)
├── test_mcp.go          # In-memory MCP integration test (build tag: test)
└── README.md

开发

依赖项
构建
make build           # build with version info injected through ldflags
go build -o drone-mcp-server .
测试
make test                                  # unit tests
make test-race                             # with the race detector
make smoke                                 # end-to-end security smoke test
make vuln                                  # govulncheck (module + standard library)
go vet ./...

单元测试涵盖身份验证中间件(包括恒定时间比较和失败请求限流)、Host 策略、日志清理、客户端地址处理、输入验证以及只读工具注册。测试不需要 Drone 实例。

验证安全加固

scripts/security-smoke-test.sh 会在环回端口启动真实二进制文件,并验证关键行为,且不会联系任何 Drone 实例:

./scripts/security-smoke-test.sh          # builds the binary first
BIN=./drone-mcp-server ./scripts/security-smoke-test.sh

该脚本会检查 HTTP 模式是否会在缺少令牌时拒绝启动;缺失或错误的凭据、错误的 Content-Type、超大请求体、伪造的 Host 标头以及跨站点 Origin 是否分别被 401/401/415/413/403/403 拒绝;默认工具列表是否恰好包含 18 个只读工具,以及隐藏的写入工具是否无法调用;工具参数中的路径遍历是否会被拒绝;日志注入和 X-Forwarded-For 欺骗是否会被中和;--allowed-hosts 是否会取代环回地址判断机制;写入工具是否仅在启用 --enable-write-tools 时出现;--sse 是否已移除;以及服务器是否会在收到 SIGTERM 时关闭。首次遇到失败时,脚本会以非零状态退出。它也会在 CI 中运行,因此还可充当回归检查门禁。

在真实的 Drone 上手动验证

先使用只读令牌,并确认工具能正常响应:

export DRONE_SERVER=https://ci.example.com
export DRONE_TOKEN=<read-only token>
export MCP_AUTH_TOKEN="$(openssl rand -base64 32)"
./drone-mcp-server --http --host 127.0.0.1 --port 8080

然后将 MCP 客户端连接到该服务。Stdio 模式:

{
  "mcpServers": {
    "drone": {
      "command": "/path/to/drone-mcp-server",
      "env": { "DRONE_SERVER": "https://ci.example.com", "DRONE_TOKEN": "..." }
    }
  }
}

Streamable HTTP 模式(大多数客户端都支持 headers 块;也可以使用 npx @modelcontextprotocol/inspector,选择“Streamable HTTP”并添加相同的标头):

{
  "mcpServers": {
    "drone": {
      "url": "http://127.0.0.1:8080/",
      "headers": { "Authorization": "Bearer <MCP_AUTH_TOKEN>" }
    }
  }
}

建议在真实实例上进行以下检查:get_self(确认令牌有效)、list_repos、get_build_last、get_build_logs(在模型上下文中测试不可信内容),然后使用 owner: "../users" 调用 get_repo,确认验证会拒绝该请求。写入工具只能对临时仓库运行。

本地运行
export DRONE_SERVER=https://drone.example.com
export DRONE_TOKEN=...

# Read-only stdio server
./drone-mcp-server

# HTTP mode with authentication and write tools
MCP_AUTH_TOKEN=dev-token ./drone-mcp-server --http --enable-write-tools

许可证

MIT

评论

支持 Markdown

第一次接触 MCP?

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

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