@seldonframe/mcp 1.20.0 → 1.20.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 (1) hide show
  1. package/package.json +2 -1
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@seldonframe/mcp",
3
- "version": "1.20.0",
3
+ "version": "1.20.1",
4
+ "description": "MCP server for SeldonFrame — AI-native Business OS platform. v1.20.1: HOTFIX — operator-initiated customer-portal invites now send a clickable MAGIC LINK instead of a 6-digit code. Pre-1.20.1 (and pre-1.20 entirely) the operator clicked 'Send invite email' on /contacts/<id>, the contact received an email containing JUST 'your code is 712420' with NO clickable URL. Customers had to know to navigate to /customer/<orgSlug>/login (or /portal/<slug>/login pre-rename) themselves — most don't know the URL. Confirmed in v1.20 production smoke 2026-05-06: customer received the access-code email but couldn't find the portal page; couldn't sign in. v1.20.1: sendPortalInviteAction (lib/portal/admin-actions.ts) now mints a /customer/<slug>/magic?token=... URL via the existing createPortalMagicLink primitive (token bound to a specific contact_id, 30-min TTL) and emails it via a new lib/emails/portal-invite-magic-link.ts template. Customer clicks → /customer/<slug>/magic verifies token + sets session cookie → lands signed-in at /customer/<slug>. Zero typing required. The 6-digit code self-service flow is UNCHANGED for customers who navigate directly to /customer/<slug>/login without an invite email — they type their email, get a code, type the code. That path covers customers who lost or deleted the invite email. Same v1.19 silent-no-op observability pattern: every short-circuit (org_not_found / plan_gate_denied / contact_not_found / portal_access_disabled / resend_not_configured / email_send_failed) emits a structured warn so production monitoring can attribute drop-offs. Same partner-agency branding override as the access-code email (footer + sender substitute when active partner agency is verified). NO migrations, NO env vars, NO new MCP tools — backend hotfix only. Backend redeploy required. The MCP package itself is unchanged in v1.20.1; version bump coupled with backend so they redeploy together. v1.21 still queued for /contacts /deals /bookings table mirrors inside operator portal + agency support sessions + customer-portal industry-aware copy.",
4
5
  "description": "MCP server for SeldonFrame — AI-native Business OS platform. v1.20.0: WHITE-LABEL CRM PHASE 3 — split the 'portal' concept into two distinct surfaces with the right URLs, auth ergonomics, and design language for each audience. WHY THIS MATTERS: pre-1.20 we had ONE surface called /portal/<orgSlug> doing one job (homeowner-facing customer portal, 6-digit code auth, dark cinematic theme). The white-label use case wants a DIFFERENT surface: a branded admin dashboard for sub-tenant operators (the HVAC owner / dentist / accountant who runs a workspace that an SF agency partner has white-labeled to them). One URL serving two audiences with conflicting auth ergonomics + UX languages was structurally wrong. v1.20 splits them. WHAT SHIPS: (1) RENAME /portal/<orgSlug>/* → /customer/<orgSlug>/* — the homeowner customer portal. Behavior unchanged (6-digit code auth, customer-facing copy, magic-code email). All 32 referencing files updated: lib/portal/auth.ts, lib/page-blocks/portal/structure.ts (customer_portal_url field surfaced via MCP), customer-login.tsx, portal-access-card.tsx, seldon/block-installer.ts, hub, settings/client-portal, api/v1/portal/invite, etc. Email-template comments + log lines also updated for clarity. (2) NEW OPERATOR PORTAL at /portal/<orgSlug>/* — the sub-tenant operator's branded admin dashboard. Twenty-CRM-inspired light-mode aesthetic (neutral grays, Inter-style sans, 1px hairline borders, dense rows, sidebar nav). Magic-link auth (single-use, 15-min TTL, JWT-only — no DB row, v1.21 adds nonce enforcement) instead of 6-digit code; operator-grade clickable URL, not consumer-grade type-the-code. Agency-branded chrome via deriveEffectiveBranding (Acme AI logo + 'on Acme AI' subtitle in header when active partner-agency exists; 'Powered by SeldonFrame' footer hidden when agency.hide_powered_by_badge=true). Sidebar nav exposes Dashboard (LIVE in v1.20) + Contacts/Deals/Bookings (placeholders for v1.21 mirrors). Dashboard surface: 4 stat cards (contacts, active deals, upcoming bookings, recent activity) + recent-activity feed, all scoped to session.orgId so URL slug tampering can't read another workspace's data. (3) OPERATOR INVITE UI on /settings/team. New OperatorInviteCard component lets the workspace owner mint a magic-link sign-in for any email — recipient becomes an operator of this workspace via the branded /portal/<orgSlug>. Email template (lib/emails/operator-magic-link.ts) carries optional 'invitedByName' for trust + context, agency-branded footer when applicable, distinct subject line ('Sign in to Cypress & Pine HVAC') from the customer-portal email. (4) AGENCY-IMPERSONATION HOOK: OperatorTokenPayload carries optional supportOriginUserId. When a session token has it set, the operator portal layout renders a yellow banner ('Agency support session active — you are signed in as <email>. All actions are audit-logged'). v1.21 will wire up the agency-side trigger (Acme AI clicks 'Open Cypress portal' from their agency dashboard → mints support-session token) + audit-log table. (5) SECURITY MODEL: Magic-link token is JWT-style HMAC-signed (kind='magic', 15-min TTL). The /portal/<slug>/magic route consumes it atomically, swaps it for a session cookie (kind='session', 7-day TTL, httpOnly, sameSite=lax, secure-in-prod). The session cookie is a DIFFERENT token shape so the magic-link can't be used as a session cookie even if leaked — the kind discriminator gates this. 10 contract tests cover the token primitives: round-trips, expiry, signature tampering, malformed input, kind discriminator, supportOriginUserId carry-through. (6) /contacts/<id>'s 'Send invite email' UNCHANGED in v1.20 (just URL-update from /portal → /customer). Operator invites originate from /settings/team (this v1.20 add); customer invites still originate from /contacts/<id> with the existing 6-digit code flow. Two clear surfaces, two clear flows, no ambiguity. v1.21 candidates: full /contacts /deals /bookings table mirrors inside /portal/<slug>; agency-side 'Open <workspace> portal' support-session UI + agency_support_sessions audit table; customer portal industry-aware copy (HVAC says 'service visits'; dental says 'appointments'); UX tightening on /customer/<slug> overview page. v1.22: agency self-serve mgmt + custom domains. NO MIGRATIONS. NO new env vars. Backend redeploy required. The MCP package itself is unchanged in v1.20; version bump coupled with backend so they redeploy together.",
5
6
  "description": "MCP server for SeldonFrame — AI-native Business OS platform. v1.19.0: STABILITY BUNDLE — five focused root-cause fixes after the v1.18 white-label CRM smoke. (1) PORTAL ACCESS-CODE CASE-INSENSITIVE LOOKUP + SILENT-NO-OP OBSERVABILITY. requestPortalAccessCodeAction + verifyPortalAccessCodeAction now normalize email at action entry (trim + lowercase) AND use lower(stored_email) at the SQL compare, so a customer typing dresslikeAG@Gmail.com still matches a stored dresslikeag@gmail.com. Pre-1.19 the silent-success branches dropped requests with zero log signal — now each branch (org_not_found / plan_gate_denied / contact_not_found / portal_access_disabled) emits a structured warn so production monitoring can attribute every silent-no-op to a specific cause. (2) POLYMORPHIC AGENCY OWNERSHIP. Migration 0041 adds owner_workspace_id to partner_agencies + at-least-one-owner CHECK + lookup index. registerPartnerAgency / attachWorkspaceToAgency / detachWorkspaceFromAgency / registerAgencySenderDomain / verifyAgencySenderDomain now accept EITHER ownerUserId OR ownerWorkspaceId; the API route /api/v1/partner-agencies resolves both identities from the bearer's workspace and falls back to ownerWorkspaceId = guard.orgId when organizations.owner_id is null. Anonymous workspaces (create_workspace_v2 + the v1.7.3 NextAuth-bug claimed-but-unlinked path) can now register agencies natively without a manual users-row INSERT unblock. 12 new contract tests covering the predicate (user-only, workspace-only, dual-pointer, empty caller, null-vs-undefined ambiguity, anonymous-workspace-as-actor). (3) DUPLICATE-CONTACT UNIQUENESS. Migration 0042 adds a partial unique index on contacts (org_id, lower(email)) WHERE email IS NOT NULL — combined with v1.19's case-insensitive lookup, this structurally prevents the dup-row class that produced the v1.18 'no email arrived' bug (two contact rows with the same lowercased email, lookup .limit(1) returned the wrong one with portal_access_enabled=false). Defensive backfill repoints FKs from loser → winner across all 14 contact-referencing tables (activities/bookings/conversations/deals/emails/intake_forms/invoices/payments/portal_access_codes/portal_documents/portal_messages/portal_resources/sms_messages/subscriptions/workspace_records) before deleting losers; idempotent — safe to re-run. Prod confirmed 0 dups before queuing the migration. (4) NEXTAUTH USERS-ROW ROOT-CAUSE FIX — SELF-HEALING JWT RECOVERY. The session strategy is JWT, so token.sub can drift from the actual users.id row in three ways (createUser failed mid-flow, JWT minted from a deleted-then-recreated user, cross-deployment carry-over). Pre-1.19 the v1.7.3 synthesizer prevented the uncaught-throw crash but left the user permanently in a 'signed in but locked in synthesized empty record' dead-state. v1.19 the JWT callback now: on byId miss, falls back to lookup by lower(email); on email match, re-anchors token.sub to that user's id silently; on neither match, logs a structured orphan_token_no_email_match warn for production monitoring to count. 8 new tests covering normalization shape + the 3-state classifier (resolved / self_healed / orphan). (5) PORTAL EXPERIENCE POLISH. The /portal/<orgSlug> client layout + overview page were using legacy crm-card / crm-button-secondary / crm-button-ghost utility classes that didn't pick up workspace branding (every workspace's portal looked SeldonFrame-blue regardless of theme). v1.19 wraps the inner client area in PortalLayout (PublicThemeProvider + branded header) and re-themes nav links, welcome card, stat cards, and message rows with --sf-* CSS variables (--sf-card-bg / --sf-text / --sf-muted / --sf-border / --sf-radius / --sf-primary / --sf-bg). Auth + data plumbing UNCHANGED — same requirePortalSessionForOrg / listPortalMessages / clearPortalSessionAction calls; only the chrome is re-themed. Portal now visually descends from the branded login experience end-to-end. NO new MCP tools; this is a backend stability ship. The MCP package itself is unchanged; version bump is coupled with the backend so they redeploy together. ⚠️ BACKEND REDEPLOY REQUIRED. Migrations 0041 + 0042 already applied to prod via Neon MCP. v1.20 — agency self-serve management tools + nightly plan-gate sweep (deferred, fresh session). v1.21 — agency custom domain + per-client wildcard subdomains via Vercel (deferred, fresh session).",
6
7
  "description": "MCP server for SeldonFrame — AI-native Business OS platform. v1.18.1: HOTFIX — register_partner_agency_sender_domain is now idempotent. Smoke surfaced 2026-05-06: agency tried to register a domain that was already registered in our Resend account (from a prior smoke), got Resend 403 'The <name> domain has been registered already'. Pre-1.18.1 the operator dead-ended with no recovery. v1.18.1: when create returns 403/already-registered, we list all domains in our Resend account, find the existing record by name, fetch its DNS records, and use that domain_id + records as if create had succeeded. If the existing record happens to be already verified by Resend (DNS resolved at some prior point), we mirror that into our agency row's verified_sender_at and skip the DNS-add prompt entirely. Edge case: if Resend says the domain is already registered but it's NOT in our account list, we surface a structured error pointing the operator at a different domain or support — that's the 'registered under a different Resend account' case which is unrecoverable without contacting Resend. NO migrations, NO env vars, NO new deps. The MCP package itself is unchanged in v1.18.1 — version bump coupled with backend so they redeploy together.",