Skip to content

Conversation

@Coldaine
Copy link
Owner

@Coldaine Coldaine commented Jan 27, 2026

User description

This submission migrates the ALAS MCP server from a hand-rolled JSON-RPC implementation to the FastMCP 3.0 framework. This change addresses several anti-patterns, including lack of type safety, manual JSON construction, and a large if/elif tool dispatch block. The new implementation is more maintainable, robust, and easier to test.

Fixes #6


PR created automatically by Jules for task 12946948930458953804 started by @Coldaine


PR Type

Enhancement


Description

  • Migrated 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

  • Refactored 7 tools as decorator-based functions with comprehensive unit tests


Diagram Walkthrough

flowchart LR
  A["Hand-rolled JSON-RPC<br/>230 lines"] -->|"Refactor"| B["FastMCP 3.0<br/>140 lines"]
  B -->|"Decorator-based"| C["Type-safe Tools<br/>7 functions"]
  B -->|"New"| D["Unit Tests<br/>104 lines"]
  B -->|"New"| E["pyproject.toml<br/>Dependencies"]
Loading

File Walkthrough

Relevant files
Enhancement
alas_mcp_server.py
Refactor to FastMCP 3.0 with type-safe tools                         

agent_orchestrator/alas_mcp_server.py

  • Replaced manual JSON-RPC implementation with FastMCP 3.0
    decorator-based approach
  • Refactored 7 tools as standalone functions with type-safe signatures
  • Removed 90 lines of protocol boilerplate (result/error methods, tool
    specs dict, request handler)
  • Introduced ALASContext class to manage ALAS script initialization
  • Simplified main entry point to initialize context and call
    mcp.run(transport="stdio")
+127/-207
Tests
test_alas_mcp.py
Add comprehensive unit tests for MCP tools                             

agent_orchestrator/test_alas_mcp.py

  • Added comprehensive unit test suite with 9 test cases covering all 7
    tools
  • Implemented mock_context fixture for mocking global ctx object
  • Tests cover happy paths and error cases (e.g., invalid page
    navigation)
  • Uses unittest.mock and pytest for proper isolation and assertions
+104/-0 
Configuration changes
run_server.sh
Add server launch script with encoding setup                         

agent_orchestrator/run_server.sh

  • New shell script to launch MCP server with proper environment setup
  • Sets PYTHONIOENCODING=utf-8 for correct character encoding
  • Accepts optional --config argument (defaults to "alas")
+4/-0     
pyproject.toml
Add project configuration with dependencies                           

agent_orchestrator/pyproject.toml

  • New project configuration file for ALAS MCP server
  • Specifies dependencies: fastmcp>=3.0.0b1, pillow>=10.0.0, lz4>=4.0.0
  • Requires Python 3.10+
  • Configured with hatchling build backend
+13/-0   
Documentation
CHANGELOG.md
Document FastMCP migration and improvements                           

CHANGELOG.md

  • Documented migration from hand-rolled JSON-RPC to FastMCP 3.0
  • Highlighted 39% code reduction (230 → 140 lines)
  • Listed improvements: type safety, error handling, maintainability
  • Noted all 7 tools remain functionally identical
+7/-0     
CLAUDE.md
Update documentation for FastMCP migration                             

CLAUDE.md

  • Updated MCP Tool Status section to reflect FastMCP 3.0 migration
  • Added improvements list: type-safe signatures, structured error
    handling, code reduction
  • Updated tool notes with new behavior (e.g., default duration for
    swipe, ValueError for invalid pages)
  • Changed tool return format from JSON strings to structured objects
  • Added launch command section with proper invocation syntax
+17/-11 

This commit refactors the `alas_mcp_server.py` to use the FastMCP 3.0 framework, eliminating several anti-patterns and improving the overall quality of the codebase.

- Replaced manual JSON-RPC implementation with FastMCP's decorator-based approach.
- Introduced type safety to all tool functions.
- Added a comprehensive unit test suite with proper mocking.
- Updated documentation to reflect the changes.

Co-authored-by: Coldaine <158332486+Coldaine@users.noreply.github.com>
@google-labs-jules
Copy link

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

Copilot AI review requested due to automatic review settings January 27, 2026 04:05
@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

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Uninitialized global context

Description: Global mutable context variable ctx is initialized as None and accessed by all tool
functions without null checks, creating potential for AttributeError if tools are called
before initialization in main().
alas_mcp_server.py [29-29]

