/** * Server mode + tool profile configuration. * * Two independent axes decide which tools this MCP server instance publishes: * * 1. LOCALITY (MCP_SERVER_MODE=full|read-only|write-only) — which half of the * toolset this process can physically serve, enabling a hybrid deployment * where an Azure-hosted instance runs 'read-only' (search / analysis) and a * local Windows VM instance runs 'write-only' (file operations). * * 2. BREADTH (MCP_TOOL_PROFILE=full|core) — how much of the toolset is worth * advertising at all. Hosts stop sending the tool catalogue inline past a * limit (VS Code: ~100 tools across all servers) and switch the model to a * search-based tool surface, which costs a discovery round trip per tool it * needs (issue #825). 'core' publishes only the plan → discover → write → * build → verify loop; MCP_EXTRA_TOOLS adds individual tools back. * * isToolEnabled() is the predicate combining both for the ListTools filter. The * runtime call gate in toolHandler checks the same two parts one at a time, so * a refusal can name its reason. The one difference is axdb_sql: unpublished * while SQL is unconfigured, it still reaches its own handler, which answers * SQL_DISABLED with the way to configure it. */ export declare const LOCAL_TOOLS: Set; /** * The LOCAL_TOOLS that do read the symbol database after all, and so must NOT * inherit this set's exemption from the dbReady wait in toolHandler. * * "Local" answers a question about LOCALITY — can this run away from the K:\ drive * — and the dbReady exemption rode along on it as if it also meant "needs no * index". For two of them it does not: * * • get_workspace_info infers the model's prefix from symbols.getModelObjectNames * and reads getLastIndexedAt. Exempt, it had no timeout either: while the 2.5 GB * database opened, the tool a session STARTS with sat there with no answer and * no way to fail. Measured live 2026-09-07: 337.5 s, and 356 s / 424 s on two * earlier cold starts, where every other tool would have said "still loading, * retry" after 55 s. It also answered from the empty stub index when it did * return early — a configured prefix reported as if the model had taught it. * • update_symbol_index writes THROUGH context.symbolIndex (removeSymbolsByFile, * removeLabelsByFile). Before the swap that is the :memory: stub, so the call * reports a successful re-index of a database nobody will ever read. * * The rest (build, bp-check, systest, verify) genuinely only touch the filesystem * and the compiler, and keep the exemption. */ export declare const DB_BACKED_LOCAL_TOOLS: Set; /** * Tools exposed in EVERY server mode, bypassing the LOCAL_TOOLS partition, * because each spans both localities and gates unavailable actions at runtime: * - get_object_info: dispatches to bridge-backed types (local VM) and * SQLite-backed types (Azure read-only). * - labels: read actions (search/info) work on Azure; write actions * (create/rename) need K:\ and error clearly when unreachable. * - d365fo_file: generate works on Azure; create/modify/delete need K:\ and * error clearly when unreachable. */ export declare const ALWAYS_TOOLS: Set; /** * @deprecated Use LOCAL_TOOLS — kept temporarily so any external import doesn't break. * Will be removed in the next major release. */ export declare const WRITE_TOOLS: Set; /** * Server mode, resolved once at startup from MCP_SERVER_MODE env var. * - 'full' (default) – all tools registered (local development) * - 'read-only' – LOCAL_TOOLS excluded (Azure App Service deployment) * - 'write-only' – only LOCAL_TOOLS exposed (lightweight local companion) */ export type ServerMode = 'full' | 'read-only' | 'write-only'; export declare const SERVER_MODE: ServerMode; /** * Single source of truth for whether a tool is callable in a given server mode. * Used by BOTH the ListTools filter (mcpServer) and the runtime call gate * (toolHandler) so the advertised tool list and call-time enforcement can * never drift apart. ALWAYS_TOOLS bypass the LOCAL_TOOLS partition in every mode. */ /** * Retired tool names that still route, mapped to the tool their handler now * lives behind. * * Both gates below match on the NAME and run before toolHandler's switch, so a * retired name that is in neither CORE_TOOLS nor LOCAL_TOOLS is refused no * matter that its `case` is still there. Judge it as its replacement instead — * that is the tool the call reaches, so it is the tool whose locality and * profile membership actually apply. * * Verified live: before this, MCP_SERVER_MODE=write-only refused * `undo_last_modification`, a local file operation that was in LOCAL_TOOLS * until the folds. `get_method` keeps being refused there, because it resolves * to `get_object_info`, which needs the symbol index — same rule, opposite and * equally correct answer. */ export declare const RETIRED_TOOL_ROUTES: ReadonlyMap; /** The tool a call under this name actually reaches. */ export declare function resolveRetiredToolName(toolName: string): string; export declare function isToolAllowedInMode(mode: ServerMode, toolName: string): boolean; /** * The tools a full "understand it, write it, build it, verify it" D365FO * workflow actually walks through. Membership is positive and explicit so a new * tool has to be argued INTO the small profile rather than drifting in * (tests/utils/toolInventory.test.ts fails until every published tool is * classified). * * Excluded from 'core' — each is a specialist detour off the write loop, and * each stays one MCP_EXTRA_TOOLS entry (or MCP_TOOL_PROFILE=full) away: * - extension_info → extension-point analysis; search + get_object_info + * object_patterns carry the write path. * - analyze_code → code-quality/pattern review, not a step in creating code. * - validate_code → advisory X++ lint; run_bp_check and the compiler are the * authoritative gates and both stay in core. * - security_info → security-model inspection, its own kind of task. * - run_systest_class → test execution, a separate phase (and gated behind an * interactive console on most boxes). * * generate_object is kept IN core against the raw never-called list from the * audit: scaffolding is load-bearing for the create path. The database sync is * still in core too — it just travels as build_d365fo_project(dbSync) now, * because a table change is not finished until the DB is synchronised. */ export declare const CORE_TOOLS: Set; /** * Tool profile, resolved once at startup from MCP_TOOL_PROFILE. * - 'full' (default) – every tool this mode allows; nobody's setup changes * - 'core' – CORE_TOOLS (+ MCP_EXTRA_TOOLS) only */ export type ToolProfile = 'full' | 'core'; export declare const TOOL_PROFILE: ToolProfile; /** Parse a comma/space separated tool list (MCP_EXTRA_TOOLS) into a set. */ export declare function parseToolList(raw: string | undefined): Set; /** Non-core tools added back on top of the 'core' profile. Ignored when full. */ export declare const EXTRA_TOOLS: ReadonlySet; /** Whether a tool survives the breadth filter. 'full' keeps everything. */ export declare function isToolInProfile(profile: ToolProfile, toolName: string, extras?: ReadonlySet): boolean; /** * Whether a tool is published: locality first, then breadth, and axdb_sql only * once SQL is configured. Used by the ListTools filter (mcpServer); the runtime * call gate (toolHandler) applies the locality and breadth parts itself. */ export declare function isToolEnabled(toolName: string, mode?: ServerMode, profile?: ToolProfile, extras?: ReadonlySet): boolean; //# sourceMappingURL=serverMode.d.ts.map