mcp-knowledge-graph 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_knowledge_graph-0.1.1/.dockerignore +14 -0
- mcp_knowledge_graph-0.1.1/.env.example +15 -0
- mcp_knowledge_graph-0.1.1/Dockerfile +43 -0
- mcp_knowledge_graph-0.1.1/PKG-INFO +196 -0
- mcp_knowledge_graph-0.1.1/README.md +183 -0
- mcp_knowledge_graph-0.1.1/data/ontology_schema.json +26 -0
- mcp_knowledge_graph-0.1.1/data/query_patterns.yaml +109 -0
- mcp_knowledge_graph-0.1.1/docker-compose.yml +36 -0
- mcp_knowledge_graph-0.1.1/pyproject.toml +32 -0
- mcp_knowledge_graph-0.1.1/scripts/docker-entrypoint.sh +37 -0
- mcp_knowledge_graph-0.1.1/scripts/verify_import_pipeline.py +59 -0
- mcp_knowledge_graph-0.1.1/src/__init__.py +1 -0
- mcp_knowledge_graph-0.1.1/src/cli.py +233 -0
- mcp_knowledge_graph-0.1.1/src/config.py +63 -0
- mcp_knowledge_graph-0.1.1/src/neo4j_client.py +229 -0
- mcp_knowledge_graph-0.1.1/src/ontology.py +58 -0
- mcp_knowledge_graph-0.1.1/src/prompts/__init__.py +1 -0
- mcp_knowledge_graph-0.1.1/src/prompts/cypher_generation.py +115 -0
- mcp_knowledge_graph-0.1.1/src/resources/__init__.py +1 -0
- mcp_knowledge_graph-0.1.1/src/resources/bundle.py +92 -0
- mcp_knowledge_graph-0.1.1/src/resources/dynamic.py +37 -0
- mcp_knowledge_graph-0.1.1/src/resources/static.py +194 -0
- mcp_knowledge_graph-0.1.1/src/server.py +483 -0
- mcp_knowledge_graph-0.1.1/src/tools/__init__.py +1 -0
- mcp_knowledge_graph-0.1.1/src/tools/context.py +56 -0
- mcp_knowledge_graph-0.1.1/src/tools/execute.py +89 -0
- mcp_knowledge_graph-0.1.1/src/tools/resolve.py +45 -0
- mcp_knowledge_graph-0.1.1/src/tools/validate.py +153 -0
- mcp_knowledge_graph-0.1.1/uv.lock +1192 -0
- mcp_knowledge_graph-0.1.1//346/211/247/350/241/214.md +27 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
NEO4J_URI=bolt://192.168.0.18:7687
|
|
2
|
+
NEO4J_USER=neo4j
|
|
3
|
+
NEO4J_PASSWORD=password
|
|
4
|
+
NEO4J_DATABASE=neo4j
|
|
5
|
+
|
|
6
|
+
# MCP 传输:stdio | streamable-http | sse
|
|
7
|
+
MCP_TRANSPORT=stdio
|
|
8
|
+
MCP_HOST=0.0.0.0
|
|
9
|
+
MCP_PORT=8000
|
|
10
|
+
MCP_SSE_PATH=/sse
|
|
11
|
+
MCP_STREAMABLE_HTTP_PATH=/mcp
|
|
12
|
+
# 局域网访问时配置对外 Host(逗号分隔);127.0.0.1/localhost 会自动并入白名单
|
|
13
|
+
MCP_ALLOWED_HOSTS=192.168.101.244:7688
|
|
14
|
+
MCP_ALLOWED_ORIGINS=http://192.168.101.244:*
|
|
15
|
+
# MCP_DISABLE_TRANSPORT_SECURITY=false
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Neo4j 知识图谱检索 MCP 服务
|
|
2
|
+
FROM python:3.11-slim-bookworm
|
|
3
|
+
|
|
4
|
+
WORKDIR /app
|
|
5
|
+
|
|
6
|
+
# 系统依赖(可选,用于健康检查等)
|
|
7
|
+
RUN apt-get update \
|
|
8
|
+
&& apt-get install -y --no-install-recommends curl \
|
|
9
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
10
|
+
|
|
11
|
+
# 先复制依赖声明,利用 Docker 层缓存
|
|
12
|
+
COPY pyproject.toml README.md ./
|
|
13
|
+
COPY src/ src/
|
|
14
|
+
COPY data/ data/
|
|
15
|
+
|
|
16
|
+
RUN pip install --no-cache-dir --upgrade pip \
|
|
17
|
+
&& pip install --no-cache-dir .
|
|
18
|
+
|
|
19
|
+
COPY scripts/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
|
20
|
+
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
|
21
|
+
|
|
22
|
+
ENV PYTHONUNBUFFERED=1 \
|
|
23
|
+
PYTHONDONTWRITEBYTECODE=1 \
|
|
24
|
+
MCP_TRANSPORT=streamable-http \
|
|
25
|
+
MCP_HOST=0.0.0.0 \
|
|
26
|
+
MCP_PORT=8000 \
|
|
27
|
+
MCP_STREAMABLE_HTTP_PATH=/mcp \
|
|
28
|
+
NEO4J_URI=bolt://neo4j:7687 \
|
|
29
|
+
NEO4J_USER=neo4j \
|
|
30
|
+
NEO4J_PASSWORD=password \
|
|
31
|
+
NEO4J_DATABASE=neo4j
|
|
32
|
+
|
|
33
|
+
EXPOSE 8000
|
|
34
|
+
|
|
35
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
|
|
36
|
+
CMD curl -sf -o /dev/null -w "%{http_code}" \
|
|
37
|
+
-X POST "http://127.0.0.1:${MCP_PORT}/mcp" \
|
|
38
|
+
-H "Content-Type: application/json" \
|
|
39
|
+
-H "Accept: application/json, text/event-stream" \
|
|
40
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"healthcheck","version":"1.0"}}}' \
|
|
41
|
+
| grep -q 200 || exit 1
|
|
42
|
+
|
|
43
|
+
ENTRYPOINT ["docker-entrypoint.sh"]
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mcp-knowledge-graph
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Neo4j knowledge graph retrieval MCP server
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: mcp>=1.6.0
|
|
7
|
+
Requires-Dist: neo4j>=5.0.0
|
|
8
|
+
Requires-Dist: pydantic-settings>=2.0
|
|
9
|
+
Requires-Dist: pydantic>=2.0
|
|
10
|
+
Requires-Dist: pyyaml>=6.0
|
|
11
|
+
Requires-Dist: twine>=6.2.0
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# Neo4j Knowledge Graph MCP Server
|
|
15
|
+
|
|
16
|
+
MCP server for enterprise knowledge graph retrieval over Neo4j. Client LLM generates Cypher; this server provides schema resources, validation, and read-only execution.
|
|
17
|
+
|
|
18
|
+
> 中文详细说明请参阅:[docs/知识图谱检索MCP服务说明.md](docs/知识图谱检索MCP服务说明.md)
|
|
19
|
+
|
|
20
|
+
## Architecture
|
|
21
|
+
|
|
22
|
+
- **10 Resources**: 7 static (schema, constraints, conventions, semantics, glossary, patterns, safety) + 3 dynamic (stats, samples, indexes)
|
|
23
|
+
- **7 Tools**: `get_graph_context`, `get_cypher_prompt`, `validate_cypher`, `execute_cypher`, `explain_cypher`, `resolve_entity`, `refresh_graph_stats`
|
|
24
|
+
- **4 Prompts**: `generate_cypher_query`, `refine_cypher_query`, `explain_query_result`, `decompose_complex_question`
|
|
25
|
+
|
|
26
|
+
Context Tools (`get_graph_context_tool`, `get_cypher_prompt_tool`) provide the same schema context as Resources/Prompts for **Tools-only** MCP clients that do not support `resources/read` or `prompts/get`.
|
|
27
|
+
|
|
28
|
+
## Setup
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
python3 -m venv .venv
|
|
32
|
+
source .venv/bin/activate
|
|
33
|
+
pip install mcp neo4j pyyaml pydantic pydantic-settings
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Environment variables:
|
|
37
|
+
|
|
38
|
+
| Variable | Default | Description |
|
|
39
|
+
|----------|---------|-------------|
|
|
40
|
+
| `NEO4J_URI` | `bolt://localhost:7687` | Neo4j Bolt URI |
|
|
41
|
+
| `NEO4J_USER` | `neo4j` | Neo4j user |
|
|
42
|
+
| `NEO4J_PASSWORD` | `password` | Neo4j password |
|
|
43
|
+
| `NEO4J_DATABASE` | `neo4j` | Neo4j database |
|
|
44
|
+
| `MCP_TRANSPORT` | `stdio` | `stdio` \| `streamable-http` \| `sse` |
|
|
45
|
+
| `MCP_HOST` | `127.0.0.1` | HTTP/SSE bind host |
|
|
46
|
+
| `MCP_PORT` | `8000` | HTTP/SSE bind port |
|
|
47
|
+
| `MCP_SSE_PATH` | `/sse` | SSE endpoint path |
|
|
48
|
+
| `MCP_STREAMABLE_HTTP_PATH` | `/mcp` | Streamable HTTP endpoint path |
|
|
49
|
+
|
|
50
|
+
Copy `.env.example` to `.env` and adjust as needed.
|
|
51
|
+
|
|
52
|
+
## Import graph data
|
|
53
|
+
|
|
54
|
+
From ontology_system:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
curl -o graph-import.cypher http://localhost:8183/graph/export/cypher
|
|
58
|
+
cypher-shell -u neo4j -p password -f graph-import.cypher
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Run MCP server
|
|
62
|
+
|
|
63
|
+
### stdio(默认,Cursor 子进程模式)
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
PYTHONPATH=. python -m src.server
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### streamable-http(推荐 HTTP 模式)
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
PYTHONPATH=. python -m src.server --transport streamable-http --host 127.0.0.1 --port 8000
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Endpoint: `http://127.0.0.1:8000/mcp`
|
|
76
|
+
|
|
77
|
+
### SSE
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
PYTHONPATH=. python -m src.server --transport sse --host 127.0.0.1 --port 8000
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Endpoint: `http://127.0.0.1:8000/sse`
|
|
84
|
+
|
|
85
|
+
### 局域网访问(Cherry Studio 等远程客户端)
|
|
86
|
+
|
|
87
|
+
绑定 `0.0.0.0` 时,必须配置允许的 Host 头,否则会出现 `421 Misdirected Request`:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
PYTHONPATH=. python -m src.server \
|
|
91
|
+
--transport streamable-http \
|
|
92
|
+
--host 0.0.0.0 \
|
|
93
|
+
--port 7688 \
|
|
94
|
+
--allowed-host 192.168.0.18:7688
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
或使用环境变量:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
export MCP_ALLOWED_HOSTS=192.168.0.18:7688
|
|
101
|
+
PYTHONPATH=. python -m src.server --transport streamable-http --host 0.0.0.0 --port 7688
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
局域网临时调试可加 `--disable-transport-security`(不推荐生产环境)。
|
|
105
|
+
|
|
106
|
+
> 绑定 `0.0.0.0` 前请确认网络访问控制;生产环境建议置于反向代理之后。
|
|
107
|
+
|
|
108
|
+
## Cursor MCP configuration
|
|
109
|
+
|
|
110
|
+
### stdio(command)
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"mcpServers": {
|
|
115
|
+
"knowledge-graph": {
|
|
116
|
+
"command": "/path/to/mcp_knowledge_graph/.venv/bin/python",
|
|
117
|
+
"args": ["-m", "src.server"],
|
|
118
|
+
"cwd": "/path/to/mcp_knowledge_graph",
|
|
119
|
+
"env": {
|
|
120
|
+
"PYTHONPATH": "/path/to/mcp_knowledge_graph",
|
|
121
|
+
"NEO4J_URI": "bolt://192.168.0.18:7687",
|
|
122
|
+
"NEO4J_PASSWORD": "password"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### streamable-http(url,含 Cherry Studio)
|
|
130
|
+
|
|
131
|
+
先启动服务(局域网需 `--allowed-host`):
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
PYTHONPATH=. python -m src.server \
|
|
135
|
+
--transport streamable-http \
|
|
136
|
+
--host 0.0.0.0 \
|
|
137
|
+
--port 7688 \
|
|
138
|
+
--allowed-host 192.168.0.18:7688
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Cherry Studio JSON 配置:
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{
|
|
145
|
+
"mcpServers": {
|
|
146
|
+
"knowledge-graph": {
|
|
147
|
+
"url": "http://192.168.0.18:7688/mcp"
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
也可通过 args 指定 transport:
|
|
154
|
+
|
|
155
|
+
```json
|
|
156
|
+
{
|
|
157
|
+
"mcpServers": {
|
|
158
|
+
"knowledge-graph": {
|
|
159
|
+
"command": "/path/to/mcp_knowledge_graph/.venv/bin/python",
|
|
160
|
+
"args": ["-m", "src.server", "--transport", "streamable-http", "--port", "8000"],
|
|
161
|
+
"cwd": "/path/to/mcp_knowledge_graph",
|
|
162
|
+
"env": {
|
|
163
|
+
"PYTHONPATH": "/path/to/mcp_knowledge_graph"
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Verify pipeline
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
PYTHONPATH=. python scripts/verify_import_pipeline.py
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Tests
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
PYTHONPATH=. pytest tests/ -v
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Workflow
|
|
183
|
+
|
|
184
|
+
### Full MCP client (Resources + Prompts)
|
|
185
|
+
|
|
186
|
+
1. Client reads `graph://schema`, `graph://ontology-constraints`, `graph://query-patterns`
|
|
187
|
+
2. Optional: `resolve_entity` for name disambiguation
|
|
188
|
+
3. Client LLM uses `generate_cypher_query` prompt to produce Cypher JSON
|
|
189
|
+
4. `validate_cypher` → `execute_cypher` → `explain_query_result`
|
|
190
|
+
|
|
191
|
+
### Tools-only client
|
|
192
|
+
|
|
193
|
+
1. `get_cypher_prompt_tool(question=...)` or `get_graph_context_tool(scope="core")`
|
|
194
|
+
2. Optional: `resolve_entity` → pass result as `entity_hints`
|
|
195
|
+
3. Platform LLM generates Cypher JSON from returned prompt fields
|
|
196
|
+
4. `validate_cypher` → `execute_cypher` → platform LLM explains records
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Neo4j Knowledge Graph MCP Server
|
|
2
|
+
|
|
3
|
+
MCP server for enterprise knowledge graph retrieval over Neo4j. Client LLM generates Cypher; this server provides schema resources, validation, and read-only execution.
|
|
4
|
+
|
|
5
|
+
> 中文详细说明请参阅:[docs/知识图谱检索MCP服务说明.md](docs/知识图谱检索MCP服务说明.md)
|
|
6
|
+
|
|
7
|
+
## Architecture
|
|
8
|
+
|
|
9
|
+
- **10 Resources**: 7 static (schema, constraints, conventions, semantics, glossary, patterns, safety) + 3 dynamic (stats, samples, indexes)
|
|
10
|
+
- **7 Tools**: `get_graph_context`, `get_cypher_prompt`, `validate_cypher`, `execute_cypher`, `explain_cypher`, `resolve_entity`, `refresh_graph_stats`
|
|
11
|
+
- **4 Prompts**: `generate_cypher_query`, `refine_cypher_query`, `explain_query_result`, `decompose_complex_question`
|
|
12
|
+
|
|
13
|
+
Context Tools (`get_graph_context_tool`, `get_cypher_prompt_tool`) provide the same schema context as Resources/Prompts for **Tools-only** MCP clients that do not support `resources/read` or `prompts/get`.
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
python3 -m venv .venv
|
|
19
|
+
source .venv/bin/activate
|
|
20
|
+
pip install mcp neo4j pyyaml pydantic pydantic-settings
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Environment variables:
|
|
24
|
+
|
|
25
|
+
| Variable | Default | Description |
|
|
26
|
+
|----------|---------|-------------|
|
|
27
|
+
| `NEO4J_URI` | `bolt://localhost:7687` | Neo4j Bolt URI |
|
|
28
|
+
| `NEO4J_USER` | `neo4j` | Neo4j user |
|
|
29
|
+
| `NEO4J_PASSWORD` | `password` | Neo4j password |
|
|
30
|
+
| `NEO4J_DATABASE` | `neo4j` | Neo4j database |
|
|
31
|
+
| `MCP_TRANSPORT` | `stdio` | `stdio` \| `streamable-http` \| `sse` |
|
|
32
|
+
| `MCP_HOST` | `127.0.0.1` | HTTP/SSE bind host |
|
|
33
|
+
| `MCP_PORT` | `8000` | HTTP/SSE bind port |
|
|
34
|
+
| `MCP_SSE_PATH` | `/sse` | SSE endpoint path |
|
|
35
|
+
| `MCP_STREAMABLE_HTTP_PATH` | `/mcp` | Streamable HTTP endpoint path |
|
|
36
|
+
|
|
37
|
+
Copy `.env.example` to `.env` and adjust as needed.
|
|
38
|
+
|
|
39
|
+
## Import graph data
|
|
40
|
+
|
|
41
|
+
From ontology_system:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
curl -o graph-import.cypher http://localhost:8183/graph/export/cypher
|
|
45
|
+
cypher-shell -u neo4j -p password -f graph-import.cypher
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Run MCP server
|
|
49
|
+
|
|
50
|
+
### stdio(默认,Cursor 子进程模式)
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
PYTHONPATH=. python -m src.server
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### streamable-http(推荐 HTTP 模式)
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
PYTHONPATH=. python -m src.server --transport streamable-http --host 127.0.0.1 --port 8000
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Endpoint: `http://127.0.0.1:8000/mcp`
|
|
63
|
+
|
|
64
|
+
### SSE
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
PYTHONPATH=. python -m src.server --transport sse --host 127.0.0.1 --port 8000
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Endpoint: `http://127.0.0.1:8000/sse`
|
|
71
|
+
|
|
72
|
+
### 局域网访问(Cherry Studio 等远程客户端)
|
|
73
|
+
|
|
74
|
+
绑定 `0.0.0.0` 时,必须配置允许的 Host 头,否则会出现 `421 Misdirected Request`:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
PYTHONPATH=. python -m src.server \
|
|
78
|
+
--transport streamable-http \
|
|
79
|
+
--host 0.0.0.0 \
|
|
80
|
+
--port 7688 \
|
|
81
|
+
--allowed-host 192.168.0.18:7688
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
或使用环境变量:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
export MCP_ALLOWED_HOSTS=192.168.0.18:7688
|
|
88
|
+
PYTHONPATH=. python -m src.server --transport streamable-http --host 0.0.0.0 --port 7688
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
局域网临时调试可加 `--disable-transport-security`(不推荐生产环境)。
|
|
92
|
+
|
|
93
|
+
> 绑定 `0.0.0.0` 前请确认网络访问控制;生产环境建议置于反向代理之后。
|
|
94
|
+
|
|
95
|
+
## Cursor MCP configuration
|
|
96
|
+
|
|
97
|
+
### stdio(command)
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"mcpServers": {
|
|
102
|
+
"knowledge-graph": {
|
|
103
|
+
"command": "/path/to/mcp_knowledge_graph/.venv/bin/python",
|
|
104
|
+
"args": ["-m", "src.server"],
|
|
105
|
+
"cwd": "/path/to/mcp_knowledge_graph",
|
|
106
|
+
"env": {
|
|
107
|
+
"PYTHONPATH": "/path/to/mcp_knowledge_graph",
|
|
108
|
+
"NEO4J_URI": "bolt://192.168.0.18:7687",
|
|
109
|
+
"NEO4J_PASSWORD": "password"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### streamable-http(url,含 Cherry Studio)
|
|
117
|
+
|
|
118
|
+
先启动服务(局域网需 `--allowed-host`):
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
PYTHONPATH=. python -m src.server \
|
|
122
|
+
--transport streamable-http \
|
|
123
|
+
--host 0.0.0.0 \
|
|
124
|
+
--port 7688 \
|
|
125
|
+
--allowed-host 192.168.0.18:7688
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Cherry Studio JSON 配置:
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
{
|
|
132
|
+
"mcpServers": {
|
|
133
|
+
"knowledge-graph": {
|
|
134
|
+
"url": "http://192.168.0.18:7688/mcp"
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
也可通过 args 指定 transport:
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"mcpServers": {
|
|
145
|
+
"knowledge-graph": {
|
|
146
|
+
"command": "/path/to/mcp_knowledge_graph/.venv/bin/python",
|
|
147
|
+
"args": ["-m", "src.server", "--transport", "streamable-http", "--port", "8000"],
|
|
148
|
+
"cwd": "/path/to/mcp_knowledge_graph",
|
|
149
|
+
"env": {
|
|
150
|
+
"PYTHONPATH": "/path/to/mcp_knowledge_graph"
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Verify pipeline
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
PYTHONPATH=. python scripts/verify_import_pipeline.py
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Tests
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
PYTHONPATH=. pytest tests/ -v
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Workflow
|
|
170
|
+
|
|
171
|
+
### Full MCP client (Resources + Prompts)
|
|
172
|
+
|
|
173
|
+
1. Client reads `graph://schema`, `graph://ontology-constraints`, `graph://query-patterns`
|
|
174
|
+
2. Optional: `resolve_entity` for name disambiguation
|
|
175
|
+
3. Client LLM uses `generate_cypher_query` prompt to produce Cypher JSON
|
|
176
|
+
4. `validate_cypher` → `execute_cypher` → `explain_query_result`
|
|
177
|
+
|
|
178
|
+
### Tools-only client
|
|
179
|
+
|
|
180
|
+
1. `get_cypher_prompt_tool(question=...)` or `get_graph_context_tool(scope="core")`
|
|
181
|
+
2. Optional: `resolve_entity` → pass result as `entity_hints`
|
|
182
|
+
3. Platform LLM generates Cypher JSON from returned prompt fields
|
|
183
|
+
4. `validate_cypher` → `execute_cypher` → platform LLM explains records
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ontology": {
|
|
3
|
+
"entity_types": {
|
|
4
|
+
"Organization": ["Group", "Company", "Department", "Team"],
|
|
5
|
+
"Person": ["Employee", "Manager", "Director", "External"],
|
|
6
|
+
"System": ["BusinessSystem", "ITSystem", "Platform", "Application"],
|
|
7
|
+
"Asset": ["Hardware", "Software", "Device"],
|
|
8
|
+
"Document": ["Policy", "Report", "Contract", "Manual"],
|
|
9
|
+
"Location": ["Office", "Branch", "Site"],
|
|
10
|
+
"Project": ["InitProject", "OperateProject"],
|
|
11
|
+
"Product": ["InternalProduct", "ExternalProduct"],
|
|
12
|
+
"Event": ["Meeting", "Audit", "Incident", "Release"]
|
|
13
|
+
},
|
|
14
|
+
"relation_types": {
|
|
15
|
+
"reports_to": {"domain": ["Person"], "range": ["Person"], "symmetric": false},
|
|
16
|
+
"belongs_to": {"domain": ["Person", "System", "Asset", "Team"], "range": ["Organization"]},
|
|
17
|
+
"controls": {"domain": ["Person", "Organization"], "range": ["System", "Asset", "Project"]},
|
|
18
|
+
"depends_on": {"domain": ["System", "Project"], "range": ["System", "Asset", "Project"]},
|
|
19
|
+
"uses": {"domain": ["Person", "Organization", "System"], "range": ["System", "Asset", "Product"]},
|
|
20
|
+
"maintains": {"domain": ["Person", "Team"], "range": ["System", "Asset"]},
|
|
21
|
+
"created_by": {"domain": ["System", "Product", "Document"], "range": ["Person", "Team"]},
|
|
22
|
+
"located_in": {"domain": ["Organization", "System", "Asset"], "range": ["Location"]},
|
|
23
|
+
"participates_in": {"domain": ["Person", "Team"], "range": ["Project", "Event"]}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
patterns:
|
|
2
|
+
- id: who_maintains_system
|
|
3
|
+
description: 谁维护某系统?
|
|
4
|
+
question_example: 谁维护 CRM 系统?
|
|
5
|
+
cypher: |
|
|
6
|
+
MATCH (p:Person)-[r:maintains]->(s:System)
|
|
7
|
+
WHERE s.name CONTAINS $system_name
|
|
8
|
+
RETURN p.name AS person, p.subtype AS subtype, r.confidence AS confidence, r.evidence_text AS evidence
|
|
9
|
+
LIMIT 20
|
|
10
|
+
parameters:
|
|
11
|
+
system_name: "CRM"
|
|
12
|
+
|
|
13
|
+
- id: dept_controls_systems
|
|
14
|
+
description: 某部门控制的系统及其依赖
|
|
15
|
+
question_example: 技术部控制了哪些系统,它们依赖什么?
|
|
16
|
+
cypher: |
|
|
17
|
+
MATCH (o:Organization)-[:controls]->(s:System)
|
|
18
|
+
WHERE o.name CONTAINS $dept_name
|
|
19
|
+
OPTIONAL MATCH (s)-[d:depends_on]->(dep)
|
|
20
|
+
RETURN o.name AS department, s.name AS system, collect(DISTINCT dep.name) AS dependencies
|
|
21
|
+
LIMIT 50
|
|
22
|
+
parameters:
|
|
23
|
+
dept_name: "技术部"
|
|
24
|
+
|
|
25
|
+
- id: person_reports_to
|
|
26
|
+
description: 某人的汇报关系
|
|
27
|
+
question_example: 张三向谁汇报?
|
|
28
|
+
cypher: |
|
|
29
|
+
MATCH (p:Person)-[r:reports_to]->(mgr:Person)
|
|
30
|
+
WHERE p.name CONTAINS $person_name
|
|
31
|
+
RETURN p.name AS person, mgr.name AS manager, r.confidence AS confidence
|
|
32
|
+
LIMIT 10
|
|
33
|
+
parameters:
|
|
34
|
+
person_name: "张三"
|
|
35
|
+
|
|
36
|
+
- id: system_dependencies
|
|
37
|
+
description: 某系统依赖哪些系统或资产
|
|
38
|
+
question_example: ERP 系统依赖什么?
|
|
39
|
+
cypher: |
|
|
40
|
+
MATCH (s:System)-[r:depends_on]->(dep)
|
|
41
|
+
WHERE s.name CONTAINS $system_name
|
|
42
|
+
RETURN s.name AS system, labels(dep)[0] AS dep_type, dep.name AS dependency, r.confidence AS confidence
|
|
43
|
+
LIMIT 30
|
|
44
|
+
parameters:
|
|
45
|
+
system_name: "ERP"
|
|
46
|
+
|
|
47
|
+
- id: org_belongs_to
|
|
48
|
+
description: 某人/系统属于哪个组织
|
|
49
|
+
question_example: 李四属于哪个部门?
|
|
50
|
+
cypher: |
|
|
51
|
+
MATCH (n)-[r:belongs_to]->(o:Organization)
|
|
52
|
+
WHERE n.name CONTAINS $entity_name AND (n:Person OR n:System OR n:Asset)
|
|
53
|
+
RETURN n.name AS entity, labels(n)[0] AS entity_type, o.name AS organization, r.confidence AS confidence
|
|
54
|
+
LIMIT 10
|
|
55
|
+
parameters:
|
|
56
|
+
entity_name: "李四"
|
|
57
|
+
|
|
58
|
+
- id: entity_neighbors
|
|
59
|
+
description: 某实体 N 跳邻居子图
|
|
60
|
+
question_example: CRM 系统周围有哪些关联实体?
|
|
61
|
+
cypher: |
|
|
62
|
+
MATCH (center {name: $entity_name})-[*1..2]-(neighbor)
|
|
63
|
+
WHERE center.name CONTAINS $entity_name
|
|
64
|
+
RETURN DISTINCT labels(neighbor)[0] AS type, neighbor.name AS name, neighbor.id AS id
|
|
65
|
+
LIMIT 50
|
|
66
|
+
parameters:
|
|
67
|
+
entity_name: "CRM"
|
|
68
|
+
|
|
69
|
+
- id: high_confidence_relations
|
|
70
|
+
description: 高置信度关系查询
|
|
71
|
+
question_example: 有哪些高置信度的系统依赖关系?
|
|
72
|
+
cypher: |
|
|
73
|
+
MATCH (a)-[r:depends_on]->(b)
|
|
74
|
+
WHERE r.confidence = 'high'
|
|
75
|
+
RETURN labels(a)[0] AS from_type, a.name AS from_name, labels(b)[0] AS to_type, b.name AS to_name, r.evidence_text AS evidence
|
|
76
|
+
LIMIT 30
|
|
77
|
+
|
|
78
|
+
- id: document_created_by
|
|
79
|
+
description: 某文档的创建者
|
|
80
|
+
question_example: 安全策略文档是谁创建的?
|
|
81
|
+
cypher: |
|
|
82
|
+
MATCH (d:Document)-[r:created_by]->(creator)
|
|
83
|
+
WHERE d.name CONTAINS $doc_name
|
|
84
|
+
RETURN d.name AS document, labels(creator)[0] AS creator_type, creator.name AS creator, r.confidence AS confidence
|
|
85
|
+
LIMIT 10
|
|
86
|
+
parameters:
|
|
87
|
+
doc_name: "安全策略"
|
|
88
|
+
|
|
89
|
+
- id: project_participants
|
|
90
|
+
description: 某项目的参与者
|
|
91
|
+
question_example: 数字化转型项目有哪些参与者?
|
|
92
|
+
cypher: |
|
|
93
|
+
MATCH (p)-[r:participates_in]->(proj:Project)
|
|
94
|
+
WHERE proj.name CONTAINS $project_name
|
|
95
|
+
RETURN proj.name AS project, labels(p)[0] AS participant_type, p.name AS participant, r.confidence AS confidence
|
|
96
|
+
LIMIT 30
|
|
97
|
+
parameters:
|
|
98
|
+
project_name: "数字化转型"
|
|
99
|
+
|
|
100
|
+
- id: location_entities
|
|
101
|
+
description: 某地点有哪些组织/系统/资产
|
|
102
|
+
question_example: 北京办公室有哪些系统?
|
|
103
|
+
cypher: |
|
|
104
|
+
MATCH (n)-[r:located_in]->(loc:Location)
|
|
105
|
+
WHERE loc.name CONTAINS $location_name
|
|
106
|
+
RETURN loc.name AS location, labels(n)[0] AS entity_type, n.name AS entity_name
|
|
107
|
+
LIMIT 50
|
|
108
|
+
parameters:
|
|
109
|
+
location_name: "北京"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
services:
|
|
2
|
+
neo4j:
|
|
3
|
+
image: neo4j:5
|
|
4
|
+
container_name: kg-neo4j
|
|
5
|
+
restart: unless-stopped
|
|
6
|
+
ports:
|
|
7
|
+
- "7474:7474"
|
|
8
|
+
- "7687:7687"
|
|
9
|
+
environment:
|
|
10
|
+
NEO4J_AUTH: neo4j/password
|
|
11
|
+
volumes:
|
|
12
|
+
- neo4j_data:/data
|
|
13
|
+
- neo4j_logs:/logs
|
|
14
|
+
|
|
15
|
+
mcp-knowledge-graph:
|
|
16
|
+
build: .
|
|
17
|
+
container_name: kg-mcp
|
|
18
|
+
restart: unless-stopped
|
|
19
|
+
ports:
|
|
20
|
+
- "7688:8000"
|
|
21
|
+
environment:
|
|
22
|
+
NEO4J_URI: bolt://neo4j:7687
|
|
23
|
+
NEO4J_USER: neo4j
|
|
24
|
+
NEO4J_PASSWORD: password
|
|
25
|
+
NEO4J_DATABASE: neo4j
|
|
26
|
+
MCP_TRANSPORT: streamable-http
|
|
27
|
+
MCP_HOST: 0.0.0.0
|
|
28
|
+
MCP_PORT: 8000
|
|
29
|
+
# 局域网客户端访问时改为宿主机 IP,例如 192.168.0.18:7688
|
|
30
|
+
MCP_ALLOWED_HOSTS: "127.0.0.1:8000,192.168.0.18:7688,localhost:7688"
|
|
31
|
+
depends_on:
|
|
32
|
+
- neo4j
|
|
33
|
+
|
|
34
|
+
volumes:
|
|
35
|
+
neo4j_data:
|
|
36
|
+
neo4j_logs:
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "mcp-knowledge-graph"
|
|
3
|
+
version = "0.1.1"
|
|
4
|
+
description = "Neo4j knowledge graph retrieval MCP server"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"mcp>=1.6.0",
|
|
9
|
+
"neo4j>=5.0.0",
|
|
10
|
+
"pyyaml>=6.0",
|
|
11
|
+
"pydantic>=2.0",
|
|
12
|
+
"pydantic-settings>=2.0",
|
|
13
|
+
"twine>=6.2.0",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[project.scripts]
|
|
17
|
+
mcp-knowledge-graph = "src.server:main"
|
|
18
|
+
|
|
19
|
+
[build-system]
|
|
20
|
+
requires = ["hatchling"]
|
|
21
|
+
build-backend = "hatchling.build"
|
|
22
|
+
|
|
23
|
+
[tool.hatch.build.targets.wheel]
|
|
24
|
+
packages = ["src"]
|
|
25
|
+
|
|
26
|
+
[tool.hatch.build.targets.wheel.force-include]
|
|
27
|
+
"data" = "data"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
[tool.pytest.ini_options]
|
|
31
|
+
pythonpath = ["."]
|
|
32
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
TRANSPORT="${MCP_TRANSPORT:-streamable-http}"
|
|
5
|
+
HOST="${MCP_HOST:-0.0.0.0}"
|
|
6
|
+
PORT="${MCP_PORT:-8000}"
|
|
7
|
+
|
|
8
|
+
set -- \
|
|
9
|
+
python -m src.server \
|
|
10
|
+
--transport "${TRANSPORT}" \
|
|
11
|
+
--host "${HOST}" \
|
|
12
|
+
--port "${PORT}"
|
|
13
|
+
|
|
14
|
+
if [ -n "${MCP_SSE_PATH}" ]; then
|
|
15
|
+
set -- "$@" --sse-path "${MCP_SSE_PATH}"
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
if [ -n "${MCP_STREAMABLE_HTTP_PATH}" ]; then
|
|
19
|
+
set -- "$@" --streamable-http-path "${MCP_STREAMABLE_HTTP_PATH}"
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
if [ -n "${MCP_ALLOWED_HOSTS}" ]; then
|
|
23
|
+
set -- "$@" --allowed-host "${MCP_ALLOWED_HOSTS}"
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
if [ -n "${MCP_ALLOWED_ORIGINS}" ]; then
|
|
27
|
+
set -- "$@" --allowed-origin "${MCP_ALLOWED_ORIGINS}"
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
if [ "${MCP_DISABLE_TRANSPORT_SECURITY}" = "true" ]; then
|
|
31
|
+
set -- "$@" --disable-transport-security
|
|
32
|
+
elif [ "${HOST}" = "0.0.0.0" ] && [ -z "${MCP_ALLOWED_HOSTS}" ]; then
|
|
33
|
+
echo "WARN: MCP_ALLOWED_HOSTS not set for 0.0.0.0; using --disable-transport-security" >&2
|
|
34
|
+
set -- "$@" --disable-transport-security
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
exec "$@"
|