Made more tools optional and shortened several docstrings

This commit is contained in:
MischaPanch authored and Michael Panchenko committed 2025-08-03 00:14:11 +02:00
1 parent 4410f37ab3
commit 99757f35eb
13 files changed
+52 -74

No files matched your search

+2 -1
View File
@@ -18,7 +18,7 @@ Run the following command to launch Serena as http server (assuming port 8000):
```bash
uvx mcpo --port 8000 --api-key <YOUR_SECRET_KEY> -- \
uvx --from git+https://github.com/oraios/serena \
serena start-mcp-server --project $(pwd)
serena start-mcp-server --context chatgpt --project $(pwd)
```
- `--api-key` is required to secure the server.
@@ -63,6 +63,7 @@ Your server is now securely exposed to the internet.
```
"servers": ["url": "<cloudflared_url>"],
```
**Important**: don't include a trailing slash at the end of the URL!
ChatGPT will read the schema and create functions automatically.
+3 -7
View File
@@ -9,7 +9,7 @@ from pprint import pprint
from serena.agent import SerenaAgent
from serena.config.serena_config import SerenaConfig
from serena.constants import REPO_ROOT
from serena.tools import FindFileTool, FindReferencingSymbolsTool, SearchForPatternTool
from serena.tools import FindFileTool, FindReferencingSymbolsTool, GetSymbolsOverviewTool, SearchForPatternTool
if __name__ == "__main__":
agent = SerenaAgent(project=REPO_ROOT, serena_config=SerenaConfig(gui_log_window_enabled=False, web_dashboard=False))
@@ -18,13 +18,9 @@ if __name__ == "__main__":
find_refs_tool = agent.get_tool(FindReferencingSymbolsTool)
find_file_tool = agent.get_tool(FindFileTool)
search_pattern_tool = agent.get_tool(SearchForPatternTool)
overview_tool = agent.get_tool(GetSymbolsOverviewTool)
result = agent.execute_task(
lambda: search_pattern_tool.apply(
r"def request_full_.*?\).*?\)",
restrict_search_to_code_files=False,
relative_path="src/solidlsp",
paths_include_glob="**/ls.py",
)
lambda: overview_tool.apply("src/solidlsp/ls.py"),
)
pprint(json.loads(result))
+1 -2
View File
@@ -3,8 +3,7 @@ Context and Mode configuration loader
"""
import os
from copy import copy
from dataclasses import asdict, dataclass, field
from dataclasses import dataclass, field
from enum import Enum
from pathlib import Path
from typing import TYPE_CHECKING, Self
@@ -3,6 +3,9 @@ description: Description of the context, not used in the code.
prompt: Prompt that will form part of the system prompt/initial instructions for agents started in this context.
excluded_tools: []
# several tools are excluded by default and have to be explicitly included by the user
included_optional_tools: []
# mapping of tool names to an override of their descriptions (the default description is the docstring of the Tool's apply method).
# Sometimes, tool descriptions are too long (e.g., for ChatGPT), or users may want to override them for another reason.
tool_description_overrides: {}
@@ -11,4 +11,7 @@ prompt: |
a diagram in addition to your text-based communication. Note that in the chat interface you have various rendering
options for text, html, and mermaid diagrams, as has been explained to you in your initial instructions.
excluded_tools: []
included_optional_tools:
- switch_modes
tool_description_overrides: {}
@@ -19,13 +19,8 @@ prompt: |
excluded_tools:
- create_text_file
- read_file
- delete_lines
- replace_lines
- insert_at_line
- execute_shell_command
- prepare_for_new_conversation
- summarize_changes
- get_current_config
tool_description_overrides: {}
@@ -2,3 +2,6 @@
description: Description of the mode, not used in the code.
prompt: Prompt that will form part of the message sent to the model when activating this mode.
excluded_tools: []
# several tools are excluded by default and have to be explicitly included by the user
included_optional_tools: []
+2 -9
View File
@@ -19,15 +19,8 @@ class ExecuteShellCommandTool(Tool, ToolMarkerCanEdit):
max_answer_chars: int = TOOL_DEFAULT_MAX_ANSWER_LENGTH,
) -> str:
"""
Execute a shell command and return its output.
IMPORTANT: you should always consider the memory about suggested shell commands before using this tool.
If this memory was not loaded in the current conversation, you should load it using the `read_memory` tool
before using this tool.
You should have at least once looked at the suggested shell commands from the corresponding memory
created during the onboarding process before using this tool.
Never execute unsafe shell commands like `rm -rf /` or similar! Generally be very careful with deletions.
Execute a shell command and return its output. If there is a memory about suggested commands, read that first.
Never execute unsafe shell commands like `rm -rf /` or similar!
:param command: the shell command to execute
:param cwd: the working directory to execute the command in. If None, the project root will be used.
+4 -4
View File
@@ -1,7 +1,7 @@
import json
from serena.config.context_mode import SerenaAgentMode
from serena.tools import Tool, ToolMarkerDoesNotRequireActiveProject
from serena.tools import Tool, ToolMarkerDoesNotRequireActiveProject, ToolMarkerOptional
class ActivateProjectTool(Tool, ToolMarkerDoesNotRequireActiveProject):
@@ -35,7 +35,7 @@ class ActivateProjectTool(Tool, ToolMarkerDoesNotRequireActiveProject):
return result_str
class RemoveProjectTool(Tool, ToolMarkerDoesNotRequireActiveProject):
class RemoveProjectTool(Tool, ToolMarkerDoesNotRequireActiveProject, ToolMarkerOptional):
"""
Removes a project from the Serena configuration.
"""
@@ -50,7 +50,7 @@ class RemoveProjectTool(Tool, ToolMarkerDoesNotRequireActiveProject):
return f"Successfully removed project '{project_name}' from configuration."
class SwitchModesTool(Tool):
class SwitchModesTool(Tool, ToolMarkerOptional):
"""
Activates modes by providing a list of their names
"""
@@ -71,7 +71,7 @@ class SwitchModesTool(Tool):
return result_str
class GetCurrentConfigTool(Tool):
class GetCurrentConfigTool(Tool, ToolMarkerOptional):
"""
Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
"""
+6 -14
View File
@@ -14,7 +14,7 @@ from fnmatch import fnmatch
from pathlib import Path
from serena.text_utils import search_files
from serena.tools import SUCCESS_RESULT, TOOL_DEFAULT_MAX_ANSWER_LENGTH, EditedFileContext, Tool, ToolMarkerCanEdit
from serena.tools import SUCCESS_RESULT, TOOL_DEFAULT_MAX_ANSWER_LENGTH, EditedFileContext, Tool, ToolMarkerCanEdit, ToolMarkerOptional
from serena.util.file_system import scan_directory
@@ -29,7 +29,6 @@ class ReadFileTool(Tool):
"""
Reads the given file or a chunk of it. Generally, symbolic operations
like find_symbol or find_referencing_symbols should be preferred if you know which symbols you are looking for.
Reading the entire file is only recommended if there is no other way to get the content required for the task.
:param relative_path: the relative path to the file to read
:param start_line: the 0-based index of the first line to be retrieved.
@@ -60,14 +59,7 @@ class CreateTextFileTool(Tool, ToolMarkerCanEdit):
def apply(self, relative_path: str, content: str) -> str:
"""
Write a new file (or overwrite an existing file). For existing files, it is strongly recommended
to use symbolic operations like replace_symbol_body or insert_after_symbol/insert_before_symbol, if possible.
You can also use insert_at_line to insert content at a specific line for existing files if the symbolic operations
are not the right choice for what you want to do.
If ever used on an existing file, the content has to be the complete content of that file (so it
may never end with something like "The remaining content of the file is left unchanged.").
For operations that just replace a part of a file, use the replace_lines or the symbolic editing tools instead.
Write a new file or overwrite an existing file.
:param relative_path: the relative path to the file to create
:param content: the (utf-8-encoded) content to write to the file
@@ -200,7 +192,7 @@ class ReplaceRegexTool(Tool, ToolMarkerCanEdit):
return SUCCESS_RESULT
class DeleteLinesTool(Tool, ToolMarkerCanEdit):
class DeleteLinesTool(Tool, ToolMarkerCanEdit, ToolMarkerOptional):
"""
Deletes a range of lines within a file.
"""
@@ -228,7 +220,7 @@ class DeleteLinesTool(Tool, ToolMarkerCanEdit):
return SUCCESS_RESULT
class ReplaceLinesTool(Tool, ToolMarkerCanEdit):
class ReplaceLinesTool(Tool, ToolMarkerCanEdit, ToolMarkerOptional):
"""
Replaces a range of lines within a file with new content.
"""
@@ -259,7 +251,7 @@ class ReplaceLinesTool(Tool, ToolMarkerCanEdit):
return SUCCESS_RESULT
class InsertAtLineTool(Tool, ToolMarkerCanEdit):
class InsertAtLineTool(Tool, ToolMarkerCanEdit, ToolMarkerOptional):
"""
Inserts content at a given line in a file.
"""
@@ -351,7 +343,7 @@ class SearchForPatternTool(Tool):
For example, for finding classes or methods from a name pattern.
Setting to False is a better choice if you also want to search in non-code files, like in html or yaml files,
which is why it is the default.
:return: A JSON object mapping file paths to lists of matched consecutive lines (with context, if requested).
:return: A mapping of file paths to lists of matched consecutive lines.
"""
abs_path = os.path.join(self.get_project_root(), relative_path)
if not os.path.exists(abs_path):
+2 -9
View File
@@ -10,15 +10,8 @@ class WriteMemoryTool(Tool):
def apply(self, memory_name: str, content: str, max_answer_chars: int = TOOL_DEFAULT_MAX_ANSWER_LENGTH) -> str:
"""
Write some information about this project that can be useful for future tasks to a memory.
Use markdown formatting for the content.
The information should be short and to the point.
The memory name should be meaningful, such that from the name you can infer what the information is about.
It is better to have multiple small memories than to have a single large one because
memories will be read one by one and we only ever want to read relevant memories.
This tool is either called during the onboarding process or when you have identified
something worth remembering about the project from the past conversation.
Write some information about this project that can be useful for future tasks to a memory in md format.
The memory name should be meaningful.
"""
if len(content) > max_answer_chars:
raise ValueError(
+22 -22
View File
@@ -4,6 +4,7 @@ Language server-related tools
import dataclasses
import json
import os
from collections.abc import Sequence
from copy import copy
from typing import Any
@@ -33,10 +34,7 @@ class RestartLanguageServerTool(Tool):
def apply(self) -> str:
"""Use this tool only on explicit user request or after confirmation.
It may be necessary to restart the language server if the user performs edits
not through Serena, so the language server state becomes outdated and further editing attempts lead to errors.
If such editing errors happen, you should suggest using this tool.
It may be necessary to restart the language server if it hangs.
"""
self.agent.reset_language_server()
return SUCCESS_RESULT
@@ -49,24 +47,28 @@ class GetSymbolsOverviewTool(Tool):
def apply(self, relative_path: str, max_answer_chars: int = TOOL_DEFAULT_MAX_ANSWER_LENGTH) -> str:
"""
Gets an overview of the given file or directory.
For each analyzed file, we list the top-level symbols in the file (name_path, kind).
Use this tool to get a high-level understanding of the code symbols.
Calling this is often a good idea before more targeted reading, searching or editing operations on the code symbols.
Before requesting a symbol overview, it is usually a good idea to narrow down the scope of the overview
by first understanding the basic directory structure of the repository that you can get from memories
or by using the `list_dir` and `find_file` tools (or similar).
Use this tool to get a high-level understanding of the code symbols in a file.
This should be the first tool to call when you want to understand a new file, unless you already know
what you are looking for.
:param relative_path: the relative path to the file or directory to get the overview of
:param max_answer_chars: if the overview is longer than this number of characters,
no content will be returned. Don't adjust unless there is really no other way to get the content
required for the task. If the overview is too long, you should use a smaller directory instead,
(e.g. a subdirectory).
:return: a JSON object mapping relative paths of all contained files to info about top-level symbols in the file (name_path, kind).
:return: a JSON object containing info about top-level symbols in the file
"""
symbol_retriever = self.create_language_server_symbol_retriever()
result = symbol_retriever.get_symbol_overview(relative_path)
result_json_str = json.dumps({k: [dataclasses.asdict(i) for i in l] for k, l in result.items()})
file_path = os.path.join(self.project.project_root, relative_path)
# The symbol overview is capable of working with both files and directories,
# but we want to ensure that the user provides a file path.
if not os.path.exists(file_path):
raise FileNotFoundError(f"File or directory {relative_path} does not exist in the project.")
if os.path.isdir(file_path):
raise ValueError(f"Expected a file path, but got a directory path: {relative_path}. ")
result = symbol_retriever.get_symbol_overview(relative_path)[relative_path]
result_json_str = json.dumps([dataclasses.asdict(i) for i in result])
return self._limit_length(result_json_str, max_answer_chars)
@@ -132,7 +134,7 @@ class FindSymbolTool(Tool):
:param exclude_kinds: Optional. List of LSP symbol kind integers to exclude. Takes precedence over `include_kinds`.
:param substring_matching: If True, use substring matching for the last segment of `name`.
:param max_answer_chars: Max characters for the JSON result. If exceeded, no content is returned.
:return: JSON string: a list of symbols (with locations) matching the name.
:return: a list of symbols (with locations) matching the name.
"""
parsed_include_kinds: Sequence[SymbolKind] | None = [SymbolKind(k) for k in include_kinds] if include_kinds else None
parsed_exclude_kinds: Sequence[SymbolKind] | None = [SymbolKind(k) for k in exclude_kinds] if exclude_kinds else None
@@ -164,10 +166,8 @@ class FindReferencingSymbolsTool(Tool):
max_answer_chars: int = TOOL_DEFAULT_MAX_ANSWER_LENGTH,
) -> str:
"""
Finds symbols that reference the symbol at the given `name_path`. The result will contain metadata about the referencing symbols
as well as a short code snippet around the reference (unless `include_body` is True, then the short snippet will be omitted).
Note that among other kinds of references, this function can be used to find (direct) subclasses of a class,
as subclasses are referencing symbols that have the kind class.
Finds references to the symbol at the given `name_path`. The result will contain metadata about the referencing symbols
as well as a short code snippet around the reference.
:param name_path: for finding the symbol to find references for, same logic as in the `find_symbol` tool.
:param relative_path: the relative path to the file containing the symbol for which to find references.
@@ -269,9 +269,9 @@ class InsertBeforeSymbolTool(Tool, ToolMarkerCanEdit):
body: str,
) -> str:
"""
Inserts the given body/content before the beginning of the definition of the given symbol (via the symbol's location).
A typical use case is to insert a new class, function, method, field or variable assignment.
It also can be used to insert a new import statement before the first symbol in the file.
Inserts the given content before the beginning of the definition of the given symbol (via the symbol's location).
A typical use case is to insert a new class, function, method, field or variable assignment; or
a new import statement before the first symbol in the file.
:param name_path: name path of the symbol before which to insert content (definitions in the `find_symbol` tool apply)
:param relative_path: the relative path to the file containing the symbol
+1 -1
View File
@@ -96,7 +96,7 @@ class ThinkAboutWhetherYouAreDoneTool(Tool):
return self.prompt_factory.create_think_about_whether_you_are_done()
class SummarizeChangesTool(Tool):
class SummarizeChangesTool(Tool, ToolMarkerOptional):
"""
Provides instructions for summarizing the changes made to the codebase.
"""