@seldonframe/mcp 1.57.0 → 1.59.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/README.md CHANGED
@@ -1,3 +1,4 @@
1
+ <!-- mcp-name: io.github.seldonframe/mcp -->
1
2
  # @seldonframe/mcp
2
3
 
3
4
  Official SeldonFrame MCP server for Claude Code and Claude Desktop.
package/package.json CHANGED
@@ -1,59 +1,59 @@
1
- {
2
- "name": "@seldonframe/mcp",
3
- "version": "1.57.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": "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.59.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
+ }
@@ -15,9 +15,16 @@
15
15
  * @param {string|null} args.aestheticArchetype — workspace's classified
16
16
  * archetype (from snapshot.theme.aestheticArchetype). Used in the
17
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.
18
25
  * @returns {string} the formatted operator summary
19
26
  */
20
- export function buildFinalizeSummary({ snapshot, durationSec, aestheticArchetype }) {
27
+ export function buildFinalizeSummary({ snapshot, durationSec, aestheticArchetype, adminUrl = null }) {
21
28
  const ws = snapshot.workspace ?? {};
22
29
  const businessName = ws.name ?? "Your workspace";
23
30
  const chatbot = snapshot.chatbot ?? null;
@@ -57,7 +64,10 @@ export function buildFinalizeSummary({ snapshot, durationSec, aestheticArchetype
57
64
  // Ops stack URLs
58
65
  lines.push(`📅 Booking: ${opsStack.booking_url ?? snapshot.public_urls?.book ?? ""}`);
59
66
  lines.push(`📝 Intake: ${opsStack.intake_url ?? snapshot.public_urls?.intake ?? ""}`);
60
- lines.push(`🔧 Admin: ${opsStack.admin_url ?? ""}`);
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 ?? ""}`);
61
71
  lines.push("");
62
72
 
63
73
  // 7-automation callout
package/src/tools.js CHANGED
@@ -471,19 +471,19 @@ export const TOOLS = [
471
471
  return firstEver ? withFirstCallBanner(payload) : payload;
472
472
  },
473
473
  },
474
- // create_workspace_from_url — atomic crawl + workspace + chatbot.
475
- // Thin wrapper around create_full_workspace with include_chatbot=true
476
- // and auto_extract_faq=true. The orchestrator at /api/v1/workspace/create
477
- // (POST) handles the multi-phase composition; this tool exists for
478
- // discoverability operators reading the MCP tool list see a name
479
- // that matches the marketing pitch ("paste a URL workspace").
474
+ // create_workspace_from_url — URL extraction playbook atomic build.
475
+ // Returns the server's extraction instructions; Claude WebFetches +
476
+ // extracts, then calls create_full_workspace (v1.58.1: the atomic path
477
+ // it now builds the production multi-page R1 site server-side, the same
478
+ // engine as the SeldonFrame dashboard's /clients/new, so the old
479
+ // block-iterated v2 follow-up is no longer the best pipeline here).
480
480
  {
481
481
  name: "create_workspace_from_url",
482
482
  description:
483
- "Entry point for URL-based workspace creation. Returns instructions Claude follows: WebFetch the URL, optionally WebFetch up to 2 priority sub-pages (e.g. /about, /services, /contact, /pricing), extract structured business facts, ask the operator for any required field that can't be determined, then call `create_workspace_v2` with the result. " +
484
- "THIS TOOL DOES NOT CREATE A WORKSPACE — it returns the playbook. The workspace is created by the follow-up `create_workspace_v2` call (then per-block: get_block_skill + persist_block + complete_workspace_v2 + finalize_workspace). " +
483
+ "Entry point for URL-based workspace creation. Returns instructions Claude follows: WebFetch the URL, optionally WebFetch up to 2 priority sub-pages (e.g. /about, /services, /contact, /pricing), extract structured business facts, ask the operator for any required field that can't be determined, then call `create_full_workspace` with the result — ONE atomic call that builds the workspace + the production multi-page website (vertical-aware landing + per-service detail pages, the same engine as the SeldonFrame dashboard) + booking page + intake form + CRM + draft chatbot. " +
484
+ "THIS TOOL DOES NOT CREATE A WORKSPACE — it returns the playbook. The workspace is created by the follow-up `create_full_workspace` call. Do NOT use the block-iterated create_workspace_v2 flow for URL builds. " +
485
485
  "When operator provides ANY of: 'http://', 'https://', a domain (.com/.io/.net/.co/.app/.dev/.us etc), or 'create workspace for <URL>' — call this tool. Do NOT manually WebFetch first; the instructions returned by this tool tell you exactly what to fetch and in what order. " +
486
- "MANDATORY FOLLOW-UP: After create_workspace_v2 returns 'ready' and all blocks land via persist_block + complete_workspace_v2, ask the operator 'What email should I use for your account?' and call finalize_workspace({ workspace_id, email }).",
486
+ "MANDATORY FOLLOW-UP: After create_full_workspace returns, ask the operator 'What email should I use for your account?' and call finalize_workspace({ workspace_id, email }).",
487
487
  inputSchema: obj(
488
488
  {
489
489
  url: str("Business website URL the operator provided, e.g. https://quigleyac.com"),
@@ -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 {
@@ -4849,6 +4852,78 @@ export const TOOLS = [
4849
4852
  },
4850
4853
  },
4851
4854
 
4855
+ // ───────────────────────────────────────────────────────────────────────
4856
+ // v1.59.0 — IMPROVE. The 5th verb of the builder ladder: build → test →
4857
+ // deploy → sell → improve. `improve_agent` replays an agent's own recent
4858
+ // REAL conversations as graded evals against the current blueprint AND a
4859
+ // proposed patch (a shadow candidate), clusters the failure modes, and
4860
+ // hands back a paired before/after score diff — a marketplace-legible
4861
+ // trust signal, not just a debugging tool. `apply_improvement` is the
4862
+ // ONLY way a proposal's patch ever reaches the live blueprint: the pair
4863
+ // together IS the propose-only contract — improve_agent can never apply
4864
+ // anything on its own, by construction (see packages/crm/src/lib/agents/
4865
+ // improve/improve-run.ts — nothing in that module imports
4866
+ // updateAgentBlueprint).
4867
+ // ───────────────────────────────────────────────────────────────────────
4868
+
4869
+ {
4870
+ name: "improve_agent",
4871
+ description:
4872
+ "USE WHEN USER SAYS: 'improve the agent', 'make the chatbot better', 'why does the agent keep messing up', 'learn from real conversations', 'find what's going wrong and fix it', 'run the improve loop'. " +
4873
+ "Replays the agent's recent REAL conversations as graded evals, clusters the failure modes, and PROPOSES a blueprint patch with before/after scores (paired per-scenario flips + an honest verdict — 'inconclusive' means the sample is too small to call). NEVER applies anything — review the proposal, then call apply_improvement. Takes 1-3 minutes (two replay passes). " +
4874
+ "Needs the workspace's OWN Anthropic key (BYOK) — unbounded build/test-tier LLM cost, same gate as run_agent_evals; returns ok:false reason='no_llm_key' (402) if unset, point the operator at configure_llm_provider. " +
4875
+ "Also returns ok:false (422) with a reason string when there's nothing to replay yet: 'no_conversations' (agent hasn't talked to anyone real), 'no_scenarios' (none of the sampled conversations converted cleanly), or 'agent_not_found'. " +
4876
+ "On success (ok:true): `proposalId` is null when the baseline was already perfect ('nothing to improve') or no patch was proposed/it failed guardrails (note explains why) — in those cases there's nothing to apply. Otherwise pass `proposalId` straight to apply_improvement if the operator approves. " +
4877
+ "`verdict` is 'better' ONLY when paired.improved - paired.regressed >= 3 AND no critical scenario regressed; 'worse' on a clear net regression; otherwise 'inconclusive' — AT THAT SAMPLE SIZE ONLY LARGE EFFECTS ARE REAL, so relay verdict='inconclusive' to the operator as: 'Small sample — apply on judgment, not on the score.' Never round an inconclusive verdict up to sounding like a pass.",
4878
+ inputSchema: obj(
4879
+ {
4880
+ workspace_id: str("Workspace id (bearer workspace)."),
4881
+ agent_id: str("Agent id from list_agents / create_agent / deploy_agent."),
4882
+ },
4883
+ ["workspace_id", "agent_id"],
4884
+ ),
4885
+ handler: async (args) => {
4886
+ const ws = args.workspace_id;
4887
+ const result = await api("POST", "/build/improve", {
4888
+ body: { agent_id: args.agent_id },
4889
+ workspace_id: ws,
4890
+ });
4891
+ if (result?.ok && result.verdict === "inconclusive") {
4892
+ return {
4893
+ ...result,
4894
+ guidance:
4895
+ "Small sample — apply on judgment, not on the score. The paired flip counts and cluster evidence are real; the verdict just isn't confident enough to call 'better' at this sample size. Read the clusters yourself before deciding whether to apply_improvement.",
4896
+ };
4897
+ }
4898
+ return result;
4899
+ },
4900
+ },
4901
+
4902
+ {
4903
+ name: "apply_improvement",
4904
+ description:
4905
+ "USE WHEN USER SAYS: 'apply that improvement', 'go with the proposed fix', 'apply the patch', 'yes, use the better version', 'accept the improve_agent proposal'. " +
4906
+ "Applies a previously PROPOSED patch (from improve_agent's `proposalId`) after human review — this is the ONLY tool that can move an improve proposal's patch onto the live blueprint. Re-validates the patch against the agent's CURRENT blueprint before writing (it may have moved since the proposal was created) and creates a new agent_versions snapshot, exactly like update_agent_blueprint. " +
4907
+ "Version drift doesn't block the apply — if the blueprint changed since the proposal was made, the result just carries a `note: 'applied over vN'` so you know it landed on a newer base than it was proposed against. " +
4908
+ "Rejects (ok:false, 422) with a reason when the proposal can't be applied: 'not_found' (wrong id/org, or already resolved), 'not_proposed' (already applied or dismissed — improve proposals are one-shot), 'revalidation_failed' (the patch no longer passes the guardrail against the current blueprint). None of these ever touch the blueprint. " +
4909
+ "After applying, the agent is on a new version — suggest re-running run_agent_evals or publish_agent({status:'live'}) (which auto-evals) to confirm the new version still passes the safety gate before the operator relies on it.",
4910
+ inputSchema: obj(
4911
+ {
4912
+ workspace_id: str("Workspace id (bearer workspace)."),
4913
+ proposal_id: str("Proposal id from improve_agent's `proposalId` field."),
4914
+ },
4915
+ ["workspace_id", "proposal_id"],
4916
+ ),
4917
+ handler: async (args) => {
4918
+ const ws = args.workspace_id;
4919
+ const result = await api("POST", "/build/improve/apply", {
4920
+ body: { proposal_id: args.proposal_id },
4921
+ workspace_id: ws,
4922
+ });
4923
+ return result;
4924
+ },
4925
+ },
4926
+
4852
4927
  {
4853
4928
  name: "update_agent_blueprint",
4854
4929
  description:
@@ -5086,11 +5161,13 @@ export const TOOLS = [
5086
5161
  error: "publish_failed_but_agent_created",
5087
5162
  agent: createResult.agent,
5088
5163
  embed_url: createResult.embed_url,
5089
- turn_url: createResult.turn_url,
5164
+ turn_api_url: createResult.turn_url,
5165
+ turn_api_method: "POST",
5090
5166
  publish_detail: publishResult,
5091
5167
  steps,
5092
5168
  next_steps: [
5093
5169
  `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}`,
5170
+ `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"}'`,
5094
5171
  ],
5095
5172
  };
