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
|
@@ -37,8 +37,10 @@ type ListRoutesInput = z.infer<typeof listRoutesInput>;
|
|
|
37
37
|
|
|
38
38
|
export const vfListRoutes: MCPTool<ListRoutesInput, RouteInfo[]> = {
|
|
39
39
|
name: "vf_list_routes",
|
|
40
|
+
title: "List Routes",
|
|
41
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
40
42
|
description:
|
|
41
|
-
"
|
|
43
|
+
"Use this when you need to discover all routes in the project including pages, API routes, layouts, error, loading, and not-found routes. Returns an array of route info with path, type, and file. Do not use for rendering a route — use vf_preview_route instead.",
|
|
42
44
|
inputSchema: listRoutesInput,
|
|
43
45
|
execute: (input) =>
|
|
44
46
|
withSpan(
|
|
@@ -142,8 +144,10 @@ async function getProjectName(projectDir: string, fs: FileSystem): Promise<strin
|
|
|
142
144
|
|
|
143
145
|
export const vfGetProjectContext: MCPTool<GetProjectContextInput, ProjectContext> = {
|
|
144
146
|
name: "vf_get_project_context",
|
|
147
|
+
title: "Project Context",
|
|
148
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
145
149
|
description:
|
|
146
|
-
"
|
|
150
|
+
"Use this when you need to understand the project structure, conventions, and capabilities at the start of a coding session. Also returns route information. Do not use for detailed per-route rendering — use vf_preview_route instead.",
|
|
147
151
|
inputSchema: getProjectContextInput,
|
|
148
152
|
execute: (input) =>
|
|
149
153
|
withSpan(
|
|
@@ -219,8 +223,10 @@ function toRelativePath(absolutePath: string, projectDir: string): string {
|
|
|
219
223
|
|
|
220
224
|
export const vfGetComponentTree: MCPTool<GetComponentTreeInput, ComponentTreeResult> = {
|
|
221
225
|
name: "vf_get_component_tree",
|
|
226
|
+
title: "Component Tree",
|
|
227
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
222
228
|
description:
|
|
223
|
-
"
|
|
229
|
+
"Use this when you need to analyze the component hierarchy for a specific route including layouts, providers, and nested components. Returns the component tree structure. Do not use for listing all routes — use vf_list_routes instead.",
|
|
224
230
|
inputSchema: getComponentTreeInput,
|
|
225
231
|
execute: (input) =>
|
|
226
232
|
withSpan(
|
|
@@ -375,8 +381,10 @@ async function scanForProjects(
|
|
|
375
381
|
|
|
376
382
|
export const vfListLocalProjects: MCPTool<ListLocalProjectsInput, LocalProjectInfo[]> = {
|
|
377
383
|
name: "vf_list_local_projects",
|
|
384
|
+
title: "List Local Projects",
|
|
385
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
378
386
|
description:
|
|
379
|
-
"
|
|
387
|
+
"Use this when you need to discover Veryfront projects on the local filesystem by scanning for veryfront.config.ts files. Returns project info including template type and integrations. Do not use for project structure details — use vf_get_project_context instead.",
|
|
380
388
|
inputSchema: listLocalProjectsInput,
|
|
381
389
|
execute: (input) =>
|
|
382
390
|
withSpan(
|
|
@@ -209,8 +209,10 @@ const SCAFFOLD_CONFIGS: Record<ScaffoldType, ScaffoldConfig> = {
|
|
|
209
209
|
|
|
210
210
|
export const vfScaffold: MCPTool<ScaffoldInput, ScaffoldResult> = {
|
|
211
211
|
name: "vf_scaffold",
|
|
212
|
+
title: "Scaffold Code",
|
|
213
|
+
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },
|
|
212
214
|
description:
|
|
213
|
-
"
|
|
215
|
+
"Use this when you need to generate new pages, API routes, layouts, components, AI tools, agents, or prompts with proper Veryfront conventions. Returns the created file path and content. May overwrite existing files at the target path. Do not use for creating entire projects — use vf_create_project instead.",
|
|
214
216
|
inputSchema: scaffoldInput,
|
|
215
217
|
execute: (input) =>
|
|
216
218
|
withSpan(
|
|
@@ -400,8 +402,10 @@ const CONVENTIONS: Record<string, Convention> = {
|
|
|
400
402
|
|
|
401
403
|
export const vfGetConventions: MCPTool<GetConventionsInput, Convention[]> = {
|
|
402
404
|
name: "vf_get_conventions",
|
|
405
|
+
title: "Get Conventions",
|
|
406
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
403
407
|
description:
|
|
404
|
-
"
|
|
408
|
+
"Use this when you need Veryfront coding conventions and best practices for routing, API, components, AI, or styling. Do not use for project structure — use vf_get_project_context instead.",
|
|
405
409
|
inputSchema: getConventionsInput,
|
|
406
410
|
execute: (input) => {
|
|
407
411
|
if (input.topic === "all") return Promise.resolve(Object.values(CONVENTIONS));
|
|
@@ -91,8 +91,10 @@ interface GetSkillsResult {
|
|
|
91
91
|
|
|
92
92
|
export const vfGetSkills: MCPTool<GetSkillsInput, GetSkillsResult> = {
|
|
93
93
|
name: "vf_get_skills",
|
|
94
|
+
title: "Get Skills",
|
|
95
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
94
96
|
description:
|
|
95
|
-
"
|
|
97
|
+
"Use this when you need to discover available Agent Skills or load a specific skill's procedural knowledge. Returns skill names and descriptions, or full skill content when name is provided. Do not use for skill reference docs — use vf_get_skill_reference instead.",
|
|
96
98
|
inputSchema: getSkillsInput,
|
|
97
99
|
execute: (input) =>
|
|
98
100
|
withSpan(
|
|
@@ -168,8 +170,10 @@ interface GetSkillReferenceResult {
|
|
|
168
170
|
|
|
169
171
|
export const vfGetSkillReference: MCPTool<GetSkillReferenceInput, GetSkillReferenceResult> = {
|
|
170
172
|
name: "vf_get_skill_reference",
|
|
173
|
+
title: "Get Skill Reference",
|
|
174
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
171
175
|
description:
|
|
172
|
-
"
|
|
176
|
+
"Use this when you need to load a specific reference document from a skill. Returns the document content as text. Do not use for skill discovery — use vf_get_skills instead.",
|
|
173
177
|
inputSchema: getSkillReferenceInput,
|
|
174
178
|
execute: async (input) => {
|
|
175
179
|
const fs = getFs();
|
package/src/cli/mcp/tools.ts
CHANGED
|
@@ -35,18 +35,24 @@ export function setServerStartTime(time: number): void {
|
|
|
35
35
|
|
|
36
36
|
const getErrorsInput = z.object({
|
|
37
37
|
type: z.enum(["compile", "runtime", "bundle", "hmr", "module"]).optional().describe(
|
|
38
|
-
"Filter by error type",
|
|
38
|
+
"Filter by error type. Example: 'compile'. Omit to return all types.",
|
|
39
|
+
),
|
|
40
|
+
file: z.string().optional().describe(
|
|
41
|
+
"Filter by file path. Example: 'app/page.tsx'. Omit to return errors from all files.",
|
|
42
|
+
),
|
|
43
|
+
limit: z.number().optional().default(50).describe(
|
|
44
|
+
"Maximum number of errors to return. Defaults to 50.",
|
|
39
45
|
),
|
|
40
|
-
file: z.string().optional().describe("Filter by file path"),
|
|
41
|
-
limit: z.number().optional().default(50).describe("Maximum number of errors to return"),
|
|
42
46
|
});
|
|
43
47
|
|
|
44
48
|
type GetErrorsInput = z.infer<typeof getErrorsInput>;
|
|
45
49
|
|
|
46
50
|
export const vfGetErrors: MCPTool<GetErrorsInput, DevError[]> = {
|
|
47
51
|
name: "vf_get_errors",
|
|
52
|
+
title: "Get Errors",
|
|
53
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
48
54
|
description:
|
|
49
|
-
"
|
|
55
|
+
"Use this when you need to check for compilation, runtime, bundle, HMR, or module errors in the dev server. Returns error details including file path, line number, and message. Do not use for server logs — use vf_get_logs instead.",
|
|
50
56
|
inputSchema: getErrorsInput,
|
|
51
57
|
execute: async (input) => {
|
|
52
58
|
const errors = getErrorCollector().getAll({
|
|
@@ -60,21 +66,31 @@ export const vfGetErrors: MCPTool<GetErrorsInput, DevError[]> = {
|
|
|
60
66
|
};
|
|
61
67
|
|
|
62
68
|
const getLogsInput = z.object({
|
|
63
|
-
level: z.enum(["debug", "info", "warn", "error"]).optional().describe(
|
|
69
|
+
level: z.enum(["debug", "info", "warn", "error"]).optional().describe(
|
|
70
|
+
"Filter by log level. Example: 'error'. Omit to return all levels.",
|
|
71
|
+
),
|
|
64
72
|
source: z.string().optional().describe(
|
|
65
|
-
"Filter by log source
|
|
73
|
+
"Filter by log source. Example: 'server', 'hmr', 'transform'. Omit to return all sources.",
|
|
74
|
+
),
|
|
75
|
+
pattern: z.string().optional().describe(
|
|
76
|
+
"Filter by pattern (case-insensitive substring match). Example: 'timeout'.",
|
|
77
|
+
),
|
|
78
|
+
limit: z.number().optional().default(100).describe(
|
|
79
|
+
"Maximum number of log entries to return. Defaults to 100.",
|
|
80
|
+
),
|
|
81
|
+
since: z.number().optional().describe(
|
|
82
|
+
"Only return logs after this Unix timestamp in milliseconds.",
|
|
66
83
|
),
|
|
67
|
-
pattern: z.string().optional().describe("Filter by pattern (case-insensitive substring match)"),
|
|
68
|
-
limit: z.number().optional().default(100).describe("Maximum number of log entries to return"),
|
|
69
|
-
since: z.number().optional().describe("Only return logs after this timestamp"),
|
|
70
84
|
});
|
|
71
85
|
|
|
72
86
|
type GetLogsInput = z.infer<typeof getLogsInput>;
|
|
73
87
|
|
|
74
88
|
export const vfGetLogs: MCPTool<GetLogsInput, LogEntry[]> = {
|
|
75
89
|
name: "vf_get_logs",
|
|
90
|
+
title: "Get Logs",
|
|
91
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
76
92
|
description:
|
|
77
|
-
"
|
|
93
|
+
"Use this when you need to inspect server logs to understand runtime behavior or debug request handling. Returns log entries with timestamp, level, source, and message. Do not use for build/compile errors — use vf_get_errors instead.",
|
|
78
94
|
inputSchema: getLogsInput,
|
|
79
95
|
execute: async (input) => {
|
|
80
96
|
return getLogBuffer().query({
|
|
@@ -89,7 +105,7 @@ export const vfGetLogs: MCPTool<GetLogsInput, LogEntry[]> = {
|
|
|
89
105
|
|
|
90
106
|
const clearCacheInput = z.object({
|
|
91
107
|
type: z.enum(["all", "modules", "mdx"]).optional().default("all").describe(
|
|
92
|
-
"Type of cache to clear",
|
|
108
|
+
"Type of cache to clear. Example: 'modules'. Defaults to 'all'.",
|
|
93
109
|
),
|
|
94
110
|
});
|
|
95
111
|
|
|
@@ -102,8 +118,15 @@ interface ClearCacheOutput {
|
|
|
102
118
|
|
|
103
119
|
export const vfClearCache: MCPTool<ClearCacheInput, ClearCacheOutput> = {
|
|
104
120
|
name: "vf_clear_cache",
|
|
121
|
+
title: "Clear Cache",
|
|
122
|
+
annotations: {
|
|
123
|
+
readOnlyHint: false,
|
|
124
|
+
destructiveHint: true,
|
|
125
|
+
idempotentHint: true,
|
|
126
|
+
openWorldHint: false,
|
|
127
|
+
},
|
|
105
128
|
description:
|
|
106
|
-
"
|
|
129
|
+
"Use this when the dev server shows stale modules or MDX content. Returns the list of cleared cache directories. Do not use to fix code errors — those require code changes.",
|
|
107
130
|
inputSchema: clearCacheInput,
|
|
108
131
|
execute: async (input) => {
|
|
109
132
|
const fs = createFileSystem();
|
|
@@ -143,7 +166,10 @@ export function createVfGetStatus(
|
|
|
143
166
|
): MCPTool<GetStatusInput, ServerStatus> {
|
|
144
167
|
return {
|
|
145
168
|
name: "vf_get_status",
|
|
146
|
-
|
|
169
|
+
title: "Server Status",
|
|
170
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
171
|
+
description:
|
|
172
|
+
"Use this when you need a quick summary of the dev server's uptime, error counts, and warning counts. Note: always reports running=true when the MCP server is reachable. Do not use for detailed error info — use vf_get_errors instead.",
|
|
147
173
|
inputSchema: getStatusInput,
|
|
148
174
|
execute: async () => {
|
|
149
175
|
const errors = getErrorCollector();
|
|
@@ -167,9 +193,11 @@ export function createVfGetStatus(
|
|
|
167
193
|
export const vfGetStatus = createVfGetStatus();
|
|
168
194
|
|
|
169
195
|
const clearErrorsInput = z.object({
|
|
170
|
-
file: z.string().optional().describe(
|
|
196
|
+
file: z.string().optional().describe(
|
|
197
|
+
"Clear errors for a specific file only. Example: 'app/page.tsx'. Omit to clear all files.",
|
|
198
|
+
),
|
|
171
199
|
type: z.enum(["compile", "runtime", "bundle", "hmr", "module"]).optional().describe(
|
|
172
|
-
"Clear errors of a specific type only",
|
|
200
|
+
"Clear errors of a specific type only. Example: 'compile'. Omit to clear all types.",
|
|
173
201
|
),
|
|
174
202
|
});
|
|
175
203
|
|
|
@@ -181,7 +209,15 @@ interface ClearErrorsOutput {
|
|
|
181
209
|
|
|
182
210
|
export const vfClearErrors: MCPTool<ClearErrorsInput, ClearErrorsOutput> = {
|
|
183
211
|
name: "vf_clear_errors",
|
|
184
|
-
|
|
212
|
+
title: "Clear Errors",
|
|
213
|
+
annotations: {
|
|
214
|
+
readOnlyHint: false,
|
|
215
|
+
destructiveHint: true,
|
|
216
|
+
idempotentHint: true,
|
|
217
|
+
openWorldHint: false,
|
|
218
|
+
},
|
|
219
|
+
description:
|
|
220
|
+
"Use this when you need to clear accumulated errors from the error collector, optionally filtering by file or type. Returns the number of cleared errors. Do not use for viewing errors — use vf_get_errors instead.",
|
|
185
221
|
inputSchema: clearErrorsInput,
|
|
186
222
|
execute: async (input) => {
|
|
187
223
|
const collector = getErrorCollector();
|
package/src/deno.js
CHANGED
|
@@ -406,7 +406,7 @@ export class AgentRuntime {
|
|
|
406
406
|
logger.error("Agent stream error", { error });
|
|
407
407
|
sendSSE(controller, encoder, {
|
|
408
408
|
type: "error",
|
|
409
|
-
error:
|
|
409
|
+
error: error instanceof Error ? error.message : String(error),
|
|
410
410
|
});
|
|
411
411
|
closeSSEStream(controller);
|
|
412
412
|
} finally {
|
|
@@ -11,6 +11,7 @@ import { executeTool, toolRegistry } from "../../tool/index.js";
|
|
|
11
11
|
import { toolToProviderDefinition } from "../../tool/registry.js";
|
|
12
12
|
import { SKILL_TOOL_IDS } from "../../skill/types.js";
|
|
13
13
|
import { serverLogger } from "../../utils/index.js";
|
|
14
|
+
import { createError, toError } from "../../errors/veryfront-error.js";
|
|
14
15
|
import {
|
|
15
16
|
executeRemoteIntegrationTool,
|
|
16
17
|
isRemoteIntegrationTool,
|
|
@@ -71,6 +72,52 @@ export function isDynamicTool(name: string): boolean {
|
|
|
71
72
|
// deno-lint-ignore no-explicit-any -- generic erasure: accepts Tool with any input/output types
|
|
72
73
|
export type ToolConfigEntry = Tool<any, any> | boolean;
|
|
73
74
|
|
|
75
|
+
function formatAvailableToolNames(names: Iterable<string>): string {
|
|
76
|
+
const sorted = [...new Set(names)].sort();
|
|
77
|
+
return sorted.length > 0 ? sorted.join(", ") : "(none)";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function throwUnknownConfiguredToolsError(
|
|
81
|
+
unknownToolNames: string[],
|
|
82
|
+
availableLocalToolNames: Iterable<string>,
|
|
83
|
+
availableRemoteToolNames: Iterable<string>,
|
|
84
|
+
): never {
|
|
85
|
+
const unknownList = unknownToolNames.sort().join(", ");
|
|
86
|
+
const availableNames = formatAvailableToolNames([
|
|
87
|
+
...availableLocalToolNames,
|
|
88
|
+
...availableRemoteToolNames,
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
throw toError(
|
|
92
|
+
createError({
|
|
93
|
+
type: "agent",
|
|
94
|
+
message:
|
|
95
|
+
`Unknown tool reference${unknownToolNames.length === 1 ? "" : "s"}: ${unknownList}. ` +
|
|
96
|
+
`Tool names must exactly match tool({ id: "..." }). Available tools: ${availableNames}`,
|
|
97
|
+
}),
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function getRemoteToolDefinitions(options?: {
|
|
102
|
+
includeIntegrationTools?: boolean;
|
|
103
|
+
allowedRemoteToolNames?: string[];
|
|
104
|
+
}): Promise<ToolDefinition[]> {
|
|
105
|
+
if (options?.includeIntegrationTools === false) {
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const { getRemoteIntegrationToolDefinitions } = await import(
|
|
111
|
+
"../../integrations/remote-tools.js"
|
|
112
|
+
);
|
|
113
|
+
return (await getRemoteIntegrationToolDefinitions()).filter((def) =>
|
|
114
|
+
!options?.allowedRemoteToolNames || options.allowedRemoteToolNames.includes(def.name)
|
|
115
|
+
);
|
|
116
|
+
} catch {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
74
121
|
export function resolveConfiguredTool(
|
|
75
122
|
toolsConfig: true | Record<string, ToolConfigEntry> | undefined,
|
|
76
123
|
toolName: string,
|
|
@@ -179,32 +226,35 @@ export async function getAvailableTools(
|
|
|
179
226
|
});
|
|
180
227
|
|
|
181
228
|
// Append remote integration tools (per-request, project-scoped)
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
"../../integrations/remote-tools.js"
|
|
186
|
-
);
|
|
187
|
-
const remoteDefs = (await getRemoteIntegrationToolDefinitions()).filter((def) =>
|
|
188
|
-
!options?.allowedRemoteToolNames || options.allowedRemoteToolNames.includes(def.name)
|
|
189
|
-
);
|
|
190
|
-
for (const def of remoteDefs) {
|
|
191
|
-
logToolDefinition(def.name, def);
|
|
192
|
-
}
|
|
193
|
-
tools.push(...remoteDefs);
|
|
194
|
-
} catch {
|
|
195
|
-
// Integration tools unavailable — non-fatal
|
|
196
|
-
}
|
|
229
|
+
const remoteDefs = await getRemoteToolDefinitions(options);
|
|
230
|
+
for (const def of remoteDefs) {
|
|
231
|
+
logToolDefinition(def.name, def);
|
|
197
232
|
}
|
|
233
|
+
tools.push(...remoteDefs);
|
|
198
234
|
|
|
199
235
|
return tools;
|
|
200
236
|
}
|
|
201
237
|
|
|
202
238
|
const tools: ToolDefinition[] = [];
|
|
239
|
+
const remoteDefs = await getRemoteToolDefinitions(options);
|
|
240
|
+
const remoteToolNames = new Set(remoteDefs.map((def) => def.name));
|
|
241
|
+
const explicitlyRequestedRemoteToolNames = new Set<string>();
|
|
242
|
+
const unresolvedConfiguredToolNames: string[] = [];
|
|
203
243
|
|
|
204
244
|
for (const [name, entry] of Object.entries(toolsConfig)) {
|
|
205
245
|
if (entry === true) {
|
|
206
246
|
const tool = toolRegistry.get(name);
|
|
207
|
-
if (tool)
|
|
247
|
+
if (tool) {
|
|
248
|
+
addToolDefinition(tools, name, tool);
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (remoteToolNames.has(name)) {
|
|
253
|
+
explicitlyRequestedRemoteToolNames.add(name);
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
unresolvedConfiguredToolNames.push(name);
|
|
208
258
|
continue;
|
|
209
259
|
}
|
|
210
260
|
|
|
@@ -213,28 +263,28 @@ export async function getAvailableTools(
|
|
|
213
263
|
}
|
|
214
264
|
}
|
|
215
265
|
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
-
//
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
);
|
|
227
|
-
|
|
228
|
-
// Skip if already present (e.g., explicitly configured by name)
|
|
229
|
-
if (!tools.some((t) => t.name === def.name)) {
|
|
230
|
-
logToolDefinition(def.name, def);
|
|
231
|
-
tools.push(def);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
} catch {
|
|
235
|
-
// Integration tools unavailable — non-fatal
|
|
266
|
+
// Explicit-object configs should only expose remote definitions that were
|
|
267
|
+
// explicitly requested, except for the internal runtime path that expands
|
|
268
|
+
// `tools: true` into an explicit local-tool map and passes the remote allowlist.
|
|
269
|
+
const remoteDefsToAppend = explicitlyRequestedRemoteToolNames.size > 0
|
|
270
|
+
? remoteDefs.filter((def) => explicitlyRequestedRemoteToolNames.has(def.name))
|
|
271
|
+
: remoteDefs.filter((def) => options?.allowedRemoteToolNames?.includes(def.name));
|
|
272
|
+
|
|
273
|
+
for (const def of remoteDefsToAppend) {
|
|
274
|
+
// Skip if already present (e.g., explicitly configured by name)
|
|
275
|
+
if (!tools.some((t) => t.name === def.name)) {
|
|
276
|
+
logToolDefinition(def.name, def);
|
|
277
|
+
tools.push(def);
|
|
236
278
|
}
|
|
237
279
|
}
|
|
238
280
|
|
|
281
|
+
if (unresolvedConfiguredToolNames.length > 0) {
|
|
282
|
+
throwUnknownConfiguredToolsError(
|
|
283
|
+
unresolvedConfiguredToolNames,
|
|
284
|
+
toolRegistry.getAll().keys(),
|
|
285
|
+
remoteToolNames,
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
239
289
|
return tools;
|
|
240
290
|
}
|
|
@@ -17,8 +17,10 @@ import {
|
|
|
17
17
|
} from "./ag-ui-sse.js";
|
|
18
18
|
import { AgentRunCancelledError, type AgentRunSessionManager } from "./session-manager.js";
|
|
19
19
|
import type { RuntimeRunAgentInput } from "./schema.js";
|
|
20
|
+
import { serverLogger } from "../utils/index.js";
|
|
20
21
|
|
|
21
22
|
const anyObjectSchema = z.record(z.string(), z.unknown());
|
|
23
|
+
const logger = serverLogger.component("internal-agent-run-stream");
|
|
22
24
|
|
|
23
25
|
type RuntimeFilteredAgent = Agent & {
|
|
24
26
|
config: Agent["config"] & {
|
|
@@ -230,6 +232,14 @@ export async function createRuntimeAgentStreamResponse(
|
|
|
230
232
|
agent: Agent,
|
|
231
233
|
deps: RuntimeAgentStreamExecutionDeps,
|
|
232
234
|
): Promise<dntShim.Response> {
|
|
235
|
+
logger.info("Starting internal agent runtime stream", {
|
|
236
|
+
runId: input.runId,
|
|
237
|
+
threadId: input.threadId,
|
|
238
|
+
agentId: input.agentId,
|
|
239
|
+
messageCount: input.messages.length,
|
|
240
|
+
toolCount: input.tools.length,
|
|
241
|
+
contextCount: input.context.length,
|
|
242
|
+
});
|
|
233
243
|
const abortSignal = deps.sessionManager.startRun({
|
|
234
244
|
runId: input.runId,
|
|
235
245
|
threadId: input.threadId,
|
|
@@ -272,8 +282,19 @@ export async function createRuntimeAgentStreamResponse(
|
|
|
272
282
|
undefined,
|
|
273
283
|
abortSignal,
|
|
274
284
|
);
|
|
285
|
+
logger.info("Internal agent runtime stream attached", {
|
|
286
|
+
runId: input.runId,
|
|
287
|
+
threadId: input.threadId,
|
|
288
|
+
agentId: input.agentId,
|
|
289
|
+
});
|
|
275
290
|
} catch (error) {
|
|
276
291
|
deps.sessionManager.failRun(input.runId);
|
|
292
|
+
logger.error("Internal agent runtime stream setup failed", {
|
|
293
|
+
runId: input.runId,
|
|
294
|
+
threadId: input.threadId,
|
|
295
|
+
agentId: input.agentId,
|
|
296
|
+
error: error instanceof Error ? error.message : String(error),
|
|
297
|
+
});
|
|
277
298
|
throw error;
|
|
278
299
|
}
|
|
279
300
|
|
|
@@ -306,6 +327,11 @@ export async function createRuntimeAgentStreamResponse(
|
|
|
306
327
|
|
|
307
328
|
const abortHandler = () => {
|
|
308
329
|
aborted = true;
|
|
330
|
+
logger.warn("Internal agent runtime stream aborted", {
|
|
331
|
+
runId: input.runId,
|
|
332
|
+
threadId: input.threadId,
|
|
333
|
+
agentId: input.agentId,
|
|
334
|
+
});
|
|
309
335
|
reader.cancel(new AgentRunCancelledError()).catch(() => {});
|
|
310
336
|
};
|
|
311
337
|
|
|
@@ -324,6 +350,11 @@ export async function createRuntimeAgentStreamResponse(
|
|
|
324
350
|
throwIfAborted();
|
|
325
351
|
|
|
326
352
|
if (done) {
|
|
353
|
+
logger.info("Internal agent runtime stream reader completed", {
|
|
354
|
+
runId: input.runId,
|
|
355
|
+
threadId: input.threadId,
|
|
356
|
+
agentId: input.agentId,
|
|
357
|
+
});
|
|
327
358
|
break;
|
|
328
359
|
}
|
|
329
360
|
|
|
@@ -353,15 +384,35 @@ export async function createRuntimeAgentStreamResponse(
|
|
|
353
384
|
enqueueIfAttached(mappedEvent.event, mappedEvent.payload);
|
|
354
385
|
}
|
|
355
386
|
deps.sessionManager.completeRun(input.runId);
|
|
387
|
+
logger.info("Internal agent runtime stream finalized", {
|
|
388
|
+
runId: input.runId,
|
|
389
|
+
threadId: input.threadId,
|
|
390
|
+
agentId: input.agentId,
|
|
391
|
+
sawVisibleOutput: state.sawVisibleOutput,
|
|
392
|
+
sawTerminalError: state.sawTerminalError,
|
|
393
|
+
finishReason: state.metadata.finishReason,
|
|
394
|
+
});
|
|
356
395
|
} catch (error) {
|
|
357
396
|
if (error instanceof AgentRunCancelledError) {
|
|
358
397
|
deps.sessionManager.cancelRun(input.runId);
|
|
398
|
+
logger.warn("Internal agent runtime stream cancelled", {
|
|
399
|
+
runId: input.runId,
|
|
400
|
+
threadId: input.threadId,
|
|
401
|
+
agentId: input.agentId,
|
|
402
|
+
error: error.message,
|
|
403
|
+
});
|
|
359
404
|
enqueueIfAttached("RunError", {
|
|
360
405
|
code: "CANCELLED",
|
|
361
406
|
message: error.message,
|
|
362
407
|
});
|
|
363
408
|
} else {
|
|
364
409
|
deps.sessionManager.failRun(input.runId);
|
|
410
|
+
logger.error("Internal agent runtime stream failed", {
|
|
411
|
+
runId: input.runId,
|
|
412
|
+
threadId: input.threadId,
|
|
413
|
+
agentId: input.agentId,
|
|
414
|
+
error: error instanceof Error ? error.message : String(error),
|
|
415
|
+
});
|
|
365
416
|
enqueueIfAttached("RunError", {
|
|
366
417
|
code: "RUNTIME_ERROR",
|
|
367
418
|
message: error instanceof Error ? error.message : String(error),
|
|
@@ -372,10 +423,21 @@ export async function createRuntimeAgentStreamResponse(
|
|
|
372
423
|
if (clientAttached) {
|
|
373
424
|
controller.close();
|
|
374
425
|
}
|
|
426
|
+
logger.debug("Internal agent runtime stream response closed", {
|
|
427
|
+
runId: input.runId,
|
|
428
|
+
threadId: input.threadId,
|
|
429
|
+
agentId: input.agentId,
|
|
430
|
+
clientAttached,
|
|
431
|
+
});
|
|
375
432
|
}
|
|
376
433
|
},
|
|
377
434
|
cancel() {
|
|
378
435
|
clientAttached = false;
|
|
436
|
+
logger.info("Internal agent runtime client detached", {
|
|
437
|
+
runId: input.runId,
|
|
438
|
+
threadId: input.threadId,
|
|
439
|
+
agentId: input.agentId,
|
|
440
|
+
});
|
|
379
441
|
return Promise.resolve();
|
|
380
442
|
},
|
|
381
443
|
});
|
package/src/src/issues/mcp.ts
CHANGED
|
@@ -53,8 +53,11 @@ type IssuesCreateInput = z.infer<typeof issuesCreateInput>;
|
|
|
53
53
|
|
|
54
54
|
const issuesCreate: MCPTool<IssuesCreateInput, Issue> = {
|
|
55
55
|
name: "issues_create",
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
title: "Create Issue",
|
|
57
|
+
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },
|
|
58
|
+
description: "Use this when you need to create a new issue, task, or plan as a markdown file. " +
|
|
59
|
+
"Use prefix 'TASK' for small work items, 'PLAN' for proposals/RFCs, 'ISSUE' for bugs/features. " +
|
|
60
|
+
"Returns the created issue. Do not use for updating — use issues_update instead.",
|
|
58
61
|
inputSchema: issuesCreateInput,
|
|
59
62
|
execute: async (input) => {
|
|
60
63
|
const manager = getManager(input.projectDir);
|
|
@@ -81,7 +84,10 @@ type IssuesGetInput = z.infer<typeof issuesGetInput>;
|
|
|
81
84
|
|
|
82
85
|
const issuesGet: MCPTool<IssuesGetInput, Issue | null> = {
|
|
83
86
|
name: "issues_get",
|
|
84
|
-
|
|
87
|
+
title: "Get Issue",
|
|
88
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
89
|
+
description:
|
|
90
|
+
"Use this when you need to retrieve a specific issue by its ID. Returns the issue or null if not found. Do not use for listing — use issues_list instead.",
|
|
85
91
|
inputSchema: issuesGetInput,
|
|
86
92
|
execute: async (input) => {
|
|
87
93
|
const manager = getManager(input.projectDir);
|
|
@@ -107,8 +113,16 @@ type IssuesUpdateInput = z.infer<typeof issuesUpdateInput>;
|
|
|
107
113
|
|
|
108
114
|
const issuesUpdate: MCPTool<IssuesUpdateInput, Issue | null> = {
|
|
109
115
|
name: "issues_update",
|
|
110
|
-
|
|
111
|
-
|
|
116
|
+
title: "Update Issue",
|
|
117
|
+
annotations: {
|
|
118
|
+
readOnlyHint: false,
|
|
119
|
+
destructiveHint: false,
|
|
120
|
+
idempotentHint: true,
|
|
121
|
+
openWorldHint: false,
|
|
122
|
+
},
|
|
123
|
+
description:
|
|
124
|
+
"Use this when you need to modify an existing issue. Only provided fields are updated. " +
|
|
125
|
+
"Returns the updated issue or null if not found. Do not use to close — use issues_close instead.",
|
|
112
126
|
inputSchema: issuesUpdateInput,
|
|
113
127
|
execute: async (input) => {
|
|
114
128
|
const manager = getManager(input.projectDir);
|
|
@@ -153,8 +167,10 @@ interface IssuesListOutput {
|
|
|
153
167
|
|
|
154
168
|
const issuesList: MCPTool<IssuesListInput, IssuesListOutput> = {
|
|
155
169
|
name: "issues_list",
|
|
156
|
-
|
|
157
|
-
|
|
170
|
+
title: "List Issues",
|
|
171
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
172
|
+
description: "Use this when you need to find issues matching criteria. " +
|
|
173
|
+
"Returns matching issues and total count. Do not use to get a single known issue — use issues_get instead.",
|
|
158
174
|
inputSchema: issuesListInput,
|
|
159
175
|
execute: async (input) => {
|
|
160
176
|
const manager = getManager(input.projectDir);
|
|
@@ -183,7 +199,15 @@ type IssuesCloseInput = z.infer<typeof issuesCloseInput>;
|
|
|
183
199
|
|
|
184
200
|
const issuesClose: MCPTool<IssuesCloseInput, Issue | null> = {
|
|
185
201
|
name: "issues_close",
|
|
186
|
-
|
|
202
|
+
title: "Close Issue",
|
|
203
|
+
annotations: {
|
|
204
|
+
readOnlyHint: false,
|
|
205
|
+
destructiveHint: false,
|
|
206
|
+
idempotentHint: true,
|
|
207
|
+
openWorldHint: false,
|
|
208
|
+
},
|
|
209
|
+
description:
|
|
210
|
+
"Use this when you need to close an issue. Returns the updated issue or null if not found. Do not use to delete — use issues_delete instead.",
|
|
187
211
|
inputSchema: issuesCloseInput,
|
|
188
212
|
execute: async (input) => {
|
|
189
213
|
const manager = getManager(input.projectDir);
|
|
@@ -207,8 +231,17 @@ interface IssuesDeleteOutput {
|
|
|
207
231
|
|
|
208
232
|
const issuesDelete: MCPTool<IssuesDeleteInput, IssuesDeleteOutput> = {
|
|
209
233
|
name: "issues_delete",
|
|
210
|
-
|
|
211
|
-
|
|
234
|
+
title: "Delete Issue",
|
|
235
|
+
annotations: {
|
|
236
|
+
readOnlyHint: false,
|
|
237
|
+
destructiveHint: true,
|
|
238
|
+
idempotentHint: true,
|
|
239
|
+
openWorldHint: false,
|
|
240
|
+
},
|
|
241
|
+
description:
|
|
242
|
+
"Use this when you need to permanently delete an issue. Returns {deleted: true/false}. " +
|
|
243
|
+
"WARNING: this is irreversible and cannot be undone. Prefer issues_close unless permanent deletion is explicitly requested. " +
|
|
244
|
+
"Do not use to close — use issues_close instead.",
|
|
212
245
|
inputSchema: issuesDeleteInput,
|
|
213
246
|
execute: async (input) => {
|
|
214
247
|
const manager = getManager(input.projectDir);
|
package/src/src/mcp/index.ts
CHANGED
|
@@ -24,7 +24,13 @@
|
|
|
24
24
|
import "../../_dnt.polyfills.js";
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
export type {
|
|
27
|
+
export type {
|
|
28
|
+
MCPServerConfig,
|
|
29
|
+
MCPStats,
|
|
30
|
+
MCPTool,
|
|
31
|
+
ToolAnnotations,
|
|
32
|
+
ToolListEntry,
|
|
33
|
+
} from "./types.js";
|
|
28
34
|
|
|
29
35
|
export {
|
|
30
36
|
clearMCPRegistry,
|