@seldonframe/mcp 1.56.0 → 1.57.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/package.json +1 -1
- package/src/finalize-summary.js +157 -157
- package/src/tools.js +175 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seldonframe/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.57.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/finalize-summary.js
CHANGED
|
@@ -1,157 +1,157 @@
|
|
|
1
|
-
// v1.55.0 — Standalone builder for the finalize_workspace operator summary.
|
|
2
|
-
// Extracted from tools.js so we can unit-test the string-building logic
|
|
3
|
-
// in isolation (no MCP server boot, no HTTP shims, no snapshot fetcher).
|
|
4
|
-
//
|
|
5
|
-
// The handler in tools.js fetches the snapshot, computes duration, and
|
|
6
|
-
// passes the result here. This file just builds the string.
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @param {object} args
|
|
10
|
-
* @param {object} args.snapshot — workspace snapshot from
|
|
11
|
-
* /api/v1/workspace/<id>/snapshot. Must include: workspace.name,
|
|
12
|
-
* public_urls.{home,book,intake}, chatbot (or null), ops_stack,
|
|
13
|
-
* available_automations, tier.
|
|
14
|
-
* @param {number} args.durationSec — total workspace creation time.
|
|
15
|
-
* @param {string|null} args.aestheticArchetype — workspace's classified
|
|
16
|
-
* archetype (from snapshot.theme.aestheticArchetype). Used in the
|
|
17
|
-
* closing landing-page nudge.
|
|
18
|
-
* @returns {string} the formatted operator summary
|
|
19
|
-
*/
|
|
20
|
-
export function buildFinalizeSummary({ snapshot, durationSec, aestheticArchetype }) {
|
|
21
|
-
const ws = snapshot.workspace ?? {};
|
|
22
|
-
const businessName = ws.name ?? "Your workspace";
|
|
23
|
-
const chatbot = snapshot.chatbot ?? null;
|
|
24
|
-
const opsStack = snapshot.ops_stack ?? {};
|
|
25
|
-
const automations = snapshot.available_automations ?? [];
|
|
26
|
-
const tier = snapshot.tier ?? {};
|
|
27
|
-
const tierLabel = tier.current_tier_label ?? "Free";
|
|
28
|
-
const isPaid = tier.current_tier === "growth" || tier.current_tier === "scale";
|
|
29
|
-
|
|
30
|
-
// Extract client domain from the operator's input (the URL they scraped).
|
|
31
|
-
// We store it on workspace.settings.source_url upstream; fall back to
|
|
32
|
-
// the public preview URL host if unavailable.
|
|
33
|
-
const clientDomain =
|
|
34
|
-
(ws.settings && typeof ws.settings.source_url === "string"
|
|
35
|
-
? new URL(ws.settings.source_url).host
|
|
36
|
-
: null) ?? "your client's site";
|
|
37
|
-
|
|
38
|
-
const lines = [];
|
|
39
|
-
|
|
40
|
-
// Header
|
|
41
|
-
lines.push(`✅ Client ops stack ready for ${businessName}. (${durationSec} seconds)`);
|
|
42
|
-
lines.push("");
|
|
43
|
-
|
|
44
|
-
// Chatbot embed snippet (the magic moment — paste on client's existing site)
|
|
45
|
-
if (chatbot && chatbot.embed_snippet) {
|
|
46
|
-
lines.push(`📞 AI receptionist — paste before </body> on ${clientDomain} to go live:`);
|
|
47
|
-
lines.push(chatbot.embed_snippet);
|
|
48
|
-
lines.push("");
|
|
49
|
-
lines.push(`🤖 Demo for your client: ${chatbot.preview_url ?? snapshot.public_urls?.home ?? ""}`);
|
|
50
|
-
lines.push(` (Chatbot live
|
|
51
|
-
} else {
|
|
52
|
-
lines.push(`🤖 AI chatbot — scaffold pending. Retry:`);
|
|
53
|
-
lines.push(` create_agent({ archetype: "website-chatbot", channel: "web_chat" })`);
|
|
54
|
-
}
|
|
55
|
-
lines.push("");
|
|
56
|
-
|
|
57
|
-
// Ops stack URLs
|
|
58
|
-
lines.push(`📅 Booking: ${opsStack.booking_url ?? snapshot.public_urls?.book ?? ""}`);
|
|
59
|
-
lines.push(`📝 Intake: ${opsStack.intake_url ?? snapshot.public_urls?.intake ?? ""}`);
|
|
60
|
-
lines.push(`🔧 Admin: ${opsStack.admin_url ?? ""}`);
|
|
61
|
-
lines.push("");
|
|
62
|
-
|
|
63
|
-
// 7-automation callout
|
|
64
|
-
if (automations.length > 0) {
|
|
65
|
-
lines.push(`⚡ ${automations.length} more automations ready to deploy for this client:`);
|
|
66
|
-
const descriptions = {
|
|
67
|
-
"speed-to-lead": "text the lead within 30 sec of intake submission",
|
|
68
|
-
"missed-call-text-back": "auto-SMS when their phone goes unanswered",
|
|
69
|
-
"review-requester": "ask for a 5★ after every completed booking",
|
|
70
|
-
"appointment-confirm-sms": "reduce no-shows automatically",
|
|
71
|
-
"weather-aware-booking": "reschedule outdoor jobs when rain is forecast",
|
|
72
|
-
"daily-digest": "morning summary of yesterday's activity",
|
|
73
|
-
"win-back": "re-engage cancelled subscribers with a time-limited code",
|
|
74
|
-
};
|
|
75
|
-
for (const a of automations) {
|
|
76
|
-
const desc = descriptions[a.id] ?? "";
|
|
77
|
-
lines.push(` • ${a.name}${desc ? " — " + desc : ""}`);
|
|
78
|
-
}
|
|
79
|
-
lines.push(` Activate any: ${opsStack.automations_url ?? ""}`);
|
|
80
|
-
lines.push(
|
|
81
|
-
` (Need API keys for SMS/email? Just ask — Claude will walk you through`,
|
|
82
|
-
);
|
|
83
|
-
lines.push(` Twilio / Resend / Stripe setup when an automation needs one.)`);
|
|
84
|
-
lines.push("");
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Tier + client portal
|
|
88
|
-
const tierUpsell = isPaid
|
|
89
|
-
? "white-label + reseller pricing on Scale ($99/mo)"
|
|
90
|
-
: "Upgrade $9/mo for unlimited workspaces";
|
|
91
|
-
const clientPortalUrl = tier.client_portal_url ?? "";
|
|
92
|
-
lines.push(
|
|
93
|
-
`💼 Tier: ${tierLabel} · ${tierUpsell}` +
|
|
94
|
-
(clientPortalUrl ? ` · Client portal: ${clientPortalUrl}` : ""),
|
|
95
|
-
);
|
|
96
|
-
lines.push("");
|
|
97
|
-
|
|
98
|
-
// v1.55.x — derive appHost for the LLM-settings + client-portal links.
|
|
99
|
-
// SELDONFRAME_APP_BASE wins when set (white-label/staging); falls back
|
|
100
|
-
// to https://app.seldonframe.com. Trailing slash stripped to match the
|
|
101
|
-
// pattern used by v2/complete when building ops_stack.automations_url.
|
|
102
|
-
const appHost = (
|
|
103
|
-
process.env.SELDONFRAME_APP_BASE ?? "https://app.seldonframe.com"
|
|
104
|
-
).replace(/\/$/, "");
|
|
105
|
-
|
|
106
|
-
// LLM key clarity — operators kept asking "which key is the chatbot
|
|
107
|
-
// using?" + had no recovery path when llm_credit_exhausted fired.
|
|
108
|
-
lines.push(
|
|
109
|
-
`🔑 Chatbot LLM key: uses your Claude Code key by default. Change at ${appHost}/settings/integrations/llm.`,
|
|
110
|
-
);
|
|
111
|
-
lines.push(
|
|
112
|
-
` (If you see "llm_credit_exhausted", top up at console.anthropic.com/settings/billing.)`,
|
|
113
|
-
);
|
|
114
|
-
lines.push("");
|
|
115
|
-
|
|
116
|
-
// v1.55.x — Client portal demo callout. Closes the demo loop:
|
|
117
|
-
// chatbot (above) → booking → CRM → portal demo for the client.
|
|
118
|
-
// Slug is required; skip gracefully if absent (shouldn't happen in
|
|
119
|
-
// practice but the snapshot can theoretically lack it).
|
|
120
|
-
//
|
|
121
|
-
// The /demo URL is a one-click deep link: workspace creation seeds
|
|
122
|
-
// a "Demo Customer" contact + sample upcoming appointment + welcome
|
|
123
|
-
// message, and visiting /customer/<slug>/demo auto-establishes a
|
|
124
|
-
// portal session for that contact — no email, no magic link. The
|
|
125
|
-
// operator can paste this URL to a prospect and they land directly
|
|
126
|
-
// in a populated portal instead of a magic-link form with empty
|
|
127
|
-
// tabs. Demo data is filtered out of operator-facing CRM / pipeline
|
|
128
|
-
// / analytics by tag, so it stays isolated from real customer data.
|
|
129
|
-
const slug = ws.slug ?? "";
|
|
130
|
-
if (slug) {
|
|
131
|
-
const portalUrl = `${appHost}/customer/${slug}/demo`;
|
|
132
|
-
lines.push(`🎬 Demo the client portal (one-click, no login):`);
|
|
133
|
-
lines.push(` ${portalUrl}`);
|
|
134
|
-
lines.push(
|
|
135
|
-
` Share this link with the prospect — opens directly as "Demo Customer" with a`,
|
|
136
|
-
);
|
|
137
|
-
lines.push(
|
|
138
|
-
` sample appointment and welcome message. Demo data stays isolated from your`,
|
|
139
|
-
);
|
|
140
|
-
lines.push(` real CRM and pipelines.`);
|
|
141
|
-
lines.push(
|
|
142
|
-
` Free tier shows the portal with SeldonFrame branding. Growth ($29/mo) unlocks custom domain + the agency's logo.`,
|
|
143
|
-
);
|
|
144
|
-
lines.push("");
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Landing-page nudge (closing)
|
|
148
|
-
const archetypeClause = aestheticArchetype ? ` in ${aestheticArchetype} style` : "";
|
|
149
|
-
lines.push(
|
|
150
|
-
`Want a landing page too? Just ask: "build a landing page for ${businessName}${archetypeClause}"`,
|
|
151
|
-
);
|
|
152
|
-
lines.push(
|
|
153
|
-
`— Claude will use the landing-page-creation skill to generate one${aestheticArchetype ? " with the archetype voice" : ""}.`,
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
return lines.join("\n");
|
|
157
|
-
}
|
|
1
|
+
// v1.55.0 — Standalone builder for the finalize_workspace operator summary.
|
|
2
|
+
// Extracted from tools.js so we can unit-test the string-building logic
|
|
3
|
+
// in isolation (no MCP server boot, no HTTP shims, no snapshot fetcher).
|
|
4
|
+
//
|
|
5
|
+
// The handler in tools.js fetches the snapshot, computes duration, and
|
|
6
|
+
// passes the result here. This file just builds the string.
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {object} args
|
|
10
|
+
* @param {object} args.snapshot — workspace snapshot from
|
|
11
|
+
* /api/v1/workspace/<id>/snapshot. Must include: workspace.name,
|
|
12
|
+
* public_urls.{home,book,intake}, chatbot (or null), ops_stack,
|
|
13
|
+
* available_automations, tier.
|
|
14
|
+
* @param {number} args.durationSec — total workspace creation time.
|
|
15
|
+
* @param {string|null} args.aestheticArchetype — workspace's classified
|
|
16
|
+
* archetype (from snapshot.theme.aestheticArchetype). Used in the
|
|
17
|
+
* closing landing-page nudge.
|
|
18
|
+
* @returns {string} the formatted operator summary
|
|
19
|
+
*/
|
|
20
|
+
export function buildFinalizeSummary({ snapshot, durationSec, aestheticArchetype }) {
|
|
21
|
+
const ws = snapshot.workspace ?? {};
|
|
22
|
+
const businessName = ws.name ?? "Your workspace";
|
|
23
|
+
const chatbot = snapshot.chatbot ?? null;
|
|
24
|
+
const opsStack = snapshot.ops_stack ?? {};
|
|
25
|
+
const automations = snapshot.available_automations ?? [];
|
|
26
|
+
const tier = snapshot.tier ?? {};
|
|
27
|
+
const tierLabel = tier.current_tier_label ?? "Free";
|
|
28
|
+
const isPaid = tier.current_tier === "growth" || tier.current_tier === "scale";
|
|
29
|
+
|
|
30
|
+
// Extract client domain from the operator's input (the URL they scraped).
|
|
31
|
+
// We store it on workspace.settings.source_url upstream; fall back to
|
|
32
|
+
// the public preview URL host if unavailable.
|
|
33
|
+
const clientDomain =
|
|
34
|
+
(ws.settings && typeof ws.settings.source_url === "string"
|
|
35
|
+
? new URL(ws.settings.source_url).host
|
|
36
|
+
: null) ?? "your client's site";
|
|
37
|
+
|
|
38
|
+
const lines = [];
|
|
39
|
+
|
|
40
|
+
// Header
|
|
41
|
+
lines.push(`✅ Client ops stack ready for ${businessName}. (${durationSec} seconds)`);
|
|
42
|
+
lines.push("");
|
|
43
|
+
|
|
44
|
+
// Chatbot embed snippet (the magic moment — paste on client's existing site)
|
|
45
|
+
if (chatbot && chatbot.embed_snippet) {
|
|
46
|
+
lines.push(`📞 AI receptionist — paste before </body> on ${clientDomain} to go live:`);
|
|
47
|
+
lines.push(chatbot.embed_snippet);
|
|
48
|
+
lines.push("");
|
|
49
|
+
lines.push(`🤖 Demo for your client: ${chatbot.preview_url ?? snapshot.public_urls?.home ?? ""}`);
|
|
50
|
+
lines.push(` (Chatbot is live and answering on your landing — share so your client can try it before pasting)`);
|
|
51
|
+
} else {
|
|
52
|
+
lines.push(`🤖 AI chatbot — scaffold pending. Retry:`);
|
|
53
|
+
lines.push(` create_agent({ archetype: "website-chatbot", channel: "web_chat" })`);
|
|
54
|
+
}
|
|
55
|
+
lines.push("");
|
|
56
|
+
|
|
57
|
+
// Ops stack URLs
|
|
58
|
+
lines.push(`📅 Booking: ${opsStack.booking_url ?? snapshot.public_urls?.book ?? ""}`);
|
|
59
|
+
lines.push(`📝 Intake: ${opsStack.intake_url ?? snapshot.public_urls?.intake ?? ""}`);
|
|
60
|
+
lines.push(`🔧 Admin: ${opsStack.admin_url ?? ""}`);
|
|
61
|
+
lines.push("");
|
|
62
|
+
|
|
63
|
+
// 7-automation callout
|
|
64
|
+
if (automations.length > 0) {
|
|
65
|
+
lines.push(`⚡ ${automations.length} more automations ready to deploy for this client:`);
|
|
66
|
+
const descriptions = {
|
|
67
|
+
"speed-to-lead": "text the lead within 30 sec of intake submission",
|
|
68
|
+
"missed-call-text-back": "auto-SMS when their phone goes unanswered",
|
|
69
|
+
"review-requester": "ask for a 5★ after every completed booking",
|
|
70
|
+
"appointment-confirm-sms": "reduce no-shows automatically",
|
|
71
|
+
"weather-aware-booking": "reschedule outdoor jobs when rain is forecast",
|
|
72
|
+
"daily-digest": "morning summary of yesterday's activity",
|
|
73
|
+
"win-back": "re-engage cancelled subscribers with a time-limited code",
|
|
74
|
+
};
|
|
75
|
+
for (const a of automations) {
|
|
76
|
+
const desc = descriptions[a.id] ?? "";
|
|
77
|
+
lines.push(` • ${a.name}${desc ? " — " + desc : ""}`);
|
|
78
|
+
}
|
|
79
|
+
lines.push(` Activate any: ${opsStack.automations_url ?? ""}`);
|
|
80
|
+
lines.push(
|
|
81
|
+
` (Need API keys for SMS/email? Just ask — Claude will walk you through`,
|
|
82
|
+
);
|
|
83
|
+
lines.push(` Twilio / Resend / Stripe setup when an automation needs one.)`);
|
|
84
|
+
lines.push("");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Tier + client portal
|
|
88
|
+
const tierUpsell = isPaid
|
|
89
|
+
? "white-label + reseller pricing on Scale ($99/mo)"
|
|
90
|
+
: "Upgrade $9/mo for unlimited workspaces";
|
|
91
|
+
const clientPortalUrl = tier.client_portal_url ?? "";
|
|
92
|
+
lines.push(
|
|
93
|
+
`💼 Tier: ${tierLabel} · ${tierUpsell}` +
|
|
94
|
+
(clientPortalUrl ? ` · Client portal: ${clientPortalUrl}` : ""),
|
|
95
|
+
);
|
|
96
|
+
lines.push("");
|
|
97
|
+
|
|
98
|
+
// v1.55.x — derive appHost for the LLM-settings + client-portal links.
|
|
99
|
+
// SELDONFRAME_APP_BASE wins when set (white-label/staging); falls back
|
|
100
|
+
// to https://app.seldonframe.com. Trailing slash stripped to match the
|
|
101
|
+
// pattern used by v2/complete when building ops_stack.automations_url.
|
|
102
|
+
const appHost = (
|
|
103
|
+
process.env.SELDONFRAME_APP_BASE ?? "https://app.seldonframe.com"
|
|
104
|
+
).replace(/\/$/, "");
|
|
105
|
+
|
|
106
|
+
// LLM key clarity — operators kept asking "which key is the chatbot
|
|
107
|
+
// using?" + had no recovery path when llm_credit_exhausted fired.
|
|
108
|
+
lines.push(
|
|
109
|
+
`🔑 Chatbot LLM key: uses your Claude Code key by default. Change at ${appHost}/settings/integrations/llm.`,
|
|
110
|
+
);
|
|
111
|
+
lines.push(
|
|
112
|
+
` (If you see "llm_credit_exhausted", top up at console.anthropic.com/settings/billing.)`,
|
|
113
|
+
);
|
|
114
|
+
lines.push("");
|
|
115
|
+
|
|
116
|
+
// v1.55.x — Client portal demo callout. Closes the demo loop:
|
|
117
|
+
// chatbot (above) → booking → CRM → portal demo for the client.
|
|
118
|
+
// Slug is required; skip gracefully if absent (shouldn't happen in
|
|
119
|
+
// practice but the snapshot can theoretically lack it).
|
|
120
|
+
//
|
|
121
|
+
// The /demo URL is a one-click deep link: workspace creation seeds
|
|
122
|
+
// a "Demo Customer" contact + sample upcoming appointment + welcome
|
|
123
|
+
// message, and visiting /customer/<slug>/demo auto-establishes a
|
|
124
|
+
// portal session for that contact — no email, no magic link. The
|
|
125
|
+
// operator can paste this URL to a prospect and they land directly
|
|
126
|
+
// in a populated portal instead of a magic-link form with empty
|
|
127
|
+
// tabs. Demo data is filtered out of operator-facing CRM / pipeline
|
|
128
|
+
// / analytics by tag, so it stays isolated from real customer data.
|
|
129
|
+
const slug = ws.slug ?? "";
|
|
130
|
+
if (slug) {
|
|
131
|
+
const portalUrl = `${appHost}/customer/${slug}/demo`;
|
|
132
|
+
lines.push(`🎬 Demo the client portal (one-click, no login):`);
|
|
133
|
+
lines.push(` ${portalUrl}`);
|
|
134
|
+
lines.push(
|
|
135
|
+
` Share this link with the prospect — opens directly as "Demo Customer" with a`,
|
|
136
|
+
);
|
|
137
|
+
lines.push(
|
|
138
|
+
` sample appointment and welcome message. Demo data stays isolated from your`,
|
|
139
|
+
);
|
|
140
|
+
lines.push(` real CRM and pipelines.`);
|
|
141
|
+
lines.push(
|
|
142
|
+
` Free tier shows the portal with SeldonFrame branding. Growth ($29/mo) unlocks custom domain + the agency's logo.`,
|
|
143
|
+
);
|
|
144
|
+
lines.push("");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Landing-page nudge (closing)
|
|
148
|
+
const archetypeClause = aestheticArchetype ? ` in ${aestheticArchetype} style` : "";
|
|
149
|
+
lines.push(
|
|
150
|
+
`Want a landing page too? Just ask: "build a landing page for ${businessName}${archetypeClause}"`,
|
|
151
|
+
);
|
|
152
|
+
lines.push(
|
|
153
|
+
`— Claude will use the landing-page-creation skill to generate one${aestheticArchetype ? " with the archetype voice" : ""}.`,
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
return lines.join("\n");
|
|
157
|
+
}
|
package/src/tools.js
CHANGED
|
@@ -4777,6 +4777,78 @@ export const TOOLS = [
|
|
|
4777
4777
|
},
|
|
4778
4778
|
},
|
|
4779
4779
|
|
|
4780
|
+
// ───────────────────────────────────────────────────────────────────────
|
|
4781
|
+
// v1.57.0 — DEPLOY-TO-SELL. The self-serve deployment verb: take a
|
|
4782
|
+
// builder-owned template (or a purchased marketplace listing) and turn
|
|
4783
|
+
// it into a REAL, answering agent — one call, idempotent, resumable
|
|
4784
|
+
// across the human-only connect steps (calendar OAuth / phone number).
|
|
4785
|
+
// This is the money moment: a builder goes from "I built an agent" to
|
|
4786
|
+
// "I'm earning from it" without leaving the IDE except for the one-time
|
|
4787
|
+
// OAuth/Twilio connect screen the wizardUrl points at.
|
|
4788
|
+
// ───────────────────────────────────────────────────────────────────────
|
|
4789
|
+
|
|
4790
|
+
{
|
|
4791
|
+
name: "deploy_agent",
|
|
4792
|
+
description:
|
|
4793
|
+
"USE WHEN USER SAYS: 'deploy this agent', 'go live with my template', 'sell this agent — get it answering', 'launch the receptionist I built', 'turn my template into a real number', 'ship this to a client'. " +
|
|
4794
|
+
"Deploys a self-built template (source.template_id) or an installed marketplace listing (source.listing_slug) into a REAL, answering agent for THIS workspace — the deploy-to-sell / earn moment, not just a build step. Idempotent: calling it again for the same source resumes the same deployment instead of creating a duplicate. " +
|
|
4795
|
+
"RESUMABLE TWO-STEP FLOW — read the `status` field: " +
|
|
4796
|
+
"• status='needs_connect' → NOT live yet. `requirements`/`missing` list what's unmet (calendar OAuth, another connector, a phone number, business info) and `wizardUrl` is a ONE-TIME human-only link (calendar OAuth + Twilio number can't be done by an agent). Relay it verbatim: 'Open this once to connect your calendar / phone number: <wizardUrl>' — then, once the human confirms they've done it, call deploy_agent again with the SAME source (and `phone` if the agent needs a number) to proceed. " +
|
|
4797
|
+
"• status='live' → deployed and answering right now. Tell the operator the number (if any) is live and taking real calls/bookings — this is revenue-generating, not a sandbox. " +
|
|
4798
|
+
"• status='disabled' → the deploy verb isn't enabled for this environment yet. " +
|
|
4799
|
+
"PHONE: only required when the template needs a number and none is attached yet — pass phone={mode:'forward', number:'+1...'} to forward an existing line, or phone={mode:'provision', area_code:'512'} to buy a new Twilio number (requires the workspace's own Twilio creds — surfaced as a `telephony` requirement if missing). " +
|
|
4800
|
+
"Exactly one of source.template_id / source.listing_slug is required — template_id for something you built with create_agent-style tools in THIS workspace, listing_slug for a marketplace agent you're installing to run for a client.",
|
|
4801
|
+
inputSchema: obj(
|
|
4802
|
+
{
|
|
4803
|
+
workspace_id: str("Workspace id (bearer workspace)."),
|
|
4804
|
+
source: {
|
|
4805
|
+
type: "object",
|
|
4806
|
+
description:
|
|
4807
|
+
"Exactly one of template_id (a template this workspace built) or listing_slug (a published marketplace listing to install-and-deploy).",
|
|
4808
|
+
properties: {
|
|
4809
|
+
template_id: str("Id of an agent_templates row owned by this workspace."),
|
|
4810
|
+
listing_slug: str("Slug of a published marketplace listing (kind='agent') to clone in and deploy."),
|
|
4811
|
+
},
|
|
4812
|
+
},
|
|
4813
|
+
phone: {
|
|
4814
|
+
type: "object",
|
|
4815
|
+
description:
|
|
4816
|
+
"Only needed when deploy_agent (or an earlier call) reported a 'telephony' requirement and no number is attached yet. Pass ONE mode.",
|
|
4817
|
+
properties: {
|
|
4818
|
+
mode: {
|
|
4819
|
+
type: "string",
|
|
4820
|
+
enum: ["forward", "provision"],
|
|
4821
|
+
description: "'forward' = point an existing number at the agent; 'provision' = buy a new Twilio number.",
|
|
4822
|
+
},
|
|
4823
|
+
number: str("E.164 phone number to forward, e.g. '+15125550148'. Required when mode='forward'."),
|
|
4824
|
+
area_code: str("3-digit US/NANP area code to provision a new number in, e.g. '512'. Required when mode='provision'."),
|
|
4825
|
+
},
|
|
4826
|
+
},
|
|
4827
|
+
},
|
|
4828
|
+
["workspace_id", "source"],
|
|
4829
|
+
),
|
|
4830
|
+
handler: async (args) => {
|
|
4831
|
+
const ws = args.workspace_id;
|
|
4832
|
+
const source = args.source ?? {};
|
|
4833
|
+
const body = {
|
|
4834
|
+
source: {
|
|
4835
|
+
...(source.template_id ? { templateId: source.template_id } : {}),
|
|
4836
|
+
...(source.listing_slug ? { listingSlug: source.listing_slug } : {}),
|
|
4837
|
+
},
|
|
4838
|
+
};
|
|
4839
|
+
if (args.phone?.mode === "forward") {
|
|
4840
|
+
body.phone = { mode: "forward", number: args.phone.number };
|
|
4841
|
+
} else if (args.phone?.mode === "provision") {
|
|
4842
|
+
body.phone = { mode: "provision", areaCode: args.phone.area_code };
|
|
4843
|
+
}
|
|
4844
|
+
const result = await api("POST", "/build/deploy", {
|
|
4845
|
+
body,
|
|
4846
|
+
workspace_id: ws,
|
|
4847
|
+
});
|
|
4848
|
+
return result;
|
|
4849
|
+
},
|
|
4850
|
+
},
|
|
4851
|
+
|
|
4780
4852
|
{
|
|
4781
4853
|
name: "update_agent_blueprint",
|
|
4782
4854
|
description:
|
|
@@ -5639,6 +5711,109 @@ export const TOOLS = [
|
|
|
5639
5711
|
});
|
|
5640
5712
|
},
|
|
5641
5713
|
},
|
|
5714
|
+
|
|
5715
|
+
// Phase T — natural-language R1 landing-page editor.
|
|
5716
|
+
//
|
|
5717
|
+
// Wraps POST /api/v1/landing/r1/customize. Same handler as the in-app
|
|
5718
|
+
// editor at /clients/<slug>/landing/edit. Auth flows through the workspace
|
|
5719
|
+
// bearer stored in ~/.seldonframe/device.json.
|
|
5720
|
+
//
|
|
5721
|
+
// IMPORTANT: The target workspace MUST already have an R1 landing page
|
|
5722
|
+
// (generated by create_full_workspace or create_workspace_from_url with
|
|
5723
|
+
// the R-framework pipeline). If no R1 page exists, the tool returns
|
|
5724
|
+
// { ok: false, error: "no_landing_exists" }.
|
|
5725
|
+
{
|
|
5726
|
+
name: "customize_landing",
|
|
5727
|
+
description:
|
|
5728
|
+
"Apply a natural-language instruction to the current R1 landing page for a workspace. " +
|
|
5729
|
+
"The LLM rewrites only the parts of the payload touched by the instruction, preserving " +
|
|
5730
|
+
"everything else. Each call creates an immutable version snapshot so the operator can " +
|
|
5731
|
+
"undo any change. Edits go live immediately (no publish step). " +
|
|
5732
|
+
"Examples:\n" +
|
|
5733
|
+
" • customize_landing({ workspace_id: '...', instruction: 'Change the hero tagline to focus on 24/7 emergency service' })\n" +
|
|
5734
|
+
" • customize_landing({ workspace_id: '...', instruction: 'Replace the hero photo with https://example.com/photo.jpg' })\n" +
|
|
5735
|
+
" • customize_landing({ workspace_id: '...', instruction: 'Make the FAQ more local-flavored for Phoenix residents' })\n" +
|
|
5736
|
+
" • customize_landing({ workspace_id: '...', instruction: 'Add a question about financing options to the FAQ' })\n" +
|
|
5737
|
+
"Requires: the workspace must have an R1 landing page (auto-generated during workspace creation). " +
|
|
5738
|
+
"Requires: an Anthropic API key configured for the workspace (BYOK via /settings/integrations or platform fallback). " +
|
|
5739
|
+
"Returns: { ok: true, summary, version_id } on success. " +
|
|
5740
|
+
"Returns: { ok: false, error: 'no_landing_exists' } if the workspace has no R1 landing yet.",
|
|
5741
|
+
inputSchema: obj(
|
|
5742
|
+
{
|
|
5743
|
+
workspace_id: str("UUID of the workspace whose landing page to edit."),
|
|
5744
|
+
instruction: str(
|
|
5745
|
+
"Natural-language description of what to change. Be specific. " +
|
|
5746
|
+
"Include image URLs verbatim if swapping a photo. " +
|
|
5747
|
+
"Example: 'Change the hero tagline to: Fast. Local. Trusted.' or " +
|
|
5748
|
+
"'Replace the hero photo with https://images.unsplash.com/photo-...'",
|
|
5749
|
+
),
|
|
5750
|
+
},
|
|
5751
|
+
["workspace_id", "instruction"],
|
|
5752
|
+
),
|
|
5753
|
+
handler: async (a) => {
|
|
5754
|
+
const ws = wsOrDefault(a.workspace_id);
|
|
5755
|
+
if (typeof a.instruction !== "string" || !a.instruction.trim()) {
|
|
5756
|
+
throw new Error("instruction must be a non-empty string.");
|
|
5757
|
+
}
|
|
5758
|
+
return api("POST", "/landing/r1/customize", {
|
|
5759
|
+
body: { instruction: a.instruction },
|
|
5760
|
+
workspace_id: ws,
|
|
5761
|
+
});
|
|
5762
|
+
},
|
|
5763
|
+
},
|
|
5764
|
+
|
|
5765
|
+
// Phase T — list R1 landing version history.
|
|
5766
|
+
{
|
|
5767
|
+
name: "list_landing_versions",
|
|
5768
|
+
description:
|
|
5769
|
+
"Return the edit history for a workspace's R1 landing page, newest first. " +
|
|
5770
|
+
"Each row has: id, instruction, summary, createdAt. Pass a version id to " +
|
|
5771
|
+
"customize_landing_revert to undo to that point. " +
|
|
5772
|
+
"Example: list_landing_versions({ workspace_id: '...' })",
|
|
5773
|
+
inputSchema: obj(
|
|
5774
|
+
{
|
|
5775
|
+
workspace_id: str("UUID of the workspace."),
|
|
5776
|
+
limit: {
|
|
5777
|
+
type: "number",
|
|
5778
|
+
description: "Optional. Max rows to return (default 20, max 100).",
|
|
5779
|
+
},
|
|
5780
|
+
},
|
|
5781
|
+
["workspace_id"],
|
|
5782
|
+
),
|
|
5783
|
+
handler: async (a) => {
|
|
5784
|
+
const ws = wsOrDefault(a.workspace_id);
|
|
5785
|
+
const qs = a.limit ? `?limit=${encodeURIComponent(String(a.limit))}` : "";
|
|
5786
|
+
return api("GET", `/landing/r1/versions${qs}`, { workspace_id: ws });
|
|
5787
|
+
},
|
|
5788
|
+
},
|
|
5789
|
+
|
|
5790
|
+
// Phase T — revert R1 landing to a prior version.
|
|
5791
|
+
{
|
|
5792
|
+
name: "revert_landing",
|
|
5793
|
+
description:
|
|
5794
|
+
"Revert the R1 landing page to a prior version's payload snapshot. " +
|
|
5795
|
+
"Creates a new immutable version row (never deletes history). " +
|
|
5796
|
+
"The page goes live immediately with the reverted content. " +
|
|
5797
|
+
"Get version IDs from list_landing_versions. " +
|
|
5798
|
+
"Example: revert_landing({ workspace_id: '...', version_id: '<uuid>' })",
|
|
5799
|
+
inputSchema: obj(
|
|
5800
|
+
{
|
|
5801
|
+
workspace_id: str("UUID of the workspace."),
|
|
5802
|
+
version_id: str("UUID of the version to revert to (from list_landing_versions)."),
|
|
5803
|
+
},
|
|
5804
|
+
["workspace_id", "version_id"],
|
|
5805
|
+
),
|
|
5806
|
+
handler: async (a) => {
|
|
5807
|
+
const ws = wsOrDefault(a.workspace_id);
|
|
5808
|
+
if (typeof a.version_id !== "string" || !a.version_id.trim()) {
|
|
5809
|
+
throw new Error("version_id must be a non-empty string.");
|
|
5810
|
+
}
|
|
5811
|
+
return api("POST", "/landing/r1/revert", {
|
|
5812
|
+
body: { version_id: a.version_id },
|
|
5813
|
+
workspace_id: ws,
|
|
5814
|
+
});
|
|
5815
|
+
},
|
|
5816
|
+
},
|
|
5642
5817
|
];
|
|
5643
5818
|
|
|
5644
5819
|
export const TOOL_MAP = Object.fromEntries(TOOLS.map((t) => [t.name, t]));
|