rsbuild-plugin-vue-mcp 0.1.0

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/dist/551.js ADDED
@@ -0,0 +1,287 @@
1
+ import { createHooks } from "hookable";
2
+ import { createBirpcGroup } from "birpc";
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { nanoid } from "nanoid";
5
+ import { z } from "zod";
6
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
7
+ function createVueMcpContext() {
8
+ return {
9
+ hooks: createHooks(),
10
+ rpc: null,
11
+ rpcServer: null
12
+ };
13
+ }
14
+ function createServerRpcFunc(ctx) {
15
+ return {
16
+ getInspectorTree: (_)=>({}),
17
+ onInspectorTreeUpdated: (event, data)=>{
18
+ ctx.hooks.callHook(event, data).finally();
19
+ },
20
+ getInspectorState: (_)=>({}),
21
+ onInspectorStateUpdated: (event, data)=>{
22
+ ctx.hooks.callHook(event, data).finally();
23
+ },
24
+ editComponentState (_) {},
25
+ highlightComponent (_) {},
26
+ getRouterInfo: (_)=>({}),
27
+ onRouterInfoUpdated: (event, data)=>{
28
+ ctx.hooks.callHook(event, data).finally();
29
+ },
30
+ getPiniaTree: (_)=>({}),
31
+ onPiniaTreeUpdated: (event, data)=>{
32
+ ctx.hooks.callHook(event, data).finally();
33
+ },
34
+ getPiniaState: (_)=>({}),
35
+ onPiniaInfoUpdated: (event, data)=>{
36
+ ctx.hooks.callHook(event, data).finally();
37
+ }
38
+ };
39
+ }
40
+ const channelCache = new WeakMap();
41
+ function createRPCServer(ws, functions, options = {}) {
42
+ const group = createBirpcGroup(functions, ()=>Array.from(ws?.clients || []).map((channel)=>{
43
+ if (channel.readyState === channel.CLOSED) return;
44
+ const cached = channelCache.get(channel);
45
+ if (cached) return cached;
46
+ const options = {
47
+ on: (fn)=>channel.on('message', fn),
48
+ post: (data)=>channel.send(data),
49
+ serialize: (v)=>JSON.stringify(v),
50
+ deserialize: (v)=>JSON.parse(v)
51
+ };
52
+ channelCache.set(channel, options);
53
+ return options;
54
+ }).filter((c)=>!!c), options);
55
+ ws.on('connection', ()=>{
56
+ group.updateChannels();
57
+ });
58
+ return group.broadcast;
59
+ }
60
+ var package_namespaceObject = {
61
+ rE: "0.1.0"
62
+ };
63
+ function stringify(value) {
64
+ return "string" == typeof value ? value : JSON.stringify(value);
65
+ }
66
+ function createMcpServerDefault(options, ctx) {
67
+ const server = new McpServer({
68
+ name: "rsbuild-vue-mcp",
69
+ description: 'Rsbuild/Rspack MCP plugin based on Vue DevTools',
70
+ version: package_namespaceObject.rE,
71
+ ...options.mcpServerInfo
72
+ });
73
+ server.registerTool('get-component-tree', {
74
+ description: 'Get the Vue component tree in markdown tree syntax format.'
75
+ }, async ()=>new Promise((resolve, reject)=>{
76
+ const eventName = nanoid();
77
+ ctx.hooks.hookOnce(eventName, (res)=>{
78
+ resolve({
79
+ content: [
80
+ {
81
+ type: 'text',
82
+ text: stringify(res)
83
+ }
84
+ ]
85
+ });
86
+ });
87
+ ctx.rpcServer.getInspectorTree({
88
+ event: eventName
89
+ }).catch((e)=>{
90
+ console.error(e);
91
+ reject(e);
92
+ });
93
+ }));
94
+ server.registerTool('get-component-state', {
95
+ description: 'Get the Vue component state in JSON structure format.',
96
+ inputSchema: {
97
+ componentName: z.string()
98
+ }
99
+ }, async ({ componentName })=>new Promise((resolve, reject)=>{
100
+ const eventName = nanoid();
101
+ ctx.hooks.hookOnce(eventName, (res)=>{
102
+ resolve({
103
+ content: [
104
+ {
105
+ type: 'text',
106
+ text: stringify(res)
107
+ }
108
+ ]
109
+ });
110
+ });
111
+ ctx.rpcServer.getInspectorState({
112
+ event: eventName,
113
+ componentName
114
+ }).catch((e)=>{
115
+ console.error(e);
116
+ reject(e);
117
+ });
118
+ }));
119
+ server.registerTool('edit-component-state', {
120
+ description: 'Edit the Vue component state.',
121
+ inputSchema: {
122
+ componentName: z.string(),
123
+ path: z.array(z.string()),
124
+ value: z.string(),
125
+ valueType: z["enum"]([
126
+ 'string',
127
+ 'number',
128
+ 'boolean',
129
+ 'object',
130
+ 'array'
131
+ ])
132
+ }
133
+ }, async ({ componentName, path, value, valueType })=>new Promise((resolve, reject)=>{
134
+ ctx.rpcServer.editComponentState({
135
+ componentName,
136
+ path,
137
+ value,
138
+ valueType
139
+ }).then(()=>{
140
+ resolve({
141
+ content: [
142
+ {
143
+ type: 'text',
144
+ text: 'ok'
145
+ }
146
+ ]
147
+ });
148
+ }).catch((e)=>{
149
+ console.error(e);
150
+ reject(e);
151
+ });
152
+ }));
153
+ server.registerTool('highlight-component', {
154
+ description: 'Highlight the Vue component.',
155
+ inputSchema: {
156
+ componentName: z.string()
157
+ }
158
+ }, async ({ componentName })=>new Promise((resolve, reject)=>{
159
+ ctx.rpcServer.highlightComponent({
160
+ componentName
161
+ }).then(()=>{
162
+ resolve({
163
+ content: [
164
+ {
165
+ type: 'text',
166
+ text: 'ok'
167
+ }
168
+ ]
169
+ });
170
+ }).catch((e)=>{
171
+ console.error(e);
172
+ reject(e);
173
+ });
174
+ }));
175
+ server.registerTool('get-router-info', {
176
+ description: 'Get the Vue router info in JSON structure format.'
177
+ }, async ()=>new Promise((resolve, reject)=>{
178
+ const eventName = nanoid();
179
+ ctx.hooks.hookOnce(eventName, (res)=>{
180
+ resolve({
181
+ content: [
182
+ {
183
+ type: 'text',
184
+ text: stringify(res)
185
+ }
186
+ ]
187
+ });
188
+ });
189
+ ctx.rpcServer.getRouterInfo({
190
+ event: eventName
191
+ }).catch((e)=>{
192
+ console.error(e);
193
+ reject(e);
194
+ });
195
+ }));
196
+ server.registerTool('get-pinia-state', {
197
+ description: 'Get the Pinia state in JSON structure format.',
198
+ inputSchema: {
199
+ storeName: z.string()
200
+ }
201
+ }, async ({ storeName })=>new Promise((resolve, reject)=>{
202
+ const eventName = nanoid();
203
+ ctx.hooks.hookOnce(eventName, (res)=>{
204
+ resolve({
205
+ content: [
206
+ {
207
+ type: 'text',
208
+ text: stringify(res)
209
+ }
210
+ ]
211
+ });
212
+ });
213
+ ctx.rpcServer.getPiniaState({
214
+ event: eventName,
215
+ storeName
216
+ }).catch((e)=>{
217
+ console.error(e);
218
+ reject(e);
219
+ });
220
+ }));
221
+ server.registerTool('get-pinia-tree', {
222
+ description: 'Get the Pinia tree in JSON structure format.'
223
+ }, async ()=>new Promise((resolve, reject)=>{
224
+ const eventName = nanoid();
225
+ ctx.hooks.hookOnce(eventName, (res)=>{
226
+ resolve({
227
+ content: [
228
+ {
229
+ type: 'text',
230
+ text: stringify(res)
231
+ }
232
+ ]
233
+ });
234
+ });
235
+ ctx.rpcServer.getPiniaTree({
236
+ event: eventName
237
+ }).catch((e)=>{
238
+ console.error(e);
239
+ reject(e);
240
+ });
241
+ }));
242
+ return server;
243
+ }
244
+ async function mcpHandlerStateless(server, req, res) {
245
+ try {
246
+ const transport = new StreamableHTTPServerTransport({
247
+ sessionIdGenerator: void 0
248
+ });
249
+ await server.connect(transport);
250
+ await transport.handleRequest(req, res);
251
+ res.on('close', ()=>{
252
+ transport.close();
253
+ server.close();
254
+ });
255
+ } catch (error) {
256
+ console.error('Error handling MCP request:', error);
257
+ if (!res.headersSent) {
258
+ res.statusCode = 500;
259
+ res.setHeader('content-type', 'application/json');
260
+ res.end(JSON.stringify({
261
+ jsonrpc: '2.0',
262
+ error: {
263
+ code: -32603,
264
+ message: 'Internal server error'
265
+ },
266
+ id: null
267
+ }));
268
+ }
269
+ }
270
+ }
271
+ function setupRoutes(base, getServer, dev) {
272
+ dev.middlewares.use(`${base}/mcp`, async (req, res)=>{
273
+ const server = await getServer();
274
+ await mcpHandlerStateless(server, req, res);
275
+ });
276
+ }
277
+ function setupRspackRoutes(base, getServer, middlewares) {
278
+ middlewares.push({
279
+ name: 'vue-devtools-mcp',
280
+ path: `${base}/mcp`,
281
+ async middleware (req, res) {
282
+ const server = await getServer();
283
+ await mcpHandlerStateless(server, req, res);
284
+ }
285
+ });
286
+ }
287
+ export { createMcpServerDefault, createRPCServer, createServerRpcFunc, createVueMcpContext, setupRoutes, setupRspackRoutes };