mirror of
https://github.com/Shubhamsaboo/awesome-llm-apps.git
synced 2026-03-11 17:48:31 -05:00
refactor: Enhance multi_mcp_agent with SQLite database integration and update requirements
- Integrated SQLite database for memory management in multi_mcp_agent. - Updated memory handling to use the new database instance.
This commit is contained in:
@@ -1,16 +1,11 @@
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
from typing import List, Optional
|
||||
from textwrap import dedent
|
||||
from agno.agent import Agent
|
||||
from agno.models.openai import OpenAIChat
|
||||
from agno.tools.mcp import MultiMCPTools
|
||||
from agno.memory.v2 import Memory
|
||||
from mcp import StdioServerParameters
|
||||
from agno.utils.pprint import apprint_run_response
|
||||
from agno.db.sqlite import SqliteDb
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables
|
||||
@@ -65,6 +60,9 @@ async def main():
|
||||
"npx @gongrzhe/server-gmail-autoauth-mcp"
|
||||
]
|
||||
|
||||
# Setup database for memory
|
||||
db = SqliteDb(db_file="tmp/multi_mcp_agent.db")
|
||||
|
||||
# Start the MCP Tools session
|
||||
async with MultiMCPTools(mcp_servers, env=env) as mcp_tools:
|
||||
print("✅ Successfully connected to all MCP servers!")
|
||||
@@ -126,10 +124,11 @@ async def main():
|
||||
REMEMBER: You're not just answering questions - you're a productivity multiplier. Think big, suggest workflows, and help users achieve more than they imagined possible!
|
||||
"""),
|
||||
markdown=True,
|
||||
show_tool_calls=True,
|
||||
debug_mode=True,
|
||||
retries=3,
|
||||
memory=Memory(),
|
||||
add_history_to_messages=True,
|
||||
db=db,
|
||||
enable_user_memories=True,
|
||||
add_history_to_context=True,
|
||||
num_history_runs=10, # Increased for better context retention
|
||||
)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
agno
|
||||
agno>=2.2.10
|
||||
openai
|
||||
mcp
|
||||
python-dotenv
|
||||
@@ -106,7 +106,3 @@ You can exit the conversation at any time by typing `exit`, `quit`, `bye`, or `g
|
||||
- "Add a comment to the first paragraph saying 'This looks good!'"
|
||||
- "Search for any mentions of meetings"
|
||||
- "Summarize our conversation so far"
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@@ -27,6 +27,7 @@ async def main():
|
||||
openai_api_key = OPENAI_API_KEY
|
||||
|
||||
# Prompt for page ID first
|
||||
page_id = None
|
||||
if len(sys.argv) > 1:
|
||||
# Use command-line argument if provided
|
||||
page_id = sys.argv[1]
|
||||
@@ -39,12 +40,13 @@ async def main():
|
||||
|
||||
user_input = input("> ")
|
||||
|
||||
# If user input is empty, use default
|
||||
# If user input is empty, prompt again
|
||||
if user_input.strip():
|
||||
page_id = user_input.strip()
|
||||
print(f"Using provided page ID: {page_id}")
|
||||
else:
|
||||
print(f"Using default page ID: {page_id}")
|
||||
print("❌ Error: Page ID is required. Please provide a Notion page ID.")
|
||||
return
|
||||
|
||||
# Generate unique user and session IDs for this terminal session
|
||||
user_id = f"user_{uuid.uuid4().hex[:8]}"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
agno
|
||||
agno>=2.2.10
|
||||
python-dotenv
|
||||
mcp
|
||||
openai
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import importlib.util
|
||||
from typing import Dict, Any
|
||||
|
||||
print("CWD:", os.getcwd(), file=sys.stderr)
|
||||
print("FILES IN CWD:", os.listdir(), file=sys.stderr)
|
||||
print("TOOLS_DIR:", os.path.join(os.path.dirname(__file__), 'tools'), file=sys.stderr)
|
||||
|
||||
TOOLS_DIR = os.path.join(os.path.dirname(__file__), 'tools')
|
||||
|
||||
class ToolManager:
|
||||
def __init__(self, tools_dir: str):
|
||||
self.tools_dir = tools_dir
|
||||
self.commands = {} # command_name -> (tool_name, function)
|
||||
self.load_tools()
|
||||
|
||||
def load_tools(self):
|
||||
if not os.path.isdir(self.tools_dir):
|
||||
return
|
||||
for tool_name in os.listdir(self.tools_dir):
|
||||
tool_path = os.path.join(self.tools_dir, tool_name)
|
||||
if not os.path.isdir(tool_path):
|
||||
continue
|
||||
mcp_json_path = os.path.join(tool_path, 'mcp.json')
|
||||
if not os.path.isfile(mcp_json_path):
|
||||
continue
|
||||
with open(mcp_json_path, 'r', encoding='utf-8') as f:
|
||||
meta = json.load(f)
|
||||
entry_point = meta.get('entry_point')
|
||||
if not entry_point:
|
||||
continue
|
||||
entry_path = os.path.join(tool_path, entry_point)
|
||||
if not os.path.isfile(entry_path):
|
||||
continue
|
||||
# Dynamically import the tool's entry point
|
||||
spec = importlib.util.spec_from_file_location(f"{tool_name}_module", entry_path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
# Register commands
|
||||
for cmd, cmd_meta in meta.get('commands', {}).items():
|
||||
if hasattr(module, cmd):
|
||||
self.commands[cmd] = (tool_name, getattr(module, cmd))
|
||||
|
||||
def dispatch(self, method: str, params: Dict[str, Any]):
|
||||
if method not in self.commands:
|
||||
raise Exception(f"Unknown command: {method}")
|
||||
tool_name, func = self.commands[method]
|
||||
return func(params)
|
||||
|
||||
def jsonrpc_response(result, id):
|
||||
response = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": id
|
||||
}
|
||||
if result is not None:
|
||||
response["result"] = result
|
||||
return json.dumps(response)
|
||||
|
||||
def jsonrpc_error(message, id=None, code=-32603):
|
||||
response = {
|
||||
"jsonrpc": "2.0",
|
||||
"error": {
|
||||
"code": code,
|
||||
"message": message
|
||||
}
|
||||
}
|
||||
# Only include id if it's not None (for notifications, id can be None)
|
||||
if id is not None:
|
||||
response["id"] = id
|
||||
return json.dumps(response)
|
||||
|
||||
def handle_initialize(params):
|
||||
return {
|
||||
"protocolVersion": "2025-06-18",
|
||||
"capabilities": {
|
||||
"tools": {},
|
||||
"resources": {},
|
||||
"prompts": {}
|
||||
},
|
||||
"serverInfo": {
|
||||
"name": "native-mcp-server",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
def handle_initialized(params):
|
||||
# Notification - no response needed
|
||||
return None
|
||||
|
||||
def handle_list_resources(params):
|
||||
return {"resources": []}
|
||||
|
||||
def handle_list_prompts(params):
|
||||
return {"prompts": []}
|
||||
|
||||
def handle_list_tools(params):
|
||||
tool_manager = ToolManager(TOOLS_DIR)
|
||||
tools = []
|
||||
for cmd, (tool_name, func) in tool_manager.commands.items():
|
||||
# Load the mcp.json to get command metadata
|
||||
tool_path = os.path.join(tool_manager.tools_dir, tool_name)
|
||||
mcp_json_path = os.path.join(tool_path, 'mcp.json')
|
||||
with open(mcp_json_path, 'r', encoding='utf-8') as f:
|
||||
meta = json.load(f)
|
||||
|
||||
cmd_meta = meta.get('commands', {}).get(cmd, {})
|
||||
tool_info = {
|
||||
"name": cmd,
|
||||
"description": cmd_meta.get('description', f'Tool: {cmd}'),
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": cmd_meta.get('params', {}),
|
||||
"required": list(cmd_meta.get('params', {}).keys())
|
||||
}
|
||||
}
|
||||
tools.append(tool_info)
|
||||
|
||||
return {"tools": tools}
|
||||
|
||||
def handle_call_tool(params):
|
||||
tool_manager = ToolManager(TOOLS_DIR)
|
||||
tool_name = params.get('name')
|
||||
arguments = params.get('arguments', {})
|
||||
|
||||
if tool_name not in tool_manager.commands:
|
||||
raise Exception(f"Unknown tool: {tool_name}")
|
||||
|
||||
result = tool_manager.dispatch(tool_name, arguments)
|
||||
return {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": json.dumps(result, indent=2)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
def main():
|
||||
tool_manager = ToolManager(TOOLS_DIR)
|
||||
|
||||
# MCP protocol handlers
|
||||
mcp_handlers = {
|
||||
"initialize": handle_initialize,
|
||||
"notifications/initialized": handle_initialized,
|
||||
"tools/list": handle_list_tools,
|
||||
"tools/call": handle_call_tool,
|
||||
"resources/list": handle_list_resources,
|
||||
"prompts/list": handle_list_prompts
|
||||
}
|
||||
|
||||
while True:
|
||||
try:
|
||||
line = sys.stdin.readline()
|
||||
if not line:
|
||||
break
|
||||
|
||||
# Skip empty lines
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
req = json.loads(line)
|
||||
method = req.get('method')
|
||||
params = req.get('params', {})
|
||||
id = req.get('id')
|
||||
|
||||
# Handle notifications (no id field, no response expected)
|
||||
if id is None and method:
|
||||
if method in mcp_handlers:
|
||||
try:
|
||||
mcp_handlers[method](params)
|
||||
except Exception as e:
|
||||
# Notifications don't send error responses
|
||||
print(f"Error in notification {method}: {e}", file=sys.stderr)
|
||||
continue
|
||||
|
||||
# Handle requests (have id field, response expected)
|
||||
if not method:
|
||||
sys.stdout.write(jsonrpc_error("Missing method", id) + '\n')
|
||||
sys.stdout.flush()
|
||||
continue
|
||||
|
||||
try:
|
||||
# Handle MCP protocol methods
|
||||
if method in mcp_handlers:
|
||||
result = mcp_handlers[method](params)
|
||||
if result is not None: # Only send response if not a notification
|
||||
sys.stdout.write(jsonrpc_response(result, id) + '\n')
|
||||
sys.stdout.flush()
|
||||
else:
|
||||
# Try to dispatch to tools (for backward compatibility)
|
||||
result = tool_manager.dispatch(method, params)
|
||||
sys.stdout.write(jsonrpc_response(result, id) + '\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
except Exception as e:
|
||||
sys.stdout.write(jsonrpc_error(str(e), id) + '\n')
|
||||
sys.stdout.flush()
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"JSON decode error: {e}", file=sys.stderr)
|
||||
sys.stdout.write(jsonrpc_error(f"Invalid JSON: {e}", None) + '\n')
|
||||
sys.stdout.flush()
|
||||
except Exception as e:
|
||||
print(f"Server error: {e}", file=sys.stderr)
|
||||
sys.stdout.write(jsonrpc_error(f"Server error: {e}", None) + '\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,59 +0,0 @@
|
||||
|
||||
# React Native MCP Agent
|
||||
|
||||
This project is an AI agent designed to interact with React Native applications using the Model Context Protocol (MCP). It enables advanced automation, reasoning, and integration capabilities for mobile app workflows.
|
||||
|
||||
## Features
|
||||
|
||||
- MCP server for agent communication
|
||||
- Native integration with React Native apps
|
||||
- Extensible agent logic in Python
|
||||
- Example usage and quickstart scripts
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Python 3.8+
|
||||
- React Native development environment
|
||||
- Node.js and npm
|
||||
|
||||
### Installation
|
||||
|
||||
1. Add the React Native MCP agent to your project:
|
||||
```bash
|
||||
npm install @mcp/react-native-agent
|
||||
```
|
||||
|
||||
2. No external dependencies required for core MCP server
|
||||
|
||||
### Usage
|
||||
|
||||
1. Import the agent in your React Native application:
|
||||
```javascript
|
||||
import { MCPAgent } from '@mcp/react-native-agent';
|
||||
```
|
||||
|
||||
2. Initialize the agent:
|
||||
```javascript
|
||||
const agent = new MCPAgent();
|
||||
await agent.initialize();
|
||||
```
|
||||
|
||||
3. Execute commands:
|
||||
```javascript
|
||||
const result = await agent.execute('analyze-screen');
|
||||
```
|
||||
|
||||
- For native integration, see `native.py` for example functions and usage.
|
||||
|
||||
## File Structure
|
||||
|
||||
- `mcp_server.py` — MCP server implementation
|
||||
- `native.py` — Native React Native integration logic
|
||||
|
||||
## Customization
|
||||
|
||||
- Extend `native.py` for additional React Native features.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user