feat(attachments): add work package attachment tools#24
Conversation
Add support for OpenProject work package attachments, which were not covered by any existing tool. Client (src/client.py): - list_work_package_attachments, get_attachment, delete_attachment - download_attachment: follows the content redirect manually so the API key is not forwarded to external object storage - upload_attachment: multipart/form-data without the JSON Content-Type header so aiohttp sets the multipart boundary Tools (src/tools/attachments.py): list_attachments, get_attachment, download_attachment (inline text or save_path), upload_attachment, delete_attachment. Registered in src/server.py (49 -> 54 tools). Docs + tests: README tool list (#41-#45) and offline test_attachments.py.
|
Thanks for this, @i-matveev — the attachment tools fill a real gap (list / get / download / upload / delete were all missing), and the details are well handled: not forwarding the API key through the storage redirect, and letting aiohttp own the multipart boundary. For transparency on where it has landed: LS Instruments maintains an actively developed downstream fork of this project (https://github.com/LS-Instruments/openproject-mcp-server) that we're advancing for day-to-day agentic project management on OpenProject. Given the current upstream review cadence we're maintaining that fork independently for now. As part of that we've integrated your change, adapted to our fork's modular layout and covered by an offline unit test plus a live end-to-end integration test (upload → list → get → download round-trip → delete against OpenProject 17.5.1). Your authorship is preserved: the commit carries you as co-author and you're credited in our NOTICE. Everything stays under the project's MIT license. Integrated into our We'd gladly contribute this back upstream if review capacity returns, and you're welcome to draw from our fork in the meantime. Thanks again. |
Adds 5 tools for work package attachments (previously unsupported):
list_attachments, get_attachment, download_attachment, upload_attachment,
delete_attachment, plus the backing client methods.
- download_attachment follows the /attachments/{id}/content storage redirect
manually so the API key is not forwarded to external object storage; text
content is returned inline, binary content is saved via save_path.
- upload_attachment sends multipart/form-data without the JSON Content-Type
header so aiohttp sets the multipart boundary.
Ported from upstream PR AndyEverything#24 by @i-matveev,
adapted to this fork's conventions. Verified end to end against an ephemeral
OpenProject 17 (upload -> list -> get -> download round-trip -> delete) and with
an offline unit test for the input model and formatting helpers.
Closes #13
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: i-matveev <i-matveev@users.noreply.github.com>
Summary
Adds support for work package attachments, which were not covered by any existing tool. This makes it possible to list, read, download, upload and delete files attached to OpenProject work packages.
New tools (5)
list_attachmentsget_attachmentdownload_attachmentsave_path)upload_attachmentdelete_attachmentTool count: 49 → 54.
Implementation notes
src/client.py— new client methodslist_work_package_attachments,get_attachment,delete_attachment,download_attachment,upload_attachment.download_attachmentfollows the/attachments/{id}/contentredirect manually so the API key is not forwarded to external object storage (which would otherwise reject the request). Local-storage instances (direct content) are handled too.upload_attachmentsendsmultipart/form-datawithout the JSONContent-Typeheader so aiohttp sets the multipart boundary itself; metadata + file parts follow the OpenProject API v3 contract.src/tools/attachments.py— FastMCP tools following the existing module pattern (pydantic input model for upload,format_success/format_errorhelpers).download_attachmentreturns text content inline and asks for asave_pathfor binary content.src/server.py— registers the new module.README.md— documents tools #41–#45.test_attachments.py— offline validation of the input model and formatting helpers (same dry-run style astest_tools.py).Testing
Verified end-to-end against a live OpenProject instance:
list_attachments/get_attachmenton a work package with attachments ✅download_attachmentinline text + redirect handling ✅upload_attachment→delete_attachmentround-trip (cleans up after itself) ✅test_attachments.pypasses ✅