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:
Dominik Jain committed 2025-03-29 23:28:13 +01:00
commit af4f405421
8 files changed
+207 -11

No files matched your search

+83 -4
View File
@@ -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"]
}
}
}
+7 -1
View File
@@ -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.
+8
View File
@@ -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"
+13
View File
@@ -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)
-5
View File
@@ -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!")
+6 -1
View File
@@ -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())
+4
View File
@@ -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()
Generated
+86
View File
@@ -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"