Skip to content

Commit

Permalink
feat: Add MCP tools integration and update requirements
Browse files Browse the repository at this point in the history
  • Loading branch information
onuratakan committed Dec 8, 2024
1 parent b3b3c24 commit 944abfd
Show file tree
Hide file tree
Showing 5 changed files with 256 additions and 3 deletions.
4 changes: 3 additions & 1 deletion gpt_computer_assistant/agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from ..cu.computer import *
from ..teams import *
from .agent_tools import get_tools
from ..mcp.tool import mcp_tools

except ImportError:
from llm import get_model
Expand All @@ -17,6 +18,7 @@
from cu.computer import *
from teams import *
from agent.agent_tools import get_tools
from mcp.tool import mcp_tools


from langgraph.prebuilt import chat_agent_executor
Expand Down Expand Up @@ -62,7 +64,7 @@ def get_agent_executor():
pass


tools += [computer_tool]
tools += [computer_tool] + mcp_tools()


if (
Expand Down
245 changes: 245 additions & 0 deletions gpt_computer_assistant/mcp/tool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
import asyncio
import pathlib
import time
from typing import List, Any, Dict

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from langchain_mcp import MCPToolkit
from langchain_core.tools import BaseTool




from typing import Any, Dict, List
from langchain_core.tools import BaseTool
from pydantic import Field, PrivateAttr





class MCPToolWrapper(BaseTool):
"""A wrapper for an individual tool managed by the SyncInvocationManager."""
_manager: Any = PrivateAttr()
_tool: Any = PrivateAttr()

def __init__(self, tool: BaseTool, manager: "SyncInvocationManager"):
super().__init__(name=tool.name, description=tool.description)
self.name = tool.name
self.description = tool.description
self._manager = manager
self._tool = tool

def _run(self, **kwargs: Any) -> Any:
"""Run the tool synchronously using the SyncInvocationManager."""



try:
from ..gpt_computer_assistant import the_main_window
except ImportError:
from gpt_computer_assistant import the_main_window





function_name = "Tool: " + self.name
the_main_window.active_border_animation(function_name)
try:
result = self._manager.invoke_tool_sync(self._tool, kwargs)
except Exception as e:
time.sleep(1)
the_main_window.deactive_border_animation(function_name)
return e
time.sleep(1)
the_main_window.deactive_border_animation(function_name)

return result

async def _arun(self, **kwargs: Any) -> Any:
"""Asynchronous run (if needed), wraps the synchronous call."""
return self._run(**kwargs)


class MCPToolManager:
"""Manages tools provided by the SyncInvocationManager and converts them into LangChain tools."""

def __init__(self, manager: "SyncInvocationManager"):
self.manager = manager
self.tools: List[BaseTool] = []

def load_tools(self) -> List[BaseTool]:
"""Load tools from SyncInvocationManager and wrap them in LangChain-compatible structure."""
raw_tools = self.manager.get_tools_sync()

self.tools = [MCPToolWrapper(tool, self.manager) for tool in raw_tools]
return self.tools


class SyncInvocationManager:
def __init__(self, command: str, args: list[str], env: dict[str, str] | None = None):
self.loop = asyncio.new_event_loop()
self.server_params = StdioServerParameters(
command=command,
args=args,
env=env,

)
self.client_ctx = None
self.client = None
self.session_ctx = None
self.session = None
self.toolkit = None
self._task = None # Add this line


async def _start_async(self):
# Manually enter the stdio_client context
self.client_ctx = stdio_client(self.server_params)
self.client = await self.client_ctx.__aenter__()
read, write = self.client

# Manually enter the ClientSession context
self.session_ctx = ClientSession(read, write)
self.session = await self.session_ctx.__aenter__()

self.toolkit = MCPToolkit(session=self.session)
await self.toolkit.initialize()

def get_tools_sync(self) -> List[BaseTool]:
# Now that session is open, just return tools directly
return self.toolkit.get_tools()

def invoke_tool_sync(self, tool: BaseTool, input_data: Dict[str, Any]) -> Any:
return self.loop.run_until_complete(tool.ainvoke(input_data))

def start(self):
asyncio.set_event_loop(self.loop)
self._task = self.loop.create_task(self._start_async())
self.loop.run_until_complete(self._task)

def stop(self):
if self._task and not self._task.done():
cleanup_task = self.loop.create_task(self._stop_async())
self.loop.run_until_complete(cleanup_task)
self.loop.close()

async def _stop_async(self):
# Exit contexts in the same task and loop they were entered
if self.session_ctx:
await self.session_ctx.__aexit__(None, None, None)
if self.client_ctx:
await self.client_ctx.__aexit__(None, None, None)




def file_system_tool():
print("""
This is file_system_tool
""")


manager = SyncInvocationManager(command="npx", args=["-y", "@modelcontextprotocol/server-filesystem", str(pathlib.Path(__file__).parent.parent)])
manager.start()
tool_manager = MCPToolManager(manager)
tools = tool_manager.load_tools()
print(tools)
return tools


def memory_tool():

print("""
This is memory_tool
""")


manager = SyncInvocationManager(command="npx", args=["-y", "@modelcontextprotocol/server-memory"])
manager.start()
tool_manager = MCPToolManager(manager)
tools = tool_manager.load_tools()
print(tools)
return tools


def playwright():

print("""
This is playwright
""")

manager = SyncInvocationManager(command="npx", args=["-y", "@executeautomation/playwright-mcp-server"])
manager.start()
tool_manager = MCPToolManager(manager)
tools = tool_manager.load_tools()
print(tools)
return tools


def youtube_transcript():

print("""
This is youtube_transcript
""")

manager = SyncInvocationManager(command="npx", args=["-y", "@kimtaeyoon83/mcp-server-youtube-transcript"])
manager.start()
tool_manager = MCPToolManager(manager)
tools = tool_manager.load_tools()
print(tools)
return tools

def fetch():

print("""
This is fetch
""")

manager = SyncInvocationManager(command="uvx", args=["mcp-server-fetch"])
manager.start()
tool_manager = MCPToolManager(manager)
tools = tool_manager.load_tools()
print(tools)
return tools




def websearch():

print("""
This is websearch
""")


manager = SyncInvocationManager(command="npx", args=["-y", "@executeautomation/playwright-mcp-server"])
manager.start()
tool_manager = MCPToolManager(manager)
tools = tool_manager.load_tools()
print(tools)
return tools




the_tools_ = None
def mcp_tools():
global the_tools_
if the_tools_ is None:
the_tools_ = file_system_tool() + memory_tool() + playwright() + youtube_transcript() + fetch() + websearch()
return the_tools_
5 changes: 4 additions & 1 deletion requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,7 @@ langchain-anthropic==0.3.0



StrEnum==0.4.15
StrEnum==0.4.15


langchain-mcp==0.1.0a1
4 changes: 3 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,6 @@ screeninfo==0.8.1
anthropic==0.40.0
langchain-anthropic==0.3.0

StrEnum==0.4.15
StrEnum==0.4.15

langchain-mcp==0.1.0a1
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"gpt_computer_assistant",
"gpt_computer_assistant.agent",
"gpt_computer_assistant.cu",
"gpt_computer_assistant.mcp",
"gpt_computer_assistant.gui",
"gpt_computer_assistant.screen",
"gpt_computer_assistant.utils",
Expand Down

0 comments on commit 944abfd

Please sign in to comment.