Skip to content

isToolTask denylists four real tool kinds, and its default branch admits any worker task as a tool #1584

Description

@ambiorix2099

Bug Description

Tool-call SSE events are wrong in both directions.

Four real tool task types are on the denylist, so no tool_call or tool_result event is
emitted for them: mcp, human, http/api and agent_tool tools. And the default branch
applies no effective filter, so a task that is not a tool emits a spurious pair. LIST_MCP_TOOLS,
the discovery task compiled into every MCP-using agent, is reported as a tool call named
list_mcp_tools.

So a consumer asking "was tool X called?" can get the wrong answer either way, and one asking
"was tool X not called?" gets a confident yes.

Root Cause

isToolTask (AgentEventListener.java:403-429) denylists twelve task types and treats
everything else as a tool.

Four denylist entries are real tool types. SUB_WORKFLOW (:419), HUMAN (:420), HTTP
(:422) and CALL_MCP_TOOL (:423). ToolCompiler.TYPE_MAP (ToolCompiler.java:140-153)
compiles real tools onto exactly those: http and api to HTTP, mcp to CALL_MCP_TOOL,
agent_tool to SUB_WORKFLOW, human to HUMAN. That is five SDK tool types across four task
types.

The default branch filters nothing. :427 returns
"SIMPLE".equals(taskType) || task.getTaskDefinition().isPresent(). The second disjunct reads as
"has a registered TaskDef", but MetadataMapperService.java:120-135 populates a TaskDef for any
task with a non-blank name, synthesizing an ad-hoc one when the DAO has none, under a gate
(:195-200) that never looks at task type. So it is true for essentially every task, and any
worker task type not explicitly denylisted is treated as a tool. LIST_MCP_TOOLS
(ai/.../MCPWorkers.java:58) is the case that bites today; the structural problem is that a new
worker task type becomes a tool by default, with nobody deciding.

Steps to Reproduce

Register an agent with one MCP tool and subscribe to its SSE stream.

  • A spurious tool_call/tool_result pair arrives named list_mcp_tools, from the compiled
    discovery task.
  • No tool_call event arrives for the MCP tool itself.

Expected Behavior

Allowlist on task type off TYPE_MAP, plus the worker case where an executed SIMPLE task's
taskType is the task's own name. Keep the existing _fw_ reference skip.

The allowlist needs a naming change alongside it. The enrich script replaces
inputParameters wholesale for three tool kinds, dropping the method key that
LLMHelper.java:407 had put there:

  • HTTP: JavaScriptBuilder.java:716 sets {http_request: hrequest}, and hrequest.method
    (:709) is the HTTP verb, not the tool name.
  • agent-tool: :740-747 sets prompt, session_id and __today__ only.
  • PULL_WORKFLOW_MESSAGES: :789 sets {batchSize} only.

MCP keeps method explicitly (:722), and HUMAN and the media/RAG kinds keep it by merging
tc.inputParameters. So with the denylist removed, resolveToolName (:439-461) would return
"http" for an HTTP tool and "sub_workflow" for an agent tool via its :455-456 branch. The
only key set for every dispatched tool is _agent_tool_name (:797), which resolveToolName
does not read. Resolution order should be inputData._agent_tool_name, then inputData.method,
then taskDefName.

Additional Notes

Verified against origin/main @ ce46eac33.

The allowlist alone does not fix HTTP or agent-as-tool, and that is a separate change.
onTaskCompleted (:99) is only reachable from TaskStatusListener.onTaskCompletedIfEnabled,
whose sole production call site is WorkflowExecutorOps.java:1115 inside updateTask(TaskResult),
the worker and task-update path. Async system tasks complete through AsyncSystemTaskExecutor
(:200, :245) and never notify the listener. HttpTask.isAsync() returns true
(http-task/.../HttpTask.java:229) and so does SubWorkflow.isAsync()
(core/.../tasks/SubWorkflow.java:328-330). So removing those two from the denylist changes
nothing on its own; they need a completion notification for async system tasks. CALL_MCP_TOOL
and HUMAN are unblocked by the denylist fix alone.

The dispatch script and tool-spec compilation are each duplicated
(JavaScriptBuilder.java:1430-1540, ToolCompiler.java:1597-1658), so a fix likely applies
twice. Marker-based identification was tried and reverted in 09842ab16, which is why this is
framed as a selection change rather than a new marker.

Four SDKs reconstruct tool calls from the polled task list and each has its own version of this
problem (filed against python-sdk, javascript-sdk, java-sdk, csharp-sdk). Getting
selection and naming right here is what makes a shared rule worth stating: a tool call is
identified by task type, and its name comes from inputData._agent_tool_name or taskDefName,
never from the task reference name.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions