ScavioScavio
ProductPricingDocs
Sign InGet Started
  1. Home
  2. Tutorials
  3. How to Combine Graph Memory with Search MCP for Agents
Tutorial

How to Combine Graph Memory with Search MCP for Agents

Combine Neo4j graph memory with Scavio search MCP so agents remember past research and ground new queries in live data. Full Python example.

Get Free API KeyAPI Docs

Combining graph-based memory with search MCP gives AI agents persistent context that improves over time while still grounding responses in live web data. Without memory, agents repeat the same searches across sessions and lose the relationships between entities they have already researched. By storing search results and entity relationships in a Neo4j knowledge graph and querying it before calling the Scavio MCP search, the agent only searches for genuinely new information while building an increasingly rich context graph.

Prerequisites

  • Python 3.10+
  • Neo4j running locally or via Aura (free tier works)
  • neo4j Python driver installed (pip install neo4j)
  • Scavio API key from scavio.dev

Walkthrough

Step 1: Set up the graph memory store

Connect to Neo4j and create the schema for storing entities, relationships, and search results with timestamps.

Python
from neo4j import GraphDatabase
import os

NEO4J_URI = os.environ.get('NEO4J_URI', 'bolt://localhost:7687')
NEO4J_USER = os.environ.get('NEO4J_USER', 'neo4j')
NEO4J_PASS = os.environ.get('NEO4J_PASS', 'password')

driver = GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USER, NEO4J_PASS))

def init_schema():
    with driver.session() as s:
        s.run('CREATE CONSTRAINT IF NOT EXISTS FOR (e:Entity) REQUIRE e.name IS UNIQUE')
        s.run('CREATE CONSTRAINT IF NOT EXISTS FOR (s:SearchResult) REQUIRE s.url IS UNIQUE')
        s.run('CREATE INDEX IF NOT EXISTS FOR (s:SearchResult) ON (s.query)')
    print('Graph schema initialized')

init_schema()

Step 2: Build the memory-aware search function

Create a search function that checks graph memory first, only calling the Scavio API for queries not seen recently.

Python
import requests
from datetime import datetime, timedelta

H = {'x-api-key': os.environ['SCAVIO_API_KEY'], 'Content-Type': 'application/json'}
CACHE_TTL_HOURS = 24

def search_with_memory(query):
    # Check graph memory first
    with driver.session() as s:
        cached = s.run(
            'MATCH (sr:SearchResult {query: $q}) '
            'WHERE sr.fetched_at > datetime() - duration({hours: $ttl}) '
            'RETURN sr.title AS title, sr.url AS url, sr.snippet AS snippet '
            'ORDER BY sr.position LIMIT 10',
            q=query, ttl=CACHE_TTL_HOURS
        ).data()
    if cached:
        print(f'Memory hit: {len(cached)} results for "{query}"')
        return cached

    # Cache miss: search live
    data = requests.post('https://api.scavio.dev/api/v1/search',
        headers=H, json={'query': query, 'country_code': 'us'}).json()
    results = data.get('organic_results', [])

    # Store in graph
    with driver.session() as s:
        for r in results:
            s.run(
                'MERGE (sr:SearchResult {url: $url}) '
                'SET sr.title = $title, sr.snippet = $snippet, '
                'sr.query = $query, sr.position = $pos, sr.fetched_at = datetime()',
                url=r.get('link', ''), title=r.get('title', ''),
                snippet=r.get('snippet', ''), query=query, pos=r.get('position', 0)
            )
    print(f'Live search: {len(results)} results stored for "{query}"')
    return results

Step 3: Add entity extraction and relationship tracking

Extract entities from search results and build relationships in the graph so the agent accumulates knowledge across sessions.

Python
import re

def extract_entities(text, entity_types=None):
    # Simple pattern-based extraction (replace with NER model for production)
    entities = set()
    # Capitalized multi-word phrases (likely proper nouns)
    for match in re.findall(r'\b([A-Z][a-z]+(?: [A-Z][a-z]+)+)\b', text):
        entities.add(match)
    return list(entities)

def store_entities_from_results(query, results):
    with driver.session() as s:
        topic = s.run(
            'MERGE (e:Entity {name: $name}) '
            'SET e.type = "topic", e.last_searched = datetime() '
            'RETURN e', name=query
        ).single()

        for r in results:
            text = f"{r.get('title', '')} {r.get('snippet', '')}"
            entities = extract_entities(text)
            for ent in entities:
                s.run(
                    'MERGE (e:Entity {name: $name}) '
                    'WITH e '
                    'MATCH (t:Entity {name: $topic}) '
                    'MERGE (t)-[:RELATED_TO]->(e)',
                    name=ent, topic=query
                )
        print(f'Stored entities for "{query}"')

# Usage
results = search_with_memory('LangGraph agent tutorial')
store_entities_from_results('LangGraph agent tutorial', results)

Step 4: Query the knowledge graph for context

Before searching, ask the graph what the agent already knows about a topic to build richer context for the LLM.

Python
def get_context_from_memory(topic, depth=2):
    with driver.session() as s:
        # Get related entities up to N hops away
        related = s.run(
            'MATCH (e:Entity {name: $name})-[:RELATED_TO*1..' + str(depth) + ']-(r:Entity) '
            'RETURN DISTINCT r.name AS name, r.type AS type '
            'LIMIT 20',
            name=topic
        ).data()

        # Get recent search results for related topics
        context_results = s.run(
            'MATCH (e:Entity {name: $name})-[:RELATED_TO*1..2]-(r:Entity) '
            'MATCH (sr:SearchResult) WHERE sr.query CONTAINS r.name '
            'RETURN sr.title AS title, sr.snippet AS snippet, sr.url AS url '
            'ORDER BY sr.fetched_at DESC LIMIT 10',
            name=topic
        ).data()

    context = {
        'known_entities': [r['name'] for r in related],
        'related_results': context_results
    }
    print(f'Context: {len(related)} entities, {len(context_results)} cached results')
    return context

ctx = get_context_from_memory('LangGraph agent tutorial')
print(f'Known entities: {ctx["known_entities"][:5]}')

Python Example

Python
import os, requests, re
from neo4j import GraphDatabase

H = {'x-api-key': os.environ['SCAVIO_API_KEY'], 'Content-Type': 'application/json'}
driver = GraphDatabase.driver(
    os.environ.get('NEO4J_URI', 'bolt://localhost:7687'),
    auth=(os.environ.get('NEO4J_USER', 'neo4j'), os.environ.get('NEO4J_PASS', 'password')))

def init_schema():
    with driver.session() as s:
        s.run('CREATE CONSTRAINT IF NOT EXISTS FOR (e:Entity) REQUIRE e.name IS UNIQUE')
        s.run('CREATE CONSTRAINT IF NOT EXISTS FOR (sr:SearchResult) REQUIRE sr.url IS UNIQUE')

def search_with_memory(query, ttl_hours=24):
    with driver.session() as s:
        cached = s.run(
            'MATCH (sr:SearchResult {query: $q}) '
            'WHERE sr.fetched_at > datetime() - duration({hours: $ttl}) '
            'RETURN sr.title AS title, sr.url AS url, sr.snippet AS snippet LIMIT 10',
            q=query, ttl=ttl_hours).data()
    if cached:
        return {'source': 'memory', 'results': cached}
    data = requests.post('https://api.scavio.dev/api/v1/search',
        headers=H, json={'query': query, 'country_code': 'us'}).json()
    results = data.get('organic_results', [])
    with driver.session() as s:
        for r in results:
            s.run('MERGE (sr:SearchResult {url: $url}) '
                  'SET sr.title=$t, sr.snippet=$sn, sr.query=$q, sr.fetched_at=datetime()',
                  url=r.get('link',''), t=r.get('title',''), sn=r.get('snippet',''), q=query)
    return {'source': 'live', 'results': results}

def get_graph_context(topic, depth=2):
    with driver.session() as s:
        return s.run(
            'MATCH (e:Entity {name:$n})-[:RELATED_TO*1..'+str(depth)+']-(r) '
            'RETURN DISTINCT r.name AS name LIMIT 20', n=topic).data()

