guohanghui commited on
Commit
656c86a
·
verified ·
1 Parent(s): d98e8e2

Update langgraph/mcp_output/mcp_plugin/mcp_service.py

Browse files
langgraph/mcp_output/mcp_plugin/mcp_service.py CHANGED
@@ -1,443 +1,192 @@
1
- import os
2
- import sys
3
- from typing import Dict, Any, List, Optional, Union
4
- import json
5
-
6
- # Add the local source directory to sys.path
7
- source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
8
- if source_path not in sys.path:
9
- sys.path.insert(0, source_path)
10
-
11
  from fastmcp import FastMCP
12
 
13
- # Import LangGraph modules
14
- try:
15
- from langgraph.graph import StateGraph, Graph, END
16
- from langgraph.prebuilt import ToolExecutor, ToolInvocation
17
- from typing_extensions import TypedDict
18
- except ImportError:
19
- # Fallback for basic functionality
20
- StateGraph = None
21
- Graph = None
22
- END = "__end__"
23
-
24
- # Create the FastMCP service application
25
  mcp = FastMCP("langgraph_service")
26
 
27
- # Storage for graphs
28
- _graphs: Dict[str, Any] = {}
29
- _compiled_graphs: Dict[str, Any] = {}
30
-
31
-
32
- @mcp.tool(name="create_state_graph", description="Create a new StateGraph")
33
- def create_state_graph(
34
- graph_id: str,
35
- state_schema: Optional[Dict[str, str]] = None
36
- ) -> Dict[str, Any]:
37
  """
38
- Create a new StateGraph for building stateful workflows.
39
 
40
- Args:
41
- graph_id: Unique identifier for the graph
42
- state_schema: Optional schema defining state structure (dict of field_name: type_name)
43
-
44
- Returns:
45
- Dictionary with success status and graph info
46
  """
47
  try:
48
- if StateGraph is None:
49
- return {"success": False, "result": None, "error": "StateGraph not available"}
50
-
51
- # Create a simple state type
52
- if state_schema:
53
- # Create a TypedDict-like state class dynamically
54
- state_fields = {k: str for k, v in state_schema.items()}
55
-
56
- class GraphState(TypedDict):
57
- pass
58
-
59
- # Add fields
60
- for field, ftype in state_fields.items():
61
- GraphState.__annotations__[field] = Any
62
-
63
- graph = StateGraph(GraphState)
64
- else:
65
- # Default state with messages
66
- class DefaultState(TypedDict):
67
- messages: List[str]
68
-
69
- graph = StateGraph(DefaultState)
70
-
71
- _graphs[graph_id] = {
72
- "graph": graph,
73
- "nodes": [],
74
- "edges": [],
75
- "compiled": False
76
- }
77
-
78
  return {
79
  "success": True,
80
- "result": {
81
- "graph_id": graph_id,
82
- "state_schema": state_schema or {"messages": "List[str]"}
83
- },
84
- "error": None
85
  }
86
  except Exception as e:
87
- return {"success": False, "result": None, "error": str(e)}
88
-
89
 
90
- @mcp.tool(name="add_node", description="Add a node to a graph")
91
- def add_node(
92
- graph_id: str,
93
- node_name: str,
94
- node_type: str = "function"
95
- ) -> Dict[str, Any]:
96
  """
97
- Add a node to a StateGraph.
98
 
99
- Args:
100
- graph_id: ID of the graph
101
- node_name: Name of the node
102
- node_type: Type of node (function, tool, etc.)
103
 
104
- Returns:
105
- Dictionary with success status
106
  """
107
  try:
108
- if graph_id not in _graphs:
109
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
110
-
111
- graph_data = _graphs[graph_id]
112
-
113
- if graph_data["compiled"]:
114
- return {"success": False, "result": None, "error": "Cannot modify compiled graph"}
115
-
116
- # Create a simple node function
117
- def node_function(state):
118
- # Default: pass through state with a message
119
- if "messages" in state:
120
- messages = state.get("messages", [])
121
- messages.append(f"Processed by {node_name}")
122
- return {"messages": messages}
123
- return state
124
-
125
- graph_data["graph"].add_node(node_name, node_function)
126
- graph_data["nodes"].append(node_name)
127
-
128
  return {
129
  "success": True,
130
- "result": {
131
- "graph_id": graph_id,
132
- "node_name": node_name,
133
- "node_type": node_type
134
- },
135
- "error": None
136
  }
