This is a personal playground for experimenting with MCP (Model Context Protocol) servers and Neo4j.
The ts-mcp-cypher
directory contains TypeScript experiments while go-mcp-cypher
contains Go experiments.
The ts-mcp-cypher
contains all the experiments as it is used to experiment with the MCP protocol and an official SDK,
the other packages may have less features.
Create the root .env
file with default Neo4j configuration:
touch .env
Configure your Environment:
NEO4J_URI="bolt://localhost:7687"
NEO4J_USERNAME="neo4j"
NEO4J_PASSWORD="password"
The repository contains multiple language-specific implementation but a root MakeFile to keep consistency when switching between them: Note that the MakeFile is used just as a task-runner; no incremental build is happening.
Run the TS-MCP-SERVER
make run-ts
Inspect the TS-MCP-SERVER
make inspect-ts
Clean the TS-MCP-SERVER build
make clean-ts
--- GO targets ---
Build the GO-MCP-SERVER
make build-go
Run the GO-MCP-SERVER
make run-go
Inspect the GO-MCP-SERVER
make inspect-go
Clean the GO-MCP-SERVER build
make clean-go
To start experimenting with it, you can:
make inspect-ts
- A shortcut fornpx @modelcontextprotocol/inspector our-stdio-server
For HTTP transport:
- Create the HTTP server:
make run-ts ARGS="--transport http"
- Run the inspector as a separate process:
npx @modelcontextprotocol/inspector
make inspect-ts
You should see a browser tab open with the mcp inspector.
You can do a simple flow such as:
- connect (stdio)
- list tools
- click on read-cypher
- click on Run Tool
The the server logs are visible in the STDERR visible on the bottom-left of the inspector. It is also possible to review the History on the bottom.
Create the server:
make run-ts ARGS="--transport http"
Run the inspector separately:
npx @modelcontextprotocol/inspector
Ignore this flow, this flow was used to test behaviour when disabling/enabling/removing tools in a multi-client environment Test it with the inspector could be misleading as it creates different session for tab but also different subprocess And tools seems not to be implicitly session bounded.
To demonstrate dynamic tooling for specific session a tool call enable-admin-cypher
is exposed as a tool.
Execute the enable-admin-cypher
passing "write" as permission to enable the admin-cypher
tool.
You should see the server notifications with the event list_changed
listed.
Refresh the tool, and you'll see admin-cypher
available for that session, while enable-admin-cypher
has been removed.
Add the environment variable READ_ONLY=true
before running the server and the tool/list will return only read-cypher
READ_ONLY=true make inspect-ts
You should see the debug log manually handle list/tools
initialize
echo '{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2024-11-05", "capabilities": { "roots": { "listChanged": true }, "sampling": {}, "elicitation": {} }, "clientInfo": { "name": "bash-test", "title": "Bash test", "version": "1.0.0" } } }' | make run-ts
tool/list
echo '{"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}}' | make run-ts
tool/call
echo '{"jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": {"name": "read-cypher", "arguments": {"cypherQuery": "MATCH (n) RETURN n LIMIT 5"}}}' | make run-ts
tool/call with server notification
echo '{"jsonrpc": "2.0", "id": 4, "method": "tools/call", "params": { "name": "enable-admin-cypher", "arguments": { "permission": "write" }, "_meta": { "progressToken": 0 } } }' | make run-ts
tool/call on disabled tool
echo '{"jsonrpc": "2.0", "id": 5, "method": "tools/call", "params": { "name": "admin-cypher", "arguments": { "cypherQuery": "CREATE(n:User) SET n.name = \"MPC-USER\"" }, "_meta": { "progressToken": 2 } } }' | make run-ts
The above recreate a new process for each tool calling, to test stateful operation such as the enable-admin-cypher
-> admin-cypher
flow,
I made a really simple REPL JSON-RPC 2.0 client that takes as input the JSON-RPC message and send it to the MCP Server subprocess.
Just run:
node json-rpc-client.js node ./ts-mcp-cypher/build/index.js
You will be prompted:
Enter JSON-RPC 2.0 message (or "quit" to exit):
Try the flow above, starting from:
{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2024-11-05", "capabilities": { "roots": { "listChanged": true }, "sampling": {}, "elicitation": {} }, "clientInfo": { "name": "bash-test", "title": "Bash test", "version": "1.0.0" } } }
The STDERR
is redirected to a file named json-rpc-client-${pid}.debuglog
Note the as only read-cypher/write-cypher are exposed with not detailed description and no schema is returned or no list-database is exposed the behavior it's actually limited
Run the http server:
make run-ts ARGS="--transport http"
Add it on VSCode: mcp.json
{
"servers": {
"mcp-cypher-playground": {
"type": "http",
"url": "http://localhost:3000/mcp/"
}
}
}
or as stdio:
{
"servers": {
"mcp-playground": {
"type": "stdio",
"command": "node",
"args": [
"{{your-folder}}/MCP-playground/ts-mcp-cypher/build/index.js"
],
"env": {
"NEO4J_PASSWORD": "your-password"
}
}
}
}
The NEO4J_TOOLSET
environment variable can be defined. This demonstrate how an environment variable can be used to enable specific tools.
Available values:
all
(default) - Registers all available tools:read-cypher
,write-cypher
,admin-cypher
, andenable-admin-cypher
read-cypher
- Only the read-only Cypher query toolwrite-cypher
- Only the write Cypher query tooladmin-cypher
- Admin Cypher tool plus the enable tool- Comma-separated combinations - e.g.,
read-cypher,write-cypher
(not trimmed for demonstration, don't add extra spaces)
Examples:
# Only read operations
NEO4J_TOOLSET=read-cypher make inspect-ts
# Read and write operations only
NEO4J_TOOLSET=read-cypher,write-cypher make inspect-ts
# Admin tools only
NEO4J_TOOLSET=admin-cypher make inspect-ts
# All tools (same as default)
NEO4J_TOOLSET=all make inspect-ts
Note: When admin-cypher
is included, the enable-admin-cypher
tool is automatically registered as well.
- test confirmation as server-initiated elicitation
- convert experiments to mark3labs/mcp-go / official SDKs
- experiment with genai-toolbox