Skip to content

Tool Catalog

Copy page

A deployed assistant can do more than generate text. Divinci’s tool surface spans three independent subsystems, each with its own config and its own gate, and all three are off by default:

SubsystemWhat it doesConfigured viaGate
RP1 tool-loopIn-chat tools the model calls during a send (search, fetch, RAG, media, CRM, drafts)Release.toolRouting + server envDouble-gated (env + release)
MCP toolsThe platform’s full surface exposed to external MCP/agent clientsRelease.mcpConfigmcpConfig.enabled
Site ControlNavigate / submit forms on the embedded customer siteRelease.siteControlsiteControl.enabled (THE WALL)

What it is. When a chat turn is routed, the send path hands the message to a tool-calling model that loops call-model → execute-tool → feed-result until the model answers tool-free or a safety net trips. Use it when you want an in-chat assistant that actually does things — searches the web, fetches a URL, retrieves from RAG, generates media, or drafts an email/event/contact.

ToolCategorySide effectIn default set?
webSearchread-onlynone
fetchUrlread-onlynone (SSRF-guarded)
ragSearchread-onlynone
composeEmaildraftnone — emits a deep-link draft
draftCalendarEventdraftnone — emits a deep-link draft
draftCrmContactdraftnone — emits a deep-link draft
generateImagemediaprovider spend❌ opt-in
generateVideomediaprovider spend❌ opt-in
generateDiagrammediaprovider spend❌ opt-in
crmCreateContactmutationcreates/upserts a contact❌ opt-in
crmLookupread-only— (no implementation)

The three draft tools are read-only: they return a structured deep-link the client turns into an “open in Gmail / Calendar / CRM” button — no server-side write happens.

Routing requires both a server env gate and per-release config. Either one alone does nothing.

Terminal window
# 1. Server env gate on the public-api service:
TOOL_ROUTING_ENABLED=true # (or 1)
Terminal window
# 2. Per-release config (read-only default tool set):
curl -X PATCH https://api.stage.divinci.app/white-label/$WL/release/$RELEASE \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"toolRouting":{"enabled":true}}'

To opt media + CRM-create tools in (and accept their un-escrowed spend), set a custom threshold, or pin the model, list them explicitly in enabledTools:

Terminal window
curl -X PATCH https://api.stage.divinci.app/white-label/$WL/release/$RELEASE \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"toolRouting":{
"enabled":true,
"threshold":0.5,
"modelId":"@cf/moonshotai/kimi-k2.7-code",
"enabledTools":[
"webSearch","fetchUrl","ragSearch",
"generateImage","generateDiagram","crmCreateContact",
"composeEmail","draftCalendarEvent","draftCrmContact"
]
}}'

SDK equivalent:

await client.releases.updateInWorkspace(workspaceId, releaseId, {
toolRouting: { enabled: true, enabledTools: ["webSearch", "ragSearch"] },
} as any); // set via the API body; not a typed CLI flag yet
FieldTypeDefaultNotes
enabledbooleanfalsePer-release gate (coerced to boolean by the API)
threshold0–10.6Tool-intent signal must cross this to route
regexWeight0–10.6Weight of the cheap regex prior in the signal
modelIdstring@cf/moonshotai/kimi-k2.7-codeTool-calling model
enabledToolsstring[]the read-only DEFAULT_ROUTED_TOOLS setOmit/empty = read-only set; list to add media/CRM

DEFAULT_ROUTED_TOOLS = webSearch, fetchUrl, ragSearch, composeEmail, draftCalendarEvent, draftCrmContact — i.e. read-only / no un-escrowed spend. Media generators and crmCreateContact are excluded unless you add them.

The orchestrator enforces hard bounds you cannot relax via release config:

