feat(core): expose mcp prompts as commands

This commit is contained in:
Aiden Cline 2026-06-29 18:53:36 -05:00
commit 1245420e53
5 changed files with 43 additions and 0 deletions

View file

@ -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
}>
}

View file

@ -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,

View file

@ -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, "_")

View file

@ -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" })

View file

@ -5166,6 +5166,7 @@ export type CommandV2Info = {
description?: string
agent?: string
model?: ModelRef
source?: "command" | "mcp" | "skill"
subtask?: boolean
}