@seldonframe/mcp 1.0.7 → 1.1.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 +2 -2
- package/src/tools.js +191 -39
- package/src/welcome.js +61 -32
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seldonframe/mcp",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "MCP server for SeldonFrame — AI-native Business OS platform. One command creates a real, hosted workspace with a website, booking page, intake form, CRM, and AI agents. v1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "MCP server for SeldonFrame — AI-native Business OS platform. One command creates a real, hosted workspace with a website, booking page, intake form, CRM, and AI agents. v1.1.0: ATOMIC WORKSPACE CREATION. New `create_full_workspace` tool runs the entire 13-step pipeline server-side in one call — workspace, business profile, CRM with industry-specific pipeline stages, booking page with availability, intake form, themed landing page, all deployed with live URLs. Same input always produces the same output, no Claude-Code orchestration, no retries, no 404s. Admin dashboard URL is now created ONLY by finalize_workspace (not by create_full_workspace or create_workspace) — structural enforcement of the email-collection step instead of prompt-wording enforcement. The legacy create_workspace tool stays for backward compatibility but is no longer the recommended path.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"seldonframe-mcp": "src/index.js"
|
package/src/tools.js
CHANGED
|
@@ -57,8 +57,10 @@ export const TOOLS = [
|
|
|
57
57
|
phone: str("Operator's business phone (any format — we render as-is). Renders in nav, hero, and footer when set."),
|
|
58
58
|
email: str("Optional contact email surfaced in the landing footer."),
|
|
59
59
|
address: str("Optional business address. Comma-separated street, city, region, postal, country renders as-is."),
|
|
60
|
+
city: str("Operator's city (e.g. 'San Diego'). Used to set the workspace timezone so the booking page renders the right hours. ALWAYS include when the user mentions a location."),
|
|
61
|
+
state: str("Operator's US state code or full name (e.g. 'CA' or 'California'), or Canadian province. Used for timezone inference. ALWAYS include when the user mentions a location."),
|
|
60
62
|
tagline: str("Short hero tagline (one line)."),
|
|
61
|
-
business_description: str("One paragraph about the business (used for hero subhead + about section)."),
|
|
63
|
+
business_description: str("One paragraph about the business (used for hero subhead + about section). ALWAYS include the industry words verbatim ('residential HVAC', 'family-owned plumbing', 'dental practice') so the right CRM personality + pipeline stages get seeded."),
|
|
62
64
|
services: {
|
|
63
65
|
type: "array",
|
|
64
66
|
description:
|
|
@@ -99,6 +101,8 @@ export const TOOLS = [
|
|
|
99
101
|
phone: args.phone ?? null,
|
|
100
102
|
email: args.email ?? null,
|
|
101
103
|
address: args.address ?? null,
|
|
104
|
+
city: args.city ?? null,
|
|
105
|
+
state: args.state ?? null,
|
|
102
106
|
tagline: args.tagline ?? null,
|
|
103
107
|
business_description: args.business_description ?? null,
|
|
104
108
|
services: args.services ?? null,
|
|
@@ -128,34 +132,46 @@ export const TOOLS = [
|
|
|
128
132
|
Object.entries(rawUrls).filter(([key]) => !key.startsWith("admin_")),
|
|
129
133
|
)
|
|
130
134
|
: null;
|
|
131
|
-
// May 2, 2026 —
|
|
135
|
+
// May 2, 2026 — bulletproof guardrail. Earlier next_step+summary
|
|
136
|
+
// shape was still being skipped: Claude Code would paste the
|
|
137
|
+
// URLs to the operator and end the turn without ever calling
|
|
138
|
+
// finalize_workspace. This shape makes that physically harder:
|
|
132
139
|
//
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
140
|
+
// 1. The TOP-LEVEL response is a giant DO_NOT_DISPLAY warning
|
|
141
|
+
// string, not a URL summary. Pasting this to the operator
|
|
142
|
+
// reads as gibberish — the model has to parse the structured
|
|
143
|
+
// next_step and call the tool to get the operator-facing
|
|
144
|
+
// output.
|
|
145
|
+
// 2. URLs and admin_url are still in the payload (consumers
|
|
146
|
+
// depend on them) but moved into a `_pending_after_email`
|
|
147
|
+
// bucket whose name signals "don't show yet."
|
|
148
|
+
// 3. next_step.required = true with explicit
|
|
149
|
+
// do_not_show_urls_until_email_collected = true.
|
|
142
150
|
const websiteUrl = publicUrls?.home ?? null;
|
|
143
151
|
const bookingUrl = publicUrls?.book ?? null;
|
|
144
152
|
const intakeUrl = publicUrls?.intake ?? null;
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
"What
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
|
|
154
|
+
const guardrail = [
|
|
155
|
+
"⚠️ WORKSPACE CREATED — DO NOT END YET ⚠️",
|
|
156
|
+
"",
|
|
157
|
+
`The workspace "${ws.name}" exists on the backend but the operator-facing flow is NOT complete. You MUST do exactly two things before responding to the user:`,
|
|
158
|
+
"",
|
|
159
|
+
" 1. Ask the user verbatim:",
|
|
160
|
+
' "What email should I use for your account? This is where you\'ll get your login link and any notifications."',
|
|
161
|
+
"",
|
|
162
|
+
" 2. Once they reply with an email, call:",
|
|
163
|
+
" finalize_workspace({ email: <their_email>, name: <optional_name> })",
|
|
164
|
+
"",
|
|
165
|
+
"DO NOT show the operator any URLs from this response. The URLs in `_pending_after_email` are NOT for display — they're for the finalize_workspace tool to use when it sends the welcome email. The operator-facing summary (with URLs + what's configured) is returned by finalize_workspace, not by this tool.",
|
|
166
|
+
"",
|
|
167
|
+
"Why: skipping finalize_workspace leaves the operator with a one-shot admin URL that expires in 7 days, no welcome email in their inbox, and no account on record. Every workspace creation flow MUST end with finalize_workspace.",
|
|
168
|
+
].join("\n");
|
|
155
169
|
|
|
156
170
|
const payload = {
|
|
157
171
|
ok: true,
|
|
158
|
-
|
|
172
|
+
// Top-level guardrail — the FIRST string the model sees in the
|
|
173
|
+
// payload. Reads as instructions, not as operator-facing copy.
|
|
174
|
+
DO_NOT_DISPLAY_TO_USER: guardrail,
|
|
159
175
|
workspace: {
|
|
160
176
|
id,
|
|
161
177
|
name: ws.name,
|
|
@@ -163,29 +179,165 @@ export const TOOLS = [
|
|
|
163
179
|
tier: ws.tier ?? "free",
|
|
164
180
|
created_at: ws.created_at,
|
|
165
181
|
},
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
website_url: websiteUrl,
|
|
169
|
-
booking_url: bookingUrl,
|
|
170
|
-
intake_url: intakeUrl,
|
|
171
|
-
admin_url: adminUrl,
|
|
172
|
-
admin_url_expires_at: result.bearer_token_expires_at ?? null,
|
|
173
|
-
public_urls: result.public_urls ?? publicUrls,
|
|
174
|
-
// MANDATORY next step. Claude Code MUST ask the operator
|
|
175
|
-
// for their email and then call finalize_workspace — DO NOT
|
|
176
|
-
// skip this, DO NOT present the URLs to the operator yet.
|
|
177
|
-
// The welcome email + admin-login + lead capture all
|
|
178
|
-
// depend on finalize_workspace running. Skipping it leaves
|
|
179
|
-
// the operator with a one-shot URL and no recovery path.
|
|
182
|
+
// Mandatory next step — described in plain English so the
|
|
183
|
+
// model has no excuse to skip it.
|
|
180
184
|
next_step: {
|
|
181
185
|
required: true,
|
|
182
|
-
|
|
186
|
+
do_not_show_urls_until_email_collected: true,
|
|
187
|
+
ask_user_verbatim:
|
|
183
188
|
"What email should I use for your account? This is where you'll get your login link and any notifications.",
|
|
184
189
|
tool_to_call: "finalize_workspace",
|
|
185
190
|
tool_args_template: { email: "<operator_email>", name: "<optional>" },
|
|
186
191
|
why_required:
|
|
187
|
-
"finalize_workspace sends the welcome email (
|
|
188
|
-
|
|
192
|
+
"finalize_workspace is what sends the welcome email (Resend), creates the operator's account so the admin login keeps working past the 7-day token, captures them as a lead in our CRM, and returns the formatted operator-facing summary (URLs + what's configured) for you to paraphrase.",
|
|
193
|
+
consequence_of_skipping:
|
|
194
|
+
"Operator gets a one-shot admin URL with no recovery path, no welcome email, no account on record, no follow-up channel. This IS a broken flow.",
|
|
195
|
+
},
|
|
196
|
+
// URLs are in the payload (downstream consumers may rely on
|
|
197
|
+
// them) but in a bucket whose name signals "do not display."
|
|
198
|
+
// May 2, 2026: admin_url REMOVED from this bucket. The admin
|
|
199
|
+
// browser URL is constructed only by finalize_workspace, so
|
|
200
|
+
// there is structurally nothing for Claude Code to leak before
|
|
201
|
+
// the email step. Public URLs stay (they're idempotent and
|
|
202
|
+
// useful to consumers other than the operator chat).
|
|
203
|
+
_pending_after_email: {
|
|
204
|
+
website_url: websiteUrl,
|
|
205
|
+
booking_url: bookingUrl,
|
|
206
|
+
intake_url: intakeUrl,
|
|
207
|
+
public_urls: result.public_urls ?? publicUrls,
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
return firstEver ? withFirstCallBanner(payload) : payload;
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
name: "create_full_workspace",
|
|
215
|
+
description:
|
|
216
|
+
"PREFERRED for new workspaces. Atomic, server-side workspace creation: takes structured business info and creates everything in ONE call — workspace, business profile, CRM with industry-specific pipeline stages, booking page with availability, intake form, themed landing page, all deployed with live URLs. " +
|
|
217
|
+
"Use this instead of create_workspace + a long sequence of customization tools. The pipeline runs server-side with a fixed order — same input always produces same output, no retries, no 404s. " +
|
|
218
|
+
"MANDATORY FOLLOW-UP: After this returns `status: 'ready'`, ask the operator verbatim 'What email should I use for your account? This is where you'll get your login link and notifications.' Then call `finalize_workspace({ workspace_id, email })`. The admin dashboard URL is ONLY created by finalize_workspace — it does not exist in this response (so there's nothing for you to display prematurely). " +
|
|
219
|
+
"Example: create_full_workspace({ business_name: 'Summit Air Comfort', city: 'Phoenix', state: 'AZ', phone: '(480) 555-2100', services: ['AC repair', 'heating installation', 'duct cleaning'], business_description: 'Residential and commercial HVAC in Phoenix', review_count: 950, review_rating: 4.7, trust_signals: ['licensed', 'bonded', 'insured'], emergency_service: true, same_day: true, service_area: ['Scottsdale', 'Tempe', 'Mesa'] })",
|
|
220
|
+
inputSchema: obj(
|
|
221
|
+
{
|
|
222
|
+
business_name: str("Business display name (e.g. 'Summit Air Comfort')."),
|
|
223
|
+
city: str("Operator's city. Drives timezone inference."),
|
|
224
|
+
state: str("US state code or full name (or Canadian province). Drives timezone inference."),
|
|
225
|
+
phone: str("Business phone, any format. Renders in nav, hero, footer."),
|
|
226
|
+
services: {
|
|
227
|
+
type: "array",
|
|
228
|
+
description:
|
|
229
|
+
"Services / offerings the business provides — each as a plain string. The classifier reads these to pick the right CRM personality (HVAC, legal, dental, coaching, agency, default).",
|
|
230
|
+
items: { type: "string" },
|
|
231
|
+
},
|
|
232
|
+
business_description: str(
|
|
233
|
+
"One paragraph describing the business — drives the hero subhead, about section, and (critically) the personality classifier. Include industry words verbatim ('residential HVAC', 'family-owned plumbing', 'dental practice')."
|
|
234
|
+
),
|
|
235
|
+
review_count: { type: "number", description: "Optional — number of reviews. Surfaces in trust strip + hero proof metric." },
|
|
236
|
+
review_rating: { type: "number", description: "Optional — average star rating (e.g. 4.7). Surfaces in trust strip." },
|
|
237
|
+
certifications: {
|
|
238
|
+
type: "array",
|
|
239
|
+
description: "Optional — credentials like ['EPA-certified', 'NATE-certified']. Surfaces in trust strip.",
|
|
240
|
+
items: { type: "string" },
|
|
241
|
+
},
|
|
242
|
+
trust_signals: {
|
|
243
|
+
type: "array",
|
|
244
|
+
description: "Optional — short claims like ['licensed', 'bonded', 'insured']. Surfaces in trust strip.",
|
|
245
|
+
items: { type: "string" },
|
|
246
|
+
},
|
|
247
|
+
emergency_service: { type: "boolean", description: "Optional — operator offers 24/7 emergency service. Surfaces in nav + hero." },
|
|
248
|
+
same_day: { type: "boolean", description: "Optional — same-day service available. Surfaces in trust strip." },
|
|
249
|
+
service_area: {
|
|
250
|
+
type: "array",
|
|
251
|
+
description: "Optional — cities or neighborhoods served (e.g. ['Scottsdale', 'Tempe']).",
|
|
252
|
+
items: { type: "string" },
|
|
253
|
+
},
|
|
254
|
+
email: str("Optional contact email surfaced in the landing footer (NOT the operator's account email — that comes via finalize_workspace)."),
|
|
255
|
+
address: str("Optional business address."),
|
|
256
|
+
},
|
|
257
|
+
["business_name", "city", "state", "phone", "services", "business_description"]
|
|
258
|
+
),
|
|
259
|
+
handler: async (args) => {
|
|
260
|
+
const firstEver = isFirstEverCall();
|
|
261
|
+
const result = await api("POST", "/workspaces/create-full", {
|
|
262
|
+
body: {
|
|
263
|
+
business_name: args.business_name,
|
|
264
|
+
city: args.city,
|
|
265
|
+
state: args.state,
|
|
266
|
+
phone: args.phone,
|
|
267
|
+
services: args.services,
|
|
268
|
+
business_description: args.business_description,
|
|
269
|
+
review_count: args.review_count ?? null,
|
|
270
|
+
review_rating: args.review_rating ?? null,
|
|
271
|
+
certifications: args.certifications ?? null,
|
|
272
|
+
trust_signals: args.trust_signals ?? null,
|
|
273
|
+
emergency_service: args.emergency_service ?? null,
|
|
274
|
+
same_day: args.same_day ?? null,
|
|
275
|
+
service_area: args.service_area ?? null,
|
|
276
|
+
email: args.email ?? null,
|
|
277
|
+
address: args.address ?? null,
|
|
278
|
+
},
|
|
279
|
+
allow_anonymous: true,
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
if (result?.status !== "ready" || !result?.workspace_id) {
|
|
283
|
+
return result;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Stash the bearer locally so subsequent MCP calls (finalize_workspace
|
|
287
|
+
// + the customization tools) can authenticate. We strip the bearer
|
|
288
|
+
// from the visible response — only Claude Code's stash uses it.
|
|
289
|
+
if (result._bearer_token) {
|
|
290
|
+
rememberWorkspace({
|
|
291
|
+
workspace_id: result.workspace_id,
|
|
292
|
+
bearer_token: result._bearer_token,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Build the operator-facing payload. Notable absence: admin_url.
|
|
297
|
+
// The admin browser URL is created in finalize_workspace, NOT
|
|
298
|
+
// here — that's the structural enforcement of email collection.
|
|
299
|
+
const guardrail = [
|
|
300
|
+
"⚠️ WORKSPACE CREATED — DO NOT END YET ⚠️",
|
|
301
|
+
"",
|
|
302
|
+
`The workspace "${args.business_name}" exists on the backend but the operator-facing flow is NOT complete. You MUST do exactly two things before responding to the user:`,
|
|
303
|
+
"",
|
|
304
|
+
" 1. Ask the user verbatim:",
|
|
305
|
+
" " + JSON.stringify(result.operator_prompt ?? "What email should I use for your account?"),
|
|
306
|
+
"",
|
|
307
|
+
" 2. Once they reply with an email, call:",
|
|
308
|
+
` finalize_workspace({ workspace_id: "${result.workspace_id}", email: <their_email>, name: <optional_name> })`,
|
|
309
|
+
"",
|
|
310
|
+
"DO NOT show the operator any URLs from this response yet. The admin dashboard URL does not exist — finalize_workspace creates it. The operator-facing summary (with all URLs + admin link + what's configured) is returned by finalize_workspace.",
|
|
311
|
+
].join("\n");
|
|
312
|
+
|
|
313
|
+
const payload = {
|
|
314
|
+
ok: true,
|
|
315
|
+
DO_NOT_DISPLAY_TO_USER: guardrail,
|
|
316
|
+
workspace: {
|
|
317
|
+
id: result.workspace_id,
|
|
318
|
+
slug: result.slug,
|
|
319
|
+
},
|
|
320
|
+
configured: result.configured,
|
|
321
|
+
next_step: {
|
|
322
|
+
required: true,
|
|
323
|
+
do_not_show_urls_until_email_collected: true,
|
|
324
|
+
ask_user_verbatim: result.operator_prompt,
|
|
325
|
+
tool_to_call: "finalize_workspace",
|
|
326
|
+
tool_args_template: {
|
|
327
|
+
workspace_id: result.workspace_id,
|
|
328
|
+
email: "<operator_email>",
|
|
329
|
+
name: "<optional>",
|
|
330
|
+
},
|
|
331
|
+
why_required:
|
|
332
|
+
"finalize_workspace creates the admin dashboard URL (it does not exist yet), sends the welcome email, captures the operator as a lead, and returns the formatted operator-facing summary. Skipping it leaves the operator with no admin access at all.",
|
|
333
|
+
consequence_of_skipping:
|
|
334
|
+
"The admin dashboard URL is structurally not in this response. Skipping finalize_workspace means the operator can't get into their workspace. This IS a broken flow.",
|
|
335
|
+
},
|
|
336
|
+
// Internal-only — public URLs only. No admin URL anywhere.
|
|
337
|
+
_pending_after_email: {
|
|
338
|
+
website_url: result.public_urls?.home ?? null,
|
|
339
|
+
booking_url: result.public_urls?.book ?? null,
|
|
340
|
+
intake_url: result.public_urls?.intake ?? null,
|
|
189
341
|
},
|
|
190
342
|
};
|
|
191
343
|
return firstEver ? withFirstCallBanner(payload) : payload;
|
package/src/welcome.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// no architecture lecture, no "Soul" / "Cal.diy" / "Formbricks" /
|
|
5
5
|
// "Brain v2" jargon.
|
|
6
6
|
|
|
7
|
-
export const VERSION = "1.0
|
|
7
|
+
export const VERSION = "1.1.0";
|
|
8
8
|
|
|
9
9
|
export const WELCOME_MARKDOWN = `# SeldonFrame — create a real Business OS in one conversation
|
|
10
10
|
|
|
@@ -50,23 +50,38 @@ welcome email, and no follow-up channel. Always finish with step 4.
|
|
|
50
50
|
- Top 3-5 services / products
|
|
51
51
|
- Brief description (1 sentence)
|
|
52
52
|
|
|
53
|
-
2. **Create the workspace.** Call \`
|
|
54
|
-
|
|
53
|
+
2. **Create the workspace atomically.** Call \`create_full_workspace\`
|
|
54
|
+
(the PREFERRED tool for new workspaces). One call → the entire
|
|
55
|
+
workspace deploys server-side: org + business profile + CRM with
|
|
56
|
+
industry-specific pipeline + booking page with availability +
|
|
57
|
+
intake form + themed landing page. Same input always produces
|
|
58
|
+
the same output — no retries, no 404s, no Claude-Code orchestration.
|
|
55
59
|
\`\`\`
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
create_full_workspace({
|
|
61
|
+
business_name: "Pacific Coast Heating & Air",
|
|
62
|
+
city: "San Diego",
|
|
63
|
+
state: "CA",
|
|
58
64
|
phone: "(555) 123-4567",
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
]
|
|
65
|
+
services: ["AC Repair", "Heating Installation", "Indoor Air Quality"],
|
|
66
|
+
business_description: "Family-owned residential HVAC contractor — heating, cooling, AC repair in the San Diego area.",
|
|
67
|
+
// Optional but recommended — enrich the hero proof metrics:
|
|
68
|
+
review_count: 950,
|
|
69
|
+
review_rating: 4.7,
|
|
70
|
+
trust_signals: ["licensed", "bonded", "insured"],
|
|
71
|
+
emergency_service: true,
|
|
72
|
+
same_day: true,
|
|
73
|
+
service_area: ["San Diego", "Chula Vista", "Oceanside"]
|
|
65
74
|
})
|
|
66
75
|
\`\`\`
|
|
67
|
-
The response
|
|
68
|
-
|
|
69
|
-
|
|
76
|
+
The response carries a guardrail (\`DO_NOT_DISPLAY_TO_USER\`) and
|
|
77
|
+
intentionally OMITS the admin dashboard URL — it does not exist
|
|
78
|
+
yet. Do NOT present anything from this response to the operator.
|
|
79
|
+
Move directly to step 3.
|
|
80
|
+
|
|
81
|
+
The legacy \`create_workspace\` tool still works for backward
|
|
82
|
+
compat but is fragile (assumes Claude Code orchestrates the
|
|
83
|
+
follow-up customization). Prefer \`create_full_workspace\` for
|
|
84
|
+
every new flow.
|
|
70
85
|
|
|
71
86
|
3. **Ask the operator for their email — VERBATIM.** Use exactly this
|
|
72
87
|
wording so the operator understands why it's needed:
|
|
@@ -76,21 +91,28 @@ welcome email, and no follow-up channel. Always finish with step 4.
|
|
|
76
91
|
is required to keep their admin login alive past 7 days — then
|
|
77
92
|
ask again. Don't move on without an answer.
|
|
78
93
|
|
|
79
|
-
4. **MANDATORY: call \`finalize_workspace({ email })\`.**
|
|
80
|
-
one tool call that closes the loop
|
|
94
|
+
4. **MANDATORY: call \`finalize_workspace({ workspace_id, email })\`.**
|
|
95
|
+
This is the one tool call that closes the loop AND creates the
|
|
96
|
+
admin dashboard URL (which doesn't exist until this runs):
|
|
81
97
|
\`\`\`
|
|
82
|
-
finalize_workspace({
|
|
98
|
+
finalize_workspace({
|
|
99
|
+
workspace_id: "<from-create-full-response>",
|
|
100
|
+
email: "max@pacificcoasthvac.com",
|
|
101
|
+
name: "Max"
|
|
102
|
+
})
|
|
83
103
|
\`\`\`
|
|
84
|
-
It
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
admin link, email
|
|
104
|
+
It mints the admin auth token, sends the welcome email (with
|
|
105
|
+
all the URLs and the admin link), captures the operator as a
|
|
106
|
+
lead in our CRM, and returns a \`summary\` field with the
|
|
107
|
+
formatted final output. PARAPHRASE that summary verbatim to the
|
|
108
|
+
operator — that's how they see what was configured (CRM
|
|
109
|
+
personality, pipeline stages, live URLs, admin link, email
|
|
110
|
+
confirmation).
|
|
90
111
|
|
|
91
112
|
Alternative: call \`collect_operator_email({ email })\` if you
|
|
92
113
|
want finer control without the formatted summary. Either tool
|
|
93
|
-
satisfies step 4; skipping both does not
|
|
114
|
+
satisfies step 4; skipping both does not — and the operator
|
|
115
|
+
is left with NO admin access at all.
|
|
94
116
|
|
|
95
117
|
After step 4 the operator can customize their workspace through
|
|
96
118
|
further natural-language requests ("change the headline to …",
|
|
@@ -101,14 +123,21 @@ further natural-language requests ("change the headline to …",
|
|
|
101
123
|
|
|
102
124
|
## What the tools do (operator language only)
|
|
103
125
|
|
|
104
|
-
- **\`
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
126
|
+
- **\`create_full_workspace\`** — PREFERRED. Atomic, server-side
|
|
127
|
+
workspace creation. One call → entire business OS deployed.
|
|
128
|
+
Always the first call for new workspaces.
|
|
129
|
+
- **\`create_workspace\`** — legacy fallback (kept for backward
|
|
130
|
+
compat). Use create_full_workspace instead for new flows.
|
|
131
|
+
- **\`finalize_workspace\`** — MANDATORY closing call. Mints the
|
|
132
|
+
admin auth token (the admin URL doesn't exist until this runs),
|
|
133
|
+
bundles email collection (welcome email + lead capture), and
|
|
134
|
+
returns the formatted final summary Claude Code paraphrases
|
|
135
|
+
verbatim to the operator. Always the last call of every
|
|
136
|
+
workspace creation flow.
|
|
137
|
+
- **\`collect_operator_email\`** — narrower variant of
|
|
138
|
+
finalize_workspace that only sends the welcome email + captures
|
|
139
|
+
the lead. Doesn't return the formatted summary. Use either;
|
|
140
|
+
never skip both.
|
|
112
141
|
- **\`update_landing_content\`** / **\`update_landing_section\`** —
|
|
113
142
|
edit the website's headline, subhead, sections, copy.
|
|
114
143
|
- **\`update_theme\`** — change colors, fonts, dark/light mode.
|