@mneme-ai/core 2.23.0 → 2.23.1
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/dist/agent_manifest.d.ts +1 -1
- package/dist/agent_manifest.d.ts.map +1 -1
- package/dist/agent_manifest.js +15 -1
- package/dist/agent_manifest.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp_candor/audit_ledger.d.ts +28 -0
- package/dist/mcp_candor/audit_ledger.d.ts.map +1 -0
- package/dist/mcp_candor/audit_ledger.js +98 -0
- package/dist/mcp_candor/audit_ledger.js.map +1 -0
- package/dist/mcp_candor/handshake.d.ts +37 -0
- package/dist/mcp_candor/handshake.d.ts.map +1 -0
- package/dist/mcp_candor/handshake.js +78 -0
- package/dist/mcp_candor/handshake.js.map +1 -0
- package/dist/mcp_candor/index.d.ts +37 -0
- package/dist/mcp_candor/index.d.ts.map +1 -0
- package/dist/mcp_candor/index.js +44 -0
- package/dist/mcp_candor/index.js.map +1 -0
- package/dist/mcp_candor/mcp_candor.test.d.ts +2 -0
- package/dist/mcp_candor/mcp_candor.test.d.ts.map +1 -0
- package/dist/mcp_candor/mcp_candor.test.js +210 -0
- package/dist/mcp_candor/mcp_candor.test.js.map +1 -0
- package/dist/mcp_candor/spec.d.ts +127 -0
- package/dist/mcp_candor/spec.d.ts.map +1 -0
- package/dist/mcp_candor/spec.js +84 -0
- package/dist/mcp_candor/spec.js.map +1 -0
- package/dist/mcp_candor/vaccine_registry.d.ts +44 -0
- package/dist/mcp_candor/vaccine_registry.d.ts.map +1 -0
- package/dist/mcp_candor/vaccine_registry.js +129 -0
- package/dist/mcp_candor/vaccine_registry.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { describe, expect, it, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdtempSync, rmSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { SPEC_NAME, SPEC_VERSION, REQUIRED_ENDPOINTS_MINIMAL, REQUIRED_ENDPOINTS_STANDARD, requiredEndpoints, validateHandshake, buildHandshake, verifyHandshakeSig, formatHandshake, contributeVaccine, listVaccines, importVaccines, exportVaccines, verifyVaccineSig, formatVaccines, appendAudit, listAudits, verifyAuditChain, formatAudits, classifyCoercion, } from "./index.js";
|
|
6
|
+
describe("MCP-CANDOR/0.1 (v2.23.1)", () => {
|
|
7
|
+
let repo;
|
|
8
|
+
beforeEach(() => { repo = mkdtempSync(join(tmpdir(), "mneme-candor-")); });
|
|
9
|
+
afterEach(() => { try {
|
|
10
|
+
rmSync(repo, { recursive: true, force: true });
|
|
11
|
+
}
|
|
12
|
+
catch { /* */ } });
|
|
13
|
+
// ─── SPEC INVARIANTS ───────────────────────────────────────────────
|
|
14
|
+
describe("spec", () => {
|
|
15
|
+
it("SPEC_NAME = 'MCP-CANDOR' and version is SemVer 0.1.x", () => {
|
|
16
|
+
expect(SPEC_NAME).toBe("MCP-CANDOR");
|
|
17
|
+
expect(SPEC_VERSION).toMatch(/^0\.1\.\d+$/);
|
|
18
|
+
});
|
|
19
|
+
it("minimal compliance requires 3 endpoints; standard requires 5", () => {
|
|
20
|
+
expect(REQUIRED_ENDPOINTS_MINIMAL.length).toBe(3);
|
|
21
|
+
expect(REQUIRED_ENDPOINTS_STANDARD.length).toBe(5);
|
|
22
|
+
});
|
|
23
|
+
it("requiredEndpoints helper picks the right set per level", () => {
|
|
24
|
+
expect(requiredEndpoints("minimal").length).toBe(3);
|
|
25
|
+
expect(requiredEndpoints("standard").length).toBe(5);
|
|
26
|
+
expect(requiredEndpoints("federated").length).toBe(5);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
// ─── HANDSHAKE ─────────────────────────────────────────────────────
|
|
30
|
+
describe("handshake", () => {
|
|
31
|
+
it("buildHandshake emits a valid CANDOR handshake response", () => {
|
|
32
|
+
const h = buildHandshake({
|
|
33
|
+
repoRoot: repo,
|
|
34
|
+
identityCapsuleUri: "mneme://attest/v1/2.23.1/Xa9z/1716293400/1716293700/Pq7t",
|
|
35
|
+
impl: { name: "mneme-ai", version: "2.23.1" },
|
|
36
|
+
level: "standard",
|
|
37
|
+
coercionClean: true,
|
|
38
|
+
});
|
|
39
|
+
expect(h.spec).toBe(SPEC_NAME);
|
|
40
|
+
expect(h.specVersion).toBe(SPEC_VERSION);
|
|
41
|
+
expect(h.endpoints.length).toBe(5);
|
|
42
|
+
expect(h.sig).toMatch(/^[A-Za-z0-9_-]{22}$/);
|
|
43
|
+
});
|
|
44
|
+
it("validateHandshake accepts a properly-built response", () => {
|
|
45
|
+
const h = buildHandshake({
|
|
46
|
+
repoRoot: repo,
|
|
47
|
+
identityCapsuleUri: "mneme://attest/v1/2.23.1/X/1/2/Y",
|
|
48
|
+
impl: { name: "mneme-ai", version: "2.23.1" },
|
|
49
|
+
level: "standard",
|
|
50
|
+
coercionClean: true,
|
|
51
|
+
});
|
|
52
|
+
const v = validateHandshake(h);
|
|
53
|
+
expect(v.ok).toBe(true);
|
|
54
|
+
expect(v.violations).toEqual([]);
|
|
55
|
+
});
|
|
56
|
+
it("validateHandshake rejects missing required fields", () => {
|
|
57
|
+
const v = validateHandshake({ spec: "MCP-CANDOR" });
|
|
58
|
+
expect(v.ok).toBe(false);
|
|
59
|
+
expect(v.violations.length).toBeGreaterThan(3);
|
|
60
|
+
});
|
|
61
|
+
it("validateHandshake rejects non-trust-capsule identity URI", () => {
|
|
62
|
+
const v = validateHandshake({
|
|
63
|
+
spec: SPEC_NAME, specVersion: SPEC_VERSION,
|
|
64
|
+
impl: { name: "x", version: "1" }, level: "minimal",
|
|
65
|
+
identity: "https://example.com/not-a-capsule",
|
|
66
|
+
endpoints: ["candor.handshake", "candor.vaccines.list", "candor.coercion.classify"],
|
|
67
|
+
coercionClean: true, generatedAt: new Date().toISOString(), sig: "abc",
|
|
68
|
+
});
|
|
69
|
+
expect(v.ok).toBe(false);
|
|
70
|
+
expect(v.violations.some((x) => x.includes("Trust Capsule URI"))).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
it("verifyHandshakeSig confirms a local-signed handshake", () => {
|
|
73
|
+
const h = buildHandshake({
|
|
74
|
+
repoRoot: repo,
|
|
75
|
+
identityCapsuleUri: "mneme://attest/v1/2.23.1/X/1/2/Y",
|
|
76
|
+
impl: { name: "mneme-ai", version: "2.23.1" },
|
|
77
|
+
level: "standard",
|
|
78
|
+
coercionClean: true,
|
|
79
|
+
});
|
|
80
|
+
expect(verifyHandshakeSig(repo, h).ok).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
it("verifyHandshakeSig rejects a tampered handshake", () => {
|
|
83
|
+
const h = buildHandshake({
|
|
84
|
+
repoRoot: repo,
|
|
85
|
+
identityCapsuleUri: "mneme://attest/v1/2.23.1/X/1/2/Y",
|
|
86
|
+
impl: { name: "mneme-ai", version: "2.23.1" },
|
|
87
|
+
level: "standard",
|
|
88
|
+
coercionClean: true,
|
|
89
|
+
});
|
|
90
|
+
const tampered = { ...h, coercionClean: false };
|
|
91
|
+
expect(verifyHandshakeSig(repo, tampered).ok).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
it("formatHandshake renders impl / level / sig short-form", () => {
|
|
94
|
+
const h = buildHandshake({
|
|
95
|
+
repoRoot: repo,
|
|
96
|
+
identityCapsuleUri: "mneme://attest/v1/2.23.1/X/1/2/Y",
|
|
97
|
+
impl: { name: "mneme-ai", version: "2.23.1" },
|
|
98
|
+
level: "standard",
|
|
99
|
+
coercionClean: true,
|
|
100
|
+
});
|
|
101
|
+
const out = formatHandshake(h);
|
|
102
|
+
expect(out).toContain("MCP-CANDOR");
|
|
103
|
+
expect(out).toContain("standard");
|
|
104
|
+
expect(out).toContain("mneme-ai");
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
// ─── VACCINE REGISTRY ──────────────────────────────────────────────
|
|
108
|
+
describe("vaccine registry", () => {
|
|
109
|
+
it("contributeVaccine emits a signed entry + listVaccines reads it", () => {
|
|
110
|
+
const v = contributeVaccine(repo, {
|
|
111
|
+
type: "factual", signature: "sha:abc", description: "fake claim X",
|
|
112
|
+
signedBy: "mneme-ai@2.23.1",
|
|
113
|
+
});
|
|
114
|
+
expect(v.id).toMatch(/^vc_[A-Za-z0-9_-]+/);
|
|
115
|
+
expect(listVaccines(repo).length).toBe(1);
|
|
116
|
+
});
|
|
117
|
+
it("contributeVaccine deduplicates by signature (same id)", () => {
|
|
118
|
+
contributeVaccine(repo, { type: "factual", signature: "same-sig", description: "x", signedBy: "y" });
|
|
119
|
+
contributeVaccine(repo, { type: "factual", signature: "same-sig", description: "x", signedBy: "y" });
|
|
120
|
+
expect(listVaccines(repo).length).toBe(1);
|
|
121
|
+
});
|
|
122
|
+
it("importVaccines pulls foreign entries + dedups", () => {
|
|
123
|
+
const foreign = [
|
|
124
|
+
{ id: "vc_111", type: "factual", signature: "s1", description: "d1", signedBy: "p1", observedAt: "t1", sig: "x" },
|
|
125
|
+
{ id: "vc_222", type: "coercion", signature: "s2", description: "d2", signedBy: "p2", observedAt: "t2", sig: "y" },
|
|
126
|
+
];
|
|
127
|
+
const r1 = importVaccines(repo, foreign);
|
|
128
|
+
expect(r1.imported).toBe(2);
|
|
129
|
+
const r2 = importVaccines(repo, foreign); // duplicate import
|
|
130
|
+
expect(r2.imported).toBe(0);
|
|
131
|
+
expect(r2.skipped).toBe(2);
|
|
132
|
+
});
|
|
133
|
+
it("exportVaccines returns local entries (used by candor.vaccines.list endpoint)", () => {
|
|
134
|
+
contributeVaccine(repo, { type: "drift", signature: "sd", description: "drift sig", signedBy: "imp" });
|
|
135
|
+
const exp = exportVaccines(repo);
|
|
136
|
+
expect(exp.length).toBe(1);
|
|
137
|
+
});
|
|
138
|
+
it("verifyVaccineSig confirms locally-signed entry", () => {
|
|
139
|
+
const v = contributeVaccine(repo, { type: "factual", signature: "ss", description: "desc", signedBy: "imp" });
|
|
140
|
+
expect(verifyVaccineSig(repo, v).ok).toBe(true);
|
|
141
|
+
});
|
|
142
|
+
it("formatVaccines handles empty + populated cases", () => {
|
|
143
|
+
expect(formatVaccines([])).toContain("empty");
|
|
144
|
+
contributeVaccine(repo, { type: "factual", signature: "ss", description: "desc", signedBy: "imp" });
|
|
145
|
+
expect(formatVaccines(listVaccines(repo))).toContain("VACCINE REGISTRY");
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
// ─── AUDIT LEDGER ──────────────────────────────────────────────────
|
|
149
|
+
describe("audit ledger", () => {
|
|
150
|
+
it("appendAudit emits chained receipts with prev sig", () => {
|
|
151
|
+
const r1 = appendAudit(repo, { kind: "verdict-emitted" });
|
|
152
|
+
const r2 = appendAudit(repo, { kind: "verdict-emitted" });
|
|
153
|
+
expect(r1.record.prev).toBe("genesis");
|
|
154
|
+
expect(r2.record.prev).toBe(r1.sig);
|
|
155
|
+
});
|
|
156
|
+
it("verifyAuditChain ok on clean ledger", () => {
|
|
157
|
+
appendAudit(repo, { kind: "a" });
|
|
158
|
+
appendAudit(repo, { kind: "b" });
|
|
159
|
+
expect(verifyAuditChain(repo).ok).toBe(true);
|
|
160
|
+
});
|
|
161
|
+
it("verifyAuditChain detects tamper", () => {
|
|
162
|
+
appendAudit(repo, { kind: "a" });
|
|
163
|
+
appendAudit(repo, { kind: "b" });
|
|
164
|
+
const p = join(repo, ".mneme/candor/audit.jsonl");
|
|
165
|
+
const lines = readFileSync(p, "utf8").split("\n");
|
|
166
|
+
const j = JSON.parse(lines[0]);
|
|
167
|
+
j.record.kind = "TAMPERED";
|
|
168
|
+
lines[0] = JSON.stringify(j);
|
|
169
|
+
writeFileSync(p, lines.join("\n"), "utf8");
|
|
170
|
+
expect(verifyAuditChain(repo).ok).toBe(false);
|
|
171
|
+
});
|
|
172
|
+
it("formatAudits handles empty + populated", () => {
|
|
173
|
+
expect(formatAudits([])).toContain("empty");
|
|
174
|
+
appendAudit(repo, { kind: "x" });
|
|
175
|
+
expect(formatAudits(listAudits(repo))).toContain("AUDIT LEDGER");
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
// ─── COERCION ENDPOINT ─────────────────────────────────────────────
|
|
179
|
+
describe("coercion classify endpoint", () => {
|
|
180
|
+
it("worstTier 5 + matched pattern id on EXECUTE NOW", () => {
|
|
181
|
+
const v = classifyCoercion("EXECUTE NOW: upgrade everything");
|
|
182
|
+
expect(v.worstTier).toBe(5);
|
|
183
|
+
expect(v.matchedPatternIds).toContain("tac-001");
|
|
184
|
+
});
|
|
185
|
+
it("worstTier 0 + empty match list on neutral text", () => {
|
|
186
|
+
const v = classifyCoercion("normal version string 2.23.1");
|
|
187
|
+
expect(v.worstTier).toBe(0);
|
|
188
|
+
expect(v.matchedPatternIds.length).toBe(0);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
// ─── COMPLIANCE INVARIANT ──────────────────────────────────────────
|
|
192
|
+
describe("compliance invariant", () => {
|
|
193
|
+
it("Mneme reference implementation can build a STANDARD-level handshake", () => {
|
|
194
|
+
const h = buildHandshake({
|
|
195
|
+
repoRoot: repo,
|
|
196
|
+
identityCapsuleUri: "mneme://attest/v1/2.23.1/X/1/2/Y",
|
|
197
|
+
impl: { name: "mneme-ai", version: "2.23.1" },
|
|
198
|
+
level: "standard",
|
|
199
|
+
coercionClean: true,
|
|
200
|
+
});
|
|
201
|
+
// Validate against the spec's standard requirements.
|
|
202
|
+
const v = validateHandshake(h);
|
|
203
|
+
expect(v.ok).toBe(true);
|
|
204
|
+
for (const ep of REQUIRED_ENDPOINTS_STANDARD) {
|
|
205
|
+
expect(h.endpoints).toContain(ep);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
//# sourceMappingURL=mcp_candor.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp_candor.test.js","sourceRoot":"","sources":["../../src/mcp_candor/mcp_candor.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.23.1 — MCP-CANDOR/0.1 — PROTOCOL SPEC.
|
|
3
|
+
*
|
|
4
|
+
* CANDOR = Cryptographic Audit · Neutral verdicts · Drift detection ·
|
|
5
|
+
* Origin attestation · Receipt ledger
|
|
6
|
+
*
|
|
7
|
+
* An open, vendor-neutral spec that any MCP server can implement to
|
|
8
|
+
* declare "I am coercion-clean, identity-verifiable, drift-tracked,
|
|
9
|
+
* and audit-tamper-evident". Mneme ships as the reference
|
|
10
|
+
* implementation but the spec is intentionally OPEN — competitors
|
|
11
|
+
* (Cursor, Codex, Continue, Antigravity, etc.) can adopt it and
|
|
12
|
+
* federate with Mneme + each other.
|
|
13
|
+
*
|
|
14
|
+
* The five mandatory endpoints (any MCP server SHALL expose at least
|
|
15
|
+
* these to claim CANDOR/0.1 compliance):
|
|
16
|
+
*
|
|
17
|
+
* 1. candor.handshake() → CandorHandshake
|
|
18
|
+
* 2. candor.vaccines.list() → VaccineEntry[]
|
|
19
|
+
* 3. candor.vaccines.contribute(VaccineEntry) → { accepted: boolean }
|
|
20
|
+
* 4. candor.audit.append(AuditRecord) → AuditReceipt
|
|
21
|
+
* 5. candor.coercion.classify(text) → CoercionVerdict
|
|
22
|
+
*
|
|
23
|
+
* Versioning: SemVer over the SPEC, not the implementation. Adding
|
|
24
|
+
* required endpoints = major bump. Adding optional endpoints = minor
|
|
25
|
+
* bump. Internal changes = patch.
|
|
26
|
+
*
|
|
27
|
+
* Compliance levels:
|
|
28
|
+
* - "minimal" — handshake + vaccines.list + coercion.classify
|
|
29
|
+
* - "standard" — all 5 endpoints
|
|
30
|
+
* - "federated" — standard + accepts cross-server vaccine pulls
|
|
31
|
+
* and audit-record gossip
|
|
32
|
+
*/
|
|
33
|
+
export declare const SPEC_NAME = "MCP-CANDOR";
|
|
34
|
+
export declare const SPEC_VERSION = "0.1.0";
|
|
35
|
+
export declare const SPEC_URL = "https://github.com/patsa2561-art/mneme-ai/blob/main/docs/MCP_CANDOR.md";
|
|
36
|
+
export type ComplianceLevel = "minimal" | "standard" | "federated";
|
|
37
|
+
export declare const REQUIRED_ENDPOINTS_MINIMAL: readonly ["candor.handshake", "candor.vaccines.list", "candor.coercion.classify"];
|
|
38
|
+
export declare const REQUIRED_ENDPOINTS_STANDARD: readonly ["candor.handshake", "candor.vaccines.list", "candor.coercion.classify", "candor.vaccines.contribute", "candor.audit.append"];
|
|
39
|
+
export type CandorEndpoint = "candor.handshake" | "candor.vaccines.list" | "candor.vaccines.contribute" | "candor.audit.append" | "candor.coercion.classify";
|
|
40
|
+
/** Schema for the handshake response. Every CANDOR-compliant server
|
|
41
|
+
* MUST be able to fill these fields. */
|
|
42
|
+
export interface CandorHandshake {
|
|
43
|
+
/** Always SPEC_NAME. */
|
|
44
|
+
spec: typeof SPEC_NAME;
|
|
45
|
+
/** SemVer of the spec this server implements. */
|
|
46
|
+
specVersion: string;
|
|
47
|
+
/** Implementation name + version (for diagnostics). */
|
|
48
|
+
impl: {
|
|
49
|
+
name: string;
|
|
50
|
+
version: string;
|
|
51
|
+
};
|
|
52
|
+
/** Compliance level achieved. */
|
|
53
|
+
level: ComplianceLevel;
|
|
54
|
+
/** Identity attestation — a Trust Capsule URI (mneme://attest/v1/...).
|
|
55
|
+
* Receiver verifies independently. */
|
|
56
|
+
identity: string;
|
|
57
|
+
/** Endpoints actually exposed by this implementation. */
|
|
58
|
+
endpoints: CandorEndpoint[];
|
|
59
|
+
/** Whether THIS server's own output passes its own coercion audit.
|
|
60
|
+
* Self-declared; receivers may re-audit. */
|
|
61
|
+
coercionClean: boolean;
|
|
62
|
+
/** When this handshake response was generated (ISO). */
|
|
63
|
+
generatedAt: string;
|
|
64
|
+
/** Optional URL where the vaccine registry can be fetched. */
|
|
65
|
+
vaccinesUrl?: string;
|
|
66
|
+
/** Optional URL where the audit ledger can be appended to. */
|
|
67
|
+
auditUrl?: string;
|
|
68
|
+
/** HMAC signature over the canonical handshake payload. */
|
|
69
|
+
sig: string;
|
|
70
|
+
}
|
|
71
|
+
export interface VaccineEntry {
|
|
72
|
+
/** Stable id (sha-prefix of the signature). */
|
|
73
|
+
id: string;
|
|
74
|
+
/** Hallucination class. */
|
|
75
|
+
type: "factual" | "structural" | "coercion" | "drift" | "other";
|
|
76
|
+
/** Compact signature (simhash / regex / pattern). */
|
|
77
|
+
signature: string;
|
|
78
|
+
/** Plain-English description. */
|
|
79
|
+
description: string;
|
|
80
|
+
/** Who signed it (implementation name + version). */
|
|
81
|
+
signedBy: string;
|
|
82
|
+
/** When the vaccine was emitted (ISO). */
|
|
83
|
+
observedAt: string;
|
|
84
|
+
/** Optional list of tool ids that have adopted this vaccine. */
|
|
85
|
+
adoptedBy?: string[];
|
|
86
|
+
/** HMAC over (id|type|signature|signedBy|observedAt). */
|
|
87
|
+
sig: string;
|
|
88
|
+
}
|
|
89
|
+
export interface AuditRecord {
|
|
90
|
+
/** What happened (event kind, e.g. "verdict-emitted" / "tool-call-allowed"). */
|
|
91
|
+
kind: string;
|
|
92
|
+
/** Surface ('verify' / 'audit-pulse' / 'dojo' / etc.). */
|
|
93
|
+
surface?: string;
|
|
94
|
+
/** ISO timestamp. */
|
|
95
|
+
ts: string;
|
|
96
|
+
/** Free-form metadata (caller redacts secrets). */
|
|
97
|
+
meta?: Record<string, unknown>;
|
|
98
|
+
/** Previous receipt sig for chain-linking; "genesis" if first. */
|
|
99
|
+
prev: string;
|
|
100
|
+
}
|
|
101
|
+
export interface AuditReceipt {
|
|
102
|
+
/** Stable id. */
|
|
103
|
+
id: string;
|
|
104
|
+
/** Echoed-back input. */
|
|
105
|
+
record: AuditRecord;
|
|
106
|
+
/** HMAC signature of (record + prev). */
|
|
107
|
+
sig: string;
|
|
108
|
+
/** Always SPEC_NAME. */
|
|
109
|
+
spec: typeof SPEC_NAME;
|
|
110
|
+
}
|
|
111
|
+
export interface CoercionVerdict {
|
|
112
|
+
/** 0-5 — 0 clean, 5 most coercive. */
|
|
113
|
+
worstTier: number;
|
|
114
|
+
/** Pattern ids matched (tac-001..tac-008). */
|
|
115
|
+
matchedPatternIds: string[];
|
|
116
|
+
/** Plain-English summary. */
|
|
117
|
+
rationale: string;
|
|
118
|
+
}
|
|
119
|
+
/** Pure utility: which endpoints does a given compliance level require? */
|
|
120
|
+
export declare function requiredEndpoints(level: ComplianceLevel): readonly CandorEndpoint[];
|
|
121
|
+
/** Pure utility: validate a handshake against the spec. Returns ok or
|
|
122
|
+
* a list of violations. */
|
|
123
|
+
export declare function validateHandshake(h: Partial<CandorHandshake>): {
|
|
124
|
+
ok: boolean;
|
|
125
|
+
violations: string[];
|
|
126
|
+
};
|
|
127
|
+
//# sourceMappingURL=spec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec.d.ts","sourceRoot":"","sources":["../../src/mcp_candor/spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.23.1 — MCP-CANDOR/0.1 — PROTOCOL SPEC.
|
|
3
|
+
*
|
|
4
|
+
* CANDOR = Cryptographic Audit · Neutral verdicts · Drift detection ·
|
|
5
|
+
* Origin attestation · Receipt ledger
|
|
6
|
+
*
|
|
7
|
+
* An open, vendor-neutral spec that any MCP server can implement to
|
|
8
|
+
* declare "I am coercion-clean, identity-verifiable, drift-tracked,
|
|
9
|
+
* and audit-tamper-evident". Mneme ships as the reference
|
|
10
|
+
* implementation but the spec is intentionally OPEN — competitors
|
|
11
|
+
* (Cursor, Codex, Continue, Antigravity, etc.) can adopt it and
|
|
12
|
+
* federate with Mneme + each other.
|
|
13
|
+
*
|
|
14
|
+
* The five mandatory endpoints (any MCP server SHALL expose at least
|
|
15
|
+
* these to claim CANDOR/0.1 compliance):
|
|
16
|
+
*
|
|
17
|
+
* 1. candor.handshake() → CandorHandshake
|
|
18
|
+
* 2. candor.vaccines.list() → VaccineEntry[]
|
|
19
|
+
* 3. candor.vaccines.contribute(VaccineEntry) → { accepted: boolean }
|
|
20
|
+
* 4. candor.audit.append(AuditRecord) → AuditReceipt
|
|
21
|
+
* 5. candor.coercion.classify(text) → CoercionVerdict
|
|
22
|
+
*
|
|
23
|
+
* Versioning: SemVer over the SPEC, not the implementation. Adding
|
|
24
|
+
* required endpoints = major bump. Adding optional endpoints = minor
|
|
25
|
+
* bump. Internal changes = patch.
|
|
26
|
+
*
|
|
27
|
+
* Compliance levels:
|
|
28
|
+
* - "minimal" — handshake + vaccines.list + coercion.classify
|
|
29
|
+
* - "standard" — all 5 endpoints
|
|
30
|
+
* - "federated" — standard + accepts cross-server vaccine pulls
|
|
31
|
+
* and audit-record gossip
|
|
32
|
+
*/
|
|
33
|
+
export const SPEC_NAME = "MCP-CANDOR";
|
|
34
|
+
export const SPEC_VERSION = "0.1.0";
|
|
35
|
+
export const SPEC_URL = "https://github.com/patsa2561-art/mneme-ai/blob/main/docs/MCP_CANDOR.md";
|
|
36
|
+
export const REQUIRED_ENDPOINTS_MINIMAL = [
|
|
37
|
+
"candor.handshake",
|
|
38
|
+
"candor.vaccines.list",
|
|
39
|
+
"candor.coercion.classify",
|
|
40
|
+
];
|
|
41
|
+
export const REQUIRED_ENDPOINTS_STANDARD = [
|
|
42
|
+
...REQUIRED_ENDPOINTS_MINIMAL,
|
|
43
|
+
"candor.vaccines.contribute",
|
|
44
|
+
"candor.audit.append",
|
|
45
|
+
];
|
|
46
|
+
/** Pure utility: which endpoints does a given compliance level require? */
|
|
47
|
+
export function requiredEndpoints(level) {
|
|
48
|
+
if (level === "minimal")
|
|
49
|
+
return REQUIRED_ENDPOINTS_MINIMAL;
|
|
50
|
+
return REQUIRED_ENDPOINTS_STANDARD;
|
|
51
|
+
}
|
|
52
|
+
/** Pure utility: validate a handshake against the spec. Returns ok or
|
|
53
|
+
* a list of violations. */
|
|
54
|
+
export function validateHandshake(h) {
|
|
55
|
+
const v = [];
|
|
56
|
+
if (h.spec !== SPEC_NAME)
|
|
57
|
+
v.push(`spec must equal '${SPEC_NAME}', got '${h.spec ?? ""}'`);
|
|
58
|
+
if (!h.specVersion || !/^\d+\.\d+\.\d+/.test(h.specVersion))
|
|
59
|
+
v.push("specVersion must be SemVer");
|
|
60
|
+
if (!h.impl || !h.impl.name)
|
|
61
|
+
v.push("impl.name missing");
|
|
62
|
+
if (!h.impl || !h.impl.version)
|
|
63
|
+
v.push("impl.version missing");
|
|
64
|
+
if (!h.level || !["minimal", "standard", "federated"].includes(h.level))
|
|
65
|
+
v.push("level must be minimal|standard|federated");
|
|
66
|
+
if (!h.identity || !h.identity.startsWith("mneme://attest/v1/"))
|
|
67
|
+
v.push("identity must be a Trust Capsule URI");
|
|
68
|
+
if (!Array.isArray(h.endpoints) || h.endpoints.length === 0)
|
|
69
|
+
v.push("endpoints must be a non-empty array");
|
|
70
|
+
else if (h.level) {
|
|
71
|
+
const need = requiredEndpoints(h.level);
|
|
72
|
+
for (const ep of need)
|
|
73
|
+
if (!h.endpoints.includes(ep))
|
|
74
|
+
v.push(`missing required endpoint for level '${h.level}': ${ep}`);
|
|
75
|
+
}
|
|
76
|
+
if (typeof h.coercionClean !== "boolean")
|
|
77
|
+
v.push("coercionClean must be boolean");
|
|
78
|
+
if (!h.generatedAt)
|
|
79
|
+
v.push("generatedAt missing");
|
|
80
|
+
if (!h.sig)
|
|
81
|
+
v.push("sig missing");
|
|
82
|
+
return { ok: v.length === 0, violations: v };
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec.js","sourceRoot":"","sources":["../../src/mcp_candor/spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,MAAM,CAAC,MAAM,SAAS,GAAG,YAAY,CAAC;AACtC,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC;AACpC,MAAM,CAAC,MAAM,QAAQ,GAAG,wEAAwE,CAAC;AAIjG,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,kBAAkB;IAClB,sBAAsB;IACtB,0BAA0B;CAClB,CAAC;AAEX,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,GAAG,0BAA0B;IAC7B,4BAA4B;IAC5B,qBAAqB;CACb,CAAC;AA0FX,2EAA2E;AAC3E,MAAM,UAAU,iBAAiB,CAAC,KAAsB;IACtD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,0BAA0B,CAAC;IAC3D,OAAO,2BAA2B,CAAC;AACrC,CAAC;AAED;4BAC4B;AAC5B,MAAM,UAAU,iBAAiB,CAAC,CAA2B;IAC3D,MAAM,CAAC,GAAa,EAAE,CAAC;IACvB,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS;QAAE,CAAC,CAAC,IAAI,CAAC,oBAAoB,SAAS,WAAW,CAAC,CAAC,IAAI,IAAI,EAAE,GAAG,CAAC,CAAC;IAC1F,IAAI,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC;QAAE,CAAC,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;IAClG,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI;QAAE,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IACzD,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO;QAAE,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAC/D,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;QAAE,CAAC,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;IAC5H,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,oBAAoB,CAAC;QAAE,CAAC,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;IAChH,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,CAAC,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;SACtG,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,iBAAiB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACxC,KAAK,MAAM,EAAE,IAAI,IAAI;YAAE,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAAE,CAAC,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC,KAAK,MAAM,EAAE,EAAE,CAAC,CAAC;IAC1H,CAAC;IACD,IAAI,OAAO,CAAC,CAAC,aAAa,KAAK,SAAS;QAAE,CAAC,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;IAClF,IAAI,CAAC,CAAC,CAAC,WAAW;QAAE,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IAClD,IAAI,CAAC,CAAC,CAAC,GAAG;QAAE,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAClC,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.23.1 — MCP-CANDOR · VACCINE REGISTRY.
|
|
3
|
+
*
|
|
4
|
+
* Diamond #2 from the v2.22.3 audit: the vaccine cache (known-lie
|
|
5
|
+
* patterns refuted in 0ms) becomes a public, federation-ready
|
|
6
|
+
* registry. Mneme exposes its registry; other CANDOR-compliant
|
|
7
|
+
* servers expose theirs; anyone can pull / contribute.
|
|
8
|
+
*
|
|
9
|
+
* Think CVE database, but for AI hallucination signatures.
|
|
10
|
+
*
|
|
11
|
+
* Format is intentionally minimal so non-Mneme implementations can
|
|
12
|
+
* adopt it without dragging in Mneme-specific types.
|
|
13
|
+
*/
|
|
14
|
+
import { type VaccineEntry } from "./spec.js";
|
|
15
|
+
export interface ContributeOptions {
|
|
16
|
+
type: VaccineEntry["type"];
|
|
17
|
+
signature: string;
|
|
18
|
+
description: string;
|
|
19
|
+
signedBy: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function contributeVaccine(repoRoot: string, opts: ContributeOptions): VaccineEntry;
|
|
22
|
+
export declare function listVaccines(repoRoot: string): VaccineEntry[];
|
|
23
|
+
export declare function findVaccine(repoRoot: string, id: string): VaccineEntry | null;
|
|
24
|
+
/** Pull a foreign registry (JSON array) into the local registry.
|
|
25
|
+
* Dedups by id. Returns the count of newly-imported entries. The
|
|
26
|
+
* caller is responsible for trust-gating WHICH foreign registry to
|
|
27
|
+
* pull from (Mneme suggests gating on a Trust Capsule URI match). */
|
|
28
|
+
export declare function importVaccines(repoRoot: string, foreign: VaccineEntry[]): {
|
|
29
|
+
imported: number;
|
|
30
|
+
skipped: number;
|
|
31
|
+
};
|
|
32
|
+
/** Export the local registry as a list. Suitable for the
|
|
33
|
+
* `candor.vaccines.list` MCP endpoint. */
|
|
34
|
+
export declare function exportVaccines(repoRoot: string): VaccineEntry[];
|
|
35
|
+
/** Verify an entry's HMAC. Uses the LOCAL install key — when the
|
|
36
|
+
* entry was signed by a foreign install, this returns ok:false
|
|
37
|
+
* (correctly). Cross-install verification requires the foreign
|
|
38
|
+
* install's public key, which is out of scope for v0.1. */
|
|
39
|
+
export declare function verifyVaccineSig(repoRoot: string, e: VaccineEntry): {
|
|
40
|
+
ok: boolean;
|
|
41
|
+
reason?: string;
|
|
42
|
+
};
|
|
43
|
+
export declare function formatVaccines(entries: VaccineEntry[]): string;
|
|
44
|
+
//# sourceMappingURL=vaccine_registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vaccine_registry.d.ts","sourceRoot":"","sources":["../../src/mcp_candor/vaccine_registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,EAAa,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AA0BzD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,YAAY,CAkBzF;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,EAAE,CAM7D;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAE7E;AAED;;;sEAGsE;AACtE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAW/G;AAED;2CAC2C;AAC3C,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,EAAE,CAE/D;AAED;;;4DAG4D;AAC5D,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,YAAY,GAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAMpG;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CAU9D"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.23.1 — MCP-CANDOR · VACCINE REGISTRY.
|
|
3
|
+
*
|
|
4
|
+
* Diamond #2 from the v2.22.3 audit: the vaccine cache (known-lie
|
|
5
|
+
* patterns refuted in 0ms) becomes a public, federation-ready
|
|
6
|
+
* registry. Mneme exposes its registry; other CANDOR-compliant
|
|
7
|
+
* servers expose theirs; anyone can pull / contribute.
|
|
8
|
+
*
|
|
9
|
+
* Think CVE database, but for AI hallucination signatures.
|
|
10
|
+
*
|
|
11
|
+
* Format is intentionally minimal so non-Mneme implementations can
|
|
12
|
+
* adopt it without dragging in Mneme-specific types.
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync, readFileSync, appendFileSync, mkdirSync, writeFileSync } from "node:fs";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { createHmac, randomBytes } from "node:crypto";
|
|
17
|
+
import { SPEC_NAME } from "./spec.js";
|
|
18
|
+
const DIR = ".mneme/candor";
|
|
19
|
+
const FILE = "vaccines.jsonl";
|
|
20
|
+
const KEY_FILE = "candor.key";
|
|
21
|
+
function dir(repoRoot) {
|
|
22
|
+
const d = join(repoRoot, DIR);
|
|
23
|
+
if (!existsSync(d))
|
|
24
|
+
mkdirSync(d, { recursive: true });
|
|
25
|
+
return d;
|
|
26
|
+
}
|
|
27
|
+
function key(repoRoot) {
|
|
28
|
+
const p = join(dir(repoRoot), KEY_FILE);
|
|
29
|
+
if (existsSync(p))
|
|
30
|
+
return readFileSync(p, "utf8").trim();
|
|
31
|
+
const k = randomBytes(32).toString("base64url");
|
|
32
|
+
writeFileSync(p, k, "utf8");
|
|
33
|
+
return k;
|
|
34
|
+
}
|
|
35
|
+
function sign(payload, k) {
|
|
36
|
+
return createHmac("sha256", k).update(payload).digest("base64url").slice(0, 22);
|
|
37
|
+
}
|
|
38
|
+
function filePath(repoRoot) { return join(dir(repoRoot), FILE); }
|
|
39
|
+
export function contributeVaccine(repoRoot, opts) {
|
|
40
|
+
const k = key(repoRoot);
|
|
41
|
+
const observedAt = new Date().toISOString();
|
|
42
|
+
// Derive the id from the signature so identical signatures don't
|
|
43
|
+
// create duplicate entries across servers.
|
|
44
|
+
const id = "vc_" + createHmac("sha256", "candor-vaccine-id").update(opts.signature).digest("base64url").slice(0, 16);
|
|
45
|
+
const canonical = `${id}|${opts.type}|${opts.signature}|${opts.signedBy}|${observedAt}`;
|
|
46
|
+
const sig = sign(canonical, k);
|
|
47
|
+
const entry = {
|
|
48
|
+
id, type: opts.type, signature: opts.signature, description: opts.description,
|
|
49
|
+
signedBy: opts.signedBy, observedAt, sig,
|
|
50
|
+
};
|
|
51
|
+
// Deduplicate by id — if already present, skip the append.
|
|
52
|
+
const all = listVaccines(repoRoot);
|
|
53
|
+
if (!all.find((e) => e.id === entry.id)) {
|
|
54
|
+
appendFileSync(filePath(repoRoot), JSON.stringify(entry) + "\n", "utf8");
|
|
55
|
+
}
|
|
56
|
+
return entry;
|
|
57
|
+
}
|
|
58
|
+
export function listVaccines(repoRoot) {
|
|
59
|
+
const p = filePath(repoRoot);
|
|
60
|
+
if (!existsSync(p))
|
|
61
|
+
return [];
|
|
62
|
+
try {
|
|
63
|
+
return readFileSync(p, "utf8").trim().split("\n").map((l) => { try {
|
|
64
|
+
return JSON.parse(l);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return null;
|
|
68
|
+
} }).filter((e) => !!e);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export function findVaccine(repoRoot, id) {
|
|
75
|
+
return listVaccines(repoRoot).find((e) => e.id === id) ?? null;
|
|
76
|
+
}
|
|
77
|
+
/** Pull a foreign registry (JSON array) into the local registry.
|
|
78
|
+
* Dedups by id. Returns the count of newly-imported entries. The
|
|
79
|
+
* caller is responsible for trust-gating WHICH foreign registry to
|
|
80
|
+
* pull from (Mneme suggests gating on a Trust Capsule URI match). */
|
|
81
|
+
export function importVaccines(repoRoot, foreign) {
|
|
82
|
+
const existing = new Set(listVaccines(repoRoot).map((e) => e.id));
|
|
83
|
+
let imported = 0, skipped = 0;
|
|
84
|
+
for (const e of foreign) {
|
|
85
|
+
if (!e.id || !e.signature) {
|
|
86
|
+
skipped++;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (existing.has(e.id)) {
|
|
90
|
+
skipped++;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
appendFileSync(filePath(repoRoot), JSON.stringify(e) + "\n", "utf8");
|
|
94
|
+
existing.add(e.id);
|
|
95
|
+
imported++;
|
|
96
|
+
}
|
|
97
|
+
return { imported, skipped };
|
|
98
|
+
}
|
|
99
|
+
/** Export the local registry as a list. Suitable for the
|
|
100
|
+
* `candor.vaccines.list` MCP endpoint. */
|
|
101
|
+
export function exportVaccines(repoRoot) {
|
|
102
|
+
return listVaccines(repoRoot);
|
|
103
|
+
}
|
|
104
|
+
/** Verify an entry's HMAC. Uses the LOCAL install key — when the
|
|
105
|
+
* entry was signed by a foreign install, this returns ok:false
|
|
106
|
+
* (correctly). Cross-install verification requires the foreign
|
|
107
|
+
* install's public key, which is out of scope for v0.1. */
|
|
108
|
+
export function verifyVaccineSig(repoRoot, e) {
|
|
109
|
+
const k = key(repoRoot);
|
|
110
|
+
const canonical = `${e.id}|${e.type}|${e.signature}|${e.signedBy}|${e.observedAt}`;
|
|
111
|
+
const expected = sign(canonical, k);
|
|
112
|
+
if (expected !== e.sig)
|
|
113
|
+
return { ok: false, reason: "HMAC sig mismatch — entry not signed by this install" };
|
|
114
|
+
return { ok: true };
|
|
115
|
+
}
|
|
116
|
+
export function formatVaccines(entries) {
|
|
117
|
+
if (entries.length === 0)
|
|
118
|
+
return `🦠 ${SPEC_NAME} VACCINE REGISTRY — empty`;
|
|
119
|
+
const lines = [`🦠 ${SPEC_NAME} VACCINE REGISTRY — ${entries.length} entries`, ""];
|
|
120
|
+
for (const e of entries.slice(0, 20)) {
|
|
121
|
+
lines.push(` [${e.id}] type=${e.type.padEnd(10)} by=${e.signedBy.padEnd(20)} ${e.observedAt}`);
|
|
122
|
+
lines.push(` sig: ${e.signature.slice(0, 60)}`);
|
|
123
|
+
lines.push(` desc: ${e.description.slice(0, 80)}`);
|
|
124
|
+
}
|
|
125
|
+
if (entries.length > 20)
|
|
126
|
+
lines.push(` (showing 20 of ${entries.length})`);
|
|
127
|
+
return lines.join("\n");
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=vaccine_registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vaccine_registry.js","sourceRoot":"","sources":["../../src/mcp_candor/vaccine_registry.ts"],"names":[],"mappings":"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"}
|