diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-mcp.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-mcp.tsx
index 9cfa30d4df9..546bff485cb 100644
--- a/packages/opencode/src/cli/cmd/tui/component/dialog-mcp.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/dialog-mcp.tsx
@@ -8,11 +8,14 @@ import { Keybind } from "@/util/keybind"
import { TextAttributes } from "@opentui/core"
import { useSDK } from "@tui/context/sdk"
-function Status(props: { enabled: boolean; loading: boolean }) {
+function Status(props: { enabled: boolean; loading: boolean; lazy: boolean }) {
const { theme } = useTheme()
if (props.loading) {
return ⋯ Loading
}
+ if (props.lazy) {
+ return ⦿ Lazy
+ }
if (props.enabled) {
return ✓ Enabled
}
@@ -26,10 +29,13 @@ export function DialogMcp() {
const [, setRef] = createSignal>()
const [loading, setLoading] = createSignal(null)
+ const mcpLazy = createMemo(() => sync.data.config.experimental?.mcp_lazy === true)
+
const options = createMemo(() => {
// Track sync data and loading state to trigger re-render when they change
const mcpData = sync.data.mcp
const loadingMcp = loading()
+ const lazy = mcpLazy()
return pipe(
mcpData ?? {},
@@ -39,7 +45,13 @@ export function DialogMcp() {
value: name,
title: name,
description: status.status === "failed" ? "failed" : status.status,
- footer: ,
+ footer: (
+
+ ),
category: undefined,
})),
)
diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts
index a231a530072..059b585c701 100644
--- a/packages/opencode/src/config/config.ts
+++ b/packages/opencode/src/config/config.ts
@@ -1182,6 +1182,12 @@ export namespace Config {
.positive()
.optional()
.describe("Timeout in milliseconds for model context protocol (MCP) requests"),
+ mcp_lazy: z
+ .boolean()
+ .optional()
+ .describe(
+ "Enable lazy loading of MCP tools. When enabled, MCP tools are not loaded into context automatically. Instead, use the mcp_search tool to discover and call MCP tools on-demand.",
+ ),
})
.optional(),
})
diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts
index ad7b6f1a91d..ef14802b14f 100644
--- a/packages/opencode/src/session/prompt.ts
+++ b/packages/opencode/src/session/prompt.ts
@@ -33,6 +33,7 @@ import { ulid } from "ulid"
import { spawn } from "child_process"
import { Command } from "../command"
import { $, fileURLToPath, pathToFileURL } from "bun"
+import { Config } from "../config/config"
import { ConfigMarkdown } from "../config/markdown"
import { SessionSummary } from "./summary"
import { NamedError } from "@opencode-ai/util/error"
@@ -614,7 +615,11 @@ export namespace SessionPrompt {
agent,
abort,
sessionID,
- system: [...(await SystemPrompt.environment(model)), ...(await InstructionPrompt.system())],
+ system: [
+ ...(await SystemPrompt.environment(model)),
+ ...(await SystemPrompt.mcpServers()),
+ ...(await InstructionPrompt.system()),
+ ],
messages: [
...MessageV2.toModelMessages(sessionMessages, model),
...(isLastStep
@@ -743,97 +748,102 @@ export namespace SessionPrompt {
})
}
- for (const [key, item] of Object.entries(await MCP.tools())) {
- const execute = item.execute
- if (!execute) continue
+ const cfg = await Config.get()
+ const mcpLazy = cfg.experimental?.mcp_lazy === true || (await ToolRegistry.hasMcpSearch())
- const transformed = ProviderTransform.schema(input.model, asSchema(item.inputSchema).jsonSchema)
- item.inputSchema = jsonSchema(transformed)
- // Wrap execute to add plugin hooks and format output
- item.execute = async (args, opts) => {
- const ctx = context(args, opts)
+ if (!mcpLazy) {
+ for (const [key, item] of Object.entries(await MCP.tools())) {
+ const execute = item.execute
+ if (!execute) continue
- await Plugin.trigger(
- "tool.execute.before",
- {
- tool: key,
- sessionID: ctx.sessionID,
- callID: opts.toolCallId,
- },
- {
- args,
- },
- )
+ const transformed = ProviderTransform.schema(input.model, asSchema(item.inputSchema).jsonSchema)
+ item.inputSchema = jsonSchema(transformed)
+ // Wrap execute to add plugin hooks and format output
+ item.execute = async (args, opts) => {
+ const ctx = context(args, opts)
- await ctx.ask({
- permission: key,
- metadata: {},
- patterns: ["*"],
- always: ["*"],
- })
+ await Plugin.trigger(
+ "tool.execute.before",
+ {
+ tool: key,
+ sessionID: ctx.sessionID,
+ callID: opts.toolCallId,
+ },
+ {
+ args,
+ },
+ )
- const result = await execute(args, opts)
+ await ctx.ask({
+ permission: key,
+ metadata: {},
+ patterns: ["*"],
+ always: ["*"],
+ })
- await Plugin.trigger(
- "tool.execute.after",
- {
- tool: key,
- sessionID: ctx.sessionID,
- callID: opts.toolCallId,
- },
- result,
- )
+ const result = await execute(args, opts)
- const textParts: string[] = []
- const attachments: MessageV2.FilePart[] = []
+ await Plugin.trigger(
+ "tool.execute.after",
+ {
+ tool: key,
+ sessionID: ctx.sessionID,
+ callID: opts.toolCallId,
+ },
+ result,
+ )
- for (const contentItem of result.content) {
- if (contentItem.type === "text") {
- textParts.push(contentItem.text)
- } else if (contentItem.type === "image") {
- attachments.push({
- id: Identifier.ascending("part"),
- sessionID: input.session.id,
- messageID: input.processor.message.id,
- type: "file",
- mime: contentItem.mimeType,
- url: `data:${contentItem.mimeType};base64,${contentItem.data}`,
- })
- } else if (contentItem.type === "resource") {
- const { resource } = contentItem
- if (resource.text) {
- textParts.push(resource.text)
- }
- if (resource.blob) {
+ const textParts: string[] = []
+ const attachments: MessageV2.FilePart[] = []
+
+ for (const contentItem of result.content) {
+ if (contentItem.type === "text") {
+ textParts.push(contentItem.text)
+ } else if (contentItem.type === "image") {
attachments.push({
id: Identifier.ascending("part"),
sessionID: input.session.id,
messageID: input.processor.message.id,
type: "file",
- mime: resource.mimeType ?? "application/octet-stream",
- url: `data:${resource.mimeType ?? "application/octet-stream"};base64,${resource.blob}`,
- filename: resource.uri,
+ mime: contentItem.mimeType,
+ url: `data:${contentItem.mimeType};base64,${contentItem.data}`,
})
+ } else if (contentItem.type === "resource") {
+ const { resource } = contentItem
+ if (resource.text) {
+ textParts.push(resource.text)
+ }
+ if (resource.blob) {
+ attachments.push({
+ id: Identifier.ascending("part"),
+ sessionID: input.session.id,
+ messageID: input.processor.message.id,
+ type: "file",
+ mime: resource.mimeType ?? "application/octet-stream",
+ url: `data:${resource.mimeType ?? "application/octet-stream"};base64,${resource.blob}`,
+ filename: resource.uri,
+ })
+ }
}
}
- }
- const truncated = await Truncate.output(textParts.join("\n\n"), {}, input.agent)
- const metadata = {
- ...(result.metadata ?? {}),
- truncated: truncated.truncated,
- ...(truncated.truncated && { outputPath: truncated.outputPath }),
- }
+ const truncated = await Truncate.output(textParts.join("\n\n"), {}, input.agent)
+ const metadata = {
+ ...(result.metadata ?? {}),
+ truncated: truncated.truncated,
+ ...(truncated.truncated && { outputPath: truncated.outputPath }),
+ }
- return {
- title: "",
- metadata,
- output: truncated.content,
- attachments,
- content: result.content, // directly return content to preserve ordering when outputting to model
+ return {
+ title: "",
+ metadata,
+ output: truncated.content,
+ attachments,
+ content: result.content, // directly return content to preserve ordering when outputting to model
+ }
}
+ tools[key] = item
}
- tools[key] = item
}
return tools
diff --git a/packages/opencode/src/session/system.ts b/packages/opencode/src/session/system.ts
index a61dd8cba55..c27a09ca90f 100644
--- a/packages/opencode/src/session/system.ts
+++ b/packages/opencode/src/session/system.ts
@@ -10,6 +10,8 @@ import PROMPT_GEMINI from "./prompt/gemini.txt"
import PROMPT_CODEX from "./prompt/codex_header.txt"
import PROMPT_TRINITY from "./prompt/trinity.txt"
import type { Provider } from "@/provider/provider"
+import { Config } from "../config/config"
+import { MCP } from "../mcp"
export namespace SystemPrompt {
export function instructions() {
@@ -26,6 +28,27 @@ export namespace SystemPrompt {
return [PROMPT_ANTHROPIC_WITHOUT_TODO]
}
+ export async function mcpServers() {
+ const config = await Config.get()
+ if (config.experimental?.mcp_lazy !== true) return []
+
+ const status = await MCP.status()
+ const servers = Object.entries(status)
+ .filter(([_, s]) => s.status === "connected")
+ .map(([name]) => name)
+
+ if (servers.length === 0) return []
+
+ return [
+ [
+ ``,
+ `Available MCP servers: ${servers.join(", ")}`,
+ `Use mcp_search tool to discover and call tools from these servers.`,
+ ``,
+ ].join("\n"),
+ ]
+ }
+
export async function environment(model: Provider.Model) {
const project = Instance.project
return [
diff --git a/packages/opencode/src/tool/mcp-search.ts b/packages/opencode/src/tool/mcp-search.ts
new file mode 100644
index 00000000000..fa85d768740
--- /dev/null
+++ b/packages/opencode/src/tool/mcp-search.ts
@@ -0,0 +1,188 @@
+import z from "zod"
+import { Tool } from "./tool"
+import { MCP } from "../mcp"
+import { Plugin } from "../plugin"
+import DESCRIPTION from "./mcp-search.txt"
+
+function sanitize(name: string) {
+ return name.replace(/[^a-zA-Z0-9_-]/g, "_")
+}
+
+function extractSchema(input: unknown): Record | undefined {
+ if (!input || typeof input !== "object") return undefined
+ if ("jsonSchema" in input) return (input as { jsonSchema: Record }).jsonSchema
+ return input as Record
+}
+
+function formatSchema(schema: Record, indent = 0): string {
+ const properties = schema.properties as Record> | undefined
+ const required = new Set((schema.required as string[]) ?? [])
+ if (!properties || Object.keys(properties).length === 0) return " ".repeat(indent) + "No parameters required"
+
+ const pad = " ".repeat(indent)
+ return Object.entries(properties)
+ .flatMap(([name, prop]) => {
+ const lines = [`${pad}- **${name}**${required.has(name) ? " (required)" : " (optional)"}: ${prop.type ?? "any"}`]
+ if (prop.description) lines.push(`${pad} ${prop.description}`)
+ if (prop.type === "object" && prop.properties) lines.push(formatSchema(prop, indent + 1))
+ if (prop.enum) lines.push(`${pad} Allowed values: ${(prop.enum as string[]).join(", ")}`)
+ return lines
+ })
+ .join("\n")
+}
+
+const parameters = z.object({
+ operation: z.enum(["list", "search", "describe", "call"]).describe("Operation to perform"),
+ query: z.string().optional().describe("Search query (for 'search')"),
+ server: z.string().optional().describe("MCP server name (for 'describe'/'call')"),
+ tool: z.string().optional().describe("Tool name (for 'describe'/'call')"),
+ args: z.record(z.string(), z.any()).optional().describe("Tool arguments (for 'call')"),
+})
+
+async function getConnectedServers() {
+ const [status, allTools] = await Promise.all([MCP.status(), MCP.tools()])
+ const toolEntries = Object.entries(allTools)
+
+ return Object.entries(status)
+ .filter(([, s]) => s.status === "connected")
+ .map(([name]) => {
+ const prefix = sanitize(name) + "_"
+ const tools = toolEntries
+ .filter(([key]) => key.startsWith(prefix))
+ .map(([key, tool]) => ({ name: key.slice(prefix.length), description: tool.description }))
+ return { name, tools }
+ })
+}
+
+async function resolveTool(server: string, tool: string) {
+ const [status, allTools] = await Promise.all([MCP.status(), MCP.tools()])
+
+ if (status[server]?.status !== "connected") throw new Error(`MCP server "${server}" is not connected`)
+
+ const prefix = sanitize(server)
+ const key = `${prefix}_${sanitize(tool)}`
+ const mcpTool = allTools[key]
+
+ if (mcpTool) return { key, mcpTool }
+
+ const available = Object.keys(allTools)
+ .filter((k) => k.startsWith(prefix + "_"))
+ .map((k) => k.slice(prefix.length + 1))
+ throw new Error(`Tool "${tool}" not found on "${server}". Available: ${available.join(", ") || "none"}`)
+}
+
+async function list() {
+ const servers = await getConnectedServers()
+ if (servers.length === 0) return { title: "No MCP servers", output: "No connected MCP servers.", metadata: {} }
+
+ const output = servers
+ .map((s) => `## ${s.name}\n${s.tools.map((t) => `- ${t.name}: ${t.description ?? "No description"}`).join("\n")}`)
+ .join("\n\n")
+
+ return { title: `${servers.length} MCP servers`, output, metadata: { servers: servers.length } }
+}
+
+async function search(query?: string) {
+ const servers = await getConnectedServers()
+ const q = query?.toLowerCase() ?? ""
+
+ const matches = servers.flatMap((s) => {
+ if (q && !s.name.toLowerCase().includes(q)) return []
+ return s.tools.map((t) => ({ server: s.name, ...t }))
+ })
+
+ if (matches.length === 0) {
+ return {
+ title: "No matches",
+ output: query ? `No tools matching "${query}"` : "No MCP tools available",
+ metadata: {},
+ }
+ }
+
+ const output = matches.map((m) => `- ${m.server}/${m.name}: ${m.description ?? "No description"}`).join("\n")
+ return {
+ title: `${matches.length} tools found`,
+ output: `Found ${matches.length} tool(s)${query ? ` matching "${query}"` : ""}:\n\n${output}\n\nYou MUST use describe before calling any of these tools.`,
+ metadata: { count: matches.length },
+ }
+}
+
+async function describe(server: string, tool: string) {
+ const { mcpTool } = await resolveTool(server, tool)
+ const schema = extractSchema(mcpTool.inputSchema)
+
+ return {
+ title: `${server}/${tool}`,
+ output: [
+ `## ${server}/${tool}`,
+ "",
+ `**Description:** ${mcpTool.description ?? "No description"}`,
+ "",
+ "**Parameters:**",
+ schema ? formatSchema(schema) : "No parameters required",
+ "",
+ "**Example:**",
+ "```",
+ `mcp_search(operation: "call", server: "${server}", tool: "${tool}", args: { ... })`,
+ "```",
+ ].join("\n"),
+ metadata: { server, tool },
+ }
+}
+
+async function call(server: string, tool: string, args: Record, ctx: Tool.Context) {
+ const { key, mcpTool } = await resolveTool(server, tool)
+ const schema = extractSchema(mcpTool.inputSchema)
+ const required = (schema?.required as string[]) ?? []
+ const missing = required.filter((r) => !(r in args))
+
+ if (missing.length > 0) {
+ return {
+ title: "Arguments required",
+ output: [
+ `Tool "${tool}" requires arguments.`,
+ "",
+ `**Missing:** ${missing.join(", ")}`,
+ "",
+ `**Tool:** ${server}/${tool}`,
+ `**Description:** ${mcpTool.description ?? "No description"}`,
+ "",
+ "**Parameters:**",
+ schema ? formatSchema(schema) : "No schema available",
+ "",
+ "**Example:**",
+ `mcp_search(operation: "call", server: "${server}", tool: "${tool}", args: { ${required.map((r) => `"${r}": ...`).join(", ")} })`,
+ ].join("\n"),
+ metadata: { server, tool, missing },
+ }
+ }
+
+ await ctx.ask({ permission: key, metadata: {}, patterns: ["*"], always: ["*"] })
+ await Plugin.trigger("tool.execute.before", { tool: key, sessionID: ctx.sessionID, callID: ctx.callID }, { args })
+
+ const result = await mcpTool.execute!(args, { toolCallId: ctx.callID ?? "", abortSignal: ctx.abort, messages: [] })
+
+ await Plugin.trigger("tool.execute.after", { tool: key, sessionID: ctx.sessionID, callID: ctx.callID }, result)
+
+ const parts: string[] = []
+ for (const c of result.content) {
+ if (c.type === "text") parts.push(c.text)
+ else if (c.type === "image") parts.push(`[Image: ${c.mimeType}, ${c.data.length} bytes]`)
+ else if (c.type === "resource") parts.push(c.resource.text ?? `[Resource: ${c.resource.uri}]`)
+ }
+ const output = parts.join("\n\n")
+
+ return { title: `${server}/${tool}`, output: output || "Success (no output)", metadata: { server, tool } }
+}
+
+export const McpSearchTool = Tool.define>("mcp_search", {
+ description: DESCRIPTION,
+ parameters,
+ async execute(params, ctx) {
+ if (params.operation === "list") return list()
+ if (params.operation === "search") return search(params.query)
+ if (!params.server || !params.tool) throw new Error("Both 'server' and 'tool' parameters are required")
+ if (params.operation === "describe") return describe(params.server, params.tool)
+ return call(params.server, params.tool, params.args ?? {}, ctx)
+ },
+})
diff --git a/packages/opencode/src/tool/mcp-search.txt b/packages/opencode/src/tool/mcp-search.txt
new file mode 100644
index 00000000000..35137350a7c
--- /dev/null
+++ b/packages/opencode/src/tool/mcp-search.txt
@@ -0,0 +1,13 @@
+Search and call MCP server tools.
+
+Operations:
+- "list": List all MCP servers and their tools
+- "search": Find tools by server name
+- "describe": Get tool's parameter schema
+- "call": Execute a tool
+
+Usage notes:
+ - The "search" query MUST be a server name (e.g., "playwright", "context7"). It only matches against server names and returns all tools from matching servers.
+ - You MUST call "describe" before "call" to check the tool's parameters.
+ - You SHOULD NOT call a tool without first checking its parameters.
+ - Examples: search "playwright" to find all browser automation tools, search "context7" to find documentation lookup tools.
diff --git a/packages/opencode/src/tool/registry.ts b/packages/opencode/src/tool/registry.ts
index 5ed5a879b48..e3d796946a1 100644
--- a/packages/opencode/src/tool/registry.ts
+++ b/packages/opencode/src/tool/registry.ts
@@ -27,6 +27,7 @@ import { LspTool } from "./lsp"
import { Truncate } from "./truncation"
import { PlanExitTool, PlanEnterTool } from "./plan"
import { ApplyPatchTool } from "./apply_patch"
+import { McpSearchTool } from "./mcp-search"
export namespace ToolRegistry {
const log = Log.create({ service: "tool.registry" })
@@ -115,10 +116,16 @@ export namespace ToolRegistry {
...(Flag.OPENCODE_EXPERIMENTAL_LSP_TOOL ? [LspTool] : []),
...(config.experimental?.batch_tool === true ? [BatchTool] : []),
...(Flag.OPENCODE_EXPERIMENTAL_PLAN_MODE && Flag.OPENCODE_CLIENT === "cli" ? [PlanExitTool, PlanEnterTool] : []),
+ ...(config.experimental?.mcp_lazy === true ? [McpSearchTool] : []),
...custom,
]
}
+ export async function hasMcpSearch(): Promise {
+ const tools = await all()
+ return tools.some((t) => t.id === "mcp_search")
+ }
+
export async function ids() {
return all().then((x) => x.map((t) => t.id))
}
diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts
index 9543e5b5796..60dbbc1d629 100644
--- a/packages/sdk/js/src/v2/gen/types.gen.ts
+++ b/packages/sdk/js/src/v2/gen/types.gen.ts
@@ -509,6 +509,75 @@ export type EventMessagePartRemoved = {
}
}
+export type EventTuiPromptAppend = {
+ type: "tui.prompt.append"
+ properties: {
+ text: string
+ }
+}
+
+export type EventTuiCommandExecute = {
+ type: "tui.command.execute"
+ properties: {
+ command:
+ | "session.list"
+ | "session.new"
+ | "session.share"
+ | "session.interrupt"
+ | "session.compact"
+ | "session.page.up"
+ | "session.page.down"
+ | "session.line.up"
+ | "session.line.down"
+ | "session.half.page.up"
+ | "session.half.page.down"
+ | "session.first"
+ | "session.last"
+ | "prompt.clear"
+ | "prompt.submit"
+ | "agent.cycle"
+ | string
+ }
+}
+
+export type EventTuiToastShow = {
+ type: "tui.toast.show"
+ properties: {
+ title?: string
+ message: string
+ variant: "info" | "success" | "warning" | "error"
+ /**
+ * Duration in milliseconds
+ */
+ duration?: number
+ }
+}
+
+export type EventTuiSessionSelect = {
+ type: "tui.session.select"
+ properties: {
+ /**
+ * Session ID to navigate to
+ */
+ sessionID: string
+ }
+}
+
+export type EventMcpToolsChanged = {
+ type: "mcp.tools.changed"
+ properties: {
+ server: string
+ }
+}
+
+export type EventMcpBrowserOpenFailed = {
+ type: "mcp.browser.open.failed"
+ properties: {
+ mcpName: string
+ url: string
+ }
+}
+
export type PermissionRequest = {
id: string
sessionID: string
@@ -680,75 +749,6 @@ export type EventTodoUpdated = {
}
}
-export type EventTuiPromptAppend = {
- type: "tui.prompt.append"
- properties: {
- text: string
- }
-}
-
-export type EventTuiCommandExecute = {
- type: "tui.command.execute"
- properties: {
- command:
- | "session.list"
- | "session.new"
- | "session.share"
- | "session.interrupt"
- | "session.compact"
- | "session.page.up"
- | "session.page.down"
- | "session.line.up"
- | "session.line.down"
- | "session.half.page.up"
- | "session.half.page.down"
- | "session.first"
- | "session.last"
- | "prompt.clear"
- | "prompt.submit"
- | "agent.cycle"
- | string
- }
-}
-
-export type EventTuiToastShow = {
- type: "tui.toast.show"
- properties: {
- title?: string
- message: string
- variant: "info" | "success" | "warning" | "error"
- /**
- * Duration in milliseconds
- */
- duration?: number
- }
-}
-
-export type EventTuiSessionSelect = {
- type: "tui.session.select"
- properties: {
- /**
- * Session ID to navigate to
- */
- sessionID: string
- }
-}
-
-export type EventMcpToolsChanged = {
- type: "mcp.tools.changed"
- properties: {
- server: string
- }
-}
-
-export type EventMcpBrowserOpenFailed = {
- type: "mcp.browser.open.failed"
- properties: {
- mcpName: string
- url: string
- }
-}
-
export type EventCommandExecuted = {
type: "command.executed"
properties: {
@@ -919,6 +919,12 @@ export type Event =
| EventMessageRemoved
| EventMessagePartUpdated
| EventMessagePartRemoved
+ | EventTuiPromptAppend
+ | EventTuiCommandExecute
+ | EventTuiToastShow
+ | EventTuiSessionSelect
+ | EventMcpToolsChanged
+ | EventMcpBrowserOpenFailed
| EventPermissionAsked
| EventPermissionReplied
| EventSessionStatus
@@ -929,12 +935,6 @@ export type Event =
| EventSessionCompacted
| EventFileWatcherUpdated
| EventTodoUpdated
- | EventTuiPromptAppend
- | EventTuiCommandExecute
- | EventTuiToastShow
- | EventTuiSessionSelect
- | EventMcpToolsChanged
- | EventMcpBrowserOpenFailed
| EventCommandExecuted
| EventSessionCreated
| EventSessionUpdated
@@ -1845,6 +1845,10 @@ export type Config = {
* Timeout in milliseconds for model context protocol (MCP) requests
*/
mcp_timeout?: number
+ /**
+ * Enable lazy loading of MCP tools. When enabled, MCP tools are not loaded into context automatically. Instead, use the mcp_search tool to discover and call MCP tools on-demand.
+ */
+ mcp_lazy?: boolean
}
}
diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json
index 18022a3384d..ed459e89682 100644
--- a/packages/sdk/openapi.json
+++ b/packages/sdk/openapi.json
@@ -7284,6 +7284,162 @@
},
"required": ["type", "properties"]
},
+ "Event.tui.prompt.append": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "tui.prompt.append"
+ },
+ "properties": {
+ "type": "object",
+ "properties": {
+ "text": {
+ "type": "string"
+ }
+ },
+ "required": ["text"]
+ }
+ },
+ "required": ["type", "properties"]
+ },
+ "Event.tui.command.execute": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "tui.command.execute"
+ },
+ "properties": {
+ "type": "object",
+ "properties": {
+ "command": {
+ "anyOf": [
+ {
+ "type": "string",
+ "enum": [
+ "session.list",
+ "session.new",
+ "session.share",
+ "session.interrupt",
+ "session.compact",
+ "session.page.up",
+ "session.page.down",
+ "session.line.up",
+ "session.line.down",
+ "session.half.page.up",
+ "session.half.page.down",
+ "session.first",
+ "session.last",
+ "prompt.clear",
+ "prompt.submit",
+ "agent.cycle"
+ ]
+ },
+ {
+ "type": "string"
+ }
+ ]
+ }
+ },
+ "required": ["command"]
+ }
+ },
+ "required": ["type", "properties"]
+ },
+ "Event.tui.toast.show": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "tui.toast.show"
+ },
+ "properties": {
+ "type": "object",
+ "properties": {
+ "title": {
+ "type": "string"
+ },
+ "message": {
+ "type": "string"
+ },
+ "variant": {
+ "type": "string",
+ "enum": ["info", "success", "warning", "error"]
+ },
+ "duration": {
+ "description": "Duration in milliseconds",
+ "default": 5000,
+ "type": "number"
+ }
+ },
+ "required": ["message", "variant"]
+ }
+ },
+ "required": ["type", "properties"]
+ },
+ "Event.tui.session.select": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "tui.session.select"
+ },
+ "properties": {
+ "type": "object",
+ "properties": {
+ "sessionID": {
+ "description": "Session ID to navigate to",
+ "type": "string",
+ "pattern": "^ses"
+ }
+ },
+ "required": ["sessionID"]
+ }
+ },
+ "required": ["type", "properties"]
+ },
+ "Event.mcp.tools.changed": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "mcp.tools.changed"
+ },
+ "properties": {
+ "type": "object",
+ "properties": {
+ "server": {
+ "type": "string"
+ }
+ },
+ "required": ["server"]
+ }
+ },
+ "required": ["type", "properties"]
+ },
+ "Event.mcp.browser.open.failed": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "mcp.browser.open.failed"
+ },
+ "properties": {
+ "type": "object",
+ "properties": {
+ "mcpName": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string"
+ }
+ },
+ "required": ["mcpName", "url"]
+ }
+ },
+ "required": ["type", "properties"]
+ },
"PermissionRequest": {
"type": "object",
"properties": {
@@ -7701,162 +7857,6 @@
},
"required": ["type", "properties"]
},
- "Event.tui.prompt.append": {
- "type": "object",
- "properties": {
- "type": {
- "type": "string",
- "const": "tui.prompt.append"
- },
- "properties": {
- "type": "object",
- "properties": {
- "text": {
- "type": "string"
- }
- },
- "required": ["text"]
- }
- },
- "required": ["type", "properties"]
- },
- "Event.tui.command.execute": {
- "type": "object",
- "properties": {
- "type": {
- "type": "string",
- "const": "tui.command.execute"
- },
- "properties": {
- "type": "object",
- "properties": {
- "command": {
- "anyOf": [
- {
- "type": "string",
- "enum": [
- "session.list",
- "session.new",
- "session.share",
- "session.interrupt",
- "session.compact",
- "session.page.up",
- "session.page.down",
- "session.line.up",
- "session.line.down",
- "session.half.page.up",
- "session.half.page.down",
- "session.first",
- "session.last",
- "prompt.clear",
- "prompt.submit",
- "agent.cycle"
- ]
- },
- {
- "type": "string"
- }
- ]
- }
- },
- "required": ["command"]
- }
- },
- "required": ["type", "properties"]
- },
- "Event.tui.toast.show": {
- "type": "object",
- "properties": {
- "type": {
- "type": "string",
- "const": "tui.toast.show"
- },
- "properties": {
- "type": "object",
- "properties": {
- "title": {
- "type": "string"
- },
- "message": {
- "type": "string"
- },
- "variant": {
- "type": "string",
- "enum": ["info", "success", "warning", "error"]
- },
- "duration": {
- "description": "Duration in milliseconds",
- "default": 5000,
- "type": "number"
- }
- },
- "required": ["message", "variant"]
- }
- },
- "required": ["type", "properties"]
- },
- "Event.tui.session.select": {
- "type": "object",
- "properties": {
- "type": {
- "type": "string",
- "const": "tui.session.select"
- },
- "properties": {
- "type": "object",
- "properties": {
- "sessionID": {
- "description": "Session ID to navigate to",
- "type": "string",
- "pattern": "^ses"
- }
- },
- "required": ["sessionID"]
- }
- },
- "required": ["type", "properties"]
- },
- "Event.mcp.tools.changed": {
- "type": "object",
- "properties": {
- "type": {
- "type": "string",
- "const": "mcp.tools.changed"
- },
- "properties": {
- "type": "object",
- "properties": {
- "server": {
- "type": "string"
- }
- },
- "required": ["server"]
- }
- },
- "required": ["type", "properties"]
- },
- "Event.mcp.browser.open.failed": {
- "type": "object",
- "properties": {
- "type": {
- "type": "string",
- "const": "mcp.browser.open.failed"
- },
- "properties": {
- "type": "object",
- "properties": {
- "mcpName": {
- "type": "string"
- },
- "url": {
- "type": "string"
- }
- },
- "required": ["mcpName", "url"]
- }
- },
- "required": ["type", "properties"]
- },
"Event.command.executed": {
"type": "object",
"properties": {
@@ -8346,52 +8346,52 @@
"$ref": "#/components/schemas/Event.message.part.removed"
},
{
- "$ref": "#/components/schemas/Event.permission.asked"
+ "$ref": "#/components/schemas/Event.tui.prompt.append"
},
{
- "$ref": "#/components/schemas/Event.permission.replied"
+ "$ref": "#/components/schemas/Event.tui.command.execute"
},
{
- "$ref": "#/components/schemas/Event.session.status"
+ "$ref": "#/components/schemas/Event.tui.toast.show"
},
{
- "$ref": "#/components/schemas/Event.session.idle"
+ "$ref": "#/components/schemas/Event.tui.session.select"
},
{
- "$ref": "#/components/schemas/Event.question.asked"
+ "$ref": "#/components/schemas/Event.mcp.tools.changed"
},
{
- "$ref": "#/components/schemas/Event.question.replied"
+ "$ref": "#/components/schemas/Event.mcp.browser.open.failed"
},
{
- "$ref": "#/components/schemas/Event.question.rejected"
+ "$ref": "#/components/schemas/Event.permission.asked"
},
{
- "$ref": "#/components/schemas/Event.session.compacted"
+ "$ref": "#/components/schemas/Event.permission.replied"
},
{
- "$ref": "#/components/schemas/Event.file.watcher.updated"
+ "$ref": "#/components/schemas/Event.session.status"
},
{
- "$ref": "#/components/schemas/Event.todo.updated"
+ "$ref": "#/components/schemas/Event.session.idle"
},
{
- "$ref": "#/components/schemas/Event.tui.prompt.append"
+ "$ref": "#/components/schemas/Event.question.asked"
},
{
- "$ref": "#/components/schemas/Event.tui.command.execute"
+ "$ref": "#/components/schemas/Event.question.replied"
},
{
- "$ref": "#/components/schemas/Event.tui.toast.show"
+ "$ref": "#/components/schemas/Event.question.rejected"
},
{
- "$ref": "#/components/schemas/Event.tui.session.select"
+ "$ref": "#/components/schemas/Event.session.compacted"
},
{
- "$ref": "#/components/schemas/Event.mcp.tools.changed"
+ "$ref": "#/components/schemas/Event.file.watcher.updated"
},
{
- "$ref": "#/components/schemas/Event.mcp.browser.open.failed"
+ "$ref": "#/components/schemas/Event.todo.updated"
},
{
"$ref": "#/components/schemas/Event.command.executed"
@@ -9927,6 +9927,10 @@
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
+ },
+ "mcp_lazy": {
+ "description": "Enable lazy loading of MCP tools. When enabled, MCP tools are not loaded into context automatically. Instead, use the mcp_search tool to discover and call MCP tools on-demand.",
+ "type": "boolean"
}
}
}