For AI agents: the complete documentation index is available at https://devcodex-labs.github.io/capability-graph/llms.txt, the full documentation bundle is available at https://devcodex-labs.github.io/capability-graph/llms-full.txt, and this page is available as Markdown at https://devcodex-labs.github.io/capability-graph/getting-started/first-provider.md.

创建第一个 Provider

可运行

最快跑通

已经完成安装时,先使用仓库中的完整受检示例目录。它包含本页全部定义、知识文件和 discover.mjs,不需要把下方片段重新拼装。

把该目录保存到项目后运行:

node first-provider/discover.mjs

成功输出包含 catalog: ["route", "route.http"]selected: ["route.http"]document: "routing-guide"specification: "SPEC-01"。下文逐项解释定义、显式选择与读取,再说明如何放进常见的 providers/acme-http 项目布局。

一个文件权威 Provider 至少需要 provider.json 和一个 *.capability.json。Core 会递归收集能力文件,但会按精确目录名跳过 .gitnode_modulesdistdist-testcoverage.cache.tmp

前置条件与目标

完成安装,在接入项目中创建下面的文件。本页从零定义两个静态能力,最后读取一份知识文档;无需启动 HTTP 服务、MCP 或数据库。

capability-graph-demo/
├── package.json
├── discover.mjs
└── providers/acme-http/
    ├── provider.json
    ├── PROVIDER.md
    ├── route.capability.json
    ├── route-http.capability.json
    └── knowledge/routing.md

前四个定义/规范文件都放在 providers/acme-http/;知识放在其 knowledge/ 子目录。

1. 定义 Provider

provider.json
{
  "providerId": "acme.http",
  "name": "Acme HTTP",
  "version": "0.1.0",
  "specification": {
    "specificationId": "acme.http.conventions",
    "version": "1",
    "appliesTo": {
      "software": "acme-http",
      "versionRange": ">=0.1.0 <1.0.0"
    },
    "documents": [{ "kind": "document", "knowledgeId": "SPEC-01", "role": "specification", "locale": "en",
      "locator": { "type": "relative-file", "path": "PROVIDER.md" } }]
  }
}
字段必填作用及本例选择
providerId稳定机器身份;acme.http 必须与加载配置一致
name展示名称,改名不会自动生成新身份
versionProvider 定义的版本标签,不属于能力二元身份,也不代替 Static Revision
specificationProvider 全局使用规则的元数据与文档映射;不需要规范时可整体省略
specificationId声明规范时是规范自身的稳定身份,本例与 Provider ID 分开
specification.version声明规范时是规范版本,可独立于 Provider 版本演进
appliesTo适用性元数据,内部 softwareversionRangeconditions 均可选
appliesTo.software本例规范面向 acme-http 软件
appliesTo.versionRange适用版本描述;Core 不探测项目版本或执行范围匹配
documents声明规范时是非空 Document 列表;每篇 role 固定为 specification,可按 knowledgeIdlocale 选择

documents 是 Provider Specification 的声明映射。Core 可在 readSpecification() 被显式调用时读取正文,但不会在目录、详情或 Selection 中自动读取,也不会把正文当作指令执行。

创建引用的 PROVIDER.md

PROVIDER.md
# Acme HTTP Provider

- 修改已有路由前,通过已配置的 Runtime 查询确认项目中的实例。
- 路由注册与请求校验是不同能力,先按任务选择。
- 只读取已选 Capability 关联的 Knowledge。

Core 校验文档引用与 Provider 范围。接入层负责判断适用版本、决定是否读取正文并如何向 Agent 投递。

Specification 写跨能力的使用规则;Capability 描述可以独立发现和选择的能力;Knowledge 写执行某个已选能力时才需要的详细知识。把所有规则塞进 description 会让每次目录查询重复传输长正文。

2. 定义主能力

route.capability.json
{
  "capabilityId": "route",
  "name": "Routing",
  "description": "Define how requests reach application handlers.",
  "whenToUse": "Choose a routing capability before changing an entrypoint.",
  "distinction": "A static capability family, not registered runtime routes."
}
字段必填如何填写
capabilityIdProvider 内稳定身份;route 表达路由能力族
name人类可读名称
description回答“这是什么能力、解决什么问题”
whenToUse回答“什么任务下应该考虑它”,帮助 Agent 选择
distinction与容易混淆的能力或 Runtime 实例区分;存在歧义时应填写

再添加一个细分能力:

route-http.capability.json
{
  "capabilityId": "route.http",
  "name": "HTTP routing",
  "description": "Expose a request handler through an HTTP route.",
  "whenToUse": "The application accepts HTTP requests.",
  "distinction": "Describes an HTTP routing capability, not a live route instance.",
  "parents": ["route"],
  "knowledge": [
    {
      "kind": "document",
      "knowledgeId": "routing-guide",
      "role": "guide",
      "locale": "en",
      "locator": { "type": "relative-file", "path": "knowledge/routing.md" }
    }
  ]
}

关系端点必须位于同一 Provider。parentsspecializesrequires 分别无环;Core 不从命名层级推导关系。

这里用 parents: ["route"] 表达 HTTP 路由属于路由能力族。route.http 中的点号只是命名约定,省略 parents 就没有父子关系。只有“保留另一能力核心语义的更具体变体”才使用 specializesrelated 只提示显式关联;只有确实需要另一能力作为必要上下文时才写 requires。本例没有依赖边,因此 Selection 不会把父能力自动加入闭包。

Knowledge 字段必填含义
knowledge本能力关联的知识引用;没有知识时可省略
kind引用内是document 表示可精确读取的一篇文档;Collection 成员也可按成员 ID 显式读取
knowledgeId引用内是本例为 routing-guide;读取用 Provider、已选能力身份和知识 ID 定位,不能当作全局地址
roleDocument 内是用途标签,本例为 guide;过滤与发现均使用它
localeBCP 47 语言标签,本例为 en;按规范值精确匹配,不做自动语言回退
locatorDocument 内是知识来源描述,不是正文
locator.typerelative-file 使用内置本地 Reader
locator.path相对该文件 Authority 的 rootDir,不是相对 process.cwd()

同时创建 knowledge/routing.md。本地知识正文不进入 Static Revision;读取结果通过独立 contentId 标识。

knowledge/routing.md
# HTTP Routing Guide

Register the route in the host framework, then expose its static capability through the Provider-owned discovery API or MCP integration. Runtime route instances remain an Adapter concern.

修改绑定或 locator 会改变静态定义;只修改正文则用新的 contentId 区分内容,避免把整篇文档混进静态图哈希。

3. 打开 Provider

以下配置片段展示各项含义;第 4 步给出包含初始化和关闭的完整 discover.mjs

import { CapabilityGraph } from '@devcodex/capability-graph';

const graph = await CapabilityGraph.open({
  hostAllowedProviders: ['acme.http'],
  integrationEnabledProviders: ['acme.http'],
  providers: [{
    providerId: 'acme.http',
    authority: { kind: 'file', rootDir: '/absolute/path/to/acme-provider' }
  }]
});

有效启用范围中的每个 Provider 必须恰有一个 Authority。缺少来源会得到 CG_CONFIG_INCOMPLETE,不会被解释为空目录。

配置项本例中的作用
hostAllowedProviders宿主允许访问 acme.http
integrationEnabledProviders本次集成启用 acme.http,不能越过宿主允许集合
providers为有效启用 Provider 配置唯一 Authority;允许集合不等于来源地址
providers[].providerId标识这条来源配置属于谁,须与 provider.json 相符
authority.kindfile 表示文件权威;数据库模式需要另配 Adapter
authority.rootDirProvider 根目录,包含上面的定义和知识文件

有效范围为 hostAllowedProviders ∩ integrationEnabledProviders ∩ requestProviderScope,未提供请求范围时使用前两者的交集。这两个数组分别服务宿主策略与当前集成开关,不能用它们代替 providers 的加载配置。

4. 执行渐进查询

在项目根创建 discover.mjs。目录返回两个能力的摘要;本例的调用方随后明确选择 route.http,Core 不会自行判断用户意图。Provider 想首轮只展示主能力时,需在接入层定义入口策略,见下一页。

