@aikdna/kdna-mcp-server 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,11 +1,15 @@
1
1
  # KDNA MCP Server
2
2
 
3
3
  `@aikdna/kdna-mcp-server` exposes KDNA as MCP tools so agent runtimes can use
4
- `.kdna` assets without learning the container internals. Stdio-only, minimal footprint.
4
+ packaged `.kdna` assets without learning the container internals. Stdio-only,
5
+ minimal footprint. The npm package source lives in this repository under
6
+ [`mcp-server/`](./); there is no separate public `kdna-mcp-server` repository.
5
7
 
6
8
  ## Why MCP instead of the kdna-loader skill?
7
9
 
8
- The `kdna-loader` skill teaches an agent the full KDNA protocol (7-part routing, silent loading, boundary respect). The MCP server provides a lower-level bridge — use it when you need programmatic access to `.kdna` assets through structured tool calls rather than letting the agent drive the CLI.
10
+ The `kdna-loader` skill teaches an agent the full toolchain-only protocol:
11
+ routing, planning, Capsule loading, application, and boundary respect. The MCP
12
+ server provides a lower-level bridge for structured tool calls.
9
13
 
10
14
  | Approach | Best for |
11
15
  |----------|----------|
@@ -16,13 +20,12 @@ The `kdna-loader` skill teaches an agent the full KDNA protocol (7-part routing,
16
20
 
17
21
  | Tool | Purpose | Input | Output |
18
22
  |------|---------|-------|--------|
19
- | `kdna.inspect` | Inspect a v1 `.kdna` asset or legacy asset | File path | Structured metadata |
23
+ | `kdna.inspect` | Inspect a `.kdna` asset | File path | Structured metadata |
20
24
  | `kdna.verify` | Verify asset integrity state | File path | Pass/fail with reasons |
21
25
  | `kdna.plan-load` | Return Core LoadPlan before loading | File path, optional password or entitlement state | LoadPlan JSON |
22
- | `kdna.load` | Load and render a `.kdna` profile for agent context | File path, optional profile | Prompt-mode text or raw JSON |
23
- | `kdna.available-local` | List local v1 `.kdna` assets without registry dependency | Root directory | Local v1 asset inventory |
26
+ | `kdna.load` | Load an authorized `.kdna` profile | File path, optional profile | Runtime Capsule |
27
+ | `kdna.available-local` | List local `.kdna` assets | Root directory | Local asset inventory |
24
28
  | `kdna.match` | Rank candidate assets for a task string | Task description | Scored list with fit signals |
25
- | `kdna.available` | Legacy registry compatibility only | `domains.json` path | Legacy domain list |
26
29
 
27
30
  ## Install & Run
28
31
 
@@ -76,10 +79,14 @@ kdna-mcp
76
79
 
77
80
  | Variable | Purpose |
78
81
  |----------|---------|
79
- | `KDNA_ASSET_DIR` | Root directory for `kdna.available-local` (default: `~/.kdna/assets`) |
80
- | `KDNA_REGISTRY_FILE` | Legacy path to `domains.json` for `kdna.available` |
82
+ | `KDNA_ASSET_DIR` | Root directory for `kdna.available-local`; overrides the default |
83
+ | `KDNA_PACKAGE_DIR` | Package directory fallback for `kdna.available-local` |
81
84
  | `KDNA_DATA_ROOT` | Override KDNA data directory (default: `~/.kdna`) |
82
85
 
86
+ When no override is provided, `kdna.available-local` scans
87
+ `~/.kdna/packages`. Explicit local roots can be provided when a user keeps
88
+ assets elsewhere.
89
+
83
90
  ## Local Development
84
91
 
85
92
  ```bash
@@ -96,7 +103,7 @@ node bin/kdna-mcp.mjs
96
103
  # Client calls kdna.plan-load { assetPath: "./dist/writing.kdna" }
97
104
  # Or kdna.plan-load { assetPath: "./dist/writing.kdna", entitlementStatus: "active" }
98
105
  # Client calls kdna.load { assetPath: "./dist/writing.kdna", profile: "compact" }
99
- # Agent injects prompt text into system context
106
+ # Agent consumes only the returned Runtime Capsule context
100
107
  ```
101
108
 
102
109
  ## Relationship to kdna-loader skill
package/bin/kdna-mcp.mjs CHANGED
@@ -9,21 +9,13 @@ import { createRequire } from 'node:module';
9
9
  const require = createRequire(import.meta.url);
10
10
  const packageInfo = require('../package.json');
11
11
  const {
12
- inspectKDNA,
13
- loadKDNA,
12
+ detectContainerFormat,
13
+ inspect,
14
+ load,
14
15
  matchDomain,
15
16
  planLoad,
16
- renderForAgent,
17
- verifyAsset,
17
+ validate,
18
18
  } = require('@aikdna/kdna-core');
19
- const {
20
- detectContainerFormat,
21
- inspect: inspectV1,
22
- isV1SourceDir,
23
- loadAuthorized,
24
- loadV1,
25
- validate: validateV1,
26
- } = require('@aikdna/kdna-core/v1');
27
19
 
28
20
  const tools = [
29
21
  {
@@ -77,7 +69,7 @@ const tools = [
77
69
  },
78
70
  {
79
71
  name: 'kdna.available-local',
80
- description: 'List local v1 .kdna files without using a registry.',
72
+ description: 'List local .kdna files without using a registry.',
81
73
  inputSchema: {
82
74
  type: 'object',
83
75
  properties: {
@@ -121,11 +113,11 @@ function textResult(value) {
121
113
  };
122
114
  }
123
115
 
124
- function isV1Asset(assetPath) {
116
+ function isKdnaAsset(assetPath) {
125
117
  if (!assetPath) return false;
126
118
  try {
127
119
  if (!fs.existsSync(assetPath) || !fs.statSync(assetPath).isFile()) return false;
128
- return assetPath.endsWith('.kdna') && detectContainerFormat(assetPath) === 'v1';
120
+ return assetPath.endsWith('.kdna') && detectContainerFormat(assetPath) === 'kdna';
129
121
  } catch {
130
122
  return false;
131
123
  }
@@ -153,12 +145,12 @@ function findLocalAssets(root = defaultAssetRoot(), maxDepth = 3) {
153
145
  const full = path.join(dir, entry.name);
154
146
  if (entry.isDirectory()) {
155
147
  visit(full, depth + 1);
156
- } else if (entry.isFile() && entry.name.endsWith('.kdna') && detectContainerFormat(full) === 'v1') {
157
- const inspection = inspectV1(full);
158
- const validation = validateV1(full);
148
+ } else if (entry.isFile() && entry.name.endsWith('.kdna') && detectContainerFormat(full) === 'kdna') {
149
+ const inspection = inspect(full);
150
+ const validation = validate(full);
159
151
  found.push({
160
152
  path: full,
161
- kind: 'v1_container',
153
+ kind: 'kdna_asset',
162
154
  asset_id: inspection.asset_id,
163
155
  title: inspection.title,
164
156
  version: inspection.version,
@@ -226,48 +218,26 @@ function planLoadThroughCoreOrCli(args = {}) {
226
218
  return runCliPlanLoad(args);
227
219
  }
228
220
 
229
- function loadV1Authorized(assetPath, options) {
230
- if (typeof loadAuthorized === 'function') return loadAuthorized(assetPath, options);
231
- const plan = planLoadThroughCoreOrCli({
232
- assetPath,
233
- hasPassword: options.hasPassword,
234
- entitlementStatus: options.entitlement && options.entitlement.status,
235
- });
236
- if (plan.can_load_now !== true) {
237
- const error = new Error(
238
- `LoadPlan denied loading: state=${plan.state || 'invalid'} required_action=${plan.required_action || 'block'}`,
239
- );
240
- error.code = (plan.issues && plan.issues[0] && plan.issues[0].code) || 'KDNA_LOAD_NOT_AUTHORIZED';
241
- throw error;
242
- }
243
- return loadV1(assetPath, options);
244
- }
245
-
246
221
  async function callTool(name, args = {}) {
247
222
  if (name === 'kdna.inspect') {
248
- if (isV1Asset(args.assetPath)) return textResult(inspectV1(args.assetPath));
249
- return textResult(await inspectKDNA(args.assetPath, { verify: args.verify !== false }));
223
+ if (!isKdnaAsset(args.assetPath)) throw new Error('assetPath is not a current KDNA asset');
224
+ return textResult(inspect(args.assetPath, { verify: args.verify !== false }));
250
225
  }
251
226
  if (name === 'kdna.verify') {
252
- if (isV1Asset(args.assetPath)) return textResult(validateV1(args.assetPath));
253
- return textResult(await verifyAsset(args.assetPath, {
227
+ if (!isKdnaAsset(args.assetPath)) throw new Error('assetPath is not a current KDNA asset');
228
+ return textResult(validate(args.assetPath, {
254
229
  asset_digest: args.asset_digest,
255
230
  content_digest: args.content_digest,
256
231
  requireSignature: Boolean(args.requireSignature),
257
232
  }));
258
233
  }
259
234
  if (name === 'kdna.load') {
260
- if (isV1Asset(args.assetPath)) {
261
- const profile = args.profile || 'compact';
262
- const loaded = loadV1Authorized(args.assetPath, { profile, as: 'json' });
263
- const prompt = profile === 'index' ? null : loadV1Authorized(args.assetPath, { profile, as: 'prompt' }).text;
264
- return textResult({ ...loaded, context: prompt });
265
- }
266
- const loaded = await loadKDNA(args.assetPath, { profile: args.profile || 'compact', input: args.input || '' });
267
- const context = args.profile === 'index'
268
- ? null
269
- : await renderForAgent(args.assetPath, { profile: args.profile || 'compact', input: args.input || '' });
270
- return textResult({ ...loaded, context });
235
+ if (!isKdnaAsset(args.assetPath)) throw new Error('assetPath is not a current KDNA asset');
236
+ return textResult(load(args.assetPath, {
237
+ profile: args.profile || 'compact',
238
+ as: 'json',
239
+ input: args.input || '',
240
+ }));
271
241
  }
272
242
  if (name === 'kdna.plan-load') {
273
243
  return textResult(planLoadThroughCoreOrCli(args));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aikdna/kdna-mcp-server",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "MCP server for loading, inspecting, verifying, and matching KDNA .kdna assets.",
5
5
  "type": "module",
6
6
  "homepage": "https://aikdna.com",
@@ -30,14 +30,17 @@
30
30
  ],
31
31
  "scripts": {
32
32
  "test": "node --test test/*.test.mjs",
33
- "prepublishOnly": "npm test"
33
+ "prepublishOnly": "npm test && node ../scripts/release-check.js"
34
34
  },
35
35
  "license": "Apache-2.0",
36
36
  "dependencies": {
37
- "@aikdna/kdna-core": "^0.12.1",
37
+ "@aikdna/kdna-core": "0.15.12",
38
38
  "ajv": "^8.20.0",
39
39
  "ajv-formats": "^3.0.1"
40
40
  },
41
+ "devDependencies": {
42
+ "cbor-x": "^1.6.4"
43
+ },
41
44
  "engines": {
42
45
  "node": ">=18"
43
46
  }