Agent App MCP tool schema ignores Agent Soul app variables (required ones fail every tools/call)
还没有人认领这个 Issue。
评估
调研方向
Start in controllers/mcp/mcp.py at MCPAppApi._get_user_input_form and trace how tools/list builds its schema from the legacy row. Compare it with AppDefinitionQueryService.get_public_parameters and the Agent Soul projection in services/app_service.py; done means tools/list exposes the declared variables, types, and required flags while preserving the existing query field.
由索引模型根据 Issue 内容生成。
描述
Found while working on #42380 / #42383; this one is a different code path and is not fixed by that PR.
What happens
An Agent App can declare app variables on its Agent Soul (AgentSoulConfig.app_variables). When such an app is registered as an MCP server, tools/list never advertises them: the tool schema only has query. That part is observable today, independently of #42380.
The runtime does enforce those variables — AgentAppGenerator validates inputs against the Soul projection — so a required variable makes the call fail, and a schema-driven client has no way to discover the missing parameter:
{"jsonrpc": "2.0", "id": 1, "error": {"code": -32602, "message": "branch_id is required in input form"}}
On current main that error is masked by the call-path bug in #42380: tools/call fails earlier with "Agent App only supports streaming mode" (fixed by #42383). The schema gap is not masked — tools/list answers today, just without the declared variables.
Steps to reproduce
- Create an Agent App whose Soul declares
branch_id(text-input, required). Today this is done through the console API or DSL import; the composer has no app-variables module yet. - Register the app as an MCP server and call
tools/list— the schema lists onlyquery. - Once the call path is fixed (#42383), call the tool without
branch_id— the client gets theINVALID_PARAMSerror above.
A client that already knows the variable name can still pass it, because prepare_tool_arguments copies unknown arguments into inputs. The failure is that the schema never reveals it.
Where it comes from
controllers/mcp/mcp.py::_get_user_input_form resolves the form for every non-workflow mode from the legacy row:
features_dict = app.app_model_config.to_dict()
raw_user_input_form = features_dict.get("user_input_form", [])
An Agent App's legacy row is created empty (services/app_service.py, the AppMode.AGENT branch): its model, prompt and variables live in the bound Agent Soul. The runtime projects the Soul instead — AgentAppConfigManager fills user_input_form from agent_app_variables_to_user_input_form(agent_soul.app_variables) — so the variable really is required by the time the tool call runs.
The rest of the platform already resolves this through the app-definition layer:
| face | resolver | Soul variables visible |
|---|---|---|
service API GET /parameters |
app_definitions.get_public_parameters -> _get_public_agent_parameter_config (published Soul) |
yes |
webapp parameters (controllers/web/app.py) |
same | yes |
OpenAPI get app / input_schema |
controllers/openapi/_input_schema.py::resolve_app_config (legacy row) |
not reachable: SupportedAppType excludes the roster-owned agent type |
MCP tools/list |
controllers/mcp/mcp.py::_get_user_input_form (legacy row) |
no |
Executed evidence
Probing the real helpers (MCPAppApi._get_user_input_form, agent_app_variables_to_user_input_form, build_parameter_schema) and calling AgentAppGenerator._prepare_user_inputs directly, since a live tools/call on current main fails earlier for the reason above. Two DB-backed lookups are stubbed (the feature-row lookup, and the annotation-reply lookup inside AppModelConfig.to_dict):
1) what the Agent Soul declares, projected by the platform owner
[{"text-input": {"label": "branch_id", "variable": "branch_id", "required": true}}]
2) what the MCP controller reads for the same Agent App
raw form: []
3) the tool schema that reaches the MCP client
{"type": "object", "properties": {"query": {"type": "string", "description": "User Input/Question content"}}, "required": ["query"]}
4) the schema the client would get with the projected form
{"type": "object", "properties": {"query": {...}, "branch_id": {"description": "", "type": "string"}}, "required": ["query", "branch_id"]}
5) what the app requires when the tool call omits the variable
-> ValueError: branch_id is required in input form
Expected behavior
tools/list advertises the Soul variables with their types and required flags, matching what the runtime enforces.
Fix direction
For AppMode.AGENT, _get_user_input_form could resolve the form through the app-definition owner (AppDefinitionQueryService.get_public_parameters, which returns user_input_form from the published Soul) instead of reading the legacy row. Two things to keep in mind:
- that reader raises
AppDefinitionNotPublishedError/AppDefinitionUnavailableErrorwhen the Agent has no active snapshot; MCP should surface that rather than answering with an empty form (note the MCP route rendersMCPRequestErroras an opaque HTTP 500 today — pre-existing for every validation error in that controller); _convert_user_input_formalready handles the form keys the projection emits (text-input,paragraph,number,checkbox).
Open question
For Agent Apps today, app variables are validated and passed to input moderation, but they do not reach the agent: AgentAppRuntimeRequestBuilder sends agent_soul_prompt / user_prompt and no variable layer, and the Agent App runner does not render a prompt template with them. So fixing the schema makes the advertised contract match the enforced one, but does not by itself let callers influence the run. That part belongs to #41779 (tool_input_bindings); worth deciding whether the schema fix should wait for it or land first as a bug fix.
Related
#41779 asks for application-controlled values bound to MCP tool parameters — a larger feature (per-tool bindings plus a composer UI for declaring variables). This issue is narrower: the MCP tool schema is built from the wrong source, so the declared variables never reach the client.
- 主要语言
- TypeScript
- 星标
- 157k
- 派生
- 24.7k
- 平均合并
- 22 小时 32 分钟
- 30 天内合并 PR
- 611
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
langgenius/dify 的其他 Issue
-
Annotation Reply: a stored score threshold of 0.0 is silently replaced with 1, disabling the feature 未关闭
难度 2/5 1-3 小时 新手友好度 88/100
langgenius/dify#42639 · 1 条评论 · 1 个 reaction ·
-
难度 2/5 1-3 小时 新手友好度 70/100
langgenius/dify#42468 · 1 条评论 · 1 个 reaction ·
-
🐞 bug
难度 2/5 1-3 小时 新手友好度 86/100
langgenius/dify#42446 · 1 个 reaction ·
-
难度 2/5 1-3 小时 新手友好度 88/100
langgenius/dify#42355 · 1 条评论 · 1 个 reaction ·
-
难度 2/5 1-3 小时 新手友好度 88/100
langgenius/dify#42350 · 1 条评论 · 1 个 reaction ·
相似的 Issue
-
calcite-components needs triage refactor
难度 2/5 1-3 小时 新手友好度 75/100
Esri/calcite-design-system#15203 ·
-
难度 2/5 1-3 小时 新手友好度 91/100
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
难度 1/5 1 小时以内 新手友好度 95/100
-
难度 2/5 1-3 小时 新手友好度 78/100
Automattic/studio#4908 ·
-
难度 2/5 1-3 小时 新手友好度 90/100