Skip to content

fix: let Scroll's optional loc accept an explicit null - #69

Merged
Jeomon merged 1 commit into
CursorTouch:mainfrom
vishalhabib99:fix/optional-args-accept-null
Oct 12, 2026
Merged

Jeomon merged 1 commit into
CursorTouch:mainfrom
vishalhabib99:fix/optional-args-accept-null

Conversation

@vishalhabib99

Copy link
Copy Markdown
Contributor

Summary of Changes

Annotate Scroll's loc, which defaults to None, as list[int] | None instead of list[int], and add a regression test.

Why

FastMCP turned loc: list[int] = None into {"type": "array", "items": {"type": "integer"}, "default": null}: a schema that advertises a null default its own type rejects. Some agent frameworks fill every optional argument with its advertised default, so they send "loc": null and the call fails validation before the tool runs. With list[int] | None, the schema becomes anyOf: [array, null] and null means "scroll at the current mouse position", as the tool description already says. This was the only tool argument in __main__.py with that pattern; the others already use T | None.

Testing

uv sync --extra dev
uv run pytest tests/test_tool_schemas.py   # 2 failed on main, 2 passed with this change
uv run pytest                              # 378 passed, 1 failed (test_screenshot_fallback, fails the same way on main)
uv run ruff check src/macos_mcp/__main__.py tests/test_tool_schemas.py   # clears RUF013 on the changed line; no new findings

The new test lists the tools through an in-memory fastmcp.Client, checks that every argument with a null default allows null, and calls Scroll with loc: null. Desktop and WatchDog are stubbed, so nothing touches the real desktop.

Reproduction

Before (in-memory client, desktop stubbed):

schema loc: {"default": null, "items": {"type": "integer"}, "type": "array"}
ToolError: 1 validation error for call[scroll_tool]
loc
  Input should be a valid list [type=list_type, input_value=None, input_type=NoneType]

After:

schema loc: {"anyOf": [{"items": {"type": "integer"}, "type": "array"}, {"type": "null"}], "default": null}
result: Scrolled vertical down by 1 wheel times.

Additional Notes

Found with mcp-doctor's none_default_type check, then reproduced as above. If useful, it can scan the rest of the server with no install: open a scan request.

Scroll's `loc` defaulted to None but was typed `list[int]`, so FastMCP
advertised {"type": "array", "default": null}: a default its own type
rejects. A client that sends the advertised default (`"loc": null`) got
a validation error before the tool ran. Annotate it `list[int] | None`.

Add a regression test that lists the tools through an in-memory client
and checks every argument with a null default accepts null, plus a call
to Scroll with `loc: null` against a stubbed desktop.
@Jeomon
Jeomon merged commit ccebebd into CursorTouch:main Oct 12, 2026
@vishalhabib99

Copy link
Copy Markdown
Contributor Author

Thanks for merging this, @Jeomon!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants