mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-03 23:53:46 +00:00
feat: mcp prompts as slash commands (alternative) (#5767)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
This commit is contained in:
@@ -132,6 +132,9 @@ export namespace MCP {
|
||||
type TransportWithAuth = StreamableHTTPClientTransport | SSEClientTransport
|
||||
const pendingOAuthTransports = new Map<string, TransportWithAuth>()
|
||||
|
||||
// Prompt cache types
|
||||
type PromptInfo = Awaited<ReturnType<MCPClient["listPrompts"]>>["prompts"][number]
|
||||
|
||||
const state = Instance.state(
|
||||
async () => {
|
||||
const cfg = await Config.get()
|
||||
@@ -176,6 +179,29 @@ export namespace MCP {
|
||||
},
|
||||
)
|
||||
|
||||
// Helper function to fetch prompts for a specific client
|
||||
async function fetchPromptsForClient(clientName: string, client: Client) {
|
||||
const prompts = await client.listPrompts().catch((e) => {
|
||||
log.error("failed to get prompts", { clientName, error: e.message })
|
||||
return undefined
|
||||
})
|
||||
|
||||
if (!prompts) {
|
||||
return
|
||||
}
|
||||
|
||||
const commands: Record<string, PromptInfo & { client: string }> = {}
|
||||
|
||||
for (const prompt of prompts.prompts) {
|
||||
const sanitizedClientName = clientName.replace(/[^a-zA-Z0-9_-]/g, "_")
|
||||
const sanitizedPromptName = prompt.name.replace(/[^a-zA-Z0-9_-]/g, "_")
|
||||
const key = sanitizedClientName + ":" + sanitizedPromptName + " (MCP)"
|
||||
|
||||
commands[key] = { ...prompt, client: clientName }
|
||||
}
|
||||
return commands
|
||||
}
|
||||
|
||||
export async function add(name: string, mcp: Config.Mcp) {
|
||||
const s = await state()
|
||||
const result = await create(name, mcp)
|
||||
@@ -492,6 +518,55 @@ export namespace MCP {
|
||||
return result
|
||||
}
|
||||
|
||||
export async function prompts() {
|
||||
const s = await state()
|
||||
const clientsSnapshot = await clients()
|
||||
|
||||
const prompts = Object.fromEntries<PromptInfo & { client: string }>(
|
||||
(
|
||||
await Promise.all(
|
||||
Object.entries(clientsSnapshot).map(async ([clientName, client]) => {
|
||||
if (s.status[clientName]?.status !== "connected") {
|
||||
return []
|
||||
}
|
||||
|
||||
return Object.entries((await fetchPromptsForClient(clientName, client)) ?? {})
|
||||
}),
|
||||
)
|
||||
).flat(),
|
||||
)
|
||||
|
||||
return prompts
|
||||
}
|
||||
|
||||
export async function getPrompt(clientName: string, name: string, args?: Record<string, string>) {
|
||||
const clientsSnapshot = await clients()
|
||||
const client = clientsSnapshot[clientName]
|
||||
|
||||
if (!client) {
|
||||
log.warn("client not found for prompt", {
|
||||
clientName,
|
||||
})
|
||||
return undefined
|
||||
}
|
||||
|
||||
const result = await client
|
||||
.getPrompt({
|
||||
name: name,
|
||||
arguments: args,
|
||||
})
|
||||
.catch((e) => {
|
||||
log.error("failed to get prompt from MCP server", {
|
||||
clientName,
|
||||
promptName: name,
|
||||
error: e.message,
|
||||
})
|
||||
return undefined
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Start OAuth authentication flow for an MCP server.
|
||||
* Returns the authorization URL that should be opened in a browser.
|
||||
|
||||
Reference in New Issue
Block a user