discover.mjs
import { fileURLToPath } from 'node:url';
import { CapabilityGraph } from '@devcodex/capability-graph';

const graph = await CapabilityGraph.open({
  hostAllowedProviders: ['acme.http'],
  integrationEnabledProviders: ['acme.http'],
  providers: [{
    providerId: 'acme.http',
    authority: {
      kind: 'file',
      rootDir: fileURLToPath(new URL('./providers/acme-http/', import.meta.url))
    }
  }]
});

try {
  const provider = graph.forProvider('acme.http');
  const catalog = await provider.listCatalog({ limit: 20 });
  const requiredStaticRevision = catalog.meta.staticRevision;
  const detail = await provider.getCapabilities(['route.http'], { requiredStaticRevision });
  const neighbors = await provider.getNeighbors('route.http', { requiredStaticRevision });
  const selection = await provider.resolveSelection({ selected: ['route.http'], requiredStaticRevision });
  const documents = await provider.readDocuments({
    selected: selection.resolved.map(({ capabilityId }) => capabilityId),
    knowledgeIds: ['routing-guide'],
    roles: ['guide'],
    locales: ['en'],
    requiredStaticRevision
  });
  const specification = await provider.readSpecification({ knowledgeIds: ['SPEC-01'], requiredStaticRevision });

  if (!detail.results[0]?.ok || !documents.results[0]?.ok || !specification.results[0]?.ok) {
    throw new Error(JSON.stringify({ detail, documents, specification }));
  }
  console.log(JSON.stringify({
    catalog: catalog.items.map(({ id }) => id.capabilityId),
    detail: detail.results[0].value.id.capabilityId,
    parents: neighbors.groups.parents.items.map(({ id }) => id.capabilityId),
    selected: selection.resolved.map(({ capabilityId }) => capabilityId),
    document: documents.results[0].value.knowledgeId,
    specification: specification.results[0].value.knowledgeId,
    text: documents.results[0].value.text,
    completeness: catalog.meta.completeness
  }, null, 2));
} finally {
  await graph.close();
}
node discover.mjs

读取顺序是:

  1. listCatalog() 返回用途、使用时机和关键区别,用于第一轮选择。
  2. getCapabilities() 返回已选能力详情,关系摘要和知识引用有各自预算,不返回知识正文。
  3. getNeighbors() 只展开请求的关系组,不递归遍历整张图。
  4. resolveSelection() 从显式能力沿 requires 计算完整必要上下文闭包;不会沿 parentsrelated 扩张。
  5. readDocuments() 按知识 ID、用途和语言筛选后读取;readSpecification() 是独立的显式规范读取。

始终检查 meta.completenesswarningsnextCursor。部分结果不是全集;已知 ID 也可以直接查询详情,无需强制先列目录。

close() 释放 Core 持有的权威视图句柄,不取消已经交给 Adapter 的底层任务。

预期结果与排错

终端输出的精简投影如下,text 字段还会包含上面知识文件的完整正文:

{
  "catalog": ["route", "route.http"],
  "detail": "route.http",
  "parents": ["route"],
  "selected": ["route.http"],
  "document": "routing-guide",
  "specification": "SPEC-01",
  "completeness": "complete"
}

这证明定义可加载、显式父关系可查询、Selection 不误把父节点当依赖、文档及规范可按需读取。requiredStaticRevision 来自本次 Catalog,后续查询沿用它,不能填写 Provider 的 version

失败优先检查
CG_CONFIG_INCOMPLETE有效启用的 Provider 是否有唯一来源配置
CG_VALIDATION_FAILEDJSON 必填字段、Provider 身份、关系端点是否完整
文档逐项读取失败knowledge/routing.md 是否位于 Authority 根内且与 locator 一致
CG_REVISION_MISMATCH是否沿用了已退休修订;重新发现后再作选择

批量详情和知识返回 results,每项检查 ok;目录返回 items。其他情况见故障排查

下一步

把这组查询原语接入 Provider 自有 API,或直接查看 Provider 自有 MCP 的渐进发现流程。