Skip to content

fix(resources): return binary application/* files from repo:// resources as blobs - #3591

Open
mmporong wants to merge 1 commit into
github:mainfrom
mmporong:fix/repo-resource-binary-application-types
Open

mmporong wants to merge 1 commit into
github:mainfrom
mmporong:fix/repo-resource-binary-application-types

Conversation

@mmporong

Copy link
Copy Markdown

Summary

repo:// repository content resources returned every application/* response as text, so binary files served as application/octet-stream (PDFs, archives, etc.) came back as corrupted text. This PR returns only text-based types as text and everything else as a blob.

Why

No existing issue. In RepositoryResourceContentsHandler the response type switch is case strings.HasPrefix(mimeType, "text"), strings.HasPrefix(mimeType, "application"):. raw.githubusercontent.com serves binary files with Content-Type: application/octet-stream (checked with curl -sI on a PDF in mozilla/pdf.js and a .tar.xz in python/cpython), while text files come back as text/plain; charset=utf-8. So repo://owner/repo/contents/file.pdf returned the bytes in Text. Invalid UTF-8 is replaced with U+FFFD when the result is JSON-encoded, so the original file cannot be recovered. get_file_contents already classifies content with an allow-list of text types.

What changed

  • pkg/github/repository_resource.go: add isTextMIMEType. text/* stays text. Under application/, only application/json, application/xml, application/javascript, application/*+json and application/*+xml are text (the same set get_file_contents uses, plus application/javascript); other application/* types such as application/octet-stream, application/pdf and application/zip are returned as Blob. Other top-level types keep their previous handling (e.g. image/svg+xml stays a blob). A Content-Type with a malformed parameter (e.g. text/plain; charset) is still classified by its media type, as before.
  • pkg/github/repository_resource_test.go: add Test_repositoryResourceContentsHandler_ApplicationTypes with eight cases: octet-stream, pdf and zip come back as blobs; application/json; charset=utf-8, application/xml, application/vnd.api+json and text/plain; charset come back as text; image/svg+xml stays a blob. On main the three binary cases fail (Should be empty, but was %PDF-1.4...) and the other five pass; with this change all eight pass.

MCP impact

  • No tool or API changes
  • Tool schema or behavior changed
  • New tool added

No tool changes; the repo:// resource read behavior changed: binary application/* files are returned in blob instead of text.

Prompts tested (tool changes only)

  • Not applicable (no tool changes); covered by the unit tests above.

Security / limits

  • No security or limits impact
  • Auth / permissions considered
  • Data exposure, filtering, or token/size limits considered

Tool renaming

  • I am renaming tools as part of this PR (e.g. a part of a consolidation effort)
    • I have added the new tool aliases in deprecated_tool_aliases.go
  • I am not renaming tools as part of this PR

Note: if you're renaming tools, you must add the tool aliases. For more information on how to do so, please refer to the official docs.

Lint & tests

  • Linted locally with ./script/lint
  • Tested locally with ./script/test

Lint: ran the same checks as script/lint directly rather than the script itself: gofmt -l is clean and golangci-lint run ./... with v2.14.0 (the version pinned in script/lint) reports 0 issues. Tests: go test ./pkg/github/ and go vet ./pkg/github/ pass with Go 1.27.3; the full ./script/test (go test -race ./...) was not run.

Docs

  • Not needed
  • Updated (README / docs / examples)

AI assistance: this change was prepared with Claude (claude-opus-5-5), which found the bug, wrote the fix and tests, ran the checks, and drafted this description.

…ces as blobs

RepositoryResourceContentsHandler returned every application/* response
as text. raw.githubusercontent.com serves binary files such as PDFs and
archives as application/octet-stream, so reading them through a
repo:// resource put the raw bytes into the text field, where invalid
UTF-8 is replaced during JSON encoding and the content cannot be
recovered.

Return text only for text/* and for text-based application types
(application/json, application/xml, application/javascript, and
application/*+json or application/*+xml), matching how
get_file_contents classifies content, and return everything else as a
blob. Other top-level types keep their previous handling (for example
image/svg+xml stays a blob), and a Content-Type with a malformed
parameter is still classified by its media type.

Assisted-by: Claude:claude-opus-5-5
@mmporong
mmporong requested a review from a team as a code owner October 26, 2026 10:21
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