fix(rpc): treat a blank IDA_MCP_URL as unset for download URLs by Lesereingrape · Pull Request #539 · mrexodia/ida-pro-mcp · GitHub
Skip to content

fix(rpc): treat a blank IDA_MCP_URL as unset for download URLs - #539

Merged
mrexodia merged 2 commits into
mrexodia:mainfrom
Lesereingrape:fix/download-base-url
Sep 22, 2026
Merged

mrexodia merged 2 commits into
mrexodia:mainfrom
Lesereingrape:fix/download-base-url

Conversation

@Lesereingrape

@Lesereingrape Lesereingrape commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Symptom

When IDA_MCP_URL is present but empty (or whitespace only), truncated tool output advertises an
unusable download location. IDA_MCP_URL is a documented knob (README "Worker behavior") and
.codex-plugin/mcp.json forwards it into the server environment, so an empty entry in a client
config or an env file is enough to hit this:

before (4e4b855)
IDA_MCP_URL=None                     -> http://127.0.0.1:13337/output/112007da-....json
IDA_MCP_URL=''                       -> /output/100beeb7-....json
IDA_MCP_URL='   '                    ->    /output/cf607d40-....json
IDA_MCP_URL='http://127.0.0.1:13337' -> http://127.0.0.1:13337/output/09fbe480-....json

In the blank cases the download_hint the agent is told to run becomes
curl -o .ida-mcp/<id>.json /output/<id>.json (or the same with leading spaces), which cannot
resolve to any host, so the full result is effectively lost.

Two details make the blank value sticky:

  • ida_mcp/rpc.py read os.environ.get("IDA_MCP_URL", "http://127.0.0.1:13337"), so an empty
    string is accepted as a base URL and replaces the default.
  • idalib_server.py only applied the host/port fallback when the variable was absent
    (if not "IDA_MCP_URL" in os.environ), so the blank value survived that guard too.

What changes

A blank or whitespace-only IDA_MCP_URL is now treated as "not set" by both readers.
configured_download_base_url() returns the stripped value or None, so the two call sites share
one rule:

  • ida_mcp/rpc.py: the module-level base URL falls back to http://127.0.0.1:13337 instead of "".
  • idalib_server.py: the host/port override triggers on "no usable URL configured" rather than
    "variable absent".

This is the same blank-value convention e67d32c introduced for the other IDA_MCP_* knobs
(_env_float / _env_int / _parse_bool_env), and it keeps what #383 asked for: a non-blank
configured URL is still used verbatim, once stripped.

How to reproduce

No IDA installation needed — it goes through the same seam the truncation tests use. Save as
_repro.py in the repo root and run PYTHONPATH=src python _repro.py:

import importlib, os, pathlib, sys
from typing import TypedDict
from unittest import mock

sys.path.insert(0, str(pathlib.Path("tests").resolve()))
from _mcp_spec_support import call_rpc, load_ida_rpc_module


class ListResult(TypedDict):
    items: list[dict]
    count: int


def download_url() -> str:
    rpc = load_ida_rpc_module()
    srv = rpc.McpServer("repro")
    original = srv.registry.methods["tools/call"]
    srv.registry.methods["tools/call"] = lambda name, arguments=None, _meta=None: (
        rpc._limit_output_response(original(name, arguments, _meta))
    )

    @srv.tool
    def big_list() -> ListResult:
        """Returns a huge list; forces truncation."""
        return {"items": [{"name": f"n{i}", "value": i} for i in range(5000)], "count": 5000}

    result = call_rpc(srv, "tools/call", name="big_list", arguments={})
    return result["_meta"]["ida_mcp"]["download_url"]


with mock.patch.dict(os.environ, clear=True):
    for value in (None, "", "   ", "http://127.0.0.1:13337"):
        if value is None:
            os.environ.pop("IDA_MCP_URL", None)
        else:
            os.environ["IDA_MCP_URL"] = value
        importlib.reload(load_ida_rpc_module())
        print(f"IDA_MCP_URL={value!r:26} -> {download_url()}")

On this branch all four lines come back absolute:

IDA_MCP_URL=None                     -> http://127.0.0.1:13337/output/8d8e880c-....json
IDA_MCP_URL=''                       -> http://127.0.0.1:13337/output/6a53d999-....json
IDA_MCP_URL='   '                    -> http://127.0.0.1:13337/output/c18a3a8b-....json
IDA_MCP_URL='http://127.0.0.1:13337' -> http://127.0.0.1:13337/output/11ee035e-....json

Checks

  • DownloadBaseUrlFromEnvTests, appended to the existing tests/test_mcp_spec_truncation.py
    (second commit, droppable on its own): on base 4e4b855 that file reports
    4 failed, 14 passed, 13 subtests; with the fix 16 passed, 15 subtests.
  • Full suite PYTHONPATH=src python -m pytest tests/ -q with the ida venv:
    • base 4e4b855 + the new tests: 5 failed, 252 passed, 115 subtests
    • this branch: 1 failed, 254 passed, 117 subtests
    • the single remaining failure is pre-existing on Windows
      (test_mcp_spec_http_e2e.py::Http11TransportTests::test_single_threaded_stop_closes_idle_keepalive_connection,
      also the target of fix(zeromcp): let stop() return while a keep-alive client idles #527) and unrelated to this change.
  • ruff check src/ida_pro_mcp/ida_mcp/rpc.py src/ida_pro_mcp/idalib_server.py: clean.

A blank or whitespace-only IDA_MCP_URL made truncated tool output advertise
relative download paths like "/output/<id>.json", so the curl hint could not
be used. The other IDA_MCP_* knobs already fall back to defaults for blank
values; the download base url now does the same.
Covers blank/whitespace/unset values falling back to the local default and
keeps the truncated-output download hint an absolute URL.
@mrexodia

Copy link
Copy Markdown
Owner

@Lesereingrape

Copy link
Copy Markdown
Contributor Author

@mrexodia
mrexodia merged commit 8ed1142 into mrexodia:main Sep 22, 2026
5 checks passed

This branch was successfully deployed

1 active deployment
external-pr — 200fa16a Deployed Sep 22, 2026 by Lesereingrape via approval #413
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.

2 participants