BoundDefault
Max iterations8
Max wall-clock90 000 ms
Repetition limit3 identical calls
Token budgetoff (undefined)
Confirmation for mutationsalways required
Per-tool-result sizebounded to 8000 chars
ragSearch topKdefault 6, clamped to ≤ 20
  • Double gate. TOOL_ROUTING_ENABLED=true and toolRouting.enabled=true are both required. One without the other = no routing.
  • Default set is read-only only. Media + crmCreateContact are opt-in because their provider cost currently bypasses chat escrow — enabling them means that spend is uncounted.
  • crmLookup has no implementation. Even if advertised it always returns “CRM lookup is not available.” Only create/upsert exists.
  • crmCreateContact needs an email. If the model didn’t supply one, the call short-circuits with a message rather than upserting an empty key.
  • Mutations always confirm. crmCreateContact requires runtime confirmation regardless of config — requireConfirmationForMutations cannot be disabled.
  • fetchUrl is SSRF-guarded. Private / loopback / link-local / metadata targets are rejected.
  • Routing reads a cheap signal, not a fresh classifier. The send-time signal is the client-carried toolLikelihood or the regex prior — there is no per-turn 70B classifier call (removed for cost). A weak prompt won’t route even with a low threshold.

What it is. The MCP server (workers/mcp-server) exposes ~60+ named tools across ~25 categories — chat, RAG, config, fine-tune, QA, analytics, terms, trust, and more — to external MCP/agent clients. Use mcpConfig when you want to expose some of that surface to an MCP client rather than to the in-chat model.

Terminal window
curl -X PATCH https://api.stage.divinci.app/white-label/$WL/release/$RELEASE \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"mcpConfig":{
"enabled":true,
"exposedTools":["send_message","search_knowledge","get_transcript"],
"allowAnonymousMcp":true,
"maxSpendPerTokenCents":500,
"mcpRateLimit":{"requestsPerMinute":30,"requestsPerDay":2000}
}}'
FieldTypeDefaultNotes
enabledbooleanfalseThe MCP gate
exposedToolsstring[]empty = all release toolsUnknown names are rejected by the API
allowedScopesstring[]Restricts callable scopes
mcpRateLimit.requestsPerMinutenumber60Per-minute cap
mcpRateLimit.requestsPerDaynumber10000Per-day cap
allowAnonymousMcpbooleanfalseRequires a positive maxSpendPerTokenCents
maxSpendPerTokenCentscents1000 ($10)Per-token spend cap
  • Unknown exposedTools are rejected. Listing a tool name the server doesn’t know fails validation (it would otherwise expose nothing).
  • Anonymous access needs a spend cap. allowAnonymousMcp: true requires a positive maxSpendPerTokenCents.

What it is. The crawler builds a SiteManifest of the embedded customer site; Site Control turns that map into executable tools — a navigate_site action plus per-form submit actions. Use it when you want the assistant to move around the embedded site (and, opt-in, fill and submit forms with runtime confirmation).

Terminal window
# Navigation only (THE WALL opt-in):
curl -X PATCH https://api.stage.divinci.app/white-label/$WL/release/$RELEASE \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"siteControl":{"enabled":true,"allowedCategories":["navigation"]}}'
# Add form submission (each submit action is confirmation-gated, always):
# -d '{"siteControl":{"enabled":true,"allowedCategories":["navigation","mutation"]}}'
FieldTypeDefaultNotes
enabledbooleanfalseTHE WALL gate
allowedCategories"navigation" | "query" | "mutation" | "utility"[]["navigation"]Which action categories are derived
allowListstring[]Strictly validated
blockListstring[]Strictly validated

Manifest-derived limits: navigation route enum capped at 60, max 20 form actions, action name length 48.

  • Form submissions always confirm. Site Control mutation / submit actions carry requiresConfirmation: true, which config cannot disable.
  • Sensitive fields are stripped. Password / hidden inputs and any name matching pass/pwd/card/cvv/ssn/secret/token/otp/pin/account-number/routing never become fillable params.
  • Cross-origin hrefs are dropped from navigation actions.
import { deriveReleaseSiteActions } from "@divinci-ai/models";
import { selectToolDefinitions, DEFAULT_ROUTED_TOOLS } from "@divinci-ai/server-resources";
// Tool-loop: which tools a release advertises to the model
const toolDefs = selectToolDefinitions(
release.toolRouting?.enabledTools?.length
? release.toolRouting.enabledTools
: [...DEFAULT_ROUTED_TOOLS],
);
// Site Control: derived actions ([] unless siteControl.enabled)
const siteActions = deriveReleaseSiteActions(release);