Referred Code
ctx: Optional[ALASContext] = None
Ticket Compliance
🟡
🎫 #6
🟢 Migrate ALAS MCP server from hand-rolled JSON-RPC to FastMCP 3.0 framework
Eliminate manual JSON-RPC envelope construction (lines 19-26, 156-196)
Remove giant if/elif tool dispatch block (lines 104-154)
Add type safety to replace zero type safety (lines 113-152)
Eliminate schema/implementation drift risk (lines 28-92 vs 104-152)
Remove 65 lines of schema boilerplate (lines 28-92)
Replace untestable stdin/stdout loop (lines 199-225)
Add parameter pre-validation (lines 113-152)
Fix inconsistent response formats (lines 106-152)
Decouple direct state machine coupling (lines 16-17, 130-152)
Setup FastMCP 3.0 environment with Python 3.10 and required dependencies
Create pyproject.toml with fastmcp>=3.0.0b1, pillow>=10.0.0, lz4>=4.0.0
Refactor all 7 tools as decorator-based functions with type-safe signatures
Preserve existing PNG encoding logic
Add comprehensive unit tests for all tools
Update documentation in CLAUDE.md and CHANGELOG.md
Maintain functional equivalence of all 7 tools
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: Robust Error Handling and Edge Case Management

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

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: 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: 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: The alas_call_tool function accepts arbitrary arguments dict without validation,
potentially allowing malicious or malformed input to be passed to underlying tools.

Referred Code
def alas_call_tool(name: str, arguments: dict = None) -> dict:
    """Invoke a deterministic ALAS tool by name.

    Args:
        name: Tool name (from alas.list_tools)
        arguments: Tool arguments (default: empty dict)

    Returns:
        Tool result (structure varies by tool)

    Raises:
        KeyError: If tool name is unknown
    """
    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

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

@kiloconnect
Copy link

kiloconnect bot commented Jan 27, 2026

Code Review Summary

Status: No Issues Found | Recommendation: Merge

The PR successfully migrates the MCP server from a hand-rolled JSON-RPC implementation to FastMCP 3.0, providing:

  • Type safety via function signature validation
  • Structured error handling
  • 39% code reduction (230 → 140 lines)
  • Comprehensive unit test coverage
  • All tools remain functionally identical
Files Reviewed (3 files)
  • agent_orchestrator/alas_mcp_server.py - MCP server implementation
  • agent_orchestrator/pyproject.toml - Project dependencies
  • agent_orchestrator/test_alas_mcp.py - Unit tests

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

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Add check for uninitialized context

Add a helper function to check that the global ctx is initialized before use in
tool functions, raising a RuntimeError if it is None to prevent AttributeError.

agent_orchestrator/alas_mcp_server.py [29-42]

 ctx: Optional[ALASContext] = None
+
+def _ensure_context() -> ALASContext:
+    if ctx is None:
+        raise RuntimeError(
+            "ALASContext not initialized. "
+            "Ensure the server is run correctly to set the context."
+        )
+    return ctx
 
 @mcp.tool()
 def adb_screenshot() -> dict:
     """Take a screenshot from the connected emulator/device.
 
     Returns a base64-encoded PNG image. Requires lz4 package for decompression.
     """
-    data = ctx.encode_screenshot_png_base64()
+    current_ctx = _ensure_context()
+    data = current_ctx.encode_screenshot_png_base64()
     return {
         "content": [
             {"type": "image", "mimeType": "image/png", "data": data}
         ]
     }
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a potential AttributeError if tool functions are used without initializing the global ctx and proposes a robust solution with a helper function for clear error handling.

Medium
  • More

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 migrates the ALAS MCP server from a hand-rolled JSON-RPC implementation to the FastMCP 3.0 framework. The migration aims to eliminate anti-patterns including manual JSON construction, lack of type safety, and a large if/elif tool dispatch block. The refactored implementation reduces code size from 229 lines to 149 lines and introduces modern Python patterns with type annotations.

Changes:

  • Replaced manual JSON-RPC implementation with FastMCP 3.0 framework
  • Converted tool handlers from if/elif dispatch to decorated functions with type safety
  • Added comprehensive unit tests with pytest and mocking
  • Created pyproject.toml for dependency management and run_server.sh for easier execution

Reviewed changes

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

Show a summary per file
File Description
agent_orchestrator/alas_mcp_server.py Migrated from hand-rolled MCP server to FastMCP 3.0 with decorated tool functions
agent_orchestrator/test_alas_mcp.py Added comprehensive unit tests for all 7 MCP tools
agent_orchestrator/pyproject.toml New dependency specification using FastMCP 3.0 beta
agent_orchestrator/run_server.sh Bash script for convenient server launch with uv
CLAUDE.md Updated documentation to reflect FastMCP migration
CHANGELOG.md Added entry documenting the migration and improvements

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

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 pyproject.toml specifies a dependency on fastmcp version 3.0.0b1 or higher (beta release). Beta versions may have breaking changes or bugs that could affect production stability. Consider whether this is appropriate for the intended use case, or if you should wait for a stable 3.0.0 release. At minimum, document that this is using a beta version and the implications for stability.

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +32
@mcp.tool()
def adb_screenshot() -> dict:
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 tool function names use underscores (e.g., adb_screenshot, adb_tap, alas_goto) instead of the dot notation used in the old implementation (e.g., adb.screenshot, adb.tap, alas.goto). Unless FastMCP's @mcp.tool() decorator is explicitly configured to use the old naming convention, this represents a breaking API change. Client code expecting the old tool names will fail. Consider using the @mcp.tool(name="adb.screenshot") syntax to preserve backward compatibility with the original tool names.

