Skip to content

fix: Hugging Face MCP calls are not dynamic - #305

Closed
cunninghamcard-bit wants to merge 1 commit into
masterfrom
fix/hugging-face-mcp-calls-are-not-dynamic
Closed

cunninghamcard-bit wants to merge 1 commit into
masterfrom
fix/hugging-face-mcp-calls-are-not-dynamic

Conversation

@cunninghamcard-bit

@cunninghamcard-bit cunninghamcard-bit commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Before: Hugging Face do_generate marked every mcp_call / mcp_list_tools tool-call and tool-result dynamic: true. @ai-sdk/huggingface 2.0.62 sets no dynamic flag on them.

After: the four parts carry no dynamic flag, as upstream. Without a local tool of that name, generate_text now returns the MCP call marked invalid (NoSuchTool), which is what the AI SDK's parseToolCall does for a provider-executed call that is not dynamic.

Found while porting the upstream Hugging Face tests (#303, case doGenerate › basic text response › should handle MCP tools with annotations). Part of the #276 list. First of three stacked Hugging Face fixes; the next two are the tool-result and stream-error PRs.

How it works today

HuggingFaceResponsesModel::do_generate                 aimux-providers/src/huggingface/responses.rs:163   // upstream: HuggingFaceResponsesLanguageModel.doGenerate  huggingface-responses-language-model.ts
└─ build_generate_content(&response)                   responses.rs:933                                   // upstream: for (const part of response.output) :220
   ├─ "mcp_call"                                        responses.rs:1013                                  // upstream: case 'mcp_call' :264
   │  ├─ RawToolCall { provider_executed: true, dynamic: Some(true) }   :1026                              //   { type: 'tool-call', providerExecuted: true }   no dynamic
   │  └─ ToolResult { dynamic: Some(true) }            :1037                                              //   { type: 'tool-result' }                          no dynamic
   └─ "mcp_list_tools"                                  responses.rs:1043                                  // upstream: case 'mcp_list_tools' :284
      ├─ RawToolCall { dynamic: Some(true) }           :1051
      └─ ToolResult { dynamic: Some(true) }            :1062
generate_text → parse_tool_call                         aimux-core/src/parse_tool_call.rs:160              // upstream: parseToolCall
   provider_executed && dynamic → skip the tool lookup  :168   → valid call although no local tool exists

What this PR changes

build_generate_content                                 responses.rs:933
├─ "mcp_call"        RawToolCall / ToolResult { dynamic: None }      :1026 / :1037   ← upstream :264-281
└─ "mcp_list_tools"  RawToolCall / ToolResult { dynamic: None }      :1051 / :1062   ← upstream :284-301
generate_text → parse_tool_call                         parse_tool_call.rs:160
   provider_executed, not dynamic, no local tool → returned as an invalid call (NoSuchTool), as upstream parseToolCall

Tests: should_handle_mcp_tools_with_annotations (tests/huggingface_responses_test.rs) asserted that the call passes Core validation without local tools; it now expects the invalid call. The full upstream case is in #303.

Docs: CHANGELOG Fixed.

Type of change

  • Bug fix (non-breaking)
  • New feature (non-breaking)
  • Breaking change
  • New provider
  • New / updated binding
  • Docs / RFC

Checklist

  • cargo +1.99 fmt -p aimux-providers -- --check passes
  • cargo +1.99 clippy -p aimux-providers --lib --tests -- -D warnings passes
  • cargo +1.99 test -p aimux-providers --test huggingface_responses_test passes (32)
  • scripts/check_provider_boundaries.sh passes
  • Updated relevant docs (CHANGELOG)
  • Commit messages follow Conventional Commits

Notes for reviewers

A caller that relied on the MCP call passing validation without a local tool now gets an invalid tool call, the same result the AI SDK gives.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5

…ce@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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
cunninghamcard-bit added a commit that referenced this pull request Oct 20, 2026
The three behaviour fixes now live in their own PRs (#305, #306, #307);
this branch stacks on the last one. responses.rs and CHANGELOG.md are the
same on both sides; the old test file keeps the markers and takes the
two aimux tests the fix PRs added.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
cunninghamcard-bit pushed a commit that referenced this pull request Oct 20, 2026
The test branch now stacks on the fix PRs #305-#307, which add two
aimux-own tests at the end of tests/huggingface_responses_test.rs. That
file is already merged into
src/huggingface/tests/responses/responses_language_model.rs, so the two
tests land there at the same places.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0116HPMoqaLfFr1TyqDq1hrW

Copy link
Copy Markdown
Contributor Author

Folded into #307 together with the other Hugging Face fixes, so each vendor has one fix PR. This change is commit 26cf0a83 there. Closing this one; the branch stays as it is.


Generated by Claude Code

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.

1 participant