@totemsdk/mcp-server 0.4.0 → 0.5.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/LICENSE +202 -21
- package/NOTICE +60 -0
- package/README.md +1 -1
- package/data/sdk-index.json +1 -1
- package/dist/composition.d.ts +77 -0
- package/dist/composition.js +375 -0
- package/dist/contracts.d.ts +18 -0
- package/dist/contracts.js +108 -0
- package/dist/indexer.js +82 -5
- package/dist/recipes.d.ts +15 -0
- package/dist/recipes.js +37 -0
- package/dist/resources.js +65 -6
- package/dist/tools.js +194 -57
- package/dist/types.d.ts +34 -0
- package/dist/workflow.d.ts +36 -0
- package/dist/workflow.js +200 -0
- package/package.json +6 -4
package/dist/indexer.js
CHANGED
|
@@ -72,6 +72,24 @@ function detectRepoRoot() {
|
|
|
72
72
|
}
|
|
73
73
|
return null;
|
|
74
74
|
}
|
|
75
|
+
/** Read the checked-out Git revision without spawning a process. */
|
|
76
|
+
function readGitRevision(root) {
|
|
77
|
+
try {
|
|
78
|
+
const git = path.join(root, '.git');
|
|
79
|
+
let head = fs.readFileSync(path.join(git, 'HEAD'), 'utf-8').trim();
|
|
80
|
+
if (head.startsWith('ref: ')) {
|
|
81
|
+
const ref = head.slice(5).trim();
|
|
82
|
+
const refPath = path.join(git, ref);
|
|
83
|
+
head = fs.existsSync(refPath)
|
|
84
|
+
? fs.readFileSync(refPath, 'utf-8').trim()
|
|
85
|
+
: '';
|
|
86
|
+
}
|
|
87
|
+
return /^[0-9a-f]{40,64}$/.test(head) ? head : undefined;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
75
93
|
function loadRepoData(root) {
|
|
76
94
|
const topPkgDir = path.join(root, 'packages');
|
|
77
95
|
const manifest = new Map();
|
|
@@ -97,13 +115,29 @@ function loadRepoData(root) {
|
|
|
97
115
|
catch {
|
|
98
116
|
// Optional artifact — fall back to the regex scan.
|
|
99
117
|
}
|
|
100
|
-
|
|
118
|
+
const maturity = new Map();
|
|
119
|
+
try {
|
|
120
|
+
const gates = JSON.parse(fs.readFileSync(path.join(root, 'scripts', 'workspace-gates.config.json'), 'utf-8'));
|
|
121
|
+
for (const [dir, meta] of Object.entries(gates.packages ?? {})) {
|
|
122
|
+
if (!meta?.maturity)
|
|
123
|
+
continue;
|
|
124
|
+
try {
|
|
125
|
+
const pj = JSON.parse(fs.readFileSync(path.join(root, dir, 'package.json'), 'utf-8'));
|
|
126
|
+
if (pj.name)
|
|
127
|
+
maturity.set(pj.name, meta.maturity);
|
|
128
|
+
}
|
|
129
|
+
catch { /* unreadable package */ }
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch { /* optional */ }
|
|
133
|
+
return { topPkgDir, manifest, maturity, ast };
|
|
101
134
|
}
|
|
102
135
|
function loadBundledIndex() {
|
|
103
136
|
try {
|
|
104
137
|
const idx = JSON.parse(fs.readFileSync(BUNDLED_INDEX_PATH, 'utf-8'));
|
|
105
|
-
if (idx && typeof idx === 'object' && idx.packages && typeof idx.packages === 'object')
|
|
106
|
-
return idx;
|
|
138
|
+
if (idx && typeof idx === 'object' && idx.packages && typeof idx.packages === 'object') {
|
|
139
|
+
return { ...idx, schemaVersion: idx.schemaVersion ?? 2, source: idx.source ?? 'bundled' };
|
|
140
|
+
}
|
|
107
141
|
return null;
|
|
108
142
|
}
|
|
109
143
|
catch {
|
|
@@ -362,15 +396,18 @@ function mergeAst(symbols, ast) {
|
|
|
362
396
|
}
|
|
363
397
|
/** Scan a monorepo root into an `SdkIndex`. */
|
|
364
398
|
function buildFromMonorepo(root, now = Date.now()) {
|
|
365
|
-
const { topPkgDir, manifest: MANIFEST, ast: AST } = loadRepoData(root);
|
|
399
|
+
const { topPkgDir, manifest: MANIFEST, maturity: MATURITY, ast: AST } = loadRepoData(root);
|
|
366
400
|
const packages = {};
|
|
367
401
|
const symbolIndex = {};
|
|
368
402
|
const domainMap = {};
|
|
403
|
+
const manifestNames = new Set(MANIFEST.keys());
|
|
404
|
+
const discoveredNames = new Set();
|
|
369
405
|
for (const dir of findPackageDirs(topPkgDir)) {
|
|
370
406
|
const pkg = readPackageJson(dir);
|
|
371
407
|
if (!pkg || !pkg.name)
|
|
372
408
|
continue;
|
|
373
409
|
const manifest = MANIFEST.get(pkg.name);
|
|
410
|
+
discoveredNames.add(pkg.name);
|
|
374
411
|
if (MANIFEST.size > 0 && !manifest)
|
|
375
412
|
continue;
|
|
376
413
|
const dirName = path.basename(dir);
|
|
@@ -386,12 +423,20 @@ function buildFromMonorepo(root, now = Date.now()) {
|
|
|
386
423
|
...(Array.isArray(pkg.keywords) ? { keywords: pkg.keywords } : {}),
|
|
387
424
|
dependencies: Object.keys(pkg.dependencies || {}),
|
|
388
425
|
devDependencies: Object.keys(pkg.devDependencies || {}),
|
|
426
|
+
dependencyGroups: {
|
|
427
|
+
runtime: { ...(pkg.dependencies || {}) },
|
|
428
|
+
peer: { ...(pkg.peerDependencies || {}) },
|
|
429
|
+
optional: { ...(pkg.optionalDependencies || {}) },
|
|
430
|
+
dev: { ...(pkg.devDependencies || {}) },
|
|
431
|
+
},
|
|
432
|
+
...(pkg.exports && typeof pkg.exports === 'object' ? { exportsMap: pkg.exports } : {}),
|
|
389
433
|
hasRust: hasSubdir(dir, 'rust') || hasSubdir(dir, 'rust-toolchain'),
|
|
390
434
|
hasGo: hasSubdir(dir, 'go'),
|
|
391
435
|
hasTests: hasTestDir(dir),
|
|
392
436
|
exports,
|
|
393
437
|
symbols: Object.fromEntries(symbols),
|
|
394
438
|
domain,
|
|
439
|
+
...(MATURITY.get(pkg.name) ? { maturity: MATURITY.get(pkg.name) } : {}),
|
|
395
440
|
};
|
|
396
441
|
packages[pkg.name] = idx;
|
|
397
442
|
if (!domainMap[domain])
|
|
@@ -407,7 +452,39 @@ function buildFromMonorepo(root, now = Date.now()) {
|
|
|
407
452
|
});
|
|
408
453
|
}
|
|
409
454
|
}
|
|
410
|
-
|
|
455
|
+
// Normalise workspace protocol ranges to the concrete workspace version so
|
|
456
|
+
// graph output is actionable outside pnpm. External ranges are preserved.
|
|
457
|
+
for (const pkg of Object.values(packages)) {
|
|
458
|
+
for (const kind of ['runtime', 'peer', 'optional', 'dev']) {
|
|
459
|
+
const group = pkg.dependencyGroups?.[kind];
|
|
460
|
+
if (!group)
|
|
461
|
+
continue;
|
|
462
|
+
for (const [dep, range] of Object.entries(group)) {
|
|
463
|
+
if (String(range).startsWith('workspace:')) {
|
|
464
|
+
const version = packages[dep]?.version;
|
|
465
|
+
group[dep] = version ? `^${version}` : String(range);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
const missingFromIndex = [...manifestNames].filter((name) => !packages[name]).sort();
|
|
471
|
+
const extraInIndex = [...discoveredNames].filter((name) => !manifestNames.has(name)).sort();
|
|
472
|
+
const sourceRevision = readGitRevision(root);
|
|
473
|
+
return {
|
|
474
|
+
schemaVersion: 2,
|
|
475
|
+
source: 'monorepo',
|
|
476
|
+
...(sourceRevision ? { sourceRevision } : {}),
|
|
477
|
+
coverage: {
|
|
478
|
+
manifestCount: manifestNames.size,
|
|
479
|
+
discoveredCount: discoveredNames.size,
|
|
480
|
+
missingFromIndex,
|
|
481
|
+
extraInIndex,
|
|
482
|
+
},
|
|
483
|
+
generatedAt: now,
|
|
484
|
+
packages,
|
|
485
|
+
symbolIndex,
|
|
486
|
+
domainMap,
|
|
487
|
+
};
|
|
411
488
|
}
|
|
412
489
|
/**
|
|
413
490
|
* Build the SDK index. Prefers a live monorepo scan; falls back to the bundled
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional verified recipe accelerators. They are fixtures for the shared
|
|
3
|
+
* planner/scaffolder, not special-cased branches and not the taxonomy of
|
|
4
|
+
* supported applications.
|
|
5
|
+
*/
|
|
6
|
+
import type { PlanCompositionInput, CompositionPlan } from './composition.js';
|
|
7
|
+
import type { SdkIndex } from './types.js';
|
|
8
|
+
export interface RecipeDefinition {
|
|
9
|
+
id: string;
|
|
10
|
+
title: string;
|
|
11
|
+
boundary: string;
|
|
12
|
+
input: Omit<PlanCompositionInput, 'brief'>;
|
|
13
|
+
}
|
|
14
|
+
export declare const RECIPES: RecipeDefinition[];
|
|
15
|
+
export declare function planRecipe(recipe: RecipeDefinition, index: SdkIndex): CompositionPlan;
|
package/dist/recipes.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Optional verified recipe accelerators. They are fixtures for the shared
|
|
4
|
+
* planner/scaffolder, not special-cased branches and not the taxonomy of
|
|
5
|
+
* supported applications.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.RECIPES = void 0;
|
|
9
|
+
exports.planRecipe = planRecipe;
|
|
10
|
+
const composition_js_1 = require("./composition.js");
|
|
11
|
+
exports.RECIPES = [
|
|
12
|
+
{
|
|
13
|
+
id: 'digital-product-passport',
|
|
14
|
+
title: 'Digital Product Passport',
|
|
15
|
+
boundary: 'Passport assembles attested event evidence; generated summaries are not certified digital twins.',
|
|
16
|
+
input: {
|
|
17
|
+
runtime: 'node-esm',
|
|
18
|
+
requiredPackages: ['@totemsdk/edge-mqtt', '@totemsdk/core', '@totemsdk/proof', '@totemsdk/proofgraph'],
|
|
19
|
+
suggestedPackages: ['@totemsdk/manifest', '@totemsdk/identity', '@totemsdk/authority', '@totemsdk/intelligence', '@totemsdk/qvac', '@totemsdk/storage'],
|
|
20
|
+
requiredCapabilities: ['attested event evidence', 'manifest identity', 'proof graph', 'local AI summary'],
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: 'machinepay-edge',
|
|
25
|
+
title: 'MachinePay Edge Gateway',
|
|
26
|
+
boundary: 'The gateway executes only within a signed policy envelope; payment receipts do not certify the physical outcome.',
|
|
27
|
+
input: {
|
|
28
|
+
runtime: 'node-esm',
|
|
29
|
+
requiredPackages: ['@totemsdk/edge', '@totemsdk/edge-mqtt', '@totemsdk/omnia', '@totemsdk/agent-policy'],
|
|
30
|
+
suggestedPackages: ['@totemsdk/manifest', '@totemsdk/proofgraph', '@totemsdk/root-identity'],
|
|
31
|
+
requiredCapabilities: ['edge device runtime', 'mqtt transport', 'payment channel', 'agent policy'],
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
function planRecipe(recipe, index) {
|
|
36
|
+
return (0, composition_js_1.planComposition)({ ...recipe.input, brief: `${recipe.title}. ${recipe.boundary}` }, index);
|
|
37
|
+
}
|
package/dist/resources.js
CHANGED
|
@@ -54,7 +54,7 @@ const PAPERS = [
|
|
|
54
54
|
{
|
|
55
55
|
uri: 'totemsdk://papers/yellow',
|
|
56
56
|
name: 'Core Yellow Paper',
|
|
57
|
-
description: 'WOTS
|
|
57
|
+
description: 'WOTS signatures, TreeKey hierarchy, MMR proofs, byte-exact serialization, lease/watermark coordination, Rust/WASM engine',
|
|
58
58
|
file: 'TOTEM_CORE_YELLOW_PAPER.md',
|
|
59
59
|
},
|
|
60
60
|
{
|
|
@@ -278,6 +278,17 @@ const CONVENTIONS = `# Totem SDK Conventions
|
|
|
278
278
|
## Async
|
|
279
279
|
- Promise<T> everywhere, typed event emitters, no Observables
|
|
280
280
|
- Action lifecycle: propose -> reserve -> execute -> confirm/fail/unknown`;
|
|
281
|
+
function decodeResourceSegment(value) {
|
|
282
|
+
try {
|
|
283
|
+
const decoded = decodeURIComponent(value);
|
|
284
|
+
if (!decoded || decoded.includes('..') || decoded.includes('\\') || decoded.includes('\0'))
|
|
285
|
+
return null;
|
|
286
|
+
return decoded;
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
281
292
|
function handleResourceRead(uri, index) {
|
|
282
293
|
if (uri === 'totemsdk://connect/methods')
|
|
283
294
|
return JSON.stringify(connectMethods(), null, 2);
|
|
@@ -300,6 +311,16 @@ function handleResourceRead(uri, index) {
|
|
|
300
311
|
}
|
|
301
312
|
return content;
|
|
302
313
|
}
|
|
314
|
+
if (uri === 'totemsdk://index') {
|
|
315
|
+
return JSON.stringify({
|
|
316
|
+
schemaVersion: index.schemaVersion ?? null,
|
|
317
|
+
source: index.source ?? 'unknown',
|
|
318
|
+
sourceRevision: index.sourceRevision ?? null,
|
|
319
|
+
generatedAt: index.generatedAt,
|
|
320
|
+
packageCount: Object.keys(index.packages).length,
|
|
321
|
+
coverage: index.coverage ?? null,
|
|
322
|
+
}, null, 2);
|
|
323
|
+
}
|
|
303
324
|
if (uri === 'totemsdk://packages') {
|
|
304
325
|
return JSON.stringify(Object.values(index.packages).map(p => ({
|
|
305
326
|
name: p.name,
|
|
@@ -331,29 +352,61 @@ function handleResourceRead(uri, index) {
|
|
|
331
352
|
}
|
|
332
353
|
const pkgMatch = uri.match(/^totemsdk:\/\/packages\/([^/]+)$/);
|
|
333
354
|
if (pkgMatch) {
|
|
334
|
-
const
|
|
355
|
+
const name = decodeResourceSegment(pkgMatch[1]);
|
|
356
|
+
if (!name)
|
|
357
|
+
return null;
|
|
358
|
+
const pkg = index.packages[name];
|
|
335
359
|
return pkg ? JSON.stringify(pkg, null, 2) : null;
|
|
336
360
|
}
|
|
361
|
+
const pkgContractsMatch = uri.match(/^totemsdk:\/\/packages\/([^/]+)\/contracts$/);
|
|
362
|
+
if (pkgContractsMatch) {
|
|
363
|
+
const name = decodeResourceSegment(pkgContractsMatch[1]);
|
|
364
|
+
if (!name)
|
|
365
|
+
return null;
|
|
366
|
+
const pkg = index.packages[name];
|
|
367
|
+
if (!pkg)
|
|
368
|
+
return null;
|
|
369
|
+
return JSON.stringify({
|
|
370
|
+
contentKind: 'contract',
|
|
371
|
+
package: pkg.name,
|
|
372
|
+
version: pkg.version,
|
|
373
|
+
origin: index.source ?? 'unknown',
|
|
374
|
+
sourceRevision: index.sourceRevision ?? null,
|
|
375
|
+
symbols: pkg.symbols ?? {},
|
|
376
|
+
}, null, 2);
|
|
377
|
+
}
|
|
337
378
|
const pkgExportsMatch = uri.match(/^totemsdk:\/\/packages\/([^/]+)\/exports$/);
|
|
338
379
|
if (pkgExportsMatch) {
|
|
339
|
-
const
|
|
380
|
+
const name = decodeResourceSegment(pkgExportsMatch[1]);
|
|
381
|
+
if (!name)
|
|
382
|
+
return null;
|
|
383
|
+
const pkg = index.packages[name];
|
|
340
384
|
return pkg ? JSON.stringify(pkg.exports, null, 2) : null;
|
|
341
385
|
}
|
|
342
386
|
const pkgDepsMatch = uri.match(/^totemsdk:\/\/packages\/([^/]+)\/dependencies$/);
|
|
343
387
|
if (pkgDepsMatch) {
|
|
344
|
-
const
|
|
388
|
+
const name = decodeResourceSegment(pkgDepsMatch[1]);
|
|
389
|
+
if (!name)
|
|
390
|
+
return null;
|
|
391
|
+
const pkg = index.packages[name];
|
|
345
392
|
if (!pkg)
|
|
346
393
|
return null;
|
|
347
394
|
return JSON.stringify({ dependencies: pkg.dependencies, devDependencies: pkg.devDependencies }, null, 2);
|
|
348
395
|
}
|
|
349
396
|
const domainMatch = uri.match(/^totemsdk:\/\/packages\/by-domain\/(.+)$/);
|
|
350
397
|
if (domainMatch) {
|
|
351
|
-
const
|
|
398
|
+
const domain = decodeResourceSegment(domainMatch[1]);
|
|
399
|
+
if (!domain)
|
|
400
|
+
return null;
|
|
401
|
+
const pkgs = index.domainMap[domain];
|
|
352
402
|
return pkgs ? JSON.stringify(pkgs.map(n => index.packages[n]).filter(Boolean), null, 2) : null;
|
|
353
403
|
}
|
|
354
404
|
const symbolMatch = uri.match(/^totemsdk:\/\/symbol\/(.+)$/);
|
|
355
405
|
if (symbolMatch) {
|
|
356
|
-
const
|
|
406
|
+
const symbol = decodeResourceSegment(symbolMatch[1]);
|
|
407
|
+
if (!symbol)
|
|
408
|
+
return null;
|
|
409
|
+
const entries = index.symbolIndex[symbol];
|
|
357
410
|
return entries ? JSON.stringify(entries, null, 2) : null;
|
|
358
411
|
}
|
|
359
412
|
return null;
|
|
@@ -362,6 +415,7 @@ function listResources(index) {
|
|
|
362
415
|
const resources = [
|
|
363
416
|
{ uri: 'totemsdk://packages', name: 'All Packages', description: `List of all ${Object.keys(index.packages).length} SDK packages with metadata` },
|
|
364
417
|
{ uri: 'totemsdk://templates', name: 'All KISSVM Templates', description: `All ${(0, template_catalog_js_1.getAllTemplates)().length} KISSVM script templates with import paths and descriptions` },
|
|
418
|
+
{ uri: 'totemsdk://index', name: 'Index Provenance', description: 'Index schema, source revision, package count and coverage reconciliation' },
|
|
365
419
|
{ uri: 'totemsdk://conventions', name: 'Coding Conventions', description: 'Totem SDK coding conventions and patterns' },
|
|
366
420
|
{ uri: 'totemsdk://domain-map', name: 'Domain Map', description: 'Packages grouped by domain layer' },
|
|
367
421
|
...PAPERS.map(p => ({ uri: p.uri, name: p.name, description: p.description })),
|
|
@@ -391,6 +445,11 @@ function listResources(index) {
|
|
|
391
445
|
name: `Dependencies: ${name}`,
|
|
392
446
|
description: `Dependencies of ${name}`,
|
|
393
447
|
});
|
|
448
|
+
resources.push({
|
|
449
|
+
uri: `totemsdk://packages/${encodeURIComponent(name)}/contracts`,
|
|
450
|
+
name: `Contracts: ${name}`,
|
|
451
|
+
description: `Exact exported contract metadata for ${name} (contentKind: contract)`,
|
|
452
|
+
});
|
|
394
453
|
}
|
|
395
454
|
// Symbol lookups are served by the `search-symbol` tool (query-driven), which
|
|
396
455
|
// is far cheaper than enumerating ~3.7k symbols as resources. Only surface a
|