fastmcp-agents-library-agent-simple-code 0.5.1__py3-none-any.whl → 0.5.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastmcp_agents/library/agent/simple_code/__init__.py +3 -0
- fastmcp_agents/library/agent/simple_code/archive/base.py.disabled +49 -0
- fastmcp_agents/library/agent/simple_code/archive/workspace.py.disabled +18 -0
- fastmcp_agents/library/agent/simple_code/helpers/filesystem.py +120 -0
- fastmcp_agents/library/agent/simple_code/helpers/git.py +10 -0
- fastmcp_agents/library/agent/simple_code/implement.py +141 -0
- fastmcp_agents/library/agent/simple_code/investigate.py +164 -0
- {fastmcp_agents_library_agent_simple_code-0.5.1.dist-info → fastmcp_agents_library_agent_simple_code-0.5.7.dist-info}/METADATA +6 -3
- fastmcp_agents_library_agent_simple_code-0.5.7.dist-info/RECORD +11 -0
- fastmcp_agents_library_agent_simple_code-0.5.7.dist-info/WHEEL +4 -0
- fastmcp_agents_library_agent_simple_code-0.5.7.dist-info/entry_points.txt +4 -0
- fastmcp_agents_library_agent_simple_code-0.5.1.dist-info/RECORD +0 -4
- fastmcp_agents_library_agent_simple_code-0.5.1.dist-info/WHEEL +0 -4
- fastmcp_agents_library_agent_simple_code-0.5.1.dist-info/entry_points.txt +0 -2
@@ -0,0 +1,49 @@
|
|
1
|
+
"""
|
2
|
+
This agent is used to perform simple code tasks.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from collections.abc import AsyncIterator
|
6
|
+
from contextlib import ExitStack, asynccontextmanager
|
7
|
+
from pathlib import Path
|
8
|
+
from typing import Any
|
9
|
+
|
10
|
+
from fastmcp import FastMCP
|
11
|
+
from fastmcp_ai_agent_bridge.pydantic_ai import FastMCPToolset
|
12
|
+
from pydantic import BaseModel, Field
|
13
|
+
from pydantic_ai import Agent
|
14
|
+
from pydantic_ai.agent import AgentRunResult
|
15
|
+
|
16
|
+
from fastmcp_agents.core.agents.base import Field
|
17
|
+
from fastmcp_agents.core.agents.task import TaskAgent
|
18
|
+
from fastmcp_agents.library.agent.simple_code.helpers.git import quick_clone_git_repo
|
19
|
+
from fastmcp_agents.library.mcp.strawgate import (
|
20
|
+
read_only_filesystem_mcp,
|
21
|
+
read_write_filesystem_mcp,
|
22
|
+
)
|
23
|
+
|
24
|
+
class BaseCodeAgent(TaskAgent):
|
25
|
+
"""An agent that can perform simple code tasks."""
|
26
|
+
|
27
|
+
clean_workspace: bool = Field(
|
28
|
+
default=True,
|
29
|
+
description="Whether to clean the workspace after the task is complete. Only applicable if a Git URL is provided.",
|
30
|
+
)
|
31
|
+
|
32
|
+
@asynccontextmanager
|
33
|
+
async def prepare_workspace(self, *, git_url: str | None = None) -> AsyncIterator[tuple[Path, dict[str, Any] | None]]:
|
34
|
+
directory = Path.cwd()
|
35
|
+
|
36
|
+
with ExitStack() as stack:
|
37
|
+
if git_url is not None:
|
38
|
+
self.logger.info(f"Cloning git repository from {git_url}")
|
39
|
+
context_manager = quick_clone_git_repo(git_url, set_cwd=True, delete_on_exit=self.clean_workspace)
|
40
|
+
|
41
|
+
git_repo = stack.enter_context(context_manager)
|
42
|
+
|
43
|
+
self.logger.info(f"Cloned git repository to {git_repo}")
|
44
|
+
|
45
|
+
tools = await self.get_tools()
|
46
|
+
|
47
|
+
structure = await tools["get_structure"].run(arguments={})
|
48
|
+
|
49
|
+
yield directory, structure.structured_content
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
from collections.abc import AsyncIterator
|
3
|
+
from contextlib import ExitStack, asynccontextmanager
|
4
|
+
from pathlib import Path
|
5
|
+
from typing import Any
|
6
|
+
|
7
|
+
from fastmcp_agents.library.agent.simple_code.helpers.git import quick_clone_git_repo
|
8
|
+
|
9
|
+
|
10
|
+
@asynccontextmanager
|
11
|
+
async def prepare_workspace(*, git_url: str | None = None, clean_workspace: bool = True) -> AsyncIterator[tuple[Path, dict[str, Any] | None]]:
|
12
|
+
directory = Path.cwd()
|
13
|
+
|
14
|
+
with ExitStack() as stack:
|
15
|
+
if git_url is not None:
|
16
|
+
context_manager = quick_clone_git_repo(git_url=git_url, set_cwd=True, delete_on_exit=clean_workspace)
|
17
|
+
|
18
|
+
_ = stack.enter_context(cm=context_manager)
|
@@ -0,0 +1,120 @@
|
|
1
|
+
from collections.abc import Generator
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
from fastmcp.mcp_config import TransformingStdioMCPServer
|
5
|
+
from fastmcp.tools.tool_transform import ArgTransformConfig, ToolTransformConfig
|
6
|
+
from pydantic import BaseModel, Field, computed_field
|
7
|
+
|
8
|
+
|
9
|
+
def read_write_filesystem_mcp(root_dir: Path | None = None) -> TransformingStdioMCPServer:
|
10
|
+
additional_args: list[str] = []
|
11
|
+
if root_dir is not None:
|
12
|
+
additional_args.append(f"--root-dir={root_dir.resolve()}")
|
13
|
+
|
14
|
+
return TransformingStdioMCPServer(
|
15
|
+
command="uvx",
|
16
|
+
args=["filesystem-operations-mcp", *additional_args],
|
17
|
+
tools={
|
18
|
+
"search_files": ToolTransformConfig(
|
19
|
+
arguments={
|
20
|
+
"included_types": ArgTransformConfig(hide=True),
|
21
|
+
"excluded_types": ArgTransformConfig(hide=True),
|
22
|
+
},
|
23
|
+
),
|
24
|
+
"find_files": ToolTransformConfig(
|
25
|
+
arguments={
|
26
|
+
"included_types": ArgTransformConfig(hide=True),
|
27
|
+
"excluded_types": ArgTransformConfig(hide=True),
|
28
|
+
},
|
29
|
+
),
|
30
|
+
},
|
31
|
+
)
|
32
|
+
|
33
|
+
|
34
|
+
def read_only_filesystem_mcp(root_dir: Path | None = None) -> TransformingStdioMCPServer:
|
35
|
+
mcp = read_write_filesystem_mcp(root_dir=root_dir)
|
36
|
+
|
37
|
+
mcp.tools = {
|
38
|
+
"find_files": ToolTransformConfig(
|
39
|
+
tags={"allowed_tools"},
|
40
|
+
arguments={
|
41
|
+
"included_types": ArgTransformConfig(hide=True),
|
42
|
+
"excluded_types": ArgTransformConfig(hide=True),
|
43
|
+
},
|
44
|
+
),
|
45
|
+
"search_files": ToolTransformConfig(
|
46
|
+
tags={"allowed_tools"},
|
47
|
+
arguments={
|
48
|
+
"included_types": ArgTransformConfig(hide=True),
|
49
|
+
"excluded_types": ArgTransformConfig(hide=True),
|
50
|
+
},
|
51
|
+
),
|
52
|
+
"get_structure": ToolTransformConfig(
|
53
|
+
tags={"allowed_tools"},
|
54
|
+
),
|
55
|
+
"get_file": ToolTransformConfig(
|
56
|
+
tags={"allowed_tools"},
|
57
|
+
),
|
58
|
+
"read_file_lines": ToolTransformConfig(
|
59
|
+
tags={"allowed_tools"},
|
60
|
+
),
|
61
|
+
}
|
62
|
+
|
63
|
+
mcp.include_tags = {"allowed_tools"}
|
64
|
+
|
65
|
+
return mcp
|
66
|
+
|
67
|
+
|
68
|
+
class FileStructure(BaseModel):
|
69
|
+
"""A file structure."""
|
70
|
+
|
71
|
+
results: list[str]
|
72
|
+
max_results: int = Field(description="The maximum number of results to return.", exclude=True)
|
73
|
+
|
74
|
+
@computed_field
|
75
|
+
@property
|
76
|
+
def limit_reached(self) -> bool:
|
77
|
+
"""Check if the limit has been reached."""
|
78
|
+
|
79
|
+
return len(self.results) >= self.max_results
|
80
|
+
|
81
|
+
|
82
|
+
def get_structure(root_dir: Path, max_results: int = 150) -> FileStructure:
|
83
|
+
"""Get the structure of a directory."""
|
84
|
+
|
85
|
+
results: list[str] = []
|
86
|
+
|
87
|
+
for item in limited_depth_iterdir(path=root_dir, max_depth=3):
|
88
|
+
if len(results) >= max_results:
|
89
|
+
break
|
90
|
+
if item.is_file():
|
91
|
+
results.append(item.name)
|
92
|
+
elif item.is_dir():
|
93
|
+
results.append(item.name + "/")
|
94
|
+
|
95
|
+
return FileStructure(results=results, max_results=max_results)
|
96
|
+
|
97
|
+
|
98
|
+
def limited_depth_iterdir(
|
99
|
+
path: Path,
|
100
|
+
max_depth: int = 3,
|
101
|
+
current_depth: int = 0,
|
102
|
+
) -> Generator[Path]:
|
103
|
+
"""
|
104
|
+
Iterates through directory contents up to a specified maximum depth.
|
105
|
+
|
106
|
+
Args:
|
107
|
+
path (Path): The starting directory path.
|
108
|
+
max_depth (int): The maximum depth to traverse (0 for current directory only).
|
109
|
+
current_depth (int): The current depth during recursion (internal use).
|
110
|
+
|
111
|
+
Yields:
|
112
|
+
Path: A path object for each file or directory within the depth limit.
|
113
|
+
"""
|
114
|
+
if current_depth > max_depth:
|
115
|
+
return
|
116
|
+
|
117
|
+
for item in path.iterdir():
|
118
|
+
yield item
|
119
|
+
if item.is_dir():
|
120
|
+
yield from limited_depth_iterdir(path=item, max_depth=max_depth, current_depth=current_depth + 1)
|
@@ -0,0 +1,10 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
|
3
|
+
import git
|
4
|
+
from git import Repo
|
5
|
+
|
6
|
+
|
7
|
+
def quick_clone_git_repo(target_dir: Path, git_url: str) -> Repo:
|
8
|
+
"""Quickly clone a git repository."""
|
9
|
+
|
10
|
+
return git.Repo.clone_from(url=git_url, to_path=target_dir, single_branch=True, depth=1)
|
@@ -0,0 +1,141 @@
|
|
1
|
+
import os
|
2
|
+
import tempfile
|
3
|
+
from pathlib import Path
|
4
|
+
from textwrap import dedent
|
5
|
+
from typing import Annotated, Any, Literal
|
6
|
+
|
7
|
+
from fastmcp import FastMCP
|
8
|
+
from fastmcp.tools.tool import Tool
|
9
|
+
from fastmcp_ai_agent_bridge.pydantic_ai import FastMCPToolset
|
10
|
+
from git import Repo
|
11
|
+
from pydantic import AnyHttpUrl, BaseModel, Field, RootModel
|
12
|
+
from pydantic_ai.agent import Agent
|
13
|
+
|
14
|
+
from fastmcp_agents.library.agent.simple_code.helpers.filesystem import (
|
15
|
+
get_structure,
|
16
|
+
read_write_filesystem_mcp,
|
17
|
+
)
|
18
|
+
|
19
|
+
code_implementation_instructions = """
|
20
|
+
You are an expert software engineer. You are able to handle a wide variety of tasks related to software development.
|
21
|
+
|
22
|
+
Your goal is to study the assigned task, gather the necessary information to properly understand the task,
|
23
|
+
produce a viable plan to complete the task and then execute the plan.
|
24
|
+
|
25
|
+
You have access to the local filesystem, the tools you have available can search, summarize, create, read, update, and delete
|
26
|
+
files and directories as needed.
|
27
|
+
|
28
|
+
For any task, it will be extremely important for you to gather the necessary information from the codebase:
|
29
|
+
1. Bug Fixes: If you are asked to fix a bug you will first understand the bug. You will review the different ways the relevant code
|
30
|
+
can be invoked and you will understand when and why the bug occurs and when and why it does not occur. You will first think
|
31
|
+
of a test that will fail if the bug is present and pass if the bug is not present. If the codebase includes tests, you will
|
32
|
+
write the test.
|
33
|
+
|
34
|
+
2. Propose a solution to a problem: If you are asked to propose a solution to a problem you will first understand the problem. You will
|
35
|
+
try to understand the cause of the problem.
|
36
|
+
|
37
|
+
3. Refactor code: If you are asked to refactor code you will first understand the code. You will review the different areas of the code
|
38
|
+
that are relevant to the refactoring and you will understand how the different parts of the code interact. You will then propose a
|
39
|
+
refactoring plan.
|
40
|
+
|
41
|
+
4. Implement a new feature: If you are asked to implement a new feature you will first understand the feature. You will review the different
|
42
|
+
areas of the code that are relevant to the feature and you will understand how the different parts of the code interact. You will then
|
43
|
+
propose an implementation plan.
|
44
|
+
|
45
|
+
If the plan is more than a couple steps, you will then write the plan to a Markdown file called `plan.md`. You will read this plan after
|
46
|
+
writing it to ensure that it accurately reflects the plan. Once you have a plan, you will execute the plan performing the necessary steps
|
47
|
+
to complete the task.
|
48
|
+
|
49
|
+
Once you believe you have completed the task you will step through the code line by line ensuring that the task is completed. You will
|
50
|
+
re-read the written plan to make sure you have not missed any steps. When calling report_task_success you will enumerate the action you
|
51
|
+
took to resolve each part of the task. If you have not completed a part of the task, you will continue working on that part.
|
52
|
+
"""
|
53
|
+
|
54
|
+
mcp_servers = {
|
55
|
+
"filesystem": read_write_filesystem_mcp(),
|
56
|
+
}
|
57
|
+
|
58
|
+
|
59
|
+
class FileLines(RootModel[list[str]]):
|
60
|
+
"""A file line with line number and content."""
|
61
|
+
|
62
|
+
|
63
|
+
class PotentialFlaw(BaseModel):
|
64
|
+
"""A potential flaw in the code."""
|
65
|
+
|
66
|
+
description: str
|
67
|
+
file_path: str | None = None
|
68
|
+
lines: FileLines = Field(default=..., description="The relevant lines of code in the file with their line numbers.")
|
69
|
+
|
70
|
+
|
71
|
+
class ImplementationResponse(BaseModel):
|
72
|
+
"""A response from the implementation agent."""
|
73
|
+
|
74
|
+
summary: str
|
75
|
+
confidence: Literal["low", "medium", "high"]
|
76
|
+
potential_flaws: list[PotentialFlaw] = Field(
|
77
|
+
default=..., description="A list of potential flaws in the code that a reviewer should review before merging."
|
78
|
+
)
|
79
|
+
|
80
|
+
|
81
|
+
code_implementation_agent: Agent[None, ImplementationResponse] = Agent[None, ImplementationResponse](
|
82
|
+
model=os.environ.get("MODEL"),
|
83
|
+
toolsets=[
|
84
|
+
# We will provide a directory-locked toolset at runtime
|
85
|
+
],
|
86
|
+
system_prompt=code_implementation_instructions,
|
87
|
+
output_type=ImplementationResponse,
|
88
|
+
retries=2,
|
89
|
+
output_retries=2,
|
90
|
+
)
|
91
|
+
|
92
|
+
server = FastMCP[Any](name="investigate-code-agent")
|
93
|
+
|
94
|
+
CODE_REPOSITORY_TYPE = Annotated[
|
95
|
+
AnyHttpUrl | Path | None,
|
96
|
+
Field(
|
97
|
+
description=dedent(
|
98
|
+
text="""
|
99
|
+
The code repository to investigate.
|
100
|
+
|
101
|
+
If a Git URL is provided, it will be cloned.
|
102
|
+
A local path can also be provided.
|
103
|
+
If neither is provided, the agent will look in the current working directory."""
|
104
|
+
).strip(),
|
105
|
+
),
|
106
|
+
]
|
107
|
+
|
108
|
+
|
109
|
+
async def implement_code(task: str, code_repository: CODE_REPOSITORY_TYPE) -> ImplementationResponse:
|
110
|
+
"""Perform a code investigation."""
|
111
|
+
|
112
|
+
with tempfile.TemporaryDirectory(delete=False) as temp_dir:
|
113
|
+
# We only actually use the tempdir if we are cloning a git repository
|
114
|
+
if isinstance(code_repository, AnyHttpUrl):
|
115
|
+
Repo.clone_from(url=str(code_repository), to_path=temp_dir, single_branch=True, depth=1)
|
116
|
+
code_repository = Path(temp_dir)
|
117
|
+
if code_repository is None:
|
118
|
+
code_repository = Path.cwd()
|
119
|
+
|
120
|
+
directory_locked_toolset = FastMCPToolset.from_mcp_config(
|
121
|
+
mcp_config={"filesystem": read_write_filesystem_mcp(root_dir=code_repository)}
|
122
|
+
)
|
123
|
+
|
124
|
+
structure = get_structure(root_dir=code_repository, max_results=10)
|
125
|
+
|
126
|
+
repo_info = f"""
|
127
|
+
We've called get_structure on your behalf, here were the initial results to get you started:
|
128
|
+
{structure}
|
129
|
+
"""
|
130
|
+
|
131
|
+
run_result = await code_implementation_agent.run(user_prompt=[task, repo_info], toolsets=[directory_locked_toolset])
|
132
|
+
|
133
|
+
return run_result.output
|
134
|
+
|
135
|
+
|
136
|
+
implement_code_tool = Tool.from_function(fn=implement_code)
|
137
|
+
|
138
|
+
server.add_tool(tool=implement_code_tool)
|
139
|
+
|
140
|
+
if __name__ == "__main__":
|
141
|
+
server.run(transport="sse")
|
@@ -0,0 +1,164 @@
|
|
1
|
+
"""
|
2
|
+
This agent is used to perform simple code tasks.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import os
|
6
|
+
import tempfile
|
7
|
+
from pathlib import Path
|
8
|
+
from textwrap import dedent
|
9
|
+
from typing import Annotated, Any, Literal
|
10
|
+
|
11
|
+
from fastmcp import FastMCP
|
12
|
+
from fastmcp.tools.tool import Tool
|
13
|
+
from fastmcp_ai_agent_bridge.pydantic_ai import FastMCPToolset
|
14
|
+
from git import Repo
|
15
|
+
from pydantic import AnyHttpUrl, BaseModel, Field, RootModel
|
16
|
+
from pydantic_ai import Agent
|
17
|
+
|
18
|
+
from fastmcp_agents.library.agent.simple_code.helpers.filesystem import (
|
19
|
+
get_structure,
|
20
|
+
read_only_filesystem_mcp,
|
21
|
+
)
|
22
|
+
|
23
|
+
mcp_servers = {
|
24
|
+
"filesystem": read_only_filesystem_mcp(),
|
25
|
+
}
|
26
|
+
|
27
|
+
toolset = FastMCPToolset.from_mcp_config(mcp_config=mcp_servers)
|
28
|
+
|
29
|
+
code_investigation_instructions = """
|
30
|
+
You are an expert software engineer. You are able to handle a wide variety of tasks related to software development.
|
31
|
+
|
32
|
+
Your goal is to study the assigned task, gather the necessary information to properly understand the task, and then
|
33
|
+
produce a viable plan to complete the task. You are to be thorough and do this right, you are not to concerned with
|
34
|
+
how much time it takes to complete the task.
|
35
|
+
|
36
|
+
You have access to the local filesystem, the tools you have available can search, summarize, and read files as needed.
|
37
|
+
|
38
|
+
For any task, it will be extremely important for you to gather the necessary information from the codebase:
|
39
|
+
1. Bug Fixes: If you are asked to fix a bug you will first understand the bug. You will review the different ways the relevant code
|
40
|
+
can be invoked and you will understand when and why the bug occurs and when and why it does not occur. You will first think
|
41
|
+
of a test that will fail if the bug is present and pass if the bug is not present. If the codebase includes tests, you will
|
42
|
+
write the test.
|
43
|
+
|
44
|
+
2. Propose a solution to a problem: If you are asked to propose a solution to a problem you will first understand the problem. You will
|
45
|
+
try to understand the cause of the problem.
|
46
|
+
|
47
|
+
3. Refactor code: If you are asked to plan a refactoring you will first understand the code. You will review the different areas of the code
|
48
|
+
that are relevant to the refactoring and you will understand how the different parts of the code interact. You will then propose a
|
49
|
+
refactoring plan.
|
50
|
+
|
51
|
+
4. Implement a new feature: If you are asked to implement a new feature you will first understand the feature. You will review the different
|
52
|
+
areas of the code that are relevant to the feature and you will understand how the different parts of the code interact. You will then
|
53
|
+
propose an implementation plan.
|
54
|
+
|
55
|
+
Once you believe you have completed the task you will step through the code line by line ensuring that the task is completed. If you have
|
56
|
+
not completed a part of the task, you will continue working on that part.
|
57
|
+
|
58
|
+
Once you have believe you have completed the task you will perform additional review of other files in the codebase, looking for any
|
59
|
+
references to the relevant code or tests that might need to be updated, or removed.
|
60
|
+
|
61
|
+
Remember, you cannot make any changes to the codebase. You can only read files.
|
62
|
+
"""
|
63
|
+
|
64
|
+
|
65
|
+
class FileLines(RootModel[list[str]]):
|
66
|
+
"""A file line with line number and content."""
|
67
|
+
|
68
|
+
|
69
|
+
class InvestigationFinding(BaseModel):
|
70
|
+
"""An investigation finding."""
|
71
|
+
|
72
|
+
description: str
|
73
|
+
file_path: str | None = None
|
74
|
+
lines: FileLines = Field(default=..., description="The relevant lines of code in the file with their line numbers.")
|
75
|
+
|
76
|
+
|
77
|
+
class InvestigationRecommendation(BaseModel):
|
78
|
+
"""An investigation recommendation."""
|
79
|
+
|
80
|
+
description: str
|
81
|
+
action: Literal["fix", "refactor", "propose", "implement"]
|
82
|
+
file_path: str | None = None
|
83
|
+
current_lines: FileLines = Field(default=..., description="The relevant lines of code in the file with their line numbers.")
|
84
|
+
proposed_lines: FileLines = Field(default=..., description="The proposed lines of code in the file with their line numbers.")
|
85
|
+
|
86
|
+
|
87
|
+
class InvestigationResponse(BaseModel):
|
88
|
+
"""An investigation response."""
|
89
|
+
|
90
|
+
summary: str = Field(default=..., description="A summary of the findings. Under 1 page.")
|
91
|
+
confidence: Literal["high", "medium", "low"] = Field(default=..., description="The confidence of the findings.")
|
92
|
+
findings: list[InvestigationFinding]
|
93
|
+
recommendations: list[InvestigationRecommendation] = Field(
|
94
|
+
default=..., description="Recommendations for next steps based on the findings."
|
95
|
+
)
|
96
|
+
|
97
|
+
|
98
|
+
code_investigation_agent = Agent[None, InvestigationResponse](
|
99
|
+
model=os.environ.get("MODEL"),
|
100
|
+
toolsets=[
|
101
|
+
# We will provide a directory-locked toolset at runtime
|
102
|
+
],
|
103
|
+
system_prompt=code_investigation_instructions,
|
104
|
+
output_type=InvestigationResponse,
|
105
|
+
retries=2,
|
106
|
+
output_retries=2,
|
107
|
+
)
|
108
|
+
|
109
|
+
server = FastMCP[Any](name="investigate-code-agent")
|
110
|
+
|
111
|
+
CODE_REPOSITORY_TYPE = Annotated[
|
112
|
+
AnyHttpUrl | Path | None,
|
113
|
+
Field(
|
114
|
+
description=dedent(
|
115
|
+
text="""
|
116
|
+
The code repository to investigate.
|
117
|
+
|
118
|
+
If a Git URL is provided, it will be cloned.
|
119
|
+
A local path can also be provided.
|
120
|
+
If neither is provided, the agent will look in the current working directory."""
|
121
|
+
).strip(),
|
122
|
+
),
|
123
|
+
]
|
124
|
+
|
125
|
+
|
126
|
+
async def investigate_code(task: str, code_repository: CODE_REPOSITORY_TYPE) -> InvestigationResponse:
|
127
|
+
"""Perform a code investigation."""
|
128
|
+
|
129
|
+
with tempfile.TemporaryDirectory(delete=False) as temp_dir:
|
130
|
+
# We only actually use the tempdir if we are cloning a git repository
|
131
|
+
if isinstance(code_repository, AnyHttpUrl):
|
132
|
+
Repo.clone_from(url=str(code_repository), to_path=temp_dir, single_branch=True, depth=1)
|
133
|
+
code_repository = Path(temp_dir)
|
134
|
+
if code_repository is None:
|
135
|
+
code_repository = Path.cwd()
|
136
|
+
|
137
|
+
directory_locked_toolset = FastMCPToolset.from_mcp_config(
|
138
|
+
mcp_config={"filesystem": read_only_filesystem_mcp(root_dir=code_repository)}
|
139
|
+
)
|
140
|
+
|
141
|
+
structure = get_structure(root_dir=code_repository, max_results=10)
|
142
|
+
|
143
|
+
repo_info = f"""
|
144
|
+
We've called get_structure on your behalf, here were the initial results to get you started:
|
145
|
+
{structure}
|
146
|
+
"""
|
147
|
+
|
148
|
+
run_result = await code_investigation_agent.run(user_prompt=[task, repo_info], toolsets=[directory_locked_toolset])
|
149
|
+
|
150
|
+
return run_result.output
|
151
|
+
|
152
|
+
|
153
|
+
investigate_code_tool = Tool.from_function(fn=investigate_code)
|
154
|
+
|
155
|
+
server.add_tool(tool=investigate_code_tool)
|
156
|
+
|
157
|
+
|
158
|
+
def run():
|
159
|
+
"""Run the agent."""
|
160
|
+
server.run(transport="stdio")
|
161
|
+
|
162
|
+
|
163
|
+
if __name__ == "__main__":
|
164
|
+
server.run(transport="sse")
|
@@ -1,7 +1,10 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.3
|
2
2
|
Name: fastmcp-agents-library-agent-simple-code
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.7
|
4
4
|
Summary: Agents for Simple Code
|
5
|
-
Requires-Python: >=3.13
|
6
5
|
Requires-Dist: fastmcp-ai-agent-bridge-pydantic-ai>=0.1.2
|
7
6
|
Requires-Dist: gitpython>=3.1.44
|
7
|
+
Requires-Dist: fastmcp-agents-library-mcp
|
8
|
+
Requires-Python: >=3.13
|
9
|
+
Description-Content-Type: text/markdown
|
10
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
fastmcp_agents/library/agent/simple_code/__init__.py,sha256=ccaec5397000d34f1eb8ff399642a582cda38cbc2b3bca0efa2d593dbce3db6c,90
|
2
|
+
fastmcp_agents/library/agent/simple_code/archive/base.py.disabled,sha256=7d6504f79b96062090f69c4b8626817bee6fda7c99a7c77ecaa5dc163511c21d,1744
|
3
|
+
fastmcp_agents/library/agent/simple_code/archive/workspace.py.disabled,sha256=d295351841e1cc42926175e2a12fc91e13f00619df6af0976afac47bac3f8f20,659
|
4
|
+
fastmcp_agents/library/agent/simple_code/helpers/filesystem.py,sha256=ced0f82524a1355cb6cc2a2a5f61b61a6c507455e49cdfdb2d0bf12695586757,3771
|
5
|
+
fastmcp_agents/library/agent/simple_code/helpers/git.py,sha256=7d3264b3b29df8025752018a51e677201f8761c07f549559260de06e6a88502f,262
|
6
|
+
fastmcp_agents/library/agent/simple_code/implement.py,sha256=5267dbc76ca6fdebeb76282846068cc96901503e3004da5ac65cb04e242c5e94,5801
|
7
|
+
fastmcp_agents/library/agent/simple_code/investigate.py,sha256=c17855e96e54c426b8c77dbf378d3bb1080ffe88bd39a65ccc9bfe1a80987851,6460
|
8
|
+
fastmcp_agents_library_agent_simple_code-0.5.7.dist-info/WHEEL,sha256=03f80d698bb4300c27bcafc3987ea73ef427fcb365e59e808e50d041a7f87b89,78
|
9
|
+
fastmcp_agents_library_agent_simple_code-0.5.7.dist-info/entry_points.txt,sha256=6a34307eece27892df63ae0af789d4ee3b839693877c185288303c27b92b5ada,166
|
10
|
+
fastmcp_agents_library_agent_simple_code-0.5.7.dist-info/METADATA,sha256=d7b5af111b2cd83faf728ace6518c73ebeaadb8eeb9c7df0c148bf87d1f5e687,314
|
11
|
+
fastmcp_agents_library_agent_simple_code-0.5.7.dist-info/RECORD,,
|
@@ -1,4 +0,0 @@
|
|
1
|
-
fastmcp_agents_library_agent_simple_code-0.5.1.dist-info/METADATA,sha256=Cq-2DBJOA0orqZGwnUi7thKP7xbGzTNYE76RI6jBsyE,231
|
2
|
-
fastmcp_agents_library_agent_simple_code-0.5.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
3
|
-
fastmcp_agents_library_agent_simple_code-0.5.1.dist-info/entry_points.txt,sha256=J00qZFTV53NbUERBJEIzUgWG5fyRNTn10uDfC33QkLo,78
|
4
|
-
fastmcp_agents_library_agent_simple_code-0.5.1.dist-info/RECORD,,
|