Skip to content

chore: Split MCP Server into 2 servers; One for recipe (A2UI over MCP…#879

Open
sugoi-yuzuru wants to merge 1 commit intomainfrom
split_mcp
Open

chore: Split MCP Server into 2 servers; One for recipe (A2UI over MCP…#879
sugoi-yuzuru wants to merge 1 commit intomainfrom
split_mcp

Conversation

@sugoi-yuzuru
Copy link
Collaborator

@sugoi-yuzuru sugoi-yuzuru commented Mar 17, 2026

…) and calculator (MCP apps).

Description

The split is to make the intent of each server clearly separate for its use case as a sample.

Pre-launch Checklist

If you need help, consider asking for advice on the discussion board.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request effectively splits the MCP server into two distinct servers for recipes and the calculator, which clarifies the purpose of each sample. The refactoring is well-structured. I've provided a few suggestions to improve security, robustness, and code quality in the new calculator server implementation. The most critical issue is in samples/agent/mcp/recipe/__main__.py, where a click command is called incorrectly, which will lead to a runtime error. Additionally, I've noted a security concern with the permissive CORS policy in the new calculator server, which should be addressed even in a sample application to avoid promoting insecure practices. Lastly, the repository style guide requires tests for code changes, but no tests have been added for the new server logic.

Comment on lines +15 to +19
import sys

from server import main

sys.exit(main()) # type: ignore[call-arg]
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

Calling the click command main() directly will result in a TypeError at runtime because the port and transport arguments are not provided. The type: ignore[call-arg] suppresses the static analysis warning but doesn't fix the underlying issue. click commands are meant to be executed from the command line, where click handles parsing arguments. If you intend for this module to be runnable, you should let click handle the execution.

Suggested change
import sys
from server import main
sys.exit(main()) # type: ignore[call-arg]
from server import main
if __name__ == "__main__":
main()

Comment on lines +77 to +79
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
Copy link
Contributor

Choose a reason for hiding this comment

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

high

Using a wildcard * for allow_origins is insecure as it allows any origin to make requests to your server. This can lead to Cross-Site Request Forgery (CSRF) attacks. While this might be acceptable for a local demo, it's a dangerous practice that could be copied into production code. It's better to restrict the allowed origins to a specific list or make it configurable via an environment variable. You'll also need to import os at the top of the file.

Suggested change
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
allow_origins=os.getenv("A2UI_DEMO_CORS_ALLOWED_ORIGINS", "http://localhost:5173").split(","),
allow_methods=["*"],
allow_headers=["*"],

]

@app.read_resource()
async def read_resource(uri: Any) -> str | bytes:
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The type hint for uri is Any, which is too generic. The mcp library might provide a more specific type for resource URIs, such as mcp.types.ResourceIdentifier. Using a more specific type would improve type safety and code clarity.

Suggested change
async def read_resource(uri: Any) -> str | bytes:
async def read_resource(uri: types.ResourceIdentifier) -> str | bytes:

Comment on lines +49 to +51
if str(uri) == "ui://calculator/app":
return (pathlib.Path(__file__).parent / "apps" / "calculator.html").read_text()
raise ValueError(f"Unknown resource: {uri}")
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The call to .read_text() could raise a FileNotFoundError if calculator.html is missing, which would result in an unhandled exception. It would be more robust to wrap this in a try...except block and raise a ValueError similar to how unknown resources are handled. This provides more consistent error handling.

    if str(uri) == "ui://calculator/app":
      try:
        return (pathlib.Path(__file__).parent / "apps" / "calculator.html").read_text()
      except FileNotFoundError:
        raise ValueError(f"Resource file not found for uri: {uri}")
    raise ValueError(f"Unknown resource: {uri}")

sse = SseServerTransport("/messages/")

async def handle_sse(request: Request):
async with sse.connect_sse(request.scope, request.receive, request._send) as streams: # type: ignore[reportPrivateUsage]
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Accessing the private member request._send is not ideal as it relies on internal implementation details of starlette.requests.Request which could change in future versions, breaking this code. If a public API is available, it should be used instead. If not, consider adding a comment explaining why this private access is necessary.


import uvicorn

print(f"Server running at 127.0.0.1:{port} using sse")
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

For consistency with the stdio transport logic and to follow best practices in click-based applications, it's better to use click.echo(..., err=True) for logging informational messages to the console instead of print(). This ensures consistent output handling and allows for easier testing and redirection.

    click.echo(f"Server running at 127.0.0.1:{port} using sse", err=True)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

1 participant