adbpg-mcp-server 1.0.8__py3-none-any.whl → 2.0.0__py3-none-any.whl

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.
adbpg_mcp_server.py DELETED
@@ -1,1175 +0,0 @@
1
- import asyncio
2
- import logging
3
- import os
4
- import sys
5
- import json
6
- import psycopg
7
- import re
8
- import ast
9
- from psycopg import OperationalError as Error
10
- from psycopg import Connection
11
- from mcp.server import Server
12
- from mcp.types import Resource, Tool, TextContent, ResourceTemplate
13
- from pydantic import AnyUrl
14
- from dotenv import load_dotenv
15
- from mcp.server.stdio import stdio_server
16
-
17
- # 配置日志,输出到标准错误
18
- logging.basicConfig(
19
- level=logging.DEBUG,
20
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
21
- stream=sys.stderr
22
- )
23
- logger = logging.getLogger("adbpg-mcp-server")
24
-
25
- # 检查环境变量Flag
26
- GRAPHRAG_ENV_IS_READY = True
27
- LLMEMORY_ENV_IS_READY = True
28
- # 加载环境变量
29
- try:
30
- load_dotenv()
31
- logger.info("Environment variables loaded")
32
-
33
- # 检查必要的环境变量
34
- required_vars = ["ADBPG_HOST", "ADBPG_PORT", "ADBPG_USER", "ADBPG_PASSWORD", "ADBPG_DATABASE"]
35
- missing_vars = [var for var in required_vars if not os.getenv(var)]
36
- if missing_vars:
37
- error_msg = f"Missing required environment variables: {', '.join(missing_vars)}"
38
- logger.error(error_msg)
39
- raise ValueError(error_msg)
40
- logger.info("All ADBPG required environment variables are set")
41
-
42
- # 检查graphrag/llmemory 环境变量
43
- required_graphrag_vars = [
44
- "GRAPHRAG_LLM_MODEL",
45
- "GRAPHRAG_API_KEY",
46
- "GRAPHRAG_BASE_URL",
47
- "GRAPHRAG_EMBEDDING_MODEL",
48
- "GRAPHRAG_EMBEDDING_BASE_URL",
49
- "GRAPHRAG_EMBEDDING_API_KEY"
50
- ]
51
- missing_graphrag_vars = [var for var in required_graphrag_vars if not os.getenv(var)]
52
- if missing_graphrag_vars:
53
- GRAPHRAG_ENV_IS_READY = False
54
- error_msg = f"Missing required graphrag environment variables:{', '.join(missing_graphrag_vars)}"
55
- logger.error(error_msg)
56
- else:
57
- logger.info("All graphRAG required environment variables are set")
58
-
59
- required_llmemory_vars = ["LLMEMORY_LLM_MODEL", "LLMEMORY_API_KEY", "LLMEMORY_BASE_URL", "LLMEMORY_EMBEDDING_MODEL"]
60
- missing_llmemory_vars = [var for var in required_llmemory_vars if not os.getenv(var)]
61
- if missing_llmemory_vars:
62
- LLMEMORY_ENV_IS_READY = False
63
- error_msg = f"Missing required llm memory environment variables:{', '.join(missing_llmemory_vars)}"
64
- logger.error(error_msg)
65
- else:
66
- logger.info("All llm memory required environment variables are set")
67
-
68
-
69
- except Exception as e:
70
- logger.error(f"Error loading environment variables: {e}")
71
- sys.exit(1)
72
-
73
- SERVER_VERSION = "0.2.0"
74
-
75
-
76
- # 获得 graphrag 初始化配置
77
- def get_graphrag_config():
78
- graphrag_config = {
79
- "llm_model": os.getenv("GRAPHRAG_LLM_MODEL"),
80
- "llm_api_key": os.getenv("GRAPHRAG_API_KEY"),
81
- "llm_url": os.getenv("GRAPHRAG_BASE_URL"),
82
- "embedding_model": os.getenv("GRAPHRAG_EMBEDDING_MODEL"),
83
- "embedding_api_key": os.getenv("GRAPHRAG_EMBEDDING_API_KEY"),
84
- "embedding_url": os.getenv("GRAPHRAG_EMBEDDING_BASE_URL"),
85
- "language": os.getenv("GRAPHRAG_LANGUAGE", "English"),
86
- "entity_types": os.getenv("GRAPHRAG_ENTITY_TYPES"),
87
- "relationship_types": os.getenv("GRAPHRAG_RELATIONSHIP_TYPES"),
88
- "postgres_password": os.getenv("ADBPG_PASSWORD")
89
- }
90
- return graphrag_config
91
-
92
- # 获得llmemory 初始化配置
93
- def get_llmemory_config():
94
- config = get_db_config()
95
- port = 3000
96
- sql = """
97
- select port from gp_segment_configuration where content = -1 and role = 'p';
98
- """
99
- try:
100
- with psycopg.connect(**config) as conn:
101
- conn.autocommit = True
102
- with conn.cursor() as cursor:
103
- cursor.execute(sql)
104
- port = cursor.fetchone()[0]
105
- except Error as e:
106
- raise RuntimeError(f"Database error: {str(e)}")
107
- llmemory_enable_graph = os.getenv("LLMEMORY_ENABLE_GRAPH", "False")
108
-
109
-
110
- llm_memory_config = {
111
- "llm": {
112
- "provider": "openai",
113
- "config": {
114
- "model": os.getenv("LLMEMORY_LLM_MODEL"),
115
- "openai_base_url": os.getenv("LLMEMORY_BASE_URL"),
116
- "api_key": os.getenv("LLMEMORY_API_KEY")
117
- }
118
- },
119
- "embedder": {
120
- "provider": "openai",
121
- "config": {
122
- "model": os.getenv("LLMEMORY_EMBEDDING_MODEL"),
123
- "embedding_dims": os.getenv("LLMEMORY_EMBEDDING_DIMS", 1024),
124
- "api_key": os.getenv("LLMEMORY_API_KEY"),
125
- "openai_base_url": os.getenv("LLMEMORY_BASE_URL")
126
- }
127
- },
128
- "vector_store": {
129
- "provider": "adbpg",
130
- "config": {
131
- "user": os.getenv("ADBPG_USER"),
132
- "password": os.getenv("ADBPG_PASSWORD"),
133
- "dbname": os.getenv("ADBPG_DATABASE"),
134
- "hnsw": "True",
135
- "embedding_model_dims": os.getenv("LLMEMORY_EMBEDDING_DIMS", 1024),
136
- "port": port
137
- }
138
- }
139
- }
140
- if llmemory_enable_graph == "True" or llmemory_enable_graph == "true":
141
- llm_memory_config["graph_store"] = {
142
- "provider": "adbpg",
143
- "config": {
144
- "url": "http://localhost",
145
- "username": os.getenv("ADBPG_USER"),
146
- "password": os.getenv("ADBPG_PASSWORD"),
147
- "database": os.getenv("ADBPG_DATABASE"),
148
- "port": port
149
- }
150
- }
151
- return llm_memory_config
152
-
153
- def get_db_config():
154
- """从环境变量获取数据库配置信息"""
155
- try:
156
- config = {
157
- "host": os.getenv("ADBPG_HOST", "localhost"),
158
- "port": os.getenv("ADBPG_PORT"),
159
- "user": os.getenv("ADBPG_USER"),
160
- "password": os.getenv("ADBPG_PASSWORD"),
161
- "dbname": os.getenv("ADBPG_DATABASE"),
162
- "application_name": f"adbpg-mcp-server-{SERVER_VERSION}"
163
- }
164
-
165
- # 记录配置信息(不包含密码)
166
- logger.info(f"Database config: host={config['host']}, port={config['port']}, user={config['user']}, dbname={config['dbname']}")
167
- return config
168
- except Exception as e:
169
- logger.error(f"Error getting database config: {str(e)}")
170
- raise
171
-
172
- # 全局graphrag长连接 并 初始化
173
- GRAPHRAG_CONN: Connection | None = None
174
- def get_graphrag_tool_connection() -> Connection:
175
- global GRAPHRAG_CONN
176
- global GRAPHRAG_ENV_IS_READY
177
- config = get_db_config()
178
- # 如果未连接,或者连接失效 重新连接
179
- if GRAPHRAG_CONN is None or GRAPHRAG_CONN.closed:
180
- GRAPHRAG_CONN = psycopg.connect(**config)
181
- GRAPHRAG_CONN.autocommit = True
182
- try:
183
- graphrag_conn = GRAPHRAG_CONN
184
- with graphrag_conn.cursor() as cursor:
185
- cursor.execute("SELECT adbpg_graphrag.initialize(%s::json);", (graphrag_config_str,))
186
- logger.info(f"[GraphRAG] Use the connection {id(graphrag_conn)} when executing the graphrag init")
187
- logger.info("Successfully initialize the graphrag server\n")
188
- except Exception as e:
189
- GRAPHRAG_ENV_IS_READY = False
190
- logger.error(f"Failed to initialize the graphrag server: {e}")
191
- # 重新执行初始化
192
- else:
193
- # 发送一个轻量级查询 检测 连接是否健康
194
- try:
195
- with GRAPHRAG_CONN.cursor() as cur:
196
- cur.execute("SELECT 1;")
197
- _ = cur.fetchone()
198
- except Exception:
199
- # 重连
200
- GRAPHRAG_CONN.close()
201
- GRAPHRAG_CONN = psycopg.connect(**config)
202
- GRAPHRAG_CONN.autocommit = True
203
- # 重新执行初始化
204
- try:
205
- graphrag_conn = GRAPHRAG_CONN
206
- with graphrag_conn.cursor() as cursor:
207
- cursor.execute("SELECT adbpg_graphrag.initialize(%s::json);", (graphrag_config_str,))
208
- logger.info(f"[GraphRAG] Use the connection {id(graphrag_conn)} when executing the graphrag init")
209
- logger.info("Successfully initialize the graphrag server\n")
210
- except Exception as e:
211
- GRAPHRAG_ENV_IS_READY = False
212
- logger.error(f"Failed to initialize the graphrag server: {e}")
213
-
214
- return GRAPHRAG_CONN
215
-
216
- LLM_MEMORY_CONN: Connection | None = None
217
- def get_llm_memory_tool_connection() -> Connection:
218
- global LLMEMORY_ENV_IS_READY
219
- global LLM_MEMORY_CONN
220
- config = get_db_config()
221
- # 如果未连接,或者连接失效 重新连接
222
- if LLM_MEMORY_CONN is None or LLM_MEMORY_CONN.closed:
223
- LLM_MEMORY_CONN = psycopg.connect(**config)
224
- LLM_MEMORY_CONN.autocommit = True
225
- try:
226
- llm_memory_conn = LLM_MEMORY_CONN
227
- with llm_memory_conn.cursor() as cursor:
228
- cursor.execute("SELECT adbpg_llm_memory.config(%s::json)", (llm_memory_config_str,))
229
- logger.info(f"[LLM Memory] Use the connection {id(llm_memory_conn)} when executing the llm_memory init")
230
- logger.info("Successfully initialize the llm server\n")
231
- except Exception as e:
232
- LLMEMORY_ENV_IS_READY = False
233
- logger.error(f"Failed to initialize the llm_memory server: {e}")
234
- else:
235
- # 发送一个轻量级查询 检测 连接是否健康
236
- try:
237
- with LLM_MEMORY_CONN.cursor() as cur:
238
- cur.execute("SELECT 1;")
239
- _ = cur.fetchone()
240
- except Exception:
241
- # 重连
242
- LLM_MEMORY_CONN.close()
243
- LLM_MEMORY_CONN = psycopg.connect(**config)
244
- LLM_MEMORY_CONN.autocommit = True
245
- try:
246
- llm_memory_conn = LLM_MEMORY_CONN
247
- with llm_memory_conn.cursor() as cursor:
248
- cursor.execute("SELECT adbpg_llm_memory.config(%s::json)", (llm_memory_config_str,))
249
- logger.info(f"[LLM Memory] Use the connection {id(llm_memory_conn)} when executing the llm_memory init")
250
- logger.info("Successfully initialize the llm server\n")
251
- except Exception as e:
252
- LLMEMORY_ENV_IS_READY = False
253
- logger.error(f"Failed to initialize the llm_memory server: {e}")
254
-
255
- return LLM_MEMORY_CONN
256
-
257
- #### 初始化
258
- if GRAPHRAG_ENV_IS_READY == True:
259
- # 初始化 graphrag
260
- logger.info("Starting graphRAG server...")
261
- graphrag_config = get_graphrag_config()
262
- graphrag_config_str = json.dumps(graphrag_config)
263
- # 建立长连接 并 初始化
264
- get_graphrag_tool_connection()
265
- if LLMEMORY_ENV_IS_READY == True:
266
- # 初始化 llmemory
267
- logger.info("Starting llmemory server...")
268
- llm_memory_config = get_llmemory_config()
269
- llm_memory_config_str = json.dumps(llm_memory_config)
270
- # 建立长连接 并 初始化
271
- get_llm_memory_tool_connection()
272
-
273
- # 初始化服务器
274
- try:
275
- app = Server("adbpg-mcp-server")
276
- logger.info("MCP server initialized")
277
- except Exception as e:
278
- logger.error(f"Error initializing MCP server: {e}")
279
- sys.exit(1)
280
-
281
- @app.list_resources()
282
- async def list_resources() -> list[Resource]:
283
- """列出可用的基本资源"""
284
- try:
285
- return [
286
- Resource(
287
- uri="adbpg:///schemas",
288
- name="All Schemas",
289
- description="AnalyticDB PostgreSQL schemas. List all schemas in the database",
290
- mimeType="text/plain"
291
- )
292
- ]
293
- except Exception as e:
294
- logger.error(f"Error listing resources: {str(e)}")
295
- raise
296
-
297
- @app.list_resource_templates()
298
- async def list_resource_templates() -> list[ResourceTemplate]:
299
- """
300
- 定义动态资源模板
301
-
302
- 返回:
303
- list[ResourceTemplate]: 资源模板列表
304
- 包含以下模板:
305
- - 列出schema中的表
306
- - 获取表DDL
307
- - 获取表统计信息
308
- """
309
- return [
310
- ResourceTemplate(
311
- uriTemplate="adbpg:///{schema}/tables", # 表列表模板
312
- name="Schema Tables",
313
- description="List all tables in a specific schema",
314
- mimeType="text/plain"
315
- ),
316
- ResourceTemplate(
317
- uriTemplate="adbpg:///{schema}/{table}/ddl", # 表DDL模板
318
- name="Table DDL",
319
- description="Get the DDL script of a table in a specific schema",
320
- mimeType="text/plain"
321
- ),
322
- ResourceTemplate(
323
- uriTemplate="adbpg:///{schema}/{table}/statistics", # 表统计信息模板
324
- name="Table Statistics",
325
- description="Get statistics information of a table",
326
- mimeType="text/plain"
327
- )
328
- ]
329
-
330
- @app.read_resource()
331
- async def read_resource(uri: AnyUrl) -> str:
332
- """
333
- 读取资源内容
334
-
335
- 参数:
336
- uri (AnyUrl): 资源URI
337
-
338
- 返回:
339
- str: 资源内容
340
-
341
- 支持的URI格式:
342
- - adbpg:///schemas: 列出所有schema
343
- - adbpg:///{schema}/tables: 列出指定schema中的表
344
- - adbpg:///{schema}/{table}/ddl: 获取表的DDL
345
- - adbpg:///{schema}/{table}/statistics: 获取表的统计信息
346
- """
347
- config = get_db_config()
348
- uri_str = str(uri)
349
-
350
- if not uri_str.startswith("adbpg:///"):
351
- raise ValueError(f"Invalid URI scheme: {uri_str}")
352
-
353
- try:
354
- with psycopg.connect(**config) as conn: # 建立数据库连接
355
- conn.autocommit = True # 设置自动提交
356
- with conn.cursor() as cursor: # 创建游标
357
- path_parts = uri_str[9:].split('/') # 解析URI路径
358
-
359
- if path_parts[0] == "schemas":
360
- # 列出所有schema
361
- query = """
362
- SELECT schema_name
363
- FROM information_schema.schemata
364
- WHERE schema_name NOT IN ('pg_catalog', 'information_schema')
365
- ORDER BY schema_name;
366
- """
367
- cursor.execute(query)
368
- schemas = cursor.fetchall()
369
- return "\n".join([schema[0] for schema in schemas])
370
-
371
- elif len(path_parts) == 2 and path_parts[1] == "tables":
372
- # 列出指定schema中的表
373
- schema = path_parts[0]
374
- query = f"""
375
- SELECT table_name, table_type
376
- FROM information_schema.tables
377
- WHERE table_schema = %s
378
- ORDER BY table_name;
379
- """
380
- cursor.execute(query, (schema,))
381
- tables = cursor.fetchall()
382
- return "\n".join([f"{table[0]} ({table[1]})" for table in tables])
383
-
384
- elif len(path_parts) == 3 and path_parts[2] == "ddl":
385
- # 获取表的DDL
386
- schema = path_parts[0]
387
- table = path_parts[1]
388
- query = f"""
389
- SELECT pg_get_ddl('{schema}.{table}'::regclass);
390
- """
391
- cursor.execute(query)
392
- ddl = cursor.fetchone()
393
- return ddl[0] if ddl else f"No DDL found for {schema}.{table}"
394
-
395
- elif len(path_parts) == 3 and path_parts[2] == "statistics":
396
- # 获取表的统计信息
397
- schema = path_parts[0]
398
- table = path_parts[1]
399
- query = """
400
- SELECT
401
- schemaname,
402
- tablename,
403
- attname,
404
- null_frac,
405
- avg_width,
406
- n_distinct,
407
- most_common_vals,
408
- most_common_freqs
409
- FROM pg_stats
410
- WHERE schemaname = %s AND tablename = %s
411
- ORDER BY attname;
412
- """
413
- cursor.execute(query, (schema, table))
414
- rows = cursor.fetchall()
415
- if not rows:
416
- return f"No statistics found for {schema}.{table}"
417
-
418
- result = []
419
- for row in rows:
420
- result.append(f"Column: {row[2]}")
421
- result.append(f" Null fraction: {row[3]}")
422
- result.append(f" Average width: {row[4]}")
423
- result.append(f" Distinct values: {row[5]}")
424
- if row[6]:
425
- result.append(f" Most common values: {row[6]}")
426
- result.append(f" Most common frequencies: {row[7]}")
427
- result.append("")
428
- return "\n".join(result)
429
-
430
- raise ValueError(f"Invalid resource URI format: {uri_str}")
431
-
432
- except Error as e:
433
- raise RuntimeError(f"Database error: {str(e)}")
434
-
435
- @app.list_tools()
436
- async def list_tools() -> list[Tool]:
437
- """
438
- 列出可用的工具
439
-
440
- 返回:
441
- list[Tool]: 工具列表
442
- 包含以下工具:
443
- - execute_select_sql: 执行SELECT查询
444
- - execute_dml_sql: 执行DML操作
445
- - execute_ddl_sql: 执行DDL操作
446
- - analyze_table: 分析表统计信息
447
- - explain_query: 获取查询执行计划
448
-
449
- - adbpg_graphrag_upload: 执行 graphRAG upload 操作,上传文本
450
- - adbpg_graphrag_query: 执行 graphRAG query 操作
451
- - adbpg_graphrag_upload_decision_tree: 上传一个决策树
452
- - adbpg_graphrag_append_decision_tree: 在某个节点上新增子树
453
- - adbpg_graphrag_delete_decision_tree: 根据节点id删除起下层子树
454
-
455
- - adbpg_llm_memory_add: 执行新增记忆操作
456
- - adbpg_llm_memory_get_all: 获取所有记忆
457
- - adbpg_llm_memory_search: 根据查询检索记忆
458
- - adbpg_llm_memory_delete_all: 删除所有记忆
459
- """
460
- return [
461
- Tool(
462
- name="execute_select_sql",
463
- description="Execute SELECT SQL to query data from ADBPG database.",
464
- inputSchema={
465
- "type": "object",
466
- "properties": {
467
- "query": {
468
- "type": "string",
469
- "description": "The (SELECT) SQL query to execute"
470
- }
471
- },
472
- "required": ["query"]
473
- }
474
- ),
475
- Tool(
476
- name="execute_dml_sql",
477
- description="Execute (INSERT, UPDATE, DELETE) SQL to modify data in ADBPG database.",
478
- inputSchema={
479
- "type": "object",
480
- "properties": {
481
- "query": {
482
- "type": "string",
483
- "description": "The DML SQL query to execute"
484
- }
485
- },
486
- "required": ["query"]
487
- }
488
- ),
489
- Tool(
490
- name="execute_ddl_sql",
491
- description="Execute (CREATE, ALTER, DROP) SQL statements to manage database objects.",
492
- inputSchema={
493
- "type": "object",
494
- "properties": {
495
- "query": {
496
- "type": "string",
497
- "description": "The DDL SQL query to execute"
498
- }
499
- },
500
- "required": ["query"]
501
- }
502
- ),
503
- Tool(
504
- name="analyze_table",
505
- description="Execute ANALYZE command to collect table statistics.",
506
- inputSchema={
507
- "type": "object",
508
- "properties": {
509
- "schema": {
510
- "type": "string",
511
- "description": "Schema name"
512
- },
513
- "table": {
514
- "type": "string",
515
- "description": "Table name"
516
- }
517
- },
518
- "required": ["schema", "table"]
519
- }
520
- ),
521
- Tool(
522
- name="explain_query",
523
- description="Get query execution plan.",
524
- inputSchema={
525
- "type": "object",
526
- "properties": {
527
- "query": {
528
- "type": "string",
529
- "description": "The SQL query to analyze"
530
- }
531
- },
532
- "required": ["query"]
533
- }
534
- ),
535
-
536
- #### graphrag & llm_memory tool list
537
- Tool(
538
- name = "adbpg_graphrag_upload",
539
- description = "Execute graphrag upload operation",
540
- # 参数:filename text, context text
541
- # filename 表示文件名称, context 表示文件内容
542
- inputSchema = {
543
- "type": "object",
544
- "properties": {
545
- "filename": {
546
- "type": "string",
547
- "description": "The file name need to upload"
548
- },
549
- "context": {
550
- "type": "string",
551
- "description": "the context of your file"
552
- }
553
- },
554
- "required": ["filename", "context"]
555
- }
556
- ),
557
- Tool(
558
- name = "adbpg_graphrag_query",
559
- description = "Execute graphrag query operation",
560
- # 参数:query_str text, [query_mode text]
561
- # query_str 是询问的问题,query_mode 选择查询模式
562
- inputSchema = {
563
- "type": "object",
564
- "properties": {
565
- "query_str": {
566
- "type": "string",
567
- "description": "The query you want to ask"
568
- },
569
- "query_mode": {
570
- "type": "string",
571
- "description": "The query mode you need to choose [ bypass,naive, local, global, hybrid, mix[default], tree ]."
572
- },
573
- "start_search_node_id": {
574
- "type": "string",
575
- "description": "If using 'tree' query mode, set the start node ID of tree."
576
- }
577
- },
578
- "required": ["query_str"]
579
- }
580
- ),
581
- Tool(
582
- name = "adbpg_graphrag_upload_decision_tree",
583
- description = " Upload a decision tree with the specified root_node. If the root_node does not exist, a new decision tree will be created. ",
584
- # context text, root_node text
585
- inputSchema = {
586
- "type": "object",
587
- "properties": {
588
- "root_node": {
589
- "type": "string",
590
- "description": "the root_noot (optional)"
591
- },
592
- "context": {
593
- "type": "string",
594
- "description": "the context of decision"
595
- }
596
- },
597
- "required": ["context"]
598
- }
599
- ),
600
- Tool(
601
- name = "adbpg_graphrag_append_decision_tree",
602
- description = "Append a subtree to an existing decision tree at the node specified by root_node_id. ",
603
- # para: context text, root_node_id text
604
- inputSchema = {
605
- "type": "object",
606
- "properties": {
607
- "root_node_id": {
608
- "type": "string",
609
- "description": "the root_noot_id"
610
- },
611
- "context": {
612
- "type": "string",
613
- "description": "the context of decision"
614
- }
615
- },
616
- "required": ["context", "root_node_id"]
617
- }
618
- ),
619
- Tool(
620
- name = "adbpg_graphrag_delete_decision_tree",
621
- description = " Delete a sub-decision tree under the node specified by root_node_entity. ",
622
- # para: root_node_entity text
623
- inputSchema = {
624
- "type": "object",
625
- "properties": {
626
- "root_node_entity": {
627
- "type": "string",
628
- "description": "the root_noot_entity"
629
-
630
- }
631
- },
632
- "required": ["root_node_entity"]
633
- }
634
- ),
635
- Tool(
636
- name = "adbpg_graphrag_reset_tree_query",
637
- description = " Reset the decision tree in the tree query mode",
638
- # para:
639
- inputSchema = {
640
- "type": "object",
641
- "required": []
642
- }
643
- ),
644
- Tool(
645
- name = "adbpg_llm_memory_add",
646
- description = "Execute llm_memory add operation",
647
- # 参数:messages json, user_id text, run_id text, agent_id text, metadata json
648
- # 增加新的记忆
649
- inputSchema={
650
- "type": "object",
651
- "properties": {
652
- "messages": {
653
- "type": "array",
654
- "items": {
655
- "type": "object",
656
- "properties": {
657
- "role": {"type": "string"},
658
- "content": {"type": "string"}
659
- },
660
- "required": ["role", "content"]
661
- },
662
- "description": "List of messages objects (e.g., conversation history)"
663
- },
664
- "user_id": {
665
- "type": "string",
666
- "description": "the user_id"
667
- },
668
- "run_id": {
669
- "type": "string",
670
- "description": "the run_id"
671
- },
672
- "agent_id": {
673
- "type": "string",
674
- "description": "the agent_id"
675
- },
676
- "metadata": {
677
- "type": "object",
678
- "description": "the metatdata json"
679
- },
680
- "memory_type": {
681
- "type": "string",
682
- "description": "the memory_type text"
683
- },
684
- "prompt": {
685
- "type": "string",
686
- "description": "the prompt"
687
- }
688
- },
689
- "required": ["messages"]
690
- }
691
- ),
692
- Tool(
693
- name = "adbpg_llm_memory_get_all",
694
- description = "Execute llm_memory get_all operation",
695
- # 参数:user_id text, run_id text, agent_id text
696
- # 获取某个用户或者某个agent的所有记忆
697
- inputSchema={
698
- "type": "object",
699
- "properties": {
700
- "user_id": {
701
- "type": "string",
702
- "description": "The user_id"
703
- },
704
- "run_id": {
705
- "type": "string",
706
- "description": "The run_id"
707
- },
708
- "agent_id": {
709
- "type": "string",
710
- "description": "The agent_id"
711
- }
712
- },
713
- "required": []
714
- }
715
- ),
716
- Tool(
717
- name = "adbpg_llm_memory_search",
718
- description = "Execute llm_memory search operation",
719
- # 参数:query text, user_id text, run_id text, agent_id text, filter json
720
- # 获取与给定 query 相关的记忆
721
- inputSchema={
722
- "type": "object",
723
- "properties": {
724
- "query": {
725
- "type": "string",
726
- "description": "llm_memory relevant query"
727
- },
728
- "user_id": {
729
- "type": "string",
730
- "description": "The search of user_id"
731
- },
732
- "run_id": {
733
- "type": "string",
734
- "description": "The search of run_id"
735
- },
736
- "agent_id": {
737
- "type": "string",
738
- "description": "The search of agent_id"
739
- },
740
- "filter": {
741
- "type": "object",
742
- "description": "The search of filter"
743
- }
744
- },
745
- "required": ["query"]
746
- }
747
- )
748
- ,
749
- Tool(
750
- name = "adbpg_llm_memory_delete_all",
751
- description = "Execute llm_memory delete_all operation",
752
- # 参数:user_id text, run_id text, agent_id text
753
- # 删除某个用户或者agent的所有记忆
754
- inputSchema={
755
- "type": "object",
756
- "properties": {
757
- "user_id": {
758
- "type": "string",
759
- "description": "The user_id"
760
- },
761
- "run_id": {
762
- "type": "string",
763
- "description": "The run_id"
764
- },
765
- "agent_id": {
766
- "type": "string",
767
- "description": "The agent_id"
768
- }
769
- },
770
- "required": []
771
- }
772
- )
773
-
774
- ]
775
-
776
- def get_graphrag_tool_result(wrapped_sql, params) -> list[TextContent]:
777
- try:
778
- conn = get_graphrag_tool_connection()
779
- with conn.cursor() as cursor:
780
- cursor.execute(wrapped_sql, params)
781
- if cursor.description:
782
- json_result = cursor.fetchone()[0]
783
- return [TextContent(type="text", text=json_result)]
784
- else:
785
- return [TextContent(type="text", text="graphrag command executed successfully")]
786
- except Exception as e:
787
- return [TextContent(type="text", text=f"Error executing graphrag command: {str(e)}")]
788
-
789
- def get_llm_memory_tool_result(wrapped_sql, params) -> list[TextContent]:
790
- try:
791
- conn = get_llm_memory_tool_connection()
792
- with conn.cursor() as cursor:
793
-
794
- cursor.execute(wrapped_sql, params)
795
-
796
- if cursor.description:
797
- json_result = cursor.fetchone()[0]
798
- return [TextContent(type="text", text=json_result)]
799
- else:
800
- return [TextContent(type="text", text="llm_memory command executed successfully")]
801
- except Exception as e:
802
- return [TextContent(type="text", text=f"Error executing llm_memory command: {str(e)}")]
803
-
804
-
805
- @app.call_tool()
806
- async def call_tool(name: str, arguments: dict) -> list[TextContent]:
807
- """
808
- 执行工具操作
809
-
810
- 参数:
811
- name (str): 工具名称
812
- arguments (dict): 工具参数
813
-
814
- 返回:
815
- list[TextContent]: 执行结果
816
-
817
- 支持的工具:
818
- - execute_select_sql: 执行SELECT查询
819
- - execute_dml_sql: 执行DML操作
820
- - execute_ddl_sql: 执行DDL操作
821
- - analyze_table: 分析表统计信息
822
- - explain_query: 获取查询执行计划
823
-
824
- - adbpg_graphrag_upload: 执行 graphRAG upload 操作,上传文本
825
- - adbpg_graphrag_query: 执行 graphRAG query 操作
826
-
827
- - adbpg_llm_memory_add: 执行新增记忆操作
828
- - adbpg_llm_memory_get_all: 获取所有记忆
829
- - adbpg_llm_memory_search: 根据查询检索记忆
830
- - adbpg_llm_memory_delete_all: 删除所有记忆
831
- """
832
- config = get_db_config()
833
- global GRAPHRAG_ENV_IS_READY
834
- # 根据工具名称处理不同的操作
835
- if name == "execute_select_sql":
836
- query = arguments.get("query")
837
- if not query:
838
- raise ValueError("Query is required")
839
- if not query.strip().upper().startswith("SELECT"):
840
- raise ValueError("Query must be a SELECT statement")
841
- query = query.rstrip().rstrip(';')
842
- query = f"""
843
- SELECT json_agg(row_to_json(t))
844
- FROM ({query}) AS t
845
- """
846
- elif name == "execute_dml_sql":
847
- query = arguments.get("query")
848
- if not query:
849
- raise ValueError("Query is required")
850
- if not any(query.strip().upper().startswith(keyword) for keyword in ["INSERT", "UPDATE", "DELETE"]):
851
- raise ValueError("Query must be a DML statement (INSERT, UPDATE, DELETE)")
852
- elif name == "execute_ddl_sql":
853
- query = arguments.get("query")
854
- if not query:
855
- raise ValueError("Query is required")
856
- if not any(query.strip().upper().startswith(keyword) for keyword in ["CREATE", "ALTER", "DROP", "TRUNCATE"]):
857
- raise ValueError("Query must be a DDL statement (CREATE, ALTER, DROP)")
858
- elif name == "analyze_table":
859
- schema = arguments.get("schema")
860
- table = arguments.get("table")
861
- if not all([schema, table]):
862
- raise ValueError("Schema and table are required")
863
- query = f"ANALYZE {schema}.{table}"
864
- elif name == "explain_query":
865
- query = arguments.get("query")
866
- if not query:
867
- raise ValueError("Query is required")
868
- query = f"EXPLAIN {query}"
869
-
870
- # adbpg_graphrag tool
871
- elif name == "adbpg_graphrag_upload":
872
- # GraphRAG 服务初始化失败,工具不可用
873
- if GRAPHRAG_ENV_IS_READY == False:
874
- raise ValueError("GraphRAG Server initialization failed. This tool cannot be used.")
875
- filename = arguments.get("filename")
876
- context = arguments.get("context")
877
- if not filename:
878
- raise ValueError("Filename is required")
879
- if not context:
880
- raise ValueError("Context if required")
881
- # 命令拼接
882
- wrapped_sql = f"""
883
- SELECT adbpg_graphrag.upload(%s::text, %s::text)
884
- """
885
- params = [filename, context]
886
- return get_graphrag_tool_result(wrapped_sql, params)
887
-
888
- elif name == "adbpg_graphrag_query":
889
- # GraphRAG 服务初始化失败,工具不可用
890
- if GRAPHRAG_ENV_IS_READY == False:
891
- raise ValueError("GraphRAG Server initialization failed. This tool cannot be used.")
892
- query_str = arguments.get("query_str")
893
- query_mode = arguments.get("query_mode")
894
- start_search_node_id = arguments.get("start_search_node_id")
895
-
896
- if not query_str:
897
- raise ValueError("Query is required")
898
- if not query_mode:
899
- # default mode
900
- query_mode = "mix"
901
-
902
- wrapped_sql = f"""
903
- SELECT adbpg_graphrag.query(%s::text, %s::text)
904
- """
905
- params = [query_str, query_mode]
906
-
907
- if start_search_node_id:
908
- wrapped_sql = f"""
909
- SELECT adbpg_graphrag.query(%s::text, %s::text, %s::text)
910
- """
911
- params = [query_str, query_mode, start_search_node_id]
912
-
913
- return get_graphrag_tool_result(wrapped_sql, params)
914
-
915
- elif name == "adbpg_graphrag_reset_tree_query":
916
- if GRAPHRAG_ENV_IS_READY == False:
917
- raise ValueError("GraphRAG Server initialization failed. This tool cannot be used.")
918
- wrapped_sql = f"""
919
- SELECT adbpg_graphrag.reset_tree_query()
920
- """
921
- params = []
922
- return get_graphrag_tool_result(wrapped_sql, params)
923
-
924
- elif name == "adbpg_graphrag_upload_decision_tree":
925
- if GRAPHRAG_ENV_IS_READY == False:
926
- raise ValueError("GraphRAG Server initialization failed. This tool cannot be used.")
927
- root_node = arguments.get("root_node")
928
- context = arguments.get("context")
929
- if not context:
930
- raise ValueError("Decision Tree Context is required")
931
- if not root_node:
932
- root_node = None
933
- wrapped_sql = f"""
934
- SELECT adbpg_graphrag.upload_decision_tree(%s::text, %s::text)
935
- """
936
- params = [context, root_node]
937
- return get_graphrag_tool_result(wrapped_sql, params)
938
-
939
- elif name == "adbpg_graphrag_append_decision_tree":
940
- if GRAPHRAG_ENV_IS_READY == False:
941
- raise ValueError("GraphRAG Server initialization failed. This tool cannot be used.")
942
- root_node = arguments.get("root_node_id")
943
- context = arguments.get("context")
944
- if not context:
945
- raise ValueError("Decision Tree Context is required")
946
- if not root_node:
947
- raise ValueError("Root node id is required")
948
- wrapped_sql = f"""
949
- SELECT adbpg_graphrag.append_decision_tree(%s::text, %s::text)
950
- """
951
- params = [context, root_node]
952
- return get_graphrag_tool_result(wrapped_sql, params)
953
-
954
- elif name == "adbpg_graphrag_delete_decision_tree":
955
- if GRAPHRAG_ENV_IS_READY == False:
956
- raise ValueError("GraphRAG Server initialization failed. This tool cannot be used.")
957
- root_node = arguments.get("root_node_entity")
958
- if not root_node:
959
- raise ValueError("Root node entity is required")
960
- wrapped_sql = f"""
961
- SELECT adbpg_graphrag.delete_decision_tree(%s::text, %s::text)
962
- """
963
- params = [root_node]
964
- return get_graphrag_tool_result(wrapped_sql, params)
965
-
966
- # adbpg_llm_memory tool
967
- elif name == "adbpg_llm_memory_add":
968
- # LLMEMORY 服务初始化失败,工具不可用
969
- if LLMEMORY_ENV_IS_READY == False:
970
- raise ValueError("LLMEMORY Server initialization failed. This tool cannot be used.")
971
-
972
- messages = arguments.get("messages")
973
- if not messages:
974
- raise ValueError("messages is required")
975
- messages_str = json.dumps(messages, ensure_ascii = False)
976
-
977
- user_id = arguments.get("user_id")
978
- if not user_id:
979
- user_id = None
980
- run_id = arguments.get("run_id")
981
- if not run_id:
982
- run_id = None
983
- agent_id = arguments.get("agent_id")
984
- if not agent_id:
985
- agent_id = None
986
- if user_id == None and run_id == None and agent_id == None:
987
- raise ValueError("At least one of user_id, run_id, or agent_id must be provided.")
988
-
989
- metadata = arguments.get("metadata")
990
- metadata_str = None
991
- if metadata:
992
- metadata_str = json.dumps(metadata, ensure_ascii = False)
993
-
994
- memory_type = arguments.get("memory_type")
995
- memory_prompt = arguments.get("prompt")
996
- if not memory_type:
997
- memory_type = None
998
- if not memory_prompt:
999
- memory_prompt = None
1000
-
1001
-
1002
- wrapped_sql = """
1003
- SELECT adbpg_llm_memory.add(
1004
- %s::json,
1005
- %s::text,
1006
- %s::text,
1007
- %s::text,
1008
- %s::json,
1009
- %s::text,
1010
- %s::text
1011
- )
1012
- """
1013
- params = [messages_str, user_id, run_id, agent_id, metadata_str, memory_type, memory_prompt]
1014
- return get_llm_memory_tool_result(wrapped_sql, params)
1015
-
1016
- elif name == "adbpg_llm_memory_get_all":
1017
- # LLMEMORY 服务初始化失败,工具不可用
1018
- if LLMEMORY_ENV_IS_READY == False:
1019
- raise ValueError("LLMEMORY Server initialization failed. This tool cannot be used.")
1020
-
1021
- user_id = arguments.get("user_id")
1022
- if not user_id:
1023
- user_id = None
1024
- run_id = arguments.get("run_id")
1025
- if not run_id:
1026
- run_id = None
1027
- agent_id = arguments.get("agent_id")
1028
- if not agent_id:
1029
- agent_id = None
1030
- if user_id == None and run_id == None and agent_id == None:
1031
- raise ValueError("At least one of user_id, run_id, or agent_id must be provided.")
1032
- wrapped_sql = f"""
1033
- SELECT adbpg_llm_memory.get_all(
1034
- %s::text,
1035
- %s::text,
1036
- %s::text
1037
- )
1038
- """
1039
- params = [user_id, run_id, agent_id]
1040
- return get_llm_memory_tool_result(wrapped_sql, params)
1041
-
1042
-
1043
- elif name == "adbpg_llm_memory_search":
1044
- # LLMEMORY 服务初始化失败,工具不可用
1045
- if LLMEMORY_ENV_IS_READY == False:
1046
- raise ValueError("LLMEMORY Server initialization failed. This tool cannot be used.")
1047
- query = arguments.get("query")
1048
- if not query:
1049
- raise ValueError("Query is required")
1050
-
1051
- user_id = arguments.get("user_id")
1052
- if not user_id:
1053
- user_id = None
1054
- run_id = arguments.get("run_id")
1055
- if not run_id:
1056
- run_id = None
1057
- agent_id = arguments.get("agent_id")
1058
- if not agent_id:
1059
- agent_id = None
1060
- if user_id == None and run_id == None and agent_id == None:
1061
- raise ValueError("At least one of user_id, run_id, or agent_id must be provided.")
1062
-
1063
- filter_json = arguments.get("filter")
1064
- filter_json_str = None
1065
- # json格式载入
1066
- if filter_json:
1067
- filter_json_str = json.dumps(filter_json, ensure_ascii = False)
1068
- # 拼接命令
1069
- wrapped_sql = f"""
1070
- SELECT adbpg_llm_memory.search(
1071
- %s::text,
1072
- %s::text,
1073
- %s::text,
1074
- %s::text,
1075
- %s::json
1076
- )
1077
- """
1078
- params = [query, user_id, run_id, agent_id, filter_json_str]
1079
- return get_llm_memory_tool_result(wrapped_sql, params)
1080
-
1081
- elif name == "adbpg_llm_memory_delete_all":
1082
- # LLMEMORY 服务初始化失败,工具不可用
1083
- if LLMEMORY_ENV_IS_READY == False:
1084
- raise ValueError("LLMEMORY Server initialization failed. This tool cannot be used.")
1085
-
1086
- user_id = arguments.get("user_id")
1087
- if not user_id:
1088
- user_id = None
1089
- run_id = arguments.get("run_id")
1090
- if not run_id:
1091
- run_id = None
1092
- agent_id = arguments.get("agent_id")
1093
- if not agent_id:
1094
- agent_id = None
1095
- if user_id == None and run_id == None and agent_id == None:
1096
- raise ValueError("At least one of user_id, run_id, or agent_id must be provided.")
1097
-
1098
- wrapped_sql = f"""
1099
- SELECT adbpg_llm_memory.delete_all(
1100
- %s::text,
1101
- %s::text,
1102
- %s::text
1103
- )
1104
- """
1105
- params = [user_id, run_id, agent_id]
1106
- return get_llm_memory_tool_result(wrapped_sql, params)
1107
-
1108
- else:
1109
- raise ValueError(f"Unknown tool: {name}")
1110
-
1111
- try:
1112
- with psycopg.connect(**config) as conn:
1113
- conn.autocommit = True
1114
- with conn.cursor() as cursor:
1115
-
1116
- cursor.execute(query)
1117
-
1118
- if name == "analyze_table":
1119
- return [TextContent(type="text", text=f"Successfully analyzed table {schema}.{table}")]
1120
-
1121
- if cursor.description:
1122
- # 将返回结果存储为json格式
1123
- json_result = cursor.fetchone()[0]
1124
- json_str = json.dumps(json_result, ensure_ascii = False, indent = 2)
1125
- result = [TextContent(type="text", text=json_str)]
1126
- try:
1127
- json.loads(result[0].text)
1128
- except json.JSONDecodeError as e:
1129
- raise Exception(f"JSON decode error: {e}\nRaw text: {result[0].text}") from e
1130
- return result
1131
-
1132
- else:
1133
- return [TextContent(type="text", text="Query executed successfully")]
1134
- except Exception as e:
1135
- return [TextContent(type="text", text=f"Error executing query: {str(e)}")]
1136
-
1137
- async def main():
1138
- """服务器主入口点"""
1139
- try:
1140
- config = get_db_config()
1141
- logger.info("Starting ADBPG MCP server...")
1142
-
1143
- # 测试数据库连接
1144
- try:
1145
- with psycopg.connect(**config) as conn:
1146
- logger.info("Successfully connected to database")
1147
- except Exception as e:
1148
- logger.error(f"Failed to connect to database: {e}")
1149
- sys.exit(1)
1150
- # 使用 stdio 传输
1151
- async with stdio_server() as (read_stream, write_stream):
1152
- try:
1153
- logger.info("Running MCP server with stdio transport...")
1154
- await app.run(
1155
- read_stream=read_stream,
1156
- write_stream=write_stream,
1157
- initialization_options=app.create_initialization_options()
1158
- )
1159
- except Exception as e:
1160
- logger.error(f"Error running server: {str(e)}")
1161
- raise
1162
- except Exception as e:
1163
- logger.error(f"Server initialization error: {str(e)}")
1164
- raise
1165
-
1166
- def run():
1167
- """同步运行入口点"""
1168
- try:
1169
- asyncio.run(main())
1170
- except Exception as e:
1171
- logger.error(f"Fatal error: {e}")
1172
- sys.exit(1)
1173
-
1174
- if __name__ == "__main__":
1175
- run()