veryfront 0.1.140 → 0.1.142
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/esm/cli/mcp/jsonrpc.d.ts +33 -1
- package/esm/cli/mcp/jsonrpc.d.ts.map +1 -1
- package/esm/cli/mcp/jsonrpc.js +63 -4
- package/esm/cli/mcp/remote-file-tools.d.ts.map +1 -1
- package/esm/cli/mcp/remote-file-tools.js +39 -0
- package/esm/cli/mcp/server.d.ts.map +1 -1
- package/esm/cli/mcp/server.js +57 -34
- package/esm/cli/mcp/standalone.d.ts.map +1 -1
- package/esm/cli/mcp/standalone.js +24 -11
- package/esm/cli/mcp/tools/catalog-tools.d.ts.map +1 -1
- package/esm/cli/mcp/tools/catalog-tools.js +15 -5
- package/esm/cli/mcp/tools/cicd-tools.d.ts.map +1 -1
- package/esm/cli/mcp/tools/cicd-tools.js +8 -0
- package/esm/cli/mcp/tools/dev-tools.d.ts.map +1 -1
- package/esm/cli/mcp/tools/dev-tools.js +32 -10
- package/esm/cli/mcp/tools/introspection-tools.d.ts.map +1 -1
- package/esm/cli/mcp/tools/introspection-tools.js +6 -2
- package/esm/cli/mcp/tools/project-tools.d.ts.map +1 -1
- package/esm/cli/mcp/tools/project-tools.js +12 -4
- package/esm/cli/mcp/tools/scaffold-tools.d.ts.map +1 -1
- package/esm/cli/mcp/tools/scaffold-tools.js +6 -2
- package/esm/cli/mcp/tools/skill-tools.d.ts.map +1 -1
- package/esm/cli/mcp/tools/skill-tools.js +6 -2
- package/esm/cli/mcp/tools.d.ts.map +1 -1
- package/esm/cli/mcp/tools.js +36 -16
- package/esm/deno.js +1 -1
- package/esm/src/agent/runtime/index.js +1 -1
- package/esm/src/agent/runtime/tool-helpers.d.ts.map +1 -1
- package/esm/src/agent/runtime/tool-helpers.js +59 -30
- package/esm/src/agent/schemas/agent.schema.d.ts +4 -4
- package/esm/src/channels/invoke.d.ts +4 -4
- package/esm/src/internal-agents/run-stream.d.ts.map +1 -1
- package/esm/src/internal-agents/run-stream.js +62 -0
- package/esm/src/issues/mcp.d.ts.map +1 -1
- package/esm/src/issues/mcp.js +39 -10
- package/esm/src/mcp/index.d.ts +1 -1
- package/esm/src/mcp/index.d.ts.map +1 -1
- package/esm/src/mcp/server.d.ts.map +1 -1
- package/esm/src/mcp/server.js +85 -25
- package/esm/src/mcp/types.d.ts +22 -0
- package/esm/src/mcp/types.d.ts.map +1 -1
- package/esm/src/server/handlers/request/agent-stream.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/request/agent-stream.handler.js +30 -0
- package/esm/src/tool/types.d.ts +5 -0
- package/esm/src/tool/types.d.ts.map +1 -1
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/esm/src/workflow/schemas/workflow.schema.d.ts +6 -6
- package/package.json +1 -1
- package/src/cli/mcp/jsonrpc.ts +72 -4
- package/src/cli/mcp/remote-file-tools.ts +39 -0
- package/src/cli/mcp/server.ts +66 -33
- package/src/cli/mcp/standalone.ts +28 -10
- package/src/cli/mcp/tools/catalog-tools.ts +15 -5
- package/src/cli/mcp/tools/cicd-tools.ts +8 -0
- package/src/cli/mcp/tools/dev-tools.ts +34 -10
- package/src/cli/mcp/tools/introspection-tools.ts +7 -2
- package/src/cli/mcp/tools/project-tools.ts +12 -4
- package/src/cli/mcp/tools/scaffold-tools.ts +6 -2
- package/src/cli/mcp/tools/skill-tools.ts +6 -2
- package/src/cli/mcp/tools.ts +52 -16
- package/src/deno.js +1 -1
- package/src/src/agent/runtime/index.ts +1 -1
- package/src/src/agent/runtime/tool-helpers.ts +86 -36
- package/src/src/internal-agents/run-stream.ts +62 -0
- package/src/src/issues/mcp.ts +43 -10
- package/src/src/mcp/index.ts +7 -1
- package/src/src/mcp/server.ts +92 -31
- package/src/src/mcp/types.ts +24 -0
- package/src/src/server/handlers/request/agent-stream.handler.ts +30 -0
- package/src/src/tool/types.ts +7 -0
- package/src/src/utils/version-constant.ts +1 -1
package/src/cli/mcp/jsonrpc.ts
CHANGED
|
@@ -43,6 +43,18 @@ export const JSONRPC_ERRORS = {
|
|
|
43
43
|
INTERNAL_ERROR: -32603,
|
|
44
44
|
} as const;
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Error with a JSON-RPC error code attached.
|
|
48
|
+
* Preserves stack traces unlike throwing plain objects.
|
|
49
|
+
*/
|
|
50
|
+
export class JsonRpcError extends Error {
|
|
51
|
+
readonly code: number;
|
|
52
|
+
constructor(code: number, message: string) {
|
|
53
|
+
super(message);
|
|
54
|
+
this.code = code;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
46
58
|
/**
|
|
47
59
|
* Create a JSON-RPC parse error response
|
|
48
60
|
*/
|
|
@@ -70,15 +82,71 @@ export function successResponse(id: string | number | undefined, result: unknown
|
|
|
70
82
|
export function errorResponse(
|
|
71
83
|
id: string | number | undefined,
|
|
72
84
|
e: unknown,
|
|
73
|
-
code
|
|
85
|
+
code?: number,
|
|
74
86
|
): JSONRPCResponse {
|
|
87
|
+
const errorCode = typeof e === "object" && e !== null && "code" in e
|
|
88
|
+
? (e as { code: unknown }).code
|
|
89
|
+
: undefined;
|
|
90
|
+
const resolvedCode = code ??
|
|
91
|
+
(typeof errorCode === "number" ? errorCode : JSONRPC_ERRORS.INTERNAL_ERROR);
|
|
92
|
+
const message = e instanceof Error
|
|
93
|
+
? e.message
|
|
94
|
+
: typeof e === "object" && e !== null && "message" in e
|
|
95
|
+
? String((e as { message: unknown }).message)
|
|
96
|
+
: String(e);
|
|
75
97
|
return {
|
|
76
98
|
jsonrpc: "2.0",
|
|
77
99
|
id,
|
|
78
|
-
error: {
|
|
79
|
-
|
|
80
|
-
|
|
100
|
+
error: { code: resolvedCode, message },
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Supported MCP protocol versions (newest first).
|
|
106
|
+
* Shared across all CLI MCP servers so the version list is maintained in one place.
|
|
107
|
+
*/
|
|
108
|
+
export const MCP_SUPPORTED_VERSIONS: [string, ...string[]] = ["2025-11-25", "2024-11-05"];
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Safely extract a record from unknown params (mirrors src/mcp toParamsRecord).
|
|
112
|
+
*/
|
|
113
|
+
export function toParamsRecord(params: unknown): Record<string, unknown> {
|
|
114
|
+
if (params && typeof params === "object" && !Array.isArray(params)) {
|
|
115
|
+
return params as Record<string, unknown>;
|
|
116
|
+
}
|
|
117
|
+
return {};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Negotiate MCP protocol version: echo the client's version if supported,
|
|
122
|
+
* otherwise fall back to the newest supported version.
|
|
123
|
+
*/
|
|
124
|
+
export function negotiateVersion(params: unknown): string {
|
|
125
|
+
const p = toParamsRecord(params);
|
|
126
|
+
const requested = typeof p.protocolVersion === "string" ? p.protocolVersion : undefined;
|
|
127
|
+
return requested && MCP_SUPPORTED_VERSIONS.includes(requested)
|
|
128
|
+
? requested
|
|
129
|
+
: MCP_SUPPORTED_VERSIONS[0];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Build a complete MCP initialize result with negotiated version,
|
|
134
|
+
* capabilities, serverInfo, and instructions.
|
|
135
|
+
*/
|
|
136
|
+
export function buildInitializeResult(
|
|
137
|
+
params: unknown,
|
|
138
|
+
serverInfo: { name: string; title: string; version: string; description: string },
|
|
139
|
+
instructions: string,
|
|
140
|
+
): Record<string, unknown> {
|
|
141
|
+
return {
|
|
142
|
+
protocolVersion: negotiateVersion(params),
|
|
143
|
+
capabilities: {
|
|
144
|
+
tools: { listChanged: true },
|
|
145
|
+
resources: { listChanged: true },
|
|
146
|
+
prompts: { listChanged: true },
|
|
81
147
|
},
|
|
148
|
+
serverInfo,
|
|
149
|
+
instructions,
|
|
82
150
|
};
|
|
83
151
|
}
|
|
84
152
|
|
|
@@ -200,6 +200,8 @@ interface RemoteListFilesOutput {
|
|
|
200
200
|
|
|
201
201
|
export const vfRemoteListFiles: MCPTool<RemoteListFilesInput, RemoteListFilesOutput> = {
|
|
202
202
|
name: "vf_remote_list_files",
|
|
203
|
+
title: "List Remote Files",
|
|
204
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
203
205
|
description:
|
|
204
206
|
"List files in a remote Veryfront project. Returns file paths, types, and sizes. Use this to explore a project's structure.",
|
|
205
207
|
inputSchema: remoteListFilesInput,
|
|
@@ -249,6 +251,8 @@ interface RemoteGetFileOutput {
|
|
|
249
251
|
|
|
250
252
|
export const vfRemoteGetFile: MCPTool<RemoteGetFileInput, RemoteGetFileOutput> = {
|
|
251
253
|
name: "vf_remote_get_file",
|
|
254
|
+
title: "Get Remote File",
|
|
255
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
252
256
|
description:
|
|
253
257
|
"Read the content of a file from a remote Veryfront project. Always use this before modifying a file.",
|
|
254
258
|
inputSchema: remoteGetFileInput,
|
|
@@ -296,6 +300,13 @@ interface RemoteUpdateFileOutput {
|
|
|
296
300
|
|
|
297
301
|
export const vfRemoteUpdateFile: MCPTool<RemoteUpdateFileInput, RemoteUpdateFileOutput> = {
|
|
298
302
|
name: "vf_remote_update_file",
|
|
303
|
+
title: "Update Remote File",
|
|
304
|
+
annotations: {
|
|
305
|
+
readOnlyHint: false,
|
|
306
|
+
destructiveHint: true,
|
|
307
|
+
idempotentHint: true,
|
|
308
|
+
openWorldHint: false,
|
|
309
|
+
},
|
|
299
310
|
description:
|
|
300
311
|
"Create or update a file in a remote Veryfront project. Always read the file first before updating to understand its current state.",
|
|
301
312
|
inputSchema: remoteUpdateFileInput,
|
|
@@ -341,6 +352,13 @@ interface RemoteDeleteFileOutput {
|
|
|
341
352
|
|
|
342
353
|
export const vfRemoteDeleteFile: MCPTool<RemoteDeleteFileInput, RemoteDeleteFileOutput> = {
|
|
343
354
|
name: "vf_remote_delete_file",
|
|
355
|
+
title: "Delete Remote File",
|
|
356
|
+
annotations: {
|
|
357
|
+
readOnlyHint: false,
|
|
358
|
+
destructiveHint: true,
|
|
359
|
+
idempotentHint: true,
|
|
360
|
+
openWorldHint: false,
|
|
361
|
+
},
|
|
344
362
|
description: "Delete a file from a remote Veryfront project.",
|
|
345
363
|
inputSchema: remoteDeleteFileInput,
|
|
346
364
|
execute: async (input) => {
|
|
@@ -378,6 +396,8 @@ interface RemoteSearchFilesOutput {
|
|
|
378
396
|
|
|
379
397
|
export const vfRemoteSearchFiles: MCPTool<RemoteSearchFilesInput, RemoteSearchFilesOutput> = {
|
|
380
398
|
name: "vf_remote_search_files",
|
|
399
|
+
title: "Search Remote Files",
|
|
400
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
381
401
|
description:
|
|
382
402
|
"Search for text patterns within file contents in a remote Veryfront project. Supports regex and glob patterns.",
|
|
383
403
|
inputSchema: remoteSearchFilesInput,
|
|
@@ -430,6 +450,8 @@ interface RemoteMoveFileOutput {
|
|
|
430
450
|
|
|
431
451
|
export const vfRemoteMoveFile: MCPTool<RemoteMoveFileInput, RemoteMoveFileOutput> = {
|
|
432
452
|
name: "vf_remote_move_file",
|
|
453
|
+
title: "Move Remote File",
|
|
454
|
+
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },
|
|
433
455
|
description: "Move or rename a file in a remote Veryfront project.",
|
|
434
456
|
inputSchema: remoteMoveFileInput,
|
|
435
457
|
execute: async (input) => {
|
|
@@ -477,6 +499,8 @@ interface RemoteListBranchesOutput {
|
|
|
477
499
|
|
|
478
500
|
export const vfRemoteListBranches: MCPTool<RemoteListBranchesInput, RemoteListBranchesOutput> = {
|
|
479
501
|
name: "vf_remote_list_branches",
|
|
502
|
+
title: "List Remote Branches",
|
|
503
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
480
504
|
description: "List branches in a remote Veryfront project.",
|
|
481
505
|
inputSchema: remoteListBranchesInput,
|
|
482
506
|
execute: async (input) => {
|
|
@@ -514,6 +538,8 @@ interface RemoteCreateBranchOutput {
|
|
|
514
538
|
|
|
515
539
|
export const vfRemoteCreateBranch: MCPTool<RemoteCreateBranchInput, RemoteCreateBranchOutput> = {
|
|
516
540
|
name: "vf_remote_create_branch",
|
|
541
|
+
title: "Create Remote Branch",
|
|
542
|
+
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },
|
|
517
543
|
description:
|
|
518
544
|
"Create a new branch in a remote Veryfront project. Branch from main by default, or specify a base branch.",
|
|
519
545
|
inputSchema: remoteCreateBranchInput,
|
|
@@ -552,6 +578,8 @@ interface RemoteMergeBranchOutput {
|
|
|
552
578
|
|
|
553
579
|
export const vfRemoteMergeBranch: MCPTool<RemoteMergeBranchInput, RemoteMergeBranchOutput> = {
|
|
554
580
|
name: "vf_remote_merge_branch",
|
|
581
|
+
title: "Merge Remote Branch",
|
|
582
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
555
583
|
description: "Merge a branch into the target branch (or main if not specified).",
|
|
556
584
|
inputSchema: remoteMergeBranchInput,
|
|
557
585
|
execute: async (input) => {
|
|
@@ -596,6 +624,13 @@ interface RemoteDeleteBranchOutput {
|
|
|
596
624
|
|
|
597
625
|
export const vfRemoteDeleteBranch: MCPTool<RemoteDeleteBranchInput, RemoteDeleteBranchOutput> = {
|
|
598
626
|
name: "vf_remote_delete_branch",
|
|
627
|
+
title: "Delete Remote Branch",
|
|
628
|
+
annotations: {
|
|
629
|
+
readOnlyHint: false,
|
|
630
|
+
destructiveHint: true,
|
|
631
|
+
idempotentHint: true,
|
|
632
|
+
openWorldHint: false,
|
|
633
|
+
},
|
|
599
634
|
description: "Delete a branch from a remote Veryfront project.",
|
|
600
635
|
inputSchema: remoteDeleteBranchInput,
|
|
601
636
|
execute: async (input) => {
|
|
@@ -633,6 +668,8 @@ interface RemoteCreateProjectOutput {
|
|
|
633
668
|
|
|
634
669
|
export const vfRemoteCreateProject: MCPTool<RemoteCreateProjectInput, RemoteCreateProjectOutput> = {
|
|
635
670
|
name: "vf_remote_create_project",
|
|
671
|
+
title: "Create Remote Project",
|
|
672
|
+
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },
|
|
636
673
|
description: "Create a new Veryfront project. Returns the project details including ID and slug.",
|
|
637
674
|
inputSchema: remoteCreateProjectInput,
|
|
638
675
|
execute: async (input) => {
|
|
@@ -671,6 +708,8 @@ interface RemoteCloneProjectOutput {
|
|
|
671
708
|
|
|
672
709
|
export const vfRemoteCloneProject: MCPTool<RemoteCloneProjectInput, RemoteCloneProjectOutput> = {
|
|
673
710
|
name: "vf_remote_clone_project",
|
|
711
|
+
title: "Clone Remote Project",
|
|
712
|
+
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },
|
|
674
713
|
description:
|
|
675
714
|
"Clone a Veryfront project by creating a new project and copying all files from the source.",
|
|
676
715
|
inputSchema: remoteCloneProjectInput,
|
package/src/cli/mcp/server.ts
CHANGED
|
@@ -13,11 +13,14 @@ import { createHttpServer, type HttpServer } from "../../src/platform/compat/htt
|
|
|
13
13
|
import type { StdinReader } from "../../src/platform/index.js";
|
|
14
14
|
import { withSpan } from "../../src/observability/tracing/otlp-setup.js";
|
|
15
15
|
import { createIssuesManager } from "../../src/issues/index.js";
|
|
16
|
+
import type { ToolListEntry } from "../../src/mcp/index.js";
|
|
16
17
|
import { getErrorCollector, getLogBuffer } from "../../src/observability/index.js";
|
|
17
18
|
import { allTools, getTool, setServerStartTime } from "./tools.js";
|
|
18
19
|
import { startStdioJsonRpc } from "./stdio.js";
|
|
19
20
|
import {
|
|
21
|
+
buildInitializeResult,
|
|
20
22
|
errorResponse,
|
|
23
|
+
JsonRpcError,
|
|
21
24
|
type JSONRPCRequest,
|
|
22
25
|
JSONRPCRequestSchema,
|
|
23
26
|
type JSONRPCResponse,
|
|
@@ -121,6 +124,17 @@ export class MCPDevServer {
|
|
|
121
124
|
|
|
122
125
|
if (req.method === "OPTIONS") return new dntShim.Response(null, { status: 204, headers });
|
|
123
126
|
|
|
127
|
+
if (origin && !isAllowedOrigin) {
|
|
128
|
+
return new dntShim.Response(
|
|
129
|
+
JSON.stringify({
|
|
130
|
+
jsonrpc: "2.0",
|
|
131
|
+
id: null,
|
|
132
|
+
error: { code: -32600, message: "Forbidden: Origin not allowed" },
|
|
133
|
+
}),
|
|
134
|
+
{ status: 403, headers },
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
124
138
|
if (url.pathname !== "/mcp") {
|
|
125
139
|
return new dntShim.Response(JSON.stringify({ error: "Not found. MCP endpoint is at /mcp" }), {
|
|
126
140
|
status: 404,
|
|
@@ -168,6 +182,8 @@ export class MCPDevServer {
|
|
|
168
182
|
switch (method) {
|
|
169
183
|
case "initialize":
|
|
170
184
|
return Promise.resolve(this.handleInitialize(params));
|
|
185
|
+
case "notifications/initialized":
|
|
186
|
+
return Promise.resolve({});
|
|
171
187
|
case "tools/list":
|
|
172
188
|
return Promise.resolve(this.handleToolsList());
|
|
173
189
|
case "tools/call":
|
|
@@ -185,53 +201,70 @@ export class MCPDevServer {
|
|
|
185
201
|
}
|
|
186
202
|
}
|
|
187
203
|
|
|
188
|
-
private handleInitialize(
|
|
189
|
-
return
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
204
|
+
private handleInitialize(params: unknown): unknown {
|
|
205
|
+
return buildInitializeResult(
|
|
206
|
+
params,
|
|
207
|
+
{
|
|
208
|
+
name: this.config.serverName ?? "veryfront-dev",
|
|
209
|
+
title: "Veryfront Dev MCP Server",
|
|
210
|
+
version: this.config.serverVersion ?? "1.0.0",
|
|
211
|
+
description:
|
|
212
|
+
"Veryfront development server tools for real-time errors, logs, HMR, and scaffolding",
|
|
195
213
|
},
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
version: this.config.serverVersion,
|
|
199
|
-
},
|
|
200
|
-
};
|
|
214
|
+
"Veryfront dev MCP server provides development tools. Use vf_get_errors to check for code errors, vf_get_logs for server logs, and vf_trigger_hmr for hot module reload.",
|
|
215
|
+
);
|
|
201
216
|
}
|
|
202
217
|
|
|
203
|
-
private handleToolsList():
|
|
218
|
+
private handleToolsList(): { tools: ToolListEntry[] } {
|
|
204
219
|
return {
|
|
205
|
-
tools: allTools.map((tool) =>
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
220
|
+
tools: allTools.map((tool) => {
|
|
221
|
+
const entry: ToolListEntry = {
|
|
222
|
+
name: tool.name,
|
|
223
|
+
description: tool.description,
|
|
224
|
+
inputSchema: this.zodToJsonSchema(tool.inputSchema),
|
|
225
|
+
};
|
|
226
|
+
if (tool.title) entry.title = tool.title;
|
|
227
|
+
if (tool.annotations) entry.annotations = tool.annotations;
|
|
228
|
+
return entry;
|
|
229
|
+
}),
|
|
210
230
|
};
|
|
211
231
|
}
|
|
212
232
|
|
|
213
233
|
private handleToolsCall(params: unknown): Promise<unknown> {
|
|
214
|
-
const { name, arguments: args } = ToolsCallParamsSchema.parse(params);
|
|
234
|
+
const { name: toolName, arguments: args } = ToolsCallParamsSchema.parse(params);
|
|
235
|
+
|
|
236
|
+
const tool = getTool(toolName);
|
|
237
|
+
if (!tool) {
|
|
238
|
+
throw new JsonRpcError(-32602, `Unknown tool: ${toolName}`);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
let input: unknown;
|
|
242
|
+
try {
|
|
243
|
+
input = tool.inputSchema.parse(args ?? {});
|
|
244
|
+
} catch (error) {
|
|
245
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
246
|
+
throw new JsonRpcError(-32602, `Invalid arguments for tool ${toolName}: ${message}`);
|
|
247
|
+
}
|
|
215
248
|
|
|
216
249
|
return withSpan(
|
|
217
250
|
"cli.mcp.handleToolsCall",
|
|
218
251
|
async () => {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const input = tool.inputSchema.parse(args ?? {});
|
|
223
|
-
const result = await tool.execute(input);
|
|
252
|
+
try {
|
|
253
|
+
const result = await tool.execute(input);
|
|
224
254
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
255
|
+
return {
|
|
256
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
257
|
+
isError: false,
|
|
258
|
+
};
|
|
259
|
+
} catch (error) {
|
|
260
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
261
|
+
return {
|
|
262
|
+
content: [{ type: "text", text: message }],
|
|
263
|
+
isError: true,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
233
266
|
},
|
|
234
|
-
{ "mcp.tool.name":
|
|
267
|
+
{ "mcp.tool.name": toolName },
|
|
235
268
|
);
|
|
236
269
|
}
|
|
237
270
|
|
|
@@ -11,7 +11,9 @@ import type { StdinReader } from "../../src/platform/index.js";
|
|
|
11
11
|
import { DevServerClient } from "./dev-server-client.js";
|
|
12
12
|
import { startStdioJsonRpc } from "./stdio.js";
|
|
13
13
|
import {
|
|
14
|
+
buildInitializeResult,
|
|
14
15
|
errorResponse,
|
|
16
|
+
JsonRpcError,
|
|
15
17
|
type JSONRPCRequest,
|
|
16
18
|
JSONRPCRequestSchema,
|
|
17
19
|
type JSONRPCResponse,
|
|
@@ -79,11 +81,16 @@ export class StandaloneMCPServer {
|
|
|
79
81
|
private dispatchMethod(method: string, params: unknown): Promise<unknown> {
|
|
80
82
|
switch (method) {
|
|
81
83
|
case "initialize":
|
|
82
|
-
return Promise.resolve(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
return Promise.resolve(buildInitializeResult(
|
|
85
|
+
params,
|
|
86
|
+
{
|
|
87
|
+
name: "veryfront-mcp",
|
|
88
|
+
title: "Veryfront Standalone MCP Server",
|
|
89
|
+
version: "1.0.0",
|
|
90
|
+
description: "Veryfront standalone MCP server for CLI-based development tools",
|
|
91
|
+
},
|
|
92
|
+
"Veryfront standalone MCP server provides development tools. Use vf_get_errors to check for code errors, vf_get_logs for server logs, and vf_get_status for dev server health.",
|
|
93
|
+
));
|
|
87
94
|
case "notifications/initialized":
|
|
88
95
|
return Promise.resolve({});
|
|
89
96
|
case "tools/list":
|
|
@@ -110,13 +117,24 @@ export class StandaloneMCPServer {
|
|
|
110
117
|
}
|
|
111
118
|
|
|
112
119
|
private async handleToolsCall(params: unknown): Promise<unknown> {
|
|
113
|
-
const { name, arguments: args } = ToolsCallParamsSchema.parse(params);
|
|
120
|
+
const { name: toolName, arguments: args } = ToolsCallParamsSchema.parse(params);
|
|
114
121
|
|
|
115
|
-
const tool = this.tools.find((t) => t.name ===
|
|
116
|
-
if (!tool) throw new
|
|
122
|
+
const tool = this.tools.find((t) => t.name === toolName);
|
|
123
|
+
if (!tool) throw new JsonRpcError(-32602, `Unknown tool: ${toolName}`);
|
|
117
124
|
|
|
118
|
-
|
|
119
|
-
|
|
125
|
+
try {
|
|
126
|
+
const result = await tool.execute(args ?? {});
|
|
127
|
+
return {
|
|
128
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
129
|
+
isError: false,
|
|
130
|
+
};
|
|
131
|
+
} catch (error) {
|
|
132
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
133
|
+
return {
|
|
134
|
+
content: [{ type: "text", text: message }],
|
|
135
|
+
isError: true,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
120
138
|
}
|
|
121
139
|
|
|
122
140
|
private handleResourcesList(): unknown {
|
|
@@ -334,8 +334,10 @@ type ListExamplesInput = z.infer<typeof listExamplesInput>;
|
|
|
334
334
|
|
|
335
335
|
export const vfListExamples: MCPTool<ListExamplesInput, ExampleInfo[]> = {
|
|
336
336
|
name: "vf_list_examples",
|
|
337
|
+
title: "List Examples",
|
|
338
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
337
339
|
description:
|
|
338
|
-
"
|
|
340
|
+
"Use this when you need to browse example projects that demonstrate Veryfront features and integrations. Returns an array of example info with name, description, and category. Do not use for project templates — use vf_list_templates instead.",
|
|
339
341
|
inputSchema: listExamplesInput,
|
|
340
342
|
execute: () => Promise.resolve(EXAMPLES),
|
|
341
343
|
};
|
|
@@ -350,8 +352,10 @@ type ListTemplatesInput = z.infer<typeof listTemplatesInput>;
|
|
|
350
352
|
|
|
351
353
|
export const vfListTemplates: MCPTool<ListTemplatesInput, TemplateInfo[]> = {
|
|
352
354
|
name: "vf_list_templates",
|
|
355
|
+
title: "List Templates",
|
|
356
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true }, // openWorldHint: templates come from remote catalog API
|
|
353
357
|
description:
|
|
354
|
-
"
|
|
358
|
+
"Use this when you need to list available project templates for creating new projects. Returns an array of template info with name and description. Do not use for example projects — use vf_list_examples instead.",
|
|
355
359
|
inputSchema: listTemplatesInput,
|
|
356
360
|
execute: () => Promise.resolve(TEMPLATES),
|
|
357
361
|
};
|
|
@@ -372,8 +376,10 @@ type ListIntegrationsInput = z.infer<typeof listIntegrationsInput>;
|
|
|
372
376
|
|
|
373
377
|
export const vfListIntegrations: MCPTool<ListIntegrationsInput, IntegrationInfo[]> = {
|
|
374
378
|
name: "vf_list_integrations",
|
|
379
|
+
title: "List Integrations",
|
|
380
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
375
381
|
description:
|
|
376
|
-
"
|
|
382
|
+
"Use this when you need to list available service integrations (Gmail, Slack, GitHub, etc.) that can be added to AI projects. Returns an array of integration info with name, category, and description. Do not use for adding integrations to a project — use vf_create_project with the integrations parameter instead.",
|
|
377
383
|
inputSchema: listIntegrationsInput,
|
|
378
384
|
execute: (input) => {
|
|
379
385
|
const { category } = input;
|
|
@@ -392,8 +398,10 @@ type ListUsecasesInput = z.infer<typeof listUsecasesInput>;
|
|
|
392
398
|
|
|
393
399
|
export const vfListUsecases: MCPTool<ListUsecasesInput, UsecaseInfo[]> = {
|
|
394
400
|
name: "vf_list_usecases",
|
|
401
|
+
title: "List Use Cases",
|
|
402
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
395
403
|
description:
|
|
396
|
-
"
|
|
404
|
+
"Use this when you need to browse pre-configured use-case templates with recommended integrations and UI layouts. Returns an array of use-case info with name, integrations, and layout. Do not use for raw templates — use vf_list_templates instead.",
|
|
397
405
|
inputSchema: listUsecasesInput,
|
|
398
406
|
execute: () => Promise.resolve(USECASES),
|
|
399
407
|
};
|
|
@@ -438,8 +446,10 @@ interface CreateProjectResult {
|
|
|
438
446
|
|
|
439
447
|
export const vfCreateProject: MCPTool<CreateProjectInput, CreateProjectResult> = {
|
|
440
448
|
name: "vf_create_project",
|
|
449
|
+
title: "Create Project",
|
|
450
|
+
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },
|
|
441
451
|
description:
|
|
442
|
-
"
|
|
452
|
+
"Use this when you need to create a new Veryfront project from a template. Returns the project directory and next steps. Do not use for scaffolding individual files — use vf_scaffold instead.",
|
|
443
453
|
inputSchema: createProjectInput,
|
|
444
454
|
execute: (input) =>
|
|
445
455
|
withSpan(
|
|
@@ -8,6 +8,8 @@ const getPipelineStatusInput = z.object({
|
|
|
8
8
|
|
|
9
9
|
const vfGetPipelineStatus: MCPTool = {
|
|
10
10
|
name: "vf_get_pipeline_status",
|
|
11
|
+
title: "Pipeline Status",
|
|
12
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
11
13
|
description: "Get the current build/deploy pipeline state for a project environment.",
|
|
12
14
|
inputSchema: getPipelineStatusInput,
|
|
13
15
|
execute: async (input: { projectSlug: string; environment: string }) => {
|
|
@@ -27,6 +29,8 @@ const getDeployHistoryInput = z.object({
|
|
|
27
29
|
|
|
28
30
|
const vfGetDeployHistory: MCPTool = {
|
|
29
31
|
name: "vf_get_deploy_history",
|
|
32
|
+
title: "Deploy History",
|
|
33
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
30
34
|
description: "List recent deployments with status, version, URL, and timestamp.",
|
|
31
35
|
inputSchema: getDeployHistoryInput,
|
|
32
36
|
execute: async (input: { projectSlug: string; limit: number }) => {
|
|
@@ -45,6 +49,8 @@ const getBuildLogsInput = z.object({
|
|
|
45
49
|
|
|
46
50
|
const vfGetBuildLogs: MCPTool = {
|
|
47
51
|
name: "vf_get_build_logs",
|
|
52
|
+
title: "Build Logs",
|
|
53
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
48
54
|
description: "Get build logs from an active or recent build/deployment.",
|
|
49
55
|
inputSchema: getBuildLogsInput,
|
|
50
56
|
execute: async (input: { projectSlug: string; deployId?: string }) => {
|
|
@@ -64,6 +70,8 @@ const triggerDeployInput = z.object({
|
|
|
64
70
|
|
|
65
71
|
const vfTriggerDeploy: MCPTool = {
|
|
66
72
|
name: "vf_trigger_deploy",
|
|
73
|
+
title: "Trigger Deploy",
|
|
74
|
+
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },
|
|
67
75
|
description:
|
|
68
76
|
"Trigger a deployment to an environment. Returns a deployment ID for status tracking.",
|
|
69
77
|
inputSchema: triggerDeployInput,
|
|
@@ -20,7 +20,7 @@ const hotReloadInput = z.object({
|
|
|
20
20
|
file: z
|
|
21
21
|
.string()
|
|
22
22
|
.optional()
|
|
23
|
-
.describe("Specific file to trigger reload for
|
|
23
|
+
.describe("Specific file to trigger reload for. Example: 'app/page.tsx'. Omit to reload all."),
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
type HotReloadInput = z.infer<typeof hotReloadInput>;
|
|
@@ -32,8 +32,15 @@ interface HotReloadResult {
|
|
|
32
32
|
|
|
33
33
|
export const vfHotReload: MCPTool<HotReloadInput, HotReloadResult> = {
|
|
34
34
|
name: "vf_hot_reload",
|
|
35
|
+
title: "Hot Reload",
|
|
36
|
+
annotations: {
|
|
37
|
+
readOnlyHint: false,
|
|
38
|
+
destructiveHint: false,
|
|
39
|
+
idempotentHint: true,
|
|
40
|
+
openWorldHint: false,
|
|
41
|
+
},
|
|
35
42
|
description:
|
|
36
|
-
"
|
|
43
|
+
"Use this when you need to signal that a hot reload should occur. Note: currently a no-op stub that returns success without triggering an actual reload. For file-level HMR that sends a WebSocket update, use vf_trigger_hmr instead.",
|
|
37
44
|
inputSchema: hotReloadInput,
|
|
38
45
|
execute: () =>
|
|
39
46
|
Promise.resolve({
|
|
@@ -72,8 +79,10 @@ interface DebugContextResult {
|
|
|
72
79
|
|
|
73
80
|
export const vfGetDebugContext: MCPTool<GetDebugContextInput, DebugContextResult> = {
|
|
74
81
|
name: "vf_get_debug_context",
|
|
82
|
+
title: "Debug Context",
|
|
83
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
75
84
|
description:
|
|
76
|
-
"
|
|
85
|
+
"Use this when you need the dev server's debug context including project slug, environment, request context mode, and multi-project configuration. Returns project info and server mode. Do not use for error details — use vf_get_errors instead.",
|
|
77
86
|
inputSchema: getDebugContextInput,
|
|
78
87
|
execute: (input) =>
|
|
79
88
|
withSpan(
|
|
@@ -114,7 +123,7 @@ export const vfGetDebugContext: MCPTool<GetDebugContextInput, DebugContextResult
|
|
|
114
123
|
// ============================================================================
|
|
115
124
|
|
|
116
125
|
const triggerHmrInput = z.object({
|
|
117
|
-
path: z.string().describe("File path that changed
|
|
126
|
+
path: z.string().describe("File path that changed. Example: 'app/page.tsx'."),
|
|
118
127
|
port: z.number().int().min(1).max(65535).optional().default(8080).describe(
|
|
119
128
|
"Dev server port (defaults to 8080)",
|
|
120
129
|
),
|
|
@@ -129,8 +138,15 @@ interface TriggerHmrResult {
|
|
|
129
138
|
|
|
130
139
|
export const vfTriggerHmr: MCPTool<TriggerHmrInput, TriggerHmrResult> = {
|
|
131
140
|
name: "vf_trigger_hmr",
|
|
141
|
+
title: "Trigger HMR",
|
|
142
|
+
annotations: {
|
|
143
|
+
readOnlyHint: false,
|
|
144
|
+
destructiveHint: false,
|
|
145
|
+
idempotentHint: true,
|
|
146
|
+
openWorldHint: false,
|
|
147
|
+
},
|
|
132
148
|
description:
|
|
133
|
-
"
|
|
149
|
+
"Use this when you need to force an HMR update for a specific file path. Sends a WebSocket reload notification to connected browsers. Returns success status and active listener count. Do not use if no browser is connected — check vf_get_flywheel_status first.",
|
|
134
150
|
inputSchema: triggerHmrInput,
|
|
135
151
|
execute: (input) => {
|
|
136
152
|
const metrics = ReloadNotifier.getMetrics();
|
|
@@ -156,7 +172,7 @@ export const vfTriggerHmr: MCPTool<TriggerHmrInput, TriggerHmrResult> = {
|
|
|
156
172
|
|
|
157
173
|
const previewRouteInput = z.object({
|
|
158
174
|
route: z.string().startsWith("/", "Route must start with /").describe(
|
|
159
|
-
"Route path to preview
|
|
175
|
+
"Route path to preview. Example: '/', '/dashboard', '/api/users'.",
|
|
160
176
|
),
|
|
161
177
|
port: z.number().int().min(1).max(65535).optional().default(8080).describe(
|
|
162
178
|
"Dev server port (defaults to 8080)",
|
|
@@ -165,7 +181,9 @@ const previewRouteInput = z.object({
|
|
|
165
181
|
.enum(["html", "json", "status"])
|
|
166
182
|
.optional()
|
|
167
183
|
.default("status")
|
|
168
|
-
.describe(
|
|
184
|
+
.describe(
|
|
185
|
+
"Output format: 'html' for full page, 'json' for API response, 'status' for just HTTP status. Defaults to 'status'.",
|
|
186
|
+
),
|
|
169
187
|
});
|
|
170
188
|
|
|
171
189
|
type PreviewRouteInput = z.infer<typeof previewRouteInput>;
|
|
@@ -182,8 +200,10 @@ interface PreviewRouteResult {
|
|
|
182
200
|
|
|
183
201
|
export const vfPreviewRoute: MCPTool<PreviewRouteInput, PreviewRouteResult> = {
|
|
184
202
|
name: "vf_preview_route",
|
|
203
|
+
title: "Preview Route",
|
|
204
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
185
205
|
description:
|
|
186
|
-
"
|
|
206
|
+
"Use this when you need to test-render a route and inspect the response. Returns rendered output, HTTP status, and render time. Note: API routes may have side effects. Do not use for listing routes — use vf_list_routes instead.",
|
|
187
207
|
inputSchema: previewRouteInput,
|
|
188
208
|
execute: (input) =>
|
|
189
209
|
withSpan(
|
|
@@ -263,8 +283,10 @@ interface WaitForReadyResult {
|
|
|
263
283
|
|
|
264
284
|
export const vfWaitForReady: MCPTool<WaitForReadyInput, WaitForReadyResult> = {
|
|
265
285
|
name: "vf_wait_for_ready",
|
|
286
|
+
title: "Wait for Ready",
|
|
287
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
266
288
|
description:
|
|
267
|
-
"
|
|
289
|
+
"Use this when you need to wait for the dev server to become ready after restart. Polls the health endpoint until responsive. Returns success status and elapsed time. Do not use for error counts or uptime — use vf_get_status instead.",
|
|
268
290
|
inputSchema: waitForReadyInput,
|
|
269
291
|
execute: (input) =>
|
|
270
292
|
withSpan(
|
|
@@ -351,8 +373,10 @@ interface FlywheelStatus {
|
|
|
351
373
|
|
|
352
374
|
export const vfGetFlywheelStatus: MCPTool<GetFlywheelStatusInput, FlywheelStatus> = {
|
|
353
375
|
name: "vf_get_flywheel_status",
|
|
376
|
+
title: "Flywheel Status",
|
|
377
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
354
378
|
description:
|
|
355
|
-
"
|
|
379
|
+
"Use this when you need a comprehensive status overview combining server health, error counts, and HMR statistics. Returns server status, error/log counts, and HMR metrics in one response. Do not use for detailed error or log content — use vf_get_errors or vf_get_logs instead.",
|
|
356
380
|
inputSchema: getFlywheelStatusInput,
|
|
357
381
|
execute: (input) =>
|
|
358
382
|
withSpan(
|
|
@@ -11,8 +11,10 @@ const getSchemaInput = z.object({
|
|
|
11
11
|
|
|
12
12
|
const vfGetSchema: MCPTool = {
|
|
13
13
|
name: "vf_get_schema",
|
|
14
|
+
title: "Get CLI Schema",
|
|
15
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
14
16
|
description:
|
|
15
|
-
"
|
|
17
|
+
"Use this when you need to discover available CLI commands, their arguments, and flags. Returns the command schema as JSON. Do not use for project info — use vf_get_project_info instead.",
|
|
16
18
|
inputSchema: getSchemaInput,
|
|
17
19
|
execute: async (input: { command?: string; category?: string }) => {
|
|
18
20
|
if (input.command) {
|
|
@@ -26,7 +28,10 @@ const getProjectInfoInput = z.object({});
|
|
|
26
28
|
|
|
27
29
|
const vfGetProjectInfo: MCPTool = {
|
|
28
30
|
name: "vf_get_project_info",
|
|
29
|
-
|
|
31
|
+
title: "Get Project Info",
|
|
32
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
33
|
+
description:
|
|
34
|
+
"Use this when you need project metadata including project slug, version, and environment. Returns slug, version, and environment config. Do not use for CLI commands — use vf_get_schema instead.",
|
|
30
35
|
inputSchema: getProjectInfoInput,
|
|
31
36
|
execute: async () => {
|
|
32
37
|
const { getEnvironmentConfig } = await import("../../../src/config/index.js");
|