Copilot uses AI. Check for mistakes.
"""
page = ctx._state_machine.get_current_state()
return str(page)
@mcp.tool()
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 tool function name alas_goto will be registered with an underscore by default, breaking compatibility with the old tool name alas.goto. Use @mcp.tool(name="alas.goto") to preserve the original tool name.

Suggested change
@mcp.tool()
@mcp.tool(name="alas.goto")

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +46
def test_adb_swipe(mock_context):
result = adb_swipe(10, 20, 30, 40, duration_ms=500)
assert result == "swiped 10,20->30,40"
mock_context.script.device.swipe_adb.assert_called_once_with(
(10, 20), (30, 40), duration=0.5
)
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 for adb_swipe only tests with an explicit duration_ms parameter (500), but doesn't test the default value of 100ms. Consider adding a test case that calls adb_swipe without the duration_ms parameter to verify that the default value works correctly.

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +52
@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)
Returns:
Confirmation message
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 tool function name adb_tap will be registered with an underscore by default, breaking compatibility with the old tool name adb.tap. Use @mcp.tool(name="adb.tap") to preserve the original tool name.

Suggested change
@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)
Returns:
Confirmation message
@mcp.tool(name="adb.tap")
def adb_tap(x: int, y: int) -> str:
"""Tap a coordinate using ADB input tap.
Args:
x: X coordinate (integer)
y: Y coordinate (integer)
Returns:
Confirmation message

Copilot uses AI. Check for mistakes.
for t in ctx._state_machine.get_all_tools()
]
return tools
@mcp.tool()
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 tool function name alas_call_tool will be registered with underscores by default, breaking compatibility with the old tool name alas.call_tool. Use @mcp.tool(name="alas.call_tool") to preserve the original tool name.

Suggested change
@mcp.tool()
@mcp.tool(name="alas.call_tool")

Copilot uses AI. Check for mistakes.
"""
ctx.script.device.click_adb(x, y)
return f"tapped {x},{y}"
@mcp.tool()
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.

Missing blank line between function definitions. According to PEP 8, top-level function definitions should be separated by two blank lines. The decorators should be considered part of the function definition, so there should be a blank line before the @mcp.tool() decorator.

Copilot uses AI. Check for mistakes.
for t in ctx._state_machine.get_all_tools()
]
return tools
@mcp.tool()
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.

Missing blank line between function definitions. According to PEP 8, top-level function definitions should be separated by two blank lines. The decorators should be considered part of the function definition, so there should be a blank line before the @mcp.tool() decorator.

Copilot uses AI. Check for mistakes.
Comment on lines +100 to +105
ValueError: If page name is unknown
"""
from module.ui.page import Page
destination = Page.all_pages.get(page)
if destination is None:
raise ValueError(f"unknown page: {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 error type for unknown pages has changed from KeyError (in the old implementation) to ValueError (in the new implementation). While this is more semantically correct, it represents a behavior change that could affect error handling in client code. Consider documenting this breaking change in the CHANGELOG.md or ensuring that FastMCP properly converts this to the expected JSON-RPC error code.

Suggested change
ValueError: If page name is unknown
"""
from module.ui.page import Page
destination = Page.all_pages.get(page)
if destination is None:
raise ValueError(f"unknown page: {page}")
KeyError: If page name is unknown
"""
from module.ui.page import Page
destination = Page.all_pages.get(page)
if destination is None:
raise KeyError(f"unknown page: {page}")

Copilot uses AI. Check for mistakes.
ctx._state_machine.transition(destination)
return f"navigated to {page}"

@mcp.tool()
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 tool function name alas_list_tools will be registered with underscores by default, breaking compatibility with the old tool name alas.list_tools. Use @mcp.tool(name="alas.list_tools") to preserve the original tool name.

Suggested change
@mcp.tool()
@mcp.tool(name="alas.list_tools")

Copilot uses AI. Check for mistakes.
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.

Migrate ALAS MCP Server to FastMCP 3.0 and Eliminate Anti-Patterns

2 participants