Merge pull request #392 from oraios/feature/chatgpt-support

Connecting Serena to chatgpt
This commit is contained in:
Michael Panchenko authored and GitHub committed 2025-08-03 13:28:55 +02:00
commit 59bc24e019
20 files changed
+255 -111

No files matched your search

+102
View File
@@ -0,0 +1,102 @@
# Connecting Serena MCP Server to ChatGPT via MCPO & Cloudflare Tunnel
This guide explains how to expose a **locally running Serena MCP server** (powered by MCPO) to the internet using **Cloudflare Tunnel**, and how to connect it to **ChatGPT as a Custom GPT with tool access**.
Once configured, ChatGPT becomes a powerful **coding agent** with direct access to your codebase, shell, and file system — so **read the security notes carefully**.
---
## Prerequisites
Make sure you have [uv](https://docs.astral.sh/uv/getting-started/installation/)
and [cloudflared](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/) installed.
## 1. Start the Serena MCP Server via MCPO
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 --context chatgpt --project $(pwd)
```
- `--api-key` is required to secure the server.
- `--project` should point to the root of your codebase.
You can also use other options, and you don't have to pass `--project` if you want to work on multiple projects
or want to activate it later. See
```shell
uvx --from git+https://github.com/oraios/serena serena start-mcp-server --help
```
---
## 2. Expose the Server Using Cloudflare Tunnel
Run:
```bash
cloudflared tunnel --url http://localhost:8000
```
This will give you a **public HTTPS URL** like:
```
https://serena-agent-tunnel.trycloudflare.com
```
Your server is now securely exposed to the internet.
---
## 3. Connect It to ChatGPT (Custom GPT)
### Steps:
1. Go to [ChatGPT → Explore GPTs → Create](https://chat.openai.com/gpts/editor)
2. During setup, click **“Add APIs”**
3. Set up **API Key authentication** with the auth type as **Bearer** and enter the api key you used to start the MCPO server.
4. In the **Schema** section, click on **import from URL** and paste `<cloudflared_url>/openapi.json` with the URL you got from the previous step.
5. Add the following line to the top of the imported JSON schema:
```
"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.
---
## Security Warning — Read Carefully
Depending on your configuration and enabled tools, Serena's MCP server may:
- Execute **arbitrary shell commands**
- Read, write, and modify **files in your codebase**
This gives ChatGPT the same powers as a remote developer on your machine.
### ⚠️ Key Rules:
- **NEVER expose your API key**
- **Only expose this server when needed**, and monitor its use.
In your project’s `.serena/project.yml` or global config, you can disable tools like:
```yaml
excluded_tools:
- execute_shell_command
- ...
read_only: true
```
This is strongly recommended if you want a read-only or safer agent.
---
## Final Thoughts
With this setup, ChatGPT becomes a coding assistant **running on your local code** — able to index, search, edit, and even run shell commands depending on your configuration.
Use responsibly, and keep security in mind.
+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))
+10
View File
@@ -227,6 +227,12 @@ class SerenaAgent:
except Exception as e:
log.error(f"Error activating project '{project}' at startup: {e}", exc_info=e)
def get_context(self) -> SerenaAgentContext:
return self._context
def get_tool_description_override(self, tool_name: str) -> str | None:
return self._context.tool_description_overrides.get(tool_name, None)
def _check_shell_settings(self) -> None:
# On Windows, Claude Code sets COMSPEC to Git-Bash (often even with a path containing spaces),
# which causes all sorts of trouble, preventing language servers from being launched correctly.
@@ -583,3 +589,7 @@ class SerenaAgent:
if self._gui_log_viewer:
log.info("Stopping the GUI log window ...")
self._gui_log_viewer.stop()
def get_tool_by_name(self, tool_name: str) -> Tool:
tool_class = ToolRegistry().get_tool_class_by_name(tool_name)
return self.get_tool(tool_class)
+23 -1
View File
@@ -26,7 +26,7 @@ from serena.constants import (
USER_CONTEXT_YAMLS_DIR,
USER_MODE_YAMLS_DIR,
)
from serena.mcp import SerenaMCPFactorySingleProcess
from serena.mcp import SerenaMCPFactory, SerenaMCPFactorySingleProcess
from serena.project import Project
from serena.tools import ToolRegistry
from serena.util.logging import MemoryLogHandler
@@ -478,6 +478,28 @@ class ToolCommands(AutoRegisteringGroup):
else:
ToolRegistry().print_tool_overview()
@staticmethod
@click.command(
"description",
help="Print the description of a tool, optionally with a specific context (the latter may modify the default description).",
)
@click.argument("tool_name", type=str)
@click.option("--context", type=str, default=None, help="Context name or path to context file.")
def description(tool_name: str, context: str | None = None) -> None:
# Load the context
serena_context = None
if context:
serena_context = SerenaAgentContext.load(context)
agent = SerenaAgent(
project=None,
serena_config=SerenaConfig(web_dashboard=False, log_level=logging.INFO),
context=serena_context,
)
tool = agent.get_tool_by_name(tool_name)
mcp_tool = SerenaMCPFactory.make_mcp_tool(tool)
click.echo(mcp_tool.description)
class PromptCommands(AutoRegisteringGroup):
def __init__(self) -> None:
+6 -24
View File
@@ -3,8 +3,7 @@ Context and Mode configuration loader
"""
import os
from copy import copy
from dataclasses import asdict, dataclass
from dataclasses import dataclass, field
from enum import Enum
from pathlib import Path
from typing import TYPE_CHECKING, Self
@@ -44,17 +43,6 @@ class SerenaAgentMode(ToolInclusionDefinition, ToStringMixin):
def _tostring_includes(self) -> list[str]:
return ["name"]
def to_json_dict(self) -> dict[str, str | list[str]]:
result = asdict(self)
result["excluded_tools"] = list(result["excluded_tools"])
return result
@classmethod
def from_json_dict(cls, data: dict) -> Self:
data = copy(data)
data["excluded_tools"] = set(data["excluded_tools"])
return cls(**data)
def print_overview(self) -> None:
"""Print an overview of the mode."""
print(f"{self.name}:\n {self.description}")
@@ -134,27 +122,21 @@ class SerenaAgentContext(ToolInclusionDefinition, ToStringMixin):
name: str
prompt: str
description: str = ""
tool_description_overrides: dict[str, str] = field(default_factory=dict)
"""Maps tool names to custom descriptions, default descriptions are extracted from the tool docstrings."""
def _tostring_includes(self) -> list[str]:
return ["name"]
def to_json_dict(self) -> dict[str, str | list[str]]:
result = asdict(self)
result["excluded_tools"] = list(result["excluded_tools"])
return result
@classmethod
def from_json_dict(cls, data: dict) -> Self:
data = copy(data)
data["excluded_tools"] = set(data["excluded_tools"])
return cls(**data)
@classmethod
def from_yaml(cls, yaml_path: str | Path) -> Self:
"""Load a context from a YAML file."""
with open(yaml_path, encoding="utf-8") as f:
data = yaml.safe_load(f)
name = data.pop("name", Path(yaml_path).stem)
# Ensure backwards compatibility for tool_description_overrides
if "tool_description_overrides" not in data:
data["tool_description_overrides"] = {}
return cls(name=name, **data)
@classmethod
+10 -2
View File
@@ -68,10 +68,17 @@ class SerenaMCPFactory:
# Mount the tool description as a combination of the docstring description and
# the return value description, if it exists.
if docstring.description:
func_doc = f"{docstring.description.strip().strip('.')}."
overridden_description = tool.agent.get_context().tool_description_overrides.get(func_name, None)
if overridden_description is not None:
func_doc = overridden_description
elif docstring.description:
func_doc = docstring.description
else:
func_doc = ""
func_doc = func_doc.strip().strip(".")
if func_doc:
func_doc += "."
if docstring.returns and (docstring_returns_descr := docstring.returns.description):
# Only add a space before "Returns" if func_doc is not empty
prefix = " " if func_doc else ""
@@ -113,6 +120,7 @@ class SerenaMCPFactory:
for tool in self._iter_tools():
mcp_tool = self.make_mcp_tool(tool)
mcp._tool_manager._tools[tool.get_name()] = mcp_tool
log.info(f"Starting MCP server with {len(mcp._tool_manager._tools)} tools: {list(mcp._tool_manager._tools.keys())}")
@abstractmethod
def _instantiate_agent(self, serena_config: SerenaConfig, modes: list[SerenaAgentMode]) -> None:
@@ -3,4 +3,6 @@ prompt: |
You are running in agent context where the system prompt is provided externally. You should use symbolic
tools when possible for code understanding and modification.
excluded_tools:
- initial_instructions
- initial_instructions
tool_description_overrides: {}
@@ -0,0 +1,28 @@
description: A configuration specific for chatgpt, which has a limit of 30 tools and requires short descriptions.
prompt: |
You are running in desktop app context where the tools give you access to the code base as well as some
access to the file system, if configured. You interact with the user through a chat interface that is separated
from the code base. As a consequence, if you are in interactive mode, your communication with the user should
involve high-level thinking and planning as well as some summarization of any code edits that you make.
For viewing the code edits the user will view them in a separate code editor window, and the back-and-forth
between the chat and the code editor should be minimized as well as facilitated by you.
If complex changes have been made, advise the user on how to review them in the code editor.
If complex relationships that the user asked for should be visualized or explained, consider creating
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:
find_symbol: |
Retrieves symbols matching `name_path` in a file.
Use `depth > 0` to include children. `name_path` can be: "foo": any symbol named "foo"; "foo/bar": "bar" within "foo"; "/foo/bar": only top-level "foo/bar"
replace_regex: |
Replaces text using regular expressions. Preferred for smaller edits where symbol-level tools aren't appropriate.
Use wildcards (.*?) to match large sections efficiently: "beginning.*?end" instead of specifying exact content.
Essential for multi-line replacements.
search_for_pattern: |
Flexible pattern search across codebase. Prefer symbolic operations when possible.
Uses DOTALL matching. Use non-greedy quantifiers (.*?) to avoid over-matching.
Supports file filtering via globs and code-only restriction.
@@ -1,4 +1,11 @@
# See Serena's documentation for more details on concept of contexts.
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: []
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: {}
@@ -10,4 +10,8 @@ prompt: |
If complex relationships that the user asked for should be visualized or explained, consider creating
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: []
excluded_tools: []
included_optional_tools:
- switch_modes
tool_description_overrides: {}
@@ -19,10 +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.
"""
@@ -7,7 +7,6 @@ import json
import logging
import os
import pathlib
import stat
import subprocess
import threading
+14 -9
View File
@@ -4,16 +4,28 @@ import pytest
from mcp.server.fastmcp.tools.base import Tool as MCPTool
from serena.agent import Tool, ToolRegistry
from serena.config.context_mode import SerenaAgentContext
from serena.mcp import SerenaMCPFactory
make_tool = SerenaMCPFactory.make_mcp_tool
# Create a mock agent for tool initialization
class MockAgent:
def __init__(self):
self.project_config = None
self.serena_config = None
@staticmethod
def get_context() -> SerenaAgentContext:
return SerenaAgentContext.load_default()
class BaseMockTool(Tool):
"""A mock Tool class for testing."""
def __init__(self) -> None:
pass
def __init__(self):
super().__init__(MockAgent())
class BasicTool(BaseMockTool):
@@ -274,13 +286,6 @@ def is_test_mock_class(tool_class: type) -> bool:
@pytest.mark.parametrize("tool_class", ToolRegistry().get_all_tool_classes())
def test_make_tool_all_tools(tool_class) -> None:
"""Test that make_tool works for all tools in the codebase."""
# Create a mock agent for tool initialization
class MockAgent:
def __init__(self):
self.project_config = None
self.serena_config = None
# Create an instance of the tool
tool_instance = tool_class(MockAgent())