Kasimir Docs DEEN To the app →

Chat Tools in Detail

Complete reference of every tool the chat model can call — built-ins, code sandbox, SQL, skills, sub-agents and integrations including activation, limits and security.

Kasimir's chat model is agentic: during a single answer it can call tools (functions), receive their results, and keep working on top of them — until it produces a final answer. Which tools are available in a turn depends on what the user activated in the Tools menu, which integrations are connected, and which server-side sidecars are configured. This page documents every tool exactly as implemented in the code (server/api/chat/conversations/[id]/messages.post.ts, server/utils/tools/adapters/*, app/components/chat/ChatToolsMenu.vue).

ℹ️

Architecture in one sentence There are two categories: built-ins (render_chart, render_scene, web_search, fetch_page) are wired directly into the chat handler; everything else flows through the provider-adapter registry (server/utils/tools/registry.ts), which per request determines which adapters are connected/activated and feeds their tools into the LLM prompt.

How a tool call runs

For each turn the chat handler assembles a tools array from three sources: built-ins, adapter tools (via getAvailableTools), and sub-agents (ask_*). The model then runs a ReAct loop of at most MAX_ROUNDS = 8 rounds. In each round the model may call one or more tools; the handler runs them, appends results as tool messages, and calls the model again. In the final round the tools array is dropped (roundTools = round < MAX_ROUNDS - 1 ? tools : undefined) so the model is forced to answer instead of searching forever.

Phase

Behaviour

Build tool list

builtinTools + activated adapter tools + ask_* sub-agents; if all empty ⇒ tools = undefined

Dispatch

web_search / fetch_page / render_chart / render_scene have dedicated branches; everything else goes through invokeTool(...) to the registry, sub-agents via runSubagent

Live feedback

Every call sends tool_call_started and tool_call_completed SSE events (chip + trace sidebar)

Result cap

Results fed live back to the model are truncated at LIVE_TOOL_RESULT_CAP = 8000 chars (capToolContent)

Persistence

Adapter tool calls land in message_tool_calls; web_search/fetch_page additionally as message_citations

Leaked calls

If a model leaks a tool call as text (Mistral [TOOL_CALLS], Gemma <tool_call:…/>), parseLeakedToolCalls parses it and synthesizes real calls

⚠️

Robustness against broken arguments vLLM models occasionally stream truncated tool arguments. The handler normalizes function.arguments before echoing via JSON.stringify(JSON.parse(...)); an unrecoverable payload degrades to {} (tool no-ops + model retries) instead of killing the whole turn with "Unterminated string". Tool-call IDs are also coerced to Mistral's enforced ^[a-zA-Z0-9]{9}$ shape.

Agentic ReAct-Loop Werkzeug-gestützter Chat · Tool-Dispatch · persistierter Trace max 8 Runden JS Nutzer-Nachricht Prompt + Kontext LLM-Runde Reasoning + Tool-Auswahl Schleife · max. 8 Iterationen dispatch letzte Runde · Tools weg Finale Antwort + persistierter Trace Tool-Calls · Ergebnisse · Kosten Tool-Dispatch · vier Lanes Jede Lane liefert ein Tool-Ergebnis zurück in die LLM-Runde Built-ins render_chart render_scene web_search fetch_page Pflicht-Paar Code-Sandbox run_python kein Netz-Egress CPU 30 s · 768 MB Artefakte → Chat SQL sql_query read-only SELECT / WITH max 200 Zeilen Integrationen Microsoft 365 Google Workspace Atlassian OAuth · ACL-gefiltert Tool-Ergebnis gekappt auf 8000 Zeichen zurück in die LLM-Runde ↻ LLM / Aktion Ergebnis / Erfolg web_search → fetch_page ist ein Pflicht-Paar Schleife endet, wenn Tools in letzter Runde entfallen
The agentic tool loop: up to eight rounds, with tools dropped in the final round so the model must answer.

Activation via the Tools menu

The Tools menu (ChatToolsMenu.vue) is a flat action list (no toggle switches): one click activates a tool for the chat state and closes the menu; active tools render as removable pills above the composer bar. The check () marks the active state. Activations are sent in the request body of POST …/messages.

Menu item

Body field

Default

Semantics

Web search

web_search: boolean

on when a provider is configured

Enables web_search + fetch_page (only if a provider exists)

Company knowledge

knowledge_document_ids: uuid[]

empty (off)

Activating selects ALL visible ready docs; RAG, not a tool

Data analysis

code_interpreter: boolean

false

Enables run_python (code sandbox)

Database (per connection)

sql_connection_ids: uuid[]

empty (off)

Activates exactly the selected SQL connections

Skills

— (always active)

self-gating

Display-only; use_skill is auto-registered when active skills exist

Integrations

enabled_tool_providers: string[]

empty (opt-in)

