Last active
January 18, 2025 15:46
-
-
Save laiso/8fa4f3b686937adc4a52c1674c1d6a6a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Anthropic from '@anthropic-ai/sdk'; | |
import { Client } from '@modelcontextprotocol/sdk/client/index.js'; | |
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'; | |
import { CallToolResultSchema } from '@modelcontextprotocol/sdk/types.js'; | |
const client = new Anthropic(); // gets API Key from environment variable ANTHROPIC_API_KEY | |
type MCPServersConfig = { | |
mcpServers: Record<string, { command: string; args: string[]; env?: Record<string, string> }>; | |
}; | |
async function getMcpClients(config: MCPServersConfig): Promise<Client[]> { | |
const clients: Client[] = []; | |
for (const key in config.mcpServers) { | |
const params = config.mcpServers[key]; | |
const client = new Client( | |
{ | |
name: key, | |
version: '0.1.0', | |
}, | |
{ | |
capabilities: { | |
sampling: {}, | |
}, | |
}, | |
); | |
await client.connect(new StdioClientTransport(params)); | |
clients.push(client); | |
} | |
return clients; | |
} | |
async function buildAllTools(config: MCPServersConfig): Promise<{ allTools: Anthropic.Tool[], toolServerMap: Map<string, Client> }> { | |
const mcpClients = await getMcpClients(config); | |
let allTools: Anthropic.Tool[] = []; | |
const toolServerMap = new Map(); | |
for (let i = 0; i < mcpClients.length; i++) { | |
const mcpClient = mcpClients[i]; | |
const toolList = await mcpClient.listTools(); | |
const tools = toolList.tools.map((tool) => { | |
toolServerMap.set(tool.name, mcpClient); | |
return { | |
name: tool.name, | |
description: tool.description, | |
input_schema: tool.inputSchema, | |
}; | |
}); | |
allTools = allTools.concat(tools); | |
} | |
return { allTools, toolServerMap }; | |
} | |
async function main(question: string, path: string, config: MCPServersConfig, maxSteps = 10) { | |
console.log('Connected to server.'); | |
console.log('Initialized.'); | |
const { allTools, toolServerMap } = await buildAllTools(config); | |
const messages: Anthropic.MessageParam[] = []; | |
const userMessage: Anthropic.MessageParam = { | |
role: 'user', | |
content: question, | |
}; | |
messages.push(userMessage); | |
const system = `You are Mini Coder, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices. | |
Current Working Directory: ${path} | |
` | |
let message = await client.messages.create({ | |
model: 'claude-3-5-sonnet-latest', | |
max_tokens: 1024, | |
system, | |
messages: messages, | |
tools: allTools, | |
}); | |
console.log('Assistaunt:', message.content[0].text); | |
messages.push({ | |
role: 'assistant', | |
content: message.content[0].text, | |
}); | |
let toolUse = message.content.find( | |
(content): content is Anthropic.ToolUseBlock => content.type === 'tool_use', | |
); | |
let iteration = 0; | |
while (toolUse && iteration < maxSteps) { | |
console.log({ toolUse }); | |
const mcpClient = toolServerMap.get(toolUse.name); | |
if (!mcpClient) { | |
throw new Error(`Tool server not found for tool ${toolUse.name}`); | |
} | |
const result = await mcpClient.callTool( | |
{ | |
name: toolUse.name, | |
arguments: toolUse.input, | |
}, | |
CallToolResultSchema, | |
); | |
console.log('Tool Result:', result); | |
result.content.forEach((content) => { | |
const m: Anthropic.MessageParam = { | |
role: 'user', | |
content: content.text, | |
}; | |
messages.push(m); | |
}); | |
message = await client.messages.create({ | |
model: 'claude-3-5-sonnet-latest', | |
max_tokens: 1024, | |
system, | |
messages: messages, | |
tools: allTools, | |
}); | |
messages.push({ | |
role: 'assistant', | |
content: message.content[0].text, | |
}); | |
console.log('Assistaunt:', message.content[0].text); | |
toolUse = message.content.find( | |
(content): content is Anthropic.ToolUseBlock => content.type === 'tool_use', | |
); | |
console.log({ toolUse }); | |
iteration++; | |
} | |
const mcpClients = await getMcpClients(config); | |
for (let i = 0; i < mcpClients.length; i++) { | |
await mcpClients[i].close(); | |
console.log('Closed.'); | |
} | |
} | |
const projectRoot = '/tmp/hono-app'; | |
const config = { | |
mcpServers: { | |
filesystem: { | |
command: 'npx', | |
args: ['-y', '@modelcontextprotocol/server-filesystem', projectRoot], | |
}, | |
}, | |
}; | |
const instruction = ` | |
Create a login API. | |
`; | |
main(instruction, projectRoot, config); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment