@cookiemunch/mcp 0.0.0-stage → 0.1.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/dist/bin.cjs ADDED
@@ -0,0 +1,1394 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // src/bin.ts
5
+ var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
6
+
7
+ // src/index.ts
8
+ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
9
+ var import_sdk = require("@cookiemunch/sdk");
10
+
11
+ // src/tools.ts
12
+ var import_zod3 = require("zod");
13
+
14
+ // src/tools-config.ts
15
+ var import_zod = require("zod");
16
+ function registerConfigTools(tool, client) {
17
+ const blockingModeEnum = import_zod.z.enum(["auto", "manual"]);
18
+ const bannerModeEnum = import_zod.z.enum(["opt-in", "opt-out", "off"]);
19
+ const bannerTypeEnum = import_zod.z.enum(["multilevel", "inline", "accept-decline", "accept-only", "do-not-sell"]);
20
+ const bannerLayoutEnum = import_zod.z.enum(["top", "bottom", "push-down", "popup", "overlay"]);
21
+ const consentModeModeEnum = import_zod.z.enum(["basic", "advanced"]);
22
+ const geoRuleSchema = import_zod.z.object({
23
+ match: import_zod.z.object({
24
+ countries: import_zod.z.array(import_zod.z.string()).optional(),
25
+ regions: import_zod.z.array(import_zod.z.string()).optional()
26
+ }),
27
+ mode: bannerModeEnum
28
+ });
29
+ const setBlockingSchema = import_zod.z.object({
30
+ cbid: import_zod.z.string().describe("Site identifier (cbid)."),
31
+ mode: blockingModeEnum.describe("Blocking mode. auto = block non-consented scripts; manual = never block."),
32
+ ignoreSelectors: import_zod.z.array(import_zod.z.string()).optional().describe("CSS selectors for elements that should never be blocked.")
33
+ });
34
+ tool(
35
+ "set_blocking",
36
+ "Configure the script-blocking mode for a site. Fetches the current config, patches only the blocking domain, and PUTs.",
37
+ setBlockingSchema.shape,
38
+ async (a) => {
39
+ const { cbid: siteId, mode, ignoreSelectors } = setBlockingSchema.parse(a);
40
+ const cfg = await client.sites.getConfig(siteId);
41
+ const existingBlocking = cfg["blocking"] ?? {};
42
+ const blocking = {
43
+ ...existingBlocking,
44
+ mode,
45
+ ...ignoreSelectors !== void 0 ? { ignoreSelectors } : {}
46
+ };
47
+ return client.sites.putConfig(siteId, { ...cfg, blocking });
48
+ }
49
+ );
50
+ const setGeoRulesSchema = import_zod.z.object({
51
+ cbid: import_zod.z.string().describe("Site identifier (cbid)."),
52
+ geoRules: import_zod.z.array(geoRuleSchema).describe("Ordered list of geo rules. First match wins."),
53
+ defaultMode: bannerModeEnum.optional().describe("Banner mode when no geo rule matches. Default: opt-in.")
54
+ });
55
+ tool(
56
+ "set_geo_rules",
57
+ "Set geo-targeting rules for a site (which banner mode to show per country/region). Fetches the current config, patches only geoRules + defaultMode, and PUTs.",
58
+ setGeoRulesSchema.shape,
59
+ async (a) => {
60
+ const { cbid: siteId, geoRules, defaultMode } = setGeoRulesSchema.parse(a);
61
+ const cfg = await client.sites.getConfig(siteId);
62
+ return client.sites.putConfig(siteId, {
63
+ ...cfg,
64
+ geoRules,
65
+ ...defaultMode !== void 0 ? { defaultMode } : {}
66
+ });
67
+ }
68
+ );
69
+ tool(
70
+ "list_supported_languages",
71
+ "The languages the banner already has copy for, with their English name, their name in themselves, whether they read right to left, and whether they ship in the base embed or in the renderer. Check this before writing translations with set_languages \u2014 the ones listed here need none. Read-only.",
72
+ {},
73
+ () => client.languages()
74
+ );
75
+ const identitySourceSchema = import_zod.z.object({
76
+ name: import_zod.z.string().describe('What you call the identifier, e.g. "customerId" or "email".'),
77
+ from: import_zod.z.enum(["dataLayer", "window", "cookie", "localStorage", "query", "meta"]).describe("Where the page keeps it."),
78
+ key: import_zod.z.string().describe("Dotted path for dataLayer/window; cookie, storage key, query parameter or meta name otherwise."),
79
+ hash: import_zod.z.boolean().optional().describe("Default true: SHA-256 the trimmed, lowercased value in the browser. false for an opaque id.")
80
+ });
81
+ const setIdentitySourcesSchema = import_zod.z.object({
82
+ cbid: import_zod.z.string().describe("Site identifier (cbid)."),
83
+ sources: import_zod.z.array(identitySourceSchema).describe("In priority order; the first with a value becomes the consent record's subjectId. Empty array removes them.")
84
+ });
85
+ tool(
86
+ "set_identity_sources",
87
+ "Tell the web embed where a site keeps its signed-in customer (data layer, window object, cookie, local storage, query parameter or meta tag), so consent records carry that customer's subjectId and can be read with GET /v1/subjects/:id/consent. Values are hashed in the browser unless a source sets hash:false. Fetches the current config, patches only identity, and PUTs.",
88
+ setIdentitySourcesSchema.shape,
89
+ async (a) => {
90
+ const { cbid: siteId, sources } = setIdentitySourcesSchema.parse(a);
91
+ const cfg = await client.sites.getConfig(siteId);
92
+ const { identity: _old, ...rest } = cfg;
93
+ return client.sites.putConfig(siteId, sources.length > 0 ? { ...rest, identity: { sources } } : rest);
94
+ }
95
+ );
96
+ const setLanguagesSchema = import_zod.z.object({
97
+ cbid: import_zod.z.string().describe("Site identifier (cbid)."),
98
+ defaultCulture: import_zod.z.string().optional().describe('Default locale/culture code, e.g. "en", "fr". Stored in i18n.defaultCulture.'),
99
+ autoDetect: import_zod.z.boolean().optional().describe("Auto-detect visitor language from browser locale. Stored in i18n.autoDetect."),
100
+ translations: import_zod.z.record(import_zod.z.record(import_zod.z.unknown())).optional().describe("Per-locale banner copy overrides. Merged onto banner.i18n (locale keys preserved)."),
101
+ overrides: import_zod.z.record(import_zod.z.object({ banner: import_zod.z.record(import_zod.z.string()).optional(), categories: import_zod.z.record(import_zod.z.object({ label: import_zod.z.string().optional(), description: import_zod.z.string().optional() })).optional() })).optional().describe("The site's own copy per language, keyed by tag (de, pt-BR): { banner: { title, body, \u2026 }, categories: { <id>: { label, description } } }. Each language given replaces its entry in i18n.overrides; other languages are kept. Strings the site wrote are shown as written in any language without an override \u2014 stock translations never replace them.")
102
+ });
103
+ tool(
104
+ "set_languages",
105
+ "Configure language/i18n settings for a site: default culture, auto-detect, and the site's own copy per language (overrides). Fetches the current config, patches only i18n + banner.i18n, and PUTs.",
106
+ setLanguagesSchema.shape,
107
+ async (a) => {
108
+ const { cbid: siteId, defaultCulture, autoDetect, translations, overrides } = setLanguagesSchema.parse(a);
109
+ const cfg = await client.sites.getConfig(siteId);
110
+ const existingI18n = cfg["i18n"] ?? {};
111
+ const existingBanner = cfg["banner"] ?? {};
112
+ const existingBannerI18n = existingBanner["i18n"] ?? {};
113
+ const i18n = {
114
+ ...existingI18n,
115
+ ...defaultCulture !== void 0 ? { defaultCulture } : {},
116
+ ...autoDetect !== void 0 ? { autoDetect } : {},
117
+ ...overrides !== void 0 ? { overrides: { ...existingI18n["overrides"] ?? {}, ...overrides } } : {}
118
+ };
119
+ const banner = {
120
+ ...existingBanner,
121
+ ...translations !== void 0 ? { i18n: { ...existingBannerI18n, ...translations } } : {}
122
+ };
123
+ return client.sites.putConfig(siteId, { ...cfg, i18n, banner });
124
+ }
125
+ );
126
+ const setConsentModeSchema = import_zod.z.object({
127
+ cbid: import_zod.z.string().describe("Site identifier (cbid)."),
128
+ enabled: import_zod.z.boolean().describe("Whether Google Consent Mode signals are emitted."),
129
+ mode: consentModeModeEnum.describe("basic = gtag consent commands only; advanced = also passes redacted pings before consent."),
130
+ waitForUpdate: import_zod.z.number().optional().describe("Milliseconds to wait for consent before gtag proceeds. Default 500.")
131
+ });
132
+ tool(
133
+ "set_consent_mode",
134
+ "Configure Google Consent Mode for a site (enabled, basic/advanced, waitForUpdate). Fetches the current config, patches only consentMode, and PUTs.",
135
+ setConsentModeSchema.shape,
136
+ async (a) => {
137
+ const { cbid: siteId, enabled, mode, waitForUpdate } = setConsentModeSchema.parse(a);
138
+ const cfg = await client.sites.getConfig(siteId);
139
+ const existingCm = cfg["consentMode"] ?? {};
140
+ const consentMode = {
141
+ ...existingCm,
142
+ enabled,
143
+ mode,
144
+ ...waitForUpdate !== void 0 ? { waitForUpdate } : {}
145
+ };
146
+ return client.sites.putConfig(siteId, { ...cfg, consentMode });
147
+ }
148
+ );
149
+ const setAbExperimentSchema = import_zod.z.object({
150
+ cbid: import_zod.z.string().describe("Site identifier (cbid)."),
151
+ enabled: import_zod.z.boolean().describe("Whether the A/B experiment is active."),
152
+ splitB: import_zod.z.number().min(0).max(100).describe("Percentage of visitors (0\u2013100) who see variant B."),
153
+ variantB: import_zod.z.record(import_zod.z.unknown()).optional().describe("Partial BannerConfig override for variant B visitors. Deep-merged over the base banner.")
154
+ });
155
+ tool(
156
+ "set_ab_experiment",
157
+ "Configure the A/B banner experiment for a site (enabled, split percentage, variant B overrides). Fetches the current config, patches only experiment, and PUTs.",
158
+ setAbExperimentSchema.shape,
159
+ async (a) => {
160
+ const { cbid: siteId, enabled, splitB, variantB } = setAbExperimentSchema.parse(a);
161
+ const cfg = await client.sites.getConfig(siteId);
162
+ const existingExperiment = cfg["experiment"] ?? {};
163
+ const experiment = {
164
+ ...existingExperiment,
165
+ enabled,
166
+ splitB,
167
+ ...variantB !== void 0 ? { variantB } : {}
168
+ };
169
+ return client.sites.putConfig(siteId, { ...cfg, experiment });
170
+ }
171
+ );
172
+ const setConsentPolicySchema = import_zod.z.object({
173
+ cbid: import_zod.z.string().describe("Site identifier (cbid)."),
174
+ expiryDays: import_zod.z.number().optional().describe("Re-prompt visitors after this many days. Omit to disable time-based expiry."),
175
+ version: import_zod.z.number().optional().describe("Bump this integer to force a re-prompt (e.g. when your cookie usage changes).")
176
+ });
177
+ tool(
178
+ "set_consent_policy",
179
+ "Configure consent persistence policy for a site (expiry days, version bump). Fetches the current config, patches only consent, and PUTs.",
180
+ setConsentPolicySchema.shape,
181
+ async (a) => {
182
+ const { cbid: siteId, expiryDays, version } = setConsentPolicySchema.parse(a);
183
+ const cfg = await client.sites.getConfig(siteId);
184
+ const existingConsent = cfg["consent"] ?? {};
185
+ const consent = {
186
+ ...existingConsent,
187
+ ...expiryDays !== void 0 ? { expiryDays } : {},
188
+ ...version !== void 0 ? { version } : {}
189
+ };
190
+ return client.sites.putConfig(siteId, { ...cfg, consent });
191
+ }
192
+ );
193
+ const setBannerBasicsSchema = import_zod.z.object({
194
+ cbid: import_zod.z.string().describe("Site identifier (cbid)."),
195
+ type: bannerTypeEnum.optional().describe("Banner type. multilevel | inline | accept-decline | accept-only | do-not-sell."),
196
+ layout: bannerLayoutEnum.optional().describe("Banner layout. top | bottom | push-down | popup | overlay."),
197
+ preset: import_zod.z.enum(["bar", "box", "full"]).optional().describe("Named shape that sets type/secondLayer (and layout for box): bar = slim one-row bar + categories in a modal; box = centred card + modal; full = every category in the first layer."),
198
+ secondLayer: import_zod.z.enum(["expand", "popup", "modal"]).optional().describe("Where the categories open from an inline banner."),
199
+ density: import_zod.z.enum(["comfortable", "compact"]).optional().describe("compact = one-row bar from 760px wide."),
200
+ theme: import_zod.z.record(import_zod.z.unknown()).optional().describe("Partial ThemeTokens \u2014 deep-merged onto the existing banner theme."),
201
+ content: import_zod.z.record(import_zod.z.unknown()).optional().describe("Partial BannerContent \u2014 banner copy overrides (title, body, button labels, policyUrl, etc.).")
202
+ });
203
+ tool(
204
+ "set_banner_basics",
205
+ "Configure v1 banner appearance for a site: preset, type, layout, second layer, density, theme tokens, and copy (content.preferencesTitle titles the preferences dialog). Fetches the current config, patches only those banner fields, and PUTs. Setting type or secondLayer (or layout under the box preset) without a preset removes the stored preset, which would otherwise re-derive them.",
206
+ setBannerBasicsSchema.shape,
207
+ async (a) => {
208
+ const { cbid: siteId, type, layout, preset, secondLayer, density, theme, content } = setBannerBasicsSchema.parse(a);
209
+ const cfg = await client.sites.getConfig(siteId);
210
+ const existingBanner = cfg["banner"] ?? {};
211
+ const existingTheme = existingBanner["theme"] ?? {};
212
+ const existingContent = existingBanner["content"] ?? {};
213
+ const banner = {
214
+ ...existingBanner,
215
+ ...type !== void 0 ? { type } : {},
216
+ ...layout !== void 0 ? { layout } : {},
217
+ ...secondLayer !== void 0 ? { secondLayer } : {},
218
+ ...density !== void 0 ? { density } : {},
219
+ ...preset !== void 0 ? { preset } : {},
220
+ ...theme !== void 0 ? { theme: { ...existingTheme, ...theme } } : {},
221
+ ...content !== void 0 ? { content: { ...existingContent, ...content } } : {}
222
+ };
223
+ const owned = type !== void 0 || secondLayer !== void 0 || layout !== void 0 && banner["preset"] === "box";
224
+ if (preset === void 0 && owned) delete banner["preset"];
225
+ return client.sites.putConfig(siteId, { ...cfg, banner });
226
+ }
227
+ );
228
+ const getInstallSnippetSchema = import_zod.z.object({
229
+ cbid: import_zod.z.string().describe("Site identifier (cbid). Must belong to your org."),
230
+ blockingMode: import_zod.z.enum(["auto", "manual"]).optional().describe(
231
+ '"auto" (default) blocks third-party scripts until consent is given; "manual" never blocks. These are the only two modes the runtime embed honors \u2014 any other value is rejected rather than silently downgraded.'
232
+ ),
233
+ culture: import_zod.z.string().optional().describe('Language culture override, e.g. "en", "fr", "de".')
234
+ });
235
+ tool(
236
+ "get_install_snippet",
237
+ "Get the exact <script> tag to install Cookie Munch on a site. Copy and paste this into the <head> of every page on the site.",
238
+ getInstallSnippetSchema.shape,
239
+ (a) => {
240
+ const { cbid: siteId, blockingMode, culture } = getInstallSnippetSchema.parse(a);
241
+ return client.sites.snippet(siteId, { blockingMode, culture });
242
+ }
243
+ );
244
+ const previewBannerSchema = import_zod.z.object({
245
+ cbid: import_zod.z.string().describe("Site identifier (cbid). Must belong to your org."),
246
+ region: import_zod.z.string().optional().describe('Region to show the banner for: a country or subdivision code ("de", "us-ca") or "eu".'),
247
+ lang: import_zod.z.string().optional().describe('Language to show it in, e.g. "de", "pt-BR".'),
248
+ url: import_zod.z.string().optional().describe("The page to open. Default: the site's registered domain."),
249
+ ttlSeconds: import_zod.z.number().int().min(1).max(3600).optional().describe("How long the link works, in seconds (default 900, max 3600).")
250
+ });
251
+ tool(
252
+ "preview_banner",
253
+ "Get a short-lived link that shows the site\u2019s banner exactly as a visitor in a given region and language sees it \u2014 it works from a headless browser too (bot suppression is bypassed), and nothing done on it is recorded as consent or counted in statistics.",
254
+ previewBannerSchema.shape,
255
+ (a) => {
256
+ const { cbid: siteId, ...opts } = previewBannerSchema.parse(a);
257
+ return client.sites.previewToken(siteId, opts);
258
+ }
259
+ );
260
+ }
261
+
262
+ // src/tools-platform.ts
263
+ var import_zod2 = require("zod");
264
+ var identifierSchema = import_zod2.z.array(import_zod2.z.object({ space: import_zod2.z.string(), value: import_zod2.z.string() })).describe("A person's identifiers, e.g. [{space:'email_sha256', value:'<sha256 of the lowercased email>'}].");
265
+ var ids = (a) => a.identifiers;
266
+ function registerPlatformTools(tool, client) {
267
+ tool(
268
+ "resolve_identity",
269
+ "Resolve identifiers to the canonical subject id for one person, or null if unknown. Read-only.",
270
+ { identifiers: identifierSchema },
271
+ (a) => client.identity.resolve(ids(a))
272
+ );
273
+ tool(
274
+ "get_identity_cluster",
275
+ "Every identifier currently stitched to one subject \u2014 what resolve_identity and link_identity have merged. Read-only.",
276
+ { subjectId: import_zod2.z.string() },
277
+ (a) => client.identity.cluster(a.subjectId)
278
+ );
279
+ tool(
280
+ "link_identity",
281
+ "Stitch identifiers together as ONE person and return the canonical subject id. This is a durable merge: if the identifiers already belong to different subjects, those subjects are combined and their consent and profile data are carried onto the survivor. Use resolve_identity if you only need to look someone up.",
282
+ { identifiers: identifierSchema },
283
+ (a) => client.identity.link(ids(a))
284
+ );
285
+ tool(
286
+ "record_consent_decision",
287
+ "Record consent decisions against the resolved person in the Permission Vault. The latest decision per purpose wins, so this is how a withdrawal is registered too (allowed:false).",
288
+ {
289
+ identifiers: identifierSchema,
290
+ decisions: import_zod2.z.array(import_zod2.z.object({
291
+ purpose: import_zod2.z.string(),
292
+ allowed: import_zod2.z.boolean(),
293
+ legalBasis: import_zod2.z.string().optional(),
294
+ jurisdiction: import_zod2.z.string().optional(),
295
+ provenance: import_zod2.z.string().optional().describe("Where the decision came from, e.g. banner_accept_all, gpc, preference_center."),
296
+ collectedAt: import_zod2.z.number().optional().describe("Unix ms; latest per purpose wins.")
297
+ })).describe("One entry per purpose.")
298
+ },
299
+ (a) => client.vault.record(ids(a), a.decisions)
300
+ );
301
+ tool(
302
+ "get_consent_state",
303
+ "A person's current allow/deny per purpose, resolved across ALL their identifiers. Read-only.",
304
+ { identifiers: identifierSchema },
305
+ (a) => client.vault.current(ids(a))
306
+ );
307
+ tool(
308
+ "list_permits",
309
+ "A person's consent decisions in full: per purpose, allowed or not, with the legal basis, jurisdiction, where it was collected and when. The audit-grade view behind get_consent_state. Read-only.",
310
+ { identifiers: identifierSchema },
311
+ (a) => client.vault.permits(ids(a))
312
+ );
313
+ tool(
314
+ "get_profile",
315
+ "The unified profile for a person: their identifiers, current permits and first-party attributes. Read-only.",
316
+ { identifiers: identifierSchema },
317
+ (a) => client.profile.get(ids(a))
318
+ );
319
+ tool(
320
+ "set_profile_attributes",
321
+ "Set first-party profile attributes for a person. A newer collectedAt wins over an older value.",
322
+ {
323
+ identifiers: identifierSchema,
324
+ attributes: import_zod2.z.record(import_zod2.z.object({ value: import_zod2.z.string(), purpose: import_zod2.z.string().optional(), collectedAt: import_zod2.z.number().optional() })).describe('Attribute name to value, e.g. { plan: { value: "pro" } }.')
325
+ },
326
+ (a) => client.profile.setAttributes(ids(a), a.attributes)
327
+ );
328
+ tool(
329
+ "activate_profile",
330
+ "Get the attribute values usable for a purpose. CONSENT-GATED: returns {} when the person has not consented to that purpose, so it is safe to call before sending data to a marketing or AI destination.",
331
+ { identifiers: identifierSchema, purpose: import_zod2.z.string().describe("The activation purpose, e.g. marketing.") },
332
+ (a) => client.profile.activate(ids(a), a.purpose)
333
+ );
334
+ tool(
335
+ "get_subscriptions",
336
+ "A subject's marketing subscription state (topics x channels). Read-only.",
337
+ { subjectId: import_zod2.z.string() },
338
+ (a) => client.subscriptions.get(a.subjectId)
339
+ );
340
+ tool(
341
+ "set_subscription",
342
+ "Opt a subject in or out of one topic on one channel.",
343
+ {
344
+ subjectId: import_zod2.z.string(),
345
+ topic: import_zod2.z.string(),
346
+ channel: import_zod2.z.string().describe("email | sms | push | mail | phone, or a custom channel."),
347
+ optedIn: import_zod2.z.boolean()
348
+ },
349
+ (a) => client.subscriptions.set(a.subjectId, a.topic, a.channel, a.optedIn)
350
+ );
351
+ tool(
352
+ "global_unsubscribe",
353
+ "Suppress every topic and channel for a subject WITHOUT erasing their per-topic choices, so a later resubscribe restores them.",
354
+ { subjectId: import_zod2.z.string() },
355
+ (a) => client.subscriptions.unsubscribeAll(a.subjectId)
356
+ );
357
+ tool(
358
+ "resubscribe",
359
+ "Lift a global unsubscribe. Restores the per-topic choices the subject had before it \u2014 it does not opt them in to anything they had not chosen.",
360
+ { subjectId: import_zod2.z.string() },
361
+ (a) => client.subscriptions.resubscribe(a.subjectId)
362
+ );
363
+ tool(
364
+ "get_subscription_activation",
365
+ "What to send downstream for a subject: per topic and channel, whether they are subscribed and which mapped downstream group (e.g. an ESP list id) that is. Honours a global unsubscribe. Read-only.",
366
+ {
367
+ subjectId: import_zod2.z.string(),
368
+ topics: import_zod2.z.array(
369
+ import_zod2.z.object({
370
+ code: import_zod2.z.string(),
371
+ name: import_zod2.z.string().optional(),
372
+ channels: import_zod2.z.array(import_zod2.z.string()),
373
+ downstream: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.string()).optional()
374
+ })
375
+ ).describe("The topics to resolve, usually the catalog from get_subscription_topics.")
376
+ },
377
+ (a) => client.subscriptions.activation(a.subjectId, a.topics)
378
+ );
379
+ tool(
380
+ "list_assessment_templates",
381
+ "The assessment templates available (DPIA, PIA, LIA, TIA, AI impact, vendor) and the questions each asks.",
382
+ {},
383
+ () => client.assessments.templates()
384
+ );
385
+ tool(
386
+ "list_assessments",
387
+ "Every assessment in the org with its status: draft, in_review, approved or rejected. Read-only.",
388
+ {},
389
+ () => client.assessments.list()
390
+ );
391
+ tool(
392
+ "autopopulate_assessment",
393
+ "Prefill an assessment from evidence you supply. Never overwrites a human answer, and stamps every filled answer with `source` so a reviewer can see where it came from. The assessment still needs a human to submit and approve it.",
394
+ { id: import_zod2.z.string(), evidence: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).describe("questionId \u2192 answer."), source: import_zod2.z.string().optional() },
395
+ (a) => client.assessments.autoPopulate(a.id, a.evidence, a.source)
396
+ );
397
+ tool(
398
+ "start_assessment",
399
+ "Start an assessment from a template against a subject (a processing activity, system or vendor).",
400
+ {
401
+ template: import_zod2.z.string().describe("dpia | pia | lia | tia | ai_impact | vendor"),
402
+ subject: import_zod2.z.string().describe("What is being assessed.")
403
+ },
404
+ (a) => client.assessments.start(a.template, a.subject)
405
+ );
406
+ tool(
407
+ "get_assessment",
408
+ "An assessment with its derived risk score, completeness (what is still missing) and which SME owns each outstanding question.",
409
+ { id: import_zod2.z.string() },
410
+ (a) => client.assessments.get(a.id)
411
+ );
412
+ tool(
413
+ "answer_assessment_question",
414
+ "Record one answer on an assessment. Note: answering an APPROVED assessment reopens it to draft, because changing the facts invalidates the sign-off.",
415
+ { id: import_zod2.z.string(), questionId: import_zod2.z.string(), value: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.number(), import_zod2.z.boolean()]) },
416
+ (a) => client.assessments.answer(a.id, a.questionId, a.value)
417
+ );
418
+ tool(
419
+ "autopopulate_assessment_from_data_map",
420
+ "Fill an assessment from the organisation\u2019s latest data-map scan. Only facts the scan can establish are applied, a human\u2019s existing answer is never overwritten, and machine answers are stamped with provenance so a reviewer can tell them apart.",
421
+ { id: import_zod2.z.string() },
422
+ (a) => client.assessments.autoPopulateFromMap(a.id)
423
+ );
424
+ tool(
425
+ "submit_assessment",
426
+ "Submit an assessment for review. Refused while any required question is unanswered.",
427
+ { id: import_zod2.z.string() },
428
+ (a) => client.assessments.submit(a.id)
429
+ );
430
+ tool(
431
+ "get_data_map",
432
+ "The latest data map: which systems hold personal data, and which categories. Read-only.",
433
+ {},
434
+ () => client.discovery.getMap()
435
+ );
436
+ tool(
437
+ "get_data_drift",
438
+ "What changed in the estate since the previous scan, and where the documented RoPA disagrees with what is actually there (undocumented systems, undeclared data categories, stale records).",
439
+ {},
440
+ () => client.discovery.drift()
441
+ );
442
+ tool(
443
+ "get_ropa_drafts",
444
+ "Draft processing-activity records generated from the latest data map. These are DRAFTS: the factual fields come from the scan, the legal ones still need a human.",
445
+ {},
446
+ () => client.discovery.ropaDrafts()
447
+ );
448
+ tool(
449
+ "get_discovery_evidence",
450
+ "The assessment answers the latest data map can actually support \u2014 only questions a scan can genuinely answer, never guesses. What autopopulate_assessment_from_data_map would apply. Read-only.",
451
+ {},
452
+ () => client.discovery.evidence()
453
+ );
454
+ tool(
455
+ "plan_warehouse_enforcement",
456
+ "Plan warehouse-native policy (Snowflake masking / row access, Postgres RLS) from the latest data map. Returns a PLAN and its revert \u2014 nothing is applied to the warehouse by calling this. Also reports what it declined to do and why, so gaps are visible rather than silently missing.",
457
+ {
458
+ dialect: import_zod2.z.enum(["postgres", "mysql", "snowflake"]),
459
+ rules: import_zod2.z.array(
460
+ import_zod2.z.object({
461
+ id: import_zod2.z.string(),
462
+ action: import_zod2.z.enum(["mask", "restrict"]),
463
+ minSensitivity: import_zod2.z.enum(["personal", "sensitive"]).optional(),
464
+ categories: import_zod2.z.array(import_zod2.z.string()).optional(),
465
+ allowRoles: import_zod2.z.array(import_zod2.z.string()).optional(),
466
+ purpose: import_zod2.z.string().optional(),
467
+ subjectColumn: import_zod2.z.string().optional()
468
+ })
469
+ ).describe("A rule with neither minSensitivity nor categories matches nothing, by design."),
470
+ permitsTable: import_zod2.z.string().optional(),
471
+ policyPrefix: import_zod2.z.string().optional()
472
+ },
473
+ (a) => client.discovery.planEnforcement(
474
+ a.dialect,
475
+ a.rules,
476
+ {
477
+ ...typeof a.permitsTable === "string" ? { permitsTable: a.permitsTable } : {},
478
+ ...typeof a.policyPrefix === "string" ? { policyPrefix: a.policyPrefix } : {}
479
+ }
480
+ )
481
+ );
482
+ tool(
483
+ "get_subscription_topics",
484
+ "The org's marketing topic catalog \u2014 what a subject can subscribe to, and on which channels. Read-only.",
485
+ {},
486
+ () => client.subscriptions.topics()
487
+ );
488
+ tool(
489
+ "set_subscription_topics",
490
+ "Replace the subscription topic catalog. The list is authored whole, so anything omitted is removed.",
491
+ {
492
+ topics: import_zod2.z.array(
493
+ import_zod2.z.object({
494
+ code: import_zod2.z.string(),
495
+ name: import_zod2.z.string().optional(),
496
+ channels: import_zod2.z.array(import_zod2.z.string()),
497
+ downstream: import_zod2.z.record(import_zod2.z.string()).optional()
498
+ })
499
+ )
500
+ },
501
+ (a) => client.subscriptions.setTopics(a.topics)
502
+ );
503
+ tool(
504
+ "get_regulatory_feed",
505
+ "The curated privacy-law dataset, optionally narrowed to the jurisdictions you operate in. Carries reviewedAt: this ships with the product and is only as current as its last review. Not legal advice.",
506
+ {
507
+ jurisdictions: import_zod2.z.array(import_zod2.z.string()).optional().describe("ISO codes, e.g. ['US-CA','BR']. A subdivision also matches its country; an EEA country matches the EU-wide regulations.")
508
+ },
509
+ (a) => client.regulatory.feed(a.jurisdictions)
510
+ );
511
+ tool(
512
+ "get_upcoming_regulations",
513
+ "Privacy laws taking effect within a horizon (default 180 days), soonest first. Same caveat as the feed: a shipped dataset with a review date, not live legal research.",
514
+ { days: import_zod2.z.number().int().positive().optional() },
515
+ (a) => client.regulatory.upcoming(a.days)
516
+ );
517
+ tool(
518
+ "get_dsr_sla",
519
+ "The rights queue at a glance: how many requests are overdue, due today, due soon or on track, plus the overdue ids.",
520
+ {},
521
+ () => client.fulfillment.sla()
522
+ );
523
+ tool(
524
+ "plan_dsr_fulfillment",
525
+ "Break a data-subject request into one fulfillment task per system. The tasks are then executed by the in-VPC agent inside the customer network \u2014 this call only queues the work.",
526
+ {
527
+ requestId: import_zod2.z.string(),
528
+ systems: import_zod2.z.array(import_zod2.z.object({ system: import_zod2.z.string(), operation: import_zod2.z.enum(["locate", "export", "erase", "optOut"]) })).describe("One entry per system to act on."),
529
+ includeHistorical: import_zod2.z.boolean().optional().describe("Also reprocess already-collected records, not just future ones.")
530
+ },
531
+ (a) => client.fulfillment.plan(
532
+ a.requestId,
533
+ a.systems,
534
+ a.includeHistorical
535
+ )
536
+ );
537
+ tool(
538
+ "list_dsar_executors",
539
+ "Systems connected to run part of a rights request themselves \u2014 an identity platform, a CRM, anything with a rights API. The in-VPC agent covers systems we cannot reach; these are the ones we can. Each carries the profile that describes its API. Never returns credentials. Read-only.",
540
+ {},
541
+ () => client.fulfillment.executors()
542
+ );
543
+ tool(
544
+ "connect_dsar_executor",
545
+ "Connect a system that runs part of a rights request \u2014 an identity platform, a CRM, anything with a rights API. Cookie Munch is not specific to any platform: the `profile` describes that system's API (paths, the words it uses for export/erase, its status vocabulary, how it signs webhooks), so connecting a new one needs no code. The secret key is stored encrypted and never returned; the response carries the webhook URL to configure in that system. Sub-tasks open automatically once a request reaches fulfilment (after identity verification), and completions are picked up by webhook or by polling.",
546
+ {
547
+ system: import_zod2.z.string().describe("The name this connection answers to in a plan, e.g. identity or crm."),
548
+ baseUrl: import_zod2.z.string().describe("The system's public https origin."),
549
+ secretKey: import_zod2.z.string().describe("Its API key, with whatever scope that system needs to export and erase a user."),
550
+ webhookSecret: import_zod2.z.string().optional().describe("Signing secret of its webhook endpoint. Optional: polling closes tasks without it."),
551
+ auto: import_zod2.z.boolean().optional().describe("Open a sub-task automatically when a request reaches fulfilment. Default true."),
552
+ profile: import_zod2.z.record(import_zod2.z.unknown()).describe("How to drive that system: { open, poll, statuses } required, plus optional subjectLookup, auth, idempotencyHeader and webhook. See the connected-systems docs for the shape.")
553
+ },
554
+ (a) => client.fulfillment.connectExecutor(a)
555
+ );
556
+ tool(
557
+ "disconnect_dsar_executor",
558
+ "Disconnect a system. Its open sub-tasks stop being driven, and the stored credentials are deleted.",
559
+ { id: import_zod2.z.string() },
560
+ async (a) => {
561
+ await client.fulfillment.disconnectExecutor(a.id);
562
+ return { disconnected: a.id };
563
+ }
564
+ );
565
+ tool(
566
+ "get_dsar_task",
567
+ "One fulfilment task in detail: its status history, what it means for the person (exported, no record, done without an export\u2026), every request Cookie Munch made to the connected system on its behalf with the answer it got (credentials removed, kept 30 days), and whether that system was redeployed mid-request. Read-only. Use it to explain why a system answered the way it did.",
568
+ { requestId: import_zod2.z.string(), taskId: import_zod2.z.string() },
569
+ (a) => client.fulfillment.task(a.requestId, a.taskId)
570
+ );
571
+ tool(
572
+ "test_dsar_executor",
573
+ "Try a connected system against one email WITHOUT filing a rights request. Runs its subject lookup and returns the raw upstream answers (credentials removed) and a one-line verdict. The open step is rendered but not sent, unless open is true on an export, which opens and polls a real export once. An erasure is never sent by a test.",
574
+ {
575
+ id: import_zod2.z.string().describe("The connection id."),
576
+ email: import_zod2.z.string().describe("The address to look up."),
577
+ operation: import_zod2.z.enum(["locate", "export", "erase"]).optional().describe("Default locate."),
578
+ open: import_zod2.z.boolean().optional().describe("For export only: open a real export and poll it once.")
579
+ },
580
+ (a) => client.fulfillment.testExecutor(a.id, {
581
+ email: a.email,
582
+ ...a.operation ? { operation: a.operation } : {},
583
+ ...typeof a.open === "boolean" ? { open: a.open } : {}
584
+ })
585
+ );
586
+ tool(
587
+ "get_dsar_task_export",
588
+ "The export bundle a connected system produced for one sub-task, fetched from that system on demand. The platform keeps no copy.",
589
+ { requestId: import_zod2.z.string(), taskId: import_zod2.z.string() },
590
+ (a) => client.fulfillment.taskExport(a.requestId, a.taskId)
591
+ );
592
+ tool(
593
+ "get_dsr_fulfillment_status",
594
+ 'Per-system fulfillment status for one request ("n of m systems done"), including which systems failed and why.',
595
+ { requestId: import_zod2.z.string() },
596
+ (a) => client.fulfillment.status(a.requestId)
597
+ );
598
+ tool(
599
+ "inspect_ai_prompt",
600
+ "Run a prompt or model response through the AI governance gateway BEFORE it reaches the model or the user. Returns action allow | redact | block plus the text to actually forward (redacted, or empty when blocked). Pass `consent` when the call concerns an identified person: data for a purpose they have not consented to is blocked outright.",
601
+ {
602
+ prompt: import_zod2.z.string(),
603
+ purpose: import_zod2.z.string().describe("What the AI call is for, e.g. support or marketing."),
604
+ model: import_zod2.z.string().optional(),
605
+ actor: import_zod2.z.string().optional(),
606
+ direction: import_zod2.z.enum(["prompt", "response"]).optional(),
607
+ consent: import_zod2.z.record(import_zod2.z.boolean()).optional().describe("The person's current permits, e.g. { marketing: false }.")
608
+ },
609
+ (a) => client.ai.inspect(a)
610
+ );
611
+ tool(
612
+ "get_ai_policy",
613
+ "The AI gateway policy: its rules, and what happens when no rule matches. `configured` is false while the built-in default applies. Read-only.",
614
+ {},
615
+ () => client.ai.getPolicy()
616
+ );
617
+ tool(
618
+ "set_ai_policy",
619
+ "REPLACE the AI gateway policy. Takes effect on the next inspected prompt: a rule with action `block` stops matching calls, `redact` strips the matched data. `default` decides every call no rule matches \u2014 `block` fails closed.",
620
+ {
621
+ rules: import_zod2.z.array(
622
+ import_zod2.z.object({
623
+ id: import_zod2.z.string(),
624
+ purposes: import_zod2.z.array(import_zod2.z.string()).optional(),
625
+ categories: import_zod2.z.array(import_zod2.z.string()).optional(),
626
+ minSensitivity: import_zod2.z.string().optional(),
627
+ action: import_zod2.z.enum(["allow", "redact", "block"]),
628
+ reason: import_zod2.z.string().describe("Recorded on every audit entry this rule decides.")
629
+ })
630
+ ).describe("Evaluated in order; the first match decides."),
631
+ default: import_zod2.z.enum(["allow", "redact", "block"])
632
+ },
633
+ (a) => client.ai.setPolicy({ rules: a.rules, default: a.default })
634
+ );
635
+ tool(
636
+ "register_ai_system",
637
+ "Declare an AI system so it appears in the inventory as registered rather than shadow. Idempotent on `id`.",
638
+ { id: import_zod2.z.string(), name: import_zod2.z.string(), provider: import_zod2.z.string().optional(), purpose: import_zod2.z.string().optional() },
639
+ (a) => client.ai.registerSystem({
640
+ id: a.id,
641
+ name: a.name,
642
+ ...a.provider ? { provider: a.provider } : {},
643
+ ...a.purpose ? { purpose: a.purpose } : {}
644
+ })
645
+ );
646
+ tool("list_ai_systems", "The AI systems declared with register_ai_system. Read-only.", {}, () => client.ai.systems());
647
+ tool(
648
+ "get_ai_audit_log",
649
+ "Recent AI gateway decisions \u2014 what was allowed, redacted or blocked, and which rule decided. Read-only.",
650
+ { limit: import_zod2.z.number().int().positive().max(500).optional() },
651
+ (a) => client.ai.audit(a.limit)
652
+ );
653
+ tool(
654
+ "get_ai_inventory",
655
+ "Every AI system seen or declared, highest risk first, plus the SHADOW subset \u2014 models observed handling data that were never registered.",
656
+ {},
657
+ () => client.ai.inventory()
658
+ );
659
+ tool(
660
+ "get_ai_lineage",
661
+ 'Which data categories reach which model. Answers "does anything send SSNs to a vendor model?".',
662
+ {},
663
+ () => client.ai.lineage()
664
+ );
665
+ }
666
+
667
+ // src/tools.ts
668
+ function text(value) {
669
+ const body = typeof value === "string" ? value : JSON.stringify(value, null, 2);
670
+ return { content: [{ type: "text", text: body }] };
671
+ }
672
+ function fail(err) {
673
+ const message = err instanceof Error ? err.message : String(err);
674
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
675
+ }
676
+ function registerTools(server, client) {
677
+ const tool = (name, description, inputSchema, run) => {
678
+ server.registerTool(name, { description, inputSchema }, async (args) => {
679
+ try {
680
+ return text(await run(args));
681
+ } catch (err) {
682
+ return fail(err);
683
+ }
684
+ });
685
+ };
686
+ const cbid = { cbid: import_zod3.z.string().describe("Site identifier (cbid). Must belong to your org.") };
687
+ const range = {
688
+ from: import_zod3.z.number().optional().describe("Epoch-ms lower bound."),
689
+ to: import_zod3.z.number().optional().describe("Epoch-ms upper bound.")
690
+ };
691
+ tool(
692
+ "whoami",
693
+ "Return the identity tied to the API key: orgId and name, the effective plan with planSource (org = its own subscription, account = covered by a higher plan another org on the same account pays for) and the features it unlocks, and the key\u2019s scopes.",
694
+ {},
695
+ () => client.me()
696
+ );
697
+ tool(
698
+ "list_account_orgs",
699
+ "The organisations on this key\u2019s account, each with its effective plan and planSource, plus the per-account org cap (max/used) and the upgrade URL. Read-only; needs an unscoped key.",
700
+ {},
701
+ () => client.orgs.list()
702
+ );
703
+ tool("list_sites", "List all sites (cbids) in your organization.", {}, () => client.sites.list());
704
+ tool(
705
+ "get_site",
706
+ "One site: its domain, cbid and verification status. Read-only.",
707
+ { cbid: import_zod3.z.string() },
708
+ (a) => client.sites.get(a.cbid)
709
+ );
710
+ tool(
711
+ "get_site_banner",
712
+ "Which banner design a site uses, or bannerId null when none is assigned. Read-only.",
713
+ { cbid: import_zod3.z.string() },
714
+ (a) => client.sites.banner(a.cbid)
715
+ );
716
+ tool(
717
+ "get_privacy_policy",
718
+ "Generate the site's privacy and cookie policy as Markdown, naming the org's data controller. Needs a contact email: the one given, else the org controller's, else the owner's. Read-only \u2014 nothing is published.",
719
+ { cbid: import_zod3.z.string(), contactEmail: import_zod3.z.string().optional(), effectiveDate: import_zod3.z.string().optional().describe("YYYY-MM-DD; defaults to today."), jurisdictions: import_zod3.z.array(import_zod3.z.string()).optional() },
720
+ (a) => client.sites.policy(a.cbid, {
721
+ ...a.contactEmail ? { contactEmail: a.contactEmail } : {},
722
+ ...a.effectiveDate ? { effectiveDate: a.effectiveDate } : {},
723
+ ...a.jurisdictions ? { jurisdictions: a.jurisdictions } : {}
724
+ })
725
+ );
726
+ tool(
727
+ "set_ad_personalization",
728
+ "Add or remove the separate 'personalised ads' choice on the site's live banner. With it on, accepting marketing no longer implies personalised ads unless `default` is true.",
729
+ { cbid: import_zod3.z.string(), enabled: import_zod3.z.boolean(), default: import_zod3.z.boolean().optional(), label: import_zod3.z.string().optional() },
730
+ (a) => client.sites.setAdPersonalization(a.cbid, {
731
+ enabled: a.enabled,
732
+ ...a.default !== void 0 ? { default: a.default } : {},
733
+ ...a.label ? { label: a.label } : {}
734
+ })
735
+ );
736
+ tool(
737
+ "analyze_session",
738
+ "Analyse a captured browsing session against the consent it ran under: which trackers fired after the visitor opted out, and what personal data left the page. Read-only.",
739
+ {
740
+ cbid: import_zod3.z.string(),
741
+ har: import_zod3.z.unknown().optional().describe("A HAR export of the session."),
742
+ requests: import_zod3.z.array(import_zod3.z.unknown()).optional().describe("Or the requests directly."),
743
+ consent: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.boolean()).optional(),
744
+ gpc: import_zod3.z.boolean().optional()
745
+ },
746
+ (a) => client.sites.analyzeSession(a.cbid, {
747
+ ...a.har !== void 0 ? { har: a.har } : {},
748
+ ...a.requests ? { requests: a.requests } : {},
749
+ ...a.consent ? { consent: a.consent } : {},
750
+ ...a.gpc !== void 0 ? { gpc: a.gpc } : {}
751
+ })
752
+ );
753
+ tool(
754
+ "create_site",
755
+ "Create a new site. The cbid is auto-generated if you omit it.",
756
+ { domain: import_zod3.z.string().describe("The site domain, e.g. example.com."), cbid: import_zod3.z.string().optional().describe("Optional explicit cbid.") },
757
+ (a) => client.sites.create({ domain: a.domain, ...typeof a.cbid === "string" ? { cbid: a.cbid } : {} })
758
+ );
759
+ tool(
760
+ "create_sites_bulk",
761
+ "Create up to 100 sites in one call. Partial success: each item reports ok or its own error, and a bad or duplicate item fails only itself.",
762
+ {
763
+ sites: import_zod3.z.array(import_zod3.z.object({ domain: import_zod3.z.string(), cbid: import_zod3.z.string().optional(), platform: import_zod3.z.string().optional() })).min(1).max(100)
764
+ },
765
+ (a) => client.sites.createBulk(a.sites)
766
+ );
767
+ tool("get_site_config", "Get the banner/consent configuration for a site.", { ...cbid }, (a) => client.sites.getConfig(a.cbid));
768
+ tool(
769
+ "update_site_config",
770
+ "Replace (overwrite) the banner/consent configuration for a site via PUT. This is destructive, not a merge: any top-level SiteConfig field you omit from `config` reverts to its default rather than keeping its current value. To change one setting, use patch_site_config instead \u2014 it merges server-side. Prefer the narrower set_* tools (set_blocking, set_banner_basics, etc.) when they cover your change.",
771
+ { ...cbid, config: import_zod3.z.record(import_zod3.z.unknown()).describe("The full SiteConfig to write, not a patch. Omitted top-level fields are NOT preserved from the current config \u2014 fetch get_site_config first and merge client-side.") },
772
+ (a) => client.sites.putConfig(a.cbid, a.config)
773
+ );
774
+ tool(
775
+ "patch_site_config",
776
+ "Change part of a site's banner/consent configuration. The patch is deep-merged over the stored config server-side, so any field you omit keeps its current value \u2014 this is the safe way to flip one setting, and it needs no read-modify-write. Use update_site_config only when you genuinely mean to replace the whole document.",
777
+ { ...cbid, config: import_zod3.z.record(import_zod3.z.unknown()).describe('The subset of SiteConfig to change, e.g. { "banner": { "showRightsLink": true } }. Everything else is preserved.') },
778
+ (a) => client.sites.patchConfig(a.cbid, a.config)
779
+ );
780
+ tool(
781
+ "preview_site_config",
782
+ 'Preview a config change WITHOUT saving it: returns the config as it would be saved, a diff of every field that would change, and any warnings (plan-gated or unrecognised fields). mode "patch" (default) previews patch_site_config; mode "put" previews update_site_config, and its diff lists everything a replace would revert to its default. Use it before any write you are not sure of.',
783
+ {
784
+ ...cbid,
785
+ config: import_zod3.z.record(import_zod3.z.unknown()).describe('The patch (or, with mode "put", the full config) to preview.'),
786
+ mode: import_zod3.z.enum(["patch", "put"]).optional().describe("patch (default) merges; put replaces.")
787
+ },
788
+ (a) => client.sites.dryRunConfig(a.cbid, a.config, { mode: a.mode ?? "patch" })
789
+ );
790
+ tool(
791
+ "verify_consent_log",
792
+ "Verify a site's consent log has not been tampered with. Each record carries the hash of the one before it, so an edited, reordered or deleted record breaks the chain and this returns valid: false. This is the evidence behind a consent log \u2014 what you check when someone asks whether the record can be trusted, not just what it says.",
793
+ { ...cbid },
794
+ (a) => client.consent.verify(a.cbid)
795
+ );
796
+ tool(
797
+ "get_consent_stats",
798
+ "Get aggregated per-day consent statistics for a site.",
799
+ { ...cbid, ...range },
800
+ (a) => client.consent.stats(a.cbid, { from: a.from, to: a.to })
801
+ );
802
+ tool(
803
+ "get_consent_log",
804
+ "Get recent anonymised consent records for a site.",
805
+ { ...cbid, ...range, limit: import_zod3.z.number().optional().describe("Max records to return (default 200).") },
806
+ (a) => client.consent.log(a.cbid, {
807
+ from: a.from,
808
+ to: a.to,
809
+ limit: a.limit
810
+ })
811
+ );
812
+ tool(
813
+ "get_subject_consent",
814
+ "One person's consent records across every site in the org \u2014 web, mobile and desktop \u2014 by the subjectId your apps attach. Consent data: needs the consent:read scope. Read-only.",
815
+ { subjectId: import_zod3.z.string() },
816
+ (a) => client.subjects.consent(a.subjectId)
817
+ );
818
+ tool("list_dsar", "List all Data Subject Access Requests for your organization.", {}, () => client.dsar.list());
819
+ tool(
820
+ "update_dsar_executor",
821
+ "Change a connected system in place \u2014 its profile, address, credential or auto flag. Only what you pass changes; the stored credential is kept unless you give a new one.",
822
+ { id: import_zod3.z.string(), patch: import_zod3.z.record(import_zod3.z.unknown()) },
823
+ (a) => client.fulfillment.updateExecutor(a.id, a.patch)
824
+ );
825
+ tool(
826
+ "get_dsar",
827
+ "One rights request by id, including its status and the deadline it must be answered by.",
828
+ { id: import_zod3.z.string() },
829
+ (a) => client.dsar.get(a.id)
830
+ );
831
+ tool(
832
+ "erase_dsar_subject",
833
+ "For a deletion request that is past identity verification: erase the person\u2019s consent records on one site, identified by their consent stamp. Irreversible. The erasure is noted on the request. If the response carries a warning, nothing was cryptographically erased \u2014 tell the user.",
834
+ { id: import_zod3.z.string(), cbid: import_zod3.z.string(), stamp: import_zod3.z.string() },
835
+ (a) => client.dsar.erase(a.id, a.cbid, a.stamp)
836
+ );
837
+ tool(
838
+ "export_dsar_subject",
839
+ "For an access or portability request that is past identity verification: the person\u2019s consent records on one site, identified by their consent stamp. The export is noted on the request.",
840
+ { id: import_zod3.z.string(), cbid: import_zod3.z.string(), stamp: import_zod3.z.string() },
841
+ (a) => client.dsar.export(a.id, a.cbid, a.stamp)
842
+ );
843
+ tool(
844
+ "get_dsar_response_notice",
845
+ "The subject-facing response notice for a request, as plain text \u2014 what to send the person. Read-only; nothing is sent.",
846
+ { id: import_zod3.z.string() },
847
+ (a) => client.dsar.response(a.id)
848
+ );
849
+ tool(
850
+ "create_dsar",
851
+ "Open a new Data Subject Access Request.",
852
+ {
853
+ type: import_zod3.z.enum(["access", "deletion", "rectification", "portability", "opt-out"]).describe("DSAR right being exercised."),
854
+ subjectEmail: import_zod3.z.string().describe("The data subject\u2019s email."),
855
+ regulation: import_zod3.z.enum(["gdpr", "ccpa"]).describe("Governing regulation."),
856
+ note: import_zod3.z.string().optional().describe("Optional free-text note."),
857
+ test: import_zod3.z.boolean().optional().describe("Mark it as a test request: kept off the SLA board and deletable afterwards with delete_test_dsar.")
858
+ },
859
+ (a) => client.dsar.create({
860
+ type: a.type,
861
+ subjectEmail: a.subjectEmail,
862
+ regulation: a.regulation,
863
+ ...typeof a.note === "string" ? { note: a.note } : {},
864
+ ...a.test === true ? { test: true } : {}
865
+ })
866
+ );
867
+ tool(
868
+ "delete_test_dsar",
869
+ "Delete a TEST rights request (one created with test: true), with its tasks and their upstream log. Real requests cannot be deleted and are refused. The deletion is recorded in the audit log.",
870
+ { id: import_zod3.z.string().describe("The test request id.") },
871
+ async (a) => {
872
+ await client.dsar.delete(a.id);
873
+ return { deleted: a.id };
874
+ }
875
+ );
876
+ tool(
877
+ "advance_dsar",
878
+ "Advance a DSAR to a new status (received \u2192 verifying \u2192 in_progress \u2192 completed, or rejected).",
879
+ {
880
+ id: import_zod3.z.string().describe("The DSAR id."),
881
+ toStatus: import_zod3.z.enum(["received", "verifying", "in_progress", "completed", "rejected"]).describe("Target status.")
882
+ },
883
+ (a) => client.dsar.advance(a.id, a.toStatus)
884
+ );
885
+ tool(
886
+ "export_consent",
887
+ "Export a site\u2019s consent log as raw CSV text.",
888
+ { ...cbid, ...range },
889
+ (a) => client.consent.export(a.cbid, { from: a.from, to: a.to })
890
+ );
891
+ tool(
892
+ "get_receipt",
893
+ "Fetch the signed consent receipt for a single consent record (by stamp).",
894
+ { ...cbid, stamp: import_zod3.z.string().describe("The consent record stamp/id.") },
895
+ (a) => client.consent.receipt(a.cbid, a.stamp)
896
+ );
897
+ tool("delete_site", "Delete a site (cbid) and its configuration from your org.", { ...cbid }, async (a) => {
898
+ await client.sites.delete(a.cbid);
899
+ return { ok: true };
900
+ });
901
+ tool("list_vendors", "List vendors (processors) with their computed risk scores.", {}, () => client.vendors.list());
902
+ tool(
903
+ "create_vendor",
904
+ "Register a vendor (processor) and compute its risk score.",
905
+ {
906
+ name: import_zod3.z.string().describe("Vendor name."),
907
+ category: import_zod3.z.string().describe("Vendor category, e.g. analytics."),
908
+ dataShared: import_zod3.z.array(import_zod3.z.string()).describe("Categories of data shared with the vendor."),
909
+ dpaSigned: import_zod3.z.boolean().describe("Whether a Data Processing Agreement is signed."),
910
+ subprocessors: import_zod3.z.number().describe("Number of subprocessors."),
911
+ certifications: import_zod3.z.array(import_zod3.z.string()).describe("Compliance certifications, e.g. ISO27001."),
912
+ region: import_zod3.z.string().describe("Primary processing region, e.g. EU.")
913
+ },
914
+ (a) => client.vendors.create({
915
+ name: a.name,
916
+ category: a.category,
917
+ dataShared: a.dataShared,
918
+ dpaSigned: a.dpaSigned,
919
+ subprocessors: a.subprocessors,
920
+ certifications: a.certifications,
921
+ region: a.region
922
+ })
923
+ );
924
+ tool("list_ropa", "List Records of Processing Activities (RoPA) for your organization.", {}, () => client.ropa.list());
925
+ tool("export_ropa_csv", "The org\u2019s RoPA (GDPR Art. 30) as CSV. Read-only.", {}, () => client.ropa.exportCsv());
926
+ tool(
927
+ "create_ropa",
928
+ "Create a Record of Processing Activity (RoPA) entry.",
929
+ {
930
+ name: import_zod3.z.string().describe("Activity name."),
931
+ purpose: import_zod3.z.string().describe("Purpose of processing."),
932
+ legalBasis: import_zod3.z.enum(["consent", "contract", "legal-obligation", "vital-interests", "public-task", "legitimate-interests"]).describe("GDPR legal basis."),
933
+ dataCategories: import_zod3.z.array(import_zod3.z.string()).describe("Categories of personal data."),
934
+ recipients: import_zod3.z.array(import_zod3.z.string()).describe("Recipients of the data."),
935
+ retentionDays: import_zod3.z.number().describe("Retention period in days."),
936
+ crossBorderTransfer: import_zod3.z.boolean().describe("Whether data is transferred across borders.")
937
+ },
938
+ (a) => client.ropa.create({
939
+ name: a.name,
940
+ purpose: a.purpose,
941
+ legalBasis: a.legalBasis,
942
+ dataCategories: a.dataCategories,
943
+ recipients: a.recipients,
944
+ retentionDays: a.retentionDays,
945
+ crossBorderTransfer: a.crossBorderTransfer
946
+ })
947
+ );
948
+ tool(
949
+ "get_site_cookies",
950
+ "List the cookies discovered on a site, with their consent categories.",
951
+ { ...cbid },
952
+ (a) => client.sites.cookies(a.cbid)
953
+ );
954
+ tool(
955
+ "scan_site",
956
+ "Trigger a cookie scan for a site. Returns the scan job state.",
957
+ { ...cbid },
958
+ (a) => client.sites.scan(a.cbid)
959
+ );
960
+ tool(
961
+ "get_scan_status",
962
+ "Get the status/result of a site\u2019s most recent cookie scan.",
963
+ { ...cbid },
964
+ (a) => client.sites.scanStatus(a.cbid)
965
+ );
966
+ tool(
967
+ "get_ab_results",
968
+ "Get A/B banner experiment results (per variant) for a site.",
969
+ { ...cbid },
970
+ (a) => client.sites.ab(a.cbid)
971
+ );
972
+ tool("list_brand_kits", "List reusable brand kits (colors/logo/typography) for your org.", {}, () => client.brandKits.list());
973
+ tool("list_preferences", "List configurable consent preferences/purposes for your org.", {}, () => client.preferences.list());
974
+ tool("list_members", "List the members of your organization.", {}, () => client.members.list());
975
+ tool("list_api_keys", "List the API keys for your organization (secrets are not returned).", {}, () => client.keys.list());
976
+ tool("get_usage", "Get the current usage/quota summary for your organization.", {}, () => client.usage());
977
+ tool("list_webhooks", "List webhook subscriptions for your organization.", {}, () => client.webhooks.list());
978
+ tool(
979
+ "create_webhook",
980
+ "Create a webhook subscription. The response includes the signing secret (shown once).",
981
+ {
982
+ url: import_zod3.z.string().describe("HTTPS endpoint to deliver events to."),
983
+ events: import_zod3.z.array(import_zod3.z.string()).describe("Event names to subscribe to, e.g. consent.created."),
984
+ cbid: import_zod3.z.string().optional().describe("Optional site cbid to scope the subscription to.")
985
+ },
986
+ (a) => client.webhooks.create({
987
+ url: a.url,
988
+ events: a.events,
989
+ ...typeof a.cbid === "string" ? { cbid: a.cbid } : {}
990
+ })
991
+ );
992
+ tool(
993
+ "roll_webhook_secret",
994
+ "Rotate a webhook subscription\u2019s signing secret. The new secret is returned once; the receiving endpoint must be updated to verify with it.",
995
+ { id: import_zod3.z.string() },
996
+ (a) => client.webhooks.rollSecret(a.id)
997
+ );
998
+ tool(
999
+ "test_webhook",
1000
+ "Send a signed test event to a webhook subscription now, and report what the endpoint answered.",
1001
+ { id: import_zod3.z.string() },
1002
+ (a) => client.webhooks.test(a.id)
1003
+ );
1004
+ tool(
1005
+ "list_webhook_dead_letters",
1006
+ "Webhook deliveries that failed every retry, newest first, with the last status or error. Read-only.",
1007
+ {},
1008
+ () => client.webhooks.deadLetters()
1009
+ );
1010
+ tool(
1011
+ "replay_webhook_dead_letter",
1012
+ "Deliver a dead-lettered webhook event again, to the subscription as it is now.",
1013
+ { id: import_zod3.z.string() },
1014
+ (a) => client.webhooks.replayDeadLetter(a.id)
1015
+ );
1016
+ tool(
1017
+ "update_webhook",
1018
+ "Change a webhook subscription, or pause it with active:false (delivery stops; the subscription is kept). Only the fields given change. cbid: null widens it to every property in the org.",
1019
+ {
1020
+ id: import_zod3.z.string(),
1021
+ url: import_zod3.z.string().optional(),
1022
+ events: import_zod3.z.array(import_zod3.z.string()).optional(),
1023
+ cbid: import_zod3.z.string().nullable().optional(),
1024
+ active: import_zod3.z.boolean().optional()
1025
+ },
1026
+ (a) => client.webhooks.update(a.id, {
1027
+ ...a.url !== void 0 ? { url: a.url } : {},
1028
+ ...a.events !== void 0 ? { events: a.events } : {},
1029
+ ...a.cbid !== void 0 ? { cbid: a.cbid } : {},
1030
+ ...a.active !== void 0 ? { active: a.active } : {}
1031
+ })
1032
+ );
1033
+ tool("delete_webhook", "Delete a webhook subscription by id.", { id: import_zod3.z.string().describe("The webhook subscription id.") }, async (a) => {
1034
+ await client.webhooks.delete(a.id);
1035
+ return { ok: true };
1036
+ });
1037
+ tool(
1038
+ "verify_site",
1039
+ "Verify that your organization controls a domain by one of three challenge methods (dns / meta / file). Required to unlock consent export and signed receipts.",
1040
+ { ...cbid, method: import_zod3.z.enum(["dns", "meta", "file"]).describe("Challenge method: dns (TXT record), meta (HTML tag), or file (/.well-known/cookiemunch-verify.txt).") },
1041
+ (a) => client.sites.verify(a.cbid, a.method)
1042
+ );
1043
+ tool(
1044
+ "get_verification_challenge",
1045
+ "Exactly what to publish to prove control of a site\u2019s domain: the DNS TXT record, the meta tag, the file, or installing the embed. Read-only \u2014 publish one, then call verify_site.",
1046
+ { cbid: import_zod3.z.string() },
1047
+ (a) => client.sites.verifyChallenge(a.cbid)
1048
+ );
1049
+ tool(
1050
+ "match_site_brand",
1051
+ "Extract theme colors and typography from a site's homepage to suggest a matching brand theme for the consent banner.",
1052
+ { ...cbid },
1053
+ (a) => client.sites.brand(a.cbid)
1054
+ );
1055
+ tool(
1056
+ "get_flow",
1057
+ "Get the v2 banner flow config for a site, including current views, categories, and any lint issues.",
1058
+ { cbid: import_zod3.z.string().describe("Site identifier (cbid).") },
1059
+ (a) => client.sites.getFlow(a.cbid)
1060
+ );
1061
+ tool(
1062
+ "edit_flow",
1063
+ "Apply a batch of structured edit operations to a site's v2 banner flow. All ops are validated server-side and the final config must be lint-clean before it is saved.",
1064
+ {
1065
+ cbid: import_zod3.z.string().describe("Site identifier (cbid)."),
1066
+ operations: import_zod3.z.array(import_zod3.z.record(import_zod3.z.unknown())).describe(
1067
+ 'Ordered list of ops. Each must have an "op" field: addView | removeView | addElement | setButtonTransition | addCustomCategory.'
1068
+ )
1069
+ },
1070
+ (a) => client.sites.editFlow(a.cbid, a.operations)
1071
+ );
1072
+ tool(
1073
+ "set_flow",
1074
+ "Wholesale replace a site's v2 banner flow with a complete config (e.g. from a template). Validated server-side before it is saved.",
1075
+ {
1076
+ cbid: import_zod3.z.string().describe("Site identifier (cbid)."),
1077
+ config: import_zod3.z.record(import_zod3.z.unknown()).describe("A full v2 FlowConfig object ({ v:2, flow, categories, ... }).")
1078
+ },
1079
+ (a) => client.sites.setFlow(a.cbid, a.config)
1080
+ );
1081
+ registerConfigTools(tool, client);
1082
+ registerPlatformTools(tool, client);
1083
+ tool(
1084
+ "invite_member",
1085
+ "Invite a person to your organization by email. role must be admin, member, or viewer. The org is derived from the API key \u2014 you cannot invite into a different org.",
1086
+ {
1087
+ email: import_zod3.z.string().describe("Email address of the person to invite."),
1088
+ role: import_zod3.z.enum(["admin", "member", "viewer"]).describe("Role to assign: admin, member, or viewer. owner is not assignable via API key.")
1089
+ },
1090
+ (a) => client.members.invite(a.email, a.role)
1091
+ );
1092
+ tool(
1093
+ "set_member_role",
1094
+ "Change the role of a member in your organization. The org owner's role cannot be changed.",
1095
+ {
1096
+ userId: import_zod3.z.string().describe("The userId of the member whose role to change."),
1097
+ role: import_zod3.z.enum(["admin", "member", "viewer"]).describe("New role: admin, member, or viewer.")
1098
+ },
1099
+ (a) => client.members.setRole(a.userId, a.role)
1100
+ );
1101
+ tool(
1102
+ "remove_member",
1103
+ "Remove a member from your organization. The org owner cannot be removed.",
1104
+ {
1105
+ userId: import_zod3.z.string().describe("The userId of the member to remove.")
1106
+ },
1107
+ async (a) => {
1108
+ await client.members.remove(a.userId);
1109
+ return { ok: true };
1110
+ }
1111
+ );
1112
+ tool(
1113
+ "create_brand_kit",
1114
+ "Create a reusable brand kit (colors, logo, typography) for your organization's consent banners.",
1115
+ {
1116
+ name: import_zod3.z.string().describe("Display name for the brand kit."),
1117
+ theme: import_zod3.z.record(import_zod3.z.unknown()).describe("Theme tokens (colors, fonts, etc.) as a JSON object."),
1118
+ content: import_zod3.z.record(import_zod3.z.unknown()).optional().describe("Optional banner copy overrides."),
1119
+ logoUrl: import_zod3.z.string().optional().describe("Optional URL for your org logo."),
1120
+ customCss: import_zod3.z.string().optional().describe("Optional custom CSS string to inject into the banner.")
1121
+ },
1122
+ (a) => client.brandKits.create({
1123
+ name: a.name,
1124
+ theme: a.theme,
1125
+ ...a.content !== void 0 ? { content: a.content } : {},
1126
+ ...typeof a.logoUrl === "string" ? { logoUrl: a.logoUrl } : {},
1127
+ ...typeof a.customCss === "string" ? { customCss: a.customCss } : {}
1128
+ })
1129
+ );
1130
+ tool(
1131
+ "delete_brand_kit",
1132
+ "Delete a brand kit by id. The kit must belong to your organization (cross-org deletes are rejected).",
1133
+ {
1134
+ id: import_zod3.z.string().describe("The brand kit id to delete.")
1135
+ },
1136
+ async (a) => {
1137
+ await client.brandKits.delete(a.id);
1138
+ return { ok: true };
1139
+ }
1140
+ );
1141
+ tool(
1142
+ "save_preference",
1143
+ "Save or update the consent preferences for an end-user (by subjectId) within your organization. purposes is a map of purpose-name \u2192 boolean.",
1144
+ {
1145
+ subjectId: import_zod3.z.string().describe("The end-user identifier (e.g. email or internal user id)."),
1146
+ purposes: import_zod3.z.record(import_zod3.z.boolean()).describe('Map of purpose names to consent values, e.g. { "newsletter": true, "analytics": false }.')
1147
+ },
1148
+ (a) => client.preferences.save(a.subjectId, a.purposes)
1149
+ );
1150
+ tool(
1151
+ "get_preference",
1152
+ "One end-user\u2019s saved preferences, by subjectId. Read-only.",
1153
+ { subjectId: import_zod3.z.string() },
1154
+ (a) => client.preferences.get(a.subjectId)
1155
+ );
1156
+ tool(
1157
+ "issue_api_key",
1158
+ "Issue a new API key. The secret is returned once and never again. Prefer the least privilege the job needs: `scopes` limits what it can do, `cbids` locks it to specific sites (it then cannot reach anything org-wide), `expiresInDays` retires it. Omitting all three mints a full-access, org-wide, non-expiring key.",
1159
+ {
1160
+ name: import_zod3.z.string().optional(),
1161
+ scopes: import_zod3.z.array(import_zod3.z.string()).optional(),
1162
+ cbids: import_zod3.z.array(import_zod3.z.string()).min(1).optional(),
1163
+ expiresInDays: import_zod3.z.number().int().min(1).max(3650).optional()
1164
+ },
1165
+ (a) => client.keys.issue({
1166
+ ...a.name ? { name: a.name } : {},
1167
+ ...a.scopes ? { scopes: a.scopes } : {},
1168
+ ...a.cbids ? { cbids: a.cbids } : {},
1169
+ ...a.expiresInDays !== void 0 ? { expiresInDays: a.expiresInDays } : {}
1170
+ })
1171
+ );
1172
+ tool("revoke_api_key", "Revoke an API key by its prefix. Takes effect immediately; anything using that key stops working.", { prefix: import_zod3.z.string() }, async (a) => {
1173
+ await client.keys.revoke(a.prefix);
1174
+ return { revoked: a.prefix };
1175
+ });
1176
+ tool(
1177
+ "import_cookie_declaration",
1178
+ "Read a cookie declaration exported from another CMP (OneTrust, Cookiebot, CookieYes, or any CSV/JSON with the same columns) and translate its categories into ours. Nothing is applied \u2014 it comes back for review, with the rows whose category could not be placed listed separately.",
1179
+ { cbid: import_zod3.z.string(), data: import_zod3.z.string().describe("The exported file, as text (CSV or JSON).") },
1180
+ (a) => client.sites.importDeclaration(a.cbid, a.data)
1181
+ );
1182
+ tool(
1183
+ "get_blocked_pages",
1184
+ "Pages where the embed could not load its banner renderer \u2014 the page\u2019s Content Security Policy or Trusted Types policy refused it, so nobody there can be asked for consent. An empty list is healthy. Read-only.",
1185
+ { cbid: import_zod3.z.string() },
1186
+ (a) => client.sites.blocked(a.cbid)
1187
+ );
1188
+ tool(
1189
+ "roll_api_key",
1190
+ "Rotate an API key: returns a new secret once, with the same name, scopes, property lock and expiry. The old secret stops working immediately \u2014 if it is the key this server runs with, this server stops working too until it is reconfigured.",
1191
+ { prefix: import_zod3.z.string() },
1192
+ (a) => client.keys.roll(a.prefix)
1193
+ );
1194
+ tool(
1195
+ "update_api_key",
1196
+ "Rename an API key, or replace its scopes or the properties it is locked to. Only the fields given change; the secret does not.",
1197
+ {
1198
+ prefix: import_zod3.z.string(),
1199
+ name: import_zod3.z.string().optional(),
1200
+ scopes: import_zod3.z.array(import_zod3.z.string()).optional(),
1201
+ cbids: import_zod3.z.array(import_zod3.z.string()).min(1).optional()
1202
+ },
1203
+ (a) => client.keys.update(a.prefix, {
1204
+ ...a.name !== void 0 ? { name: a.name } : {},
1205
+ ...a.scopes ? { scopes: a.scopes } : {},
1206
+ ...a.cbids ? { cbids: a.cbids } : {}
1207
+ })
1208
+ );
1209
+ tool("get_org", "Your organisation: id, name, plan and logo URL. Read-only.", {}, () => client.org.get());
1210
+ tool(
1211
+ "update_org",
1212
+ "Rename your organisation or set its logo. logoUrl: null removes the logo; upload an image with upload_asset to get a URL. Deleting the organisation is not possible here.",
1213
+ { name: import_zod3.z.string().min(1).optional(), logoUrl: import_zod3.z.string().nullable().optional() },
1214
+ (a) => client.org.update({
1215
+ ...a.name !== void 0 ? { name: a.name } : {},
1216
+ ...a.logoUrl !== void 0 ? { logoUrl: a.logoUrl } : {}
1217
+ })
1218
+ );
1219
+ tool(
1220
+ "get_audit_log",
1221
+ "The organisation\u2019s audit log, newest first: administrative changes made in the dashboard or through the API, and who made them. Read-only.",
1222
+ { limit: import_zod3.z.number().int().min(1).max(500).optional() },
1223
+ (a) => client.audit(a.limit)
1224
+ );
1225
+ tool(
1226
+ "upload_asset",
1227
+ "Upload an image \u2014 a banner logo \u2014 and get its public URL. data is base64 (a data: URL also works); PNG, JPEG, WebP, GIF or SVG, up to 1,000,000 bytes.",
1228
+ {
1229
+ data: import_zod3.z.string(),
1230
+ contentType: import_zod3.z.enum(["image/png", "image/jpeg", "image/webp", "image/gif", "image/svg+xml"])
1231
+ },
1232
+ (a) => client.assets.upload({ data: a.data, contentType: a.contentType })
1233
+ );
1234
+ tool(
1235
+ "delete_asset",
1236
+ "Delete a stored image by its URL (or file name). Only your own organisation\u2019s images can be named.",
1237
+ { url: import_zod3.z.string().describe("The URL upload_asset returned, or just its file name.") },
1238
+ async (a) => {
1239
+ await client.assets.delete(a.url);
1240
+ return { deleted: a.url };
1241
+ }
1242
+ );
1243
+ tool(
1244
+ "list_customers",
1245
+ "The child orgs this reseller has provisioned, with pooled usage against the reseller plan\u2019s cap. Read-only.",
1246
+ {},
1247
+ () => client.reseller.list()
1248
+ );
1249
+ tool(
1250
+ "get_customer",
1251
+ "One child org: its controller details and usage. Read-only.",
1252
+ { id: import_zod3.z.string() },
1253
+ (a) => client.reseller.get(a.id)
1254
+ );
1255
+ tool(
1256
+ "provision_customer",
1257
+ "Create a new child org under this reseller. With `mintKey`, its first API key is returned once. Counts toward the reseller plan\u2019s pooled limits.",
1258
+ {
1259
+ name: import_zod3.z.string(),
1260
+ ownerEmail: import_zod3.z.string().optional(),
1261
+ delegatedAccess: import_zod3.z.boolean().optional().describe("Whether this reseller may act inside the child."),
1262
+ mintKey: import_zod3.z.boolean().optional(),
1263
+ keyScopes: import_zod3.z.array(import_zod3.z.string()).optional()
1264
+ },
1265
+ (a) => client.reseller.create({
1266
+ name: a.name,
1267
+ ...a.ownerEmail ? { ownerEmail: a.ownerEmail } : {},
1268
+ ...a.delegatedAccess !== void 0 ? { delegatedAccess: a.delegatedAccess } : {},
1269
+ ...a.mintKey !== void 0 ? { mintKey: a.mintKey } : {},
1270
+ ...a.keyScopes ? { keyScopes: a.keyScopes } : {}
1271
+ })
1272
+ );
1273
+ tool(
1274
+ "update_customer",
1275
+ "Change a child org\u2019s status, delegated access or DSAR routing. `dsarRouting: null` clears the override.",
1276
+ {
1277
+ id: import_zod3.z.string(),
1278
+ status: import_zod3.z.enum(["active", "suspended"]).optional(),
1279
+ delegatedAccess: import_zod3.z.boolean().optional(),
1280
+ dsarRouting: import_zod3.z.enum(["reseller", "child"]).nullable().optional()
1281
+ },
1282
+ (a) => client.reseller.update(a.id, {
1283
+ ...a.status ? { status: a.status } : {},
1284
+ ...a.delegatedAccess !== void 0 ? { delegatedAccess: a.delegatedAccess } : {},
1285
+ ...a.dsarRouting !== void 0 ? { dsarRouting: a.dsarRouting } : {}
1286
+ })
1287
+ );
1288
+ tool(
1289
+ "suspend_customer",
1290
+ "Suspend a child org: it stops ingesting consent and its keys stop working, and it can be reactivated with update_customer. Its data is kept. (Permanent deletion is deliberately not available here.)",
1291
+ { id: import_zod3.z.string() },
1292
+ async (a) => {
1293
+ await client.reseller.deprovision(a.id);
1294
+ return { suspended: a.id };
1295
+ }
1296
+ );
1297
+ tool(
1298
+ "list_customer_keys",
1299
+ "A child org's API key prefixes. Never the secrets. Read-only.",
1300
+ { id: import_zod3.z.string() },
1301
+ (a) => client.reseller.listKeys(a.id)
1302
+ );
1303
+ tool(
1304
+ "issue_customer_key",
1305
+ "Mint an API key for a child org. The secret is returned once. Prefer scopes and a property lock over a full-access key.",
1306
+ { id: import_zod3.z.string(), name: import_zod3.z.string().optional(), scopes: import_zod3.z.array(import_zod3.z.string()).optional(), cbids: import_zod3.z.array(import_zod3.z.string()).min(1).optional() },
1307
+ (a) => client.reseller.mintKey(a.id, {
1308
+ ...a.name ? { name: a.name } : {},
1309
+ ...a.scopes ? { scopes: a.scopes } : {},
1310
+ ...a.cbids ? { cbids: a.cbids } : {}
1311
+ })
1312
+ );
1313
+ tool("revoke_customer_key", "Revoke a child org's API key by prefix. Takes effect immediately.", { id: import_zod3.z.string(), prefix: import_zod3.z.string() }, async (a) => {
1314
+ await client.reseller.revokeKey(a.id, a.prefix);
1315
+ return { revoked: a.prefix };
1316
+ });
1317
+ tool(
1318
+ "erase_subject_data",
1319
+ "IRREVERSIBLY crypto-erase a data subject's consent records by their consent-receipt stamp (GDPR/CCPA deletion). Destroys the encryption key; the tamper-evident chain stays intact.",
1320
+ { cbid: import_zod3.z.string(), stamp: import_zod3.z.string() },
1321
+ (a) => client.consent.eraseSubject(a.cbid, a.stamp)
1322
+ );
1323
+ tool(
1324
+ "export_subject_data",
1325
+ "Export a data subject's consent records (GDPR access/portability).",
1326
+ { cbid: import_zod3.z.string(), stamp: import_zod3.z.string() },
1327
+ (a) => client.consent.exportSubject(a.cbid, a.stamp)
1328
+ );
1329
+ tool("list_banners", "List reusable banner designs in your org.", {}, () => client.banners.list());
1330
+ tool(
1331
+ "create_banner",
1332
+ "Create a reusable banner design.",
1333
+ { name: import_zod3.z.string(), json: import_zod3.z.record(import_zod3.z.unknown()).describe("A v2 BannerConfig.") },
1334
+ (a) => client.banners.create({ name: a.name, json: a.json })
1335
+ );
1336
+ tool("get_banner", "Get a banner design by id.", { id: import_zod3.z.string() }, (a) => client.banners.get(a.id));
1337
+ tool(
1338
+ "update_banner",
1339
+ "Update a banner design (name and/or config).",
1340
+ { id: import_zod3.z.string(), name: import_zod3.z.string().optional(), json: import_zod3.z.record(import_zod3.z.unknown()).optional() },
1341
+ (a) => client.banners.update(a.id, {
1342
+ ...a.name !== void 0 ? { name: a.name } : {},
1343
+ ...a.json !== void 0 ? { json: a.json } : {}
1344
+ })
1345
+ );
1346
+ tool("delete_banner", "Delete a banner design (fails if assigned to sites).", { id: import_zod3.z.string() }, async (a) => {
1347
+ await client.banners.delete(a.id);
1348
+ return { ok: true };
1349
+ });
1350
+ tool(
1351
+ "get_banner_assignments",
1352
+ "The sites (cbids) a banner design is currently assigned to. Read-only.",
1353
+ { id: import_zod3.z.string() },
1354
+ (a) => client.banners.assignments(a.id)
1355
+ );
1356
+ tool(
1357
+ "assign_banner",
1358
+ "Set which sites (cbids) use a banner design.",
1359
+ { id: import_zod3.z.string(), cbids: import_zod3.z.array(import_zod3.z.string()) },
1360
+ (a) => client.banners.setAssignments(a.id, a.cbids)
1361
+ );
1362
+ tool(
1363
+ "publish_banner",
1364
+ "Publish a design live to all its assigned sites.",
1365
+ { id: import_zod3.z.string() },
1366
+ (a) => client.banners.publish(a.id)
1367
+ );
1368
+ }
1369
+
1370
+ // src/index.ts
1371
+ function createCookieMunchMcp(opts) {
1372
+ const client = opts.client ?? (0, import_sdk.createCookieMunch)({ apiKey: opts.apiKey, baseUrl: opts.baseUrl });
1373
+ const server = new import_mcp.McpServer({ name: "cookiemunch", version: "1.0.0" });
1374
+ registerTools(server, client);
1375
+ return server;
1376
+ }
1377
+
1378
+ // src/bin.ts
1379
+ async function main() {
1380
+ const apiKey = process.env.COOKIEMUNCH_API_KEY;
1381
+ if (!apiKey) {
1382
+ console.error("COOKIEMUNCH_API_KEY is required.");
1383
+ process.exit(1);
1384
+ }
1385
+ const baseUrl = process.env.COOKIEMUNCH_BASE_URL ?? "http://localhost:8787";
1386
+ const server = createCookieMunchMcp({ apiKey, baseUrl });
1387
+ const transport = new import_stdio.StdioServerTransport();
1388
+ await server.connect(transport);
1389
+ }
1390
+ main().catch((err) => {
1391
+ console.error("cookiemunch-mcp failed to start:", err);
1392
+ process.exit(1);
1393
+ });
1394
+ //# sourceMappingURL=bin.cjs.map