Skip to content

Conversation

@Coldaine
Copy link
Owner

@Coldaine Coldaine commented Jan 27, 2026

User description

This PR refactors the ALAS MCP server to FastMCP 3.0, improving type safety and reducing boilerplate. It includes comprehensive unit tests and an ALAS launcher wrapper.


PR Type

Enhancement, Tests


Description

  • Migrated MCP server from hand-rolled JSON-RPC to FastMCP 3.0 framework

    • Eliminated 90 lines of protocol boilerplate (39% code reduction)
    • Added full type safety via function signature validation
    • Improved error handling with structured exception types
  • Added comprehensive unit tests for all 7 MCP tools

  • Created ALAS launcher wrapper and shell script for server execution

  • Updated documentation with FastMCP migration details and launch instructions


Diagram Walkthrough

flowchart LR
  A["Hand-rolled JSON-RPC<br/>230 lines"] -->|"Refactor to FastMCP 3.0"| B["FastMCP Framework<br/>140 lines"]
  B -->|"Type-safe decorators"| C["7 Tool Functions<br/>@mcp.tool()"]
  C -->|"Automatic schema"| D["Type Validation<br/>& Error Handling"]
  E["Unit Tests<br/>test_alas_mcp.py"] -->|"Mock ALAS/FastMCP"| F["Tool Coverage<br/>8 test cases"]
  G["run_server.sh"] -->|"Launch wrapper"| H["stdio transport<br/>mcp.run()"]
Loading

File Walkthrough

Relevant files
Enhancement
alas_mcp_server.py
Refactor to FastMCP 3.0 decorator-based tools                       

agent_orchestrator/alas_mcp_server.py

  • Refactored from class-based JSON-RPC handler to FastMCP 3.0
    decorator-based tools
  • Replaced _McpServer class with ALASContext wrapper and 7 @mcp.tool()
    decorated functions
  • Removed manual protocol handling (_result, _error, _tool_specs, handle
    methods)
  • Simplified main loop from manual JSON-RPC parsing to
    mcp.run(transport="stdio")
  • Added docstrings and type hints to all tool functions for clarity
+125/-202
run_server.sh
Add server launch shell script wrapper                                     

agent_orchestrator/run_server.sh

  • Created shell script wrapper for launching ALAS MCP server
  • Sets UTF-8 encoding and accepts optional config parameter
  • Simplifies server startup with single command
+4/-0     
Tests
test_alas_mcp.py
Add comprehensive unit tests for MCP tools                             

agent_orchestrator/test_alas_mcp.py

  • Created comprehensive unit test suite with 8 test cases covering all
    MCP tools
  • Mocked external dependencies (alas, module.ui.page, fastmcp) to enable
    isolated testing
  • Tests verify tool functionality: screenshot encoding, tap/swipe
    coordinates, state navigation, tool listing
  • Includes error case testing (invalid page navigation)
+98/-0   
Documentation
CHANGELOG.md
Document FastMCP migration and improvements                           

CHANGELOG.md

  • Documented FastMCP 3.0 migration with quantified improvements (39%
    code reduction, type safety)
  • Added entry for ALAS launcher wrapper tool
  • Updated MCP tool status section with migration details
+12/-0   
CLAUDE.md
Update documentation for FastMCP migration                             

CLAUDE.md

  • Updated MCP Tool Status section header to reflect FastMCP 3.0
    migration
  • Added improvements list highlighting type safety, error handling, code
    reduction
  • Updated tool table with FastMCP-specific notes (type-safe signatures,
    error types)
  • Added launch command section with uv run instructions
+21/-8   
Configuration changes
pyproject.toml
Add project configuration with FastMCP dependency               

agent_orchestrator/pyproject.toml

  • Created new project configuration file for ALAS MCP server
  • Specified FastMCP 3.0+ as primary dependency along with Pillow and lz4
  • Set Python 3.10+ requirement and hatchling build backend
+13/-0   

Copilot AI review requested due to automatic review settings January 27, 2026 06:22
@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@qodo-free-for-open-source-projects
Copy link

qodo-free-for-open-source-projects bot commented Jan 27, 2026

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Unauthenticated screenshot access

Description: The adb_screenshot() function returns base64-encoded device screenshots without
authentication or authorization checks, potentially exposing sensitive user data or game
state information to unauthorized MCP clients.
alas_mcp_server.py [39-46]

Referred Code
if ctx is None:
    raise RuntimeError("ALAS context not initialized")
data = ctx.encode_screenshot_png_base64()
return {
    "content": [
        {"type": "image", "mimeType": "image/png", "data": data}
    ]
}
Unrestricted ADB command execution

Description: The adb_tap() and adb_swipe() functions allow arbitrary ADB input commands without
validation or rate limiting, enabling potential abuse such as automated clicking, UI
manipulation, or denial-of-service attacks on the connected device.
alas_mcp_server.py [56-76]

Referred Code
    if ctx is None:
        raise RuntimeError("ALAS context not initialized")
    ctx.script.device.click_adb(x, y)
    return f"tapped {x},{y}"

@mcp.tool()
def adb_swipe(x1: int, y1: int, x2: int, y2: int, duration_ms: int = 100) -> str:
    """Swipe between coordinates using ADB input swipe.

    Args:
        x1: Starting X coordinate
        y1: Starting Y coordinate
        x2: Ending X coordinate
        y2: Ending Y coordinate
        duration_ms: Duration in milliseconds (default: 100)
    """
    if ctx is None:
        raise RuntimeError("ALAS context not initialized")
    duration = duration_ms / 1000.0
    ctx.script.device.swipe_adb((x1, y1), (x2, y2), duration=duration)
    return f"swiped {x1},{y1}->{x2},{y2}"
Arbitrary tool execution

Description: The alas_call_tool() function executes arbitrary registered tools with user-supplied
arguments without input sanitization or capability restrictions, potentially allowing
execution of unintended operations or exploitation of vulnerabilities in downstream tool
implementations.
alas_mcp_server.py [129-140]

Referred Code
def alas_call_tool(name: str, arguments: Optional[Dict[str, Any]] = None) -> Any:
    """Invoke a deterministic ALAS tool by name.

    Args:
        name: Tool name (from alas.list_tools)
        arguments: Tool arguments (default: empty dict)
    """
    if ctx is None:
        raise RuntimeError("ALAS context not initialized")
    args = arguments or {}
    result = ctx._state_machine.call_tool(name, **args)
    return result
Global state race condition

Description: The global ctx variable is initialized in main() and accessed by all tool functions
without thread-safety mechanisms, creating potential race conditions if FastMCP handles
requests concurrently or if the server is extended for multi-threaded operation.
alas_mcp_server.py [143-149]

Referred Code
global ctx
parser = argparse.ArgumentParser()
parser.add_argument("--config", default="alas")
args = parser.parse_args()

ctx = ALASContext(config_name=args.config)
mcp.run(transport="stdio")
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
Internal error exposure: RuntimeError messages expose internal implementation details like "ALAS context not
initialized" which reveals system architecture

Referred Code
    raise RuntimeError("ALAS context not initialized")
data = ctx.encode_screenshot_png_base64()

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Missing input validation: Function alas_call_tool accepts arbitrary arguments dict without validation or
sanitization before passing to call_tool

Referred Code
def alas_call_tool(name: str, arguments: Optional[Dict[str, Any]] = None) -> Any:
    """Invoke a deterministic ALAS tool by name.

    Args:
        name: Tool name (from alas.list_tools)
        arguments: Tool arguments (default: empty dict)
    """
    if ctx is None:
        raise RuntimeError("ALAS context not initialized")
    args = arguments or {}
    result = ctx._state_machine.call_tool(name, **args)
    return result

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Missing null validation: Functions like adb_tap and adb_swipe do not validate coordinate boundaries or handle
potential device communication failures

