Skip to content

Accept null for optional tool arguments that default to None - #71

Merged
call518 merged 1 commit into
call518:mainfrom
vishalhabib99:fix/optional-none-defaults
Oct 9, 2026
Merged

call518 merged 1 commit into
call518:mainfrom
vishalhabib99:fix/optional-none-defaults

Conversation

@vishalhabib99

Copy link
Copy Markdown
Contributor

What

41 optional arguments on 28 tools are annotated str / int / bool with a None default, e.g. get_table_list(database_name: str = None). FastMCP turns each into a schema that advertises "default": null on a field whose type excludes null:

{"default": null, "type": "string"}

A client that sends that advertised default back (some agent frameworks and OpenAPI bridges fill in every optional argument) gets a validation error, and the tool never runs.

Reproduction (fastmcp 3.2.0 from uv.lock, no database running)

get_table_list {}
  -> reached the tool: "Error retrieving table list: [Errno 61] Connect call failed"   # expected here

get_table_list {"database_name": null}
  -> 1 validation error for call[get_table_list]
     database_name  Input should be a valid string [type=string_type, input_value=None]

Calling every tool that has a null-default argument with those arguments set to null (required ones filled in): 28 of 29 rejected on main, 0 of 29 with this change.

Change

  • Annotate the 41 arguments as Optional[T], the style mcp_main.py already uses (get_prompt_template, main). Callers who omit them see no difference. The schema becomes {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null}.
  • tests/test_tool_schema_null_defaults.py: offline (no database), lists the tools and checks that every argument defaulting to null accepts null. It fails on main and passes here. Added to the unit-test step in test.yml.
  • I couldn't run the Docker-based suite locally (no Docker on this machine), so CI will be the first full run. The source change is type annotations only.
All 41 arguments
Tool Arguments
get_lock_monitoring database_name, granted, state, mode, locktype, username
get_current_database_info database_name
get_table_list database_name
get_table_schema_info table_name
get_database_schema_info schema_name
get_table_relationships table_name
get_pg_stat_statements_top_queries database_name
get_pg_stat_monitor_recent_queries database_name
get_table_size_info database_name
get_postgresql_config config_name, filter_text
get_index_usage_stats database_name
get_vacuum_analyze_stats database_name
get_table_bloat_analysis database_name, schema_name, table_pattern
get_database_bloat_overview database_name
get_autovacuum_status database_name, schema_name, table_pattern
get_autovacuum_activity database_name, schema_name
get_running_vacuum_operations database_name
get_vacuum_effectiveness_analysis database_name, schema_name
get_io_stats database_name
get_table_io_stats database_name
get_index_io_stats database_name
get_all_tables_stats database_name
get_user_functions_stats database_name
get_database_conflicts_stats database_name
get_wait_events database_name, wait_event_type
get_wal_summarizer_status database_name
get_async_io_status database_name
get_per_backend_io_stats database_name

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.

🤖 Generated with Claude Code

41 arguments on 28 tools were annotated `str`/`int`/... with a `None`
default. FastMCP advertises `"default": null` on them but types them
non-nullable, so a client that sends the advertised default gets a
validation error before the tool runs. Annotate them `Optional[T]`
(the style this file already uses) and add an offline schema test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vishalhabib99

Copy link
Copy Markdown
Contributor Author

Hi @call518, a gentle nudge on this when you have time. It still merges cleanly. The change is annotation-only (T | None on the 41 null-default arguments) plus a schema test, so calls that omit those arguments behave exactly as before. Glad to split it or trim the CI step if you'd prefer.

@call518

call518 commented Oct 9, 2026

Copy link
Copy Markdown
Owner

Thanks for the fix. I reproduced the validation error with an explicit null and confirmed that this change resolves it. The server changes are limited to 41 type annotations across 28 tools, preserving the existing logic and defaults.

All 169 unit tests passed with the Docker-dependent fixture excluded. The new schema test still inherits that fixture, so it is not fully offline under the normal pytest command. This is an existing test setup issue and can be addressed separately.

Although CI results are not available yet, I found no blocking issues. The fix is appropriately scoped and looks good to merge.

@call518
call518 merged commit 3354605 into call518:main Oct 9, 2026
2 checks passed
@call518

call518 commented Oct 9, 2026

Copy link
Copy Markdown
Owner

Thanks again for the contribution! This fix has been released in v3.4.0. It resolves explicit null argument validation failures across 28 tools.

@vishalhabib99

Copy link
Copy Markdown
Contributor Author

Thanks @call518 for reproducing it yourself and for getting it into v3.4.0 so quickly.

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