Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 97 additions & 22 deletions open-ai-sdk/openai-agents-with-mcp-main/README.md
Original file line number Diff line number Diff line change
@@ -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
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`.
78 changes: 78 additions & 0 deletions open-ai-sdk/openai-agents-with-mcp-main/interactive_main-2.py
Original file line number Diff line number Diff line change
@@ -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())
57 changes: 30 additions & 27 deletions open-ai-sdk/openai-agents-with-mcp-main/interactive_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand Down
50 changes: 50 additions & 0 deletions open-ai-sdk/openai-agents-with-mcp-main/my_advanced_tools-1.py
Original file line number Diff line number Diff line change
@@ -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")
Loading