diff --git a/packages/core/src/mcp/client.ts b/packages/core/src/mcp/client.ts index b8e9b10767..41ef8f030e 100644 --- a/packages/core/src/mcp/client.ts +++ b/packages/core/src/mcp/client.ts @@ -13,6 +13,8 @@ import { ListToolsResultSchema, type LoggingMessageNotification, LoggingMessageNotificationSchema, + PromptListChangedNotificationSchema, + ResourceListChangedNotificationSchema, ToolListChangedNotificationSchema, ToolSchema, } from "@modelcontextprotocol/sdk/types.js" @@ -79,7 +81,7 @@ export interface CallToolResult { export interface LogMessage { readonly level: LoggingMessageNotification["params"]["level"] - readonly logger: LoggingMessageNotification["params"]["logger"] + readonly logger?: LoggingMessageNotification["params"]["logger"] readonly data: LoggingMessageNotification["params"]["data"] } @@ -106,6 +108,10 @@ export interface Connection { readonly onLog: (callback: (message: LogMessage) => void) => void /** Registers a callback fired when the server announces its tool list changed; no-op if unsupported. */ readonly onToolsChanged: (callback: () => void) => void + /** Registers a callback fired when the server announces its prompt list changed; no-op if unsupported. */ + readonly onPromptsChanged: (callback: () => void) => void + /** Registers a callback fired when the server announces its resource list changed; no-op if unsupported. */ + readonly onResourcesChanged: (callback: () => void) => void } /** Connects an MCP server; closing the calling scope tears down the transport and any spawned process. */ @@ -267,6 +273,14 @@ export const connect = Effect.fnUntraced(function* ( if (!client.getServerCapabilities()?.tools?.listChanged) return client.setNotificationHandler(ToolListChangedNotificationSchema, async () => callback()) }, + onPromptsChanged: (callback) => { + if (!client.getServerCapabilities()?.prompts?.listChanged) return + client.setNotificationHandler(PromptListChangedNotificationSchema, async () => callback()) + }, + onResourcesChanged: (callback) => { + if (!client.getServerCapabilities()?.resources?.listChanged) return + client.setNotificationHandler(ResourceListChangedNotificationSchema, async () => callback()) + }, } satisfies Connection } diff --git a/packages/core/src/mcp/index.ts b/packages/core/src/mcp/index.ts index 2f21d075aa..39ecc0ccd1 100644 --- a/packages/core/src/mcp/index.ts +++ b/packages/core/src/mcp/index.ts @@ -264,7 +264,16 @@ export const layer = Layer.effect( entry.client = undefined entry.tools = undefined entry.status = { status: "failed", error: "Connection closed" } - fork(events.publish(McpEvent.ToolsChanged, { server: name }).pipe(Effect.ignore)) + fork( + Effect.all( + [ + events.publish(McpEvent.ToolsChanged, { server: name }), + events.publish(McpEvent.PromptsChanged, { server: name }), + events.publish(McpEvent.ResourcesChanged, { server: name }), + ], + { discard: true }, + ).pipe(Effect.ignore), + ) }) connection.onLog((message) => fork(serverLog(name, message).pipe(Effect.ignore))) connection.onToolsChanged(() => { @@ -275,6 +284,12 @@ export const layer = Layer.effect( ), ) }) + connection.onPromptsChanged(() => { + fork(events.publish(McpEvent.PromptsChanged, { server: name }).pipe(Effect.ignore)) + }) + connection.onResourcesChanged(() => { + fork(events.publish(McpEvent.ResourcesChanged, { server: name }).pipe(Effect.ignore)) + }) } const serverLog = (server: ServerName, message: MCPClient.LogMessage) => { diff --git a/packages/schema/src/mcp-event.ts b/packages/schema/src/mcp-event.ts index 1d050df927..ee2307e88e 100644 --- a/packages/schema/src/mcp-event.ts +++ b/packages/schema/src/mcp-event.ts @@ -10,6 +10,20 @@ export const ToolsChanged = Event.define({ }, }) +export const PromptsChanged = Event.define({ + type: "mcp.prompts.changed", + schema: { + server: Schema.String, + }, +}) + +export const ResourcesChanged = Event.define({ + type: "mcp.resources.changed", + schema: { + server: Schema.String, + }, +}) + export const BrowserOpenFailed = Event.define({ type: "mcp.browser.open.failed", schema: { @@ -18,4 +32,4 @@ export const BrowserOpenFailed = Event.define({ }, }) -export const Definitions = Event.inventory(ToolsChanged, BrowserOpenFailed) +export const Definitions = Event.inventory(ToolsChanged, PromptsChanged, ResourcesChanged, BrowserOpenFailed) diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index 80aec1244c..8e5bc60c4d 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -279,6 +279,8 @@ import type { V2FsListResponses, V2FsReadErrors, V2FsReadResponses, + V2GenerateTextErrors, + V2GenerateTextResponses, V2HealthGetErrors, V2HealthGetResponses, V2IntegrationAttemptCancelErrors, @@ -5944,6 +5946,48 @@ export class Model extends HeyApiClient { } } +export class Generate extends HeyApiClient { + /** + * Generate text + * + * Run one stateless model generation at the requested location and return the assistant text. Uses the location's default model when none is specified. + */ + public text( + parameters?: { + location?: { + directory?: string + workspace?: string + } + prompt?: string + model?: ModelRef + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "query", key: "location" }, + { in: "body", key: "prompt" }, + { in: "body", key: "model" }, + ], + }, + ], + ) + return (options?.client ?? this.client).post({ + url: "/api/generate", + ...options, + ...params, + headers: { + "Content-Type": "application/json", + ...options?.headers, + ...params.headers, + }, + }) + } +} + export class Provider2 extends HeyApiClient { /** * List providers @@ -7233,6 +7277,11 @@ export class V2 extends HeyApiClient { return (this._model ??= new Model({ client: this.client })) } + private _generate?: Generate + get generate(): Generate { + return (this._generate ??= new Generate({ client: this.client })) + } + private _provider?: Provider2 get provider(): Provider2 { return (this._provider ??= new Provider2({ client: this.client })) diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 9259dad941..64aec23f62 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -80,6 +80,8 @@ export type Event = | EventTuiToastShow2 | EventTuiSessionSelect2 | EventMcpToolsChanged + | EventMcpPromptsChanged + | EventMcpResourcesChanged | EventMcpBrowserOpenFailed | EventCommandExecuted | EventProjectUpdated @@ -646,7 +648,6 @@ export type Prompt = { text: string files?: Array agents?: Array - system?: string } export type Pty = { @@ -1513,6 +1514,20 @@ export type GlobalEvent = { server: string } } + | { + id: string + type: "mcp.prompts.changed" + properties: { + server: string + } + } + | { + id: string + type: "mcp.resources.changed" + properties: { + server: string + } + } | { id: string type: "mcp.browser.open.failed" @@ -2760,7 +2775,6 @@ export type PromptInput = { text: string files?: Array agents?: Array - system?: string } export type ConflictError = { @@ -2834,6 +2848,12 @@ export type SessionMessagesResponse = { } } +export type GenerateTextResponse = { + data: { + text: string + } +} + export type ProviderNotFoundError = { _tag: "ProviderNotFoundError" providerID: string @@ -2999,6 +3019,8 @@ export type V2Event = | TuiToastShow | TuiSessionSelect | McpToolsChanged + | McpPromptsChanged + | McpResourcesChanged | McpBrowserOpenFailed | CommandExecuted | ProjectUpdated @@ -4096,7 +4118,6 @@ export type SessionMessageUser = { text: string files?: Array agents?: Array - system?: string type: "user" } @@ -6036,6 +6057,40 @@ export type McpToolsChanged = { } } +export type McpPromptsChanged = { + id: string + metadata?: { + [key: string]: unknown + } + type: "mcp.prompts.changed" + durable?: { + aggregateID: string + seq: number + version: number + } + location?: LocationRef + data: { + server: string + } +} + +export type McpResourcesChanged = { + id: string + metadata?: { + [key: string]: unknown + } + type: "mcp.resources.changed" + durable?: { + aggregateID: string + seq: number + version: number + } + location?: LocationRef + data: { + server: string + } +} + export type McpBrowserOpenFailed = { id: string metadata?: { @@ -7117,6 +7172,22 @@ export type EventMcpToolsChanged = { } } +export type EventMcpPromptsChanged = { + id: string + type: "mcp.prompts.changed" + properties: { + server: string + } +} + +export type EventMcpResourcesChanged = { + id: string + type: "mcp.resources.changed" + properties: { + server: string + } +} + export type EventMcpBrowserOpenFailed = { id: string type: "mcp.browser.open.failed" @@ -12353,6 +12424,47 @@ export type V2ModelListResponses = { export type V2ModelListResponse = V2ModelListResponses[keyof V2ModelListResponses] +export type V2GenerateTextData = { + body: { + prompt: string + model?: ModelRef + } + path?: never + query?: { + location?: { + directory?: string + workspace?: string + } + } + url: "/api/generate" +} + +export type V2GenerateTextErrors = { + /** + * InvalidRequestError + */ + 400: InvalidRequestError + /** + * UnauthorizedError + */ + 401: UnauthorizedError + /** + * ServiceUnavailableError + */ + 503: ServiceUnavailableError +} + +export type V2GenerateTextError = V2GenerateTextErrors[keyof V2GenerateTextErrors] + +export type V2GenerateTextResponses = { + /** + * GenerateTextResponse + */ + 200: GenerateTextResponse +} + +export type V2GenerateTextResponse = V2GenerateTextResponses[keyof V2GenerateTextResponses] + export type V2ProviderListData = { body?: never path?: never