Referred Code
def adb_tap(x: int, y: int) -> str:
    """Tap a coordinate using ADB input tap.

    Args:
        x: X coordinate (integer)
        y: Y coordinate (integer)
    """
    if ctx is None:
        raise RuntimeError("ALAS context not initialized")
    ctx.script.device.click_adb(x, y)
    return f"tapped {x},{y}"

@mcp.tool()
def adb_swipe(x1: int, y1: int, x2: int, y2: int, duration_ms: int = 100) -> str:
    """Swipe between coordinates using ADB input swipe.

    Args:
        x1: Starting X coordinate
        y1: Starting Y coordinate
        x2: Ending X coordinate
        y2: Ending Y coordinate


 ... (clipped 7 lines)

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-free-for-open-source-projects
Copy link

qodo-free-for-open-source-projects bot commented Jan 27, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Remove global context for better testability

Refactor the code to eliminate the global ctx variable. Instead, use a
dependency injection pattern, such as encapsulating the context and tool
functions within a class, to improve testability and make the state management
more explicit.

Examples:

agent_orchestrator/alas_mcp_server.py [31-149]
ctx: Optional[ALASContext] = None

@mcp.tool()
def adb_screenshot() -> Dict[str, Any]:
    """Take a screenshot from the connected emulator/device.
    
    Returns a base64-encoded PNG image.
    """
    if ctx is None:
        raise RuntimeError("ALAS context not initialized")

 ... (clipped 109 lines)
agent_orchestrator/test_alas_mcp.py [55]
    alas_mcp_server.ctx = ctx

Solution Walkthrough:

Before:

# Global context initialized in main()
ctx: Optional[ALASContext] = None

@mcp.tool()
def adb_screenshot() -> Dict[str, Any]:
    if ctx is None:
        raise RuntimeError("ALAS context not initialized")
    data = ctx.encode_screenshot_png_base64()
    # ...
    return result

# ... other tools also access the global `ctx`

def main():
    global ctx
    # ...
    ctx = ALASContext(config_name=args.config)
    mcp.run(transport="stdio")

After:

class ToolServer:
    def __init__(self, config_name: str):
        self.ctx = ALASContext(config_name)

    @mcp.tool()
    def adb_screenshot(self) -> Dict[str, Any]:
        # Context is accessed via `self` instead of a global
        data = self.ctx.encode_screenshot_png_base64()
        # ...
        return result

    # ... other tools are methods of this class

def main():
    # ...
    server_instance = ToolServer(config_name=args.config)
    # Assumes FastMCP can register methods from an instance
    mcp.register_instance(server_instance)
    mcp.run(transport="stdio")
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a significant architectural weakness (use of a global ctx variable) and proposes a valid alternative (dependency injection), which would improve the code's testability and maintainability.

Medium
Possible issue
Convert ADB coordinates to strings

Convert integer coordinates x and y to strings before calling
ctx.script.device.click_adb to prevent a runtime TypeError in the underlying
shell command.

agent_orchestrator/alas_mcp_server.py [48-59]

 @mcp.tool()
 def adb_tap(x: int, y: int) -> str:
     """Tap a coordinate using ADB input tap.
     
     Args:
         x: X coordinate (integer)
         y: Y coordinate (integer)
     """
     if ctx is None:
         raise RuntimeError("ALAS context not initialized")
-    ctx.script.device.click_adb(x, y)
+    ctx.script.device.click_adb(str(x), str(y))
     return f"tapped {x},{y}"
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a runtime TypeError because the underlying adb_shell command expects string arguments for coordinates but receives integers, and provides a correct fix.

Medium
Convert ADB swipe coordinates to strings

Convert integer coordinates x1, y1, x2, and y2 to strings before calling
ctx.script.device.swipe_adb to prevent a runtime TypeError in the underlying
shell command.

agent_orchestrator/alas_mcp_server.py [61-76]

 @mcp.tool()
 def adb_swipe(x1: int, y1: int, x2: int, y2: int, duration_ms: int = 100) -> str:
     """Swipe between coordinates using ADB input swipe.
     
     Args:
         x1: Starting X coordinate
         y1: Starting Y coordinate
         x2: Ending X coordinate
         y2: Ending Y coordinate
         duration_ms: Duration in milliseconds (default: 100)
     """
     if ctx is None:
         raise RuntimeError("ALAS context not initialized")
     duration = duration_ms / 1000.0
-    ctx.script.device.swipe_adb((x1, y1), (x2, y2), duration=duration)
+    p1 = (str(x1), str(y1))
+    p2 = (str(x2), str(y2))
+    ctx.script.device.swipe_adb(p1, p2, duration=duration)
     return f"swiped {x1},{y1}->{x2},{y2}"
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a runtime TypeError because the underlying adb_shell command expects string arguments for coordinates but receives integers, and provides a correct fix.

Medium
  • Update

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR modernizes the ALAS MCP server by migrating from a hand-rolled JSON-RPC implementation to the FastMCP 3.0 framework. The refactoring improves type safety, reduces boilerplate code, and introduces comprehensive unit tests while maintaining functional equivalence for all 7 tools.

Changes:

  • Replaced custom JSON-RPC protocol handling with FastMCP 3.0 decorators and automatic schema generation
  • Added comprehensive unit tests with proper mocking for all 7 MCP tools
  • Introduced project configuration (pyproject.toml) and shell launcher script (run_server.sh) for improved deployment

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
agent_orchestrator/alas_mcp_server.py Complete rewrite using FastMCP decorators; tools now use typed function signatures instead of manual JSON-RPC message handling
agent_orchestrator/test_alas_mcp.py New comprehensive unit test suite covering all 7 tools with proper mocking of ALAS dependencies
agent_orchestrator/pyproject.toml New project configuration defining dependencies (fastmcp>=3.0.0b1, pillow, lz4) and build system
agent_orchestrator/run_server.sh New shell launcher script that sets UTF-8 encoding and runs server via uv with configurable ALAS config
CLAUDE.md Updated tool status section to reflect FastMCP 3.0 migration and improvements
CHANGELOG.md Documented migration with claimed 39% code reduction and improved error handling

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

CHANGELOG.md Outdated

### Changed
- **MCP Server**: Migrated from hand-rolled JSON-RPC to FastMCP 3.0 framework
- Eliminated 90 lines of protocol boilerplate (39% code reduction)
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The line count claim in the CHANGELOG states "Eliminated 90 lines of protocol boilerplate (39% code reduction)" which is inconsistent with the actual file. The new file is 152 lines, not 140, meaning if the old file was truly 230 lines, the reduction is 78 lines (34%), not 90 lines (39%).

Copilot uses AI. Check for mistakes.
import argparse
import base64
import io
import json
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The json import on line 4 is unused in the refactored code. It was needed for the old hand-rolled JSON-RPC implementation but FastMCP handles JSON serialization internally. This import should be removed.

Suggested change
import json

Copilot uses AI. Check for mistakes.
return {}
return json.loads(line)

# Global context initialized in main
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The module-level mutable global variable 'ctx' is set in main() and accessed by all tool functions. While this works for a single-instance server process, this pattern can make testing harder and introduces implicit state dependencies. The current test file addresses this by injecting mocks into the global ctx, which is a reasonable workaround. However, consider documenting this design decision explicitly, as it's a deviation from typical FastMCP patterns which might use dependency injection or context managers.

Suggested change
# Global context initialized in main
# Global context initialized in main.
#
# Design note:
# We intentionally keep a module-level mutable `ctx` that is set once in `main()`
# and then accessed by all MCP tools. This fits the single-process MCP server
# model and keeps tool signatures simple (no explicit context parameters).
#
# In tests, we inject mock ALASContext instances into this global `ctx` to
# control behavior and avoid relying on real devices. This is a conscious
# deviation from typical FastMCP patterns (which often use dependency
# injection or context managers), but is kept for simplicity here.

Copilot uses AI. Check for mistakes.
version = "1.0.0"
requires-python = ">=3.10"
dependencies = [
"fastmcp>=3.0.0b1",
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dependency specifies "fastmcp>=3.0.0b1" which includes a beta version constraint. Beta versions (indicated by "b1") can have breaking changes between releases. Consider either pinning to a specific beta version (e.g., "fastmcp==3.0.0b1") or waiting for a stable 3.0.0 release before merging to production. If this is intentional for early adoption, document the rationale.

Suggested change
"fastmcp>=3.0.0b1",
# Intentionally pinned to a specific beta; update to stable 3.x when available.
"fastmcp==3.0.0b1",

Copilot uses AI. Check for mistakes.
Comment on lines +77 to +87
def test_alas_goto_success(mock_ctx):
mock_page = mock.Mock()
alas_mcp_server.Page.all_pages = {"page_main": mock_page}
result = alas_mcp_server.alas_goto("page_main")
assert result == "navigated to page_main"
mock_ctx._state_machine.transition.assert_called_with(mock_page)

def test_alas_goto_invalid(mock_ctx):
alas_mcp_server.Page.all_pages = {}
with pytest.raises(ValueError, match="unknown page"):
alas_mcp_server.alas_goto("invalid_page")
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test_alas_goto_success test modifies alas_mcp_server.Page.all_pages (line 79) and test_alas_goto_invalid clears it (line 85), but these modifications aren't isolated per test. This could cause test order dependencies if additional tests are added later. Consider resetting Page.all_pages in the mock_ctx fixture or using a separate fixture for Page setup to ensure test isolation.

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +46
def adb_screenshot() -> Dict[str, Any]:
"""Take a screenshot from the connected emulator/device.
Returns a base64-encoded PNG image.
"""
if ctx is None:
raise RuntimeError("ALAS context not initialized")
data = ctx.encode_screenshot_png_base64()
return {
"content": [
{"type": "image", "mimeType": "image/png", "data": data}
]
}
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The adb_screenshot function returns a Dict[str, Any] with MCP protocol structure (content array with type/mimeType/data), while all other tool functions return simple strings or structured data without this wrapping. This inconsistency could be confusing. Consider either documenting why screenshot needs special handling, or making the return types more uniform if FastMCP supports it.

Copilot uses AI. Check for mistakes.
Comment on lines 1 to 98
import pytest
import unittest.mock as mock
from typing import Dict, Any
import sys
from types import ModuleType

# Mock the 'alas' and 'module' modules
m = ModuleType("alas")
sys.modules["alas"] = m
m.AzurLaneAutoScript = mock.Mock()

m2 = ModuleType("module")
sys.modules["module"] = m2
m3 = ModuleType("module.ui")
sys.modules["module.ui"] = m3
m4 = ModuleType("module.ui.page")
sys.modules["module.ui.page"] = m4
m4.Page = mock.Mock()
m4.Page.all_pages = {}

# Mock fastmcp BEFORE importing alas_mcp_server
fastmcp_module = ModuleType("fastmcp")
sys.modules["fastmcp"] = fastmcp_module

def mock_tool_decorator(*args, **kwargs):
def decorator(func):
return func
return decorator

mock_fastmcp_class = mock.Mock()
mock_fastmcp_class.return_value.tool = mock_tool_decorator
fastmcp_module.FastMCP = mock_fastmcp_class

import alas_mcp_server as alas_mcp_server

# Inject Page into alas_mcp_server because it's imported inside a function there
alas_mcp_server.Page = m4.Page

@pytest.fixture
def mock_ctx():
ctx = mock.Mock()
# Mock screenshot encoding
ctx.encode_screenshot_png_base64.return_value = "base64data"
# Mock state machine
ctx._state_machine = mock.Mock()
ctx._state_machine.get_current_state.return_value = "page_main"

# Mock internal tool list
mock_tool = mock.Mock()
mock_tool.name = "test_tool"
mock_tool.description = "test description"
mock_tool.parameters = {}
ctx._state_machine.get_all_tools.return_value = [mock_tool]

alas_mcp_server.ctx = ctx
return ctx

def test_adb_screenshot(mock_ctx):
result = alas_mcp_server.adb_screenshot()
assert result["content"][0]["type"] == "image"
assert result["content"][0]["data"] == "base64data"

def test_adb_tap(mock_ctx):
result = alas_mcp_server.adb_tap(100, 200)
assert result == "tapped 100,200"
mock_ctx.script.device.click_adb.assert_called_with(100, 200)

def test_adb_swipe(mock_ctx):
result = alas_mcp_server.adb_swipe(100, 100, 200, 200, 500)
assert result == "swiped 100,100->200,200"
mock_ctx.script.device.swipe_adb.assert_called_with((100, 100), (200, 200), duration=0.5)

def test_alas_get_current_state(mock_ctx):
result = alas_mcp_server.alas_get_current_state()
assert result == "page_main"

def test_alas_goto_success(mock_ctx):
mock_page = mock.Mock()
alas_mcp_server.Page.all_pages = {"page_main": mock_page}
result = alas_mcp_server.alas_goto("page_main")
assert result == "navigated to page_main"
mock_ctx._state_machine.transition.assert_called_with(mock_page)

def test_alas_goto_invalid(mock_ctx):
alas_mcp_server.Page.all_pages = {}
with pytest.raises(ValueError, match="unknown page"):
alas_mcp_server.alas_goto("invalid_page")

def test_alas_list_tools(mock_ctx):
result = alas_mcp_server.alas_list_tools()
assert len(result) == 1
assert result[0]["name"] == "test_tool"

def test_alas_call_tool(mock_ctx):
mock_ctx._state_machine.call_tool.return_value = {"success": True}
result = alas_mcp_server.alas_call_tool("test_tool", {"arg": 1})
assert result == {"success": True}
mock_ctx._state_machine.call_tool.assert_called_with("test_tool", arg=1) No newline at end of file
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All tool functions check if ctx is None and raise RuntimeError, but there are no tests verifying this error handling. Consider adding tests that call each tool function before ctx is initialized to ensure the RuntimeError is raised correctly with the expected message.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +3
#!/bin/bash
cd "$(dirname "$0")"
export PYTHONIOENCODING=utf-8
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shell script doesn't include error handling (e.g., 'set -e' to exit on errors) or validation that required commands exist (e.g., checking if 'uv' is installed). Consider adding basic error handling to fail fast if the environment isn't properly configured. For example, add 'set -euo pipefail' at the top to enable strict error handling.

Suggested change
#!/bin/bash
cd "$(dirname "$0")"
export PYTHONIOENCODING=utf-8
#!/bin/bash
set -euo pipefail
cd "$(dirname "$0")"
export PYTHONIOENCODING=utf-8
if ! command -v uv >/dev/null 2>&1; then
echo "Error: 'uv' command not found. Please install 'uv' and ensure it is on your PATH." >&2
exit 1
fi

Copilot uses AI. Check for mistakes.
CHANGELOG.md Outdated
Comment on lines 14 to 17
### Added
- **ALAS Launcher Wrapper** (`my_tools/alas_launcher.py`): Combines emulator start + Azur Lane app launch
- Detects correct package name for server region (EN/CN/JP/etc.)
- Uses direct ADB fallback for launch if MCP client not yet configured
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CHANGELOG doesn't mention the addition of comprehensive unit tests (test_alas_mcp.py) which is a significant addition mentioned in the PR description. Consider adding a line in the "Added" section noting the introduction of unit tests for all 7 MCP tools, as this is an important improvement for maintainability.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,98 @@
import pytest
import unittest.mock as mock
from typing import Dict, Any
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'Dict' is not used.
Import of 'Any' is not used.

Suggested change
from typing import Dict, Any

Copilot uses AI. Check for mistakes.
CHANGELOG.md Outdated
- All 7 tools remain functionally identical, now with better maintainability

### Added
- **ALAS Launcher Wrapper** (`my_tools/alas_launcher.py`): Combines emulator start + Azur Lane app launch
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: The my_tools/alas_launcher.py mentioned in the CHANGELOG is not present in the PR diff. Either add the file or remove this entry from the CHANGELOG.

mock_ctx._state_machine.call_tool.return_value = {"success": True}
result = alas_mcp_server.alas_call_tool("test_tool", {"arg": 1})
assert result == {"success": True}
mock_ctx._state_machine.call_tool.assert_called_with("test_tool", arg=1) No newline at end of file
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: File is missing a trailing newline at the end. This can cause issues with some tools and editors.

await mcp.call_tool("alas_goto", {"page": "invalid"})
assert False, "Should have raised an exception"
except Exception as e:
assert "unknown page" in str(e).lower() No newline at end of file
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: File is missing a trailing newline at the end. This can cause issues with some tools and editors.

m4 = mock.Mock()
sys.modules["module.ui.page"] = m4

from alas_mcp_server import mcp, ALASContext
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: The integration test imports from alas_mcp_server import mcp, ALASContext before all mocks are fully set up, which could lead to unexpected behavior. Consider moving imports inside the test functions or ensuring all mocks are properly established first.

@kiloconnect
Copy link

kiloconnect bot commented Jan 27, 2026

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (X files)
  • docs/FEATURE_IDEAS.md - Added new feature ideas for dorm morale rotation, exercise optimization, dock inventory scanner, equipment outfitter, and anti-scrap/retire safety guard
  • todo.md - Slimmed down todo list with links to detailed feature ideas in FEATURE_IDEAS.md

"command": "uv",
"args": [
"--directory",
"C:\\_projects\\ALAS\\agent_orchestrator",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: The settings file contains a hard-coded Windows path (C:\_projects\ALAS\agent_orchestrator) which may not work on other operating systems. Consider using relative paths or environment variables.

import argparse
import base64
import io
import json
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: The json import is unused and can be removed.

@@ -0,0 +1,98 @@
import pytest
import unittest.mock as mock
from typing import Dict, Any
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: The Dict and Any imports from typing are unused and can be removed.

mock_ctx._state_machine.call_tool.return_value = {"success": True}
result = alas_mcp_server.alas_call_tool("test_tool", {"arg": 1})
assert result == {"success": True}
mock_ctx._state_machine.call_tool.assert_called_with("test_tool", arg=1) No newline at end of file
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: File is missing a trailing newline at the end.

await mcp.call_tool("alas_goto", {"page": "invalid"})
assert False, "Should have raised an exception"
except Exception as e:
assert "unknown page" in str(e).lower() No newline at end of file
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: File is missing a trailing newline at the end.

Coldaine and others added 7 commits January 27, 2026 02:59
Capture requirements for two new feature ideas:
- Dorm morale rotation tool to swap ship girls in/out based on morale thresholds
- Exercise optimization with better scheduling and rank/points logging

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Capture requirements for a deterministic dock scanning tool that
iterates through all ship girls to build a structured inventory
(levels, skills, gear, skins) with a stretch goal for item inventory.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract detailed requirements for dorm morale rotation, exercise
optimization, and dock inventory scanner into docs/FEATURE_IDEAS.md.
Keep todo.md as a brief checklist with a link to the full write-ups.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tool to redistribute limited best-in-slot gear across ship girls
depending on game mode. Builds on dock inventory scanner data.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Cross-cutting safety concern: state-machine-aware safeguards to
prevent accidental ship retirement, scrapping, or deletion. Defines
dangerous screen detection, automatic cancel, halt-on-confusion,
and screenshot-on-danger principles.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove unused `json` import from alas_mcp_server.py
- Remove unused `Dict`, `Any` imports from test_alas_mcp.py
- Add trailing newlines to both test files
- Fix inaccurate line count claims (was "39% / 230→140", actual ~30% / 230→160)
- Replace stale `my_tools/alas_launcher.py` CHANGELOG entry with actual PR additions
  (unit tests, integration tests, run_server.sh, pyproject.toml)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@Coldaine Coldaine merged commit 8b82e33 into master Jan 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants