コンテンツにスキップ

Tool System

このコンテンツはまだ日本語訳がありません。

The tool system owns tool registration, availability filtering, dynamic schema assembly, and execution dispatch across built-in tools and guild Model Context Protocol (MCP) integrations. The bot starts no local or stdio MCP server. Model orchestration, multi-turn tool loops, and result serialization belong to the tool loop.

Tool registration and discovery coordinate through two core modules:

  • Registration (src/tools/toolRegistry.ts): Built-in tools implement BaseTool and register with ToolRegistry.registerTool(). Provider tool adapters convert declarations only; dispatch never routes through them.
  • Availability filtering (src/tools/availability.ts): getAvailableToolsWithMCP() provides centralized availability checks before tools are exposed to the model.

Filtering evaluates multiple layers:

  1. Provider support: The tool must declare compatibility with the active LLM provider via isAvailableFor(provider).
  2. Model capabilities: The tool’s requiredModelCapabilities must match active model flags in ToolAvailabilityLlmState.
  3. Feature flags: Tools requiring feature flags (requiresFeatureFlag) are evaluated against guild configuration via configToFeatureFlags.
  4. Channel permissions: Tools with requiresPermissions verify that the bot holds necessary Discord channel permissions (such as SendMessages or UseExternalStickers).
  5. Backend slot availability: Feature tools verify configured backend slots before admission. generate_image requires a standard diffusion model; generate_image_nai requires a NovelAI image slot; generate_video requires a video model slot; and generate_voice_message requires a saved persona source with a usable matching Design or Clone target for the triggering user.
  6. Streaming context filtering: applyStreamContextAvailability() evaluates live-turn constraints (isAvailableForContext) before schema declaration. This removes tools whose turn eligibility has lapsed (such as per-turn deduplication flags) so unavailable tools are not advertised to the model.

roll_dice requires dice_rolling_enabled, which defaults to true and is managed through /config > Plugins > Available Tools. The switch controls tool advertisement and execution independently of the /roll command. Both callers use src/utils/dice/diceRoll.ts for validated dice notation and OS-backed random integers, and diceRollEmbed.ts for their result embeds.

Migration 122_enable_dice_rolling_by_default enables the tool once for existing servers. The migration ledger prevents later startups from overwriting saved choices. New setups default to enabled and can turn it off in the /setup feature checklist.

Dice result embeds have no title. The embed protocol reads their localized visibility footers and admits only shared results into model history, including prompt inspection. These footer strings must remain recognizable in stored Discord messages after copy changes. Unshared dice embeds are excluded from Self Debug and link preview extraction too, so a red natural 1 does not disclose an unshared roll. Shared results become available in subsequent chat context without triggering a reply. Tool rolls also return their structured result to the current model turn.

Before provider serialization, tools pass through assembleToolsForContext() in src/tools/assembly.ts. Most tools return their static definition unchanged. Capability-sensitive tools implement assembleForContext(context) and return tailored variants via createToolVariant():

  • web_search: Narrows category enums to the active search backend. SearXNG exposes all categories; Brave exposes text, image, video, and news. With neither available, the schema advertises text search only, including on OpenRouter turns.
  • interact_with_recent_message: Offers only reply when emoji_usage_enabled is off, removing reaction instructions from its schema while retaining text replies; execution rejects reaction calls using the refreshed setting too.
  • generate_image: Prunes parameters to match active backend capabilities (text-to-image, reference image fields, and ComfyUI inpaint or outpaint controls). Parameter descriptions omit instructions for unconfigured modes, preventing the model from receiving unsupported guidance.
  • generate_voice_message: Resolves persona priority against independently inherited personal/server Design and Clone targets, then prunes markup and delivery controls for the selected target. Execution revalidates that source, target, and credential revision. Speech synthesis and delivery are shared with /generate voice-message; see Voice System.

Assembly guards prevent the model from seeing unsupported parameters, while execution-time checks guard against stale configurations and crafted tool calls.

generate_voice_message and interact_with_target also offer an optional sprite parameter when the turn includes the active persona’s sprite list. The parameter refers to that list without repeating it in either schema. Execution checks the selection against both the advertised list and the current persona sprites before synthesis or interaction writes. Omission preserves appearance inheritance; an empty string selects the base appearance. Both tools use the shared persona delivery resolver, which remembers the selected appearance only after successful delivery. Sprite selection does not add speech delivery instructions.

Tool execution coordinates through a single entry point: ToolRegistry.executeTool().

Model tool call
↓
ToolRegistry.executeTool()
↓
Resolve opaque IDs (media_id, message_id) & aliases
↓
Dispatch:
├─ Guild MCP Manager ──→ guildMcpManager.executeGuildMCPFunction()
└─ Built-in Tool ──→ tool.execute() (availability & context checked)
↓
Redact parameters & record ToolExecutionEvent
↓
Return ToolResult (success / error)

Capability credential resolution captures the active model ID with its provider from one server settings read. Media, vision, and embedding callers use getResolvedCapabilityModelId() to keep that model paired with its credentials when their turn’s cached settings predate a model change.

Execution dispatch enforces the following rules:

  1. Alias and opaque ID resolution: Built-in aliases (BUILTIN_TOOL_ALIASES) resolve to canonical names. Opaque identifiers (media_id, message_id, end_message_id) resolve to Discord snowflakes via MessageIdMap before reaching tool code.
  2. Guild MCP dispatch: Advertisement and dispatch use getGuildMCPRouting() for the actual workspace and exact enabled registration. Only explicit url_fetcher and web_search selections can replace their corresponding built-in family. prose_linter registrations are never routed; only Response Drafting’s rule checker calls them. Dispatch checks the family’s existing web_search_enabled switch again.
  3. Built-in tool execution: executeBuiltInTool() reuses the availability predicate for provider support, model capabilities, declared feature flags, live context, and declared bot permissions. Dispatch refreshes workspace feature switches before executing, including after asynchronous discovery. Unavailable or stale MCP configuration reads preserve unrelated built-ins and their aliases with those checks; remote calls and native fetch/search refuse because the current replacement selection cannot be verified. Tool code still owns target access, invoking-user authorization, quotas, and backend-specific validation. These checks preserve internal engine calls and do not create a new administrator restriction.
  4. Execution history and redaction: Parameters are sanitized via redactToolParametersForStorage() before recording in ToolExecutionEvent history.
  5. Error handling: Thrown exceptions are caught and wrapped in a structured ToolResult with success: false and a localized or descriptive error message. The error returns to the model turn so the persona can adapt or explain the failure; see Execute Tool Call.

Guilds manage remote MCP servers through /config > Plugins > MCP Servers (src/utils/mcp/mcpConfigOperations.ts):

  • Connection pooling and lifecycle: GuildMcpManager (src/utils/mcp/guildMcpManager.ts) connects servers lazily during tool gathering. Streamable HTTP and SSE use fresh clients bounded by GUILD_MCP_CONNECT_TIMEOUT_MS. Both use guildMcpFetch.ts for the shared URL gate, pinned DNS, same-origin requests without URL credentials, and redirect refusal. A response stops at 8 MiB before SDK JSON or SSE parsing, including discovery and internal rule checks. The cap applies cumulatively to each SSE response stream. Result envelopes must pass the SDK’s schema before storage.
  • Expired sessions: A Streamable HTTP 404 on a connection with a session ID removes the client from the pool and closes it. The next operation reconnects; stateful endpoints receive a fresh handshake without the expired session ID. Failed tool calls are never replayed automatically because they may have side effects, such as sending a message.
  • Circuit breaker quarantine: A server that fails to connect is skipped until its quarantine ends, so an unreachable endpoint cannot spend the connect timeout on every generation turn. The quarantine starts at GUILD_MCP_FAILURE_COOLDOWN_MS (default 5 minutes) and grows with consecutive failures, capping at 36 times the base (3 hours by default). A transport answering 401 or 403 records the failure as unauthorized; anything else is unreachable.
  • Auto-disable: Each failure extends a streak persisted in failing_since and failure_reason, so restarts do not reset it. Once a streak is 24 hours old, the next failure sets is_enabled = false and the MCP panel explains why. A successful connect, or an administrator enabling or disabling the server, clears the streak.
  • OAuth sign-in: A registration added with a blank token whose server answers 401 can sign in through the MCP authorization flow (src/utils/mcp/oauth/). Two fetchers split that work. Authorization-server discovery and client registration use createMcpOAuthDiscoveryFetch (strict SSRF gate, HTTPS only, no redirects, byte and time caps), while MCP traffic stays on createGuildMcpFetch. The SDK transport gets no authProvider, because the SDK would send authorization-server calls through the origin-confined transport fetch, and widening that fetch would let the access token leave the MCP origin.
  • Token audience: The authorization and token requests name the server’s protected-resource identifier as resource, so the issued token is bound to that server. createGuildMcpFetch attaches it per request after the origin check, because a pooled connection outlives short access tokens. The code exchange and every refresh go only to the token endpoint recorded at discovery (src/utils/mcp/oauth/accessToken.ts).
  • Reauthorization: A 401 gets one refresh and retry inside the fetch. A second 401, a grant without a refresh token, a blocked refresh, or a 403 insufficient_scope marks the grant needs_reauthorization; a scope challenge also records the union of requested and challenged scopes for the next sign-in. Tool gathering skips such rows without extending the failure streak, so auto-disable never fires for an expired grant, and the panel offers Reconnect.
  • In-flight sign-ins: The PKCE verifier, recorded issuer, and pending code live in an in-memory map for 10 minutes (src/utils/mcp/oauth/signIn.ts). A restart or a second bot instance loses them and the admin starts again; a multi-instance deployment needs shared storage first.
  • Tool name snapshots: Discovered tool names persist in last_discovered_tool_names as a bounded display cache, capped at 100 names and 128 Unicode characters per name (mcpToolSnapshot.ts). Live listTools() output remains authoritative for invocation.
  • Collision and replacement policy: All built-in names and aliases remain reserved even when disabled. A selected url_fetcher may supply fetch_url; a selected web_search may supply web_search. Other collisions are hidden and cannot intercept dispatch. Unfamiliar discovered names retain their schemas. Prompt macros, deliberate tool filtering, follow-up handling, and execution use the same family selection. Each family permits one enabled registration. Disable the old selection before enabling its replacement. Existing duplicate family selections or duplicate tool names across servers are refused until the administrator resolves the ambiguity. An unavailable selected service never silently switches to the native tool. With no selection, native search and fetch remain the defaults.

