Skip to content

Commit 0d170ca

Browse files
committed
Add client basics
1 parent 35bd73a commit 0d170ca

File tree

1 file changed

+96
-0
lines changed

1 file changed

+96
-0
lines changed

backend/src/neuroagent/mcp.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
"""MCP client logic."""
2+
3+
import asyncio
4+
import json
5+
from contextlib import AsyncExitStack
6+
from pathlib import Path
7+
8+
from mcp import ClientSession, StdioServerParameters
9+
from mcp.client.stdio import stdio_client
10+
from pydantic import BaseModel
11+
12+
13+
class MCPServerConfig(BaseModel):
14+
command: str
15+
args: list[str] | None = None
16+
env: dict[str, str] | None = None
17+
18+
19+
class MCPConfig(BaseModel):
20+
servers: dict[str, MCPServerConfig]
21+
22+
@classmethod
23+
def parse_file(cls, config_path: Path) -> "MCPConfig":
24+
"""Parse the configuration file and return an MCPConfig object."""
25+
if not config_path.exists():
26+
raise FileNotFoundError(f"Configuration file {config_path} does not exist.")
27+
dct = json.loads(config_path.read_text())
28+
29+
return cls(**dct)
30+
31+
32+
class MCPClient:
33+
def __init__(self, config_path: Path):
34+
# Initialize session and client objects
35+
self.config = MCPConfig.parse_file(config_path)
36+
self.exit_stack: dict[str, AsyncExitStack] = {
37+
name: AsyncExitStack() for name in self.config.servers.keys()
38+
}
39+
self.sessions: dict[str, ClientSession] = {}
40+
41+
async def connect_to_servers(self) -> None:
42+
"""Connect to an MCP server
43+
44+
Args:
45+
server_script_path: Path to the server script (.py or .js)
46+
"""
47+
for name, server_config in self.config.servers.items():
48+
print(f"Connecting to server: {name}")
49+
server_params = StdioServerParameters(
50+
command=server_config.command,
51+
args=server_config.args or [],
52+
env=server_config.env or None,
53+
)
54+
55+
stdio_transport = await self.exit_stack[name].enter_async_context(
56+
stdio_client(server_params)
57+
)
58+
self.sessions[name] = await self.exit_stack[name].enter_async_context(
59+
ClientSession(*stdio_transport)
60+
)
61+
62+
await self.sessions[name].initialize()
63+
64+
# List available tools
65+
response = await self.sessions[name].list_tools()
66+
tools = response.tools
67+
print("\nConnected to server with tools:", [tool.name for tool in tools])
68+
69+
async def cleanup(self):
70+
"""Clean up resources"""
71+
# await self.exit_stack.aclose()
72+
73+
for name, stack in self.exit_stack.items():
74+
print(f"Cleaning up server: {name}")
75+
await stack.aclose()
76+
77+
78+
async def main():
79+
# Path to the configuration file
80+
config_path = Path("mcp_config.json")
81+
82+
# Create an instance of MCPClient
83+
mcp_client = MCPClient(config_path)
84+
85+
# Connect to the server
86+
await mcp_client.connect_to_servers()
87+
88+
# # Clean up resources
89+
# await mcp_client.cleanup()
90+
91+
while True:
92+
pass
93+
94+
95+
if __name__ == "__main__":
96+
asyncio.run(main())

0 commit comments

Comments
 (0)