Skip to content

fix(workspace): honor an explicit max_live_stateful_mcps of 0 - #2741

Merged
qbc2016 merged 2 commits into
agentscope-ai:mainfrom
Lesereingrape:fix/zero-stateful-mcp-cap
Nov 10, 2026
Merged

qbc2016 merged 2 commits into
agentscope-ai:mainfrom
Lesereingrape:fix/zero-stateful-mcp-cap

Conversation

@Lesereingrape

Copy link
Copy Markdown
Contributor

Closes #2738

AgentScope Version

2.0.8 (src/agentscope/_version.py), branch cut from main @ 226daf0, Python 3.12.11.

Description

WorkspaceBase.__init__ fell back with or:

self.max_live_stateful_mcps = max_live_stateful_mcps or max(
    _DEFAULT_MAX_LIVE_STATEFUL_MCPS,
    2 * len([m for m in self.default_mcps if m.is_stateful]),
)

0 is falsy, so an explicit max_live_stateful_mcps=0 was indistinguishable from "argument not given" and was replaced by the derived default. The one value that means "retain no stateful MCP instance for another scope" could not be configured, while every other value is passed straight through — including -1, which is meaningless but survives. That asymmetry is what makes it a bug rather than an undocumented choice.

The change derives a cap only when the argument is None; an explicit value is used as given, and the constructor docstring now says so.

_enforce_mcp_capacity needs no change: with a cap of 0 it immediately stops sparing other scopes, and when every live instance belongs to the caller it logs the existing "exceeding max_live_stateful_mcps" warning and lets the caller through — i.e. the documented "the requesting scope always gets its full set" behaviour is preserved.

Design alternatives considered:

  • Reject 0 (or non-positive values) with a ValueError. Rejected: the argument is typed int, -1 already works, and raising would add a new failure mode without removing the asymmetry.
  • Keep the fallback and document "0 means the derived default". Rejected: the docstring already frames derivation as the None case, and a silent override of an explicitly passed cap is the surprising reading.

How to test

RED — the new test against the unpatched package (a main checkout shadowing the installed one), which fails on behaviour rather than on a mock:

PYTHONPATH=/path/to/base/checkout/src python -m pytest tests/workspace_local_test.py -k zero_stateful_cap -q
# E AssertionError: {'cap=0': 40, 'cap=1': 1, 'cap=None': 40} != {'cap=0': 0, 'cap=1': 1, 'cap=None': 40}

GREEN — this branch:

python -m pytest tests/workspace_local_test.py -q
# 36 passed
python -m pytest tests/compress_context_test.py tests/service_workspace_files_test.py \
    tests/service_workspace_status_test.py tests/workspace_skill_archive_test.py -q
# 74 passed, 2 subtests passed

Lint: black --line-length=79, flake8 --extend-ignore=E203, mypy and pylint were run at the versions and with the exact arguments pinned in .pre-commit-config.yaml; all four are clean on both changed files. pre-commit run --all-files itself could not run in this environment (the hook environments need to be downloaded), and the Docker / E2B / Daytona / Kubernetes workspace modules were not executed locally — both are left to the CI runs on this PR.

Checklist

Please check the following items before code is ready to be reviewed.


Authorship disclosure: this PR was written autonomously by an AI agent, and no human read the diff before submission. The measurements above were taken by that agent on the machine described and are reproducible with the commands given; CONTRIBUTING.md §2 was followed in that the change is atomic (2 commits, +44/−4) and keeps the repository's conventions.

The constructor used `max_live_stateful_mcps or max(...)`, so an
explicit 0 was discarded and replaced by the derived default, making
"no stateful instance is retained for other scopes" unconfigurable.
Derive only when the argument is None.
The review guide asks for assertions that compare an entire data
structure, so the zero/one/None expectations are checked as a single
table instead of three separate scalars.

@qbc2016 qbc2016 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@github-actions github-actions Bot removed the pr/awaiting-review Waiting for maintainers to review this PR label Nov 10, 2026
@qbc2016 qbc2016 closed this Nov 10, 2026
@qbc2016 qbc2016 reopened this Nov 10, 2026
@github-actions github-actions Bot added the pr/awaiting-review Waiting for maintainers to review this PR label Nov 10, 2026
@qbc2016
qbc2016 merged commit 4ea302a into agentscope-ai:main Nov 10, 2026
14 of 17 checks passed
zuizhuai pushed a commit to zuizhuai/agentscope that referenced this pull request Nov 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr/awaiting-review Waiting for maintainers to review this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: max_live_stateful_mcps=0 is silently replaced by the derived default (40)

2 participants