From 12c4790b43f4eac909e559c31a7696f25ffbfc4e Mon Sep 17 00:00:00 2001 From: vga91 Date: Wed, 4 Feb 2026 16:06:48 +0100 Subject: [PATCH 1/2] cleanup --- .../openai-agents-with-mcp-main/README.md | 119 ++++++++++++++---- .../interactive_main-2.py | 78 ++++++++++++ .../interactive_main.py | 57 +++++---- .../my_advanced_tools-1.py | 50 ++++++++ .../my_advanced_tools.py | 61 +++++---- 5 files changed, 293 insertions(+), 72 deletions(-) create mode 100644 open-ai-sdk/openai-agents-with-mcp-main/interactive_main-2.py create mode 100644 open-ai-sdk/openai-agents-with-mcp-main/my_advanced_tools-1.py diff --git a/open-ai-sdk/openai-agents-with-mcp-main/README.md b/open-ai-sdk/openai-agents-with-mcp-main/README.md index 63da30f..6fb06c0 100644 --- a/open-ai-sdk/openai-agents-with-mcp-main/README.md +++ b/open-ai-sdk/openai-agents-with-mcp-main/README.md @@ -1,32 +1,107 @@ -# OpenAI Agents w/ MCP +# OpenAI Agents + Neo4j MCP: Dual Server Architecture -Example app for using OpenAI Agents with MCP Server(s). +This project demonstrates how to build an advanced AI Agent using the **OpenAI Python SDK** that connects to **Neo4j** using the **Model Context Protocol (MCP)**. +It implements a **Dual Server Architecture**: +1. **Standard Server (`neo4j-mcp`)**: Provides out-of-the-box tools for schema exploration and basic read/write operations. +2. **Custom Server (`my_advanced_tools.py`)**: Implements custom logic (e.g., GraphRAG, Vector Search, complex traversals) using `FastMCP`. -## Requirements -- [uv](https://docs.astral.sh/uv/) -- [OpenAI API Key](https://platform.openai.com) -- [Neo4j Database](https://neo4j.com) -- [Github Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) +## Prerequisites + +* Python 3.10+ +* [uv](https://docs.astral.sh/uv/) (Recommended for dependency management) +* A Neo4j Database (AuraDB or Local) +* OpenAI API Key ## Installation -1. Download or clone this repo -2. Run -``` -uv sync -``` -## Running Simple Example -Runs an interactive CLI agent with a single MCP Stdio server -``` -uv run python main_simple.py -``` +1. **Clone the repository** and navigate to the folder. + +2. **Install dependencies** using `uv`: + ```bash + uv add openai-agents mcp neo4j fastmcp python-dotenv + ``` + *(Or use `pip install ...` if not using uv)* + +3. **Ensure the standard MCP server is installed**: + ```bash + uv pip install mcp-neo4j-cypher + # or ensure "neo4j-mcp" command is available in your path + ``` + +## Configuration + +Create a `.env` file in the root directory: + +```env +OPENAI_API_KEY=sk-proj-... +OPENAI_MODEL=gpt-4o + +# Neo4j Credentials +NEO4J_URI=neo4j+s://your-instance.databases.neo4j.io +NEO4J_USERNAME=neo4j +NEO4J_PASSWORD=your-password +NEO4J_DATABASE=neo4j # or your specific database name -## Running Custom Multi Server Example -Runs an interactive CLI agent with multiple mixed MCP servers with a custom MCP Manager ``` -uv run python main_multi.py + +## Architecture Overview + +### 1. `interactive_main.py` (The Agent) + +This is the entry point. It configures the **OpenAI Agent** with two MCP connections: + +* It launches the standard `neo4j-mcp` toolset via `uvx` or command line. +* It launches the custom Python script (`my_advanced_tools.py`) as a subprocess. +* It orchestrates the conversation, allowing the LLM to choose between standard schema inspection and advanced analysis. + +### 2. `my_advanced_tools.py` (Custom Logic) + +This file uses `FastMCP` to expose specific Python functions as tools. + +* **`graph_rag_search`**: A tool designed for complex queries. Instead of letting the LLM write arbitrary Cypher (which can be error-prone for vectors), this tool encapsulates the logic (Embedding + Vector Index Search + Graph Traversal) in a safe, controlled function. + +## Usage + +Run the interactive agent: + +```bash +uv run python interactive_main.py + ``` -## License -MIT License \ No newline at end of file +You will see a prompt: `User:`. The agent is now ready. + +## Example Prompts + +Here are some questions to test if the Agent is correctly switching between the two servers. + +### Discovery (Uses Standard Server) + +*These prompts force the agent to look at the database structure.* + +* "Show me the database schema and list all relationship types." +* "How many nodes labeled 'Company' are currently in the database?" +* "List the properties available on the 'Company' nodes." + +### Analysis (Uses Custom GraphRAG Tool) + +*These prompts force the agent to use your custom Python logic.* + +* "Use the advanced search tool to find companies related to 'cloud services' and list their competitors." +* "Analyze the competitors of 'Google' using the GraphRAG logic and tell me who they are." +* "Find companies similar to 'Apple' based on their description and show their connections." + +### Hybrid (Uses Both) + +*The agent must explore first, then analyze.* + +* "First, check if a company named 'Salesforce' exists. If yes, use the advanced tool to find its main competitors." +* "I want to compare 'Google' and 'Microsoft'. Run the advanced analysis for both and summarize the common competitors." + +## Troubleshooting + +* **`ModuleNotFoundError: No module named 'fastmcp'`**: +Ensure you ran `uv add fastmcp` and that `interactive_main.py` is using `sys.executable` in the command parameters. +* **`Connection closed` error**: +This usually means the `my_advanced_tools.py` script crashed on startup. Try running it manually to see the error: `uv run python my_advanced_tools.py`. diff --git a/open-ai-sdk/openai-agents-with-mcp-main/interactive_main-2.py b/open-ai-sdk/openai-agents-with-mcp-main/interactive_main-2.py new file mode 100644 index 0000000..efb95c3 --- /dev/null +++ b/open-ai-sdk/openai-agents-with-mcp-main/interactive_main-2.py @@ -0,0 +1,78 @@ +from agents import Agent, Runner +from agents.mcp import MCPServerStdio +from dotenv import load_dotenv +import asyncio +import os +import sys # Necessario per il server custom + +load_dotenv() + +async def interactive_main(): + + # --- SERVER 1: IL TUO ORIGINALE (Standard) --- + # Questo è il blocco esatto che avevi all'inizio e che funzionava. + async with MCPServerStdio( + name="neo4j_standard", + cache_tools_list=True, + params={ + "type": "stdio", + "command": "neo4j-mcp", # <--- Il tuo comando originale + "args": [], # <--- I tuoi argomenti originali + "env": { + "NEO4J_URI": "neo4j+s://demo.neo4jlabs.com:7687", + "NEO4J_USERNAME": "companies", + "NEO4J_PASSWORD": "companies", + "NEO4J_DATABASE": "companies" + } + }, + ) as standard_server: + + # --- SERVER 2: IL NUOVO CUSTOM (GraphRAG) --- + # Questo aggiunge le funzioni extra descritte nel PDF + async with MCPServerStdio( + name="neo4j_custom", + params={ + "type": "stdio", + "command": sys.executable, # Usa lo stesso python dell'ambiente + "args": [ + os.path.join(os.path.dirname(__file__), "my_advanced_tools.py") + ], + "env": { + # Passiamo le stesse variabili anche al server custom + "NEO4J_URI": "neo4j+s://demo.neo4jlabs.com:7687", + "NEO4J_USERNAME": "companies", + "NEO4J_PASSWORD": "companies", + "NEO4J_DATABASE": "companies", + "OPENAI_API_KEY": os.environ.get("OPENAI_API_KEY") + } + } + ) as custom_server: + + # --- AGENTE UNIFICATO --- + agent = Agent( + name="OpenAI + MCP Agent", + instructions=( + "Sei un esperto di dati aziendali. " + "Per esplorare il database o fare query semplici, usa i tool standard. " + "Se l'utente fa domande complesse, vaghe o che richiedono ragionamento (es. GraphRAG), " + "usa il tool 'graph_rag_search' dal server custom." + ), + # Qui passiamo ENTRAMBI i server + mcp_servers=[standard_server, custom_server], + model=os.environ.get("OPENAI_MODEL"), + ) + + print("\nType your request (or 'exit' to quit):") + while True: + user_input = input("👶 You: ").strip() + if user_input.lower() in {"exit", "quit"}: + print("Exiting interactive session.") + break + try: + result = await Runner.run(starting_agent=agent, input=user_input) + print(f"\n🤖 Agent: {result.final_output}\n") + except Exception as e: + print(f"\nError processing request: {str(e)}\n") + +if __name__ == "__main__": + asyncio.run(interactive_main()) \ No newline at end of file diff --git a/open-ai-sdk/openai-agents-with-mcp-main/interactive_main.py b/open-ai-sdk/openai-agents-with-mcp-main/interactive_main.py index efb95c3..a839d88 100644 --- a/open-ai-sdk/openai-agents-with-mcp-main/interactive_main.py +++ b/open-ai-sdk/openai-agents-with-mcp-main/interactive_main.py @@ -3,74 +3,77 @@ from dotenv import load_dotenv import asyncio import os -import sys # Necessario per il server custom +import sys load_dotenv() async def interactive_main(): - # --- SERVER 1: IL TUO ORIGINALE (Standard) --- - # Questo è il blocco esatto che avevi all'inizio e che funzionava. + # --- SERVER 1: STANDARD NEO4J SERVER --- + # Used for general exploration (schema, basic Cypher queries). async with MCPServerStdio( name="neo4j_standard", cache_tools_list=True, params={ "type": "stdio", - "command": "neo4j-mcp", # <--- Il tuo comando originale - "args": [], # <--- I tuoi argomenti originali + "command": "neo4j-mcp", # Ensure this is installed via pip/uv + "args": [], "env": { - "NEO4J_URI": "neo4j+s://demo.neo4jlabs.com:7687", - "NEO4J_USERNAME": "companies", - "NEO4J_PASSWORD": "companies", - "NEO4J_DATABASE": "companies" + "NEO4J_URI": os.environ.get("NEO4J_URI"), + "NEO4J_USERNAME": os.environ.get("NEO4J_USERNAME"), + "NEO4J_PASSWORD": os.environ.get("NEO4J_PASSWORD"), + "NEO4J_DATABASE": os.environ.get("NEO4J_DATABASE", "neo4j"), } }, ) as standard_server: - # --- SERVER 2: IL NUOVO CUSTOM (GraphRAG) --- - # Questo aggiunge le funzioni extra descritte nel PDF + # --- SERVER 2: CUSTOM PYTHON SERVER (GraphRAG) --- + # Used for advanced logic, vector search, and complex traversals. async with MCPServerStdio( name="neo4j_custom", params={ "type": "stdio", - "command": sys.executable, # Usa lo stesso python dell'ambiente + "command": sys.executable, # Uses the current Python environment "args": [ os.path.join(os.path.dirname(__file__), "my_advanced_tools.py") ], "env": { - # Passiamo le stesse variabili anche al server custom - "NEO4J_URI": "neo4j+s://demo.neo4jlabs.com:7687", - "NEO4J_USERNAME": "companies", - "NEO4J_PASSWORD": "companies", - "NEO4J_DATABASE": "companies", + "NEO4J_URI": os.environ.get("NEO4J_URI"), + "NEO4J_USERNAME": os.environ.get("NEO4J_USERNAME"), + "NEO4J_PASSWORD": os.environ.get("NEO4J_PASSWORD"), + "NEO4J_DATABASE": os.environ.get("NEO4J_DATABASE", "neo4j"), "OPENAI_API_KEY": os.environ.get("OPENAI_API_KEY") } } ) as custom_server: - # --- AGENTE UNIFICATO --- + # --- UNIFIED AGENT --- agent = Agent( - name="OpenAI + MCP Agent", + name="OpenAI + Neo4j Advanced Agent", instructions=( - "Sei un esperto di dati aziendali. " - "Per esplorare il database o fare query semplici, usa i tool standard. " - "Se l'utente fa domande complesse, vaghe o che richiedono ragionamento (es. GraphRAG), " - "usa il tool 'graph_rag_search' dal server custom." + "You are an expert business data analyst using a Neo4j Knowledge Graph. " + "You have access to two sets of tools:\n" + "1. Standard Tools: Use these to explore the database schema, check node labels, or run simple queries.\n" + "2. Advanced Tools: Use 'graph_rag_search' when the user asks complex, vague, or semantic questions " + "(e.g., 'find similar companies', 'analyze competitors').\n" + "Always choose the most appropriate tool for the specific task." ), - # Qui passiamo ENTRAMBI i server mcp_servers=[standard_server, custom_server], - model=os.environ.get("OPENAI_MODEL"), + model=os.environ.get("OPENAI_MODEL", "gpt-4o"), ) print("\nType your request (or 'exit' to quit):") + while True: - user_input = input("👶 You: ").strip() + user_input = input("User: ").strip() + if user_input.lower() in {"exit", "quit"}: print("Exiting interactive session.") break + try: result = await Runner.run(starting_agent=agent, input=user_input) - print(f"\n🤖 Agent: {result.final_output}\n") + print(f"\nAgent: {result.final_output}\n") except Exception as e: print(f"\nError processing request: {str(e)}\n") diff --git a/open-ai-sdk/openai-agents-with-mcp-main/my_advanced_tools-1.py b/open-ai-sdk/openai-agents-with-mcp-main/my_advanced_tools-1.py new file mode 100644 index 0000000..6150153 --- /dev/null +++ b/open-ai-sdk/openai-agents-with-mcp-main/my_advanced_tools-1.py @@ -0,0 +1,50 @@ +from fastmcp import FastMCP +from neo4j import GraphDatabase +import os + +# Definiamo il server +mcp = FastMCP("Neo4j Advanced Tools") + +# Funzione helper per ottenere il driver solo quando serve +def get_driver(): + uri = os.getenv("NEO4J_URI") + user = os.getenv("NEO4J_USERNAME") + password = os.getenv("NEO4J_PASSWORD") + + print(f"Connessione a Neo4j con URI: {uri}", file=os.sys.stderr) # Log su stderr + print(f"Utente Neo4j: {user}", file=os.sys.stderr) # Log su stderr + print(f"Password Neo4j: {password}", file=os.sys.stderr) # Log su stderr + + if not uri or not user or not password: + raise ValueError("Variabili d'ambiente NEO4J mancanti") + + return GraphDatabase.driver(uri, auth=(user, password)) + +@mcp.tool +def graph_rag_search(question: str) -> str: + """Esegue una ricerca complessa su Neo4j.""" + try: + # Ottieni il driver qui dentro, così se fallisce non crasha l'intero server all'avvio + driver = get_driver() + + cypher = """ + MATCH (n:Company) + WHERE n.description CONTAINS $keyword + RETURN n.name as Azienda + LIMIT 3 + """ + keyword = question.split()[-1] if question else "" + + with driver.session() as session: + result = session.run(cypher, keyword=keyword) + data = [dict(r) for r in result] + return str(data) + + except Exception as e: + return f"Errore durante l'esecuzione: {str(e)}" + +if __name__ == "__main__": + # IMPORTANTE: A volte FastMCP prova a lanciare un server HTTP se non specificato. + # Forziamo stdio per essere sicuri che funzioni con l'agente. + print("Avvio server MCP Custom...", file=os.sys.stderr) # Stampa su stderr per non rompere il protocollo + mcp.run(transport="stdio") \ No newline at end of file diff --git a/open-ai-sdk/openai-agents-with-mcp-main/my_advanced_tools.py b/open-ai-sdk/openai-agents-with-mcp-main/my_advanced_tools.py index 6150153..63a1413 100644 --- a/open-ai-sdk/openai-agents-with-mcp-main/my_advanced_tools.py +++ b/open-ai-sdk/openai-agents-with-mcp-main/my_advanced_tools.py @@ -1,50 +1,65 @@ from fastmcp import FastMCP from neo4j import GraphDatabase import os +import sys -# Definiamo il server +# Initialize the Custom MCP Server mcp = FastMCP("Neo4j Advanced Tools") -# Funzione helper per ottenere il driver solo quando serve def get_driver(): + """Lazily initializes the Neo4j driver to avoid startup crashes.""" uri = os.getenv("NEO4J_URI") user = os.getenv("NEO4J_USERNAME") password = os.getenv("NEO4J_PASSWORD") - - print(f"Connessione a Neo4j con URI: {uri}", file=os.sys.stderr) # Log su stderr - print(f"Utente Neo4j: {user}", file=os.sys.stderr) # Log su stderr - print(f"Password Neo4j: {password}", file=os.sys.stderr) # Log su stderr - + if not uri or not user or not password: - raise ValueError("Variabili d'ambiente NEO4J mancanti") + raise ValueError("Missing Neo4j environment variables (URI, USERNAME, or PASSWORD).") return GraphDatabase.driver(uri, auth=(user, password)) @mcp.tool def graph_rag_search(question: str) -> str: - """Esegue una ricerca complessa su Neo4j.""" + """ + Executes a GraphRAG (Graph Retrieval-Augmented Generation) search. + Use this tool for complex analysis, finding similar entities, or exploring + competitor landscapes where simple lookups are insufficient. + """ try: - # Ottieni il driver qui dentro, così se fallisce non crasha l'intero server all'avvio driver = get_driver() + db_name = os.getenv("NEO4J_DATABASE", "neo4j") + + # NOTE: In a real production scenario, you would generate embeddings here + # using the 'question' and OpenAI API, then use vector search in Neo4j. + + # Simulated complex logic for demonstration: + cypher_query = """ + MATCH (c:Company) + WHERE toLower(c.name) CONTAINS toLower($keyword) + OR toLower(c.description) CONTAINS toLower($keyword) - cypher = """ - MATCH (n:Company) - WHERE n.description CONTAINS $keyword - RETURN n.name as Azienda - LIMIT 3 + OPTIONAL MATCH (c)-[:COMPETES_WITH]->(competitor) + + RETURN c.name AS Company, + c.description AS Description, + collect(competitor.name) AS Competitors + LIMIT 5 """ + + # Simple keyword extraction for simulation purposes keyword = question.split()[-1] if question else "" - with driver.session() as session: - result = session.run(cypher, keyword=keyword) - data = [dict(r) for r in result] - return str(data) + with driver.session(database=db_name) as session: + result = session.run(cypher_query, keyword=keyword) + data = [dict(record) for record in result] + + if not data: + return f"No results found in the graph for keyword: '{keyword}'." + + return str(data) except Exception as e: - return f"Errore durante l'esecuzione: {str(e)}" + return f"Error executing GraphRAG search: {str(e)}" if __name__ == "__main__": - # IMPORTANTE: A volte FastMCP prova a lanciare un server HTTP se non specificato. - # Forziamo stdio per essere sicuri che funzioni con l'agente. - print("Avvio server MCP Custom...", file=os.sys.stderr) # Stampa su stderr per non rompere il protocollo + # Force stdio transport for compatibility with the OpenAI Python SDK agent mcp.run(transport="stdio") \ No newline at end of file From d66893e96739526607e8370ab45d734c31373fcb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Feb 2026 15:07:59 +0000 Subject: [PATCH 2/2] Bump python-multipart in /open-ai-sdk/openai-agents-with-mcp-main Bumps [python-multipart](https://github.com/Kludex/python-multipart) from 0.0.20 to 0.0.22. - [Release notes](https://github.com/Kludex/python-multipart/releases) - [Changelog](https://github.com/Kludex/python-multipart/blob/master/CHANGELOG.md) - [Commits](https://github.com/Kludex/python-multipart/compare/0.0.20...0.0.22) --- updated-dependencies: - dependency-name: python-multipart dependency-version: 0.0.22 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- open-ai-sdk/openai-agents-with-mcp-main/uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/open-ai-sdk/openai-agents-with-mcp-main/uv.lock b/open-ai-sdk/openai-agents-with-mcp-main/uv.lock index cf2a64f..adbd637 100644 --- a/open-ai-sdk/openai-agents-with-mcp-main/uv.lock +++ b/open-ai-sdk/openai-agents-with-mcp-main/uv.lock @@ -1021,11 +1021,11 @@ wheels = [ [[package]] name = "python-multipart" -version = "0.0.20" +version = "0.0.22" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, + { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, ] [[package]]