plainstamp 0.5.0 → 0.7.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/CHANGELOG.md CHANGED
@@ -16,6 +16,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
16
16
 
17
17
  Distribution is **npm-only**. Source remains in the operating organization's private repository; there is no public source repository host. Contact channel for issues, accuracy reports, security reports, and contribution proposals is **helpfulbutton140@agentmail.to** (see `docs/CONTRIBUTING.md`, `docs/SECURITY.md`).
18
18
 
19
+ ## [0.7.0] — 2026-05-08
20
+
21
+ ### Added (transport-independent MCP tool module)
22
+
23
+ - New module `src/mcp-tools.ts` exporting `mcpTools` (the tool descriptors) and `executeMcpTool(name, args)` (the dispatcher). Both are now public API exports from the package root. Purpose: when the Cloudflare Workers cf-worker binds an MCP Streamable HTTP transport in Phase 4 of `<autobiz>/ops/cloudflare/CLOUDFLARE_DEPLOY.md`, it imports the same tool list and dispatcher used by the existing stdio transport. No drift between transports.
24
+ - `mcp-server.ts` (the stdio transport) is now a thin wrapper around `mcpTools` and `executeMcpTool`. Behavior is unchanged for stdio clients.
25
+ - Tests still 51/51 passing. Rule count unchanged at 23.
26
+
27
+ ## [0.6.0] — 2026-05-08
28
+
29
+ ### Added
30
+
31
+ - FCC Declaratory Ruling on AI-generated voice in robocalls (CG Docket No. 23-362, FCC 24-17, released February 8, 2024). Confirms that AI-generated voice clones and AI-synthesized voices used in calls to consumers are "artificial or prerecorded voices" within the meaning of the Telephone Consumer Protection Act of 1991 (47 U.S.C. § 227) and the Commission's implementing rules at 47 CFR § 64.1200. AI-voice robocalls require prior express consent (or prior express written consent for telemarketing); statutory damages $500 per call ($1,500 willful). Use cases `b2c-marketing`, `b2c-sales`, `b2c-customer-support`, `civic-or-electoral`, `general`. Channel `voice`. Severity `mandatory`.
32
+ - Runtime Zod schema exports: `Channel`, `UseCase`, `Severity`, `JurisdictionId`, `LookupQuery`, `DisclosureElement`, `DisclosureRule`, `RuleSet` are now exported from the package root (previously only the corresponding TypeScript types were exported). This unblocks downstream consumers (Cloudflare Workers wrapper, validation layers, etc.) from re-implementing the validators.
33
+ - Rule count 22 → 23. Tests still 51/51 passing.
34
+
35
+ ### Sibling project (not bundled in npm)
36
+
37
+ - `cf-worker/` — Cloudflare Workers HTTP wrapper that exposes the plainstamp lookup engine over JSON-over-HTTP. Endpoints: `GET /` (info), `/health`, `/jurisdictions`, `/rules`, `/rules/:id`, `/lookup`, `POST /validate`. Scaffold only in this release (deploy in next iteration). Plan doc at `<autobiz>/ops/cloudflare/CLOUDFLARE_DEPLOY.md`. The cf-worker depends on plainstamp@^0.6.0 (this release).
38
+
19
39
  ## [0.5.0] — 2026-05-08
20
40
 
21
41
  ### Added
package/dist/index.d.ts CHANGED
@@ -2,6 +2,8 @@ export { lookup, generateDisclosureText, validateDisclosure, } from "./lookup.js
2
2
  export { loadBundledRules, loadRulesFromPath } from "./rules-loader.js";
3
3
  export { computeCoverageMatrix, renderCoverageMarkdown, renderCoverageCsv, type CoverageMatrix, type CoverageCell, } from "./coverage.js";
4
4
  export type { DisclosureRuleT, RuleSetT, LookupQueryT, LookupResultT, ChannelT, UseCaseT, SeverityT, JurisdictionIdT, DisclosureElementT, } from "./schema.js";
