mcpcompat drops entire tool/prompt result when it contains an embedded resource

オープン
#170 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

  • #171 @jhrozek による — マージされずにクローズ

評価

難易度
3/5
見積もり時間
1〜2日
初心者へのやさしさ
76/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
静か
技術スタック
go
領域
api

調査の方向性

EmbeddedResource と UnmarshalContent から始め、次に CallToolResult.UnmarshalJSON と issue に記載されている convertResult パスを追跡します。提供された wire payload で失敗を再現し、リソースのみのコンテンツと混在したツールコンテンツが、兄弟項目を失わずに正常にデコードされることを確認します。prompts/get パスも確認してください。

索引モデルが issue の本文から書いたものです。

説明

needs-triage

Summary

mcpcompat.EmbeddedResource has a Resource ResourceContents field where ResourceContents is a non-empty interface, and EmbeddedResource has no custom UnmarshalJSON. So json.Unmarshal of any "type":"resource" content item errors:

json: cannot unmarshal object into Go struct field EmbeddedResource.resource of type mcp.ResourceContents

Because CallToolResult.UnmarshalJSON (and the client's convertResult) propagate that error, any tool result — or prompt message — containing an embedded resource is lost in its entirety, including any sibling text/image parts in the same content array.

Impact

Discovered from a ToolHive vMCP MCP-conformance run (the mcp-go → mcpcompat swap). Two conformance scenarios regressed:

  • tools-call-embedded-resource → "No resource content found"
  • tools-call-mixed-content → "Expected multiple content items; Missing image content; Missing resource content"

The prompts/get path is affected too, since convertGetPromptResult also decodes content via mcp.UnmarshalContent.

Call chain

  1. client.CallTool converts the go-sdk result via the generic convertResult → jsonConvert.
  2. That runs CallToolResult.UnmarshalJSON, which calls UnmarshalContent per item.
  3. UnmarshalContent, case ContentTypeResource, does json.Unmarshal(data, &EmbeddedResource{}) — stdlib cannot populate the ResourceContents interface field → error.
  4. The error aborts the whole decode; convertResult discards the partially-decoded result.

ReadResource and GetPrompt were hand-written to avoid this exact trap (convertReadResourceResult maps concrete text/blob types explicitly); CallTool was left on the generic path.

Fix

Add a custom UnmarshalJSON to EmbeddedResource that resolves the polymorphic Resource field to a concrete TextResourceContents/BlobResourceContents (blob-precedence, mirroring convertReadResourceResult). UnmarshalContent is the single chokepoint backing both tools/call and prompts/get.

Repro

wire := []byte(`{"type":"resource","resource":{"uri":"file:///x.txt","mimeType":"text/plain","text":"body"}}`)
_, err := mcp.UnmarshalContent(wire)
// err: json: cannot unmarshal object into Go struct field EmbeddedResource.resource of type mcp.ResourceContents
主要言語
Go
スター
2
フォーク
6
平均マージ
23時間 44分
マージ済み PR(30日)
133

環境構築

このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

stacklok/toolhive-core のほかの issue

stacklok/toolhive-core の issue をすべて見る

似ている issue

Go の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。