plainstamp 0.0.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/AI-DISCLOSURE.md +39 -0
- package/CHANGELOG.md +57 -0
- package/LICENSE +21 -0
- package/README.md +179 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +147 -0
- package/dist/cli.js.map +1 -0
- package/dist/coverage.d.ts +48 -0
- package/dist/coverage.d.ts.map +1 -0
- package/dist/coverage.js +96 -0
- package/dist/coverage.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/lookup.d.ts +42 -0
- package/dist/lookup.d.ts.map +1 -0
- package/dist/lookup.js +170 -0
- package/dist/lookup.js.map +1 -0
- package/dist/mcp-server.d.ts +3 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +199 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/rules-loader.d.ts +10 -0
- package/dist/rules-loader.d.ts.map +1 -0
- package/dist/rules-loader.js +23 -0
- package/dist/rules-loader.js.map +1 -0
- package/dist/schema.d.ts +526 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +96 -0
- package/dist/schema.js.map +1 -0
- package/dist/watcher/cli.d.ts +3 -0
- package/dist/watcher/cli.d.ts.map +1 -0
- package/dist/watcher/cli.js +47 -0
- package/dist/watcher/cli.js.map +1 -0
- package/dist/watcher/index.d.ts +23 -0
- package/dist/watcher/index.d.ts.map +1 -0
- package/dist/watcher/index.js +71 -0
- package/dist/watcher/index.js.map +1 -0
- package/dist/watcher/sources/federal-register.d.ts +13 -0
- package/dist/watcher/sources/federal-register.d.ts.map +1 -0
- package/dist/watcher/sources/federal-register.js +44 -0
- package/dist/watcher/sources/federal-register.js.map +1 -0
- package/dist/watcher/sources/url-monitor.d.ts +33 -0
- package/dist/watcher/sources/url-monitor.d.ts.map +1 -0
- package/dist/watcher/sources/url-monitor.js +67 -0
- package/dist/watcher/sources/url-monitor.js.map +1 -0
- package/dist/watcher/state-store.d.ts +9 -0
- package/dist/watcher/state-store.d.ts.map +1 -0
- package/dist/watcher/state-store.js +23 -0
- package/dist/watcher/state-store.js.map +1 -0
- package/dist/watcher/types.d.ts +59 -0
- package/dist/watcher/types.d.ts.map +1 -0
- package/dist/watcher/types.js +14 -0
- package/dist/watcher/types.js.map +1 -0
- package/package.json +60 -0
- package/rules/seed.json +620 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,YAAY,GAClB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,eAAe,EACf,QAAQ,EACR,YAAY,EACZ,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,eAAe,EACf,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAIrB,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEhF;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,aAAa,EAAE,CAEnE;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,IAAI,MAAM,EAAE,CAK5C;AAED,uDAAuD;AACvD,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAGnE;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,YAAY,EACnB,aAAa,EAAE,MAAM;;;;;IAKtB"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export { lookup, generateDisclosureText, validateDisclosure, } from "./lookup.js";
|
|
2
|
+
export { loadBundledRules, loadRulesFromPath } from "./rules-loader.js";
|
|
3
|
+
export { computeCoverageMatrix, renderCoverageMarkdown, renderCoverageCsv, } from "./coverage.js";
|
|
4
|
+
import { loadBundledRules } from "./rules-loader.js";
|
|
5
|
+
import { lookup as lookupFn, validateDisclosure as validateFn } from "./lookup.js";
|
|
6
|
+
/**
|
|
7
|
+
* High-level convenience: load the bundled rules and look up disclosures for
|
|
8
|
+
* a query. For long-running processes that issue many lookups, prefer caching
|
|
9
|
+
* the rule set with `loadBundledRules()` once.
|
|
10
|
+
*/
|
|
11
|
+
export function disclosuresFor(query) {
|
|
12
|
+
return lookupFn(loadBundledRules(), query);
|
|
13
|
+
}
|
|
14
|
+
/** Returns every distinct jurisdiction id present in the bundled rules. */
|
|
15
|
+
export function listJurisdictions() {
|
|
16
|
+
const rules = loadBundledRules();
|
|
17
|
+
const set = new Set();
|
|
18
|
+
for (const r of rules.rules)
|
|
19
|
+
set.add(r.jurisdiction);
|
|
20
|
+
return [...set].sort();
|
|
21
|
+
}
|
|
22
|
+
/** Returns the rule whose id matches, or undefined. */
|
|
23
|
+
export function getRuleById(id) {
|
|
24
|
+
const rules = loadBundledRules();
|
|
25
|
+
return rules.rules.find((r) => r.id === id);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Heuristic disclosure-text validator (substring check). NOT a legal-sufficiency
|
|
29
|
+
* determination. Returns one validation report per rule that applies to the
|
|
30
|
+
* provided query.
|
|
31
|
+
*/
|
|
32
|
+
export function validateDisclosureForQuery(query, candidateText) {
|
|
33
|
+
const rules = loadBundledRules();
|
|
34
|
+
const applicable = lookupFn(rules, query);
|
|
35
|
+
return applicable.map((m) => validateFn(m.rule, candidateText));
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,GAGlB,MAAM,eAAe,CAAC;AAavB,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,MAAM,IAAI,QAAQ,EAAE,kBAAkB,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AAGnF;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,KAAmB;IAChD,OAAO,QAAQ,CAAC,gBAAgB,EAAE,EAAE,KAAK,CAAC,CAAC;AAC7C,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,iBAAiB;IAC/B,MAAM,KAAK,GAAG,gBAAgB,EAAE,CAAC;IACjC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK;QAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;IACrD,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,WAAW,CAAC,EAAU;IACpC,MAAM,KAAK,GAAG,gBAAgB,EAAE,CAAC;IACjC,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CACxC,KAAmB,EACnB,aAAqB;IAErB,MAAM,KAAK,GAAG,gBAAgB,EAAE,CAAC;IACjC,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC1C,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC;AAClE,CAAC"}
|
package/dist/lookup.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type DisclosureRuleT, type LookupQueryT, type LookupResultT, type RuleSetT } from "./schema.js";
|
|
2
|
+
/**
|
|
3
|
+
* Returns the rules that apply to a given (jurisdiction × channel × use_case)
|
|
4
|
+
* query, plus a generated disclosure-text result for each.
|
|
5
|
+
*
|
|
6
|
+
* Match semantics:
|
|
7
|
+
* - jurisdiction: the rule's jurisdiction must equal the query jurisdiction,
|
|
8
|
+
* or the rule's jurisdiction must be a less-specific prefix of the query
|
|
9
|
+
* jurisdiction. Example: a query for "us-ca" matches rules with
|
|
10
|
+
* jurisdiction "us-ca" (exact) and rules with jurisdiction "us" (parent).
|
|
11
|
+
* - channel: the rule's channels list must include the query channel.
|
|
12
|
+
* - use_case: the rule's use_cases list must include the query use_case
|
|
13
|
+
* OR include the universal "general" use_case.
|
|
14
|
+
*
|
|
15
|
+
* Rules are returned in deterministic order: by severity (mandatory first),
|
|
16
|
+
* then by id (alphabetical).
|
|
17
|
+
*/
|
|
18
|
+
export declare function lookup(rules: RuleSetT, query: LookupQueryT): LookupResultT[];
|
|
19
|
+
export declare function generateDisclosureText(rule: DisclosureRuleT): {
|
|
20
|
+
plain: string;
|
|
21
|
+
formal?: string | undefined;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Validates a candidate disclosure text against a rule's required elements.
|
|
25
|
+
*
|
|
26
|
+
* The check is heuristic. For each required element it derives a small set of
|
|
27
|
+
* keyword signals (id tokens, significant words from the element description
|
|
28
|
+
* and example) and checks whether any appear in the candidate (case-insensitive).
|
|
29
|
+
* An element with no signals found is reported as missing.
|
|
30
|
+
*
|
|
31
|
+
* This is a sanity check, NOT a legal-sufficiency determination. False
|
|
32
|
+
* negatives (rejecting compliant text) are possible and false positives
|
|
33
|
+
* (accepting non-compliant text) are possible. For high-stakes disclosures,
|
|
34
|
+
* verify against the cited regulator-published text and consult counsel.
|
|
35
|
+
*/
|
|
36
|
+
export declare function validateDisclosure(rule: DisclosureRuleT, candidate: string): {
|
|
37
|
+
rule_id: string;
|
|
38
|
+
passes: boolean;
|
|
39
|
+
missing_elements: string[];
|
|
40
|
+
warning: string;
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=lookup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lookup.d.ts","sourceRoot":"","sources":["../src/lookup.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,QAAQ,EACd,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,GAAG,aAAa,EAAE,CAuB5E;AAoCD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,eAAe,GAAG;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B,CAOA;AAiED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,eAAe,EACrB,SAAS,EAAE,MAAM,GAChB;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB,CAkBA"}
|
package/dist/lookup.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the rules that apply to a given (jurisdiction × channel × use_case)
|
|
3
|
+
* query, plus a generated disclosure-text result for each.
|
|
4
|
+
*
|
|
5
|
+
* Match semantics:
|
|
6
|
+
* - jurisdiction: the rule's jurisdiction must equal the query jurisdiction,
|
|
7
|
+
* or the rule's jurisdiction must be a less-specific prefix of the query
|
|
8
|
+
* jurisdiction. Example: a query for "us-ca" matches rules with
|
|
9
|
+
* jurisdiction "us-ca" (exact) and rules with jurisdiction "us" (parent).
|
|
10
|
+
* - channel: the rule's channels list must include the query channel.
|
|
11
|
+
* - use_case: the rule's use_cases list must include the query use_case
|
|
12
|
+
* OR include the universal "general" use_case.
|
|
13
|
+
*
|
|
14
|
+
* Rules are returned in deterministic order: by severity (mandatory first),
|
|
15
|
+
* then by id (alphabetical).
|
|
16
|
+
*/
|
|
17
|
+
export function lookup(rules, query) {
|
|
18
|
+
const matches = [];
|
|
19
|
+
for (const rule of rules.rules) {
|
|
20
|
+
const reasons = matchReasons(rule, query);
|
|
21
|
+
if (reasons.length === 0)
|
|
22
|
+
continue;
|
|
23
|
+
const generated = generateDisclosureText(rule);
|
|
24
|
+
matches.push({
|
|
25
|
+
rule,
|
|
26
|
+
applies_because: reasons,
|
|
27
|
+
generated_text: generated,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
matches.sort((a, b) => {
|
|
31
|
+
const sevOrder = { mandatory: 0, recommended: 1, "best-practice": 2 };
|
|
32
|
+
const sevDiff = sevOrder[a.rule.severity] - sevOrder[b.rule.severity];
|
|
33
|
+
if (sevDiff !== 0)
|
|
34
|
+
return sevDiff;
|
|
35
|
+
return a.rule.id.localeCompare(b.rule.id);
|
|
36
|
+
});
|
|
37
|
+
return matches;
|
|
38
|
+
}
|
|
39
|
+
function matchReasons(rule, query) {
|
|
40
|
+
const reasons = [];
|
|
41
|
+
if (!matchesJurisdiction(rule.jurisdiction, query.jurisdiction))
|
|
42
|
+
return [];
|
|
43
|
+
reasons.push(rule.jurisdiction === query.jurisdiction
|
|
44
|
+
? `jurisdiction exact match: ${rule.jurisdiction}`
|
|
45
|
+
: `jurisdiction parent match: rule covers '${rule.jurisdiction}', query is '${query.jurisdiction}'`);
|
|
46
|
+
if (!rule.channels.includes(query.channel))
|
|
47
|
+
return [];
|
|
48
|
+
reasons.push(`channel match: rule covers '${query.channel}'`);
|
|
49
|
+
const matchesGeneral = rule.use_cases.includes("general");
|
|
50
|
+
if (!rule.use_cases.includes(query.use_case) && !matchesGeneral)
|
|
51
|
+
return [];
|
|
52
|
+
reasons.push(rule.use_cases.includes(query.use_case)
|
|
53
|
+
? `use case match: rule covers '${query.use_case}'`
|
|
54
|
+
: `use case match via 'general' (rule applies to general AI interactions)`);
|
|
55
|
+
return reasons;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* A rule's jurisdiction matches a query jurisdiction if they're equal or if
|
|
59
|
+
* the rule's jurisdiction is a hyphen-bounded prefix of the query (so 'us'
|
|
60
|
+
* matches 'us-ca' but not 'us-coxx' — and certainly not 'usa').
|
|
61
|
+
*/
|
|
62
|
+
function matchesJurisdiction(rule, query) {
|
|
63
|
+
if (rule === query)
|
|
64
|
+
return true;
|
|
65
|
+
return query.startsWith(`${rule}-`);
|
|
66
|
+
}
|
|
67
|
+
export function generateDisclosureText(rule) {
|
|
68
|
+
return {
|
|
69
|
+
plain: rule.template.plain,
|
|
70
|
+
...(rule.template.formal === undefined
|
|
71
|
+
? {}
|
|
72
|
+
: { formal: rule.template.formal }),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const STOPWORDS = new Set([
|
|
76
|
+
"about",
|
|
77
|
+
"above",
|
|
78
|
+
"after",
|
|
79
|
+
"again",
|
|
80
|
+
"against",
|
|
81
|
+
"being",
|
|
82
|
+
"below",
|
|
83
|
+
"between",
|
|
84
|
+
"both",
|
|
85
|
+
"doing",
|
|
86
|
+
"during",
|
|
87
|
+
"each",
|
|
88
|
+
"from",
|
|
89
|
+
"further",
|
|
90
|
+
"have",
|
|
91
|
+
"here",
|
|
92
|
+
"into",
|
|
93
|
+
"more",
|
|
94
|
+
"most",
|
|
95
|
+
"must",
|
|
96
|
+
"other",
|
|
97
|
+
"over",
|
|
98
|
+
"same",
|
|
99
|
+
"should",
|
|
100
|
+
"some",
|
|
101
|
+
"such",
|
|
102
|
+
"than",
|
|
103
|
+
"that",
|
|
104
|
+
"their",
|
|
105
|
+
"them",
|
|
106
|
+
"these",
|
|
107
|
+
"they",
|
|
108
|
+
"this",
|
|
109
|
+
"those",
|
|
110
|
+
"through",
|
|
111
|
+
"under",
|
|
112
|
+
"until",
|
|
113
|
+
"what",
|
|
114
|
+
"when",
|
|
115
|
+
"where",
|
|
116
|
+
"which",
|
|
117
|
+
"while",
|
|
118
|
+
"with",
|
|
119
|
+
"would",
|
|
120
|
+
"your",
|
|
121
|
+
]);
|
|
122
|
+
function signalsFor(element) {
|
|
123
|
+
const signals = [];
|
|
124
|
+
for (const token of element.id.split("-")) {
|
|
125
|
+
if (token.length > 3)
|
|
126
|
+
signals.push(token.toLowerCase());
|
|
127
|
+
}
|
|
128
|
+
const collect = (text) => {
|
|
129
|
+
for (const word of text.toLowerCase().split(/[^a-z0-9]+/)) {
|
|
130
|
+
if (word.length > 4 && !STOPWORDS.has(word))
|
|
131
|
+
signals.push(word);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
collect(element.description);
|
|
135
|
+
if (element.example !== undefined)
|
|
136
|
+
collect(element.example);
|
|
137
|
+
return [...new Set(signals)];
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Validates a candidate disclosure text against a rule's required elements.
|
|
141
|
+
*
|
|
142
|
+
* The check is heuristic. For each required element it derives a small set of
|
|
143
|
+
* keyword signals (id tokens, significant words from the element description
|
|
144
|
+
* and example) and checks whether any appear in the candidate (case-insensitive).
|
|
145
|
+
* An element with no signals found is reported as missing.
|
|
146
|
+
*
|
|
147
|
+
* This is a sanity check, NOT a legal-sufficiency determination. False
|
|
148
|
+
* negatives (rejecting compliant text) are possible and false positives
|
|
149
|
+
* (accepting non-compliant text) are possible. For high-stakes disclosures,
|
|
150
|
+
* verify against the cited regulator-published text and consult counsel.
|
|
151
|
+
*/
|
|
152
|
+
export function validateDisclosure(rule, candidate) {
|
|
153
|
+
const lower = candidate.toLowerCase();
|
|
154
|
+
const missing = [];
|
|
155
|
+
for (const el of rule.required_elements) {
|
|
156
|
+
if (!el.required)
|
|
157
|
+
continue;
|
|
158
|
+
const signals = signalsFor(el);
|
|
159
|
+
const found = signals.some((s) => lower.includes(s));
|
|
160
|
+
if (!found)
|
|
161
|
+
missing.push(el.id);
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
rule_id: rule.id,
|
|
165
|
+
passes: missing.length === 0,
|
|
166
|
+
missing_elements: missing,
|
|
167
|
+
warning: "Heuristic substring check. NOT a legal-sufficiency determination. For high-stakes disclosures, verify against the cited regulator-published text and consult counsel.",
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=lookup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lookup.js","sourceRoot":"","sources":["../src/lookup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import { disclosuresFor, getRuleById, listJurisdictions, loadBundledRules, validateDisclosureForQuery, } from "./index.js";
|
|
6
|
+
import { Channel, LookupQuery, UseCase } from "./schema.js";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
/**
|
|
9
|
+
* MCP server exposing the disclo lookup engine to autonomous agents.
|
|
10
|
+
*
|
|
11
|
+
* Tool surface:
|
|
12
|
+
* - list_jurisdictions -> returns supported jurisdiction ids
|
|
13
|
+
* - list_rules -> returns rule summaries (id, jurisdiction, severity, title)
|
|
14
|
+
* - get_rule -> returns a single rule with full citation and template
|
|
15
|
+
* - lookup_disclosure -> primary tool: applicable rules + generated text for a (jurisdiction, channel, use_case)
|
|
16
|
+
* - validate_disclosure -> heuristic check that a candidate disclosure mentions a rule's required elements
|
|
17
|
+
*/
|
|
18
|
+
const server = new Server({
|
|
19
|
+
name: "plainstamp",
|
|
20
|
+
version: "0.0.1",
|
|
21
|
+
}, {
|
|
22
|
+
capabilities: {
|
|
23
|
+
tools: {},
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
const ValidateInput = LookupQuery.extend({
|
|
27
|
+
candidate_text: z.string().min(1),
|
|
28
|
+
});
|
|
29
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
30
|
+
tools: [
|
|
31
|
+
{
|
|
32
|
+
name: "list_jurisdictions",
|
|
33
|
+
description: "List all jurisdiction ids supported by the bundled rules. Use this first to discover what's covered before calling lookup_disclosure.",
|
|
34
|
+
inputSchema: {
|
|
35
|
+
type: "object",
|
|
36
|
+
properties: {},
|
|
37
|
+
additionalProperties: false,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "list_rules",
|
|
42
|
+
description: "List all bundled disclosure rules as compact summaries. Useful for browsing the rule database.",
|
|
43
|
+
inputSchema: {
|
|
44
|
+
type: "object",
|
|
45
|
+
properties: {},
|
|
46
|
+
additionalProperties: false,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "get_rule",
|
|
51
|
+
description: "Fetch a single disclosure rule by id, with full citation, required elements, templates, and last-verified date.",
|
|
52
|
+
inputSchema: {
|
|
53
|
+
type: "object",
|
|
54
|
+
properties: {
|
|
55
|
+
rule_id: { type: "string" },
|
|
56
|
+
},
|
|
57
|
+
required: ["rule_id"],
|
|
58
|
+
additionalProperties: false,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "lookup_disclosure",
|
|
63
|
+
description: "Primary tool. Given a jurisdiction (e.g. 'us-ca' or 'eu'), a channel (e.g. 'live-chat', 'ai-generated-content'), and a use case (e.g. 'b2c-customer-support'), return every applicable disclosure rule plus a generated plain-language and formal disclosure text. Results are sorted with mandatory rules first.",
|
|
64
|
+
inputSchema: {
|
|
65
|
+
type: "object",
|
|
66
|
+
properties: {
|
|
67
|
+
jurisdiction: {
|
|
68
|
+
type: "string",
|
|
69
|
+
description: "Lowercase jurisdiction id (ISO-style). Examples: 'us', 'us-ca', 'eu'.",
|
|
70
|
+
},
|
|
71
|
+
channel: {
|
|
72
|
+
type: "string",
|
|
73
|
+
enum: Channel.options,
|
|
74
|
+
},
|
|
75
|
+
use_case: {
|
|
76
|
+
type: "string",
|
|
77
|
+
enum: UseCase.options,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
required: ["jurisdiction", "channel", "use_case"],
|
|
81
|
+
additionalProperties: false,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: "validate_disclosure",
|
|
86
|
+
description: "Heuristic substring check that a candidate disclosure text mentions the required elements of every rule that applies to the (jurisdiction, channel, use_case). Returns a per-rule pass/fail report. NOT a legal-sufficiency determination — for high-stakes disclosures, verify against the cited regulator-published text and consult counsel.",
|
|
87
|
+
inputSchema: {
|
|
88
|
+
type: "object",
|
|
89
|
+
properties: {
|
|
90
|
+
jurisdiction: { type: "string" },
|
|
91
|
+
channel: { type: "string", enum: Channel.options },
|
|
92
|
+
use_case: { type: "string", enum: UseCase.options },
|
|
93
|
+
candidate_text: { type: "string", minLength: 1 },
|
|
94
|
+
},
|
|
95
|
+
required: ["jurisdiction", "channel", "use_case", "candidate_text"],
|
|
96
|
+
additionalProperties: false,
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
}));
|
|
101
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
102
|
+
const { name, arguments: args } = req.params;
|
|
103
|
+
switch (name) {
|
|
104
|
+
case "list_jurisdictions": {
|
|
105
|
+
const ids = listJurisdictions();
|
|
106
|
+
return {
|
|
107
|
+
content: [
|
|
108
|
+
{ type: "text", text: JSON.stringify({ jurisdictions: ids }, null, 2) },
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
case "list_rules": {
|
|
113
|
+
const rules = loadBundledRules();
|
|
114
|
+
const summaries = rules.rules.map((r) => ({
|
|
115
|
+
id: r.id,
|
|
116
|
+
jurisdiction: r.jurisdiction,
|
|
117
|
+
severity: r.severity,
|
|
118
|
+
short_title: r.short_title,
|
|
119
|
+
channels: r.channels,
|
|
120
|
+
use_cases: r.use_cases,
|
|
121
|
+
last_verified: r.last_verified,
|
|
122
|
+
}));
|
|
123
|
+
return {
|
|
124
|
+
content: [
|
|
125
|
+
{
|
|
126
|
+
type: "text",
|
|
127
|
+
text: JSON.stringify({
|
|
128
|
+
schema_version: rules.schema_version,
|
|
129
|
+
generated_at: rules.generated_at,
|
|
130
|
+
rules: summaries,
|
|
131
|
+
}, null, 2),
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
case "get_rule": {
|
|
137
|
+
const ruleId = z.object({ rule_id: z.string() }).parse(args).rule_id;
|
|
138
|
+
const rule = getRuleById(ruleId);
|
|
139
|
+
if (rule === undefined) {
|
|
140
|
+
return {
|
|
141
|
+
isError: true,
|
|
142
|
+
content: [{ type: "text", text: `unknown rule_id: ${ruleId}` }],
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
content: [{ type: "text", text: JSON.stringify(rule, null, 2) }],
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
case "lookup_disclosure": {
|
|
150
|
+
const parsed = LookupQuery.parse(args);
|
|
151
|
+
const results = disclosuresFor(parsed);
|
|
152
|
+
return {
|
|
153
|
+
content: [
|
|
154
|
+
{
|
|
155
|
+
type: "text",
|
|
156
|
+
text: JSON.stringify({
|
|
157
|
+
query: parsed,
|
|
158
|
+
disclaimer: "Informational only. Not legal advice. Each rule cites its source; verify against the regulator-published text before relying on it.",
|
|
159
|
+
count: results.length,
|
|
160
|
+
results,
|
|
161
|
+
}, null, 2),
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
case "validate_disclosure": {
|
|
167
|
+
const parsed = ValidateInput.parse(args);
|
|
168
|
+
const reports = validateDisclosureForQuery({
|
|
169
|
+
jurisdiction: parsed.jurisdiction,
|
|
170
|
+
channel: parsed.channel,
|
|
171
|
+
use_case: parsed.use_case,
|
|
172
|
+
}, parsed.candidate_text);
|
|
173
|
+
return {
|
|
174
|
+
content: [
|
|
175
|
+
{
|
|
176
|
+
type: "text",
|
|
177
|
+
text: JSON.stringify({
|
|
178
|
+
query: {
|
|
179
|
+
jurisdiction: parsed.jurisdiction,
|
|
180
|
+
channel: parsed.channel,
|
|
181
|
+
use_case: parsed.use_case,
|
|
182
|
+
},
|
|
183
|
+
disclaimer: "Heuristic substring check. NOT a legal-sufficiency determination.",
|
|
184
|
+
reports,
|
|
185
|
+
}, null, 2),
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
default:
|
|
191
|
+
return {
|
|
192
|
+
isError: true,
|
|
193
|
+
content: [{ type: "text", text: `unknown tool: ${name}` }],
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
const transport = new StdioServerTransport();
|
|
198
|
+
await server.connect(transport);
|
|
199
|
+
//# sourceMappingURL=mcp-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type RuleSetT } from "./schema.js";
|
|
2
|
+
/**
|
|
3
|
+
* Loads and validates the bundled seed rules. Throws on schema violation —
|
|
4
|
+
* an invalid rules file is a hard failure because the product's correctness
|
|
5
|
+
* depends on it.
|
|
6
|
+
*/
|
|
7
|
+
export declare function loadBundledRules(): RuleSetT;
|
|
8
|
+
/** Loads rules from an arbitrary path. Useful for tests and for callers that maintain their own rules feed. */
|
|
9
|
+
export declare function loadRulesFromPath(path: string): RuleSetT;
|
|
10
|
+
//# sourceMappingURL=rules-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules-loader.d.ts","sourceRoot":"","sources":["../src/rules-loader.ts"],"names":[],"mappings":"AAGA,OAAO,EAAW,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAIrD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,QAAQ,CAK3C;AAED,+GAA+G;AAC/G,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAIxD"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { RuleSet } from "./schema.js";
|
|
5
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
/**
|
|
7
|
+
* Loads and validates the bundled seed rules. Throws on schema violation —
|
|
8
|
+
* an invalid rules file is a hard failure because the product's correctness
|
|
9
|
+
* depends on it.
|
|
10
|
+
*/
|
|
11
|
+
export function loadBundledRules() {
|
|
12
|
+
const path = join(here, "..", "rules", "seed.json");
|
|
13
|
+
const raw = readFileSync(path, "utf-8");
|
|
14
|
+
const parsed = JSON.parse(raw);
|
|
15
|
+
return RuleSet.parse(parsed);
|
|
16
|
+
}
|
|
17
|
+
/** Loads rules from an arbitrary path. Useful for tests and for callers that maintain their own rules feed. */
|
|
18
|
+
export function loadRulesFromPath(path) {
|
|
19
|
+
const raw = readFileSync(path, "utf-8");
|
|
20
|
+
const parsed = JSON.parse(raw);
|
|
21
|
+
return RuleSet.parse(parsed);
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=rules-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules-loader.js","sourceRoot":"","sources":["../src/rules-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAiB,MAAM,aAAa,CAAC;AAErD,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAErD;;;;GAIG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC;IACpD,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,OAAO,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC;AAED,+GAA+G;AAC/G,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,OAAO,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC"}
|