@seldonframe/mcp 1.56.0 → 1.58.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 +59 -59
- package/src/finalize-summary.js +167 -157
- package/src/tools.js +184 -2
- package/src/welcome.js +1 -1
package/package.json
CHANGED
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@seldonframe/mcp",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"mcpName": "io.github.seldonframe/seldonframe-mcp",
|
|
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
|
-
"license": "AGPL-3.0-or-later",
|
|
7
|
-
"type": "module",
|
|
8
|
-
"bin": {
|
|
9
|
-
"seldonframe-mcp": "src/index.js"
|
|
10
|
-
},
|
|
11
|
-
"files": [
|
|
12
|
-
"src",
|
|
13
|
-
"README.md"
|
|
14
|
-
],
|
|
15
|
-
"engines": {
|
|
16
|
-
"node": ">=18"
|
|
17
|
-
},
|
|
18
|
-
"scripts": {
|
|
19
|
-
"start": "node src/index.js",
|
|
20
|
-
"check:syntax": "node --check --input-type=module < src/index.js && node --check --input-type=module < src/client.js && node --check --input-type=module < src/tools.js && node --check --input-type=module < src/welcome.js",
|
|
21
|
-
"prepublishOnly": "npm run check:syntax"
|
|
22
|
-
},
|
|
23
|
-
"dependencies": {
|
|
24
|
-
"@modelcontextprotocol/sdk": "^1.0.4"
|
|
25
|
-
},
|
|
26
|
-
"keywords": [
|
|
27
|
-
"mcp",
|
|
28
|
-
"model-context-protocol",
|
|
29
|
-
"seldonframe",
|
|
30
|
-
"ai",
|
|
31
|
-
"agents",
|
|
32
|
-
"business-os",
|
|
33
|
-
"claude-code",
|
|
34
|
-
"crm",
|
|
35
|
-
"gohighlevel-alternative",
|
|
36
|
-
"gohighlevel",
|
|
37
|
-
"agency",
|
|
38
|
-
"whitelabel",
|
|
39
|
-
"partner-agency",
|
|
40
|
-
"booking",
|
|
41
|
-
"intake-form",
|
|
42
|
-
"landing-page",
|
|
43
|
-
"local-service-business",
|
|
44
|
-
"sms",
|
|
45
|
-
"twilio",
|
|
46
|
-
"ai-receptionist",
|
|
47
|
-
"ai-chatbot",
|
|
48
|
-
"missed-call"
|
|
49
|
-
],
|
|
50
|
-
"homepage": "https://seldonframe.com",
|
|
51
|
-
"repository": {
|
|
52
|
-
"type": "git",
|
|
53
|
-
"url": "https://github.com/seldonframe/seldonframe",
|
|
54
|
-
"directory": "skills/mcp-server"
|
|
55
|
-
},
|
|
56
|
-
"publishConfig": {
|
|
57
|
-
"access": "public"
|
|
58
|
-
}
|
|
59
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@seldonframe/mcp",
|
|
3
|
+
"version": "1.58.0",
|
|
4
|
+
"mcpName": "io.github.seldonframe/seldonframe-mcp",
|
|
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
|
+
"license": "AGPL-3.0-or-later",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"seldonframe-mcp": "src/index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"src",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=18"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"start": "node src/index.js",
|
|
20
|
+
"check:syntax": "node --check --input-type=module < src/index.js && node --check --input-type=module < src/client.js && node --check --input-type=module < src/tools.js && node --check --input-type=module < src/welcome.js",
|
|
21
|
+
"prepublishOnly": "npm run check:syntax"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@modelcontextprotocol/sdk": "^1.0.4"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"mcp",
|
|
28
|
+
"model-context-protocol",
|
|
29
|
+
"seldonframe",
|
|
30
|
+
"ai",
|
|
31
|
+
"agents",
|
|
32
|
+
"business-os",
|
|
33
|
+
"claude-code",
|
|
34
|
+
"crm",
|
|
35
|
+
"gohighlevel-alternative",
|
|
36
|
+
"gohighlevel",
|
|
37
|
+
"agency",
|
|
38
|
+
"whitelabel",
|
|
39
|
+
"partner-agency",
|
|
40
|
+
"booking",
|
|
41
|
+
"intake-form",
|
|
42
|
+
"landing-page",
|
|
43
|
+
"local-service-business",
|
|
44
|
+
"sms",
|
|
45
|
+
"twilio",
|
|
46
|
+
"ai-receptionist",
|
|
47
|
+
"ai-chatbot",
|
|
48
|
+
"missed-call"
|
|
49
|
+
],
|
|
50
|
+
"homepage": "https://seldonframe.com",
|
|
51
|
+
"repository": {
|
|
52
|
+
"type": "git",
|
|
53
|
+
"url": "git+https://github.com/seldonframe/seldonframe.git",
|
|
54
|
+
"directory": "skills/mcp-server"
|
|
55
|
+
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public"
|
|
58
|
+
}
|
|
59
|
+
}
|
package/src/finalize-summary.js
CHANGED
|
@@ -1,157 +1,167 @@
|
|
|
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
|
-
* @
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
lines.push(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
);
|
|
144
|
-
lines.push(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
`
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
+
* @param {string|null} [args.adminUrl] — bearer-token-scoped admin URL
|
|
19
|
+
* (built in tools.js as `${appHost}/admin/${workspaceId}?token=${bearer}`,
|
|
20
|
+
* same variable the welcome-email path already uses). Takes priority
|
|
21
|
+
* over snapshot.ops_stack.admin_url, which is tokenless (the snapshot
|
|
22
|
+
* endpoint has no bearer to mint one) and 404s at /admin/invalid if
|
|
23
|
+
* printed as-is. Falls back to the tokenless value only if omitted, so
|
|
24
|
+
* existing callers that don't pass it keep working.
|
|
25
|
+
* @returns {string} the formatted operator summary
|
|
26
|
+
*/
|
|
27
|
+
export function buildFinalizeSummary({ snapshot, durationSec, aestheticArchetype, adminUrl = null }) {
|
|
28
|
+
const ws = snapshot.workspace ?? {};
|
|
29
|
+
const businessName = ws.name ?? "Your workspace";
|
|
30
|
+
const chatbot = snapshot.chatbot ?? null;
|
|
31
|
+
const opsStack = snapshot.ops_stack ?? {};
|
|
32
|
+
const automations = snapshot.available_automations ?? [];
|
|
33
|
+
const tier = snapshot.tier ?? {};
|
|
34
|
+
const tierLabel = tier.current_tier_label ?? "Free";
|
|
35
|
+
const isPaid = tier.current_tier === "growth" || tier.current_tier === "scale";
|
|
36
|
+
|
|
37
|
+
// Extract client domain from the operator's input (the URL they scraped).
|
|
38
|
+
// We store it on workspace.settings.source_url upstream; fall back to
|
|
39
|
+
// the public preview URL host if unavailable.
|
|
40
|
+
const clientDomain =
|
|
41
|
+
(ws.settings && typeof ws.settings.source_url === "string"
|
|
42
|
+
? new URL(ws.settings.source_url).host
|
|
43
|
+
: null) ?? "your client's site";
|
|
44
|
+
|
|
45
|
+
const lines = [];
|
|
46
|
+
|
|
47
|
+
// Header
|
|
48
|
+
lines.push(`✅ Client ops stack ready for ${businessName}. (${durationSec} seconds)`);
|
|
49
|
+
lines.push("");
|
|
50
|
+
|
|
51
|
+
// Chatbot embed snippet (the magic moment — paste on client's existing site)
|
|
52
|
+
if (chatbot && chatbot.embed_snippet) {
|
|
53
|
+
lines.push(`📞 AI receptionist — paste before </body> on ${clientDomain} to go live:`);
|
|
54
|
+
lines.push(chatbot.embed_snippet);
|
|
55
|
+
lines.push("");
|
|
56
|
+
lines.push(`🤖 Demo for your client: ${chatbot.preview_url ?? snapshot.public_urls?.home ?? ""}`);
|
|
57
|
+
lines.push(` (Chatbot is live and answering on your landing — share so your client can try it before pasting)`);
|
|
58
|
+
} else {
|
|
59
|
+
lines.push(`🤖 AI chatbot — scaffold pending. Retry:`);
|
|
60
|
+
lines.push(` create_agent({ archetype: "website-chatbot", channel: "web_chat" })`);
|
|
61
|
+
}
|
|
62
|
+
lines.push("");
|
|
63
|
+
|
|
64
|
+
// Ops stack URLs
|
|
65
|
+
lines.push(`📅 Booking: ${opsStack.booking_url ?? snapshot.public_urls?.book ?? ""}`);
|
|
66
|
+
lines.push(`📝 Intake: ${opsStack.intake_url ?? snapshot.public_urls?.intake ?? ""}`);
|
|
67
|
+
// The token-bearing adminUrl (computed in tools.js from the workspace's
|
|
68
|
+
// bearer) is required to open the dashboard — the snapshot's
|
|
69
|
+
// ops_stack.admin_url has no token and 404s at /admin/invalid if clicked.
|
|
70
|
+
lines.push(`🔧 Admin: ${adminUrl ?? opsStack.admin_url ?? ""}`);
|
|
71
|
+
lines.push("");
|
|
72
|
+
|
|
73
|
+
// 7-automation callout
|
|
74
|
+
if (automations.length > 0) {
|
|
75
|
+
lines.push(`⚡ ${automations.length} more automations ready to deploy for this client:`);
|
|
76
|
+
const descriptions = {
|
|
77
|
+
"speed-to-lead": "text the lead within 30 sec of intake submission",
|
|
78
|
+
"missed-call-text-back": "auto-SMS when their phone goes unanswered",
|
|
79
|
+
"review-requester": "ask for a 5★ after every completed booking",
|
|
80
|
+
"appointment-confirm-sms": "reduce no-shows automatically",
|
|
81
|
+
"weather-aware-booking": "reschedule outdoor jobs when rain is forecast",
|
|
82
|
+
"daily-digest": "morning summary of yesterday's activity",
|
|
83
|
+
"win-back": "re-engage cancelled subscribers with a time-limited code",
|
|
84
|
+
};
|
|
85
|
+
for (const a of automations) {
|
|
86
|
+
const desc = descriptions[a.id] ?? "";
|
|
87
|
+
lines.push(` • ${a.name}${desc ? " — " + desc : ""}`);
|
|
88
|
+
}
|
|
89
|
+
lines.push(` Activate any: ${opsStack.automations_url ?? ""}`);
|
|
90
|
+
lines.push(
|
|
91
|
+
` (Need API keys for SMS/email? Just ask — Claude will walk you through`,
|
|
92
|
+
);
|
|
93
|
+
lines.push(` Twilio / Resend / Stripe setup when an automation needs one.)`);
|
|
94
|
+
lines.push("");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Tier + client portal
|
|
98
|
+
const tierUpsell = isPaid
|
|
99
|
+
? "white-label + reseller pricing on Scale ($99/mo)"
|
|
100
|
+
: "Upgrade $9/mo for unlimited workspaces";
|
|
101
|
+
const clientPortalUrl = tier.client_portal_url ?? "";
|
|
102
|
+
lines.push(
|
|
103
|
+
`💼 Tier: ${tierLabel} · ${tierUpsell}` +
|
|
104
|
+
(clientPortalUrl ? ` · Client portal: ${clientPortalUrl}` : ""),
|
|
105
|
+
);
|
|
106
|
+
lines.push("");
|
|
107
|
+
|
|
108
|
+
// v1.55.x — derive appHost for the LLM-settings + client-portal links.
|
|
109
|
+
// SELDONFRAME_APP_BASE wins when set (white-label/staging); falls back
|
|
110
|
+
// to https://app.seldonframe.com. Trailing slash stripped to match the
|
|
111
|
+
// pattern used by v2/complete when building ops_stack.automations_url.
|
|
112
|
+
const appHost = (
|
|
113
|
+
process.env.SELDONFRAME_APP_BASE ?? "https://app.seldonframe.com"
|
|
114
|
+
).replace(/\/$/, "");
|
|
115
|
+
|
|
116
|
+
// LLM key clarity — operators kept asking "which key is the chatbot
|
|
117
|
+
// using?" + had no recovery path when llm_credit_exhausted fired.
|
|
118
|
+
lines.push(
|
|
119
|
+
`🔑 Chatbot LLM key: uses your Claude Code key by default. Change at ${appHost}/settings/integrations/llm.`,
|
|
120
|
+
);
|
|
121
|
+
lines.push(
|
|
122
|
+
` (If you see "llm_credit_exhausted", top up at console.anthropic.com/settings/billing.)`,
|
|
123
|
+
);
|
|
124
|
+
lines.push("");
|
|
125
|
+
|
|
126
|
+
// v1.55.x — Client portal demo callout. Closes the demo loop:
|
|
127
|
+
// chatbot (above) → booking → CRM → portal demo for the client.
|
|
128
|
+
// Slug is required; skip gracefully if absent (shouldn't happen in
|
|
129
|
+
// practice but the snapshot can theoretically lack it).
|
|
130
|
+
//
|
|
131
|
+
// The /demo URL is a one-click deep link: workspace creation seeds
|
|
132
|
+
// a "Demo Customer" contact + sample upcoming appointment + welcome
|
|
133
|
+
// message, and visiting /customer/<slug>/demo auto-establishes a
|
|
134
|
+
// portal session for that contact — no email, no magic link. The
|
|
135
|
+
// operator can paste this URL to a prospect and they land directly
|
|
136
|
+
// in a populated portal instead of a magic-link form with empty
|
|
137
|
+
// tabs. Demo data is filtered out of operator-facing CRM / pipeline
|
|
138
|
+
// / analytics by tag, so it stays isolated from real customer data.
|
|
139
|
+
const slug = ws.slug ?? "";
|
|
140
|
+
if (slug) {
|
|
141
|
+
const portalUrl = `${appHost}/customer/${slug}/demo`;
|
|
142
|
+
lines.push(`🎬 Demo the client portal (one-click, no login):`);
|
|
143
|
+
lines.push(` ${portalUrl}`);
|
|
144
|
+
lines.push(
|
|
145
|
+
` Share this link with the prospect — opens directly as "Demo Customer" with a`,
|
|
146
|
+
);
|
|
147
|
+
lines.push(
|
|
148
|
+
` sample appointment and welcome message. Demo data stays isolated from your`,
|
|
149
|
+
);
|
|
150
|
+
lines.push(` real CRM and pipelines.`);
|
|
151
|
+
lines.push(
|
|
152
|
+
` Free tier shows the portal with SeldonFrame branding. Growth ($29/mo) unlocks custom domain + the agency's logo.`,
|
|
153
|
+
);
|
|
154
|
+
lines.push("");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Landing-page nudge (closing)
|
|
158
|
+
const archetypeClause = aestheticArchetype ? ` in ${aestheticArchetype} style` : "";
|
|
159
|
+
lines.push(
|
|
160
|
+
`Want a landing page too? Just ask: "build a landing page for ${businessName}${archetypeClause}"`,
|
|
161
|
+
);
|
|
162
|
+
lines.push(
|
|
163
|
+
`— Claude will use the landing-page-creation skill to generate one${aestheticArchetype ? " with the archetype voice" : ""}.`,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
return lines.join("\n");
|
|
167
|
+
}
|
package/src/tools.js
CHANGED
|
@@ -914,6 +914,9 @@ export const TOOLS = [
|
|
|
914
914
|
snapshot,
|
|
915
915
|
durationSec: 0, // smoke test reports actual duration via Vercel logs, not the summary text
|
|
916
916
|
aestheticArchetype,
|
|
917
|
+
adminUrl, // token-bearing (same variable the welcome-email body uses below) —
|
|
918
|
+
// without this the summary's Admin line falls back to the snapshot's
|
|
919
|
+
// tokenless ops_stack.admin_url and 404s at /admin/invalid when clicked.
|
|
917
920
|
});
|
|
918
921
|
|
|
919
922
|
return {
|
|
@@ -4777,6 +4780,78 @@ export const TOOLS = [
|
|
|
4777
4780
|
},
|
|
4778
4781
|
},
|
|
4779
4782
|
|
|
4783
|
+
// ───────────────────────────────────────────────────────────────────────
|
|
4784
|
+
// v1.57.0 — DEPLOY-TO-SELL. The self-serve deployment verb: take a
|
|
4785
|
+
// builder-owned template (or a purchased marketplace listing) and turn
|
|
4786
|
+
// it into a REAL, answering agent — one call, idempotent, resumable
|
|
4787
|
+
// across the human-only connect steps (calendar OAuth / phone number).
|
|
4788
|
+
// This is the money moment: a builder goes from "I built an agent" to
|
|
4789
|
+
// "I'm earning from it" without leaving the IDE except for the one-time
|
|
4790
|
+
// OAuth/Twilio connect screen the wizardUrl points at.
|
|
4791
|
+
// ───────────────────────────────────────────────────────────────────────
|
|
4792
|
+
|
|
4793
|
+
{
|
|
4794
|
+
name: "deploy_agent",
|
|
4795
|
+
description:
|
|
4796
|
+
"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'. " +
|
|
4797
|
+
"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. " +
|
|
4798
|
+
"RESUMABLE TWO-STEP FLOW — read the `status` field: " +
|
|
4799
|
+
"• 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. " +
|
|
4800
|
+
"• 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. " +
|
|
4801
|
+
"• status='disabled' → the deploy verb isn't enabled for this environment yet. " +
|
|
4802
|
+
"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). " +
|
|
4803
|
+
"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.",
|
|
4804
|
+
inputSchema: obj(
|
|
4805
|
+
{
|
|
4806
|
+
workspace_id: str("Workspace id (bearer workspace)."),
|
|
4807
|
+
source: {
|
|
4808
|
+
type: "object",
|
|
4809
|
+
description:
|
|
4810
|
+
"Exactly one of template_id (a template this workspace built) or listing_slug (a published marketplace listing to install-and-deploy).",
|
|
4811
|
+
properties: {
|
|
4812
|
+
template_id: str("Id of an agent_templates row owned by this workspace."),
|
|
4813
|
+
listing_slug: str("Slug of a published marketplace listing (kind='agent') to clone in and deploy."),
|
|
4814
|
+
},
|
|
4815
|
+
},
|
|
4816
|
+
phone: {
|
|
4817
|
+
type: "object",
|
|
4818
|
+
description:
|
|
4819
|
+
"Only needed when deploy_agent (or an earlier call) reported a 'telephony' requirement and no number is attached yet. Pass ONE mode.",
|
|
4820
|
+
properties: {
|
|
4821
|
+
mode: {
|
|
4822
|
+
type: "string",
|
|
4823
|
+
enum: ["forward", "provision"],
|
|
4824
|
+
description: "'forward' = point an existing number at the agent; 'provision' = buy a new Twilio number.",
|
|
4825
|
+
},
|
|
4826
|
+
number: str("E.164 phone number to forward, e.g. '+15125550148'. Required when mode='forward'."),
|
|
4827
|
+
area_code: str("3-digit US/NANP area code to provision a new number in, e.g. '512'. Required when mode='provision'."),
|
|
4828
|
+
},
|
|
4829
|
+
},
|
|
4830
|
+
},
|
|
4831
|
+
["workspace_id", "source"],
|
|
4832
|
+
),
|
|
4833
|
+
handler: async (args) => {
|
|
4834
|
+
const ws = args.workspace_id;
|
|
4835
|
+
const source = args.source ?? {};
|
|
4836
|
+
const body = {
|
|
4837
|
+
source: {
|
|
4838
|
+
...(source.template_id ? { templateId: source.template_id } : {}),
|
|
4839
|
+
...(source.listing_slug ? { listingSlug: source.listing_slug } : {}),
|
|
4840
|
+
},
|
|
4841
|
+
};
|
|
4842
|
+
if (args.phone?.mode === "forward") {
|
|
4843
|
+
body.phone = { mode: "forward", number: args.phone.number };
|
|
4844
|
+
} else if (args.phone?.mode === "provision") {
|
|
4845
|
+
body.phone = { mode: "provision", areaCode: args.phone.area_code };
|
|
4846
|
+
}
|
|
4847
|
+
const result = await api("POST", "/build/deploy", {
|
|
4848
|
+
body,
|
|
4849
|
+
workspace_id: ws,
|
|
4850
|
+
});
|
|
4851
|
+
return result;
|
|
4852
|
+
},
|
|
4853
|
+
},
|
|
4854
|
+
|
|
4780
4855
|
{
|
|
4781
4856
|
name: "update_agent_blueprint",
|
|
4782
4857
|
description:
|
|
@@ -5014,11 +5089,13 @@ export const TOOLS = [
|
|
|
5014
5089
|
error: "publish_failed_but_agent_created",
|
|
5015
5090
|
agent: createResult.agent,
|
|
5016
5091
|
embed_url: createResult.embed_url,
|
|
5017
|
-
|
|
5092
|
+
turn_api_url: createResult.turn_url,
|
|
5093
|
+
turn_api_method: "POST",
|
|
5018
5094
|
publish_detail: publishResult,
|
|
5019
5095
|
steps,
|
|
5020
5096
|
next_steps: [
|
|
5021
5097
|
`Agent ${agentId} was created but couldn't be published to test. Call publish_agent({ agent_id: '${agentId}', status: 'test' }) manually, or check the dashboard at /agents/${agentId}`,
|
|
5098
|
+
`Turn API (POST) — this is not a clickable link, it's a POST-only endpoint: curl -sX POST ${createResult.turn_url} -H "Content-Type: application/json" -d '{"message":"hi"}'`,
|
|
5022
5099
|
],
|
|
5023
5100
|
};
|
|
5024
5101
|
}
|
|
@@ -5042,7 +5119,8 @@ export const TOOLS = [
|
|
|
5042
5119
|
ok: true,
|
|
5043
5120
|
agent: createResult.agent,
|
|
5044
5121
|
embed_url: createResult.embed_url,
|
|
5045
|
-
|
|
5122
|
+
turn_api_url: createResult.turn_url,
|
|
5123
|
+
turn_api_method: "POST",
|
|
5046
5124
|
dashboard_url: dashboardUrl,
|
|
5047
5125
|
sandbox_url: `${dashboardUrl}/test`,
|
|
5048
5126
|
llm_mode: llmMode,
|
|
@@ -5053,6 +5131,7 @@ export const TOOLS = [
|
|
|
5053
5131
|
`3. When ready, publish to live: call publish_agent({ agent_id: '${agentId}', status: 'live' }) — auto-runs eval gate, requires ≥87.5% pass.`,
|
|
5054
5132
|
`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>)`,
|
|
5055
5133
|
`5. ${llmKeyNote}`,
|
|
5134
|
+
`6. Turn API (POST) — not a clickable link, it's a POST-only endpoint: curl -sX POST ${createResult.turn_url} -H "Content-Type: application/json" -d '{"message":"hi"}'`,
|
|
5056
5135
|
],
|
|
5057
5136
|
};
|
|
5058
5137
|
},
|
|
@@ -5639,6 +5718,109 @@ export const TOOLS = [
|
|
|
5639
5718
|
});
|
|
5640
5719
|
},
|
|
5641
5720
|
},
|
|
5721
|
+
|
|
5722
|
+
// Phase T — natural-language R1 landing-page editor.
|
|
5723
|
+
//
|
|
5724
|
+
// Wraps POST /api/v1/landing/r1/customize. Same handler as the in-app
|
|
5725
|
+
// editor at /clients/<slug>/landing/edit. Auth flows through the workspace
|
|
5726
|
+
// bearer stored in ~/.seldonframe/device.json.
|
|
5727
|
+
//
|
|
5728
|
+
// IMPORTANT: The target workspace MUST already have an R1 landing page
|
|
5729
|
+
// (generated by create_full_workspace or create_workspace_from_url with
|
|
5730
|
+
// the R-framework pipeline). If no R1 page exists, the tool returns
|
|
5731
|
+
// { ok: false, error: "no_landing_exists" }.
|
|
5732
|
+
{
|
|
5733
|
+
name: "customize_landing",
|
|
5734
|
+
description:
|
|
5735
|
+
"Apply a natural-language instruction to the current R1 landing page for a workspace. " +
|
|
5736
|
+
"The LLM rewrites only the parts of the payload touched by the instruction, preserving " +
|
|
5737
|
+
"everything else. Each call creates an immutable version snapshot so the operator can " +
|
|
5738
|
+
"undo any change. Edits go live immediately (no publish step). " +
|
|
5739
|
+
"Examples:\n" +
|
|
5740
|
+
" • customize_landing({ workspace_id: '...', instruction: 'Change the hero tagline to focus on 24/7 emergency service' })\n" +
|
|
5741
|
+
" • customize_landing({ workspace_id: '...', instruction: 'Replace the hero photo with https://example.com/photo.jpg' })\n" +
|
|
5742
|
+
" • customize_landing({ workspace_id: '...', instruction: 'Make the FAQ more local-flavored for Phoenix residents' })\n" +
|
|
5743
|
+
" • customize_landing({ workspace_id: '...', instruction: 'Add a question about financing options to the FAQ' })\n" +
|
|
5744
|
+
"Requires: the workspace must have an R1 landing page (auto-generated during workspace creation). " +
|
|
5745
|
+
"Requires: an Anthropic API key configured for the workspace (BYOK via /settings/integrations or platform fallback). " +
|
|
5746
|
+
"Returns: { ok: true, summary, version_id } on success. " +
|
|
5747
|
+
"Returns: { ok: false, error: 'no_landing_exists' } if the workspace has no R1 landing yet.",
|
|
5748
|
+
inputSchema: obj(
|
|
5749
|
+
{
|
|
5750
|
+
workspace_id: str("UUID of the workspace whose landing page to edit."),
|
|
5751
|
+
instruction: str(
|
|
5752
|
+
"Natural-language description of what to change. Be specific. " +
|
|
5753
|
+
"Include image URLs verbatim if swapping a photo. " +
|
|
5754
|
+
"Example: 'Change the hero tagline to: Fast. Local. Trusted.' or " +
|
|
5755
|
+
"'Replace the hero photo with https://images.unsplash.com/photo-...'",
|
|
5756
|
+
),
|
|
5757
|
+
},
|
|
5758
|
+
["workspace_id", "instruction"],
|
|
5759
|
+
),
|
|
5760
|
+
handler: async (a) => {
|
|
5761
|
+
const ws = wsOrDefault(a.workspace_id);
|
|
5762
|
+
if (typeof a.instruction !== "string" || !a.instruction.trim()) {
|
|
5763
|
+
throw new Error("instruction must be a non-empty string.");
|
|
5764
|
+
}
|
|
5765
|
+
return api("POST", "/landing/r1/customize", {
|
|
5766
|
+
body: { instruction: a.instruction },
|
|
5767
|
+
workspace_id: ws,
|
|
5768
|
+
});
|
|
5769
|
+
},
|
|
5770
|
+
},
|
|
5771
|
+
|
|
5772
|
+
// Phase T — list R1 landing version history.
|
|
5773
|
+
{
|
|
5774
|
+
name: "list_landing_versions",
|
|
5775
|
+
description:
|
|
5776
|
+
"Return the edit history for a workspace's R1 landing page, newest first. " +
|
|
5777
|
+
"Each row has: id, instruction, summary, createdAt. Pass a version id to " +
|
|
5778
|
+
"customize_landing_revert to undo to that point. " +
|
|
5779
|
+
"Example: list_landing_versions({ workspace_id: '...' })",
|
|
5780
|
+
inputSchema: obj(
|
|
5781
|
+
{
|
|
5782
|
+
workspace_id: str("UUID of the workspace."),
|
|
5783
|
+
limit: {
|
|
5784
|
+
type: "number",
|
|
5785
|
+
description: "Optional. Max rows to return (default 20, max 100).",
|
|
5786
|
+
},
|
|
5787
|
+
},
|
|
5788
|
+
["workspace_id"],
|
|
5789
|
+
),
|
|
5790
|
+
handler: async (a) => {
|
|
5791
|
+
const ws = wsOrDefault(a.workspace_id);
|
|
5792
|
+
const qs = a.limit ? `?limit=${encodeURIComponent(String(a.limit))}` : "";
|
|
5793
|
+
return api("GET", `/landing/r1/versions${qs}`, { workspace_id: ws });
|
|
5794
|
+
},
|
|
5795
|
+
},
|
|
5796
|
+
|
|
5797
|
+
// Phase T — revert R1 landing to a prior version.
|
|
5798
|
+
{
|
|
5799
|
+
name: "revert_landing",
|
|
5800
|
+
description:
|
|
5801
|
+
"Revert the R1 landing page to a prior version's payload snapshot. " +
|
|
5802
|
+
"Creates a new immutable version row (never deletes history). " +
|
|
5803
|
+
"The page goes live immediately with the reverted content. " +
|
|
5804
|
+
"Get version IDs from list_landing_versions. " +
|
|
5805
|
+
"Example: revert_landing({ workspace_id: '...', version_id: '<uuid>' })",
|
|
5806
|
+
inputSchema: obj(
|
|
5807
|
+
{
|
|
5808
|
+
workspace_id: str("UUID of the workspace."),
|
|
5809
|
+
version_id: str("UUID of the version to revert to (from list_landing_versions)."),
|
|
5810
|
+
},
|
|
5811
|
+
["workspace_id", "version_id"],
|
|
5812
|
+
),
|
|
5813
|
+
handler: async (a) => {
|
|
5814
|
+
const ws = wsOrDefault(a.workspace_id);
|
|
5815
|
+
if (typeof a.version_id !== "string" || !a.version_id.trim()) {
|
|
5816
|
+
throw new Error("version_id must be a non-empty string.");
|
|
5817
|
+
}
|
|
5818
|
+
return api("POST", "/landing/r1/revert", {
|
|
5819
|
+
body: { version_id: a.version_id },
|
|
5820
|
+
workspace_id: ws,
|
|
5821
|
+
});
|
|
5822
|
+
},
|
|
5823
|
+
},
|
|
5642
5824
|
];
|
|
5643
5825
|
|
|
5644
5826
|
export const TOOL_MAP = Object.fromEntries(TOOLS.map((t) => [t.name, t]));
|
package/src/welcome.js
CHANGED
|
@@ -109,7 +109,7 @@ of v1.28.0 this is **ONE tool call**:
|
|
|
109
109
|
// /settings/integrations/llm. Pass explicitly only when you need
|
|
110
110
|
// per-workspace billing isolation (white-label / agency).
|
|
111
111
|
})
|
|
112
|
-
// → returns { agent, embed_url,
|
|
112
|
+
// → returns { agent, embed_url, turn_api_url, turn_api_method: "POST", dashboard_url, llm_mode, next_steps }
|
|
113
113
|
// Internally: creates agent, publishes to test. Skips set_llm_key when
|
|
114
114
|
// no key supplied (platform fallback in lib/ai/client.ts handles inference).
|
|
115
115
|
|