#2528·mempalace

mcp_server parses sys.argv at import time: mempalace-light-mcp --help shows the full server's options, and any importer's argv is consumed

Author: audrumCreated Sep 16, 2026Updated Sep 16, 2026

What happened?

mempalace/mcp_server/_guards.py calls parse_known_args() on sys.argv at module scope (line 73, _args = _parse_args()), and then acts on the result — setting os.environ["MEMPALACE_PALACE_PATH"], selecting a backend, and so on. Because mempalace.mcp_server is an ordinary importable module, this runs on import, not on entry, so it reads the argv of whatever process imports it.

Two visible consequences:

1. mempalace-light-mcp --help documents options the light server rejects.

$ mempalace-light-mcp --help
usage: mempalace-light-mcp [-h] [--palace PATH] [--backend NAME]
                           [--transport {stdio,http}] [--host HOST]
                           [--port PORT] [--tls-cert PATH] [--tls-key PATH]
                           [--read-only]

MemPalace MCP Server

That is the full server's parser (note description="MemPalace MCP Server" and metavar="PATH"). The light server's real parser is in mcp_light_server.main() and accepts only four options:

$ mempalace-light-mcp --transport http --host 127.0.0.1 --port 8775
usage: mempalace-light-mcp [-h] [--palace PALACE] [--collection COLLECTION]
                           [--backend BACKEND] [--read-only]
mempalace-light-mcp: error: unrecognized arguments: --transport http --host 127.0.0.1 --port 8775

mcp_light_server.py:38 does from . import mcp_server, which executes _guards.py at import; argparse's -h action prints that parser's help and sys.exit(0)s before main() ever builds its own parser. So --help can never reach the light server's parser.

The practical cost: the help text advertises an HTTP transport the light server does not have. Following it is the natural way to deploy the light server beside an existing HTTP one, and it fails only at runtime.

2. Importing the library consumes the importing program's argv. This is the part I think matters more.

# import_test.py
import sys, os
print('MEMPALACE_PALACE_PATH before:', os.environ.get('MEMPALACE_PALACE_PATH'))
from mempalace import mcp_server   # library import, not a CLI entry point
print('MEMPALACE_PALACE_PATH after :', os.environ.get('MEMPALACE_PALACE_PATH'))
$ python import_test.py --palace /tmp/not-my-palace
MEMPALACE_PALACE_PATH before: None
MEMPALACE_PALACE_PATH after : /tmp/not-my-palace

An unrelated host program that happens to have its own --palace flag silently redirects MemPalace to a different palace, just by importing it. And --help is hijacked outright:

$ python import_test.py --help
usage: import_test.py [-h] [--palace PATH] [--backend NAME]
                      [--transport {stdio,http}] [--host HOST] [--port PORT]
                      [--tls-cert PATH] [--tls-key PATH] [--read-only]

The host program prints MemPalace's options under its own name and exits 0 — its own --help never runs.

Since parse_known_args() tolerates unknown flags, this is silent in every case except --help: there is no error, and nothing indicates that argv was read.

What did you expect?

Argument parsing and its side effects belong behind an entry point, not at module scope. Concretely: move _parse_args() and the os.environ / backend mutations that follow it into the main() of the full server, and have mcp_light_server.main() parse its own argv only. Importing mempalace.mcp_server should not read sys.argv, should not mutate os.environ, and should not be able to terminate the importing process.

mempalace-light-mcp --help should then print the four options the light server actually accepts — or, if an HTTP transport is intended for the light server too, it could gain the --transport/--host/--port trio its help currently promises. From the outside the latter looks useful: the light server can only be spawned as a local stdio child today, which rules it out for any client that reaches the palace over the network rather than through the filesystem.

How to reproduce:

  1. mempalace-light-mcp --help — observe the full server's option list, including --transport.
  2. mempalace-light-mcp --transport http --port 8775 — observe unrecognized arguments.
  3. Save the import_test.py above and run it with --palace /tmp/not-my-palace, then with --help.

Environment:

  • MemPalace 3.10.0, chromadb 1.5.9, backend chroma
  • Python 3.13.5
  • Debian 13 (trixie), Linux 6.18.34 aarch64 (Raspberry Pi 4B)
Where it comes from

mempalace/mcp_server/_guards.py:

    args, unknown = parser.parse_known_args()      # ~line 67
    if unknown:
        logger.debug("Ignoring unknown args: %s", unknown)
    return args


_args = _parse_args()                              # line 73 — module scope

if _args.palace:
    os.environ["MEMPALACE_PALACE_PATH"] = os.path.abspath(_args.palace)
if _args.backend:
    backend_name = str(_args.backend).strip().lower()
    ...

mempalace/mcp_light_server.py:38: from . import mcp_server, which triggers the above. The light server's own parser lives at mcp_light_server.py:1007-1016 and is never reached when -h/--help is present.

I found this while trying to deploy mempalace-light-mcp alongside three existing mempalace-mcp --transport http systemd units, following its --help. The 3-tool surface itself works well — 9.9 KB of schema against ~41.6 KB, and it started cleanly against a palace whose HTTP server was live, taking no writer lease.