Clarify registry tools vs native model tool binding (#20022)

## Intent

This is a small foundation cleanup for the tool architecture.

The main decision is: registry tools and native SDK/model tools are
different things.

- Registry tools have descriptors, schemas, catalog entries, and execute
through `ToolExecutorService`
- Native model tools are opaque AI SDK objects, bound directly into the
model `ToolSet`
- Surfaces still own their policy: chat, MCP, and workflow agents decide
what they expose

## What changed

- Removed `NATIVE_MODEL` from `ToolCategory`
- Kept `ToolRegistryService` focused on registry-backed tools only
- Moved native model tool binding through `NativeToolBinderService`
- Reused native binding from chat instead of duplicating
provider-specific web-search logic
- Kept MCP local execution exclusions in a dedicated constant
- Moved surface-specific constants into dedicated constant files

## What comes next

- Move hardcoded chat app preloads, like Exa web search, into
app/manifest metadata
- Decide a clearer policy for local runtime tools like code interpreter
and HTTP request
- Gradually document the three tool shapes: registry tools, native model
tools, and local runtime tools

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Félix Malfait <FelixMalfait@users.noreply.github.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
nitin
2026-04-24 20:48:19 +05:30
committed by GitHub
parent 251f5deab6
commit 6c1c0737b0
15 changed files with 115 additions and 183 deletions
@@ -0,0 +1,4 @@
export const MCP_EXCLUDED_TOOL_NAMES = new Set([
'code_interpreter',
'http_request',
]);
@@ -4,6 +4,7 @@ import { type ToolSet, zodSchema } from 'ai';
import { isDefined } from 'twenty-shared/utils';
import { JSON_RPC_ERROR_CODE } from 'src/engine/api/mcp/constants/json-rpc-error-code.const';
import { MCP_EXCLUDED_TOOL_NAMES } from 'src/engine/api/mcp/constants/mcp-excluded-tool-names.const';
import { MCP_PROTOCOL_VERSION } from 'src/engine/api/mcp/constants/mcp-protocol-version.const';
import { MCP_SERVER_INFO } from 'src/engine/api/mcp/constants/mcp-server-info.const';
import { MCP_SERVER_INSTRUCTIONS } from 'src/engine/api/mcp/constants/mcp-server-instructions.const';
@@ -40,8 +41,6 @@ import { type FlatWorkspace } from 'src/engine/core-modules/workspace/types/flat
import { SkillService } from 'src/engine/metadata-modules/skill/skill.service';
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
const MCP_EXCLUDED_TOOLS = new Set(['code_interpreter', 'http_request']);
@Injectable()
export class McpProtocolService {
constructor(
@@ -127,7 +126,7 @@ export class McpProtocolService {
...createGetToolCatalogTool(this.toolRegistry, workspace.id, roleId, {
userId: options?.userId,
userWorkspaceId: options?.userWorkspaceId,
excludeTools: MCP_EXCLUDED_TOOLS,
excludeTools: MCP_EXCLUDED_TOOL_NAMES,
}),
inputSchema: zodSchema(getToolCatalogInputSchema),
},
@@ -135,13 +134,13 @@ export class McpProtocolService {
...createLearnToolsTool(
this.toolRegistry,
toolContext,
MCP_EXCLUDED_TOOLS,
MCP_EXCLUDED_TOOL_NAMES,
),
inputSchema: zodSchema(learnToolsInputSchema),
},
[EXECUTE_TOOL_TOOL_NAME]: {
...createExecuteToolTool(this.toolRegistry, toolContext, {
excludeTools: MCP_EXCLUDED_TOOLS,
excludeTools: MCP_EXCLUDED_TOOL_NAMES,
}),
inputSchema: executeToolInputSchema,
},