Skip to content

Instantly share code, notes, and snippets.

@laiso
Last active January 18, 2025 15:29
Show Gist options
  • Save laiso/921b69a52a390eaa4a6889587e74d22b to your computer and use it in GitHub Desktop.
Save laiso/921b69a52a390eaa4a6889587e74d22b to your computer and use it in GitHub Desktop.
#!/usr/bin/env -S npm run tsn -T
import Anthropic from '@anthropic-ai/sdk';
import { Client } from '@modelcontextprotocol/sdk/client/index';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio';
import { CallToolResultSchema } from '@modelcontextprotocol/sdk/types';
const client = new Anthropic(); // gets API Key from environment variable ANTHROPIC_API_KEY
const CLIENT_CONFIGS = [
{
command: 'npx',
args: ['-y', 'mcp-obsidian', '/tmp/obsidian'],
},
{
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-memory'],
},
];
async function getMcpClients(): Promise<Client[]> {
const clients: Client[] = [];
for (const config of CLIENT_CONFIGS) {
const client = new Client(
{
name: `mcp-client-${clients.length + 1}`,
version: '0.1.0',
},
{
capabilities: {
sampling: {},
},
},
);
await client.connect(new StdioClientTransport(config));
clients.push(client);
return clients;
}
return clients;
}
async function ask(question: string) {
console.log('Connected to server.');
const mcpClients = await getMcpClients();
console.log('Initialized.');
let allTools = [];
const toolServerMap = new Map();
const messages: Anthropic.MessageParam[] = [];
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);
}
const userMessage: Anthropic.MessageParam = {
role: 'user',
content: question,
};
messages.push(userMessage);
let message = await client.messages.create({
model: 'claude-3-5-sonnet-latest',
max_tokens: 1024,
messages: messages,
tools: allTools,
});
console.log('Assist:', message.content[0].text);
let toolUse = message.content.find(
(content): content is Anthropic.ToolUseBlock => content.type === 'tool_use',
);
let iteration = 0;
while (toolUse && iteration < 10) {
const mcpClient = toolServerMap.get(toolUse.name);
const result = await mcpClient.request(
{
method: 'tools/call',
params: {
name: toolUse.name,
arguments: toolUse.input,
},
},
CallToolResultSchema,
);
console.log('Tool Result:', result.content[0].text.slice(0, 255));
const toolingMessage: Anthropic.MessageParam = {
role: 'user',
content: result.content,
};
messages.push(toolingMessage);
message = await client.messages.create({
model: 'claude-3-5-sonnet-latest',
max_tokens: 1024,
messages: messages,
tools: allTools,
});
toolUse = message.content.find(
(content): content is Anthropic.ToolUseBlock => content.type === 'tool_use',
);
if (!toolUse) {
console.log('Message:', message.content[message.content.length - 1].text);
}
iteration++;
}
for (let i = 0; i < mcpClients.length; i++) {
await mcpClients[i].close();
console.log('Closed.');
}
}
ask('Summarize the MCP article in Obsidian and memorize the content.');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment