diff --git a/apps/device-host-agent/host_agent/cli.py b/apps/device-host-agent/host_agent/cli.py index 4fba57b..67650da 100644 --- a/apps/device-host-agent/host_agent/cli.py +++ b/apps/device-host-agent/host_agent/cli.py @@ -10,6 +10,7 @@ from host_agent.app import create_application from host_agent.config import load_host_agent_config from host_agent.instance_lock import InstanceAlreadyRunningError from host_agent.local_account import LocalAccountStore +from host_agent.mcp_token import McpTokenStore class LocalAccountSetupError(RuntimeError): @@ -20,8 +21,16 @@ def main(argv: Sequence[str] | None = None) -> None: parser = argparse.ArgumentParser(description="Run the Device Host Agent") subparsers = parser.add_subparsers(dest="command") subparsers.add_parser("setup", help="Create the local operator account") + subparsers.add_parser( + "mcp-token", + help="Print the MCP server bearer token (generating if missing)", + ) args = parser.parse_args(argv) + if args.command == "mcp-token": + _print_mcp_token() + return + try: if args.command == "setup": _run_setup() @@ -54,6 +63,12 @@ def _run_setup() -> None: print(f"Local account '{account.username}' created.") +def _print_mcp_token() -> None: + config = load_host_agent_config() + store = McpTokenStore(config.identity_path.parent / "host_mcp_token.json") + print(store.load_or_create().token) + + def _resolve_config_with_local_account(): config = load_host_agent_config() store = LocalAccountStore(config.local_account_path) diff --git a/apps/device-host-agent/tests/test_cli.py b/apps/device-host-agent/tests/test_cli.py index bdfcea0..9061ade 100644 --- a/apps/device-host-agent/tests/test_cli.py +++ b/apps/device-host-agent/tests/test_cli.py @@ -146,3 +146,19 @@ def test_duplicate_instance_exits_with_clear_error( err = capsys.readouterr().err assert "another Host Agent instance" in err assert str(lock_path) in err + + +def test_mcp_token_subcommand_prints_token(tmp_path, capsys, monkeypatch) -> None: + monkeypatch.setenv("HOST_AGENT_IDENTITY_PATH", str(tmp_path / "host_identity.json")) + monkeypatch.setenv("HOST_AGENT_LOCAL_ACCOUNT_PATH", str(tmp_path / "host_local_account.json")) + # Also set control plane URL to satisfy config loading + monkeypatch.setenv("HOST_AGENT_CONTROL_PLANE_URL", "https://cloud.example") + from host_agent.cli import main + + main(["mcp-token"]) + out = capsys.readouterr().out.strip() + assert len(out) >= 40 # token is ~43 chars + # Subsequent invocation prints the same token (idempotent). + main(["mcp-token"]) + out2 = capsys.readouterr().out.strip() + assert out == out2