137
  except Exception as e:
138
- return {"success": False, "result": None, "error": str(e)}
139
-
140
 
141
- @mcp.tool(name="add_edge", description="Add an edge between two nodes")
142
- def add_edge(
143
- graph_id: str,
144
- from_node: str,
145
- to_node: str
146
- ) -> Dict[str, Any]:
147
  """
148
- Add an edge connecting two nodes in a graph.
149
 
150
- Args:
151
- graph_id: ID of the graph
152
- from_node: Source node name
153
- to_node: Target node name (use "__end__" for terminal node)
154
 
155
- Returns:
156
- Dictionary with success status
157
  """
158
  try:
159
- if graph_id not in _graphs:
160
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
161
-
162
- graph_data = _graphs[graph_id]
163
-
164
- if graph_data["compiled"]:
165
- return {"success": False, "result": None, "error": "Cannot modify compiled graph"}
166
-
167
- graph_data["graph"].add_edge(from_node, to_node)
168
- graph_data["edges"].append({"from": from_node, "to": to_node})
169
-
170
- return {
171
- "success": True,
172
- "result": {
173
- "graph_id": graph_id,
174
- "from_node": from_node,
175
- "to_node": to_node
176
- },
177
- "error": None
178
- }
179
- except Exception as e:
180
- return {"success": False, "result": None, "error": str(e)}
181
 
 
 
182
 
183
- @mcp.tool(name="add_conditional_edge", description="Add a conditional edge with routing logic")
184
- def add_conditional_edge(
185
- graph_id: str,
186
- from_node: str,
187
- condition_type: str = "simple"
188
- ) -> Dict[str, Any]:
189
- """
190
- Add a conditional edge that routes based on state.
191
 
192
- Args:
193
- graph_id: ID of the graph
194
- from_node: Source node name
195
- condition_type: Type of condition (simple, multi, etc.)
196
 
197
- Returns:
198
- Dictionary with success status
199
- """
200
- try:
201
- if graph_id not in _graphs:
202
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
203
-
204
- graph_data = _graphs[graph_id]
205
-
206
- if graph_data["compiled"]:
207
- return {"success": False, "result": None, "error": "Cannot modify compiled graph"}
208
-
209
- # Simple routing function
210
- def route_function(state):
211
- # Default routing based on message count
212
- messages = state.get("messages", [])
213
- if len(messages) > 3:
214
- return END
215
- else:
216
- # Route to first available node or END
217
- nodes = graph_data["nodes"]
218
- return nodes[0] if nodes else END
219
-
220
- graph_data["graph"].add_conditional_edges(
221
- from_node,
222
- route_function
223
- )
224
-
225
- return {
226
- "success": True,
227
- "result": {
228
- "graph_id": graph_id,
229
- "from_node": from_node,
230
- "condition_type": condition_type
231
- },
232
- "error": None
233
- }
234
- except Exception as e:
235
- return {"success": False, "result": None, "error": str(e)}
236
 
 
 
 
237
 
238
- @mcp.tool(name="set_entry_point", description="Set the entry point of the graph")
239
- def set_entry_point(
240
- graph_id: str,
241
- node_name: str
242
- ) -> Dict[str, Any]:
243
  """
244
- Set the entry point (starting node) for a graph.
245
 
246
- Args:
247
- graph_id: ID of the graph
248
- node_name: Name of the entry node
249
 
250
- Returns:
251
- Dictionary with success status
252
  """
253
  try:
254
- if graph_id not in _graphs:
255
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
256
-
257
- graph_data = _graphs[graph_id]
258
-
259
- if graph_data["compiled"]:
260
- return {"success": False, "result": None, "error": "Cannot modify compiled graph"}
261
-
262
- graph_data["graph"].set_entry_point(node_name)
263
-
264
- return {
265
- "success": True,
266
- "result": {
267
- "graph_id": graph_id,
268
- "entry_point": node_name
269
- },
270
- "error": None
271
- }
272
- except Exception as e:
273
- return {"success": False, "result": None, "error": str(e)}
274
 
 
 
275
 
276
- @mcp.tool(name="compile_graph", description="Compile a graph for execution")
277
- def compile_graph(
278
- graph_id: str
279
- ) -> Dict[str, Any]:
280
- """
281
- Compile a StateGraph into an executable workflow.
282
 
283
- Args:
284
- graph_id: ID of the graph to compile
 
285
 
286
- Returns:
287
- Dictionary with success status and compiled graph info
288
- """
289
- try:
290
- if graph_id not in _graphs:
291
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
292
-
293
- graph_data = _graphs[graph_id]
294
-
295
- if graph_data["compiled"]:
296
- return {"success": False, "result": None, "error": "Graph already compiled"}
297
-
298
- compiled = graph_data["graph"].compile()
299
- _compiled_graphs[graph_id] = compiled
300
- graph_data["compiled"] = True
301
-
302
- return {
303
- "success": True,
304
- "result": {
305
- "graph_id": graph_id,
306
- "num_nodes": len(graph_data["nodes"]),
307
- "num_edges": len(graph_data["edges"]),
308
- "compiled": True
309
- },
310
- "error": None
311
- }
312
  except Exception as e:
313
- return {"success": False, "result": None, "error": str(e)}
314
-
315
 
316
- @mcp.tool(name="invoke_graph", description="Execute a compiled graph with input")
317
- def invoke_graph(
318
- graph_id: str,
319
- input_data: Dict[str, Any]
320
- ) -> Dict[str, Any]:
321
  """
322
- Execute a compiled graph with given input.
323
 
324
- Args:
325
- graph_id: ID of the compiled graph
326
- input_data: Input state/data for the graph
327
 
328
- Returns:
329
- Dictionary with execution result
330
  """
331
  try:
332
- if graph_id not in _compiled_graphs:
333
- return {"success": False, "result": None, "error": f"Compiled graph '{graph_id}' not found"}
334
-
335
- compiled_graph = _compiled_graphs[graph_id]
336
- result = compiled_graph.invoke(input_data)
337
-
338
- return {
339
- "success": True,
340
- "result": {
341
- "graph_id": graph_id,
342
- "output": result
343
- },
344
- "error": None
345
- }
346
- except Exception as e:
347
- return {"success": False, "result": None, "error": str(e)}
348
 
 
 
 
 
 
349
 
350
- @mcp.tool(name="get_graph_info", description="Get information about a graph")
351
- def get_graph_info(graph_id: str) -> Dict[str, Any]:
352
  """
353
- Get detailed information about a graph.
354
 
355
- Args:
356
- graph_id: ID of the graph
357
 
358
- Returns:
359
- Dictionary with graph details
360
  """
361
  try:
362
- if graph_id not in _graphs:
363
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
364
-
365
- graph_data = _graphs[graph_id]
366
-
367
- return {
368
- "success": True,
369
- "result": {
370
- "graph_id": graph_id,
371
- "nodes": graph_data["nodes"],
372
- "edges": graph_data["edges"],
373
- "compiled": graph_data["compiled"]
374
- },
375
- "error": None
376
- }
377
- except Exception as e:
378
- return {"success": False, "result": None, "error": str(e)}
379
-
380
 
381
- @mcp.tool(name="list_graphs", description="List all stored graphs")
382
- def list_graphs() -> Dict[str, Any]:
383
- """
384
- List all stored graphs.
385
-
386
- Returns:
387
- Dictionary with list of graph IDs
388
- """
389
- try:
390
- graphs_info = []
391
- for gid, gdata in _graphs.items():
392
- graphs_info.append({
393
- "graph_id": gid,
394
- "num_nodes": len(gdata["nodes"]),
395
- "num_edges": len(gdata["edges"]),
396
- "compiled": gdata["compiled"]
397
- })
398
-
399
- return {
400
- "success": True,
401
- "result": {"graphs": graphs_info},
402
- "error": None
403
- }
404
  except Exception as e:
405
- return {"success": False, "result": None, "error": str(e)}
406
 
407
-
408
- @mcp.tool(name="delete_graph", description="Delete a graph")
409
- def delete_graph(graph_id: str) -> Dict[str, Any]:
410
  """
411
- Delete a stored graph.
412
 
413
- Args:
414
- graph_id: ID of the graph to delete
415
 
416
- Returns:
417
- Dictionary with success status
418
  """
419
  try:
420
- if graph_id not in _graphs:
421
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
422
-
423
- del _graphs[graph_id]
424
- if graph_id in _compiled_graphs:
425
- del _compiled_graphs[graph_id]
426
-
427
- return {
428
- "success": True,
429
- "result": {"deleted": graph_id},
430
- "error": None
431
- }
432
- except Exception as e:
433
- return {"success": False, "result": None, "error": str(e)}
434
 
 
 
 
 
435
 
436
  def create_app() -> FastMCP:
437
  """
438
- Create and return the FastMCP application instance.
439
 
440
- Returns:
441
- FastMCP: The FastMCP application instance.
442
  """
443
  return mcp
 
 
 
 
 
 
 
 
 
 
 
1
  from fastmcp import FastMCP
2
 
3
+ # 创建 FastMCP 服务应用
 
 
 
 
 
 
 
 
 
 
 
4
  mcp = FastMCP("langgraph_service")
5
 
6
+ @mcp.tool(name="list_available_features", description="列出所有可用的 LangGraph 核心功能")
7
+ def list_available_features() -> dict:
 
 
 
 
 
 
 
 
8
  """
9
+ 列出 LangGraph 的所有核心功能。
10
 
11
+ 返回:
12
+ - dict: 包含 success 状态和功能列表。
 
 
 
 
13
  """
14
  try:
15
+ features = [
16
+ "list_available_features",
17
+ "get_feature_info",
18
+ "execute_workflow",
19
+ "create_state_graph",
20
+ "validate_graph",
21
+ "draw_graph",
22
+ "deploy_agent"
23
+ ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  return {
25
  "success": True,
26
+ "features": features,
27
+ "count": len(features)
 
 
 
28
  }
29
  except Exception as e:
30
+ return {"success": False, "error": str(e)}
 
31
 
32
+ @mcp.tool(name="get_feature_info", description="获取特定功能的详细信息")
33
+ def get_feature_info(feature_name: str) -> dict:
 
 
 
 
34
  """
35
+ 获取 LangGraph 中某个功能的详细信息。
36
 
37
+ 参数:
38
+ - feature_name: 功能名称。
 
 
39
 
40
+ 返回:
41
+ - dict: 包含功能的详细信息。
42
  """
43
  try:
44
+ feature_info = {
45
+ "list_available_features": "列出所有可用的 LangGraph 核心功能。",
46
+ "get_feature_info": "获取特定功能的详细信息。",
47
+ "execute_workflow": "执行 LangGraph 的工作流。",
48
+ "create_state_graph": "创建一个状态图。",
49
+ "validate_graph": "验证图的正确性。",
50
+ "draw_graph": "可视化图结构。",
51
+ "deploy_agent": "部署 LangGraph 的代理。"
52
+ }
53
+ if feature_name not in feature_info:
54
+ return {"success": False, "error": f"功能 {feature_name} 不存在。"}
 
 
 
 
 
 
 
 
 
55
  return {
56
  "success": True,
57
+ "feature_name": feature_name,
58
+ "description": feature_info[feature_name]
 
 
 
 
59
  }
60
  except Exception as e:
61
+ return {"success": False, "error": str(e)}
 
62
 
63
+ @mcp.tool(name="execute_workflow", description="执行 LangGraph 的工作流")
64
+ def execute_workflow(workflow_definition: dict) -> dict:
 
 
 
 
65
  """
66
+ 执行 LangGraph 的工作流。
67
 
68
+ 参数:
69
+ - workflow_definition: 工作流的定义,包括节点和边的信息。
 
 
70
 
71
+ 返回:
72
+ - dict: 包含执行结果或错误信息。
73
  """
74
  try:
75
+ from langgraph.graph import StateGraph
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
+ # 创建状态图
78
+ graph = StateGraph(workflow_definition.get("state", {}))
79
 
80
+ # 添加节点
81
+ for node_name, node_func in workflow_definition.get("nodes", {}).items():
82
+ graph.add_node(node_name, node_func)
 
 
 
 
 
83
 
84
+ # 添加边
85
+ for edge in workflow_definition.get("edges", []):
86
+ graph.add_edge(edge[0], edge[1])
 
87
 
88
+ # 编译并执行工作流
89
+ compiled_graph = graph.compile()
90
+ result = compiled_graph.invoke(workflow_definition.get("input", {}))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
+ return {"success": True, "result": result}
93
+ except Exception as e:
94
+ return {"success": False, "error": str(e)}
95
 
96
+ @mcp.tool(name="create_state_graph", description="创建一个状态图")
97
+ def create_state_graph(graph_definition: dict) -> dict:
 
 
 
98
  """
99
+ 创建一个状态图。
100
 
101
+ 参数:
102
+ - graph_definition: 图的定义,包括节点和边的信息。
 
103
 
104
+ 返回:
105
+ - dict: 包含状态图的定义或错误信息。
106
  """
107
  try:
108
+ from langgraph.graph import StateGraph
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
 
110
+ # 创建状态图
111
+ graph = StateGraph(graph_definition.get("state", {}))
112
 
113
+ # 添加节点
114
+ for node_name, node_func in graph_definition.get("nodes", {}).items():
115
+ graph.add_node(node_name, node_func)
 
 
 
116
 
117
+ # 添加边
118
+ for edge in graph_definition.get("edges", []):
119
+ graph.add_edge(edge[0], edge[1])
120
 
121
+ return {"success": True, "graph": graph}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  except Exception as e:
123
+ return {"success": False, "error": str(e)}
 
124
 
125
+ @mcp.tool(name="validate_graph", description="验证图的正确性")
126
+ def validate_graph(graph: object) -> dict:
 
 
 
127
  """
128
+ 验证图的正确性。
129
 
130
+ 参数:
131
+ - graph: 要验证的图对象。
 
132
 
133
+ 返回:
134
+ - dict: 包含验证结果或错误信息。
135
  """
136
  try:
137
+ from langgraph.pregel._validate import validate_graph
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
+ # 验证图
140
+ validate_graph(graph)
141
+ return {"success": True, "message": "图验证通过"}
142
+ except Exception as e:
143
+ return {"success": False, "error": str(e)}
144
 
145
+ @mcp.tool(name="draw_graph", description="可视化图结构")
146
+ def draw_graph(graph: object) -> dict:
147
  """
148
+ 可视化图结构。
149
 
150
+ 参数:
151
+ - graph: 要可视化的图对象。
152
 
153
+ 返回:
154
+ - dict: 包含可视化结果或错误信息。
155
  """
156
  try:
157
+ from langgraph.pregel._draw import draw_graph
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
 
159
+ # 绘制图
160
+ draw_graph(graph)
161
+ return {"success": True, "message": "图已绘制"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  except Exception as e:
163
+ return {"success": False, "error": str(e)}
164
 
165
+ @mcp.tool(name="deploy_agent", description="部署 LangGraph 的代理")
166
+ def deploy_agent(agent_config: dict) -> dict:
 
167
  """
168
+ 部署 LangGraph 的代理。
169
 
170
+ 参数:
171
+ - agent_config: 代理的配置,包括名称和参数。
172
 
173
+ 返回:
174
+ - dict: 包含部署结果或错误信息。
175
  """
176
  try:
177
+ # 示例:假设有一个部署代理的函数
178
+ from langgraph.agent import deploy_agent
 
 
 
 
 
 
 
 
 
 
 
 
179
 
180
+ deploy_agent(agent_config)
181
+ return {"success": True, "message": "代理已部署"}
182
+ except Exception as e:
183
+ return {"success": False, "error": str(e)}
184
 
185
  def create_app() -> FastMCP:
186
  """
187
+ 创建并返回 FastMCP 应用实例。
188
 
189
+ 返回:
190
+ - FastMCP: FastMCP 应用实例。
191
  """
192
  return mcp