-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathserver.py
More file actions
124 lines (112 loc) · 5.97 KB
/
server.py
File metadata and controls
124 lines (112 loc) · 5.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#!/usr/bin/env python3
"""Main MCP file server class."""
import logging
from pathlib import Path
from typing import Optional, Any
from models import MCPMessage
from tools import get_tool_definitions
from handlers import FileHandlers
from git_handlers import GitHandlers
logger = logging.getLogger(__name__)
class MCPFileServer:
"""
File‑system MCP server with enhanced security features.
The working directory can be supplied on start‑up (via CLI) or defaults to the current
process directory. All file operations are sandboxed inside this directory — any attempt
to access a path outside results in an "Access denied" error.
"""
def __init__(self, work_dir: Optional[Path] = None):
# Resolve once at start‑up so we always compare absolute paths.
self.working_directory = (work_dir or Path.cwd()).resolve()
self.handlers = FileHandlers(self.working_directory)
self.git_handlers = GitHandlers(self.working_directory)
logger.info(f"Server started with working directory: {self.working_directory}")
async def handle_message(self, message: MCPMessage) -> Optional[MCPMessage]:
"""Dispatch incoming RPC messages to the appropriate handler."""
try:
if message.method == "initialize":
return self.handle_initialize(message)
elif message.method == "tools/list":
return self.handle_tools_list(message)
elif message.method == "tools/call":
return await self.handle_tool_call(message)
else:
return self.handlers.create_error_response(
message.id, -32601, f"Method not found: {message.method}"
)
except Exception as e:
logger.error(f"Error handling message: {e}")
return self.handlers.create_error_response(
message.id, -32603, f"Internal error: {str(e)}"
)
def handle_initialize(self, message: MCPMessage) -> MCPMessage:
"""Respond to the `initialize` request."""
result = {
"protocolVersion": "2024-11-05",
"capabilities": {"tools": {}},
"serverInfo": {"name": "file-server", "version": "1.2.0"},
}
return MCPMessage(id=message.id, result=result)
def handle_tools_list(self, message: MCPMessage) -> MCPMessage:
"""Return the list of tools that the server supports."""
tools = get_tool_definitions()
result = {"tools": [tool.to_dict() for tool in tools]}
return MCPMessage(id=message.id, result=result)
async def handle_tool_call(self, message: MCPMessage) -> MCPMessage:
"""Dispatch a tool call to the concrete implementation."""
try:
params = message.params
tool_name = params.get("name")
arguments = params.get("arguments", {})
if tool_name == "read_file":
return await self.handlers.handle_read_file(message.id, arguments)
elif tool_name == "write_file":
return await self.handlers.handle_write_file(message.id, arguments)
elif tool_name == "list_files":
return self.handlers.handle_list_files(message.id, arguments)
elif tool_name == "create_directory":
return self.handlers.handle_create_directory(message.id, arguments)
elif tool_name == "delete_file":
return await self.handlers.handle_delete_file(message.id, arguments)
elif tool_name == "delete_directory":
return await self.handlers.handle_delete_directory(message.id, arguments)
elif tool_name == "search_in_file":
return await self.handlers.handle_search_in_file(message.id, arguments)
elif tool_name == "git_status":
return self.git_handlers.handle_git_status(message.id, arguments)
elif tool_name == "git_log":
return self.git_handlers.handle_git_log(message.id, arguments)
elif tool_name == "git_checkout":
return self.git_handlers.handle_git_checkout(message.id, arguments)
elif tool_name == "git_branch_create":
return self.git_handlers.handle_git_branch_create(message.id, arguments)
elif tool_name == "git_branch_delete":
return self.git_handlers.handle_git_branch_delete(message.id, arguments)
elif tool_name == "git_branch_list":
return self.git_handlers.handle_git_branch_list(message.id, arguments)
elif tool_name == "git_add":
return self.git_handlers.handle_git_add(message.id, arguments)
elif tool_name == "git_commit":
return self.git_handlers.handle_git_commit(message.id, arguments)
elif tool_name == "git_push":
return self.git_handlers.handle_git_push(message.id, arguments)
elif tool_name == "git_pull":
return self.git_handlers.handle_git_pull(message.id, arguments)
elif tool_name == "git_diff":
return self.git_handlers.handle_git_diff(message.id, arguments)
elif tool_name == "git_clone":
return self.git_handlers.handle_git_clone(message.id, arguments)
elif tool_name == "git_submodule_add":
return self.git_handlers.handle_git_submodule_add(message.id, arguments)
elif tool_name == "git_submodule_update":
return self.git_handlers.handle_git_submodule_update(message.id, arguments)
elif tool_name == "git_submodule_list":
return self.git_handlers.handle_git_submodule_list(message.id, arguments)
else:
return self.handlers.create_error_response(
message.id, -32602, f"Unknown tool: {tool_name}"
)
except Exception as e:
return self.handlers.create_error_response(
message.id, -32603, f"Error processing tool call: {str(e)}"
)