Agent App MCP tool schema ignores Agent Soul app variables (required ones fail every tools/call)
まだ誰も着手していません。
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 68/100
調査の方向性
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分
- マージ済み PR(30日)
- 611
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- 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 件 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
langgenius/dify#42468 · コメント 1 件 · リアクション 1 件 ·
-
🐞 bug
難易度 2/5 1〜3時間 初心者へのやさしさ 86/100
langgenius/dify#42446 · リアクション 1 件 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
langgenius/dify#42355 · コメント 1 件 · リアクション 1 件 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
langgenius/dify#42350 · コメント 1 件 · リアクション 1 件 ·
langgenius/dify の issue をすべて見る
似ている 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