5
+ export { Channel, UseCase, Severity, JurisdictionId, LookupQuery, DisclosureElement, DisclosureRule, RuleSet, } from "./schema.js";
6
+ export { mcpTools, executeMcpTool, type McpToolDescriptor, type McpToolResult, } from "./mcp-tools.js";
5
7
  import type { LookupQueryT, LookupResultT, DisclosureRuleT } from "./schema.js";
6
8
  /**
7
9
  * High-level convenience: load the bundled rules and look up disclosures for
@@ -1 +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"}
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;AACrB,OAAO,EACL,OAAO,EACP,OAAO,EACP,QAAQ,EACR,cAAc,EACd,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,OAAO,GACR,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,QAAQ,EACR,cAAc,EACd,KAAK,iBAAiB,EACtB,KAAK,aAAa,GACnB,MAAM,gBAAgB,CAAC;AAIxB,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 CHANGED
@@ -1,6 +1,8 @@
1
1
  export { lookup, generateDisclosureText, validateDisclosure, } from "./lookup.js";
2
2
  export { loadBundledRules, loadRulesFromPath } from "./rules-loader.js";
3
3
  export { computeCoverageMatrix, renderCoverageMarkdown, renderCoverageCsv, } from "./coverage.js";
4
+ export { Channel, UseCase, Severity, JurisdictionId, LookupQuery, DisclosureElement, DisclosureRule, RuleSet, } from "./schema.js";
5
+ export { mcpTools, executeMcpTool, } from "./mcp-tools.js";
4
6
  import { loadBundledRules } from "./rules-loader.js";
5
7
  import { lookup as lookupFn, validateDisclosure as validateFn } from "./lookup.js";
6
8
  /**
package/dist/index.js.map CHANGED
@@ -1 +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"}
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;AAYvB,OAAO,EACL,OAAO,EACP,OAAO,EACP,QAAQ,EACR,cAAc,EACd,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,OAAO,GACR,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,QAAQ,EACR,cAAc,GAGf,MAAM,gBAAgB,CAAC;AAExB,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"}
@@ -2,18 +2,12 @@
2
2
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
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";
5
+ import { executeMcpTool, mcpTools } from "./mcp-tools.js";
8
6
  /**
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
7
+ * MCP server (stdio transport) exposing the plainstamp lookup engine
8
+ * to local agent runtimes. The tool surface is defined in
9
+ * `mcp-tools.ts` and is shared with any future HTTP / Streamable-HTTP
10
+ * MCP transport binding (e.g. the Cloudflare Workers wrapper).
17
11
  */
18
12
  const server = new Server({
19
13
  name: "plainstamp",
@@ -23,176 +17,14 @@ const server = new Server({
23
17
  tools: {},
24
18
  },
25
19
  });
26
- const ValidateInput = LookupQuery.extend({
27
- candidate_text: z.string().min(1),
28
- });
29
20
  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
- ],
21
+ tools: mcpTools,
100
22
  }));
101
23
  server.setRequestHandler(CallToolRequestSchema, async (req) => {
102
24
  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
- }
25
+ // The SDK's response type union includes a task-style variant and the
26
+ // structured-content variant; our tools always return the latter.
27
+ return executeMcpTool(name, args);
196
28
  });
197
29
  const transport = new StdioServerTransport();
198
30
  await server.connect(transport);
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,cAAc,EACd,WAAW,EACX,iBAAiB,EACjB,gBAAgB,EAChB,0BAA0B,GAC3B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,OAAO,EAAkB,WAAW,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;GASG;AAEH,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB;IACE,IAAI,EAAE,YAAY;IAClB,OAAO,EAAE,OAAO;CACjB,EACD;IACE,YAAY,EAAE;QACZ,KAAK,EAAE,EAAE;KACV;CACF,CACF,CAAC;AAEF,MAAM,aAAa,GAAG,WAAW,CAAC,MAAM,CAAC;IACvC,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAClC,CAAC,CAAC;AAEH,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;IAC5D,KAAK,EAAE;QACL;YACE,IAAI,EAAE,oBAAoB;YAC1B,WAAW,EACT,uIAAuI;YACzI,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE,EAAE;gBACd,oBAAoB,EAAE,KAAK;aAC5B;SACF;QACD;YACE,IAAI,EAAE,YAAY;YAClB,WAAW,EACT,gGAAgG;YAClG,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE,EAAE;gBACd,oBAAoB,EAAE,KAAK;aAC5B;SACF;QACD;YACE,IAAI,EAAE,UAAU;YAChB,WAAW,EACT,iHAAiH;YACnH,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;iBAC5B;gBACD,QAAQ,EAAE,CAAC,SAAS,CAAC;gBACrB,oBAAoB,EAAE,KAAK;aAC5B;SACF;QACD;YACE,IAAI,EAAE,mBAAmB;YACzB,WAAW,EACT,mTAAmT;YACrT,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,YAAY,EAAE;wBACZ,IAAI,EAAE,QAAQ;wBACd,WAAW,EACT,uEAAuE;qBAC1E;oBACD,OAAO,EAAE;wBACP,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,OAAO,CAAC,OAAO;qBACtB;oBACD,QAAQ,EAAE;wBACR,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,OAAO,CAAC,OAAO;qBACtB;iBACF;gBACD,QAAQ,EAAE,CAAC,cAAc,EAAE,SAAS,EAAE,UAAU,CAAC;gBACjD,oBAAoB,EAAE,KAAK;aAC5B;SACF;QACD;YACE,IAAI,EAAE,qBAAqB;YAC3B,WAAW,EACT,iVAAiV;YACnV,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBAChC,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE;oBAClD,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE;oBACnD,cAAc,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,EAAE;iBACjD;gBACD,QAAQ,EAAE,CAAC,cAAc,EAAE,SAAS,EAAE,UAAU,EAAE,gBAAgB,CAAC;gBACnE,oBAAoB,EAAE,KAAK;aAC5B;SACF;KACF;CACF,CAAC,CAAC,CAAC;AAEJ,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;IAC5D,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;IAE7C,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,oBAAoB,CAAC,CAAC,CAAC;YAC1B,MAAM,GAAG,GAAG,iBAAiB,EAAE,CAAC;YAChC,OAAO;gBACL,OAAO,EAAE;oBACP,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,aAAa,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;iBACxE;aACF,CAAC;QACJ,CAAC;QAED,KAAK,YAAY,CAAC,CAAC,CAAC;YAClB,MAAM,KAAK,GAAG,gBAAgB,EAAE,CAAC;YACjC,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACxC,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,YAAY,EAAE,CAAC,CAAC,YAAY;gBAC5B,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,aAAa,EAAE,CAAC,CAAC,aAAa;aAC/B,CAAC,CAAC,CAAC;YACJ,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAClB;4BACE,cAAc,EAAE,KAAK,CAAC,cAAc;4BACpC,YAAY,EAAE,KAAK,CAAC,YAAY;4BAChC,KAAK,EAAE,SAAS;yBACjB,EACD,IAAI,EACJ,CAAC,CACF;qBACF;iBACF;aACF,CAAC;QACJ,CAAC;QAED,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC;YACrE,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;YACjC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,OAAO;oBACL,OAAO,EAAE,IAAI;oBACb,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,MAAM,EAAE,EAAE,CAAC;iBAChE,CAAC;YACJ,CAAC;YACD,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;aACjE,CAAC;QACJ,CAAC;QAED,KAAK,mBAAmB,CAAC,CAAC,CAAC;YACzB,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACvC,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;YACvC,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAClB;4BACE,KAAK,EAAE,MAAM;4BACb,UAAU,EACR,qIAAqI;4BACvI,KAAK,EAAE,OAAO,CAAC,MAAM;4BACrB,OAAO;yBACR,EACD,IAAI,EACJ,CAAC,CACF;qBACF;iBACF;aACF,CAAC;QACJ,CAAC;QAED,KAAK,qBAAqB,CAAC,CAAC,CAAC;YAC3B,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACzC,MAAM,OAAO,GAAG,0BAA0B,CACxC;gBACE,YAAY,EAAE,MAAM,CAAC,YAAY;gBACjC,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,QAAQ,EAAE,MAAM,CAAC,QAAQ;aAC1B,EACD,MAAM,CAAC,cAAc,CACtB,CAAC;YACF,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAClB;4BACE,KAAK,EAAE;gCACL,YAAY,EAAE,MAAM,CAAC,YAAY;gCACjC,OAAO,EAAE,MAAM,CAAC,OAAO;gCACvB,QAAQ,EAAE,MAAM,CAAC,QAAQ;6BAC1B;4BACD,UAAU,EACR,mEAAmE;4BACrE,OAAO;yBACR,EACD,IAAI,EACJ,CAAC,CACF;qBACF;iBACF;aACF,CAAC;QACJ,CAAC;QAED;YACE,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,IAAI,EAAE,EAAE,CAAC;aAC3D,CAAC;IACN,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC"}
1
+ {"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1D;;;;;GAKG;AAEH,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB;IACE,IAAI,EAAE,YAAY;IAClB,OAAO,EAAE,OAAO;CACjB,EACD;IACE,YAAY,EAAE;QACZ,KAAK,EAAE,EAAE;KACV;CACF,CACF,CAAC;AAEF,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;IAC5D,KAAK,EAAE,QAAQ;CAChB,CAAC,CAAC,CAAC;AAEJ,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;IAC5D,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;IAC7C,sEAAsE;IACtE,kEAAkE;IAClE,OAAO,cAAc,CAAC,IAAI,EAAE,IAAI,CAE/B,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Transport-independent MCP tool surface for plainstamp.
3
+ *
4
+ * Exposes the same tool definitions and dispatch logic to:
5
+ * - the stdio MCP server (`mcp-server.ts`), which talks to local
6
+ * agent runtimes (Claude Code, Codex, etc.); and
7
+ * - any HTTP / Streamable-HTTP MCP transport binding (e.g. the
8
+ * Cloudflare Workers wrapper at `<autobiz>/ventures/plainstamp/cf-worker/`),
9
+ * which will be wired in Phase 4 of `<autobiz>/ops/cloudflare/CLOUDFLARE_DEPLOY.md`.
10
+ *
11
+ * Keeping the tool list and dispatcher in one place means stdio and
12
+ * HTTP transports cannot drift in their tool surface.
13
+ */
14
+ /**
15
+ * Shape returned by an MCP tool call. Mirrors the
16
+ * `CallToolResult` shape from the MCP TypeScript SDK so this module
17
+ * stays SDK-version-agnostic.
18
+ */
19
+ export interface McpToolResult {
20
+ content: Array<{
21
+ type: "text";
22
+ text: string;
23
+ }>;
24
+ isError?: boolean;
25
+ }
26
+ /**
27
+ * MCP tool descriptor. Matches the shape registered with
28
+ * `ListToolsRequestSchema` in the MCP TypeScript SDK.
29
+ */
30
+ export interface McpToolDescriptor {
31
+ name: string;
32
+ description: string;
33
+ inputSchema: {
34
+ type: "object";
35
+ properties: Record<string, unknown>;
36
+ required?: string[];
37
+ additionalProperties: false;
38
+ };
39
+ }
40
+ /**
41
+ * The full list of plainstamp tool descriptors. Pass this to a
42
+ * `ListToolsRequestSchema` handler.
43
+ */
44
+ export declare const mcpTools: McpToolDescriptor[];
45
+ /**
46
+ * Dispatch an MCP tool call. Returns the canonical `McpToolResult`
47
+ * shape regardless of which transport invoked it.
48
+ *
49
+ * Unknown tool names return `{ isError: true }` rather than throwing,
50
+ * matching MCP's convention.
51
+ */
52
+ export declare function executeMcpTool(name: string, args: unknown): McpToolResult;
53
+ //# sourceMappingURL=mcp-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-tools.d.ts","sourceRoot":"","sources":["../src/mcp-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAYH;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACpC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,oBAAoB,EAAE,KAAK,CAAC;KAC7B,CAAC;CACH;AAMD;;;GAGG;AACH,eAAO,MAAM,QAAQ,EAAE,iBAAiB,EA2EvC,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,GACZ,aAAa,CAoHf"}
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Transport-independent MCP tool surface for plainstamp.
3
+ *
4
+ * Exposes the same tool definitions and dispatch logic to:
5
+ * - the stdio MCP server (`mcp-server.ts`), which talks to local
6
+ * agent runtimes (Claude Code, Codex, etc.); and
7
+ * - any HTTP / Streamable-HTTP MCP transport binding (e.g. the
8
+ * Cloudflare Workers wrapper at `<autobiz>/ventures/plainstamp/cf-worker/`),
9
+ * which will be wired in Phase 4 of `<autobiz>/ops/cloudflare/CLOUDFLARE_DEPLOY.md`.
10
+ *
11
+ * Keeping the tool list and dispatcher in one place means stdio and
12
+ * HTTP transports cannot drift in their tool surface.
13
+ */
14
+ import { z } from "zod";
15
+ import { disclosuresFor, getRuleById, listJurisdictions, loadBundledRules, validateDisclosureForQuery, } from "./index.js";
16
+ import { Channel, LookupQuery, UseCase } from "./schema.js";
17
+ const ValidateInput = LookupQuery.extend({
18
+ candidate_text: z.string().min(1),
19
+ });
20
+ /**
21
+ * The full list of plainstamp tool descriptors. Pass this to a
22
+ * `ListToolsRequestSchema` handler.
23
+ */
24
+ export const mcpTools = [
25
+ {
26
+ name: "list_jurisdictions",
27
+ description: "List all jurisdiction ids supported by the bundled rules. Use this first to discover what's covered before calling lookup_disclosure.",
28
+ inputSchema: {
29
+ type: "object",
30
+ properties: {},
31
+ additionalProperties: false,
32
+ },
33
+ },
34
+ {
35
+ name: "list_rules",
36
+ description: "List all bundled disclosure rules as compact summaries. Useful for browsing the rule database.",
37
+ inputSchema: {
38
+ type: "object",
39
+ properties: {},
40
+ additionalProperties: false,
41
+ },
42
+ },
43
+ {
44
+ name: "get_rule",
45
+ description: "Fetch a single disclosure rule by id, with full citation, required elements, templates, and last-verified date.",
46
+ inputSchema: {
47
+ type: "object",
48
+ properties: {
49
+ rule_id: { type: "string" },
50
+ },
51
+ required: ["rule_id"],
52
+ additionalProperties: false,
53
+ },
54
+ },
55
+ {
56
+ name: "lookup_disclosure",
57
+ 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.",
58
+ inputSchema: {
59
+ type: "object",
60
+ properties: {
61
+ jurisdiction: {
62
+ type: "string",
63
+ description: "Lowercase jurisdiction id (ISO-style). Examples: 'us', 'us-ca', 'eu'.",
64
+ },
65
+ channel: {
66
+ type: "string",
67
+ enum: Channel.options,
68
+ },
69
+ use_case: {
70
+ type: "string",
71
+ enum: UseCase.options,
72
+ },
73
+ },
74
+ required: ["jurisdiction", "channel", "use_case"],
75
+ additionalProperties: false,
76
+ },
77
+ },
78
+ {
79
+ name: "validate_disclosure",
80
+ 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.",
81
+ inputSchema: {
82
+ type: "object",
83
+ properties: {
84
+ jurisdiction: { type: "string" },
85
+ channel: { type: "string", enum: Channel.options },
86
+ use_case: { type: "string", enum: UseCase.options },
87
+ candidate_text: { type: "string", minLength: 1 },
88
+ },
89
+ required: ["jurisdiction", "channel", "use_case", "candidate_text"],
90
+ additionalProperties: false,
91
+ },
92
+ },
93
+ ];
94
+ /**
95
+ * Dispatch an MCP tool call. Returns the canonical `McpToolResult`
96
+ * shape regardless of which transport invoked it.
97
+ *
98
+ * Unknown tool names return `{ isError: true }` rather than throwing,
99
+ * matching MCP's convention.
100
+ */
101
+ export function executeMcpTool(name, args) {
102
+ switch (name) {
103
+ case "list_jurisdictions": {
104
+ const ids = listJurisdictions();
105
+ return {
106
+ content: [
107
+ { type: "text", text: JSON.stringify({ jurisdictions: ids }, null, 2) },
108
+ ],
109
+ };
110
+ }
111
+ case "list_rules": {
112
+ const rules = loadBundledRules();
113
+ const summaries = rules.rules.map((r) => ({
114
+ id: r.id,
115
+ jurisdiction: r.jurisdiction,
116
+ severity: r.severity,
117
+ short_title: r.short_title,
118
+ channels: r.channels,
119
+ use_cases: r.use_cases,
120
+ last_verified: r.last_verified,
121
+ }));
122
+ return {
123
+ content: [
124
+ {
125
+ type: "text",
126
+ text: JSON.stringify({
127
+ schema_version: rules.schema_version,
128
+ generated_at: rules.generated_at,
129
+ rules: summaries,
130
+ }, null, 2),
131
+ },
132
+ ],
133
+ };
134
+ }
135
+ case "get_rule": {
136
+ const ruleId = z.object({ rule_id: z.string() }).parse(args).rule_id;
137
+ const rule = getRuleById(ruleId);
138
+ if (rule === undefined) {
139
+ return {
140
+ isError: true,
141
+ content: [{ type: "text", text: `unknown rule_id: ${ruleId}` }],
142
+ };
143
+ }
144
+ return {
145
+ content: [{ type: "text", text: JSON.stringify(rule, null, 2) }],
146
+ };
147
+ }
148
+ case "lookup_disclosure": {
149
+ const parsed = LookupQuery.parse(args);
150
+ const results = disclosuresFor(parsed);
151
+ return {
152
+ content: [
153
+ {
154
+ type: "text",
155
+ text: JSON.stringify({
156
+ query: parsed,
157
+ disclaimer: "Informational only. Not legal advice. Each rule cites its source; verify against the regulator-published text before relying on it.",
158
+ count: results.length,
159
+ results,
160
+ }, null, 2),
161
+ },
162
+ ],
163
+ };
164
+ }
165
+ case "validate_disclosure": {
166
+ const parsed = ValidateInput.parse(args);
167
+ const reports = validateDisclosureForQuery({
168
+ jurisdiction: parsed.jurisdiction,
169
+ channel: parsed.channel,
170
+ use_case: parsed.use_case,
171
+ }, parsed.candidate_text);
172
+ return {
173
+ content: [
174
+ {
175
+ type: "text",
176
+ text: JSON.stringify({
177
+ query: {
178
+ jurisdiction: parsed.jurisdiction,
179
+ channel: parsed.channel,
180
+ use_case: parsed.use_case,
181
+ },
182
+ disclaimer: "Heuristic substring check. NOT a legal-sufficiency determination.",
183
+ reports,
184
+ }, null, 2),
185
+ },
186
+ ],
187
+ };
188
+ }
189
+ default:
190
+ return {
191
+ isError: true,
192
+ content: [{ type: "text", text: `unknown tool: ${name}` }],
193
+ };
194
+ }
195
+ }
196
+ //# sourceMappingURL=mcp-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-tools.js","sourceRoot":"","sources":["../src/mcp-tools.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plainstamp",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "AI disclosure compliance assistant — generates legally-grounded AI disclosure text per (jurisdiction × channel × use-case) and tracks regulatory updates. Operated by an autonomous AI agent under KS Elevated Solutions LLC.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/rules/seed.json CHANGED
@@ -1004,6 +1004,58 @@
1004
1004
  "formal": "Notice under FD&C Act § 515C (21 U.S.C. § 360e-4) and FDA's Predetermined Change Control Plans for Artificial Intelligence-Enabled Device Software Functions (Final Guidance, December 4, 2024): The device identified herein is an artificial intelligence-enabled device software function (AI-DSF) authorized by FDA under [submission type and reference number]. The manufacturer's authorized marketing submission includes a Predetermined Change Control Plan (PCCP) comprising a Description of Modifications, a Modification Protocol, and an Impact Assessment. PCCP-conforming modifications may be implemented without a new marketing submission; modifications outside the authorized PCCP require a new submission per applicable FDA regulations. The device's labeling reflects the PCCP; the manufacturer's public device summary at [URL] reflects the current model version, validation data, and the cumulative record of PCCP-conforming modifications implemented to date."
1005
1005
  },
1006
1006
  "notes": "PCCP is the FDA's response to the 'locked algorithm' problem for AI/ML medical devices: prior to FDORA § 515C (2022), any change to the algorithm of a cleared/authorized AI/ML device that affected safety or effectiveness typically required a new 510(k) / De Novo / PMA submission, which made iterative model improvement impractical. The PCCP framework lets manufacturers pre-authorize a bounded set of modifications and the validation methods for each. The December 2024 final guidance applies to all medical devices regardless of pathway (510(k), De Novo, PMA) and supersedes the April 2023 draft. Disclosure scope: the FDA-required labeling under 21 CFR Part 801 (device labeling) and the public-facing 510(k) summary / De Novo decision summary / PMA approval order published on FDA's website constitute the public disclosure surface; manufacturers typically also publish device-summary pages on their own websites with current model version and validation data. Use case is `healthcare`. Stack with HHS Section 1557 PCDST nondiscrimination obligations and with state-level rules like California SB 1120 — Physicians Make Decisions Act when the device is used in coverage decisions. The patient-facing element is conditional: most FDA-regulated AI/ML devices are clinician-facing tools, but where the device produces output that is shown to patients (e.g., consumer-facing diabetes risk estimators, certain digital health products), the AI/ML disclosure should be patient-facing. The 'mandatory' severity reflects that AI/ML modifications must be authorized — either through PCCP or through a new submission — and that labeling disclosure is required; the 'recommended' framing applies to design choices about how detailed to make the user-facing AI/ML summary. Verify against the current FDA guidance and any device-class-specific guidance before production deployment."
1007
+ },
1008
+ {
1009
+ "id": "us-fcc-tcpa-ai-voice-robocall-2024",
1010
+ "jurisdiction": "us",
1011
+ "channels": ["voice"],
1012
+ "use_cases": [
1013
+ "b2c-marketing",
1014
+ "b2c-sales",
1015
+ "b2c-customer-support",
1016
+ "civic-or-electoral",
1017
+ "general"
1018
+ ],
1019
+ "severity": "mandatory",
1020
+ "short_title": "FCC Declaratory Ruling — AI-generated voice in robocalls is an 'artificial or prerecorded voice' under TCPA (February 2024)",
1021
+ "summary": "On February 8, 2024, the U.S. Federal Communications Commission issued a Declaratory Ruling (CG Docket No. 23-362, FCC 24-17) confirming that AI-generated voice clones and other AI-synthesized voices used in calls to consumers are 'artificial or prerecorded voices' within the meaning of the Telephone Consumer Protection Act of 1991 (TCPA), 47 U.S.C. § 227(b)(1)(A)–(B), and the Commission's implementing rules at 47 CFR § 64.1200. The ruling means that any robocall to a wireless number that uses an AI-generated voice (or to a residential landline for a telemarketing purpose) requires the called party's prior express written consent (for telemarketing) or prior express consent (for non-telemarketing/informational) — and remains subject to the TCPA's identification-of-caller and opt-out requirements. Statutory damages under the TCPA are $500 per violation (per call), up to $1,500 per willful or knowing violation. State Attorneys General, the FCC, and a private right of action under § 227(b)(3) are all available enforcement paths.",
1022
+ "required_elements": [
1023
+ {
1024
+ "id": "prior-express-consent",
1025
+ "description": "Prior express written consent (for telemarketing AI-voice calls to wireless numbers and residential landlines) or prior express consent (for non-telemarketing/informational AI-voice calls) before placing the call. (Pre-call consent requirement; the consumer-facing disclosure occurs at consent-collection time, not at call time.)",
1026
+ "required": false
1027
+ },
1028
+ {
1029
+ "id": "caller-identification",
1030
+ "description": "At the beginning of the AI-voice call, the message must clearly state the identity of the business, individual, or other entity that is responsible for initiating the call.",
1031
+ "required": true,
1032
+ "example": "This is an automated call from [business name]."
1033
+ },
1034
+ {
1035
+ "id": "callback-number",
1036
+ "description": "During or after the AI-voice message, the called party must be provided with a telephone number (other than that of the autodialer or prerecorded message player) that the called party can use to make a do-not-call request.",
1037
+ "required": true,
1038
+ "example": "To stop receiving calls from us, please call [phone number] or press [digit] now."
1039
+ },
1040
+ {
1041
+ "id": "interactive-opt-out",
1042
+ "description": "For telemarketing AI-voice calls, an automated, interactive voice- and/or key-press-activated opt-out mechanism must be available throughout the duration of the call.",
1043
+ "required": false
1044
+ }
1045
+ ],
1046
+ "citation": {
1047
+ "statute": "Telephone Consumer Protection Act of 1991, codified at 47 U.S.C. § 227; 47 CFR § 64.1200",
1048
+ "section": "FCC Declaratory Ruling, CG Docket No. 23-362, FCC 24-17 (released February 8, 2024)",
1049
+ "source_url": "https://www.fcc.gov/document/fcc-makes-ai-generated-voices-robocalls-illegal",
1050
+ "publisher": "U.S. Federal Communications Commission"
1051
+ },
1052
+ "effective_date": "2024-02-08",
1053
+ "last_verified": "2026-05-08",
1054
+ "template": {
1055
+ "plain": "Notice — Automated Call: This is an automated call from [business name]. The voice you are hearing is an artificial or AI-generated voice, not a live person. To stop receiving calls from us, please press [digit] or call [phone number].",
1056
+ "formal": "Notice under the Telephone Consumer Protection Act, 47 U.S.C. § 227, and the Federal Communications Commission's Declaratory Ruling FCC 24-17 (February 8, 2024) confirming that AI-generated voices in robocalls are 'artificial or prerecorded voices' under the TCPA: This call is being placed by [business name and contact information]. The voice in this call is artificially generated. The called party may opt out of future calls from this caller at any time by [opt-out instructions]. Calls placed in violation of the TCPA are subject to statutory damages of $500 per call, up to $1,500 per willful or knowing violation."
1057
+ },
1058
+ "notes": "The FCC's February 2024 Declaratory Ruling closed an interpretive gap — TCPA's 'artificial or prerecorded voice' language predates AI voice cloning, and there had been arguments that AI-generated voices were not covered. The ruling makes clear they are. Practical consequences: (1) any AI-voice call to a wireless number for any purpose typically requires prior express consent; (2) AI-voice calls for telemarketing require prior express written consent; (3) every AI-voice call must include caller identification and an opt-out path. The ruling stacks with state-level robocall laws (e.g., Florida, Oklahoma, Pennsylvania, Washington) that may impose additional consent or disclosure requirements; with California's B&P § 17941 bot-disclosure rule when the caller is in or reaching California; and with EU AI Act Article 50 when the caller reaches EU residents. The FCC has paired this ruling with separate caller-ID authentication enforcement (STIR/SHAKEN) targeting AI-voice scam robocalls. Class actions under TCPA are common; the per-call statutory damages structure means even small-volume AI-voice campaigns carry significant exposure. Legal-services and political-campaign callers face additional state-law restrictions. The April 2024 FCC Notice of Proposed Rulemaking (CG Docket 23-362) proposed disclosure rules specific to AI-generated content in calls and texts; verify the latest rulemaking status before production deployment."
1007
1059
  }
1008
1060
  ]
1009
1061
  }