mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-27 07:16:19 +09:00
feat: mcp (#31)
* feat: add mcp connections table and related crud api * feat: mcp-stdio api
This commit is contained in:
+14
-3
@@ -1,5 +1,5 @@
|
||||
import { generateText, ImagePart, LanguageModelUsage, ModelMessage, stepCountIs, streamText, UserModelMessage } from 'ai'
|
||||
import { AgentInput, AgentParams, AgentSkill, allActions, HTTPMCPConnection, MCPConnection, Schedule } from './types'
|
||||
import { AgentInput, AgentParams, AgentSkill, allActions, HTTPMCPConnection, MCPConnection, Schedule, StdioMCPConnection } from './types'
|
||||
import { system, schedule, user, subagentSystem } from './prompts'
|
||||
import { AuthFetcher } from './index'
|
||||
import { createModel } from './model'
|
||||
@@ -56,7 +56,14 @@ export const createAgent = ({
|
||||
'Authorization': `Bearer ${auth.bearer}`,
|
||||
},
|
||||
}
|
||||
return [fs]
|
||||
const mcpFetch: StdioMCPConnection = {
|
||||
type: 'stdio',
|
||||
name: 'mcp-fetch',
|
||||
command: 'npx',
|
||||
args: ['fetch-mcp'],
|
||||
env: {},
|
||||
}
|
||||
return [fs, mcpFetch]
|
||||
}
|
||||
|
||||
const generateSystemPrompt = () => {
|
||||
@@ -82,7 +89,11 @@ export const createAgent = ({
|
||||
const { tools: mcpTools, close: closeMCP } = await getMCPTools([
|
||||
...defaultMCPConnections,
|
||||
...mcpConnections,
|
||||
])
|
||||
], {
|
||||
botId: identity.botId,
|
||||
auth,
|
||||
fetch,
|
||||
})
|
||||
Object.assign(tools, mcpTools)
|
||||
return {
|
||||
tools,
|
||||
|
||||
+55
-7
@@ -1,7 +1,15 @@
|
||||
import { HTTPMCPConnection, MCPConnection, SSEMCPConnection, StdioMCPConnection } from '../types'
|
||||
import { createMCPClient } from '@ai-sdk/mcp'
|
||||
import { AuthFetcher } from '../index'
|
||||
import type { AgentAuthContext } from '../types/agent'
|
||||
|
||||
export const getMCPTools = async (connections: MCPConnection[]) => {
|
||||
type MCPToolOptions = {
|
||||
botId?: string
|
||||
auth?: AgentAuthContext
|
||||
fetch?: AuthFetcher
|
||||
}
|
||||
|
||||
export const getMCPTools = async (connections: MCPConnection[], options: MCPToolOptions = {}) => {
|
||||
const closeCallbacks: Array<() => Promise<void>> = []
|
||||
|
||||
const getHTTPTools = async (connection: HTTPMCPConnection) => {
|
||||
@@ -13,7 +21,8 @@ export const getMCPTools = async (connections: MCPConnection[]) => {
|
||||
}
|
||||
})
|
||||
closeCallbacks.push(() => client.close())
|
||||
return await client.tools()
|
||||
const tools = await client.tools()
|
||||
return tools
|
||||
}
|
||||
|
||||
const getSSETools = async (connection: SSEMCPConnection) => {
|
||||
@@ -25,15 +34,51 @@ export const getMCPTools = async (connections: MCPConnection[]) => {
|
||||
}
|
||||
})
|
||||
closeCallbacks.push(() => client.close())
|
||||
return await client.tools()
|
||||
const tools = await client.tools()
|
||||
return tools
|
||||
}
|
||||
|
||||
const getStdioTools = async (connection: StdioMCPConnection) => {
|
||||
// TODO: Implement stdio tools
|
||||
return {}
|
||||
if (!options.fetch || !options.botId || !options.auth) {
|
||||
throw new Error('stdio mcp requires auth fetcher and bot id')
|
||||
}
|
||||
const response = await options.fetch(`/bots/${options.botId}/mcp-stdio`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: connection.name,
|
||||
command: connection.command,
|
||||
args: connection.args ?? [],
|
||||
env: connection.env ?? {},
|
||||
cwd: connection.cwd ?? ''
|
||||
})
|
||||
})
|
||||
if (!response.ok) {
|
||||
const text = await response.text().catch(() => '')
|
||||
throw new Error(`mcp-stdio failed: ${response.status} ${text}`)
|
||||
}
|
||||
const data = await response.json().catch(() => ({} as { url?: string }))
|
||||
const rawUrl = typeof data?.url === 'string' ? data.url : ''
|
||||
if (!rawUrl) {
|
||||
throw new Error('mcp-stdio response missing url')
|
||||
}
|
||||
const baseUrl = options.auth.baseUrl ?? ''
|
||||
const url = rawUrl.startsWith('http')
|
||||
? rawUrl
|
||||
: `${baseUrl.replace(/\/$/, '')}/${rawUrl.replace(/^\//, '')}`
|
||||
return await getHTTPTools({
|
||||
type: 'http',
|
||||
name: connection.name,
|
||||
url,
|
||||
headers: {
|
||||
'Authorization': `Bearer ${options.auth.bearer}`
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const toolSets = await Promise.all(connections.map(connection => {
|
||||
const toolSets = await Promise.all(connections.map(async (connection) => {
|
||||
switch (connection.type) {
|
||||
case 'http':
|
||||
return getHTTPTools(connection)
|
||||
@@ -41,6 +86,9 @@ export const getMCPTools = async (connections: MCPConnection[]) => {
|
||||
return getSSETools(connection)
|
||||
case 'stdio':
|
||||
return getStdioTools(connection)
|
||||
default:
|
||||
console.warn('unknown mcp connection type', connection)
|
||||
return {}
|
||||
}
|
||||
}))
|
||||
|
||||
@@ -50,4 +98,4 @@ export const getMCPTools = async (connections: MCPConnection[]) => {
|
||||
await Promise.all(closeCallbacks.map(callback => callback()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user