Skip to content

Commit ee53fe4

Browse files
Python: Correction of MCP image type conversion in _mcp.py (#2901)
* Correction of MCP image type conversion in _mcp.py * Added a new overload to the init function of the DataContent() type of the Agent Framework, edited the test case to correctly test the usage of the data and uri fields while using DataContent() * Fixed tests related to the changes of the DataContent type, added testing for both string and byte representations
1 parent 3cd805f commit ee53fe4

File tree

3 files changed

+49
-10
lines changed

3 files changed

+49
-10
lines changed

python/packages/core/agent_framework/_mcp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ def _mcp_type_to_ai_content(
152152
case types.ImageContent() | types.AudioContent():
153153
return_types.append(
154154
DataContent(
155-
uri=mcp_type.data,
155+
data=mcp_type.data,
156156
media_type=mcp_type.mimeType,
157157
raw_representation=mcp_type,
158158
)

python/packages/core/agent_framework/_types.py

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,10 @@ class DataContent(BaseContent):
925925
image_data = b"raw image bytes"
926926
data_content = DataContent(data=image_data, media_type="image/png")
927927
928+
# Create from base64-encoded string
929+
base64_string = "iVBORw0KGgoAAAANS..."
930+
data_content = DataContent(data=base64_string, media_type="image/png")
931+
928932
# Create from data URI
929933
data_uri = "data:image/png;base64,iVBORw0KGgoAAAANS..."
930934
data_content = DataContent(uri=data_uri)
@@ -986,11 +990,38 @@ def __init__(
986990
**kwargs: Any additional keyword arguments.
987991
"""
988992

993+
@overload
994+
def __init__(
995+
self,
996+
*,
997+
data: str,
998+
media_type: str,
999+
annotations: Sequence[Annotations | MutableMapping[str, Any]] | None = None,
1000+
additional_properties: dict[str, Any] | None = None,
1001+
raw_representation: Any | None = None,
1002+
**kwargs: Any,
1003+
) -> None:
1004+
"""Initializes a DataContent instance with base64-encoded string data.
1005+
1006+
Important:
1007+
This is for binary data that is represented as a data URI, not for online resources.
1008+
Use ``UriContent`` for online resources.
1009+
1010+
Keyword Args:
1011+
data: The base64-encoded string data represented by this instance.
1012+
The data is used directly to construct a data URI.
1013+
media_type: The media type of the data.
1014+
annotations: Optional annotations associated with the content.
1015+
additional_properties: Optional additional properties associated with the content.
1016+
raw_representation: Optional raw representation of the content.
1017+
**kwargs: Any additional keyword arguments.
1018+
"""
1019+
9891020
def __init__(
9901021
self,
9911022
*,
9921023
uri: str | None = None,
993-
data: bytes | None = None,
1024+
data: bytes | str | None = None,
9941025
media_type: str | None = None,
9951026
annotations: Sequence[Annotations | MutableMapping[str, Any]] | None = None,
9961027
additional_properties: dict[str, Any] | None = None,
@@ -1006,8 +1037,9 @@ def __init__(
10061037
Keyword Args:
10071038
uri: The URI of the data represented by this instance.
10081039
Should be in the form: "data:{media_type};base64,{base64_data}".
1009-
data: The binary data represented by this instance.
1010-
The data is transformed into a base64-encoded data URI.
1040+
data: The binary data or base64-encoded string represented by this instance.
1041+
If bytes, the data is transformed into a base64-encoded data URI.
1042+
If str, it is assumed to be already base64-encoded and used directly.
10111043
media_type: The media type of the data.
10121044
annotations: Optional annotations associated with the content.
10131045
additional_properties: Optional additional properties associated with the content.
@@ -1017,7 +1049,9 @@ def __init__(
10171049
if uri is None:
10181050
if data is None or media_type is None:
10191051
raise ValueError("Either 'data' and 'media_type' or 'uri' must be provided.")
1020-
uri = f"data:{media_type};base64,{base64.b64encode(data).decode('utf-8')}"
1052+
1053+
base64_data: str = base64.b64encode(data).decode("utf-8") if isinstance(data, bytes) else data
1054+
uri = f"data:{media_type};base64,{base64_data}"
10211055

10221056
# Validate URI format and extract media type if not provided
10231057
validated_uri = self._validate_uri(uri)

python/packages/core/tests/core/test_mcp.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,21 @@ def test_mcp_call_tool_result_to_ai_contents():
7575
mcp_result = types.CallToolResult(
7676
content=[
7777
types.TextContent(type="text", text="Result text"),
78-
types.ImageContent(type="image", data="data:image/png;base64,xyz", mimeType="image/png"),
78+
types.ImageContent(type="image", data="xyz", mimeType="image/png"),
79+
types.ImageContent(type="image", data=b"abc", mimeType="image/webp"),
7980
]
8081
)
8182
ai_contents = _mcp_call_tool_result_to_ai_contents(mcp_result)
8283

83-
assert len(ai_contents) == 2
84+
assert len(ai_contents) == 3
8485
assert isinstance(ai_contents[0], TextContent)
8586
assert ai_contents[0].text == "Result text"
8687
assert isinstance(ai_contents[1], DataContent)
8788
assert ai_contents[1].uri == "data:image/png;base64,xyz"
8889
assert ai_contents[1].media_type == "image/png"
90+
assert isinstance(ai_contents[2], DataContent)
91+
assert ai_contents[2].uri == "data:image/webp;base64,abc"
92+
assert ai_contents[2].media_type == "image/webp"
8993

9094

9195
def test_mcp_call_tool_result_with_meta_error():
@@ -183,7 +187,7 @@ def test_mcp_call_tool_result_regression_successful_workflow():
183187
mcp_result = types.CallToolResult(
184188
content=[
185189
types.TextContent(type="text", text="Success message"),
186-
types.ImageContent(type="image", data="data:image/jpeg;base64,abc123", mimeType="image/jpeg"),
190+
types.ImageContent(type="image", data="abc123", mimeType="image/jpeg"),
187191
]
188192
)
189193

@@ -218,7 +222,8 @@ def test_mcp_content_types_to_ai_content_text():
218222

219223
def test_mcp_content_types_to_ai_content_image():
220224
"""Test conversion of MCP image content to AI content."""
221-
mcp_content = types.ImageContent(type="image", data="data:image/jpeg;base64,abc", mimeType="image/jpeg")
225+
mcp_content = types.ImageContent(type="image", data="abc", mimeType="image/jpeg")
226+
mcp_content = types.ImageContent(type="image", data=b"abc", mimeType="image/jpeg")
222227
ai_content = _mcp_type_to_ai_content(mcp_content)[0]
223228

224229
assert isinstance(ai_content, DataContent)
@@ -229,7 +234,7 @@ def test_mcp_content_types_to_ai_content_image():
229234

230235
def test_mcp_content_types_to_ai_content_audio():
231236
"""Test conversion of MCP audio content to AI content."""
232-
mcp_content = types.AudioContent(type="audio", data="data:audio/wav;base64,def", mimeType="audio/wav")
237+
mcp_content = types.AudioContent(type="audio", data="def", mimeType="audio/wav")
233238
ai_content = _mcp_type_to_ai_content(mcp_content)[0]
234239

235240
assert isinstance(ai_content, DataContent)

0 commit comments

Comments
 (0)