mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-14 06:17:58 +00:00
Merge branch 'main' of github.com:oraios/serena
Conflicts: scripts/mcp_server.py src/serena/llm/prompt_factory.py src/serena/mcp.py
This commit is contained in:
commit
af4f405421
8 files changed
+207
-11
No files matched your search
@@ -1,6 +1,85 @@
|
||||
# serena
|
||||
# Serena
|
||||
|
||||
Welcome to the serena library!
|
||||
> **🚀 A powerful, fully-featured coding agent that you can use for free. No API key and no subscription required!**
|
||||
|
||||
|
||||
Serena answers the following important question:
|
||||
Can I have a state-of-the-art coding agent without paying (enormous) API costs
|
||||
or constantly purchasing tokens? The answer is: yes, you can!
|
||||
|
||||
We enable developers to harness the full coding power of Claude
|
||||
(and soon also ChatGPT, see [here](https://x.com/OpenAIDevs/status/1904957755829481737))
|
||||
for free by leveraging two key technologies:
|
||||
**MCP Servers** and **Language Servers**.
|
||||
|
||||
The former allows MCP clients like Claude Desktop to gain access to your codebase and developer
|
||||
tools. Using the latter, we built the core logic of serena - a set of versatile code querying
|
||||
and editing functionalities based on symbolic understanding of the code. Serena discovers and
|
||||
edits code just like a seasoned developer would do it by navigating the codebase inside an IDE.
|
||||
Because of this, serena can find the right context and do the right thing even in very large and
|
||||
complex projects! So not only is it free and open source, it is also often better than other existing
|
||||
solutions.
|
||||
|
||||
Serena stands for *Semantic Retrieval and Editing Noetic Agent*, but we also just liked the name.
|
||||
|
||||
|
||||
## Is it Really Free?
|
||||
|
||||
Yes! Even the free tier of Anthropic has support for MCP Servers, so you can use serena there.
|
||||
Presumably, the same will be true for ChatGPT Desktop once they add support for MCP servers.
|
||||
But we do recommend to buy the Claude Pro subscription for 20$ per month as this way the rate
|
||||
limits are much higher.
|
||||
|
||||
Serena is [Oraios AI](https://oraios-ai.de/)'s contribution to the developer community. We use it ourselves every day.
|
||||
|
||||
We got tired of having to pay multiple
|
||||
subscriptions (Cursor, Windsurf) that forced us to keep purchasing tokens on top of the subscription costs.
|
||||
We also got tired of paying the huge API costs from Claude Code, Cline, Aider and other API-based tools.
|
||||
This is why we built serena and cancelled most subscriptions.
|
||||
See below for a more detailed comparison to existing technologies.
|
||||
|
||||
## What if I Do Want to Pay?
|
||||
|
||||
If you want to use your own API key, connect serena to a custom model,
|
||||
or use it within a paid tool, you can do so.
|
||||
We decoupled the tools offered by serena from the MCP server implementation, so they can also
|
||||
be used in any agent framework and with any model. See the section [Serena as Agent](#serena-as-agent).
|
||||
This way, you can run serena as a command line tool or within a UI of your choice.
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Install `uv` if not done yet (instructions [here](https://docs.astral.sh/uv/getting-started/installation/))
|
||||
2. Clone the repository to `/path/to/serena`.
|
||||
3. Create a configuration file for your project, say `myproject.yml`. See `myproject.demo.yml` for the structure.
|
||||
You can just copy it and edit the entries.
|
||||
4. Configure the mcp server in your client. For example, for Claude Desktop, you need to go to `Settings->Developer->MCP Servers`,
|
||||
which will let you open a json file. There add the following to enable serena:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"serena": {
|
||||
"command": "/abs/path/to/uv",
|
||||
"args": ["run", "--directory", "/abs/path/to/serena", "serena", "/abs/path/to/myproject.yml"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
That's it! Save the config, restart Claude (you will have actually terminate the process on windows, just closing the window is not enough),
|
||||
and you should see the serena mcp tools in your chat interface (the small hammer icon).
|
||||
|
||||
Note that serena is always configured **for a single project**. To use it for another, you will have to
|
||||
write a new configuration file and adjust the config in the MCP client (don't forget to restart after).
|
||||
|
||||
For more info on MCP servers with Claude Desktop see [here](https://modelcontextprotocol.io/quickstart/user).
|
||||
|
||||
## Serena as Agent
|
||||
|
||||
In the agent mode, serena can be used with any model, including the currently popular (and SOTA in coding)
|
||||
Gemini-2.5-pro.
|
||||
|
||||
...
|
||||
|
||||
## Getting Started
|
||||
|
||||
@@ -68,8 +147,8 @@ This is typically done by providing a configuration file in JSON format as follo
|
||||
{
|
||||
"mcpServers": {
|
||||
"serena": {
|
||||
"command": "uv",
|
||||
"args": ["run", "--directory", "/path/to/serena", "python", "scripts/mcp_server.py", "myproject.yml"]
|
||||
"command": "/path/to/uv",
|
||||
"args": ["run", "--directory", "/path/to/serena", "serena", "/path/to/myproject.yml"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,4 +65,10 @@ prompts:
|
||||
Recommend which tools to use and the next steps to take. Imagine that you are handing over the task to another person who has access to the
|
||||
same tools and memory files as you do, but has not been part of the conversation so far. If you have discovered anything general about the project
|
||||
in the course of the conversation, consider writing a new memory file to store that information.
|
||||
|
||||
|
||||
system_prompt: |
|
||||
You are a professional coding agent who is concerned with one particular codebase. You have tools to explore the codebase
|
||||
iteratively, to edit files and to execute arbitrary commands. You heavily rely on these tools to solve the tasks give to you,
|
||||
but you also operate in a frugal and intelligent manner, always
|
||||
keeping in mind to not load content that is not needed for the task at hand. If you cannot find everything you need in the codebase
|
||||
and generally when in doubt, you ask the user clarifying questions.
|
||||
@@ -27,8 +27,13 @@ dependencies = [
|
||||
"sensai-utils>=1.4.0",
|
||||
"pydantic>=2.10.6",
|
||||
"types-pyyaml>=6.0.12.20241230",
|
||||
"pyyaml>=6.0.2",
|
||||
"jinja2>=3.1.6",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
serena = "serena.mcp:start_mcp_server"
|
||||
|
||||
[project.license]
|
||||
text = "MIT"
|
||||
|
||||
@@ -60,6 +65,9 @@ dev = [
|
||||
"sphinxcontrib-spelling>=8.0.0",
|
||||
"types-pyyaml>=6.0.12.20241230",
|
||||
]
|
||||
agno = [
|
||||
"agno>=1.2.6",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/oraios/serena"
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
from agno.agent import Agent
|
||||
from agno.models.google import Gemini
|
||||
from agno.tools.duckduckgo import DuckDuckGoTools
|
||||
|
||||
agent = Agent(
|
||||
model=Gemini(id="gemini-2.5-pro-exp-03-25"),
|
||||
description="",
|
||||
tools=[], # Add serena tools here
|
||||
show_tool_calls=True,
|
||||
markdown=True,
|
||||
context=
|
||||
)
|
||||
agent.print_response("Tell me about a breaking news story from New York.", stream=True)
|
||||
@@ -1,5 +0,0 @@
|
||||
from serena.sample_module import SampleClass
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(SampleClass().sample_method("Oraios AI"))
|
||||
print("Your new library project serena is waiting for you!")
|
||||
@@ -38,4 +38,9 @@ class PromptFactory:
|
||||
return self._format_prompt("summarize_changes", locals())
|
||||
|
||||
def create_prepare_for_new_conversation(self) -> str:
|
||||
return self._format_prompt("prepare_for_new_conversation", locals())
|
||||
return self._format_prompt('prepare_for_new_conversation', locals())
|
||||
|
||||
def create_system_prompt(self) -> str:
|
||||
return self._format_prompt('system_prompt', locals())
|
||||
|
||||
|
||||
@@ -94,3 +94,7 @@ def create_mcp_server() -> FastMCP:
|
||||
mcp._tool_manager._tools[tool.get_name()] = make_tool(tool)
|
||||
|
||||
return mcp
|
||||
|
||||
|
||||
def start_mcp_server():
|
||||
create_mcp_server().run()
|
||||
@@ -14,6 +14,29 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/3f/95338030883d8c8b91223b4e21744b04d11b161a3ef117295d8241f50ab4/accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7", size = 1395903 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "agno"
|
||||
version = "1.2.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "docstring-parser" },
|
||||
{ name = "gitpython" },
|
||||
{ name = "httpx" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "pydantic-settings" },
|
||||
{ name = "python-dotenv" },
|
||||
{ name = "python-multipart" },
|
||||
{ name = "pyyaml" },
|
||||
{ name = "rich" },
|
||||
{ name = "tomli" },
|
||||
{ name = "typer" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/56/52971b8f6d17af618a3f88aaa55a06b388f1c2871db509efe6f792d1e662/agno-1.2.6.tar.gz", hash = "sha256:0e83cf44e2957823814b47a8321be23e7faba0ff144845e60eea585fb86fdf57", size = 441431 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/ca/c0bf8045f6ca7cf8e99fe2f60c1162eca7f056c6b447fad27e0fd81e88ec/agno-1.2.6-py3-none-any.whl", hash = "sha256:b008416f01f4a6b2ff106777586ccc0d93d50f114944140dd95e2ff8adf7802a", size = 586946 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alabaster"
|
||||
version = "0.7.16"
|
||||
@@ -449,6 +472,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "docstring-parser"
|
||||
version = "0.16"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/08/12/9c22a58c0b1e29271051222d8906257616da84135af9ed167c9e28f85cb3/docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e", size = 26565 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/7c/e9fcff7623954d86bdc17782036cbf715ecab1bec4847c008557affe1ca8/docstring_parser-0.16-py3-none-any.whl", hash = "sha256:bf0a1387354d3691d102edef7ec124f219ef639982d096e26e3b60aeffa90637", size = 36533 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "docutils"
|
||||
version = "0.20.1"
|
||||
@@ -524,6 +556,30 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gitdb"
|
||||
version = "4.0.12"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "smmap" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gitpython"
|
||||
version = "3.1.44"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "gitdb" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c0/89/37df0b71473153574a5cdef8f242de422a0f5d26d7a9e231e6f169b4ad14/gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269", size = 214196 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/9a/4114a9057db2f1462d5c8f8390ab7383925fe1ac012eaa42402ad65c2963/GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110", size = 207599 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "greenlet"
|
||||
version = "3.1.1"
|
||||
@@ -1780,6 +1836,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/08/20/0f2523b9e50a8052bc6a8b732dfc8568abbdc42010aef03a2d750bdab3b2/python_json_logger-3.3.0-py3-none-any.whl", hash = "sha256:dd980fae8cffb24c13caf6e158d3d61c0d6d22342f932cb6e9deedab3d35eec7", size = 15163 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-multipart"
|
||||
version = "0.0.20"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pywin32"
|
||||
version = "310"
|
||||
@@ -2008,11 +2073,13 @@ version = "0.1.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "fastmcp" },
|
||||
{ name = "jinja2" },
|
||||
{ name = "mcp" },
|
||||
{ name = "overrides" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "pyright" },
|
||||
{ name = "python-dotenv" },
|
||||
{ name = "pyyaml" },
|
||||
{ name = "requests" },
|
||||
{ name = "sensai-utils" },
|
||||
{ name = "types-pyyaml" },
|
||||
@@ -2046,11 +2113,17 @@ dev = [
|
||||
{ name = "types-pyyaml" },
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
agno = [
|
||||
{ name = "agno" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "black", extras = ["jupyter"], marker = "extra == 'dev'", specifier = ">=23.7.0" },
|
||||
{ name = "docutils", marker = "extra == 'dev'", specifier = ">=0.20.1" },
|
||||
{ name = "fastmcp", specifier = ">=0.4.1" },
|
||||
{ name = "jinja2", specifier = ">=3.1.6" },
|
||||
{ name = "jinja2", marker = "extra == 'dev'" },
|
||||
{ name = "jupyter", marker = "extra == 'dev'", specifier = ">=1.0.0" },
|
||||
{ name = "jupyter-book", marker = "extra == 'dev'", specifier = "==1.0.0" },
|
||||
@@ -2066,6 +2139,7 @@ requires-dist = [
|
||||
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.2" },
|
||||
{ name = "pytest-cov", marker = "extra == 'dev'" },
|
||||
{ name = "python-dotenv", specifier = ">=1.0.0,<2" },
|
||||
{ name = "pyyaml", specifier = ">=6.0.2" },
|
||||
{ name = "requests", specifier = ">=2.32.3,<3" },
|
||||
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.0.285" },
|
||||
{ name = "sensai-utils", specifier = ">=1.4.0" },
|
||||
@@ -2084,6 +2158,9 @@ requires-dist = [
|
||||
]
|
||||
provides-extras = ["dev"]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
agno = [{ name = "agno", specifier = ">=1.2.6" }]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "78.0.1"
|
||||
@@ -2111,6 +2188,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smmap"
|
||||
version = "5.0.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sniffio"
|
||||
version = "1.3.1"
|
||||
|
||||
Reference in new issue
Block a user