@seldonframe/mcp 1.46.1 → 1.48.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/tools.js +59 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seldonframe/mcp",
3
- "version": "1.46.1",
3
+ "version": "1.48.0",
4
4
  "mcpName": "io.github.seldonframe/seldonframe-mcp",
5
5
  "description": "Open-source GoHighLevel alternative for agencies. 146+ MCP tools that let Claude Code spin up white-labeled client workspaces — CRM, booking, intake forms, landing pages, AI chatbot, and pre-wired agent archetypes (speed-to-lead, missed-call-text-back, review-requester) — in minutes. AGPL-3.0.",
6
6
  "license": "AGPL-3.0-or-later",
package/src/tools.js CHANGED
@@ -101,8 +101,19 @@ export const TOOLS = [
101
101
  },
102
102
  email: str("Optional contact email surfaced in the landing footer (NOT the operator's account email — that comes via finalize_workspace)."),
103
103
  address: str("Optional business address."),
104
+ // v1.48 — schema-level routing guard. The model MUST pass `true`
105
+ // to confirm no URL was available. When the operator's input
106
+ // contains a URL, this assertion is false (don't lie); use
107
+ // `create_workspace_from_url` instead. This makes the routing
108
+ // a hard schema constraint, not just description prose.
109
+ confirmed_no_url_available: {
110
+ type: "boolean",
111
+ enum: [true],
112
+ description:
113
+ "MUST be `true`. Set to true ONLY if the operator did NOT provide a website URL (e.g. they pasted Google Maps text or typed structured fields). If a URL IS available, use `create_workspace_from_url` instead — do NOT lie here. The schema rejects calls without this confirmation.",
114
+ },
104
115
  },
105
- ["business_name", "city", "state", "phone", "services", "business_description"]
116
+ ["business_name", "city", "state", "phone", "services", "business_description", "confirmed_no_url_available"]
106
117
  ),
107
118
  handler: async (args) => {
108
119
  const firstEver = isFirstEverCall();
@@ -478,14 +489,17 @@ export const TOOLS = [
478
489
  {
479
490
  name: "create_workspace_from_url",
480
491
  description:
481
- "⚡ MUST USE WHEN A URL IS PROVIDED — this is the atomic URL-input path for agency client workspace creation. " +
492
+ "⚡ MUST USE WHEN A URL IS PROVIDED — this is the LEAN atomic URL-input path for agency client workspace creation (v1.47, ~10-30s). " +
482
493
  "PATTERNS THAT ROUTE HERE (do not even consider other workspace-creation tools when input matches): " +
483
494
  "'create a workspace for <URL>', 'set up <client> at <URL>', 'spin up <URL>', '<URL>' alone, or any operator message containing http://, https://, or a recognizable domain (.com/.io/.net/.co/.app/.dev/.us etc). " +
484
- "WHAT THIS TOOL ALONE DOES: backend crawls the URL → soul-extracts business info → creates CRM + booking + intake + landing + eval-gated AI chatbot wired to FAQs auto-pulled from the site + the booking calendar. " +
495
+ "WHAT THIS TOOL DOES: backend crawls the URL → extracts business info → creates CRM + booking + intake + eval-gated AI chatbot wired to FAQs auto-pulled from the site. " +
496
+ "DOES NOT GENERATE A LANDING PAGE BY DEFAULT — agency clients typically already have their own website (the URL the operator passed). The chatbot ships as an embed snippet (`chatbot_embed_snippet` in the response) which the agency pastes onto the client's existing site. " +
497
+ "If you ALSO need a SeldonFrame-hosted landing page (only when the client has no site of their own), call `generate_landing_page({ workspace_id })` AFTER this returns — it's a separate ~30-60s opt-in step. " +
485
498
  "DO NOT use create_workspace_v2 or create_full_workspace when a URL is available. Those tools require PRE-EXTRACTED structured fields (business_name, services[], phone) and will produce INFERIOR results for URL input because they skip the URL crawler, the FAQ extractor, and the auto-chatbot build. " +
486
499
  "DO NOT manually WebFetch the URL first — the SeldonFrame backend's soul-compiler is already wired to scrape + extract. Forwarding pre-fetched HTML wastes a round-trip and bypasses the FAQ-from-URL pipeline. " +
487
500
  "Eval gate: chatbot must pass ≥10 of 11 safety + behavior scenarios to ship 'live'. White-label-ready under partner-agency attachment. " +
488
- "Returns: workspace + agent + embed_url + faq_summary. MANDATORY FOLLOW-UP: ask the operator verbatim 'What email should I use for your account?' then call finalize_workspace({ workspace_id, email }) — the admin dashboard URL is ONLY created by finalize_workspace.",
501
+ "Returns: workspace + agent + chatbot_embed_snippet + faq_summary. MANDATORY FOLLOW-UP: ask the operator verbatim 'What email should I use for your account?' then call finalize_workspace({ workspace_id, email }) — the admin dashboard URL is ONLY created by finalize_workspace. " +
502
+ "⭐ WHEN RENDERING THE RESPONSE BACK TO THE OPERATOR: lead with `primary_deliverable.embed_snippet` + `primary_deliverable.paste_instruction`. The chatbot embed snippet is the agency's HEADLINE deliverable — the agency pastes it onto their CLIENT'S existing website (e.g. dallasplumbing.com), not onto a SeldonFrame-hosted page. DO NOT lead the response summary with 'Public URLs' or a `*.app.seldonframe.com` subdomain. When `include_landing_page: false` (the default for URL flow), there is NO SeldonFrame-hosted landing page — the subdomain is for previewing the chatbot only; don't promote it as a deliverable.",
489
503
  inputSchema: obj(
490
504
  {
491
505
  url: str("Business website URL, e.g. https://dallasplumbing.com"),
@@ -498,6 +512,36 @@ export const TOOLS = [
498
512
  url: args.url,
499
513
  include_chatbot: true,
500
514
  auto_extract_faq: true,
515
+ include_landing_page: false,
516
+ },
517
+ allow_anonymous: true,
518
+ }),
519
+ },
520
+ // v1.47 — explicit opt-in landing-page generator. create_workspace_from_url
521
+ // defaults to NO landing page (the client already has their own site);
522
+ // when the operator DOES want a SeldonFrame-hosted landing page, this
523
+ // tool generates it on demand.
524
+ {
525
+ name: "generate_landing_page",
526
+ description:
527
+ "Generate a SeldonFrame-hosted landing page for an EXISTING workspace. " +
528
+ "USE-WHEN: operator explicitly asks for a landing page after the workspace exists, OR the client has no website of their own and the agency wants SeldonFrame to host the public-facing site. " +
529
+ "If the client already has a website (the common agency case), the chatbot embed snippet returned by create_workspace_from_url is the canonical deliverable; you do NOT need a generated landing page. " +
530
+ "Latency: ~30-60s. Returns the public landing URL. Operator can later customize per-block via update_landing_section / persist_block.",
531
+ inputSchema: obj(
532
+ {
533
+ workspace_id: str("Workspace UUID (from create_workspace_from_url or other creation tool)."),
534
+ style: str(
535
+ "Optional archetype override: 'bold-urgency', 'editorial-warm', 'clinical-trust', 'cinematic-aspirational'. If omitted, the soul's base_framework picks the default style."
536
+ ),
537
+ },
538
+ ["workspace_id"]
539
+ ),
540
+ handler: async (args) =>
541
+ api("POST", "/workspace/generate-landing-page", {
542
+ body: {
543
+ workspace_id: args.workspace_id,
544
+ style: args.style ?? null,
501
545
  },
502
546
  allow_anonymous: true,
503
547
  }),
@@ -3002,8 +3046,18 @@ export const TOOLS = [
3002
3046
  service_area: { type: "array", items: { type: "string" } },
3003
3047
  email: str("Optional contact email surfaced in the landing footer (NOT the operator's account email)."),
3004
3048
  address: str("Optional business address."),
3049
+ // v1.48 — schema-level routing guard (same as create_full_workspace).
3050
+ // Forces the model to acknowledge no URL was available, preventing
3051
+ // accidental routing to v2 when create_workspace_from_url is the
3052
+ // correct path.
3053
+ confirmed_no_url_available: {
3054
+ type: "boolean",
3055
+ enum: [true],
3056
+ description:
3057
+ "MUST be `true`. Set to true ONLY if the operator did NOT provide a website URL. If a URL IS available, use `create_workspace_from_url` instead — do NOT lie here.",
3058
+ },
3005
3059
  },
3006
- ["business_name", "city", "state", "phone", "services", "business_description"],
3060
+ ["business_name", "city", "state", "phone", "services", "business_description", "confirmed_no_url_available"],
3007
3061
  ),
3008
3062
  handler: async (args) => {
3009
3063
  const firstEver = isFirstEverCall();