Describe the capabilities of a framework, SDK, or project in static JSON. Let applications and AI hosts discover them, read usage guidance, check applicability, and resolve declared dependencies.
v2.0.1 · Node.js / TypeScript · MCP stdio / Streamable HTTP · Apache-2.0
A Provider is a named source of capability definitions. Its author declares capabilities and relationships; the application or model selects existing IDs; Graph validates access, supplied facts, and dependencies. The caller decides how to use the result. Graph does not execute capabilities, infer project facts, or provide an agent runtime, RAG, or embeddings.
Documentation (中文) · Examples · Issues · Changelog
Choose a path · Install · First graph · Configuration · Local documents and URL references · Integrations · Results and errors · Contributing
| Your task | Start here | Who selects capabilities? |
|---|---|---|
| Your application already knows the capability IDs | Run the SDK example below | Your application; no model required |
| An existing AI client should discover capabilities | Codex setup or MCP | The host's current model |
| Your application needs natural-language selection | Model matching with an explicit ModelExecutor | The model configured by your application |
For an existing Agent or MCP Server, use the tool adapter or server integration. Skill, Plugin, and transport options are explained in the complete integration guide.
Requires Node.js >=22.12.0 and ESM. Use .mjs, or set "type":"module" for JavaScript; compile TypeScript as ESM/NodeNext.
In a new application directory (Bash or PowerShell):
mkdir graph-demo
cd graph-demo
npm init -y
npm install @devcodex/capability-graphStatic queries need no model SDK, account, or API key. Installation does not start a service or register anything in your AI host. Run the local CLI with npx --no-install capability-graph; installation options include a global CLI install.
This example describes an HTTP endpoint that requires a request schema. It resolves that declared dependency without running a web framework. Create two files in graph-demo/; no knowledge directory is needed.
First, save this as provider.json:
{
"providerId": "acme",
"name": "Acme HTTP",
"version": "1.0.0",
"capabilities": [
{
"id": "route",
"name": "Validated HTTP endpoint",
"description": "Create an HTTP endpoint with request validation.",
"whenToUse": "The application needs an HTTP endpoint with validated input.",
"usage": { "instructions": "Declare the request schema, then register the endpoint." },
"relations": { "requires": ["schema"] }
},
{ "id": "schema", "name": "Request schema", "description": "Declare a schema to validate request input." }
]
}The Provider requires providerId, name, version, and capabilities. Each capability requires id, name, and description; guidance and relationships are optional. Here, route explicitly requires schema, which has no usage instructions.
Next, save static.mjs beside that JSON. It locates the declaration relative to the script, prints the complete result, reports Graph errors on stderr, and closes Graph even after failure.
import { CapabilityGraph, GraphError } from '@devcodex/capability-graph';
import { fileURLToPath } from 'node:url';
let graph;
try {
graph = await CapabilityGraph.open({
providers: [{ file: fileURLToPath(new URL('./provider.json', import.meta.url)) }]
});
const result = await graph.resolve({ ids: ['acme::route'] });
console.log(JSON.stringify(result, null, 2));
} catch (error) {
if (!(error instanceof GraphError)) throw error;
console.error(JSON.stringify({ code: error.code, phase: error.phase, nextAction: error.nextAction }));
process.exitCode = 1;
} finally {
await graph?.close();
}node static.mjs| Option / call | Meaning and default |
|---|---|
providers: [{file: ...}] |
Load this Provider JSON. A source accepts either file or an inline definition. |
Omitted allowedProviders |
Allow all loaded Providers; [] allows none. |
resolve.ids: ['acme::route'] |
Select this root by its full providerId::capabilityId. |
Omitted relations |
Expand the transitive requires dependencies; [] keeps only roots. |
Omitted context |
Supply no facts. This example declares no conditions, so its nodes are applicable. |
Omitted model |
Static methods work without invoking a model. |
The result contains acme::route and its dependency acme::schema. Their origin values explain why each appears. unresolved: [] and meta.completeness: 'complete' mean the requested graph was fully resolved; they do not mean the capabilities were executed.
Complete output from node static.mjs
{
"selected": [
"acme::route"
],
"nodes": [
{
"capability": {
"id": "acme::route",
"providerId": "acme",
"name": "Validated HTTP endpoint",
"description": "Create an HTTP endpoint with request validation.",
"whenToUse": "The application needs an HTTP endpoint with validated input.",
"usage": {
"instructions": "Declare the request schema, then register the endpoint."
},
"relations": {
"parents": [],
"specializes": [],
"related": [],
"requires": [
"acme::schema"
]
},
"documents": []
},
"origin": "selected",
"applicability": {
"status": "applicable",
"missingFacts": [],
"invalidFacts": [],
"failedConditions": []
}
},
{
"capability": {
"id": "acme::schema",
"providerId": "acme",
"name": "Request schema",
"description": "Declare a schema to validate request input.",
"relations": {
"parents": [],
"specializes": [],
"related": [],
"requires": []
},
"documents": []
},
"origin": "dependency",
"applicability": {
"status": "applicable",
"missingFacts": [],
"invalidFacts": [],
"failedConditions": []
}
}
],
"edges": [
{
"from": "acme::route",
"type": "requires",
"to": "acme::schema"
}
],
"unresolved": [],
"meta": {
"revision": "sha256:be714d7dc37a706d72862ee2d485c7c127a9f684d8124796f4fc65e786a5285a",
"completeness": "complete",
"warnings": []
}
}To display the returned guidance, insert this result-consumption fragment after const result = ... in the script above. It adds human-readable lines to stdout:
for (const { capability, origin, applicability } of result.nodes) {
console.log(`${capability.id} (${origin}, ${applicability.status})`);
console.log(capability.usage?.instructions ?? 'No usage instructions provided');
}Use optional chaining because usage instructions are optional. The quick start also includes a complete inline version; the result guide explains the remaining fields and how to act on them.
The SDK takes options in CapabilityGraph.open(options). The CLI reads a separate JSON file. For the same example, save graph.config.json:
{
"schemaVersion": 1,
"providers": [{ "file": "./provider.json" }],
"allowedProviders": ["acme"]
}npx --no-install capability-graph validate --file provider.json
npx --no-install capability-graph doctor --config graph.config.jsonvalidate checks the declaration; doctor checks the graph configuration and tool conditions without calling a model. CLI file paths are relative to the config file. SDK relative paths use the working directory at open; the first example avoids that difference with import.meta.url.
| Setting | When to use it |
|---|---|
providers[].file |
Load a JSON file; its directory is the root for local documents. |
providers[].definition |
Load an in-memory Provider value. |
providers[].documentRoot |
Give an inline Provider a trusted local document root. File-based Providers already have one. |
allowedProviders |
Set the fixed access boundary. Queries can narrow it, never expand it. |
model |
Supply an explicit executor for match(); omit it for static queries. |
limits |
Set byte budgets; see defaults and validation. |
For multiple Providers, add one source per Provider and use full IDs for cross-source relationships, such as shared::schema. See the runnable dependency example.
Associate a capability with a local guide or an HTTPS reference using capabilities[].documents[].source. Capability definitions remain in the Provider's capabilities array. The following is a capability fragment, to be placed inside that array:
{
"id": "route",
"name": "Validated HTTP endpoint",
"description": "Create an HTTP endpoint with request validation.",
"documents": [
{ "id": "guide", "source": { "type": "file", "path": "knowledge/route.md" } },
{ "id": "upstream", "source": { "type": "url", "url": "https://example.com/guide" } }
]
}For a file-based Provider, create knowledge/route.md beside provider.json. The relative path stays inside that Provider's directory. For an inline Provider, set documentRoot to the trusted directory containing knowledge/.
After opening this Provider, use these fragments inside its existing Graph lifecycle:
const doc = await graph.readDocument({ capabilityId: 'acme::route', documentId: 'guide' });
console.log(doc.text); // Local UTF-8 text; doc also includes byteLength and contentHash.
const details = await graph.getCapabilities({ ids: ['acme::route'] });
const upstream = details.items[0].capability.documents.find(doc => doc.id === 'upstream');
console.log(upstream.source.url); // https://example.com/guidereadDocument accepts declared capability/document IDs rather than arbitrary paths. URL documents are references only: Graph does not fetch them; calling readDocument on upstream throws CG_DOCUMENT_REFERENCE_ONLY. See the complete local-file and URL walkthrough for all files, parameters, and error handling.
| Goal | Guide and prerequisites |
|---|---|
| Use the current Codex model | Codex setup: explicitly register stdio MCP and the official discovery Skill; no second model or API key. |
| Connect another MCP client | stdio and HTTP: client-owned stdio process or a separately running HTTP service. |
| Use a private remote service | HTTPS and Bearer client: reachable TLS endpoint, compatible protocol, and authentication accepted by the host. |
| Distribute a Skill / Plugin | Plugin installation: an explicit host installation and a running HTTP service for the Plugin. |
| Match using OpenAI | OpenAI: optional openai@7.39.0, API credentials, and Responses with strict Structured Outputs. |
| Match using a separate Codex process | Independent Codex CLI: Linux, bubblewrap, pinned native CLI 0.163.0, trusted runtime and dedicated login directory. |
| Reuse an existing model | Custom executor: adapt the application's model call to the public ModelExecutor contract. |
The independent Codex adapter rejects native Windows. WSL and Linux containers must satisfy the CLI, bubblewrap, and mount-permission requirements in their actual environment; a Linux platform value alone does not establish support. This restriction does not apply to static SDK queries or Codex using its current model over MCP.
MCP stdio supports 2026-07-28 and 2025-06-18; HTTP supports 2026-07-28 only. There are five static tools by default. The sixth, capability_graph_match, requires an explicitly configured executor and enableMatch: true. See compatibility.
| Check | Meaning / next action |
|---|---|
match.status |
matched, no_match, ambiguous, or needs_context; clarify the task or supply facts when needed. |
node.applicability.status |
applicable, inapplicable, or unknown; inspect missing/invalid facts and failed conditions. |
meta.completeness |
complete or partial; inspect unresolved before relying on dependencies. |
meta.revision |
Identifies the authorized static view. It does not hash document bodies; use contentHash for a document read. |
GraphError |
Inspect code, phase, and optional nextAction. An access, authentication, or timeout failure is an error, not no_match. |
A model match can still contain unknown applicability or unresolved dependencies. For recovery steps, use errors and troubleshooting. Complete method signatures, types, and defaults are in the SDK, types, and configuration references.
Copy the package's examples into a directory that does not already exist. In Bash:
cp -R node_modules/@devcodex/capability-graph/examples ./examplesIn PowerShell:
Copy-Item -LiteralPath node_modules/@devcodex/capability-graph/examples -Destination ./examples -RecurseThen run either shell's Node commands from the project root:
node examples/minimal/static.mjs
node examples/multi-provider/main.mjs
node examples/vextjs/resolve.mjsThese three commands use static declarations and need no model. The example index lists expected results and optional dependencies, including the separate VextJS framework check.
Bug reports and focused pull requests are welcome on GitHub. Include the package and Node versions, a minimal declaration/configuration, the command you ran, and the observed error when reporting a problem.
For source setup, checks, and documentation development, see the contributor guide. Documentation source lives in website/content; the website and its build output are separate from the SDK package.