@og-mcp/reactflow-mcp 1.0.4 → 1.0.6
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.
- package/LICENSE +21 -21
- package/README.md +74 -74
- package/dist/data/api-types.js +69 -69
- package/dist/data/components.js +217 -217
- package/dist/data/hooks.js +106 -106
- package/dist/data/migration.js +44 -44
- package/dist/data/patterns.js +582 -582
- package/dist/data/templates.js +126 -126
- package/dist/data/utilities.js +29 -29
- package/dist/index.js +0 -0
- package/dist/tools/cheatsheet.js +84 -84
- package/dist/tools/generate-flow.js +89 -89
- package/package.json +54 -54
|
@@ -41,22 +41,22 @@ function register(server) {
|
|
|
41
41
|
if (desc.includes("custom node") || desc.includes("custom-node")) {
|
|
42
42
|
imports.add("Handle");
|
|
43
43
|
imports.add("Position");
|
|
44
|
-
additionalComponents += `
|
|
45
|
-
type CustomNodeData = { label: string };
|
|
46
|
-
type CustomNodeType = Node<CustomNodeData, 'custom'>;
|
|
47
|
-
|
|
48
|
-
const CustomNode = memo(({ data, selected }: NodeProps<CustomNodeType>) => (
|
|
49
|
-
<>
|
|
50
|
-
<Handle type="target" position={Position.Left} />
|
|
51
|
-
<div className={\`px-4 py-2 rounded border shadow-sm bg-white \${selected ? 'border-blue-500' : 'border-gray-200'}\`}>
|
|
52
|
-
{data.label}
|
|
53
|
-
</div>
|
|
54
|
-
<Handle type="source" position={Position.Right} />
|
|
55
|
-
</>
|
|
56
|
-
));
|
|
57
|
-
CustomNode.displayName = 'CustomNode';
|
|
58
|
-
|
|
59
|
-
const nodeTypes = { custom: CustomNode };
|
|
44
|
+
additionalComponents += `
|
|
45
|
+
type CustomNodeData = { label: string };
|
|
46
|
+
type CustomNodeType = Node<CustomNodeData, 'custom'>;
|
|
47
|
+
|
|
48
|
+
const CustomNode = memo(({ data, selected }: NodeProps<CustomNodeType>) => (
|
|
49
|
+
<>
|
|
50
|
+
<Handle type="target" position={Position.Left} />
|
|
51
|
+
<div className={\`px-4 py-2 rounded border shadow-sm bg-white \${selected ? 'border-blue-500' : 'border-gray-200'}\`}>
|
|
52
|
+
{data.label}
|
|
53
|
+
</div>
|
|
54
|
+
<Handle type="source" position={Position.Right} />
|
|
55
|
+
</>
|
|
56
|
+
));
|
|
57
|
+
CustomNode.displayName = 'CustomNode';
|
|
58
|
+
|
|
59
|
+
const nodeTypes = { custom: CustomNode };
|
|
60
60
|
`;
|
|
61
61
|
extraImports.push("import { memo } from 'react';");
|
|
62
62
|
xyflowImports.add("type NodeProps");
|
|
@@ -66,21 +66,21 @@ const nodeTypes = { custom: CustomNode };
|
|
|
66
66
|
// Drag and drop
|
|
67
67
|
if (desc.includes("drag") && desc.includes("drop") || desc.includes("sidebar")) {
|
|
68
68
|
xyflowImports.add("useReactFlow");
|
|
69
|
-
beforeReturn += `
|
|
70
|
-
const { screenToFlowPosition, addNodes } = useReactFlow();
|
|
71
|
-
|
|
72
|
-
const onDragOver = useCallback((event: React.DragEvent) => {
|
|
73
|
-
event.preventDefault();
|
|
74
|
-
event.dataTransfer.dropEffect = 'move';
|
|
75
|
-
}, []);
|
|
76
|
-
|
|
77
|
-
const onDrop = useCallback((event: React.DragEvent) => {
|
|
78
|
-
event.preventDefault();
|
|
79
|
-
const type = event.dataTransfer.getData('application/reactflow');
|
|
80
|
-
if (!type) return;
|
|
81
|
-
const position = screenToFlowPosition({ x: event.clientX, y: event.clientY });
|
|
82
|
-
addNodes({ id: crypto.randomUUID(), type, position, data: { label: 'New Node' } });
|
|
83
|
-
}, [screenToFlowPosition, addNodes]);
|
|
69
|
+
beforeReturn += `
|
|
70
|
+
const { screenToFlowPosition, addNodes } = useReactFlow();
|
|
71
|
+
|
|
72
|
+
const onDragOver = useCallback((event: React.DragEvent) => {
|
|
73
|
+
event.preventDefault();
|
|
74
|
+
event.dataTransfer.dropEffect = 'move';
|
|
75
|
+
}, []);
|
|
76
|
+
|
|
77
|
+
const onDrop = useCallback((event: React.DragEvent) => {
|
|
78
|
+
event.preventDefault();
|
|
79
|
+
const type = event.dataTransfer.getData('application/reactflow');
|
|
80
|
+
if (!type) return;
|
|
81
|
+
const position = screenToFlowPosition({ x: event.clientX, y: event.clientY });
|
|
82
|
+
addNodes({ id: crypto.randomUUID(), type, position, data: { label: 'New Node' } });
|
|
83
|
+
}, [screenToFlowPosition, addNodes]);
|
|
84
84
|
`;
|
|
85
85
|
extraImports.push("import { useCallback } from 'react';");
|
|
86
86
|
flowProps.push("onDragOver={onDragOver}", "onDrop={onDrop}");
|
|
@@ -93,27 +93,27 @@ const nodeTypes = { custom: CustomNode };
|
|
|
93
93
|
if (desc.includes("dag") || desc.includes("cycle") || desc.includes("pipeline")) {
|
|
94
94
|
xyflowImports.add("getOutgoers");
|
|
95
95
|
xyflowImports.add("useReactFlow");
|
|
96
|
-
beforeReturn += `
|
|
97
|
-
const { getNodes, getEdges } = useReactFlow();
|
|
98
|
-
|
|
99
|
-
const isValidConnection = useCallback((connection: Connection) => {
|
|
100
|
-
const allNodes = getNodes();
|
|
101
|
-
const allEdges = getEdges();
|
|
102
|
-
const target = allNodes.find((n) => n.id === connection.target);
|
|
103
|
-
const source = allNodes.find((n) => n.id === connection.source);
|
|
104
|
-
if (!target || !source) return false;
|
|
105
|
-
// Prevent cycles
|
|
106
|
-
const hasCycle = (node: Node, visited = new Set<string>()): boolean => {
|
|
107
|
-
if (visited.has(node.id)) return false;
|
|
108
|
-
visited.add(node.id);
|
|
109
|
-
if (node.id === source.id) return true;
|
|
110
|
-
for (const outgoer of getOutgoers(node, allNodes, allEdges)) {
|
|
111
|
-
if (hasCycle(outgoer, visited)) return true;
|
|
112
|
-
}
|
|
113
|
-
return false;
|
|
114
|
-
};
|
|
115
|
-
return !hasCycle(target);
|
|
116
|
-
}, [getNodes, getEdges]);
|
|
96
|
+
beforeReturn += `
|
|
97
|
+
const { getNodes, getEdges } = useReactFlow();
|
|
98
|
+
|
|
99
|
+
const isValidConnection = useCallback((connection: Connection) => {
|
|
100
|
+
const allNodes = getNodes();
|
|
101
|
+
const allEdges = getEdges();
|
|
102
|
+
const target = allNodes.find((n) => n.id === connection.target);
|
|
103
|
+
const source = allNodes.find((n) => n.id === connection.source);
|
|
104
|
+
if (!target || !source) return false;
|
|
105
|
+
// Prevent cycles
|
|
106
|
+
const hasCycle = (node: Node, visited = new Set<string>()): boolean => {
|
|
107
|
+
if (visited.has(node.id)) return false;
|
|
108
|
+
visited.add(node.id);
|
|
109
|
+
if (node.id === source.id) return true;
|
|
110
|
+
for (const outgoer of getOutgoers(node, allNodes, allEdges)) {
|
|
111
|
+
if (hasCycle(outgoer, visited)) return true;
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
};
|
|
115
|
+
return !hasCycle(target);
|
|
116
|
+
}, [getNodes, getEdges]);
|
|
117
117
|
`;
|
|
118
118
|
xyflowImports.add("type Connection");
|
|
119
119
|
xyflowImports.add("type Node");
|
|
@@ -123,35 +123,35 @@ const nodeTypes = { custom: CustomNode };
|
|
|
123
123
|
// Store vs useState
|
|
124
124
|
if (useStore) {
|
|
125
125
|
flowProps.push("onNodesChange={onNodesChange}", "onEdgesChange={onEdgesChange}", "onConnect={onConnect}");
|
|
126
|
-
storeCode = `
|
|
127
|
-
// --- store.ts ---
|
|
128
|
-
import { create } from 'zustand';
|
|
129
|
-
import { type Node, type Edge, type OnNodesChange, type OnEdgesChange, type OnConnect, applyNodeChanges, applyEdgeChanges, addEdge } from '@xyflow/react';
|
|
130
|
-
|
|
131
|
-
const initialNodes: Node[] = [
|
|
132
|
-
{ id: '1', position: { x: 0, y: 0 }, data: { label: 'Node 1' } },
|
|
133
|
-
{ id: '2', position: { x: 250, y: 100 }, data: { label: 'Node 2' } },
|
|
134
|
-
];
|
|
135
|
-
|
|
136
|
-
const initialEdges: Edge[] = [
|
|
137
|
-
{ id: 'e1-2', source: '1', target: '2' },
|
|
138
|
-
];
|
|
139
|
-
|
|
140
|
-
type FlowState = {
|
|
141
|
-
nodes: Node[]; edges: Edge[];
|
|
142
|
-
onNodesChange: OnNodesChange; onEdgesChange: OnEdgesChange; onConnect: OnConnect;
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
const useFlowStore = create<FlowState>((set, get) => ({
|
|
146
|
-
nodes: initialNodes,
|
|
147
|
-
edges: initialEdges,
|
|
148
|
-
onNodesChange: (changes) => set({ nodes: applyNodeChanges(changes, get().nodes) }),
|
|
149
|
-
onEdgesChange: (changes) => set({ edges: applyEdgeChanges(changes, get().edges) }),
|
|
150
|
-
onConnect: (connection) => set({ edges: addEdge(connection, get().edges) }),
|
|
151
|
-
}));
|
|
152
|
-
|
|
153
|
-
const selector = (s: FlowState) => s;
|
|
154
|
-
export { useFlowStore, selector };
|
|
126
|
+
storeCode = `
|
|
127
|
+
// --- store.ts ---
|
|
128
|
+
import { create } from 'zustand';
|
|
129
|
+
import { type Node, type Edge, type OnNodesChange, type OnEdgesChange, type OnConnect, applyNodeChanges, applyEdgeChanges, addEdge } from '@xyflow/react';
|
|
130
|
+
|
|
131
|
+
const initialNodes: Node[] = [
|
|
132
|
+
{ id: '1', position: { x: 0, y: 0 }, data: { label: 'Node 1' } },
|
|
133
|
+
{ id: '2', position: { x: 250, y: 100 }, data: { label: 'Node 2' } },
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
const initialEdges: Edge[] = [
|
|
137
|
+
{ id: 'e1-2', source: '1', target: '2' },
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
type FlowState = {
|
|
141
|
+
nodes: Node[]; edges: Edge[];
|
|
142
|
+
onNodesChange: OnNodesChange; onEdgesChange: OnEdgesChange; onConnect: OnConnect;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const useFlowStore = create<FlowState>((set, get) => ({
|
|
146
|
+
nodes: initialNodes,
|
|
147
|
+
edges: initialEdges,
|
|
148
|
+
onNodesChange: (changes) => set({ nodes: applyNodeChanges(changes, get().nodes) }),
|
|
149
|
+
onEdgesChange: (changes) => set({ edges: applyEdgeChanges(changes, get().edges) }),
|
|
150
|
+
onConnect: (connection) => set({ edges: addEdge(connection, get().edges) }),
|
|
151
|
+
}));
|
|
152
|
+
|
|
153
|
+
const selector = (s: FlowState) => s;
|
|
154
|
+
export { useFlowStore, selector };
|
|
155
155
|
`;
|
|
156
156
|
beforeReturn =
|
|
157
157
|
` const { nodes, edges, onNodesChange, onEdgesChange, onConnect } = useFlowStore(selector);\n` +
|
|
@@ -164,14 +164,14 @@ export { useFlowStore, selector };
|
|
|
164
164
|
imports.add("addEdge");
|
|
165
165
|
extraImports.push("import { useCallback } from 'react';");
|
|
166
166
|
beforeReturn =
|
|
167
|
-
` const [nodes, setNodes, onNodesChange] = useNodesState([
|
|
168
|
-
{ id: '1', position: { x: 0, y: 0 }, data: { label: 'Node 1' } },
|
|
169
|
-
{ id: '2', position: { x: 250, y: 100 }, data: { label: 'Node 2' } },
|
|
170
|
-
]);
|
|
171
|
-
const [edges, setEdges, onEdgesChange] = useEdgesState([
|
|
172
|
-
{ id: 'e1-2', source: '1', target: '2' },
|
|
173
|
-
]);
|
|
174
|
-
const onConnect = useCallback((conn) => setEdges((eds) => addEdge(conn, eds)), [setEdges]);
|
|
167
|
+
` const [nodes, setNodes, onNodesChange] = useNodesState([
|
|
168
|
+
{ id: '1', position: { x: 0, y: 0 }, data: { label: 'Node 1' } },
|
|
169
|
+
{ id: '2', position: { x: 250, y: 100 }, data: { label: 'Node 2' } },
|
|
170
|
+
]);
|
|
171
|
+
const [edges, setEdges, onEdgesChange] = useEdgesState([
|
|
172
|
+
{ id: 'e1-2', source: '1', target: '2' },
|
|
173
|
+
]);
|
|
174
|
+
const onConnect = useCallback((conn) => setEdges((eds) => addEdge(conn, eds)), [setEdges]);
|
|
175
175
|
` + beforeReturn;
|
|
176
176
|
flowProps.push("onNodesChange={onNodesChange}", "onEdgesChange={onEdgesChange}", "onConnect={onConnect}");
|
|
177
177
|
}
|
package/package.json
CHANGED
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@og-mcp/reactflow-mcp",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "MCP server that gives AI assistants accurate React Flow (@xyflow/react) v12 documentation, API references, patterns, and code generation",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"reactflow-mcp": "dist/index.js"
|
|
8
|
-
},
|
|
9
|
-
"scripts": {
|
|
10
|
-
"build": "tsc",
|
|
11
|
-
"start": "node dist/index.js",
|
|
12
|
-
"dev": "tsc --watch"
|
|
13
|
-
},
|
|
14
|
-
"keywords": [
|
|
15
|
-
"mcp",
|
|
16
|
-
"model-context-protocol",
|
|
17
|
-
"reactflow",
|
|
18
|
-
"xyflow",
|
|
19
|
-
"react-flow",
|
|
20
|
-
"react",
|
|
21
|
-
"node-based-ui",
|
|
22
|
-
"graph",
|
|
23
|
-
"flow",
|
|
24
|
-
"ai",
|
|
25
|
-
"claude",
|
|
26
|
-
"cursor",
|
|
27
|
-
"windsurf"
|
|
28
|
-
],
|
|
29
|
-
"repository": {
|
|
30
|
-
"type": "git",
|
|
31
|
-
"url": "https://github.com/codingdud/reactflow-mcp-server.git"
|
|
32
|
-
},
|
|
33
|
-
"homepage": "https://github.com/codingdud/reactflow-mcp-server#readme",
|
|
34
|
-
"bugs": {
|
|
35
|
-
"url": "https://github.com/codingdud/reactflow-mcp-server/issues"
|
|
36
|
-
},
|
|
37
|
-
"author": "Orkait",
|
|
38
|
-
"license": "MIT",
|
|
39
|
-
"engines": {
|
|
40
|
-
"node": ">=18"
|
|
41
|
-
},
|
|
42
|
-
"files": [
|
|
43
|
-
"dist",
|
|
44
|
-
"LICENSE",
|
|
45
|
-
"README.md"
|
|
46
|
-
],
|
|
47
|
-
"dependencies": {
|
|
48
|
-
"@modelcontextprotocol/sdk": "^1.17.0"
|
|
49
|
-
},
|
|
50
|
-
"devDependencies": {
|
|
51
|
-
"@types/node": "^20.0.0",
|
|
52
|
-
"typescript": "^5.5.0"
|
|
53
|
-
}
|
|
54
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@og-mcp/reactflow-mcp",
|
|
3
|
+
"version": "1.0.6",
|
|
4
|
+
"description": "MCP server that gives AI assistants accurate React Flow (@xyflow/react) v12 documentation, API references, patterns, and code generation",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"reactflow-mcp": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"start": "node dist/index.js",
|
|
12
|
+
"dev": "tsc --watch"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"mcp",
|
|
16
|
+
"model-context-protocol",
|
|
17
|
+
"reactflow",
|
|
18
|
+
"xyflow",
|
|
19
|
+
"react-flow",
|
|
20
|
+
"react",
|
|
21
|
+
"node-based-ui",
|
|
22
|
+
"graph",
|
|
23
|
+
"flow",
|
|
24
|
+
"ai",
|
|
25
|
+
"claude",
|
|
26
|
+
"cursor",
|
|
27
|
+
"windsurf"
|
|
28
|
+
],
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/codingdud/reactflow-mcp-server.git"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/codingdud/reactflow-mcp-server#readme",
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/codingdud/reactflow-mcp-server/issues"
|
|
36
|
+
},
|
|
37
|
+
"author": "Orkait",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"LICENSE",
|
|
45
|
+
"README.md"
|
|
46
|
+
],
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@modelcontextprotocol/sdk": "^1.17.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^20.0.0",
|
|
52
|
+
"typescript": "^5.5.0"
|
|
53
|
+
}
|
|
54
|
+
}
|