A stdio MCP server that lets Snowflake Cortex Code ask natural-language questions to a Neo4j Aura Agent and get back the answer, the Cypher the agent executed, and a preview of the result rows.
Neo4j Aura Agents expose two endpoints for the same agent:
- A streamable HTTP MCP endpoint at
https://mcp.neo4j.io/agent?.... This endpoint requires an interactive user OAuth flow (openid profile emailscopes viaauthorization_codeagainst a separateaura-mcp.eu.auth0.comtenant). It is well suited to MCP clients that can drive a browser login, such as Claude Desktop or MCP Inspector. - A REST
/invokeendpoint athttps://api.neo4j.io/v2beta1/.../agents/<id>/invoke. This endpoint accepts service credentials via the standard Aura API OAuthclient_credentialsgrant. It is well suited to headless, automated, and CI environments where no human is present to complete a browser login.
Snowflake Cortex Code's MCP HTTP transport accepts only static headers. It
cannot drive an interactive OAuth flow, and per-request token minting is not
something the transport will do for you. As a result, plugging Cortex Code
directly into the Aura MCP endpoint fails with Unauthorized and there is no
combination of static credentials that fixes it.
This project takes the second path: a small local stdio MCP server that uses
the REST /invoke endpoint, mints and caches the bearer token internally,
and exposes a single MCP tool that Cortex Code can call like any other.
The server exposes one MCP tool:
| Tool | Arguments | Returns |
|---|---|---|
ask_neo4j_agent |
question: str, verbose: bool = False |
Markdown with the answer, executed Cypher, a preview of result rows, and token usage |
By default the agent's verbose reasoning trace is suppressed. Pass verbose=true
to include it for debugging.
+------------------+ stdio (MCP) +-------------------------+ HTTPS +---------------------+
| | <-----------------------> | | <------------------> | |
| Cortex Code | | cortex-code-neo4j-mcp | /oauth/token | api.neo4j.io |
| CLI (MCP host) | | (stdio MCP server) | /agents/.../invoke | (Aura REST API) |
| | | | | |
+------------------+ +-------------------------+ +---------------------+
| ^
| |
in-memory token cache
(refresh 60s before expiry)
Three components, each with a single responsibility:
config.pyvalidates the environment and produces a typedSettingsobject. Missing credentials fail at startup rather than silently at first call.aura_client.pyowns the OAuth dance, the token cache, and the/invokecall. It retries once onHTTP 401so that a server-side token rotation does not surface as a transient failure.server.pyregisters a single MCP tool and renders the Aura Agent's response envelope into a compact, human-readable markdown report.
The recommended path is uvx, which runs the server in an isolated
environment without installing anything globally.
# One-time: install uv (https://github.com/astral-sh/uv) if you do not have it.
brew install uvTo run the server directly from this repository:
uvx --from git+https://github.com/neo4j-field/cortex-code-neo4j-mcp cortex-code-neo4j-mcpPip-based install works equally well:
pip install git+https://github.com/neo4j-field/cortex-code-neo4j-mcp
cortex-code-neo4j-mcpThe server reads four required environment variables. All of them come from the Neo4j Aura console.
| Variable | Where to find it |
|---|---|
NEO4J_CLIENT_ID |
Aura Console -> Project Settings -> API Keys -> Create. The client ID is shown once. |
NEO4J_CLIENT_SECRET |
Same flow as above. The secret is shown once and cannot be retrieved later. |
NEO4J_PROJECT_ID |
Visible in the agent's external endpoint URL: .../projects/<PROJECT_ID>/agents/.... |
NEO4J_AGENT_ID |
Visible in the same URL: .../agents/<AGENT_ID>/invoke. |
Optional overrides are documented in .env.example.
Do not commit credentials. This repository's
.gitignoreblocks.envand several conventional credential filenames, but the responsibility for keeping secrets out of source control is yours. Prefer the OS keychain or a secrets manager in any environment you do not personally own.
Cortex Code reads MCP servers from ~/.snowflake/cortex/mcp.json. Add this
entry, substituting your credentials:
{
"mcpServers": {
"neo4j_aura_agent": {
"type": "stdio",
"command": "/opt/homebrew/bin/uvx",
"args": [
"--from",
"git+https://github.com/neo4j-field/cortex-code-neo4j-mcp",
"cortex-code-neo4j-mcp"
],
"env": {
"NEO4J_CLIENT_ID": "...",
"NEO4J_CLIENT_SECRET": "...",
"NEO4J_PROJECT_ID": "...",
"NEO4J_AGENT_ID": "..."
}
}
}
}A copy is available in examples/mcp.json. Restart Cortex Code and the
neo4j_aura_agent server should appear with one tool, ask_neo4j_agent.
Once Cortex Code is running, try a simple question. The agent in your Aura
instance must be resumed; a paused database returns HTTP 404 from
/invoke with a "paused or deleted" message, which the server surfaces back
into the tool response.
ask_neo4j_agent("How many nodes are in the graph?")
A successful response includes the executed Cypher (typically
MATCH (n) RETURN count(n)), the result, and the final answer.
Once the wrapper is registered and Cortex Code is restarted, the MCP server status panel reports the bridge as connected with one tool available:
From the chat prompt, you ask a natural-language question. The example below is one of the synthetic-identity questions from the next section: "Find clusters of customers who share the same Device, Email, or Phone but have different names. List up to 5 clusters with their members."
The agent translates the question to Cypher, runs it against the Aura instance, and returns a structured answer. Cortex Code renders the markdown directly in the chat surface:
This is the practical value of pointing Cortex Code at a graph: a question that would require recursive joins in a relational store becomes one sentence of natural language and a clean, explained answer in seconds.
Additional screenshots, including the Cortex Code home screen and the
tool-details panel, are available in
docs/screenshots/.
These are written for a financial-crimes graph with labels such as
Customer, Account, Transaction, Card, Device, Beneficiary,
Merchant, Address, Email, Phone, and RiskRating. Adjust the
nouns for your own schema.
- "What node labels exist in the graph, and how many of each?"
- "What relationship types exist, and which are the most common?"
- "Give me one example of each relationship pattern, formatted as
(:Source)-[:REL]->(:Target)."
- "Find the top 10 chains of three or more transactions, starting from any customer flagged HIGH risk, where each hop is at least $10,000 and the total path value exceeds $100,000."
- "Show me customers who are connected to a HIGH risk customer within three hops via shared accounts or beneficiaries."
- "Identify round-trip transaction paths: where money leaves a customer's account and returns to the same customer within five hops."
- "Find clusters of customers who share the same Device, Email, or Phone but have different names. List up to 20 clusters with their members."
- "Which addresses are tied to the highest number of distinct customers, and how many high-value accounts hang off each?"
- "Find pairs of customers that share at least two of: address, phone, email, employer. Return them with the count of shared attributes."
- "Surface the top 10 merchants by transaction volume, and for each list the customers who accounted for more than five percent of that volume."
- "Identify groups of three or more customers who all transact with the same beneficiary and the same merchant within the same month."
- "For every customer connected to a
RiskRatingof HIGH or CRITICAL, summarise their account count, transaction volume, and the average path length to the nearest high-risk neighbour." - "Rank countries by the total transaction value flowing into them from accounts owned by customers flagged HIGH risk."
- "List devices used by more than five distinct customers in the last 24 hours. Include the customer names and their risk ratings."
- "Find customers whose number of distinct sessions in the last seven days is more than three standard deviations above their 90-day mean."
The agent will translate each question into Cypher, run it against your
Aura instance, and return the answer along with the query. Use the
verbose=true argument on any call to see the agent's reasoning trace.
Why the REST endpoint and not the MCP endpoint? The MCP endpoint at
mcp.neo4j.io/agent is gated by user-bound OAuth (verified via the
WWW-Authenticate: Bearer resource_metadata=... header it returns on
unauthenticated requests, with required scopes openid profile email).
Cortex Code's MCP HTTP transport supports static headers only. The REST
endpoint accepts service credentials and is therefore reachable from a
headless agent.
Why a wrapper and not a direct HTTP entry in mcp.json? Cortex Code's
HTTP MCP transport does not mint or refresh OAuth tokens per request. A
static Bearer in mcp.json works for at most one hour, after which it
fails until manually rotated. A local stdio process can refresh tokens on
its own without any user interaction.
Why one tool and not many? The Aura Agent already plans, tools, and executes internally. Exposing many MCP tools that all delegate to the same agent would only confuse the upstream model. One tool, one mental model.
Why hide the reasoning trace by default? The agent's thinking blocks
are useful for debugging but verbose enough to drown the answer in chat
clients. The verbose=True flag is available when you want it.
- Credentials are read from environment variables only. They are never written to disk, logged, or echoed in tool responses.
- The token cache is in-memory and lives only for the lifetime of the process. There is no on-disk token cache to leak.
- The server only reaches
api.neo4j.ioover TLS. No other outbound calls are made. - Aura API credentials grant access to the agent and to project metadata. Scope the credentials to the minimum project required, and rotate them if they are ever exposed.
- See SECURITY.md for how to report vulnerabilities.
git clone https://github.com/neo4j-field/cortex-code-neo4j-mcp.git
cd cortex-code-neo4j-mcp
uv venv
source .venv/bin/activate
uv pip install -e ".[dev]"
pytest
ruff check src testsThe test suite uses httpx.MockTransport and does not require network
access. Running it does not incur Aura API calls or token charges.
The full prompt history that produced this implementation is preserved in
prompts/ so the design decisions are reproducible and
reviewable. See prompts/initial_design.md for the original conversation
and prompts/final_prompt.md for the consolidated spec.
Apache License 2.0. See LICENSE.


