{"slug": "run-the-neo4j-mcp-server-locally-with-docker-no-codespaces-needed", "title": "Run the Neo4j MCP Server Locally with Docker (No Codespaces Needed)", "summary": "Neo4j's MCP server can now be run locally with Docker, bypassing the need for GitHub Codespaces and their free-tier limits. A docker-compose setup launches both Neo4j and the MCP server, enabling developers to test MCP tools like get-schema and read-cypher on their own machines. The guide clarifies that the MCP server is a self-hosted program connecting to any database, not a cloud service.", "body_md": "Neo4j’s GraphAcademy course *“Developing with Neo4j MCP Tools”* is excellent, but it assumes you’ll run everything inside a GitHub Codespace. Codespaces have a monthly free limit, and once you hit it you’re stuck — unless you self-host.\n\nGood news: you don’t need Codespaces at all. The MCP server is just a small program, and you can run the whole stack locally with Docker in about half an hour. This guide walks through exactly that, and along the way you’ll pick up how MCP actually fits together.\n\nEverything below has been tested end-to-end.\n\n```\nVS Code (host)  ──HTTP /mcp + auth──►  neo4j-mcp (server)  ──Bolt──►  Neo4j (database)\n```\n\nThree separate pieces — and understanding the split is half the battle:\n\nA common misconception: there is **no remote “MCP database” somewhere in the cloud**. The MCP server is a program *you* run. It connects out to whatever database you point it at.\n\nThat’s it. No local Python, no Neo4j install — Docker handles both.\n\nMake a project folder and drop in a docker-compose.yml:\n\n```\nservices:  neo4j:    image: neo4j:5.26    container_name: neo4j    ports:      - \"7474:7474\"          # Neo4j Browser  -> http://localhost:7474      - \"7687:7687\"          # Bolt    environment:      NEO4J_AUTH: \"neo4j/password123\"      NEO4J_PLUGINS: '[\"apoc\",\"graph-data-science\"]'   # needed for get-schema + GDS tool    volumes:      - neo4j_data:/data    healthcheck:      test: [\"CMD-SHELL\", \"cypher-shell -u neo4j -p password123 'RETURN 1' || exit 1\"]      interval: 10s      timeout: 10s      retries: 12      start_period: 30s\nneo4j-mcp:    build:      context: \"https://github.com/neo4j/mcp.git#v1.5.2\"   # builds the official server from source    container_name: neo4j-mcp    depends_on:      neo4j:        condition: service_healthy    ports:      - \"8000:8000\"          # MCP endpoint -> http://localhost:8000/mcp    environment:      NEO4J_URI: \"bolt://neo4j:7687\"      NEO4J_DATABASE: \"neo4j\"      NEO4J_TRANSPORT_MODE: \"http\"     # networked service, not a stdio subprocess      NEO4J_MCP_HTTP_HOST: \"0.0.0.0\"   # bind all interfaces so the host can reach it      NEO4J_MCP_HTTP_PORT: \"8000\"      # must be >1024 (container runs as non-root)\nvolumes:  neo4j_data:\n```\n\n**Two things worth knowing here:**\n\n```\ndocker compose up -d --build\n```\n\nFirst run pulls the Neo4j image, builds the Go MCP server, and downloads the APOC + GDS plugins. Give it a minute, then check both containers are up:\n\n```\ndocker compose ps\n```\n\nWait until neo4j shows (healthy).\n\nThe MCP endpoint is http://localhost:8000/mcp. Don't open it in a browser — you'll get Method Not Allowed: only POST is supported, which is actually a *good sign* (the server is up; browsers just send GET).\n\nTest it properly with curl. First, confirm auth is enforced:\n\n```\ncurl -i -X POST http://localhost:8000/mcp        # expect 401 Unauthorized\n```\n\nNow list the tools, authenticating with the database credentials (neo4j:password123):\n\n```\ncurl -s -u neo4j:password123 -H \"Content-Type: application/json\" \\  -d '{\"jsonrpc\":\"2.0\",\"method\":\"tools/list\",\"id\":1}' \\  http://localhost:8000/mcp\n```\n\nYou should see: get-schema, read-cypher, write-cypher.\n\nThe course lists **four** tools — the fourth is list-gds-procedures. If it's missing, don't panic. That tool is registered *lazily during the MCP **initialize handshake*, and only if Graph Data Science is installed. A bare tools/list curl skips initialize, so it never appears that way.\n\nSend an initialize request first, then list again:\n\n```\ncurl -s -u neo4j:password123 -H \"Content-Type: application/json\" \\  -d '{\"jsonrpc\":\"2.0\",\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2024-11-05\",\"capabilities\":{},\"clientInfo\":{\"name\":\"t\",\"version\":\"1\"}},\"id\":1}' \\  http://localhost:8000/mcp\ncurl -s -u neo4j:password123 -H \"Content-Type: application/json\" \\  -d '{\"jsonrpc\":\"2.0\",\"method\":\"tools/list\",\"id\":2}' \\  http://localhost:8000/mcp\n```\n\nNow all **four** appear. (Real MCP clients like VS Code do the initialize step automatically — this only bites you with raw curl.)\n\nIn your project, create .vscode/mcp.json:\n\n```\n{    \"servers\": {        \"neo4j\": {            \"type\": \"http\",            \"url\": \"http://localhost:8000/mcp\",            \"headers\": {                \"Authorization\": \"Basic bmVvNGo6cGFzc3dvcmQxMjM=\"            }        }    }}\n```\n\nThat Authorization value is just base64 of neo4j:password123. If you change the password, regenerate it:\n\n```\nprintf 'neo4j:yourpassword' | base64\n```\n\n⚠️Most common mistake:using the course’sstdioconfig (\"type\": \"stdio\", \"command\": \"neo4j-mcp\") with this Docker setup. That tells VS Code to launch a local binary you don't have. For the containerized server you must use thehttpconfig above.\n\nThen in VS Code: Cmd/Ctrl + Shift + P → **MCP: List Servers** → select **neo4j** → **Start Server**. Open Chat in **Agent mode** (Cmd/Ctrl + Shift + I) and ask:\n\nWhich MCP tools are available? List their ID and description.\n\nYou’ll get all four tools back.\n\nYour local database is empty, so let’s point a second, throwaway MCP container at Neo4j’s public **movies** demo database — the same dataset the course uses — on a different port. This leaves your main stack untouched:\n\n```\ndocker run --rm -d --name mcp-movies \\  -e NEO4J_URI=\"neo4j+s://demo.neo4jlabs.com\" \\  -e NEO4J_DATABASE=\"recommendations\" \\  -e NEO4J_TRANSPORT_MODE=\"http\" \\  -e NEO4J_MCP_HTTP_HOST=\"0.0.0.0\" \\  -e NEO4J_MCP_HTTP_PORT=\"8001\" \\  -p 8001:8001 \\  <your-project-name>-neo4j-mcp\n```\n\n(The image name is whatever docker compose built — check docker images; it looks like yourfolder-neo4j-mcp.)\n\n**Read the schema** — what relationships does a User have?\n\n```\ncurl -s -u recommendations:recommendations -H \"Content-Type: application/json\" \\  -d '{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"read-cypher\",\"arguments\":{\"query\":\"MATCH (u:User)-[r]->() RETURN DISTINCT type(r) AS rel\"}},\"id\":2}' \\  http://localhost:8001/mcp\n```\n\nResult: RATED — i.e. (:User)-[:RATED]->(:Movie).\n\n**Generate a query** — the highest-rated movie (note the non-null guard, since not every movie has a rating):\n\n```\ncurl -s -u recommendations:recommendations -H \"Content-Type: application/json\" \\  -d '{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"read-cypher\",\"arguments\":{\"query\":\"MATCH (m:Movie) WHERE m.imdbRating IS NOT NULL RETURN m.title AS title, m.imdbRating AS rating ORDER BY m.imdbRating DESC LIMIT 1\"}},\"id\":3}' \\  http://localhost:8001/mcp\n```\n\nWhen you’re done with the demo:\n\n```\ndocker stop mcp-movies\n```\n\nThe course’s capstone is “vibe coding” a Python CLI that returns the top 5 movies in a genre. Here’s a clean, dependency-light version. The genre is passed as a **query parameter** (never string-concatenated) and the query filters out null ratings:\n\n``` python\nimport os, sysfrom neo4j import GraphDatabase\nphp\nQUERY = \"\"\"MATCH (m:Movie)-[:IN_GENRE]->(:Genre {name: $genre})WHERE m.imdbRating IS NOT NULLRETURN m.title AS title, m.imdbRating AS ratingORDER BY m.imdbRating DESCLIMIT 5\"\"\"\npython\ndef main():    uri = os.environ[\"NEO4J_URI\"]    user = os.environ[\"NEO4J_USERNAME\"]    pwd = os.environ[\"NEO4J_PASSWORD\"]    db = os.environ.get(\"NEO4J_DATABASE\", \"neo4j\")\ngenre = input(\"Enter a movie genre: \").strip()    with GraphDatabase.driver(uri, auth=(user, pwd)) as driver:        driver.verify_connectivity()        records, _, _ = driver.execute_query(QUERY, genre=genre, database_=db)\nprint(f\"\\nTop {len(records)} '{genre}' movies:\")    for i, r in enumerate(records, 1):        print(f\"  {i}. {r['title']} ({r['rating']})\")\nif __name__ == \"__main__\":    main()\n```\n\nRun it against the demo database:\n\n```\npip install neo4jexport NEO4J_URI=\"neo4j+s://demo.neo4jlabs.com\"export NEO4J_USERNAME=\"recommendations\"export NEO4J_PASSWORD=\"recommendations\"export NEO4J_DATABASE=\"recommendations\"python3 movie_recommender.py      # then type: Mystery\n```\n\nIn about 30 minutes you’ve stood up the full Neo4j MCP stack locally — no Codespaces, no cloud bill:\n\nIt’s tempting to see this as just “avoiding a paywall,” but the self-hosted setup teaches something more useful. MCP is quietly becoming the standard way AI agents reach real systems — databases, APIs, internal tools — and the interesting engineering questions live exactly where we ended up: *How is the server transported? How are requests authenticated? What stops an agent from running a destructive query?*\n\nRunning the server yourself over HTTP, with per-request auth and a read-only switch, surfaces those questions in a way a pre-baked cloud sandbox hides. As more teams put agents in front of production data, the ability to run an MCP server as a controlled, observable service — rather than a mystery box someone else hosts — stops being a nice-to-have. The 30 minutes you spent here map directly onto how you’d deploy the same server behind a gateway with TLS and a secrets manager for real.\n\nSymptomCause / FixMethod Not Allowed in browserYou sent a GET. The endpoint is POST-only — use curl or a client.401 UnauthorizedMissing/wrong Basic Auth. Use the DB username:password.Only 3 tools listedSend an initialize request first; ensure APOC + GDS are installed.get-schema errorsAPOC not installed — add it to NEO4J_PLUGINS.MCP server won't start, complains about username/passwordYou set NEO4J_USERNAME/PASSWORD in HTTP mode. Remove them.VS Code says server \"requires interaction to start\"Your mcp.json is using the stdio config. Switch to the http config.Permission denied binding portKeep NEO4J_MCP_HTTP_PORT above 1024 (container is non-root).\n\n*Happy graphing.*\n\n[Run the Neo4j MCP Server Locally with Docker (No Codespaces Needed)](https://pub.towardsai.net/run-the-neo4j-mcp-server-locally-with-docker-no-codespaces-needed-b9a3792562ef) was originally published in [Towards AI](https://pub.towardsai.net) on Medium, where people are continuing the conversation by highlighting and responding to this story.", "url": "https://wpnews.pro/news/run-the-neo4j-mcp-server-locally-with-docker-no-codespaces-needed", "canonical_source": "https://pub.towardsai.net/run-the-neo4j-mcp-server-locally-with-docker-no-codespaces-needed-b9a3792562ef?source=rss----98111c9905da---4", "published_at": "2026-07-01 07:18:40+00:00", "updated_at": "2026-07-01 07:55:10.585612+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools", "ai-infrastructure"], "entities": ["Neo4j", "Docker", "GitHub Codespaces", "MCP", "GraphAcademy", "APOC", "Graph Data Science"], "alternates": {"html": "https://wpnews.pro/news/run-the-neo4j-mcp-server-locally-with-docker-no-codespaces-needed", "markdown": "https://wpnews.pro/news/run-the-neo4j-mcp-server-locally-with-docker-no-codespaces-needed.md", "text": "https://wpnews.pro/news/run-the-neo4j-mcp-server-locally-with-docker-no-codespaces-needed.txt", "jsonld": "https://wpnews.pro/news/run-the-neo4j-mcp-server-locally-with-docker-no-codespaces-needed.jsonld"}}