You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Exception raised when a nullable parameter is sent as an empty string in the Http request #601
I have an MCP server that uses nullable types for its parameters. For example:
[McpServerTool(Name="get_data"),Description("Get data from the store")]publicasyncTask<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]
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.
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.
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?
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).
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.
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.
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.
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.
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.
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.
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"
]
}
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.
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 😊
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:
Activity
aschabus commentedon Jul 9, 2025
I also tested it with version "0.3.0-preview.1" and it fails as well.
aschabus commentedon Jul 9, 2025
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.
eiriktsarpalis commentedon Jul 10, 2025
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.Issue modelcontextprotocol#601 repro
sarahecraddock commentedon Jul 14, 2025
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:
Are you able to repro the behaviour with this sample?
eiriktsarpalis commentedon Jul 14, 2025
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.
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 interpretsnullinputs 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 fromDateTime?(a struct) intostring?(a reference type).sarahecraddock commentedon Jul 14, 2025
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.
eiriktsarpalis commentedon Jul 14, 2025
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.
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.
sarahecraddock commentedon Jul 14, 2025
Version 0.1.0-preview.12
sarahecraddock commentedon Jul 14, 2025
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.
eiriktsarpalis commentedon Jul 14, 2025
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.
sarahecraddock commentedon Jul 14, 2025
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?
eiriktsarpalis commentedon Jul 14, 2025
Can you try again using dotnet/extensions@9b624bc? It accounts for a corner case where
JsonNumberHandling.AllowReadingFromStringhas been enabled.sarahecraddock commentedon Jul 15, 2025
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.
eiriktsarpalis commentedon Jul 15, 2025
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:
Were you expecting something different?
sarahecraddock commentedon Jul 15, 2025
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:
Are you seeing this behaviour when using the latest versions of the MCP packages?
eiriktsarpalis commentedon Jul 15, 2025
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.
Update MEAI version and add regression test for modelcontextprotocol#601
sarahecraddock commentedon Jul 16, 2025
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 😊
Update MEAI version and add regression test for #601. (#628)
alainkaiser commentedon Sep 22, 2025
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