@seldonframe/mcp 1.40.9 → 1.40.10

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/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@seldonframe/mcp",
3
- "version": "1.40.9",
3
+ "version": "1.40.10",
4
+ "description": "MCP server for SeldonFrame — AI-native Business OS platform. v1.40.10: ZERO-FRICTION CHATBOT CREATION (platform-key fallback). The 'extract testimonials and build chatbot' test surfaced friction: build_website_chatbot blocked workspace chatbot creation when no Anthropic key was found in process.env.ANTHROPIC_API_KEY, even though the SeldonFrame backend's lib/ai/client.ts -> getAIClient already falls back to a platform Anthropic key for any workspace without BYOK. The chatbot WORKS without operator BYOK; we were just refusing to create it. v1.40.10 fixes the false barrier. NEW BEHAVIOR: build_website_chatbot now has three LLM-key paths in priority order — (1) explicit anthropic_api_key arg → set as workspace BYOK, (2) process.env.ANTHROPIC_API_KEY in MCP server env → set as workspace BYOK, (3) NEITHER → skip set_llm_key entirely; workspace uses SeldonFrame's platform Anthropic key automatically. Pre-1.40.10 path 3 returned `error: 'no_anthropic_key'` and refused to create the agent; v1.40.10 path 3 succeeds, creates the agent, returns `llm_mode: 'platform'` so Claude Code can naturally tell the operator their chatbot is using SF's shared key and they can BYOK later in /settings/integrations/llm. Removes the 'where do I get an Anthropic key' question from the critical path of a first-time chatbot setup. The runtime behavior was already correct (getAIClient falls back to platform key when no BYOK is configured); v1.40.10 just stops blocking creation. UX SHIFT: operator says 'create a chatbot' → Claude Code calls build_website_chatbot → chatbot is live in ~30s, no key prompt → operator sandbox-tests, publishes to live, chatbot serves visitors using SF platform inference. Operator who eventually wants their own billing pastes their key in /settings/integrations/llm — chatbot picks it up on next turn, no agent recreation. Welcome banner updated with the three-path priority order + the operator-facing messaging Claude Code should use. NO new env vars (uses existing SF backend ANTHROPIC_API_KEY). NO migrations. NO new MCP tools. Backend redeploy required for the build_website_chatbot logic change to take effect via MCP.",
4
5
  "description": "MCP server for SeldonFrame — AI-native Business OS platform. v1.40.9: BOOKING-FLOW HARDENING. Three coordinated fixes addressing the bugs surfaced on the v1.40.8 Sunset Plumbing chatbot test (where the agent confidently said 'Tuesday, May 13' for a date that was actually Wednesday, then booked at 9 AM but the calendar displayed 12 PM). (1) STRENGTHENED TEMPORAL-REASONING SKILL. The website-chatbot's temporal-reasoning skill (lib/agents/skills/website-chatbot/temporal-reasoning.ts) had a single paragraph telling the agent to resolve relative dates using {{currentDate}}. Two failure modes slipped through: the LLM computed weekday names by arithmetic and got them wrong, AND the LLM computed slot times in its head rather than using the look_up_availability tool. v1.40.9 expands the skill into four sections: (a) explicit interpretation rules for 'today/tomorrow/this <weekday>/next <weekday>/next week' with disambiguation guidance; (b) MUST state both weekday name AND date when communicating ('Wednesday, May 13' not 'May 13' or 'Wednesday' alone) so any LLM arithmetic error surfaces visibly to the visitor; (c) NEVER quote a specific time from memory — always call look_up_availability first; (d) restated that look_up_availability returns ground truth slots tied to the workspace's actual calendar in the workspace timezone. (2) TIGHTENED book_appointment TOOL DESCRIPTION. Pre-1.40.9 the tool's slotIso parameter said 'ISO local (e.g. 2026-05-14T14:30)' which the LLM interpreted as license to invent naive local timestamps. Server then parsed naive timestamps as UTC, leading to silent timezone drift on every booking from a workspace whose timezone wasn't UTC. v1.40.9 reframes the parameter as 'MUST be one of the slot strings returned by look_up_availability, copied VERBATIM' with explicit warning: 'Do NOT pass naive local times like 2026-05-13T09:00 — those get misinterpreted as UTC and book the wrong time.' Tool description also explicitly states the call order (look_up_availability FIRST, book_appointment SECOND with verbatim slot). (3) DASHBOARD BOOKINGS RENDER IN WORKSPACE TZ. Pre-1.40.9 the operator's /bookings calendar formatted times via toLocaleTimeString without a timeZone option — defaulting to the VIEWER'S browser timezone. A 9 AM PDT booking displayed as 12 PM EDT for an East Coast operator-viewer (the exact bug Sunset Plumbing exposed). v1.40.9 threads workspaceTimezone from organizations.timezone all the way down to BookingsPageContent and applies { timeZone: workspaceTimezone } on every formatter (toLocaleTimeString, formatDateGroupLabel, keyYmd, labelRangeStart/End, dayHeaderLabel). The keyYmd helper now uses Intl.DateTimeFormat with en-CA locale (which emits ISO YYYY-MM-DD) so day-grouping is also workspace-anchored — bookings near midnight local don't drift to the wrong day for viewers in different timezones. NO new env vars (uses existing organizations.timezone). NO migrations (timezone column already exists from earlier ships). NO new MCP tools. Backend redeploy required for the system-prompt + dashboard fixes to take effect. EXPECTED IMPACT: a chatbot conversation booking 'next Tuesday at 9 AM' from an LA workspace now (a) computes the correct date with weekday verification, (b) calls look_up_availability + uses a verbatim slot string, (c) the booking lands at 9 AM LA time AND displays as 9 AM in the operator's dashboard (regardless of where the operator is viewing from). End-to-end booking flow consistent across timezones. Caps the v1.40.8 ship.",
5
6
  "description": "MCP server for SeldonFrame — AI-native Business OS platform. v1.40.8: HOTFIX — chatbot widget bugs surfaced on the v1.40.7 first-test. Two bugs blocked the just-shipped chatbot embed from actually working end-to-end on Sunset Plumbing. (1) INPUT TEXT INVISIBLE (white-on-white). The .sf-agent-input CSS in embed.js had no explicit color rule, so typed characters inherited the host page's color (var(--sf-text)). On workspaces with stored dark themes OR pages that set color globally, characters rendered white-on-white-background — invisible while typing. v1.40.8 hardcodes color:#111 + background:#fff + -webkit-text-fill-color:#111 on the input element, plus a placeholder color rule. The chat widget is now isolated from host-page CSS — typed characters always readable regardless of host theme. (2) CORS BLOCKING /turn FETCH. The chatbot widget loads as <script src='https://app.seldonframe.com/...'> on workspace subdomain pages (e.g. sunset-plumbing-co.app.seldonframe.com). Script-tag loading isn't CORS-restricted, but the widget's POST fetch from inside the script to https://app.seldonframe.com/api/v1/public/agent/.../turn IS cross-origin. Pre-1.40.8 the turn endpoint returned ZERO CORS headers — browser blocked the preflight, fetch threw, widget surfaced 'Connection issue. Please try again.' on every send. v1.40.8 adds Access-Control-Allow-Origin: * + Methods + Headers + Max-Age, plus an OPTIONS handler for preflight, on every code path of the turn endpoint (invalid_json, missing_message, message_too_long, agent_not_found, agent_not_active, conversation_create_failed, the SSE stream, and the JSON fallback). Origin=* is correct here because the chatbot is intentionally embeddable on any operator's website (the entire point of the embed); the endpoint serves only public, conversation-scoped data; downstream agent runtime already enforces per-conversation auth via conversation_id + anonymous_session_id. Loosening CORS doesn't loosen application authorization. NO new env vars, NO migrations, NO new MCP tools. Backend redeploy required for both fixes (embed.js is server-rendered per-request — operators don't need to re-embed; bubble auto-pulls new CSS on next page load). EXPECTED IMPACT: visit https://sunset-plumbing-co.app.seldonframe.com → typed characters in the chat input are dark gray and readable; sending a message reaches the agent + streams the SSE response back. End-to-end chatbot UX now actually works on workspace subdomains. Caps the v1.40.7 ship.",
6
7
  "description": "MCP server for SeldonFrame — AI-native Business OS platform. v1.40.7: WORKSPACE-LEVEL CHATBOT EMBED. The natural-language ask 'add the chatbot to my landing page' now works as a single MCP call. Pre-1.40.7 the only way to put the chatbot bubble on a page was to copy-paste the embed snippet into Pages → Edit (the v1.40.6 banner promised an automated path that didn't actually exist — this fixes that). v1.40.7 ships the real implementation. NEW lib/agents/public-embed.ts: getPublicChatbotEmbed(orgId) reads, setPublicChatbotEmbed(orgId, {embedUrl, agentId}) writes to organizations.settings.chatbot. Storage validates https-only embed URLs as defense-in-depth (the writer only ever derives URLs from WORKSPACE_BASE_DOMAIN, but the read-side check ensures a malformed JSONB value can't inject arbitrary script). NEW components/landing/chatbot-script.tsx wraps Next.js <Script strategy='afterInteractive'> so the embed.js loads off-critical-path. Wired into BOTH /s/[orgSlug]/[...slug]/page.tsx (workspace home rewrite target) and /l/[orgSlug]/[slug]/page.tsx (legacy landing route) — every public page of every workspace honors the embed if set. NEW api/v1/agents POST op 'embed_on_landing' validates agent ownership + status (test/live only — refuses draft) + builds embed.js URL from agent.orgSlug + agent.slug + WORKSPACE_BASE_DOMAIN, then calls setPublicChatbotEmbed. Sibling op 'remove_from_landing' clears the embed without deleting the agent (still serves /agents/[id]/test for sandboxing). NEW MCP tool embed_chatbot_on_workspace_landing({ workspace_id, agent_id }) is the operator-facing one-call wrapper. Sibling remove_chatbot_from_landing({ workspace_id }) clears it. build_website_chatbot's next_steps step 4 updated to reference embed_chatbot_on_workspace_landing as a one-call action (was misleading add_composite_section guidance pre-1.40.7). Welcome banner v1.40.7 section walks through the embed flow with the actual tool name. NO new env vars (uses existing WORKSPACE_BASE_DOMAIN). NO migrations (settings is already a flexible JSONB; new chatbot key merges in cleanly with existing keys). Backend redeploy required for the new API op + page-renderer injection. Pairs with v1.40.6's Custom Agent → Custom Workflow rename for a coherent post-creation operator UX: agents and workflows are now visually distinct, AND the chatbot is one natural-language ask away from being live on the landing page. EXPECTED IMPACT: operator says 'add the chatbot to the Sunset Plumbing landing' → Claude Code calls embed_chatbot_on_workspace_landing → bubble appears bottom-right on https://sunset-plumbing-co.app.seldonframe.com immediately on next page load. Closes the gap between agent creation and agent-on-page that v1.40.6 left open.",
package/src/tools.js CHANGED
@@ -4855,48 +4855,57 @@ export const TOOLS = [
4855
4855
  const ws = args.workspace_id;
4856
4856
  const steps = [];
4857
4857
 
4858
- // 1. Configure LLM (auto-detect from env if no explicit key)
4859
- // We try this BEFORE create_agent so failures surface clearly.
4860
- // If a key is already configured for this workspace, the set_llm_key
4861
- // op is idempotent (overwrites); harmless to call. If neither
4862
- // explicit nor env key is available, fail fast with a clear error
4863
- // so the user can paste a key.
4858
+ // v1.40.10 graceful no-key path.
4859
+ //
4860
+ // Pre-1.40.10 this tool blocked workspace chatbot creation when no
4861
+ // Anthropic key was present in process.env. That assumed the
4862
+ // operator had to BYOK upfront but the SF backend already falls
4863
+ // back to its platform ANTHROPIC_API_KEY (in lib/ai/client.ts ->
4864
+ // getAIClient) for any workspace without BYOK. The chatbot WORKS
4865
+ // without operator BYOK; pre-1.40.10 we just refused to create it.
4866
+ //
4867
+ // v1.40.10: if explicit key OR env key, set_llm_key as before
4868
+ // (configures BYOK on the workspace, operator pays Anthropic
4869
+ // directly). If NEITHER, skip set_llm_key entirely — the workspace
4870
+ // will use SF platform key automatically. In the response we tell
4871
+ // the operator they can BYOK later via /settings/integrations/llm.
4872
+ // Removes the friction "where do I get an Anthropic key" step
4873
+ // that v1.40.9 Sunset Plumbing test exposed.
4864
4874
  const explicitKey = args.anthropic_api_key;
4865
4875
  const envKey = process.env.ANTHROPIC_API_KEY;
4866
4876
  const keyToUse = explicitKey || envKey;
4867
- if (!keyToUse) {
4868
- return {
4869
- ok: false,
4870
- error: "no_anthropic_key",
4871
- hint:
4872
- "No Anthropic key available. Either: (a) set ANTHROPIC_API_KEY in your shell before launching Claude Code (most Claude Code users already have this), " +
4873
- "or (b) pass anthropic_api_key='sk-ant-...' explicitly to this tool, " +
4874
- "or (c) configure via the dashboard at /settings/integrations/llm before calling create_agent. " +
4875
- "Without a key, the agent will be created in draft but every customer turn will return 'I'm not set up yet'.",
4876
- steps,
4877
- };
4878
- }
4879
- const configResult = await api("POST", "/agents", {
4880
- body: {
4881
- op: "set_llm_key",
4882
- provider: "anthropic",
4883
- api_key: keyToUse,
4884
- },
4885
- workspace_id: ws,
4886
- });
4887
- if (!configResult || configResult.ok === false) {
4888
- return {
4889
- ok: false,
4890
- error: "llm_config_failed",
4891
- detail: configResult,
4892
- steps,
4893
- };
4877
+ let llmMode = "platform";
4878
+ if (keyToUse) {
4879
+ const configResult = await api("POST", "/agents", {
4880
+ body: {
4881
+ op: "set_llm_key",
4882
+ provider: "anthropic",
4883
+ api_key: keyToUse,
4884
+ },
4885
+ workspace_id: ws,
4886
+ });
4887
+ if (!configResult || configResult.ok === false) {
4888
+ return {
4889
+ ok: false,
4890
+ error: "llm_config_failed",
4891
+ detail: configResult,
4892
+ steps,
4893
+ };
4894
+ }
4895
+ llmMode = explicitKey ? "byok_explicit" : "byok_env";
4896
+ steps.push({
4897
+ step: "configure_llm_provider",
4898
+ ok: true,
4899
+ source: explicitKey ? "explicit" : "env_inherited",
4900
+ });
4901
+ } else {
4902
+ steps.push({
4903
+ step: "configure_llm_provider",
4904
+ ok: true,
4905
+ source: "platform_fallback",
4906
+ note: "No BYOK key supplied — agent will use SeldonFrame platform Anthropic key. Operator can switch to BYOK later in /settings/integrations/llm.",
4907
+ });
4894
4908
  }
4895
- steps.push({
4896
- step: "configure_llm_provider",
4897
- ok: true,
4898
- source: explicitKey ? "explicit" : "env_inherited",
4899
- });
4900
4909
 
4901
4910
  // 2. Create the agent
4902
4911
  const createResult = await api("POST", "/agents", {
@@ -4968,6 +4977,15 @@ export const TOOLS = [
4968
4977
  process.env.WORKSPACE_BASE_DOMAIN?.trim() || "app.seldonframe.com";
4969
4978
  const dashboardUrl = `https://${baseDomain}/agents/${agentId}`;
4970
4979
 
4980
+ // v1.40.10 — surface the LLM-key mode in the response so Claude
4981
+ // Code can naturally tell the operator "your chatbot is using
4982
+ // SeldonFrame's shared Anthropic key right now; switch to your
4983
+ // own anytime in Settings."
4984
+ const llmKeyNote =
4985
+ llmMode === "platform"
4986
+ ? `Note on LLM billing: this chatbot is using SeldonFrame's shared Anthropic key (no setup required from you). To switch to your own Anthropic billing (higher rate limits, separate invoice), open ${dashboardUrl.replace("/agents/" + agentId, "/settings/integrations/llm")} and paste your sk-ant-... key. The chatbot picks up the new key automatically — no code changes.`
4987
+ : `LLM billing: this workspace is on BYOK (your Anthropic key). Switch in /settings/integrations/llm anytime.`;
4988
+
4971
4989
  return {
4972
4990
  ok: true,
4973
4991
  agent: createResult.agent,
@@ -4975,16 +4993,14 @@ export const TOOLS = [
4975
4993
  turn_url: createResult.turn_url,
4976
4994
  dashboard_url: dashboardUrl,
4977
4995
  sandbox_url: `${dashboardUrl}/test`,
4996
+ llm_mode: llmMode,
4978
4997
  steps,
4979
4998
  next_steps: [
4980
4999
  `1. Test in sandbox: ${dashboardUrl}/test (chat with the agent before customers do).`,
4981
5000
  `2. Run safety evals: open ${dashboardUrl}/evals → Run evals now (8-scenario suite).`,
4982
5001
  `3. When ready, publish to live: call publish_agent({ agent_id: '${agentId}', status: 'live' }) — auto-runs eval gate, requires ≥87.5% pass.`,
4983
- // v1.40.7 — embed-on-workspace-landing is now a real one-call MCP
4984
- // tool (was misleading guidance in v1.40.6). When the operator asks
4985
- // to put the chatbot on their site, call embed_chatbot_on_workspace_landing
4986
- // and the public landing renderer auto-injects the script tag.
4987
5002
  `4. Add to the operator's SF-hosted landing page (one MCP call): embed_chatbot_on_workspace_landing({ workspace_id, agent_id: '${agentId}' }) — the chatbot bubble appears bottom-right on every public page. (For an external website the operator owns, paste this snippet manually: <script src="${createResult.embed_url}" async></script>)`,
5003
+ `5. ${llmKeyNote}`,
4988
5004
  ],
4989
5005
  };
4990
5006
  },
package/src/welcome.js CHANGED
@@ -8,7 +8,7 @@
8
8
  // stripped. `create_full_workspace` is the only workspace-creation
9
9
  // path mentioned anywhere in this briefing.
10
10
 
11
- export const VERSION = "1.40.9";
11
+ export const VERSION = "1.40.10";
12
12
 
13
13
  export const WELCOME_MARKDOWN = `# SeldonFrame — create a real Business OS in one conversation
14
14
 
@@ -104,12 +104,14 @@ of v1.28.0 this is **ONE tool call**:
104
104
  { label: "Service call", amount: 89, currency: "USD" },
105
105
  { label: "AC tune-up", amount: 149, currency: "USD" }
106
106
  ]
107
- // anthropic_api_key omitted → auto-detected from
108
- // process.env.ANTHROPIC_API_KEY in the MCP server's environment
109
- // (most Claude Code users already have this set).
107
+ // anthropic_api_key omitted → workspace falls back to SeldonFrame
108
+ // platform key automatically. Operator can BYOK later via
109
+ // /settings/integrations/llm. Pass explicitly only when you need
110
+ // per-workspace billing isolation (white-label / agency).
110
111
  })
111
- // → returns { agent, embed_url, turn_url, dashboard_url, next_steps }
112
- // Internally: configures LLM key, creates agent, publishes to test.
112
+ // → returns { agent, embed_url, turn_url, dashboard_url, llm_mode, next_steps }
113
+ // Internally: creates agent, publishes to test. Skips set_llm_key when
114
+ // no key supplied (platform fallback in lib/ai/client.ts handles inference).
113
115
 
114
116
  2. (Operator sandbox-tests at /agents/[id]/test.)
115
117
 
@@ -121,7 +123,29 @@ of v1.28.0 this is **ONE tool call**:
121
123
  If the operator manages multiple workspaces with separate Anthropic
122
124
  billing (e.g. Acme AI agency managing Cypress Pine HVAC + Sunset
123
125
  Dental), pass anthropic_api_key explicitly per workspace instead of
124
- relying on env auto-detect.
126
+ relying on platform fallback.
127
+
128
+ ### v1.40.10 — LLM key handling
129
+
130
+ build_website_chatbot has THREE possible LLM-key paths, in priority
131
+ order:
132
+
133
+ 1. **Explicit** — \`anthropic_api_key: 'sk-ant-...'\` arg → workspace
134
+ stored as BYOK, operator pays Anthropic directly.
135
+ 2. **Env-inherited** — process.env.ANTHROPIC_API_KEY in the MCP
136
+ server's environment → workspace stored as BYOK with that key.
137
+ 3. **Platform fallback** — neither explicit nor env → no BYOK
138
+ stored. The workspace uses SeldonFrame's platform Anthropic key
139
+ automatically (via lib/ai/client.ts -> getAIClient). Operator
140
+ sees no setup friction; they can BYOK later in
141
+ /settings/integrations/llm.
142
+
143
+ When option 3 fires, the response \`llm_mode\` is \`"platform"\`. Tell
144
+ the operator: "Your chatbot is using SeldonFrame's shared Anthropic
145
+ key. You can switch to your own anytime in Settings → Integrations
146
+ → LLM, no code changes needed." This removes the "where do I get an
147
+ Anthropic key" question from the critical path of a first-time
148
+ chatbot setup.
125
149
 
126
150
  For custom flows (different archetype, custom capability allowlist,
127
151
  multi-step blueprint construction), drop down to the primitives:
@@ -461,4 +485,4 @@ admin dashboard. Pre-fills their email automatically.
461
485
  <https://seldonframe.com> · **Discord:** <https://discord.gg/sbVUu976NW>
462
486
  `;
463
487
 
464
- export const FIRST_CALL_BANNER = `🚀 SeldonFrame v1.40.9 is connected. STEP ZERO: for any workspace task call get_workspace_state({workspace_id}) FIRST — returns workspace identity + integrations status + agents with inline health stats + counts + tailored next_steps in ONE round-trip. Replaces 4-6 progressive discovery calls. ANTI-PATTERNS: don't ls/cat/read .env (SF is hosted, not local files); don't check node/npm versions (irrelevant); don't ask 'is Anthropic key configured?' (state response tells you); don't create a duplicate agent (state response shows existing ones — use update_website_chatbot instead of build_website_chatbot when one exists). Token-budget concept removed in v1.27.9 — ignore stale references. CAPABILITY MAP — pick the right primitive BEFORE exploring tools: (a) "build me a website" → WORKSPACE → create_workspace_v2 + block tools. (b) "add a chatbot to my website / landing page" → AGENT (web chat) → build_website_chatbot (v1.28+ skill bundle: configure_llm + create_agent + publish-test in 1 call; auto-detects ANTHROPIC_API_KEY from env). Drop to primitives (configure_llm_provider + create_agent + publish_agent) only for custom flows. CRITICAL ANTI-PATTERN: chat widgets are NOT blocks. Don't search list_blocks for chat — chat agents are a separate primitive (v1.26+). (c) "auto-reply to inbound SMS/email" → CONVERSATION → send_conversation_turn (one-shot Soul-aware reply, not a website widget). (d) "add hero/services/faq/cta section" → BLOCK → get_block_skill + persist_block. (e) "send campaign email/sms" → MESSAGING. (f) CRM ops → list_contacts/create_deal/etc. CHATBOT CANONICAL FLOW (v1.28+ — 1 call instead of 5): build_website_chatbot({workspace_id, name, faq, pricing_facts, greeting}) → wraps configure_llm + create_agent + publish-test in one call, auto-detects ANTHROPIC_API_KEY from env. Then: operator sandbox-tests at /agents/[id]/test → publish_agent({status:"live"}) auto-runs 8-scenario eval gate (≥87.5% pass required). Observability tools after publish: list_agents, tail_agent_conversations, get_agent_conversation, get_agent_metrics, run_agent_evals, replay_conversation. Dashboard surfaces /agents, /agents/[id]/test, /agents/[id]/settings, /agents/[id]/evals, /agents/[id]/conversations let operators iterate without leaving the browser. WORKSPACE FLOW (legacy reference): create_workspace_v2 → IN PARALLEL for all 7 recommended_blocks (hero, services, about, faq, cta, booking, intake): get_block_skill + persist_block → complete_workspace_v2 → finalize_workspace({workspace_id, email}). Run blocks in PARALLEL (Promise.all) — sequential takes 60+ seconds. v1.10+ CUSTOMIZE: regenerate_block, upload_workspace_image (image_url preferred over base64). v1.11+ STRUCTURAL: get_landing_structure, move_section, delete_section. v1.12+ COMPOSITES: add_composite_section / update_composite_section — manifest ANY block from 12 low-level primitives. Skipping finalize_workspace leaves the operator with no admin login. Every URL is real. NEVER create local files. v1.34+ UI POLISH (smart defaults + optional levers): every published page ships with motion baked in (RevealOnScroll on sections, Stagger on grid blocks, HoverLift on CTAs) — no operator action required. For operators who want to tune intensity: apply_motion_preset({preset: "minimal"|"subtle"|"balanced"|"editorial"}). For operators bringing their own design system: apply_design_md({design_md_content}) accepts the Google Labs DESIGN.md format; import_claude_design_handoff({bundle}) accepts Claude Design's handoff bundle. All three tools are OPTIONAL — surface them only when the operator's vibe asks for it ("make it feel premium", "I have a brand kit", "I just exported from Claude Design"). Never push these on a user who just wants a working site. v1.36+ PROMPT-FATIGUE GUIDANCE: workspace creation typically fires 8–18 MCP tool calls (workspace + blocks + agent + booking + intake + landing). Claude Code prompts on each by default and create_landing_page's Puck schema is verbose JSON the operator should NOT read. DO TWO THINGS to avoid spamming: (1) RECOMMEND THE OPERATOR pick option 2 ('Yes, and do not ask again for seldonframe – <tool>') the FIRST time create_landing_page / persist_block / build_website_chatbot fire — Claude Code caches per-tool autoapprove for the project, the rest of workspace creation runs without interrupting them. Mention this proactively before the flow starts. (2) BUNDLE TOOL CALLS in parallel via Promise.all — never call get_block_skill + persist_block sequentially for 7 blocks; fire them all at once. v1.36+ LANDING-PAGE COMPOSITION FOR LOCAL-SERVICE BUSINESSES: for trades/service businesses (plumbing, HVAC, locksmith, electrician, roofing, towing, dental, salon, mobile mechanic), default to a 10-section composition: navbar → hero (branded gradient empty-state when no photo) → emergencyStrip (sticky brand-colored banner with phone) → trust strip via the Grid block → servicesGrid (per-service cards with price + duration + Book CTA — replaces the SaaS-style pricing block) → benefits (3 differentiators) → process (3-step what-happens-after-booking) → serviceArea (chip cloud of cities/neighborhoods) → testimonials (5+ quotes — placeholder OK pre-launch) → faq → cta → footer. The Pricing block belongs to TIER-based businesses (SaaS, coaches, gyms) NOT per-service businesses — substitute servicesGrid. v1.36.0 added the servicesGrid, emergencyStrip, serviceArea block types — use them when persisting landing pages for trades verticals. The hero block ships a branded-gradient empty state when heroImage is missing; if the operator does not provide a photo, suggest a stock-photo prompt ('your tech truck on a job, sunset shot in Phoenix') so they can drop one in via /landing → Edit later. v1.37.0 GOOGLE MAPS PASTE → WORKSPACE: when the operator pastes a Google Maps business listing (Top Plumbing Experts, ABC Plumbing of San Antonio, etc.), use create_workspace_from_google_paste — NOT create_full_workspace. Same backend pipeline + same finalize_workspace follow-up; the paste-tool's docstring documents the extraction rules (name → bold title, phone → phone-icon row, address → location-pin line, services → categories chip row + 'Services' section deduped, rating + count → '4.7 ★ (950)' element, weekly_hours → 'Monday: 9 AM-5 PM, Tuesday: closed' parsed into canonical {monday:{enabled:true,start:'09:00',end:'17:00'},...} shape). The weekly_hours field flows DIRECTLY into the booking template's metadata.availability, so the operator's actual hours appear on the public /book page on first GET — no separate configure_booking call. 'Open 24 hours' → start:'00:00', end:'23:59'. 'Closed' day → enabled:false. Wrong key shape (e.g. 'mon' instead of 'monday') is silently dropped by the backend and falls back to Mon-Fri 9-5 defaults. NO Google Places API key required — the paste IS the source of truth. v1.36.4 paired fix: the booking page's normalizeAvailability now accepts both 3-letter and full-name day keys, so any legacy rows already in the DB also hydrate correctly. v1.38.3 ADDITIONAL QUALITY BLOCKS: (a) projectGallery — 6-photo masonry of stock photos auto-fetched per service via Unsplash from queries Claude generates inside enhance-blocks; closes the "feels populated" gap that's the single biggest visible difference between a generic SF workspace and a real-business landing page. (b) stickyMobileCTA — fixed bottom-of-screen Call/Book bar, MOBILE ONLY (md:hidden), industry-standard for trades sites with 2-3x mobile booking lift; auto-included whenever a phone is set. (c) testimonial synthesis from Maps paste — when the operator pastes a Google Maps listing that contains review excerpts, Claude Code extracts them VERBATIM into a 'testimonials' field on create_workspace_from_google_paste; backend renders them as-is. NEVER fabricated — if the paste has no review text, the testimonials block is OMITTED from the page entirely (better empty than fake). The testimonials field in the MCP tool input is OPTIONAL — Claude must omit it when the paste doesn't contain real review text. v1.38.0 ATOMIC HORMOZI-QUALITY OUTPUT: every workspace produced by create_full_workspace OR create_workspace_from_google_paste now ships with Hormozi value-equation hero copy + per-business Unsplash photos + scroll-triggered motion baked in — atomically, no follow-up tool calls, no operator action. Pre-1.38.0 the atomic create produced canned 'Welcome to X' copy from personality content templates; only workspaces where Claude Code did the BLOCK-AS-SKILL flow afterward (get_block_skill + persist_block per block) got the rich output. Tirionforge HVAC happened to look great because of that follow-up; atlantic-plumbing didn't get it. v1.38.0 closes the gap by adding Step 12.7 inside the orchestrator: ONE Claude Opus 4.7 call generates hero + servicesGrid + about + benefits + process + faq + cta from src/blocks/*/SKILL.md (the exact same fat skill files Claude Code reads via get_block_skill — single source of truth, no duplication). Output writes to landingPages.sections (LandingPageSection[]), contentHtml/Css get NULLED, route /l/[orgSlug]/[slug] falls through to <PageRenderer> which auto-wraps below-fold sections in <RevealOnScroll>. Hormozi copy + per-business Unsplash + motion all light up at once. EMAIL-FIRST GUARDRAIL UNCHANGED: create_full_workspace + create_workspace_from_google_paste both still RETURN BEFORE THE EMAIL DANCE — operator can't see URLs until they reply with email, finalize_workspace mints the magic admin link + sends the welcome email via Resend. BYOK pass-through: getAIClient checks organizations.integrations.anthropic.apiKey first, falls back to platform ANTHROPIC_API_KEY env. Cost ~$0.10 per workspace, irrelevant since the operator typically supplies their own key. SOFT-FAIL: any error in Step 12.7 leaves the workspace valid (canned-copy Path A intact); never blocks creation. EXPECTED IMPACT: every fresh workspace looks $10k-tier first-shot, no "create workspace then enhance blocks" two-step dance. v1.40.3 IMAGE PIPELINE FOOLPROOFING: removed the deprecated source.unsplash.com keyless fallback from BOTH resolveHeroImageUrlForQuery and resolveGalleryImageUrlsForQueries. Pre-1.40.3 when the official Unsplash API errored or returned no results we fell back to https://source.unsplash.com/{w}x{h}/?{query} — that endpoint is deprecated and now frequently returns broken responses, which were stored verbatim in landingPages.sections and rendered as broken-image icons before the v1.40.2 onError handler eventually flipped state. The HERO Aesthetic Co. test exposed this: hero showed a broken-image icon despite the gallery rendering 4 medspa photos correctly (proving onError wasn't reliably catching deprecated-CDN failures). Fix: never store a known-broken URL. Hero query miss → empty string → branded-gradient empty-state from frame zero (no broken icon ever). Gallery query miss → SKIP that slot → grid auto-reflows to a clean 4-tile composition instead of 6 tiles with 2 broken. Trade-off: workspaces without UNSPLASH_ACCESS_KEY now ship gradient hero + empty gallery (better than broken images). Operators get full control by setting the env var or replacing images post-launch via update_landing_section. ws1-webhook-pricing-fixes branch ships this; redeploy required.`;
488
+ export const FIRST_CALL_BANNER = `🚀 SeldonFrame v1.40.10 is connected. STEP ZERO: for any workspace task call get_workspace_state({workspace_id}) FIRST — returns workspace identity + integrations status + agents with inline health stats + counts + tailored next_steps in ONE round-trip. Replaces 4-6 progressive discovery calls. ANTI-PATTERNS: don't ls/cat/read .env (SF is hosted, not local files); don't check node/npm versions (irrelevant); don't ask 'is Anthropic key configured?' (state response tells you); don't create a duplicate agent (state response shows existing ones — use update_website_chatbot instead of build_website_chatbot when one exists). Token-budget concept removed in v1.27.9 — ignore stale references. CAPABILITY MAP — pick the right primitive BEFORE exploring tools: (a) "build me a website" → WORKSPACE → create_workspace_v2 + block tools. (b) "add a chatbot to my website / landing page" → AGENT (web chat) → build_website_chatbot (v1.28+ skill bundle: configure_llm + create_agent + publish-test in 1 call; auto-detects ANTHROPIC_API_KEY from env). Drop to primitives (configure_llm_provider + create_agent + publish_agent) only for custom flows. CRITICAL ANTI-PATTERN: chat widgets are NOT blocks. Don't search list_blocks for chat — chat agents are a separate primitive (v1.26+). (c) "auto-reply to inbound SMS/email" → CONVERSATION → send_conversation_turn (one-shot Soul-aware reply, not a website widget). (d) "add hero/services/faq/cta section" → BLOCK → get_block_skill + persist_block. (e) "send campaign email/sms" → MESSAGING. (f) CRM ops → list_contacts/create_deal/etc. CHATBOT CANONICAL FLOW (v1.28+ — 1 call instead of 5): build_website_chatbot({workspace_id, name, faq, pricing_facts, greeting}) → wraps configure_llm + create_agent + publish-test in one call, auto-detects ANTHROPIC_API_KEY from env. Then: operator sandbox-tests at /agents/[id]/test → publish_agent({status:"live"}) auto-runs 8-scenario eval gate (≥87.5% pass required). Observability tools after publish: list_agents, tail_agent_conversations, get_agent_conversation, get_agent_metrics, run_agent_evals, replay_conversation. Dashboard surfaces /agents, /agents/[id]/test, /agents/[id]/settings, /agents/[id]/evals, /agents/[id]/conversations let operators iterate without leaving the browser. WORKSPACE FLOW (legacy reference): create_workspace_v2 → IN PARALLEL for all 7 recommended_blocks (hero, services, about, faq, cta, booking, intake): get_block_skill + persist_block → complete_workspace_v2 → finalize_workspace({workspace_id, email}). Run blocks in PARALLEL (Promise.all) — sequential takes 60+ seconds. v1.10+ CUSTOMIZE: regenerate_block, upload_workspace_image (image_url preferred over base64). v1.11+ STRUCTURAL: get_landing_structure, move_section, delete_section. v1.12+ COMPOSITES: add_composite_section / update_composite_section — manifest ANY block from 12 low-level primitives. Skipping finalize_workspace leaves the operator with no admin login. Every URL is real. NEVER create local files. v1.34+ UI POLISH (smart defaults + optional levers): every published page ships with motion baked in (RevealOnScroll on sections, Stagger on grid blocks, HoverLift on CTAs) — no operator action required. For operators who want to tune intensity: apply_motion_preset({preset: "minimal"|"subtle"|"balanced"|"editorial"}). For operators bringing their own design system: apply_design_md({design_md_content}) accepts the Google Labs DESIGN.md format; import_claude_design_handoff({bundle}) accepts Claude Design's handoff bundle. All three tools are OPTIONAL — surface them only when the operator's vibe asks for it ("make it feel premium", "I have a brand kit", "I just exported from Claude Design"). Never push these on a user who just wants a working site. v1.36+ PROMPT-FATIGUE GUIDANCE: workspace creation typically fires 8–18 MCP tool calls (workspace + blocks + agent + booking + intake + landing). Claude Code prompts on each by default and create_landing_page's Puck schema is verbose JSON the operator should NOT read. DO TWO THINGS to avoid spamming: (1) RECOMMEND THE OPERATOR pick option 2 ('Yes, and do not ask again for seldonframe – <tool>') the FIRST time create_landing_page / persist_block / build_website_chatbot fire — Claude Code caches per-tool autoapprove for the project, the rest of workspace creation runs without interrupting them. Mention this proactively before the flow starts. (2) BUNDLE TOOL CALLS in parallel via Promise.all — never call get_block_skill + persist_block sequentially for 7 blocks; fire them all at once. v1.36+ LANDING-PAGE COMPOSITION FOR LOCAL-SERVICE BUSINESSES: for trades/service businesses (plumbing, HVAC, locksmith, electrician, roofing, towing, dental, salon, mobile mechanic), default to a 10-section composition: navbar → hero (branded gradient empty-state when no photo) → emergencyStrip (sticky brand-colored banner with phone) → trust strip via the Grid block → servicesGrid (per-service cards with price + duration + Book CTA — replaces the SaaS-style pricing block) → benefits (3 differentiators) → process (3-step what-happens-after-booking) → serviceArea (chip cloud of cities/neighborhoods) → testimonials (5+ quotes — placeholder OK pre-launch) → faq → cta → footer. The Pricing block belongs to TIER-based businesses (SaaS, coaches, gyms) NOT per-service businesses — substitute servicesGrid. v1.36.0 added the servicesGrid, emergencyStrip, serviceArea block types — use them when persisting landing pages for trades verticals. The hero block ships a branded-gradient empty state when heroImage is missing; if the operator does not provide a photo, suggest a stock-photo prompt ('your tech truck on a job, sunset shot in Phoenix') so they can drop one in via /landing → Edit later. v1.37.0 GOOGLE MAPS PASTE → WORKSPACE: when the operator pastes a Google Maps business listing (Top Plumbing Experts, ABC Plumbing of San Antonio, etc.), use create_workspace_from_google_paste — NOT create_full_workspace. Same backend pipeline + same finalize_workspace follow-up; the paste-tool's docstring documents the extraction rules (name → bold title, phone → phone-icon row, address → location-pin line, services → categories chip row + 'Services' section deduped, rating + count → '4.7 ★ (950)' element, weekly_hours → 'Monday: 9 AM-5 PM, Tuesday: closed' parsed into canonical {monday:{enabled:true,start:'09:00',end:'17:00'},...} shape). The weekly_hours field flows DIRECTLY into the booking template's metadata.availability, so the operator's actual hours appear on the public /book page on first GET — no separate configure_booking call. 'Open 24 hours' → start:'00:00', end:'23:59'. 'Closed' day → enabled:false. Wrong key shape (e.g. 'mon' instead of 'monday') is silently dropped by the backend and falls back to Mon-Fri 9-5 defaults. NO Google Places API key required — the paste IS the source of truth. v1.36.4 paired fix: the booking page's normalizeAvailability now accepts both 3-letter and full-name day keys, so any legacy rows already in the DB also hydrate correctly. v1.38.3 ADDITIONAL QUALITY BLOCKS: (a) projectGallery — 6-photo masonry of stock photos auto-fetched per service via Unsplash from queries Claude generates inside enhance-blocks; closes the "feels populated" gap that's the single biggest visible difference between a generic SF workspace and a real-business landing page. (b) stickyMobileCTA — fixed bottom-of-screen Call/Book bar, MOBILE ONLY (md:hidden), industry-standard for trades sites with 2-3x mobile booking lift; auto-included whenever a phone is set. (c) testimonial synthesis from Maps paste — when the operator pastes a Google Maps listing that contains review excerpts, Claude Code extracts them VERBATIM into a 'testimonials' field on create_workspace_from_google_paste; backend renders them as-is. NEVER fabricated — if the paste has no review text, the testimonials block is OMITTED from the page entirely (better empty than fake). The testimonials field in the MCP tool input is OPTIONAL — Claude must omit it when the paste doesn't contain real review text. v1.38.0 ATOMIC HORMOZI-QUALITY OUTPUT: every workspace produced by create_full_workspace OR create_workspace_from_google_paste now ships with Hormozi value-equation hero copy + per-business Unsplash photos + scroll-triggered motion baked in — atomically, no follow-up tool calls, no operator action. Pre-1.38.0 the atomic create produced canned 'Welcome to X' copy from personality content templates; only workspaces where Claude Code did the BLOCK-AS-SKILL flow afterward (get_block_skill + persist_block per block) got the rich output. Tirionforge HVAC happened to look great because of that follow-up; atlantic-plumbing didn't get it. v1.38.0 closes the gap by adding Step 12.7 inside the orchestrator: ONE Claude Opus 4.7 call generates hero + servicesGrid + about + benefits + process + faq + cta from src/blocks/*/SKILL.md (the exact same fat skill files Claude Code reads via get_block_skill — single source of truth, no duplication). Output writes to landingPages.sections (LandingPageSection[]), contentHtml/Css get NULLED, route /l/[orgSlug]/[slug] falls through to <PageRenderer> which auto-wraps below-fold sections in <RevealOnScroll>. Hormozi copy + per-business Unsplash + motion all light up at once. EMAIL-FIRST GUARDRAIL UNCHANGED: create_full_workspace + create_workspace_from_google_paste both still RETURN BEFORE THE EMAIL DANCE — operator can't see URLs until they reply with email, finalize_workspace mints the magic admin link + sends the welcome email via Resend. BYOK pass-through: getAIClient checks organizations.integrations.anthropic.apiKey first, falls back to platform ANTHROPIC_API_KEY env. Cost ~$0.10 per workspace, irrelevant since the operator typically supplies their own key. SOFT-FAIL: any error in Step 12.7 leaves the workspace valid (canned-copy Path A intact); never blocks creation. EXPECTED IMPACT: every fresh workspace looks $10k-tier first-shot, no "create workspace then enhance blocks" two-step dance. v1.40.3 IMAGE PIPELINE FOOLPROOFING: removed the deprecated source.unsplash.com keyless fallback from BOTH resolveHeroImageUrlForQuery and resolveGalleryImageUrlsForQueries. Pre-1.40.3 when the official Unsplash API errored or returned no results we fell back to https://source.unsplash.com/{w}x{h}/?{query} — that endpoint is deprecated and now frequently returns broken responses, which were stored verbatim in landingPages.sections and rendered as broken-image icons before the v1.40.2 onError handler eventually flipped state. The HERO Aesthetic Co. test exposed this: hero showed a broken-image icon despite the gallery rendering 4 medspa photos correctly (proving onError wasn't reliably catching deprecated-CDN failures). Fix: never store a known-broken URL. Hero query miss → empty string → branded-gradient empty-state from frame zero (no broken icon ever). Gallery query miss → SKIP that slot → grid auto-reflows to a clean 4-tile composition instead of 6 tiles with 2 broken. Trade-off: workspaces without UNSPLASH_ACCESS_KEY now ship gradient hero + empty gallery (better than broken images). Operators get full control by setting the env var or replacing images post-launch via update_landing_section. ws1-webhook-pricing-fixes branch ships this; redeploy required.`;