init_schema()
result = search_with_memory('LangGraph agent patterns 2026')
print(f"Source: {result['source']}, Results: {len(result['results'])}")

JavaScript Example

JavaScript
const neo4j = require('neo4j-driver');
const driver = neo4j.driver(
  process.env.NEO4J_URI || 'bolt://localhost:7687',
  neo4j.auth.basic(process.env.NEO4J_USER || 'neo4j', process.env.NEO4J_PASS || 'password'));
const H = {'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json'};

async function searchWithMemory(query, ttlHours = 24) {
  const session = driver.session();
  try {
    const cached = await session.run(
      'MATCH (sr:SearchResult {query: $q}) '
      + 'WHERE sr.fetched_at > datetime() - duration({hours: $ttl}) '
      + 'RETURN sr.title AS title, sr.url AS url LIMIT 10',
      {q: query, ttl: neo4j.int(ttlHours)});
    if (cached.records.length > 0) {
      return {source: 'memory', results: cached.records.map(r => r.toObject())};
    }
    const data = await fetch('https://api.scavio.dev/api/v1/search', {
      method: 'POST', headers: H,
      body: JSON.stringify({query, country_code: 'us'})
    }).then(r => r.json());
    const results = data.organic_results || [];
    for (const r of results) {
      await session.run(
        'MERGE (sr:SearchResult {url: $url}) SET sr.title=$t, sr.query=$q, sr.fetched_at=datetime()',
        {url: r.link || '', t: r.title || '', q: query});
    }
    return {source: 'live', results};
  } finally { await session.close(); }
}

searchWithMemory('LangGraph agent patterns').then(r =>
  console.log(\`Source: \${r.source}, Results: \${r.results.length}\`));

Expected Output

JSON
Graph schema initialized
Live search: 10 results stored for "LangGraph agent patterns 2026"
Source: live, Results: 10

# Second run:
Memory hit: 10 results for "LangGraph agent patterns 2026"
Source: memory, Results: 10

Related Tutorials

  • How to Ground a Local LLM with Structured Search
  • How to Build Curated Search for AI Agents
  • How to Add MCP Search to Claude Code

Frequently Asked Questions

Most developers complete this tutorial in 15 to 30 minutes. You will need a Scavio API key (free tier works) and a working Python or JavaScript environment.

Python 3.10+. Neo4j running locally or via Aura (free tier works). neo4j Python driver installed (pip install neo4j). Scavio API key from scavio.dev. A Scavio API key gives you 50 free credits on signup.

Yes. The free tier includes 50 credits on signup, which is more than enough to complete this tutorial and prototype a working solution.

Scavio has a native LangChain package (langchain-scavio), an MCP server, and a plain REST API that works with any HTTP client. This tutorial uses the raw REST API, but you can adapt to your framework of choice.

Related Resources

Use Case

MCP Search Gateway for Multi-Agent Systems

Read more
Use Case

Graph Memory + Search for Coding Agents

Read more
Glossary

Graph Memory for AI Agents

Read more
Best Of

Best MCP Search Servers: Community Edition, May 2026

Read more
Best Of

Best MCP Search Tools for IDE Integration in 2026

Read more
Solution

Agent Memory with Search Grounding

Read more

Start Building

Combine Neo4j graph memory with Scavio search MCP so agents remember past research and ground new queries in live data. Full Python example.

Get Free API KeyRead the Docs
ScavioScavio

Real-time search API for AI agents. Search every platform, not just Google.

Product

  • Features
  • Pricing
  • Dashboard
  • Affiliates

Developers

  • Documentation
  • API Reference
  • Quickstart
  • MCP Integration
  • Python SDK

Alternatives

  • Tavily Alternative
  • SerpAPI Alternative
  • Firecrawl Alternative
  • Exa Alternative

Tools

  • JSON Formatter
  • cURL to Code
  • Token Counter
  • All Tools

© 2026 Scavio. All rights reserved.

Featured on TAAFT
Terms of ServicePrivacy Policy