update api

This commit is contained in:
Aiden Cline 2025-11-07 13:54:45 -06:00
parent f11a8bdf71
commit f964c4f15c
5 changed files with 126 additions and 8 deletions

View file

@ -509,7 +509,18 @@ export namespace ACP {
await Promise.all(
Object.entries(mcpServers).map(async ([key, mcp]) => {
await MCP.add(key, mcp)
await this.sdk.mcp
.add({
throwOnError: true,
query: { directory },
body: {
name: key,
config: mcp,
},
})
.catch((error) => {
log.error("failed to add mcp server", { name: key, error })
})
}),
)

View file

@ -92,13 +92,28 @@ export namespace MCP {
export async function add(name: string, mcp: Config.Mcp) {
const s = await state()
const result = await create(name, mcp)
if (!result) return
if (!result) {
const status = {
status: "failed" as const,
error: "unknown error",
}
s.status[name] = status
return {
status,
}
}
if (!result.mcpClient) {
s.status[name] = result.status
return
return {
status: s.status,
}
}
s.clients[name] = result.mcpClient
s.status[name] = result.status
return {
status: s.status,
}
}
async function create(key: string, mcp: Config.Mcp) {
@ -207,8 +222,12 @@ export namespace MCP {
}
}
const result = await withTimeout(mcpClient.tools(), mcp.timeout ?? 5000).catch(() => {})
const result = await withTimeout(mcpClient.tools(), mcp.timeout ?? 5000).catch((err) => {
log.error("create() failed to get tools from client", { key, error: err })
return undefined
})
if (!result) {
log.info("create() tools() returned nothing, closing client", { key })
await mcpClient.close().catch((error) => {
log.error("Failed to close MCP client", {
error,
@ -227,6 +246,7 @@ export namespace MCP {
}
}
log.info("create() successfully created client", { key, toolCount: Object.keys(result).length })
return {
mcpClient,
status,
@ -238,13 +258,18 @@ export namespace MCP {
}
export async function clients() {
return state().then((state) => state.clients)
const s = await state()
log.info("clients() called", { clientCount: Object.keys(s.clients).length })
return s.clients
}
export async function tools() {
const result: Record<string, Tool> = {}
const s = await state()
for (const [clientName, client] of Object.entries(await clients())) {
log.info("tools() called", { clientCount: Object.keys(s.clients).length })
const clientsSnapshot = await clients()
for (const [clientName, client] of Object.entries(clientsSnapshot)) {
log.info("tools() fetching tools for client", { clientName })
const tools = await client.tools().catch((e) => {
log.error("failed to get tools", { clientName, error: e.message })
const failedStatus = {
@ -255,14 +280,17 @@ export namespace MCP {
delete s.clients[clientName]
})
if (!tools) {
log.info("tools() no tools returned for client", { clientName })
continue
}
log.info("tools() got tools for client", { clientName, toolCount: Object.keys(tools).length })
for (const [toolName, tool] of Object.entries(tools)) {
const sanitizedClientName = clientName.replace(/[^a-zA-Z0-9_-]/g, "_")
const sanitizedToolName = toolName.replace(/[^a-zA-Z0-9_-]/g, "_")
result[sanitizedClientName + "_" + sanitizedToolName] = tool
}
}
log.info("tools() final result", { toolCount: Object.keys(result).length })
return result
}
}

View file

@ -1359,6 +1359,36 @@ export namespace Server {
return c.json(await MCP.status())
},
)
.post(
"/mcp",
describeRoute({
description: "Add MCP server dynamically",
operationId: "mcp.add",
responses: {
200: {
description: "MCP server added successfully",
content: {
"application/json": {
schema: resolver(z.record(z.string(), MCP.Status)),
},
},
},
...errors(400),
},
}),
validator(
"json",
z.object({
name: z.string(),
config: Config.Mcp,
}),
),
async (c) => {
const { name, config } = c.req.valid("json")
const result = await MCP.add(name, config)
return c.json(result.status)
},
)
.get(
"/lsp",
describeRoute({

View file

@ -106,6 +106,9 @@ import type {
AppAgentsResponses,
McpStatusData,
McpStatusResponses,
McpAddData,
McpAddResponses,
McpAddErrors,
LspStatusData,
LspStatusResponses,
FormatterStatusData,
@ -764,6 +767,20 @@ class Mcp extends _HeyApiClient {
...options,
})
}
/**
* Add MCP server dynamically
*/
public add<ThrowOnError extends boolean = false>(options?: Options<McpAddData, ThrowOnError>) {
return (options?.client ?? this._client).post<McpAddResponses, McpAddErrors, ThrowOnError>({
url: "/mcp",
...options,
headers: {
"Content-Type": "application/json",
...options?.headers,
},
})
}
}
class Lsp extends _HeyApiClient {

View file

@ -1979,9 +1979,9 @@ export type SessionMessagesData = {
*/
id: string
}
query?: {
query: {
directory?: string
limit?: number
limit: number
}
url: "/session/{id}/message"
}
@ -2552,6 +2552,38 @@ export type McpStatusResponses = {
export type McpStatusResponse = McpStatusResponses[keyof McpStatusResponses]
export type McpAddData = {
body?: {
name: string
config: McpLocalConfig | McpRemoteConfig
}
path?: never
query?: {
directory?: string
}
url: "/mcp"
}
export type McpAddErrors = {
/**
* Bad request
*/
400: BadRequestError
}
export type McpAddError = McpAddErrors[keyof McpAddErrors]
export type McpAddResponses = {
/**
* MCP server added successfully
*/
200: {
[key: string]: McpStatus
}
}
export type McpAddResponse = McpAddResponses[keyof McpAddResponses]
export type LspStatusData = {
body?: never
path?: never