oct
Loading...
Searching...
No Matches
oct.mcp.__main__ Namespace Reference

Functions

argparse.ArgumentParser _build_parser ()
argparse.Namespace _parse_args (list[str]|None argv=None)
None main (list[str]|None argv=None)

Detailed Description

Purpose
-------
Entry point for ``python -m oct.mcp``: declare the command-line surface
of the ``oct-mcp`` server, parse it, and hand the resolved options to
:func:`oct.mcp.server.start_server`.

Responsibilities
----------------
- Declare every CLI option (project root, profile, config file,
  transport, bind host and port, metrics port) in one parser.
- Translate ``--config`` into a :class:`~pathlib.Path` before handing it
  on, so the server layer never parses strings.
- Under the ``stdio`` transport, divert standard output (fd 1) to
  standard error from just after argument parsing until
  :func:`oct.mcp.server.start_server` hands it to the SDK transport
  (:mod:`oct.mcp.stdout_diversion`), so nothing written during startup
  reaches the JSON-RPC channel.
- Translate an interactive ``Ctrl-C`` into a clean exit code 0.

Usage
-----
    python -m oct.mcp [--project-root PATH] [--profile PROFILE]
                      [--transport {stdio,sse,streamable-http}]
                      [--host HOST] [--port PORT]
                      [--metrics-port PORT]

Options
-------
--project-root PATH
    Absolute or relative path to the Option C project root.
    Defaults to auto-discovery from the current working directory.

--profile PROFILE
    MCP security profile: ``default``, ``strict``, or ``airgapped``.
    Overrides ``OCT_MCP_PROFILE`` and the config file value.

--config PATH
    Path to the user-level config file.
    Defaults to ``~/.oct-mcp/config.json``.

--transport {stdio,sse,streamable-http}
    MCP transport. ``stdio`` (default) for local AI host integration
    (Claude Code, Cursor, VS Code). ``streamable-http`` for remote
    access. ``sse`` is the legacy remote transport, kept for hosts that
    have not moved to streamable HTTP.

--host HOST
    Bind host for the HTTP transports (default: ``127.0.0.1``).
    Only used with ``--transport streamable-http`` or ``--transport
    sse``. Use ``0.0.0.0`` with caution and only behind a reverse proxy
    with TLS and authentication.

--port PORT
    Bind port for the HTTP transports (default: ``3001``).
    Only used with ``--transport streamable-http`` or ``--transport
    sse``.

--metrics-port PORT
    Start a Prometheus ``/metrics`` HTTP server on this port.
    Disabled by default. Requires ``oct[metrics]`` to be installed.

Environment variables
---------------------
OCT_MCP_SAFE_MODE=1        Block all tool calls (emergency kill-switch).
OCT_TRUST_REPO_CONFIG=1    Trust project-level config (debug_config.json).
OCT_MCP_PROFILE=<name>     Override active profile.
OCT_MCP_SANDBOX=<name>     Override sandbox backend (auto/native/bubblewrap/sandbox_exec).

Diagnostics
-----------
Domain: MCP
Levels:
    L1 - errors: startup failures raised out of :func:`start_server`
    L2 - lifecycle: server start and shutdown
    L3 - request details
    L4 - deep trace

This module emits one ``_dbg`` record of its own, the L3 server entry,
after argument parsing and after the stdio diversion has started, so it
can never reach the protocol channel. Every other diagnostic for a run
comes from :mod:`oct.mcp.server` and the pipeline layers beneath it.

Contracts
---------
- :func:`main` returns ``None``. A fatal startup error reaches the
  caller as the ``SystemExit(1)`` raised by
  :func:`oct.mcp.server.start_server`, so the process exit code is the
  server layer's, not this module's.
- An unrecognised ``--transport`` or ``--profile`` value is refused by
  ``argparse`` with exit code 2 and the offending value named.
- ``KeyboardInterrupt`` exits 0: stopping a stdio server with Ctrl-C is
  the normal way a host detaches, not a failure.
- Under the ``stdio`` transport, fd 1 is diverted before the first
  ``_dbg`` call. :func:`oct.mcp.server.start_server` hands it back
  immediately before the transport runs, and exits with code 1 instead
  of serving when that fails; :func:`main` hands it back after a
  startup failure or a ``Ctrl-C``. ``--help`` and ``argparse`` errors
  are printed before the diversion, to standard output and standard
  error as before. The HTTP transports are not diverted.
- Nothing imported before the diversion starts (the ``oct.mcp``
  package and this module's module-level imports) may emit a ``_dbg``
  record at import time, because such a record would reach the wire.
  :mod:`oct.mcp.server` is imported lazily, after the diversion. The
  end-to-end test with every domain at level 9 in
  ``tests/tests_mcp/test_mcp_stdout_diversion.py`` guards this.

Dependencies
------------
- oct.core.diagnostics (_dbg structured logger)
- oct.mcp.stdout_diversion (divert_stdout and restore_stdout, which keep startup output off the stdio wire)
- oct.mcp.server (lazy; start_server, which runs the server with the parsed options)

Function Documentation

◆ _build_parser()

argparse.ArgumentParser oct.mcp.__main__._build_parser ( )
protected
Build the ``python -m oct.mcp`` argument parser.

Factored out of :func:`_parse_args` so the declared transport
choices can be inspected without parsing an argument vector.

Definition at line 128 of file __main__.py.

Here is the caller graph for this function:

◆ _parse_args()

argparse.Namespace oct.mcp.__main__._parse_args ( list[str] | None argv = None)
protected
Parse *argv* with the :func:`_build_parser` parser.

Definition at line 197 of file __main__.py.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ main()

None oct.mcp.__main__.main ( list[str] | None argv = None)
Parse CLI arguments and start the MCP server.

Under the ``stdio`` transport, standard output is diverted to
standard error right after parsing, before the first ``_dbg`` call,
and :func:`oct.mcp.server.start_server` hands it back to the SDK
transport immediately before serving (or exits with code 1 when it
cannot). The ``finally`` clause hands it back after a startup
failure too; after a normal run it is a no-op.

Definition at line 202 of file __main__.py.

Here is the call graph for this function:
Here is the caller graph for this function: