Skip to main content

MCP Neo4j Entity Graph Server

PyPI version Python 3.10+ License: MIT

MCP server for extracting entities and relationships from graph nodes using LLM structured output, creating entity graphs directly in Neo4j.

Supports 100+ LLM providers via LiteLLM (OpenAI, Anthropic, Google, Azure, Bedrock, Ollama, etc.)

Features

  • Dual extraction pipeline: Text-only (LLM) and visual (VLM) extraction auto-routed per chunk
  • Grammar-enforced structured output: Pydantic models used as response_format — the LLM cannot violate the schema
  • Async background processing: Long extractions run in background with job tracking
  • Multi-provider LLM support: Use any LLM via LiteLLM (OpenAI, Claude, Gemini, etc.)
  • Schema-driven: Define entity types and relationships to extract
  • Provenance tracking: EXTRACTED_FROM relationships link entities to source chunks
  • High parallelism: Configurable concurrency (text: up to 50, VLM: up to 50)
  • Batched writes: Optimized Neo4j writes (configurable batch size)
  • Incremental: Only processes nodes without prior extraction (unless force=true)
  • Multi-pass ready: Architecture supports entity-only, relationship-only, and corrective passes (v2)

Tools

convert_schema

Converts data model output from the Data Modeling MCP to a Pydantic extraction schema.

Parameters:

Parameter Required Description
modeling_output Yes JSON output from the Data Modeling MCP server
output_path Yes Path to save the Pydantic .py file (e.g. /path/to/schema.py)

Output:

  • {output_path} — Strongly-typed Pydantic models used as response_format for LLM structured extraction

The .py file can be customized before running extraction:

  • Add Literal types to constrain categorical fields (phase, status, therapeutic area...)
  • Add @field_validator for normalization (strip legal suffixes, resolve aliases...)

extract_entities

Extracts entities and relationships from graph nodes using LLM. Returns immediately with a job ID.

The tool auto-detects chunk types and routes accordingly:

  • Text chunks (type="text"): sent to LLM with text only
  • Image/Table chunks (with imageBase64): sent to VLM with text + image
  • Page nodes (:Page label with imageBase64): sent to VLM with text + page image

Parameters:

Parameter Default Description
schema required Path to the Pydantic .py file generated by convert_schema
source_label "Chunk" Label of source nodes (Chunk or Page)
force false Re-extract all nodes (ignore existing EXTRACTED_FROM)
text_parallel 20 Max concurrent text extractions
vlm_parallel 5 Max concurrent VLM extractions
batch_size 10 Chunks to batch before writing to Neo4j
model env var LLM model (defaults to EXTRACTION_MODEL)
pass_type "full" full, entities_only, relationships_only, corrective
pass_number 1 Pass number for multi-pass extraction

check_extraction_status

Monitor background extraction jobs.

Parameter Default Description
job_id None Specific job to check. If omitted, returns all jobs.

cancel_extraction

Cancel a running extraction job.

Parameter Required Description
job_id Yes Job ID to cancel

Quick Start

# 1. Convert schema from Data Modeling MCP
convert_schema(
    modeling_output='{"nodes": [...], "relationships": [...]}',
    output_path="data_models/my_schema.py"
)
# Creates: my_schema.py (Pydantic models, ready for customization)

# 2. (Optional) Open my_schema.py and add Literal constraints / field_validators

# 3. Extract entities (runs in background)
extract_entities(
    schema="data_models/my_schema.py",
)
# Returns: {"job_id": "abc123", "status": "started", ...}

# 4. Check progress
check_extraction_status(job_id="abc123")
# Returns: {"status": "extracting", "chunks_completed": 45, ...}

# 5. Re-run extraction (incremental — only unprocessed nodes)
extract_entities(schema="data_models/my_schema.py")

# 6. Force full re-extraction (e.g. after schema changes)
extract_entities(schema="data_models/my_schema.py", force=True)

Generated Pydantic Models

convert_schema generates a .py file with strongly-typed Pydantic models. The ExtractionOutput class is sent to the LLM as response_format, meaning the LLM output is grammar-constrained — it literally cannot produce values outside the schema.

class DrugEntity(BaseModel):
    _node_label: ClassVar[str] = "Drug"
    _key_property: ClassVar[str] = "name"

    name: str = Field(..., description="Drug name")
    dose: Optional[str] = Field(default=None, description="Dosage")

    @field_validator("name", mode="before")
    @classmethod
    def _normalize_name(cls, v):
        if isinstance(v, str):
            return v.strip()
        return v

class TreatsRel(BaseModel):
    _relationship_type: ClassVar[str] = "TREATS"
    drug_name: str = Field(..., description="Drug name")
    disease_name: str = Field(..., description="Disease name")

class ExtractionOutput(BaseModel):
    drugs: list[DrugEntity] = Field(default_factory=list)
    treats: list[TreatsRel] = Field(default_factory=list)

Customizing the Schema

