Merge pull request #139 from sigoden/feat-mcp-server

This commit is contained in:
sigoden
2024-12-11 08:42:16 +08:00
committed by GitHub
3 changed files with 180 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
# MCP-Server
Let LLM-functions tools/agents be used through the Model Context Protocol.
## Serve tools
```json
{
"mcpServers": {
"tools": {
"command": "node",
"args": [
"<path-to-llm-functions>/mcp/server/index.js",
"<path-to-llm-functions>"
]
}
}
}
```
## Serve the agent
```json
{
"mcpServers": {
"<agent-name>": {
"command": "node",
"args": [
"<path-to-llm-functions>/mcp/server/index.js",
"<path-to-llm-functions>",
"<agent-name>",
]
}
}
}
```
+120
View File
@@ -0,0 +1,120 @@
#!/usr/bin/env node
import * as path from "node:path";
import * as fs from "node:fs";
import * as os from "node:os";
import { v4 as uuid } from "uuid";
import { spawn } from "node:child_process";
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ErrorCode,
ListToolsRequestSchema,
McpError,
} from "@modelcontextprotocol/sdk/types.js";
let [rootDir, agentName] = process.argv.slice(2);
if (!rootDir) {
console.error("Usage: mcp-llm-functions <llm-functions-dir> [<agent-name>]");
process.exit(1);
}
rootDir = path.resolve(rootDir);
let functionsJsonPath = path.join(rootDir, "functions.json");
if (agentName) {
functionsJsonPath = path.join(rootDir, "agents", agentName, "functions.json");
}
let functions = [];
try {
const data = await fs.promises.readFile(functionsJsonPath, "utf8");
functions = JSON.parse(data);
} catch {
console.error(`Failed to read functions at '${functionsJsonPath}'`);
process.exit(1);
}
const env = Object.assign({}, process.env, {
PATH: `${path.join(rootDir, "bin")}:${process.env.PATH}`
});
const server = new Server(
{
name: `llm-functions/${agentName || "common-tools"}`,
version: "0.1.0",
},
{
capabilities: {
tools: {},
},
},
);
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: functions.map((f) => ({
name: f.name,
description: f.description,
inputSchema: f.parameters,
})),
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const functionObj = functions.find((f) => f.name === request.params.name);
if (!functionObj) {
throw new McpError(ErrorCode.InvalidRequest, `Unexpected tool '${request.params.name}'`);
}
let command = request.params.name;
let args = [JSON.stringify(request.params.arguments || {})];
if (agentName && functionObj.agent) {
args.unshift(command);
command = agentName;
}
const tmpFile = path.join(os.tmpdir(), `mcp-llm-functions-${process.pid}-eval-${uuid()}`);
const { exitCode, stderr } = await runCommand(command, args, { ...env, LLM_OUTPUT: tmpFile });
if (exitCode === 0) {
let output = '';
try {
output = await fs.promises.readFile(tmpFile, "utf8");
} catch { };
return {
content: [{ type: "text", value: output }],
}
} else {
return {
isError: true,
error: stderr,
};
}
});
function runCommand(command, args, env) {
return new Promise((resolve, reject) => {
const child = spawn(command, args, {
stdio: ['ignore', 'ignore', 'pipe'],
env,
});
let stderr = '';
child.stderr.on('data', (data) => {
stderr += data.toString();
});
child.on('close', (exitCode) => {
resolve({ exitCode, stderr });
});
child.on('error', (err) => {
reject(err);
});
});
}
async function runServer() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("LLM-Functions MCP Server running on stdio");
}
runServer().catch(console.error);
+24
View File
@@ -0,0 +1,24 @@
{
"name": "mcp-llm-functions",
"version": "1.0.0",
"description": "Let LLM-functions tools/agents be used through the Model Context Protocol",
"license": "MIT",
"author": "sigoden <sigoden@gmail.com>",
"homepage": "https://github.com/sigoden/llm-functions/tree/main/mcp/server",
"repository": {
"type": "git",
"url": "git+https://github.com/sigoden/llm-functions.git",
"directory": "mcp/server"
},
"publishConfig": {
"access": "public"
},
"type": "module",
"bin": {
"mcp-llm-functions": "index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.3",
"uuid": "^11.0.3"
}
}