Skip to content

Issue #41 : fix(tools): keep null branch in registered outputSchema - #46

Open
Shreyavemana wants to merge 8 commits into
nitrocloudofficial:developfrom
Shreyavemana:fix/output-schema-keep-null
Open

Shreyavemana wants to merge 8 commits into
nitrocloudofficial:developfrom
Shreyavemana:fix/output-schema-keep-null

Conversation

@Shreyavemana

@Shreyavemana Shreyavemana commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Registered output schemas lose the null branch of Optional fields. A tool that returns None for an X | None field produces a result that a client validating against the advertised outputSchema rejects.

Observed

A tool declares an output model with Optional fields (str | None = None, int | None = None) and returns None for them. A client that validates structuredContent against the advertised outputSchema fails the call with errors such as:

data/<field> must be string
data/<field> must be integer

Cause

In nitrostack/core/app.py:

  • inspector_friendly_schema (via _unwrap_nullable_schema) rewrites anyOf: [T, null] to plain T. Its docstring says this is so MCP Inspector can render form fields.
  • tool_json_schema applies the same rewrite to the output schema, so the registered outputSchema no longer allows null.

Repro

Run on main (0.4.0):

from typing import Optional
from pydantic import BaseModel
from nitrostack.core.app import tool_json_schema

class Out(BaseModel):
    maybe: Optional[str] = None
    n: Optional[int] = None

print(Out.model_json_schema()["properties"])
print(tool_json_schema(Out)["properties"])

Pydantic emits anyOf: [{type: string}, {type: null}] for maybe. tool_json_schema returns:

{'maybe': {'type': 'string', 'title': 'Maybe'}, 'n': {'type': 'integer', 'title': 'N'}}

NitroTestingModule.call_tool returns the null value without validating it against the schema, so the in-process harness does not catch this.

Expected

The registered outputSchema keeps the null branch, because it describes what the tool returns. The Inspector rewrite is only needed for input forms.

Workaround

Remove output_schema from @tool.

Proposal

  1. Apply inspector_friendly_schema to input schemas only.
  2. Add a test that registers an output model with an Optional field and checks the schema still accepts null.

manish-wekan and others added 8 commits September 10, 2026 18:05
…ase PR.

Keep pyproject.toml and uv.lock in sync, skip a bump while a version is still unpublished, and only hit the PyPI environment when an upload will actually run.
Bring the bump/publish workflows in line with develop (via main) so the PR can merge.
…ure/release-automation

Automate version bumps and PyPI publishes from main
@Shreyavemana
Shreyavemana marked this pull request as draft October 14, 2026 12:01
@Shreyavemana

Shreyavemana commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator Author

The fix makes tool_json_schema keep the null option for Optional fields in output schemas, so tools can safely return None. Input schemas still use the Inspector rewrite, and all regression tests passed successfully (the new tests created - 5 along with the all the test cases - (818 total, 0 failed)).

@Shreyavemana
Shreyavemana marked this pull request as ready for review October 15, 2026 10:34
@manish-wekan

Copy link
Copy Markdown
Collaborator

Install the entire nitrostack-py package from output-schema-keep-null branch, either locally or in /tmp/<name_of_dir>/
then import that package
and test it against #42

#42
If the tests pass, add the test report as comments. here.

@Shreyavemana

Copy link
Copy Markdown
Collaborator Author

Verification of the proposed fix (fix/output-schema-keep-null, 416fe98)

Changes: Removed 'inspectore_friendly_schema' from output - schema processing and added regression tests.

Installation and test environment

Cloned the branch into a temporary directory, created a fresh virtual environment, and installed the package using pip install <repo>.

The targeted tests were run from outside the repository, and the import resolved to venv\Lib\site-packages\nitrostack, confirming that the installed package was being tested rather than the source checkout.

Results:

  • 31 tests passed against the installed package, covering output schemas, input schemas, and MCP contracts.
  • 818 tests passed in the source checkout.
  • End-to-end test passed: The original main branch fails to validate nullable output, while the fix branch correctly preserves nullability in the advertised JSON Schema.
  • Input behavior unchanged: Optional fields are still handled correctly for Inspector form rendering.

Conclusion: The fix resolves the nullable output-schema issue, and all reported tests passed in their respective environments.

@Shreyavemana
Shreyavemana changed the base branch from main to develop October 23, 2026 14:38

This branch has not been deployed

No deployments
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