Selected remote fetch/search servers are administrator-trusted network runtimes. Where a discovered schema identifies a URL field, the bot checks that input through the existing fetch destination gate before calling the server. This preflight cannot control the server’s DNS, redirects, subresources, or download bytes. The transport controls above govern the bot’s connection to that server. The MCP registration panel explains this distinction before registration.

Web access tools enforce security boundaries before network dispatch:

  • web_search: Tries Brave, SearXNG, OpenRouter Search, and DuckDuckGo in that order and returns the first success. Engine-specific tool names stay hidden from the model. On OpenRouter turns with an active credential, openrouterEngine.ts makes a separate search request using the active model and the Exa web plugin. It returns citation excerpts and URLs for fetch_url, without fetching the result pages. OpenRouter also handles news, papers, and code calls after an earlier engine fails, with category-specific query hints and progress notices. It skips image, video, files, and music; if their configured engines fail, the model receives the localized category-unavailable result. The request shares WEB_SEARCH_TIMEOUT_MS and turn cancellation; credentials go only to OpenRouter’s fixed endpoint, with redirects refused. Search uses OpenRouter credits even for free chat models. Failed text searches or unusable text citations fall through to DuckDuckGo without a Discord error embed. DuckDuckGo reads its fixed HTML results endpoint through the strict pinned fetcher, with a deadline and a 1 MiB cap, and sends no credentials. It unwraps result links without following them. A challenge, rate limit, or changed markup returns a failure, so the model cannot mistake a blocked search for zero results. Search results are untrusted model input. Search dispatch shares one progress-notice state across engine attempts, so the conversation and thought log each receive at most one progress notice per call. Error embeds remain separate. Turn cancellation ends the chain before another engine starts; an engine’s own timeout can still fall through.
  • fetch_url: The primary URL-reading tool. Requests to private, localhost, or reserved IP ranges are blocked in every RUN_ENV unless FETCH_URL_ALLOW_PRIVATE_NETWORK=true is set. A hostname that does not resolve is refused in every environment.
  • Crawl4AI engine: Optional. Setting CRAWL4AI_BASE_URL puts it ahead of safe_http, but only where private-network fetching is permitted. Its external browser resolves DNS, follows redirects, and loads subresources itself, so the bot’s preflight and metadata denylist do not cover those requests. Responses are read under a byte limit and checked for the expected shape before use. See Setup: Crawl4AI.
  • Cloud metadata protection: Cloud instance metadata and link-local ranges (169.254.0.0/16, IPv6 link-local, AWS IMDS) are blocked unconditionally by src/utils/security/cloudMetadata.ts. This check cannot be bypassed by configuration.
  • Safe HTTP engine: The safe_http engine validates and pins DNS, follows a bounded number of redirects (revalidating each hop), limits downloaded byte counts, and converts HTML to Markdown.
  • Result storage: Successful content is stored in ToolResult.data.summary so streaming tool loops serialize the text into provider turn history.

Participant-aware tools resolve user and persona targets through ParticipantTargetIndex attached to the prepared participant context; see Participants. The index is constructed from typed identities and purpose-filtered aliases, avoiding ambiguous display-name searches.

For image analysis, analyze_image resolves attachments from the target Discord message. When that message is a text-only reply, it checks the directly referenced message. The tool enforces the VISION_ANALYSIS_TIMEOUT_MS deadline (default 60 seconds) across media downloads and vision provider inference.

cross_channel_message excludes full-privacy authors from peek results and boomerang history before formatting their messages. Verified proxy messages use the sender account’s privacy setting, matching ordinary conversation history. Both paths stop at the most recent refresh marker.

The block_user and unblock_user tools allow personas to manage interpersonal boundaries, gated by the user_blocking_enabled permission in /config:

  • Scoping: Blocks persist in persona_user_blocks, keyed by server, local persona ID, and target user ID.
  • Channel permission check: Before saving a block, block_user checks whether the bot can view the channel and send embeds. Missing permissions abort the save and return an error to the model.
  • Confirmation notices: Mutes, blocks, and unblocks use the shared notice router, so notice visibility, verbosity, and thought-log privacy checks apply. A Minimal server shows the title alone and mirrors the full card to the thought log.
  • Dialogue filtering: Active blocks suppress the target user’s messages in dialogue history, replacing them with a system notice: [System: ... sent a message but is currently blocked by you ...]. The notice is an English system injection, mirroring reminder and join injections. The pointer to unblock_user lives in the active-blocks knowledge item instead, inside {{if tool:unblock_user}}, so it appears only on turns that expose the tool. Blocking suppresses dialogue turns and reply annotations without deleting stored long-term memories or documents.

The update_user_info tool (src/tools/functionCalls/updateUserInfoTool.ts) updates personal profile and addressing preferences:

  • Target resolution: Resolves target users across mentions, names, aliases, and Discord IDs using resolveUserTarget. Omitted targets default to the invoking user; wildcards (all, everyone) are rejected.
  • Field scoping:
    • Persona-specific fields (nickname, prefix, suffix) persist in user_persona_naming_preferences, scoped to the active persona lineage.
    • Global fields (gender_identity, pronouns, addressing_style, timezone_offset) persist in user_personalization_configs, shared across all personas.
  • Clearing values: Fields listed in the clear array are removed. Submitting empty string values folds into clearing. Cleared prefixes or suffixes persist as explicit suppressions (none), preventing lower-precedence defaults from resurfacing.
  • Affix deduplication: When a submitted nickname contains an already-resolved prefix or suffix, redundant affixes are stripped by comparing against resolved values.
  • Privacy and permissions: Mutations require user_info_updates_enabled. Restrictive privacy levels block updates but permit clearing existing values.
  • Confirmation notices: Successful updates use the shared notice router even on turns that suppress progress notices, matching memory and task updates. Notice visibility, verbosity, and thought-log privacy checks still apply.

The review_capabilities tool inspects system configuration dynamically:

  • Inventory sources: Gathers active tools from ToolRegistry runtime availability and registered commands from cached loadCommandData().
  • Privilege boundaries: General members receive safe reports detailing commands and feature states. Detailed reports containing credential presence, rotation pool counts, internal identifiers, and system-prompt metadata require the requesting member to hold ManageGuild permissions. Invocations without verified member context fail closed with the redacted report.

NovelAI text models do not support tool calling. The provider returns an empty tool list, and prompt inspection omits tool schemas. NovelAI image generation remains available through its dedicated commands and through tools called by other text providers.

  • src/tools/toolRegistry.ts: Central tool registry and execution dispatcher.
  • src/tools/availability.ts: Availability filtering, model capability checks, and backend slot validation.
  • src/tools/assembly.ts: Per-turn dynamic schema assembly.
  • src/utils/mcp/guildMcpManager.ts: Guild MCP connection pooling, circuit breaking, and dispatch.
  • src/utils/mcp/oauth/signIn.ts: MCP sign-in start, callback confirmation, and code exchange.
  • src/tools/functionCalls/updateUserInfoTool.ts: Structured user profile and naming preferences.
  • src/tools/fetchUrl/dispatcher.ts: URL fetch security and safe HTTP engine dispatch.
  • src/tools/webSearch/duckduckgoEngine.ts: Native DuckDuckGo request, parsing, and failure classification.
  • src/utils/security/cloudMetadata.ts: Cloud instance metadata denylist.