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

Update langgraph/mcp_output/mcp_plugin/mcp_service.py

Browse files
langgraph/mcp_output/mcp_plugin/mcp_service.py CHANGED
@@ -1,192 +1,443 @@
 
 
 
 
 
 
 
 
 
 
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
 
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