anthropics / anthropics/anthropic-sdk-csharp
MEAI adapter: HostedMcpServerTool requests are always rejected
- Lingua principale
- C#
- Stelle
- 322
- Fork
- 105
- Merge medio
- 1g 6h
- PR unite (30g)
- 9
Descrizione
## Environment
- `Anthropic` 12.44.0
- `Microsoft.Extensions.AI.Abstractions` 10.9.0
- .NET 10
## Repro
Any `HostedMcpServerTool` passed through the beta `AsIChatClient`:
```csharp
var client = new AnthropicClient(new ClientOptions { ApiKey = apiKey }).Beta.AsIChatClient();
var options = new ChatOptions
{
ModelId = "claude-haiku-4-5",
MaxOutputTokens = 1024,
Tools = [new HostedMcpServerTool("microsoft_learn", "https://learn.microsoft.com/api/mcp")]
};
var response = await client.GetResponseAsync(
[new ChatMessage(ChatRole.User, "What tools are available?")], options);
```
## Actual
```
Anthropic.Exceptions.AnthropicBadRequestException: Status Code: BadRequest
{"type":"error","error":{"type":"invalid_request_error","message":"MCP server 'mcp' is defined but not referenced by any `mcp_toolset` in `tools`."}}
```
Note the server is reported as `'mcp'` even though it was configured as `microsoft_learn`.
## Cause
Three issues in the `HostedMcpServerTool` branch of the beta tool mapping
(`AnthropicBetaClientExtensions`):
1. **Wrong name property.** The server definition is built with `Name = mcp.Name` — but
`HostedMcpServerTool.Name` (the `AITool.Name` override) is hardcoded to the literal `"mcp"`
in Microsoft.Extensions.AI 10.9; the configured server name is `ServerName`, which the
mapping never reads. Every server serializes as `"name": "mcp"`, so multiple servers also
collide.
2. **No `mcp_toolset` is emitted.** The mapping adds the server to `mcp_servers` and opts into
the `mcp-client-2025-11-20` beta, but never adds a `BetaMcpToolset` to `tools`. That API
shape requires every `mcp_servers` entry to be referenced by exactly one MCPToolset
([docs — validation rules](https://platform.claude.com/docs/en/agents-and-tools/mcp-connector#validation-rules)),
so the request is rejected unconditionally.
3. **Deprecated allowlist shape.** `AllowedTools` is mapped to `tool_configuration` on the
server definition, which is deprecated under `mcp-client-2025-11-20` — the allowlist now
belongs in the toolset's `default_config`/`configs`
([migration guide](https://platform.claude.com/docs/en/agents-and-tools/mcp-connector#migration-guide)).
The unit tests pin the broken output rather than catching it: `GetResponseAsync_WithHostedMcpServerTool`
asserts (against a verbatim mock) that the request contains `"name": "mcp"` with no toolset, and
`GetResponseAsync_WithMultipleHostedMcpServerTools` asserts both servers serialize as `"name": "mcp"` —
so CI passes while every live call fails.
## Expected
```csharp
case HostedMcpServerTool mcp:
(betaHeaders ??= []).Add("mcp-client-2025-11-20");
// The wire-level server name must be the configured ServerName — AITool.Name is the
// fixed tool-type identifier "mcp" and would collide across multiple servers.
(mcpServers ??= []).Add(new() { Name = mcp.ServerName, Url = mcp.ServerAddress });
// mcp-client-2025-11-20 requires every mcp_servers entry to be referenced by exactly
// one mcp_toolset in `tools`; tool_configuration on the server definition is deprecated.
// AllowedTools maps to the documented allowlist pattern: disabled by default, listed
// tools explicitly enabled.
(createdTools ??= []).Add(
mcp.AllowedTools is { Count: > 0 } allowedTools
? new BetaMcpToolset(mcp.ServerName)
{
DefaultConfig = new() { Enabled = false },
Configs = allowedTools.ToDictionary(
static tool => tool,
static _ => new BetaMcpToolConfig { Enabled = true }),
}
: new BetaMcpToolset(mcp.ServerName)
);
break;
```
This makes `HostedMcpServerTool` usable end-to-end through `AsIChatClient`, matching the
`AsAITool` docs' guidance that the portable MEAI tool types are preferred over raw tool unions.
One migration note: callers currently working around this by passing `HostedMcpServerTool`
*plus* a hand-wrapped `BetaMcpToolset` (via `AsAITool`) for the same server would then send two
toolsets referencing one server, which the API also rejects — the fix may want to skip emitting
a toolset when one already exists for that `ServerName`, or call this out as a breaking note.
The test expectations in `AnthropicClientBetaExtensionsTests` need updating alongside, e.g.:
```diff
"mcp_servers": [{
- "name": "mcp",
+ "name": "my-mcp-server",
"type": "url",
"url": "https://mcp.example.com/server"
- }]
+ }],
+ "tools": [{
+ "type": "mcp_toolset",
+ "mcp_server_name": "my-mcp-server"
+ }]
```
Guida per i contributori
Apri la guida per i contributori
Valutazione
Questa issue non è ancora stata valutata.