Skip to content

Commit 91dc73d

Browse files
authored
Merge branch 'develop' into rpatel/linear-webhook-example
2 parents 62551f5 + d8278f0 commit 91dc73d

File tree

135 files changed

+10754
-1572
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

135 files changed

+10754
-1572
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Codegen MCP Server
2+
3+
<p align="center">
4+
<a href="https://docs.codegen.com">
5+
<img src="https://i.imgur.com/6RF9W0z.jpeg" />
6+
</a>
7+
</p>
8+
9+
<h2 align="center">
10+
An MCP server implementation that integrates the codegen sdk.
11+
12+
</h2>
13+
14+
<div align="center">
15+
16+
[![Documentation](https://img.shields.io/badge/Docs-docs.codegen.com-purple?style=flat-square)](https://docs.codegen.com)
17+
[![License](https://img.shields.io/badge/Code%20License-Apache%202.0-gray?&color=gray)](https://github.com/codegen-sh/codegen-sdk/tree/develop?tab=Apache-2.0-1-ov-file)
18+
19+
</div>
20+
21+
This example demonstrates how to run a Model Control Protocol (MCP) server that integrates with Codegen. The server provides:
22+
23+
1. A standardized interface for model inference
24+
1. Integration with Codegen's core functionality, parsing codebases and executing codemods
25+
1. Support for various LLM providers through the MCP protocol
26+
27+
## Quick Start
28+
29+
### Prerequisites
30+
31+
- [uv](https://docs.astral.sh/uv/getting-started/installation/)
32+
- [python](https://www.python.org/downloads/) 3.12+
33+
34+
### Direct Execution
35+
36+
1. No installation is necessary, with the following command. Run this command directly or add it your `.json` mcp config file.
37+
38+
```bash
39+
uvx --from 'git+https://github.com/codegen-sh/codegen-sdk.git#egg=codegen-mcp-server&subdirectory=codegen-examples/examples/codegen-mcp-server' codegen-mcp-server
40+
```
41+
42+
### Example MCP Config
43+
44+
Here is an example mcp config that can be used with Cline or Claude desktop to integrate this MCP server
45+
46+
```json
47+
{
48+
"mcpServers": {
49+
"codegen-mcp": {
50+
"command": "uvx",
51+
"args": [
52+
"--from",
53+
"git+https://github.com/codegen-sh/codegen-sdk.git#egg=codegen-mcp-server&subdirectory=codegen-examples/examples/codegen-mcp-server",
54+
"codegen-mcp-server"
55+
],
56+
"disabled": false,
57+
"autoApprove": []
58+
}
59+
}
60+
}
61+
```
62+
63+
### MCP Tools:
64+
65+
- `parse_codebase`: Parses a codebase located at the provided path.
66+
- `check_parse_status`: Provides the current parsing status for the provided codebase.
67+
- `execute_codemod`: Executes a codemod script on a parsed codebase. This is where the codegen sdk leveraged to run simple or sophisticated codemods on the codebase.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Please read two things before trying to install this codegen MCP server:
2+
3+
1. [Readme](https://raw.githubusercontent.com/codegen-sh/codegen-sdk/refs/heads/develop/codegen-examples/examples/codegen-mcp-server/README.md)
4+
5+
- This contains the instructions for setting up the mcp server
6+
7+
2. [Documentation Index for Agents](https://docs.codegen.com/llms.txt)
8+
9+
- This contains a list of urls to the documentation for topics related to creating codegen codemods
10+
11+
Please use a CURL command to retrieve the contents of the readme and documentation and read both before starting to install the server.
12+
13+
1. curl -sL "https://raw.githubusercontent.com/codegen-sh/codegen-sdk/refs/heads/develop/README.md" | cat
14+
15+
1. curl -sL "https://docs.codegen.com/llms.txt" | cat
16+
17+
This should give you all the context you need to successfully install and utilize the server.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[project]
2+
name = "codegen-mcp-server"
3+
version = "0.1.0"
4+
description = "MCP server implementation adding tools for the Codegen SDK"
5+
readme = "README.md"
6+
requires-python = ">=3.12"
7+
dependencies = [
8+
"codegen>=0.14.5",
9+
"uvicorn>=0.27.1",
10+
"fastapi>=0.110.0",
11+
"pydantic>=2.6.3",
12+
"rich>=13.7.1",
13+
"click>=8.1.7",
14+
"mcp[cli]>=1.2.1",
15+
]
16+
17+
[tool.poetry.scripts]
18+
codegen-mcp-server = "server:main"
19+
[project.scripts]
20+
codegen-mcp-server = "server:main"
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import asyncio
2+
from dataclasses import dataclass, field
3+
from typing import Annotated, Optional, Dict, Any, List
4+
from mcp.server.fastmcp import FastMCP
5+
from codegen import Codebase
6+
7+
8+
@dataclass
9+
class CodebaseState:
10+
"""Class to manage codebase state and parsing."""
11+
12+
parse_task: Optional[asyncio.Future] = None
13+
parsed_codebase: Optional[Codebase] = None
14+
log_buffer: List[str] = field(default_factory=list)
15+
16+
def parse(self, path: str) -> Codebase:
17+
"""Parse the codebase at the given path."""
18+
codebase = Codebase(path)
19+
self.parsed_codebase = codebase
20+
return codebase
21+
22+
def reset(self) -> None:
23+
"""Reset the state."""
24+
self.log_buffer.clear()
25+
26+
27+
# Initialize FastMCP server
28+
mcp = FastMCP(
29+
"codegen-mcp-server",
30+
instructions="""This server provides tools to parse and modify a codebase using codemods.
31+
It can initiate parsing, check parsing status, and execute codemods.""",
32+
)
33+
34+
# Initialize state
35+
state = CodebaseState()
36+
37+
38+
def capture_output(*args, **kwargs) -> None:
39+
"""Capture and log output messages."""
40+
for arg in args:
41+
state.log_buffer.append(str(arg))
42+
43+
44+
def update_codebase(future: asyncio.Future):
45+
try:
46+
result = future.result()
47+
if result is not None:
48+
state.parsed_codebase = result
49+
else:
50+
state.parsed_codebase = None
51+
except Exception:
52+
pass
53+
54+
55+
@mcp.tool(name="parse_codebase", description="Initiate codebase parsing")
56+
async def parse_codebase(codebase_path: Annotated[str, "path to the codebase to be parsed"]) -> Dict[str, str]:
57+
if not state.parse_task or state.parse_task.done():
58+
state.parse_task = asyncio.get_event_loop().run_in_executor(None, lambda: state.parse(codebase_path))
59+
state.parse_task.add_done_callback(update_codebase)
60+
return {"message": "Codebase parsing initiated, this may take some time depending on the size of the codebase. Use the `check_parsing_status` tool to check if the parse has completed."}
61+
return {"message": "Codebase is already being parsed.", "status": "error"}
62+
63+
64+
@mcp.tool(name="check_parse_status", description="Check if codebase parsing has completed")
65+
async def check_parse_status() -> Dict[str, str]:
66+
if not state.parse_task:
67+
return {"message": "No codebase provided to parse."}
68+
if state.parse_task.done():
69+
return {"message": "Codebase parsing completed."}
70+
return {"message": "Codebase parsing in progress."}
71+
72+
73+
@mcp.tool(name="execute_codemod", description="Execute a codemod on the codebase")
74+
async def execute_codemod(codemod: Annotated[str, "The python codemod code to execute on the codebase"]) -> Dict[str, Any]:
75+
if not state.parse_task or not state.parse_task.done():
76+
return {"error": "Codebase is not ready for codemod execution."}
77+
78+
try:
79+
await state.parse_task
80+
if state.parsed_codebase is None:
81+
return {"error": "Codebase path is not set."}
82+
else:
83+
# TODO: Implement proper sandboxing for code execution
84+
context = {
85+
"codebase": state.parsed_codebase,
86+
"print": capture_output,
87+
}
88+
exec(codemod, context)
89+
90+
logs = "\n".join(state.log_buffer)
91+
state.reset()
92+
return {"message": "Codemod executed, view the logs for any output and your source code for any resulting updates.", "logs": logs}
93+
except Exception as e:
94+
return {"error": f"Error executing codemod: {str(e)}", "details": {"type": type(e).__name__, "message": str(e)}}
95+
96+
97+
def main():
98+
print("starting codegen-mcp-server")
99+
run = mcp.run_stdio_async()
100+
print("codegen-mcp-server started")
101+
asyncio.get_event_loop().run_until_complete(run)
102+
103+
104+
if __name__ == "__main__":
105+
main()

0 commit comments

Comments
 (0)