@seldonframe/mcp 1.40.14 → 1.45.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/package.json +18 -122
  2. package/src/tools.js +210 -0
  3. package/src/welcome.js +488 -488
package/src/tools.js CHANGED
@@ -5371,6 +5371,216 @@ export const TOOLS = [
5371
5371
  return result;
5372
5372
  },
5373
5373
  },
5374
+ // ─── v1.47.0 — Partner-agency onboarding (Build P1 #2) ─────────────────
5375
+ //
5376
+ // Thin MCP wrappers around the partner-agency API ops shipped in
5377
+ // v1.17 - v1.20. The backend logic + state model already exist;
5378
+ // these tools let Claude Code drive end-to-end agency setup in one
5379
+ // conversation instead of routing the operator through dashboard
5380
+ // pages.
5381
+ //
5382
+ // Canonical sequence Claude Code follows when an agency owner says
5383
+ // "set up my agency brand":
5384
+ // 1. register_partner_agency({ name, logo_url?, primary_color?, ... })
5385
+ // → returns agency_id + gated_pending flag
5386
+ // 2. (optional) register_partner_agency_sender_domain({ agency_id,
5387
+ // domain }) → returns DNS records the operator adds at their
5388
+ // registrar
5389
+ // 3. (after DNS propagates) verify_partner_agency_sender_domain(
5390
+ // { agency_id }) → polls Resend; flips verified_sender_at on
5391
+ // success so the agency's outbound emails can ship from
5392
+ // welcome@agency-domain.com
5393
+ // 4. For each client workspace the agency manages:
5394
+ // attach_workspace_to_partner_agency({ workspace_id, agency_id })
5395
+ // → chrome substitution applies on next page load
5396
+ //
5397
+ // The full sequence + DNS gotchas + A2P 10DLC implications live in
5398
+ // packages/crm/src/lib/partner-agencies/SETUP_GUIDE.md — point the
5399
+ // operator there if they need depth beyond the tool descriptions.
5400
+ {
5401
+ name: "register_partner_agency",
5402
+ description:
5403
+ "Create a partner-agency entity for white-label SaaS reselling. STEP 1 of agency onboarding. " +
5404
+ "The agency owns multiple client workspaces; chrome (brand name, logo, colors, support URLs) " +
5405
+ "is substituted on each workspace when its parent_agency_id is set to this agency. " +
5406
+ "Plan-gate: caller must own a workspace on the Scale tier ($99/mo). If not, the agency is " +
5407
+ "created in 'pending' status (gated_pending=true) — caller upgrades a workspace and re-runs to flip it. " +
5408
+ "After this call succeeds: optionally register a sender domain (register_partner_agency_sender_domain), " +
5409
+ "then attach client workspaces (attach_workspace_to_partner_agency). " +
5410
+ 'Example: register_partner_agency({ name: "Acme Digital", primary_color: "#1FAE85", support_email: "help@acmedigital.com" })',
5411
+ inputSchema: obj(
5412
+ {
5413
+ workspace_id: str(
5414
+ "Workspace bearer that owns the agency. Falls back to default workspace if omitted.",
5415
+ ),
5416
+ name: str(
5417
+ "Agency display name (2+ chars). Surfaces as the brand_name in operator-facing chrome.",
5418
+ ),
5419
+ slug: str(
5420
+ "Optional URL-safe slug. Defaults to slugified name. Must be unique across all agencies.",
5421
+ ),
5422
+ logo_url: str(
5423
+ "Optional logo URL (publicly accessible PNG/SVG). Replaces SeldonFrame logo in chrome.",
5424
+ ),
5425
+ primary_color: str(
5426
+ "Optional brand primary color (hex, e.g. '#1FAE85'). Cascades to UI components.",
5427
+ ),
5428
+ accent_color: str(
5429
+ "Optional brand accent color (hex). Used for secondary surfaces.",
5430
+ ),
5431
+ support_email: str(
5432
+ "Optional support email shown in operator-facing chrome (e.g., 'help@acmedigital.com').",
5433
+ ),
5434
+ support_url: str(
5435
+ "Optional support URL shown in chrome (e.g., 'https://acmedigital.com/help').",
5436
+ ),
5437
+ hide_powered_by_badge: {
5438
+ type: "boolean",
5439
+ description:
5440
+ "Hide the 'Powered by SeldonFrame' footer badge. Defaults to false. Scale tier feature.",
5441
+ },
5442
+ },
5443
+ ["name"],
5444
+ ),
5445
+ handler: async (a) => {
5446
+ const ws = wsOrDefault(a.workspace_id);
5447
+ return api("POST", "/partner-agencies", {
5448
+ body: {
5449
+ op: "register",
5450
+ name: a.name,
5451
+ slug: a.slug,
5452
+ logo_url: a.logo_url,
5453
+ primary_color: a.primary_color,
5454
+ accent_color: a.accent_color,
5455
+ support_email: a.support_email,
5456
+ support_url: a.support_url,
5457
+ hide_powered_by_badge: a.hide_powered_by_badge,
5458
+ },
5459
+ workspace_id: ws,
5460
+ });
5461
+ },
5462
+ },
5463
+ {
5464
+ name: "register_partner_agency_sender_domain",
5465
+ description:
5466
+ "Register a custom email-sender domain for a partner-agency. STEP 2 of agency onboarding (optional but recommended — without it, agency emails ship from welcome@seldonframe.com). " +
5467
+ "Calls Resend's /domains endpoint, persists the resend_domain_id on the agency row, and returns the DNS records the agency must add at their registrar (Cloudflare / Namecheap / GoDaddy / etc.). " +
5468
+ "DNS propagation typically takes 5-60 minutes. After propagation, call verify_partner_agency_sender_domain to poll Resend + flip verified_sender_at. " +
5469
+ "Requires: RESEND_API_KEY configured on the SeldonFrame backend. " +
5470
+ 'Example: register_partner_agency_sender_domain({ agency_id: "uuid", domain: "acmedigital.com", sender_local_part: "hello" })',
5471
+ inputSchema: obj(
5472
+ {
5473
+ workspace_id: str("Workspace bearer that owns the agency."),
5474
+ agency_id: str("Agency id from register_partner_agency response."),
5475
+ domain: str(
5476
+ "Domain to register (e.g., 'acmedigital.com'). The agency's customer-facing emails ship from <local>@<domain> once verified.",
5477
+ ),
5478
+ sender_local_part: str(
5479
+ "Optional local part for the sender address. Defaults to 'welcome'. Final address: <local>@<domain>.",
5480
+ ),
5481
+ },
5482
+ ["agency_id", "domain"],
5483
+ ),
5484
+ handler: async (a) => {
5485
+ const ws = wsOrDefault(a.workspace_id);
5486
+ return api("POST", "/partner-agencies", {
5487
+ body: {
5488
+ op: "register_sender_domain",
5489
+ agency_id: a.agency_id,
5490
+ domain: a.domain,
5491
+ sender_local_part: a.sender_local_part,
5492
+ },
5493
+ workspace_id: ws,
5494
+ });
5495
+ },
5496
+ },
5497
+ {
5498
+ name: "verify_partner_agency_sender_domain",
5499
+ description:
5500
+ "Poll Resend for sender-domain verification status. STEP 3 of agency onboarding. " +
5501
+ "Run this after the agency has added the DNS records returned by register_partner_agency_sender_domain " +
5502
+ "and DNS has had time to propagate (usually 5-60 minutes; some registrars can take longer). " +
5503
+ "On success: sets verified_sender_at on the agency row + populates sender_email_address. The branding " +
5504
+ "resolver then exposes the verified sender to outbound email paths automatically. " +
5505
+ "Idempotent + safe to call repeatedly while waiting on DNS. " +
5506
+ 'Example: verify_partner_agency_sender_domain({ agency_id: "uuid" })',
5507
+ inputSchema: obj(
5508
+ {
5509
+ workspace_id: str("Workspace bearer that owns the agency."),
5510
+ agency_id: str("Agency id from register_partner_agency response."),
5511
+ },
5512
+ ["agency_id"],
5513
+ ),
5514
+ handler: async (a) => {
5515
+ const ws = wsOrDefault(a.workspace_id);
5516
+ return api("POST", "/partner-agencies", {
5517
+ body: {
5518
+ op: "verify_sender_domain",
5519
+ agency_id: a.agency_id,
5520
+ },
5521
+ workspace_id: ws,
5522
+ });
5523
+ },
5524
+ },
5525
+ {
5526
+ name: "attach_workspace_to_partner_agency",
5527
+ description:
5528
+ "Attach a client workspace to a partner-agency so chrome substitution applies on that workspace's operator-facing surfaces. " +
5529
+ "STEP 4 of agency onboarding — run once per client workspace the agency manages. Caller must own both the workspace AND the agency. " +
5530
+ "Effect is immediate: next page load in that workspace renders the agency's brand_name, logo, colors, support URLs " +
5531
+ "(replacing SeldonFrame's defaults). The workspace's own data (contacts, bookings, agents, etc.) is unchanged. " +
5532
+ "Reversible via detach_workspace_from_partner_agency. " +
5533
+ 'Example: attach_workspace_to_partner_agency({ workspace_id: "phoenix-hvac-uuid", agency_id: "acme-digital-uuid" })',
5534
+ inputSchema: obj(
5535
+ {
5536
+ workspace_id: str(
5537
+ "Client workspace id to attach. Caller's bearer workspace doesn't have to match — caller just has to own this workspace.",
5538
+ ),
5539
+ agency_id: str("Agency id the workspace will inherit chrome from."),
5540
+ },
5541
+ ["workspace_id", "agency_id"],
5542
+ ),
5543
+ handler: async (a) => {
5544
+ // The attach op's auth derives the agency ownership from the
5545
+ // caller's bearer (orgRow.ownerId). The bearer here is whichever
5546
+ // workspace the agency operator is currently authenticated as
5547
+ // (typically their own primary workspace, not the client's).
5548
+ const ws = wsOrDefault(a.workspace_id);
5549
+ return api("POST", "/partner-agencies", {
5550
+ body: {
5551
+ op: "attach",
5552
+ workspace_id: a.workspace_id,
5553
+ agency_id: a.agency_id,
5554
+ },
5555
+ workspace_id: ws,
5556
+ });
5557
+ },
5558
+ },
5559
+ {
5560
+ name: "detach_workspace_from_partner_agency",
5561
+ description:
5562
+ "Detach a client workspace from its parent partner-agency. The workspace's chrome falls back to SeldonFrame defaults on next page load. " +
5563
+ "Use when an agency loses a client or when the workspace transitions out of agency management. " +
5564
+ "Caller must own the workspace OR own the agency the workspace is attached to (either authorizes detach). " +
5565
+ "Workspace data is unchanged; only parent_agency_id is set to null. Reversible via attach_workspace_to_partner_agency. " +
5566
+ 'Example: detach_workspace_from_partner_agency({ workspace_id: "phoenix-hvac-uuid" })',
5567
+ inputSchema: obj(
5568
+ {
5569
+ workspace_id: str("Client workspace id to detach from its agency."),
5570
+ },
5571
+ ["workspace_id"],
5572
+ ),
5573
+ handler: async (a) => {
5574
+ const ws = wsOrDefault(a.workspace_id);
5575
+ return api("POST", "/partner-agencies", {
5576
+ body: {
5577
+ op: "detach",
5578
+ workspace_id: a.workspace_id,
5579
+ },
5580
+ workspace_id: ws,
5581
+ });
5582
+ },
5583
+ },
5374
5584
  ];
5375
5585
 
5376
5586
  export const TOOL_MAP = Object.fromEntries(TOOLS.map((t) => [t.name, t]));