From 1245420e5316e4d85111060ed1b8feea3fdeede2 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Mon, 29 Jun 2026 18:53:36 -0500 Subject: [PATCH] feat(core): expose mcp prompts as commands --- packages/client/src/generated/types.ts | 1 + packages/core/src/plugin/internal.ts | 7 ++++++ packages/core/src/plugin/mcp-command.ts | 33 +++++++++++++++++++++++++ packages/schema/src/command.ts | 1 + packages/sdk/js/src/v2/gen/types.gen.ts | 1 + 5 files changed, 43 insertions(+) create mode 100644 packages/core/src/plugin/mcp-command.ts diff --git a/packages/client/src/generated/types.ts b/packages/client/src/generated/types.ts index cffc6fac5c..cdf420977e 100644 --- a/packages/client/src/generated/types.ts +++ b/packages/client/src/generated/types.ts @@ -2564,6 +2564,7 @@ export type CommandsListOutput = { readonly description?: string readonly agent?: string readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string } + readonly source?: "command" | "mcp" | "skill" readonly subtask?: boolean }> } diff --git a/packages/core/src/plugin/internal.ts b/packages/core/src/plugin/internal.ts index d1262372a9..c9c5fec29e 100644 --- a/packages/core/src/plugin/internal.ts +++ b/packages/core/src/plugin/internal.ts @@ -20,6 +20,7 @@ import { FSUtil } from "../fs-util" import { Global } from "../global" import { Integration } from "../integration" import { Location } from "../location" +import { MCP } from "../mcp" import { ModelsDev } from "../models-dev" import { Npm } from "../npm" import { PluginV2 } from "../plugin" @@ -29,6 +30,7 @@ import { State } from "../state" import { FetchHttpClient, HttpClient } from "effect/unstable/http" import { AgentPlugin } from "./agent" import { CommandPlugin } from "./command" +import { McpCommandPlugin } from "./mcp-command" import { ModelsDevPlugin } from "./models-dev" import { ProviderPlugins } from "./provider" import { SdkPlugins } from "./sdk" @@ -47,6 +49,7 @@ export type Requirements = | HttpClient.HttpClient | Integration.Service | Location.Service + | MCP.Service | ModelsDev.Service | Npm.Service | Reference.Service @@ -71,6 +74,7 @@ const layer = Layer.effectDiscard( const agents = yield* AgentV2.Service const config = yield* Config.Service const location = yield* Location.Service + const mcp = yield* MCP.Service const modelsDev = yield* ModelsDev.Service const npm = yield* Npm.Service const events = yield* EventV2.Service @@ -93,6 +97,7 @@ const layer = Layer.effectDiscard( Effect.provideService(AgentV2.Service, agents), Effect.provideService(Config.Service, config), Effect.provideService(Location.Service, location), + Effect.provideService(MCP.Service, mcp), Effect.provideService(ModelsDev.Service, modelsDev), Effect.provideService(Npm.Service, npm), Effect.provideService(EventV2.Service, events), @@ -116,6 +121,7 @@ const layer = Layer.effectDiscard( yield* add(ModelsDevPlugin) yield* add(ConfigAgentPlugin.Plugin) yield* add(ConfigCommandPlugin.Plugin) + yield* add(McpCommandPlugin.Plugin) yield* add(ConfigSkillPlugin.Plugin) for (const item of ProviderPlugins) yield* add(item) yield* add(ConfigExternalPlugin.Plugin) @@ -146,6 +152,7 @@ export const node = makeLocationNode({ AgentV2.node, Config.node, Location.node, + MCP.node, ModelsDev.node, Npm.node, EventV2.node, diff --git a/packages/core/src/plugin/mcp-command.ts b/packages/core/src/plugin/mcp-command.ts new file mode 100644 index 0000000000..30c7a4fd8f --- /dev/null +++ b/packages/core/src/plugin/mcp-command.ts @@ -0,0 +1,33 @@ +export * as McpCommandPlugin from "./mcp-command" + +import { McpEvent } from "@opencode-ai/schema/mcp-event" +import { Effect, Stream } from "effect" +import { EventV2 } from "../event" +import { MCP } from "../mcp" +import { define } from "./internal" + +export const Plugin = define({ + id: "mcp-command", + effect: Effect.fn(function* (ctx) { + const mcp = yield* MCP.Service + const events = yield* EventV2.Service + yield* ctx.command.transform( + Effect.fn(function* (draft) { + for (const prompt of yield* mcp.prompts()) { + draft.update(commandName(prompt), (command) => { + command.template = "" + command.description = prompt.description + command.source = "mcp" + }) + } + }), + ) + yield* events + .subscribe(McpEvent.PromptsChanged) + .pipe(Stream.runForEach(() => ctx.command.reload()), Effect.forkScoped({ startImmediately: true })) + }), +}) + +const commandName = (prompt: MCP.Prompt) => `${sanitize(prompt.server)}:${sanitize(prompt.name)}` + +const sanitize = (value: string) => value.replace(/[^a-zA-Z0-9_-]/g, "_") diff --git a/packages/schema/src/command.ts b/packages/schema/src/command.ts index 2bdf3a95c5..d689cc7a5b 100644 --- a/packages/schema/src/command.ts +++ b/packages/schema/src/command.ts @@ -11,5 +11,6 @@ export const Info = Schema.Struct({ description: Schema.String.pipe(optional), agent: Schema.String.pipe(optional), model: Model.Ref.pipe(optional), + source: Schema.Literals(["command", "mcp", "skill"]).pipe(optional), subtask: Schema.Boolean.pipe(optional), }).annotate({ identifier: "CommandV2.Info" }) diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 64aec23f62..f9ca5278ab 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -5166,6 +5166,7 @@ export type CommandV2Info = { description?: string agent?: string model?: ModelRef + source?: "command" | "mcp" | "skill" subtask?: boolean }