QuickApps version
latest (0.8.0)
What steps will reproduce the bug?
- Register an MCP toolset in DIAL Core whose display name is
My MCP, exposing a tool named validate_sql.
- Build a Quick App with an orchestrator model (reproduced with
anthropic.claude-sonnet-4-20250514-v1:0) and a single dial-mcp tool set referencing that toolset. Note that dial-mcp tool sets carry no name in the manifest, so the prefix is taken from the toolset's display name in Core.
- QuickApps registers the tool as
My_MCP_validate_sql (mcp_tooling/_mcp_tool_initializer.py:268-275, sanitize_toolname("My MCP" + "_" + tool.name)), and this is the name sent to the model in tools[].
- Ask the app something that makes the model call the tool, from two different front ends:
- DIAL Chat, where the client sends no system message of its own;
- an embedded widget (in our case the DIAL Admin analytics query builder) that sends its own
system message as the first message of the request, e.g. {"role": "system", "content": "The user is building a query against ..."} followed by the user message.
From the widget the request fails within one orchestrator iteration; from Chat the same app usually succeeds.
What is the expected behavior?
- A tool name the model got wrong should not abort the whole request. QuickApps should return a tool-result error to the model (naming the tool that does not exist, and ideally the available names) so the model can retry on the next iteration, exactly as it recovers from any other tool failure. Optionally, an unprefixed name that matches exactly one registered tool by suffix (
*_<name>) should resolve to it.
- The system prompt configured in the app manifest should reach the model regardless of whether the client supplied a system message of its own. A client-supplied system message should be combined with the app's own prompt, not silently replace it.
What do you see instead?
The model calls validate_sql instead of My_MCP_validate_sql, and the entire request fails with HTTP 500:
DEBUG | quickapp.core.agent.orchestrator | Agent requests internal tool calls: ['validate_sql']
ERROR | quickapp.core.agent.tool_executor | Model requested unknown tool(s) ['validate_sql']; registered=['My_MCP_describe_entity', ..., 'My_MCP_validate_sql', 'internal_skills_read_skill', 'internal_timeawareness_current_timestamp']
WARNING | quickapp.core.agent.orchestrator | Orchestrator interrupted by Unknown tool(s) requested by the model: ['validate_sql'], saving state before re-raising
Traceback (most recent call last):
File "quickapp/core/application/_quick_app_completion.py", line 91, in chat_completion
File "quickapp/core/agent/orchestrator.py", line 157, in invoke
File "quickapp/core/agent/orchestrator.py", line 238, in _run_iteration
File "quickapp/core/agent/tool_executor.py", line 50, in execute
RuntimeError: Unknown tool(s) requested by the model: ['validate_sql']
INFO | quickapp.core.application._quick_app_completion | Request completed: outcome=failed, iterations=1, tool_calls=1
The user sees a generic 500 - Something went wrong with the execution of your request, and everything the model had already produced in that turn is lost.
Additional information
Two independent defects combine here.
1. ToolExecutor treats a wrong tool name as fatal. core/agent/tool_executor.py:42-49 raises RuntimeError for any name missing from its registry, which propagates out of the orchestrator loop and fails the request. Every other tool-level failure is reported back to the model and recovered from; this one is not, even though the model needs only to be told the name was wrong.
2. A client-supplied system message silently discards the app's configured system prompt. core/agent/_messages_transformers.py:29-33:
if len(messages) > 0 and messages[0].role != Role.SYSTEM:
return [Message(role=Role.SYSTEM, content=system_prompt)] + messages
return messages # client sent a system message -> the app's prompt is never added
When the first incoming message has role system, the prompt assembled from all PromptPartProviders — the manifest's system_prompt, plus every provider-contributed part — is dropped without a log entry. This is what makes the two front ends behave differently: from Chat the app's instructions are in play, from the widget none of them reach the model. This affects any Quick App called by a client that sends its own system message, not just this MCP case.
The tool list itself is not the problem: the names in tools[] and the names in ToolExecutor's registry come from the same open_ai_tool object (core/agent/agent_module.py:169-177, common/staged_base_tool.py:77-80), so they cannot diverge, and stream accumulation concatenates name fragments correctly (common/chat_completion_stream/tool_call.py:34-44). The wrong name is what the model produced. But the tool-name prefix is derived from a toolset display name owned by another resource in Core, so app authors cannot control it from the manifest and cannot make it stable — which is why the failure needs to be recoverable rather than fatal.
QuickApps version
latest (0.8.0)
What steps will reproduce the bug?
My MCP, exposing a tool namedvalidate_sql.anthropic.claude-sonnet-4-20250514-v1:0) and a singledial-mcptool set referencing that toolset. Note thatdial-mcptool sets carry nonamein the manifest, so the prefix is taken from the toolset's display name in Core.My_MCP_validate_sql(mcp_tooling/_mcp_tool_initializer.py:268-275,sanitize_toolname("My MCP" + "_" + tool.name)), and this is the name sent to the model intools[].systemmessage as the first message of the request, e.g.{"role": "system", "content": "The user is building a query against ..."}followed by the user message.From the widget the request fails within one orchestrator iteration; from Chat the same app usually succeeds.
What is the expected behavior?
*_<name>) should resolve to it.What do you see instead?
The model calls
validate_sqlinstead ofMy_MCP_validate_sql, and the entire request fails with HTTP 500:The user sees a generic
500 - Something went wrong with the execution of your request, and everything the model had already produced in that turn is lost.Additional information
Two independent defects combine here.
1.
ToolExecutortreats a wrong tool name as fatal.core/agent/tool_executor.py:42-49raisesRuntimeErrorfor any name missing from its registry, which propagates out of the orchestrator loop and fails the request. Every other tool-level failure is reported back to the model and recovered from; this one is not, even though the model needs only to be told the name was wrong.2. A client-supplied system message silently discards the app's configured system prompt.
core/agent/_messages_transformers.py:29-33:When the first incoming message has role
system, the prompt assembled from allPromptPartProviders — the manifest'ssystem_prompt, plus every provider-contributed part — is dropped without a log entry. This is what makes the two front ends behave differently: from Chat the app's instructions are in play, from the widget none of them reach the model. This affects any Quick App called by a client that sends its own system message, not just this MCP case.The tool list itself is not the problem: the names in
tools[]and the names inToolExecutor's registry come from the sameopen_ai_toolobject (core/agent/agent_module.py:169-177,common/staged_base_tool.py:77-80), so they cannot diverge, and stream accumulation concatenates name fragments correctly (common/chat_completion_stream/tool_call.py:34-44). The wrong name is what the model produced. But the tool-name prefix is derived from a toolset display name owned by another resource in Core, so app authors cannot control it from the manifest and cannot make it stable — which is why the failure needs to be recoverable rather than fatal.