From 26cf0a834ecdf82bc67dda746f7b7c2b8657f3aa Mon Sep 17 00:00:00 2001 From: cunninghamcard-bit Date: Tue, 20 Oct 2026 06:46:48 +0000 Subject: [PATCH] fix(huggingface): MCP calls are not dynamic matches @ai-sdk/huggingface@2.0.62 doGenerate pushes mcp_call and mcp_list_tools as provider-executed tool-calls and tool-results without a dynamic flag (huggingface-responses-language-model.ts:264-301). aimux set dynamic: true on all four parts. Without a local tool of that name, Core now returns the call marked invalid, as parseToolCall does. Tests: should_handle_mcp_tools_with_annotations (tests/huggingface_responses_test.rs) now expects the invalid call. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5 --- CHANGELOG.md | 4 ++++ aimux-providers/src/huggingface/responses.rs | 8 ++++---- aimux-providers/tests/huggingface_responses_test.rs | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e03fed7f..54c0da76f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1148,6 +1148,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Hugging Face MCP calls (`mcp_call`, `mcp_list_tools`) and their results are + no longer marked `dynamic`, as in `@ai-sdk/huggingface` 2.0.62. Without a + local tool of that name, `generate_text` now returns the call marked invalid + (`NoSuchTool`), as the AI SDK's `parseToolCall` does. - `default_providers()`, `openai()` and `anthropic()` no longer panic on an invalid `OPENAI_BASE_URL` / `ANTHROPIC_BASE_URL`. The variable is still read when the provider is created, as the AI SDK does; an invalid value now diff --git a/aimux-providers/src/huggingface/responses.rs b/aimux-providers/src/huggingface/responses.rs index d965c10bc..da79fada1 100644 --- a/aimux-providers/src/huggingface/responses.rs +++ b/aimux-providers/src/huggingface/responses.rs @@ -1023,7 +1023,7 @@ fn build_generate_content(response: &Value) -> Result, AiMu tool_name: name.to_string(), input, provider_executed: Some(true), - dynamic: Some(true), + dynamic: None, provider_metadata: None, })); @@ -1034,7 +1034,7 @@ fn build_generate_content(response: &Value) -> Result, AiMu result: output.clone(), is_error: None, preliminary: None, - dynamic: Some(true), + dynamic: None, provider_metadata: None, })); } @@ -1048,7 +1048,7 @@ fn build_generate_content(response: &Value) -> Result, AiMu tool_name: "list_tools".to_string(), input: json!({ "server_label": server_label }).to_string(), provider_executed: Some(true), - dynamic: Some(true), + dynamic: None, provider_metadata: None, })); @@ -1059,7 +1059,7 @@ fn build_generate_content(response: &Value) -> Result, AiMu result: json!({ "tools": tools }), is_error: None, preliminary: None, - dynamic: Some(true), + dynamic: None, provider_metadata: None, })); } diff --git a/aimux-providers/tests/huggingface_responses_test.rs b/aimux-providers/tests/huggingface_responses_test.rs index 8bb5c913e..e04347dd5 100644 --- a/aimux-providers/tests/huggingface_responses_test.rs +++ b/aimux-providers/tests/huggingface_responses_test.rs @@ -575,14 +575,16 @@ async fn should_handle_mcp_tools_with_annotations() { other => panic!("expected Source at [4], got {other:?}"), } + // Like upstream, the MCP call is not dynamic: without a local `search` + // tool, Core returns it marked invalid (parseToolCall's NoSuchToolError). let result = generate_text(&model, "Hello", GenerateTextOptions::default()) .await - .expect("dynamic MCP call should pass Core validation without local tools"); + .expect("an unknown tool is an invalid call, not an error"); let call = *result.tool_calls().first().expect("MCP tool call"); assert_eq!(call.tool_name, "search"); assert_eq!(call.provider_executed, Some(true)); assert_eq!(call.dynamic, Some(true)); - assert_eq!(call.invalid, None); + assert_eq!(call.invalid, Some(true)); assert_eq!(call.input, json!({ "query": "San Francisco tech events" })); }