Summary
session/new fails with Invalid params for any ACP agent whose schema requires mcpServers to be present, because an empty MCP server list is coerced to None and then dropped during serialization.
Hit this trying to run Devin CLI (devin acp) as a provider: custom ACP agent. Every run fails at pool init.
Reproduce
# agents.yml
agents:
devin:
type: acp
provider: custom
command: devin
args: ["acp"]
auto_approve: true
$ agentpool run devin "hello" --config agents.yml
...
File ".../acp/client/connection.py", line 104, in new_session
resp = await self._conn.send_request("session/new", dct)
acp.exceptions.RequestError: Invalid params
Error: Failed to initialize agent pool
Probing devin acp directly with raw JSON-RPC shows what the agent actually wants:
So the field must be present as an empty array; it may not be omitted.
Root cause
src/acp/agent/acp_agent_api.py coerces an empty sequence to None:
# line 97 (new_session) and line 113 (load_session)
mcp_servers=list(mcp_servers) if mcp_servers else None,
[] is falsy, so it becomes None. connection.new_session() then serializes with
exclude_none=True, which drops mcpServers entirely — producing a request the agent rejects.
Because line 113 has the same expression, session/load is affected too, so session resume
would fail against the same agents.
Fix
Distinguish "no list supplied" from "empty list":
mcp_servers=list(mcp_servers) if mcp_servers is not None else [],
Verified: patching line 97 alone makes it work end to end —
$ agentpool run devin "Reply with exactly: ACP-FIX-ALONE-OK and nothing else." --config agents.yml
devin: ACP-FIX-ALONE-OK
Related, but not required
src/agentpool/agents/acp_agent/acp_agent.py lines 375 and 698 pass mcp_servers or None,
which has the same empty-list-to-None intent. I tested reverting those to the original
or None while keeping only the acp_agent_api.py fix, and it still works — so they're
harmless once the API layer is correct. You may still want them for clarity.
Why it may be worth fixing beyond my case
ACP leaves mcpServers optionality to the agent, and agents disagree: some accept omission,
some require an empty array, and at least one other client codebase I looked at documents an
agent that rejects any entry in the list but accepts []. Sending [] rather than omitting
satisfies all three shapes, so it looks strictly safer than the current behaviour.
Environment
|
|
| agentpool |
2.9.18 (PyPI) |
| Python |
3.14.6 |
| OS |
macOS (darwin arm64) |
| agent |
Devin CLI 3000.2.17, devin acp |
Happy to open a PR with the one-line change if useful.
Summary
session/newfails withInvalid paramsfor any ACP agent whose schema requiresmcpServersto be present, because an empty MCP server list is coerced toNoneand then dropped during serialization.Hit this trying to run Devin CLI (
devin acp) as aprovider: customACP agent. Every run fails at pool init.Reproduce
Probing
devin acpdirectly with raw JSON-RPC shows what the agent actually wants:So the field must be present as an empty array; it may not be omitted.
Root cause
src/acp/agent/acp_agent_api.pycoerces an empty sequence toNone:[]is falsy, so it becomesNone.connection.new_session()then serializes withexclude_none=True, which dropsmcpServersentirely — producing a request the agent rejects.Because line 113 has the same expression,
session/loadis affected too, so session resumewould fail against the same agents.
Fix
Distinguish "no list supplied" from "empty list":
Verified: patching line 97 alone makes it work end to end —
Related, but not required
src/agentpool/agents/acp_agent/acp_agent.pylines 375 and 698 passmcp_servers or None,which has the same empty-list-to-
Noneintent. I tested reverting those to the originalor Nonewhile keeping only theacp_agent_api.pyfix, and it still works — so they'reharmless once the API layer is correct. You may still want them for clarity.
Why it may be worth fixing beyond my case
ACP leaves
mcpServersoptionality to the agent, and agents disagree: some accept omission,some require an empty array, and at least one other client codebase I looked at documents an
agent that rejects any entry in the list but accepts
[]. Sending[]rather than omittingsatisfies all three shapes, so it looks strictly safer than the current behaviour.
Environment
devin acpHappy to open a PR with the one-line change if useful.