@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 +16 -9
- package/bin/kdna-mcp.mjs +21 -51
- package/package.json +6 -3
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,
|
|
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
|
|
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
|
|
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
|
|
23
|
-
| `kdna.available-local` | List local
|
|
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
|
|
80
|
-
| `
|
|
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
|
|
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
|
-
|
|
13
|
-
|
|
12
|
+
detectContainerFormat,
|
|
13
|
+
inspect,
|
|
14
|
+
load,
|
|
14
15
|
matchDomain,
|
|
15
16
|
planLoad,
|
|
16
|
-
|
|
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
|
|
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
|
|
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) === '
|
|
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) === '
|
|
157
|
-
const inspection =
|
|
158
|
-
const validation =
|
|
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: '
|
|
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 (
|
|
249
|
-
return textResult(
|
|
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 (
|
|
253
|
-
return textResult(
|
|
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 (
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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.
|
|
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": "
|
|
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
|
}
|