Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package mcp-redmine for openSUSE:Factory checked in at 2026-09-11 18:02:14 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/mcp-redmine (Old) and /work/SRC/openSUSE:Factory/.mcp-redmine.new.1265 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "mcp-redmine" Fri Sep 11 18:02:14 2026 rev:4 rq:1376988 version:2026.9.10.84818 Changes: -------- --- /work/SRC/openSUSE:Factory/mcp-redmine/mcp-redmine.changes 2026-09-08 17:02:02.290802679 +0200 +++ /work/SRC/openSUSE:Factory/.mcp-redmine.new.1265/mcp-redmine.changes 2026-09-11 18:05:38.003623983 +0200 @@ -1,0 +2,7 @@ +Thu Sep 10 18:28:23 UTC 2026 - Martin Pluskal <[email protected]> + +- Update to version 2026.9.10.84818: + * Save attachments byte-exact: JSON-content attachments no longer + fail redmine_download with TypeError (request gains a raw flag) + +------------------------------------------------------------------- Old: ---- mcp_redmine-2026.9.5.185311.tar.gz New: ---- mcp_redmine-2026.9.10.84818.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ mcp-redmine.spec ++++++ --- /var/tmp/diff_new_pack.p5ir8F/_old 2026-09-11 18:05:38.689652662 +0200 +++ /var/tmp/diff_new_pack.p5ir8F/_new 2026-09-11 18:05:38.691652746 +0200 @@ -20,7 +20,7 @@ # it against the distribution's primary Python flavour. %define pythons %{primary_python} Name: mcp-redmine -Version: 2026.9.5.185311 +Version: 2026.9.10.84818 Release: 0 Summary: Model Context Protocol server for Redmine License: MPL-2.0 ++++++ mcp_redmine-2026.9.5.185311.tar.gz -> mcp_redmine-2026.9.10.84818.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mcp_redmine-2026.9.5.185311/Makefile new/mcp_redmine-2026.9.10.84818/Makefile --- old/mcp_redmine-2026.9.5.185311/Makefile 2020-02-02 01:00:00.000000000 +0100 +++ new/mcp_redmine-2026.9.10.84818/Makefile 2020-02-02 01:00:00.000000000 +0100 @@ -40,7 +40,7 @@ package-inspect-test: rm -rf /tmp/test-$(PROJECT) uv venv /tmp/test-$(PROJECT) --python 3.12 - source /tmp/test-$(PROJECT)/bin/activate && uv pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ $(PROJECT) + source /tmp/test-$(PROJECT)/bin/activate && uv pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ --index-strategy unsafe-best-match $(PROJECT) tree /tmp/test-$(PROJECT)/lib/python3.12/site-packages/$(PACKAGE) source /tmp/test-$(PROJECT)/bin/activate && which $(PROJECT) @@ -52,7 +52,7 @@ source /tmp/test-$(PROJECT)/bin/activate && which $(PROJECT) package-run-test: - uvx --default-index https://test.pypi.org/simple/ --index https://pypi.org/simple/ --from $(PROJECT) $(PROJECT) + uvx --index-strategy unsafe-best-match --default-index https://test.pypi.org/simple/ --index https://pypi.org/simple/ --from $(PROJECT) $(PROJECT) package-run-prod: uvx --from $(PROJECT) $(PROJECT) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mcp_redmine-2026.9.5.185311/PKG-INFO new/mcp_redmine-2026.9.10.84818/PKG-INFO --- old/mcp_redmine-2026.9.5.185311/PKG-INFO 2020-02-02 01:00:00.000000000 +0100 +++ new/mcp_redmine-2026.9.10.84818/PKG-INFO 2020-02-02 01:00:00.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.5 Name: mcp-redmine -Version: 2026.9.5.185311 +Version: 2026.9.10.84818 Summary: A MCP server that connects to your Redmine instance Project-URL: Homepage, https://github.com/runekaagaard/mcp-redmine Project-URL: Issues, https://github.com/runekaagaard/mcp-redmine/issues @@ -67,7 +67,7 @@ "mcpServers": { "redmine": { "command": "uvx", - "args": ["--from", "mcp-redmine==2026.09.05.185311", + "args": ["--from", "mcp-redmine==2026.09.10.084818", "--refresh-package", "mcp-redmine", "mcp-redmine"], "env": { "REDMINE_URL": "https://your-redmine-instance.example.com", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mcp_redmine-2026.9.5.185311/README.md new/mcp_redmine-2026.9.10.84818/README.md --- old/mcp_redmine-2026.9.5.185311/README.md 2020-02-02 01:00:00.000000000 +0100 +++ new/mcp_redmine-2026.9.10.84818/README.md 2020-02-02 01:00:00.000000000 +0100 @@ -49,7 +49,7 @@ "mcpServers": { "redmine": { "command": "uvx", - "args": ["--from", "mcp-redmine==2026.09.05.185311", + "args": ["--from", "mcp-redmine==2026.09.10.084818", "--refresh-package", "mcp-redmine", "mcp-redmine"], "env": { "REDMINE_URL": "https://your-redmine-instance.example.com", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mcp_redmine-2026.9.5.185311/mcp_redmine/server.py new/mcp_redmine-2026.9.10.84818/mcp_redmine/server.py --- old/mcp_redmine-2026.9.5.185311/mcp_redmine/server.py 2020-02-02 01:00:00.000000000 +0100 +++ new/mcp_redmine-2026.9.10.84818/mcp_redmine/server.py 2020-02-02 01:00:00.000000000 +0100 @@ -7,7 +7,7 @@ ### Constants ### -VERSION = "2026.09.05.185311" +VERSION = "2026.09.10.084818" # Load OpenAPI spec current_dir = pathlib.Path(__file__).parent @@ -70,7 +70,7 @@ # Core def request(path: str, method: str = 'get', data: dict = None, params: dict = None, - content_type: str = 'application/json', content: bytes = None) -> dict: + content_type: str = 'application/json', content: bytes = None, raw: bool = False) -> dict: if REDMINE_READ_ONLY and method.lower() != 'get': return {"status_code": 0, "body": None, "error": f"REDMINE_READ_ONLY is enabled: refusing {method.upper()} request"} @@ -95,7 +95,11 @@ response.raise_for_status() body = None - if response.content: + if raw: + # Downloads must keep the exact bytes: an attachment that happens to be valid JSON + # (e.g. a Postman collection) must not be parsed into a dict (#46). + body = response.content + elif response.content: try: body = response.json() except ValueError: @@ -270,7 +274,7 @@ filename = attachment_response["body"]["attachment"]["filename"] response = request(f"attachments/download/{attachment_id}/{filename}", "get", - content_type="application/octet-stream") + content_type="application/octet-stream", raw=True) if response["status_code"] != 200 or not response["body"]: return format_response(response) @@ -319,7 +323,7 @@ "Use redmine_download to save it to disk instead."}) response = request(f"attachments/download/{attachment_id}/{attachment['filename']}", "get", - content_type="application/octet-stream") + content_type="application/octet-stream", raw=True) if response["status_code"] != 200 or not response["body"]: return format_response(response) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mcp_redmine-2026.9.5.185311/pyproject.toml new/mcp_redmine-2026.9.10.84818/pyproject.toml --- old/mcp_redmine-2026.9.5.185311/pyproject.toml 2020-02-02 01:00:00.000000000 +0100 +++ new/mcp_redmine-2026.9.10.84818/pyproject.toml 2020-02-02 01:00:00.000000000 +0100 @@ -1,6 +1,6 @@ [project] name = "mcp-redmine" -version = "2026.09.05.185311" +version = "2026.09.10.084818" description = "A MCP server that connects to your Redmine instance" readme = "README.md" requires-python = ">=3.10" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mcp_redmine-2026.9.5.185311/tests/test_server.py new/mcp_redmine-2026.9.10.84818/tests/test_server.py --- old/mcp_redmine-2026.9.5.185311/tests/test_server.py 2020-02-02 01:00:00.000000000 +0100 +++ new/mcp_redmine-2026.9.10.84818/tests/test_server.py 2020-02-02 01:00:00.000000000 +0100 @@ -149,3 +149,36 @@ monkeypatch.setattr(server, "request", fake_request) result = server.redmine_attachment_image(1) assert isinstance(result, server.Image) + + +# Raw downloads (#46): attachments whose content is valid JSON must be saved byte-exact + +JSON_BYTES = b'{"info": {"name": "collection"}, "item": []}' + + +def test_request_raw_returns_bytes_for_json_content(monkeypatch): + def fake_request(method, url, **kwargs): + response = FakeResponse(json_body={"info": {"name": "collection"}, "item": []}) + response.content = JSON_BYTES + return response + + monkeypatch.setattr(server._http_client, "request", fake_request) + assert server.request("attachments/download/1/collection.json", raw=True)["body"] == JSON_BYTES + # without raw, the same response is parsed (existing behaviour for API calls) + assert isinstance(server.request("some.json")["body"], dict) + + +def test_redmine_download_saves_json_attachment(monkeypatch, tmp_path): + monkeypatch.setattr(server, "REDMINE_ALLOWED_DIRECTORIES", [tmp_path]) + + def fake_request(path, method="get", **kwargs): + if path.endswith(".json") and not kwargs.get("raw"): + return {"status_code": 200, "error": "", + "body": {"attachment": {"filename": "collection.json"}}} + assert kwargs.get("raw") is True + return {"status_code": 200, "error": "", "body": JSON_BYTES} + + monkeypatch.setattr(server, "request", fake_request) + result = server.redmine_download(1, str(tmp_path / "out.json")) + assert "saved_to" in result and "error: ''" in result + assert (tmp_path / "out.json").read_bytes() == JSON_BYTES diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mcp_redmine-2026.9.5.185311/uv.lock new/mcp_redmine-2026.9.10.84818/uv.lock --- old/mcp_redmine-2026.9.5.185311/uv.lock 2020-02-02 01:00:00.000000000 +0100 +++ new/mcp_redmine-2026.9.10.84818/uv.lock 2020-02-02 01:00:00.000000000 +0100 @@ -298,7 +298,7 @@ version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform != 'emscripten') or (python_full_version < '3.12' and sys_platform == 'emscripten')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749 } wheels = [ @@ -448,7 +448,7 @@ version = "8.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "zipp", marker = "python_full_version < '3.14'" }, + { name = "zipp", marker = "(python_full_version < '3.14' and sys_platform != 'emscripten') or (python_full_version < '3.12' and sys_platform == 'emscripten')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641 } wheels = [ @@ -650,7 +650,7 @@ [[package]] name = "mcp-redmine" -version = "2026.8.1.2543" +version = "2026.9.5.185311" source = { editable = "." } dependencies = [ { name = "httpx" }, @@ -1420,8 +1420,7 @@ version = "0.34.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click" }, - { name = "h11", version = "0.14.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'" }, + { name = "click", marker = "python_full_version < '3.12' or python_full_version >= '3.14' or sys_platform != 'emscripten'" }, { name = "h11", version = "0.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14' or sys_platform != 'emscripten'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ]
