Description
MarkItDownMcpReader (Microsoft.Extensions.DataIngestion.MarkItDown)
- Failed conversions are silently returned as document content. When the tool reports a failure, it returns a
CallToolResult with IsError = true and the error message in Content. ConvertToMarkdownAsync returns the first text block without checking IsError, so the error string becomes the "markdown" body of the document. Downstream this produces a single bogus chunk and the ingestion is reported as successful.
Reproduction Steps
Using Microsoft.Extensions.DataIngestion.MarkItDown 10.7.0-preview.1.26309.5 against a MarkItDown MCP server (mcp/markitdown, --http):
var reader = new MarkItDownMcpReader(new Uri("http://localhost:3001/mcp"));
// Any input the server cannot convert makes the tool return IsError=true with the error in Content.
// Simplest reproducible case: a path the server cannot resolve.
var doc = await reader.ReadAsync(new FileInfo("does-not-exist-or-unconvertible.docx"), ct);
// No exception is thrown. doc has a single text element whose value is the tool's error string, e.g.:
// "Error executing tool convert_to_markdown: [Errno 2] No such file or directory: '...'"
// (verified directly: CallToolResult.IsError == true, and the message is carried in Content as text)
Expected behavior
When the tool returns IsError = true, the reader should throw (or otherwise surface a failure) instead of returning the error text as document content.
Actual behavior
The error message string is returned as the document body; no exception; the failure is invisible to callers and logs.
Relevant current code (MarkItDownMcpReader.ConvertToMarkdownAsync):
Dictionary<string, object?> parameters = new() { ["uri"] = dataContent.Uri }; // always a data: URI
var result = await client.CallToolAsync("convert_to_markdown", parameters, cancellationToken);
// no check of result.IsError before returning the first text block
foreach (var content in result.Content)
if (content.Type == "text" && content is TextContentBlock textBlock)
return textBlock.Text;
Regression?
Not a regression — Microsoft.Extensions.DataIngestion.MarkItDown is preview (10.7.0-preview.1.26309.5); behavior present since the reader was introduced.
Known Workarounds
Bypass MarkItDownMcpReader for the conversion call: use ModelContextProtocol directly to invoke convert_to_markdown, check CallToolResult.IsError, then round-trip the returned Markdown back through MarkItDownMcpReader's stream overload so the internal MarkdownParser still builds the IngestionDocument. Works, but reimplements the reader's core.
Configuration
- .NET 9 (SDK 9.0.315), Windows 11 x64.
Microsoft.Extensions.DataIngestion.MarkItDown 10.7.0-preview.1.26309.5, Microsoft.Extensions.AI 10.7.0, ModelContextProtocol.Core 1.2.0.
- MarkItDown MCP server:
mcp/markitdown 1.8.1, --http (Streamable HTTP).
- Not configuration-specific.
Other information
Proposed fixes:
IsError (pure bug, no API change): in ConvertToMarkdownAsync, if result.IsError == true, throw an InvalidOperationException containing the returned text.
Description
MarkItDownMcpReader(Microsoft.Extensions.DataIngestion.MarkItDown)CallToolResultwithIsError = trueand the error message inContent.ConvertToMarkdownAsyncreturns the first text block without checkingIsError, so the error string becomes the "markdown" body of the document. Downstream this produces a single bogus chunk and the ingestion is reported as successful.Reproduction Steps
Using
Microsoft.Extensions.DataIngestion.MarkItDown10.7.0-preview.1.26309.5against a MarkItDown MCP server (mcp/markitdown,--http):Expected behavior
When the tool returns
IsError = true, the reader should throw (or otherwise surface a failure) instead of returning the error text as document content.Actual behavior
The error message string is returned as the document body; no exception; the failure is invisible to callers and logs.
Relevant current code (
MarkItDownMcpReader.ConvertToMarkdownAsync):Regression?
Not a regression —
Microsoft.Extensions.DataIngestion.MarkItDownis preview (10.7.0-preview.1.26309.5); behavior present since the reader was introduced.Known Workarounds
Bypass
MarkItDownMcpReaderfor the conversion call: useModelContextProtocoldirectly to invokeconvert_to_markdown, checkCallToolResult.IsError, then round-trip the returned Markdown back throughMarkItDownMcpReader's stream overload so the internalMarkdownParserstill builds theIngestionDocument. Works, but reimplements the reader's core.Configuration
Microsoft.Extensions.DataIngestion.MarkItDown10.7.0-preview.1.26309.5,Microsoft.Extensions.AI10.7.0,ModelContextProtocol.Core1.2.0.mcp/markitdown1.8.1,--http(Streamable HTTP).Other information
Proposed fixes:
IsError(pure bug, no API change): inConvertToMarkdownAsync, ifresult.IsError == true, throw anInvalidOperationExceptioncontaining the returned text.