5096
5173
  }
@@ -5114,7 +5191,8 @@ export const TOOLS = [
5114
5191
  ok: true,
5115
5192
  agent: createResult.agent,
5116
5193
  embed_url: createResult.embed_url,
5117
- turn_url: createResult.turn_url,
5194
+ turn_api_url: createResult.turn_url,
5195
+ turn_api_method: "POST",
5118
5196
  dashboard_url: dashboardUrl,
5119
5197
  sandbox_url: `${dashboardUrl}/test`,
5120
5198
  llm_mode: llmMode,
@@ -5125,6 +5203,7 @@ export const TOOLS = [
5125
5203
  `3. When ready, publish to live: call publish_agent({ agent_id: '${agentId}', status: 'live' }) — auto-runs eval gate, requires ≥87.5% pass.`,
5126
5204
  `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>)`,
5127
5205
  `5. ${llmKeyNote}`,
5206
+ `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"}'`,
5128
5207
  ],
5129
5208
  };
5130
5209
  },
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, turn_url, dashboard_url, llm_mode, next_steps }
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
 
@@ -191,6 +191,18 @@ Code at any time:
191
191
  - \`run_agent_evals\` — manual eval suite trigger
192
192
  - \`replay_conversation\` — re-run a past chat against current blueprint
193
193
 
194
+ ### v1.59.0 — the builder ladder gains a 5th verb: improve
195
+
196
+ The full ladder is now **build → test → deploy → sell → improve**. Once
197
+ an agent has real conversation history, \`improve_agent({ agent_id })\`
198
+ replays it as graded evals, clusters the failure modes, and proposes a
199
+ blueprint patch with paired before/after scores — never applying
200
+ anything on its own. Review the proposal with the operator, then call
201
+ \`apply_improvement({ proposal_id })\` if it looks good. Treat an
202
+ \"inconclusive\" verdict as a real answer, not a soft failure — small
203
+ samples can't support a confident "better," so apply on judgment, not
204
+ on the score.
205
+
194
206
  The dashboard surfaces (\`/agents\`, \`/agents/[id]/test\`, \`/agents/[id]/settings\`,
195
207
  \`/agents/[id]/evals\`, \`/agents/[id]/conversations\`) provide the same view
196
208
  without leaving the browser — operators iterate FAQ/pricing/greeting inline
@@ -226,9 +238,13 @@ server-side personality system still runs underneath for everything
226
238
  else (CRM, booking, intake, theme, pipeline) — v2 only owns the copy
227
239
  surfaces where v1's layer-mismatch bugs hurt most.
228
240
 
229
- If you find yourself reaching for \`create_full_workspace\` instead of
230
- \`create_workspace_v2\`, stop that's the legacy path. It still works
231
- but the output quality is worse on niches outside SF's curated set.
241
+ Flow choice: when the structured business FACTS are already in hand —
242
+ a URL extraction (create_workspace_from_url's playbook), a Google Maps
243
+ paste, or a filled fact sheet call \`create_full_workspace\` instead.
244
+ Its atomic build now runs the same multi-page R1 site engine as the
245
+ SeldonFrame dashboard (vertical-aware landing + per-service detail
246
+ pages), which beats block-by-block copy generation. Use the v2 flow
247
+ below when the operator wants to craft the copy interactively with you.
232
248
 
233
249
  1. **Ask for the business details** (same as before — gather conversationally):
234
250
  - Business name
@@ -322,17 +338,21 @@ but the output quality is worse on niches outside SF's curated set.
322
338
  output. PARAPHRASE that summary verbatim to the operator — that's
323
339
  how they see what was configured.
324
340
 
325
- ## v1 fallback (legacyNOT preferred)
341
+ ## Atomic vs block-iteratedwhen to use which
326
342
 
327
- \`create_full_workspace\` still works as the v1 atomic creation path.
328
- Use it ONLY when:
329
- - The operator's IDE agent has no LLM (script context, no Anthropic key)
330
- - A network failure prevents repeated tool calls and you need atomicity
331
- - You're writing automated tests that don't want to think about block
332
- generation
343
+ \`create_full_workspace\` is the atomic creation path and (since v1.58)
344
+ builds the production multi-page website server-side — the same R1
345
+ engine the SeldonFrame dashboard's /clients/new uses: vertical-aware
346
+ landing, per-service detail pages, booking, intake, CRM, and a draft
347
+ chatbot, all from one call. Prefer it whenever the business facts are
348
+ already in hand:
349
+ - create_workspace_from_url extractions (its playbook routes here)
350
+ - Google Maps paste flows
351
+ - scripted/automated creation
333
352
 
334
- In normal interactive operator-facing flows, prefer v2 every time. The
335
- v2 quality gradient over v1 is meaningful on long-tail niches.
353
+ Prefer \`create_workspace_v2\` when the operator wants to shape the copy
354
+ interactively you generate hero/services/faq blocks yourself from
355
+ block skills and iterate with them before completing.
336
356
 
337
357
  After step 4 the operator can customize their workspace through
338
358
  further natural-language requests ("change the headline to …",
@@ -456,6 +476,24 @@ further natural-language requests ("change the headline to …",
456
476
  title, description.
457
477
  - **\`install_vertical_pack\`** — set up an industry template
458
478
  (real-estate, dental, legal, plumbing, …).
479
+ - **\`improve_agent\`** (v1.59.0) — replays an agent's recent REAL
480
+ conversations as graded evals, clusters the failure modes, and
481
+ proposes a blueprint patch with paired before/after scores; it never
482
+ applies anything by itself. Takes 1-3 minutes (two eval replay
483
+ passes) and needs the workspace's own Anthropic key (BYOK), same gate
484
+ as \`run_agent_evals\`. Treat verdict='inconclusive' as an honest
485
+ answer, not a failure — the sample is too small to call "better," so
486
+ relay it to the operator as "small sample — apply on judgment, not
487
+ on the score."
488
+ - **\`apply_improvement\`** (v1.59.0) — applies a proposal from
489
+ \`improve_agent\`'s \`proposalId\` after the operator reviews it,
490
+ re-validating the patch against the current blueprint and creating a
491
+ new version, exactly like \`update_agent_blueprint\`. This is the
492
+ ONLY tool that can move an improve proposal onto the live blueprint —
493
+ \`improve_agent\` is propose-only by construction. Re-run
494
+ \`run_agent_evals\` (or \`publish_agent({status:'live'})\`, which
495
+ auto-evals) after applying to confirm the new version still clears
496
+ the safety gate.
459
497
  - **\`list_contacts\`** / **\`create_contact\`** / **\`update_contact\`** —
460
498
  manage the CRM.
461
499
  - **\`list_deals\`** / **\`create_deal\`** / **\`move_deal_stage\`** —