After running convert_schema, open the .py file and add constraints before extraction:

Literal constraints (normalize categorical fields)

from typing import Literal

class ClinicalProgramEntity(BaseModel):
    # Forces the LLM to pick from these exact values — no more "Phase III" vs "Phase 3"
    phase: Optional[Literal["Phase 1", "Phase 2", "Phase 3", "Registration", "Approved"]] = Field(
        default=None,
        description="Clinical phase — map Phase I→Phase 1, Phase II→Phase 2, Phase III→Phase 3"
    )

Field validators (normalize entity keys to avoid duplicates)

import re

_LEGAL_SUFFIX_RE = re.compile(r",?\s*(Inc\.?|Ltd\.?|AG|SE|GmbH|Pharmaceuticals?)\s*$", re.IGNORECASE)

class CompanyEntity(BaseModel):
    @field_validator("name", mode="before")
    @classmethod
    def _normalize(cls, v):
        if isinstance(v, str):
            v = v.strip()
            while True:
                cleaned = _LEGAL_SUFFIX_RE.sub("", v).strip()
                if cleaned == v:
                    break
                v = cleaned
        return v

Important: Apply the same normalization to the corresponding relationship field (e.g. company_name in DevelopsRel) so that Neo4j MERGE keys always match.

Environment Variables

Variable Default Description
NEO4J_URI bolt://localhost:7687 Neo4j connection URI
NEO4J_USERNAME neo4j Neo4j username
NEO4J_PASSWORD (required) Neo4j password
NEO4J_DATABASE neo4j Neo4j database name
EXTRACTION_MODEL gpt-5.4-mini Default LLM model for extraction
OPENAI_API_KEY - Required for OpenAI models

Usage with Cursor

Add to your ~/.cursor/mcp.json:

{
  "mcpServers": {
    "neo4j-entity-graph": {
      "command": "uv",
      "args": [
        "--directory", "/path/to/mcp-neo4j-entity-graph",
        "run", "mcp-neo4j-entity-graph"
      ],
      "env": {
        "NEO4J_URI": "neo4j://127.0.0.1:7687",
        "NEO4J_USERNAME": "neo4j",
        "NEO4J_PASSWORD": "your-password",
        "OPENAI_API_KEY": "your-api-key",
        "EXTRACTION_MODEL": "gpt-5.4-mini"
      }
    }
  }
}

Performance

Tested on pharma pipeline PDFs with gpt-5.4-mini:

Mode Concurrency Time Entities Relationships
Text-only 50 107s 1,584 1,257
VLM (page images) 50 114s 1,597 1,378

Architecture

server.py           - MCP tools (convert_schema, extract_entities, check/cancel)
job_manager.py      - Async job tracking, progress, cancellation
base_extractor.py   - Shared: prompts, parsing, Pydantic model loading
text_extractor.py   - Text-only LLM extraction (high parallelism)
vlm_extractor.py    - Vision+text VLM extraction (configurable parallelism)
schema_generator.py - Pydantic model code generation from data model
models.py           - Internal types (ExtractionSchema, ClassifiedChunk, etc.)

Graph Schema

After extraction, your Neo4j database will contain:

(:Entity)-[:EXTRACTED_FROM]->(:Chunk)
(:Entity)-[relationship]->(:Entity)

Example query:

MATCH (e)-[:EXTRACTED_FROM]->(c:Chunk)-[:PART_OF]->(d:Document {name: "my-doc"})
RETURN labels(e)[0] as type, count(e) as count
ORDER BY count DESC

Metadata

Release files for mcp-neo4j-entity-graph 0.4.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mcp-neo4j-entity-graph 0.4.1
File Size Uploaded
mcp_neo4j_entity_graph-0.4.1.tar.gz 244.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mcp-neo4j-entity-graph 0.4.1
File Interpreter ABI Platform
mcp_neo4j_entity_graph-0.4.1-py3-none-any.whl Python 3 none any Details

Total release size: 286.3 kB

Release files / mcp_neo4j_entity_graph-0.4.1.tar.gz

Download URL mcp_neo4j_entity_graph-0.4.1.tar.gz
Size 244.9 kB
Tags Source
SHA-256 checksum
How to use checksums
e0e0672652d319d2fee625f7ff3632dff3529a5b88e5f9bfef2b241d2db9b02c
BLAKE2b-256 checksum
How to use checksums
d1e5acd4e463200c0702bd39905e99989753798f2970ae6df2b6e74ce33611a0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / mcp_neo4j_entity_graph-0.4.1-py3-none-any.whl

Download URL mcp_neo4j_entity_graph-0.4.1-py3-none-any.whl
Size 41.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4c90acb5bc9e1ea8c7fe715d2a60ce300d98e6f3659f532b33eaf14f54b81085
BLAKE2b-256 checksum
How to use checksums
99701a769def89aab6b09fe6179e96152aecaa94d1d3f3b540f6810450c5b6ff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.4.1 This release

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page