An integration contributes tools ONLY when its provider ID is listed here

💡

Opt-in, Langdock-style Integrations are fundamentally opt-in: a connected adapter contributes zero tools per turn unless its provider ID is in enabled_tool_providers. Exceptions that self-gate: sql (via the connection picker), code (via code_interpreter), and skills (always active). An assistant bound to the conversation (ai_apps.tool_providers) auto-enables its integrations — the user only has to establish the OAuth connection.

Built-in tools

These four are hard-coded in the chat handler. render_chart and render_scene are always registered (no provider/config needed); web_search and fetch_page only when a search provider is configured.

render_chart — inline chart

Aspect

Detail

Purpose

Render numeric data as an interactive Chart.js chart in the chat bubble

When

Always available; the model is instructed to use it after SQL queries / for tables instead of ASCII charts

Types

bar, line, pie, doughnut, scatter, radar, polarArea

Input

type, labels[], datasets[{label?, data[], backgroundColor?, borderColor?}], optional title, options{stacked, horizontal, yMin, yMax, legend}

Limits

≤ 1000 labels, ≤ 12 datasets, ≤ 1000 data points/dataset, title/label ≤ 200 chars, color ≤ 64 chars

Security

validateChartSpec (strict Zod whitelist): unknown keys, functions, callback-looking strings (function(, =>, ){) and invalid CSS colors are rejected; no raw Chart.js code

Output

SSE event chart with the validated spec; persisted as tool-call data → re-renders on reload

Errors

Invalid spec ⇒ error hint to the model (incl. downsampling / run_python tip) for a retry

render_scene — interactive 3D scene

Aspect

Detail

Purpose

Render a simple 3D scene (room/floor plan) inline with three.js

When

ONLY when the user explicitly wants a spatial/3D visualization; not for charts

Input

room{width,depth,height,wallColor?,floorColor?} (meters), openings[] (doors/windows per wall), objects[] (primitives box/cylinder/plane/cone/sphere), optional camera

Limits

Room 0.5–100 m (height 0.5–20 m), ≤ 20 openings, ≤ 100 objects

Security

validateSceneSpec (same Zod posture as chart): strict whitelist, no functions, CSS colors validated, dimensions bounded; SceneBlock.vue builds the scene purely declaratively (no string is ever evaluated)

Output

SSE event scene; persisted as tool-call data

web_search — step 1 of web research

Aspect

Detail

Purpose

Returns ONLY titles + URLs + 1-line snippets (top 5)

Activation

web_search: true (default) AND a configured provider (getWebSearchProvider())

Providers

tavily (api.tavily.com, US-hosted) or searxng (self-hosted, EU-by-construction); selected via WEB_SEARCH_PROVIDER

Input

query (focused, < 100 chars)

Limits

Top 5 results, snippet ≤ 300 chars; isolate semaphore withSearchSlot (max 5 concurrent)

Required follow-up

The system prompt enforces: never answer from snippets alone — then fetch_page on the 1–2 most relevant URLs

Persistence

Results become numbered [n] citations (message_citations, web source_kind)

fetch_page — step 2 of web research

Aspect

Detail

Purpose

Fetches a page's readable full text (up to 30,000 chars of cleaned text)

Input

url (exact, from web_search, must be http(s))

Path

Prefers the Scrapling sidecar (scrapling.kasimir.ai, curl_cffi, browser fingerprint); falls back to native fetch

Security

assertSafeUrl (SSRF guard) before every fetch AND on every redirect hop (max 5); only text/html, ≤ 5 MB

Error behaviour

403/429/451/timeout are NORMAL (cloud-IP blocks) — the prompt instructs trying ONE alternative URL and otherwise answering honestly from snippets + URLs

Concurrency

Isolate semaphore withFetchSlot (max 8 concurrent)

ℹ️

"Concrete products" enforcement When a search provider is active, the handler adds a prompt rule: for questions about specific products, manufacturers, model numbers, datasheets, standards, prices or tender (LV) lines the model MUST use web_search (then fetch_page) first and may never invent such facts from memory.

run_python — data analysis (code sandbox)

Aspect

Detail

Adapter

codeAdapter (provider code), sidecar kasimir-codebox

Activation

code_interpreter: true in the Tools menu AND isCodeboxConfigured()

Purpose

Runs a complete Python script in an isolated sandbox (pandas, numpy, matplotlib, openpyxl); no internet access

Statefulness

Stateless — every call starts fresh, variables don't survive; always send the complete script

File injection

The most recently claimed chat attachments (.csv/.xlsx/.xls/.json/.txt/.md, ≤ 5 files / ≤ 10 MB total, newest version wins) sit in the working directory; readable by filename

Artifact return

Generated files (e.g. plt.savefig(...) plots) are uploaded as chat_attachments; their IDs land in codeArtifactIds and are claimed onto the assistant message → embedded in the bubble

Limits

Sidecar: CPU 30 s, RAM 768 MB, FSIZE 20 MB; stdout to the model ≤ 12,000 chars

Security

Read-only rootfs, fixed subnet + iptables egress DROP (no network), fresh tmp workspace per run

SQL tools — read-only database access

Four tools from sqlAdapter (provider sql) routed through the read-only kasimir-sqlbridge sidecar. Active once the sidecar is configured AND the user activated ≥ 1 connection in the picker (sql_connection_ids). An empty array ⇒ no SQL tools this turn.

Tool

Purpose

Key rules

sql_list_connections

Lists available connections (name, engine, host label, connection_id)

Call FIRST; never credentials; only visible connections

sql_list_tables

Tables of a connection

List truncated above 20,000 chars (table_count stays exact)

sql_table_schema

Columns (name, type, nullable) of a table

Use the exact table name from sql_list_tables

sql_query

One single read query, returns columns + rows

ONLY SELECT/WITH, no ;-stacking; max 200 rows; content cap 20,000 chars

⚠️

Hard invariants of the SQL adapter The decrypted DSN never leaves a tool result. sql_query rejects anything that isn't a single SELECT/WITH statement before calling the sidecar (assertReadOnlySql); the sidecar re-checks (read-only transaction, row cap, timeouts). Access is checked per connection: own user-scope rows, any company-scope rows of the company, and — only in a project chat — project-scope rows of exactly that project. A project-bound connection is invisible in a private chat or another project. Errors are returned as a Fehler: result, not thrown (except AbortError).

use_skill — skills (progressive disclosure)

Aspect

Detail

Adapter

skillsAdapter (provider skills), always active (no opt-in)

Availability

Only when active skills exist for the company (or the assistant via ai_app_skills)

Pattern

A single use_skill tool; its description lists up to 25 skills with name, slug and description

Input

slug (exact, from the list)

Flow

invoke returns the skill's full instructions as the tool result before the model tackles the task

Cost

Purely DB-side — no external provider, no extra token spend beyond the description

ask_* — delegation to sub-agents

When the conversation is bound to an assistant that has direct sub-agents (ai_app_subagents), one tool ask_<sanitized-id> is registered per child assistant. Input is task (the fully phrased sub-task). Execution runs via runSubagent, which enforces all guards: depth ≤ MAX_DELEGATION_DEPTH, cycle detection via a chain set (a grandchild can never loop back to a top persona), and a shared per-turn budget MAX_SUBAGENT_CALLS. When the budget is exhausted, the model is told to solve the task itself.

Integrations (provider adapters)

All of the following are opt-in via enabled_tool_providers (unless a bound assistant enables them). Each adapter checks its own connection via isAvailableFor; if absent, the sources popover shows a "Connect" CTA (connectUrl()/integrations).

Provider

Tools (selection)

onedrive

m365_onedrive_search, onedrive_list_files, onedrive_download_file, onedrive_upload_text, onedrive_create_folder, onedrive_move_item, onedrive_get_folder, onedrive_list_drives

sharepoint

m365_sharepoint_search, sharepoint_list_sites, sharepoint_list_files

outlook_mail

outlook_mail_search/list/read/send_mail/reply/forward/move/mark_read, drafts (create/update/send/list), folders, categories, contacts, outlook_find_person, outlook_mailbox_settings

outlook_calendar

outlook_calendar_list, outlook_create_event, outlook_event_update/delete/respond, outlook_availability

google_drive

google_drive_search/list_files/read_file/download_file/upload_text/create_folder/move_file

google_calendar

google_calendar_list_events/create_event/update_event/delete_event

google_sheets

google_sheets_create_spreadsheet/read_range/update_range/append_row/clear_range

google_docs

google_docs_create/read/append

google_slides

google_slides_create

google_tasks

google_tasks_list/create/complete/delete

teams

teams_list_channels, teams_post_card (self-gates on admin-registered destinations; no OAuth)

jira

jira_search_issues, jira_get_issue, jira_create_issue, jira_add_comment

confluence

confluence_search, confluence_get_page, confluence_create_page

mcp:<id>

loaded dynamically from the upstream MCP server (registered only when a server URL is set in runtime config)

ℹ️

Collision rule If two adapters register the same tool name, the one registered first wins. The order in ensureToolsReady() deliberately places first-party adapters before the MCP bridge adapter.

What chat does NOT have

⚠️

No image-generation tool in chat There is no generate_image/image-gen tool in the chat tool loop. Image generation is a workflow node (#7/#11), not a tool the chat model can call. Likewise load_table is a workflow concept, and the doc_search defined in agentTools.ts is the tool of the agentic workflow LLM node — chat instead uses RAG retrieval (built-in, not a tool call) over the composer selection for document knowledge.