Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,21 @@ Where `$PROJECT_DIR` is the path to the project you want to work on.
Then chat with Claude about what changes you want to make to the project.
Every time codemcp makes a change to your code, it will generate a commit.

### Using with claude.ai web interface

You can also use codemcp with the Claude web interface at claude.ai by running the SSE server:

```bash
codemcp serve
```

This will start a local SSE server on port 8000 that can be connected to from claude.ai. The server
has CORS enabled for claude.ai by default. You can customize the host, port, and allowed CORS origins:

```bash
codemcp serve --host 0.0.0.0 --port 8765 --cors-origin https://claude.ai --cors-origin https://example.com
```

To see some sample transcripts using this tool, check out:

- [Implement a new feature](https://claude.ai/share/a229d291-6800-4cb8-a0df-896a47602ca0)
Expand Down
67 changes: 67 additions & 0 deletions codemcp/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
import os
import re
from pathlib import Path
from typing import List, Optional

import click
import uvicorn
from fastapi.middleware.cors import CORSMiddleware
from mcp.server.fastmcp import FastMCP
from starlette.applications import Starlette
from starlette.routing import Mount

from .common import normalize_file_path
from .tools.chmod import chmod
Expand Down Expand Up @@ -629,7 +634,69 @@ def init(path: str, python: bool) -> None:
click.echo(result)


def create_sse_app(allowed_origins: Optional[List[str]] = None) -> Starlette:
"""Create an SSE app with the MCP server.

Args:
allowed_origins: List of origins to allow CORS for. If None, only claude.ai is allowed.

Returns:
A Starlette application with the MCP server mounted.
"""
if allowed_origins is None:
allowed_origins = ["https://claude.ai"]

app = Starlette(
routes=[
Mount("/", app=mcp.sse_app()),
]
)

# Add CORS middleware to the app
app.add_middleware(
CORSMiddleware,
allow_origins=allowed_origins,
allow_credentials=True,
allow_methods=["GET", "POST"],
allow_headers=["*"],
)

return app


def run() -> None:
"""Run the MCP server."""
configure_logging()
mcp.run()


@cli.command()
@click.option(
"--host",
default="127.0.0.1",
help="Host to bind the server to (default: 127.0.0.1)",
)
@click.option("--port", default=8000, help="Port to bind the server to (default: 8000)")
@click.option(
"--cors-origin",
multiple=True,
help="Origins to allow CORS for (default: https://claude.ai)",
)
def serve(host: str, port: int, cors_origin: List[str]) -> None:
"""Run the MCP SSE server.

This command mounts the MCP as an SSE server that can be connected to from web applications.
By default, it allows CORS requests from claude.ai.
"""
configure_logging()
logging.info(f"Starting MCP SSE server on {host}:{port}")

# If no origins provided, use the default
allowed_origins = list(cors_origin) if cors_origin else None
if allowed_origins:
logging.info(f"Allowing CORS for: {', '.join(allowed_origins)}")
else:
logging.info("Allowing CORS for: https://claude.ai")

app = create_sse_app(allowed_origins)
uvicorn.run(app, host=host, port=port)
24 changes: 23 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,25 @@ dependencies = [
"pyyaml>=6.0.0",
"editorconfig>=0.17.0",
"click>=8.1.8",
"agno>=1.2.16",
"anthropic>=0.49.0",
"fastapi>=0.115.12",
"uvicorn>=0.28.0",
"starlette>=0.35.1",
]

[dependency-groups]
dev = [
"pytest>=7.0.0",
"pytest-xdist>=3.6.1",
"pytest-asyncio>=0.23.0",
"black>=23.0.0",
"mypy>=1.0.0",
"expecttest>=0.1.4",
"ruff>=0.1.5",
"pyright>=1.1.350",
"tomli_w>=1.0.0",
"requests>=2.30.0",
]

[project.scripts]
Expand All @@ -37,7 +44,6 @@ requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.uv]
# uv-specific settings can go here

[tool.ruff]
# Enable the formatter
Expand Down Expand Up @@ -100,3 +106,19 @@ errorCodes = ["reportUnknownMemberType"]
[[tool.pyright.ignoreExtraErrors]]
path = "codemcp/testing.py"
errorCodes = ["reportUnknownMemberType", "reportUnknownArgumentType", "reportUnknownVariableType"]

[[tool.pyright.ignoreExtraErrors]]
path = "codemcp/main.py"
errorCodes = ["reportUnknownMemberType", "reportUnknownArgumentType", "reportUnknownVariableType", "reportUnknownParameterType", "reportMissingParameterType"]

[[tool.pyright.ignoreExtraErrors]]
path = "codemcp/agno.py"
errorCodes = ["reportUnknownMemberType", "reportUnknownArgumentType", "reportUnknownVariableType", "reportUnknownParameterType", "reportMissingParameterType", "reportPrivateImportUsage"]

[[tool.pyright.ignoreExtraErrors]]
path = "codemcp/config.py"
errorCodes = ["reportUnknownVariableType"]

[[tool.pyright.ignoreExtraErrors]]
path = "codemcp/multi_entry.py"
errorCodes = ["reportUnknownParameterType", "reportUnknownArgumentType", "reportMissingTypeArgument"]
8 changes: 8 additions & 0 deletions stubs/mcp_stubs/server/fastmcp.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,11 @@ class FastMCP:
def run(self) -> None:
"""Run the server."""
...

def sse_app(self) -> Any:
"""Return an ASGI application for the MCP server that can be used with SSE.

Returns:
An ASGI application
"""
...
Loading
Loading