Skip to content

Exception raised when a nullable parameter is sent as an empty string in the Http request #601

Closed

Description

@aschabus

I have an MCP server that uses nullable types for its parameters. For example:

[McpServerTool(Name = "get_data"), Description("Get data from the store")]
public async Task<IEnumerable<ResponseType>> GetData(
   [Description("Number of records to return")] int? limit = null,
   [Description("Start date")] DateTime? from = null,

When I use streamable HTTP (for example, via MCP Inspector), the data is sent as an empty string, like this:

"arguments":{ "limit": null, "from":"2025-01-01"}

When I compile this with ModelContextProtocol and ModelContextProtocol.AspNetCore version "0.1.0-preview.12", everything works as expected. However, if I use version "0.2.0-preview.3", I get the following Exception:

System.Text.Json.JsonException: The JSON value could not be converted to System.Nullable`1[System.DateTime]

How can I get this working?

Activity

  1. aschabus commented on Jul 9, 2025

    @aschabus
    Author

    I also tested it with version "0.3.0-preview.1" and it fails as well.

  2. aschabus commented on Jul 9, 2025

    @aschabus
    Author

    There is one more thing which seems to be related.
    When the tools/list method is executed on the server, the server sends back default(T) for T?, even if the default value for the parameter is set to null for the parameter. In "0.1.0-preview.12", this works as expected.

  3. eiriktsarpalis commented on Jul 10, 2025

    @eiriktsarpalis
    Member

    I can't reproduce the issue from the inspector, however I noticed that we're producing invalid JSON schemas for Nullable<T> parameters which might be relevant to this issue. I filed dotnet/extensions#6596 to address this in the MEAI layer.

  4. added a commit that references this issue on Jul 11, 2025
  5. sarahecraddock commented on Jul 14, 2025

    @sarahecraddock

    Hi @eiriktsarpalis, I'm going to be taking over this issue from my teammate @aschabus (as he's on vacation for a couple of weeks). I just wanted to confirm that I've pulled down the changes in your PR and tested with out code and I'm still seeing the same unexpected behaviour, so this doesn't seem to resolve the issue.

    I've created a repro setup by making a couple of small tweaks to one of your samples in this repo (on a fork here). All the behaviour I'm going to mention I am repro-ing by running the sample (with my changes) and then spinning up MCP inspector to interact with the sample MCP server. I am seeing:

    • With a nullable parameter is defined in the tools file with no default value, when I hit /list/tools I'm seeing no default value returned but the parameter appears as required.
    • With a nullable parameter is defined in the tools file with a default null value, when I hit /list/tools I'm seeing the parameter correctly marked as not required but a default value returned from /list/tools and if I remove this default value and make it null then I can see an error in the logs which appears to be around it not liking that a null value has been provided.

    Are you able to repro the behaviour with this sample?

  6. eiriktsarpalis commented on Jul 14, 2025

    @eiriktsarpalis
    Member

    With a nullable parameter is defined in the tools file with no default value, when I hit /list/tools I'm seeing no default value returned but the parameter appears as required.

    This is by design. Whether a parameter is nullable is independent of whether it is optional (aka specifying a default value). You can have nullable parameters that are required or non-nullable parameters that are optional. See this article for more details.

    if I remove this default value and make it null then I can see an error in the logs which appears to be around it not liking that a null value has been provided.

    This is caused by the aforementioned bug that dotnet/extensions#6596 is fixing. Because the generated schemas for Nullable<T> parameters is being incorrectly advertised as non-nullable, the inspector interprets null inputs as "null" JSON strings resulting in the server (correctly) rejecting those inputs. You can verify that this is the case by changing your tool parameters from DateTime? (a struct) into string? (a reference type).

  7. sarahecraddock commented on Jul 14, 2025

    @sarahecraddock

    Is /list/tools returning a default (non-null) value when the default is explicitly configured as null in the tools file expected behaviour? We weren't seeing this behaviour with the previous version of the package, but are with the current version.

  8. eiriktsarpalis commented on Jul 14, 2025

    @eiriktsarpalis
    Member

    Is /list/tools returning a default (non-null) value when the default is explicitly configured as null in the tools file expected behaviour?

    No, this is part of the issue that is being fixed by dotnet/extensions#6596. A nullable parameter without a default value should be required and not emit a default value in the schema.

    We weren't seeing this behaviour with the previous version of the package, but are with the current version.

    Which version specifically? I find that surprising since the fixed issue existed since the release of MEAI. Perhaps there existed other confounding factors that made it work, sharing a minimal repro targeting the previous version would help me diagnose.

  9. sarahecraddock commented on Jul 14, 2025

    @sarahecraddock

    Version 0.1.0-preview.12

  10. sarahecraddock commented on Jul 14, 2025

    @sarahecraddock

    I've pushed the downgraded versions change to another branch of the same repro I mentioned earlier in my fork here, so you can see that the default values were being passed as null with the older MCP package versions.

    And when I bump the MCP package versions to latest with that same setup, and bump the Microsoft.Extensions.AI and Microsoft.Extensions.AI.Abstractions versions to a local build of the branch that you currently have PR-ed, I can still see default values being passed (even though the default should be null), unlike with the older versions of the MCP package.

  11. eiriktsarpalis commented on Jul 14, 2025

    @eiriktsarpalis
    Member

    I can confirm that default parameter handling works with 0.1.0-preview.12. This is because it references an older version of MEAI (9.4.3-preview.1.25230.7) which handled default values differently. Note that even with that earlier version the schema for nullable types was still being represented as non-nullable. All issues should be addressed once MCP incorporates the fix in dotnet/extensions#6596.

  12. sarahecraddock commented on Jul 14, 2025

    @sarahecraddock

    Sorry for all the backwards and forwards but I'm not sure if I'm missing something here...

    Even after I build the code from your branch (that you have the PR open for), I am still seeing that for DateTime fields, where the default is set explicitly as null, /list/tools is passing back non-null default values using both our repo code and also the repro with the sample from the MCP repo. Whereas with the previous version of the MCP package /list/tools is passing back null values as expected.

    Are you seeing different behaviour?

  13. eiriktsarpalis commented on Jul 14, 2025

    @eiriktsarpalis
    Member

    Can you try again using dotnet/extensions@9b624bc? It accounts for a corner case where JsonNumberHandling.AllowReadingFromString has been enabled.

  14. sarahecraddock commented on Jul 15, 2025

    @sarahecraddock

    Building from that commit ID I'm still seeing non-null default values being passed back (when a null default is configured) as well as it failing to parse a null value if I remove the default value and try to run the tool.

  15. eiriktsarpalis commented on Jul 15, 2025

    @eiriktsarpalis
    Member

    What is your expected vs. actual behavior? With the changes applied your repro is producing this schema

    {
       "type": "object",
       "properties": {
          "state": {
             "description": "The US state to get alerts for. Use the 2 letter abbreviation for the state (e.g. NY).",
             "type": "string"
          },
          "optionalParam1": {
             "description": "This parameter is nullable, i.e. should be optional, but is showing as required in the /list/tools response.",
             "type": [
                "string",
                "null"
             ],
             "format": "date-time"
          },
          "optionalInt": {
             "description": "Integer parameters always return a default value of 0 even when nullable.",
             "type": [
                "integer",
                "null"
             ]
          },
          "optionalParam2": {
             "description": "This parameter is nullable, i.e. should be optional, and a default value of null is specified but /list/tools now returns a default value.",
             "type": [
                "string",
                "null"
             ],
             "format": "date-time",
             "default": null
          }
       },
       "required": [
          "state",
          "optionalParam1",
          "optionalInt"
       ]
    }

    Which in the inspector is rendered as follows:

    Image

    Were you expecting something different?

  16. sarahecraddock commented on Jul 15, 2025

    @sarahecraddock

    What you've just described is the behaviour I am expecting, but I am not seeing that same behaviour when I run the repro I shared with:

    1. Microsoft.Extensions.AI & Microsoft.Extensions.AI.Abstractions packages built from the git commit you mentioned
    2. MCP packages v0.3.0-preview.2, which I believe is the newest version

    Are you seeing this behaviour when using the latest versions of the MCP packages?

  17. eiriktsarpalis commented on Jul 15, 2025

    @eiriktsarpalis
    Member

    MCP packages v0.3.0-preview.2, which I believe is the newest version

    I am building the MCP sdk off the commit in your repro, which is based off a slightly newer commit than the most recent release. I don't believe the subsequent changes should have any impact on the result.

  18. sarahecraddock commented on Jul 16, 2025

    @sarahecraddock

    Sorry, yes just re-tested and confirmed that it's now working as expected. Thanks for your help @eiriktsarpalis, I can't close the issue but feel free to close it off 😊

  19. alainkaiser commented on Sep 22, 2025

    @alainkaiser

    Just checked this issue. I found something which is most likely related to this, but specific to the Client (in this case Cursor). Maybe this is helpful:

    https://github.com/cursor/cursor/issues/3614

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

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions