feat: support MCP bridge (#140)
This commit is contained in:
@@ -15,3 +15,4 @@ node_modules
|
|||||||
/package.json
|
/package.json
|
||||||
package-lock.json
|
package-lock.json
|
||||||
*.lock
|
*.lock
|
||||||
|
/mcp.json
|
||||||
+19
-4
@@ -2,7 +2,7 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
BIN_DIR=bin
|
BIN_DIR=bin
|
||||||
TMP_DIR="cache/tmp"
|
TMP_DIR="cache/__tmp__"
|
||||||
VENV_DIR=".venv"
|
VENV_DIR=".venv"
|
||||||
|
|
||||||
LANG_CMDS=( \
|
LANG_CMDS=( \
|
||||||
@@ -421,9 +421,8 @@ test-demo@tool() {
|
|||||||
# @cmd Test agents
|
# @cmd Test agents
|
||||||
# @alias agent:test
|
# @alias agent:test
|
||||||
test@agent() {
|
test@agent() {
|
||||||
tmp_dir="cache/tmp"
|
mkdir -p "$TMP_DIR"
|
||||||
mkdir -p "$tmp_dir"
|
names_file="$TMP_DIR/agents.txt"
|
||||||
names_file="$tmp_dir/agents.txt"
|
|
||||||
argc list@agent > "$names_file"
|
argc list@agent > "$names_file"
|
||||||
argc build@agent --names-file "$names_file"
|
argc build@agent --names-file "$names_file"
|
||||||
test-demo@agent
|
test-demo@agent
|
||||||
@@ -499,6 +498,12 @@ install() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# @cmd Run mcp command
|
||||||
|
# @arg args~[?`_choice_mcp_args`] The mcp command and arguments
|
||||||
|
mcp() {
|
||||||
|
bash ./scripts/mcp.sh "$@"
|
||||||
|
}
|
||||||
|
|
||||||
# @cmd Create a boilplate tool script
|
# @cmd Create a boilplate tool script
|
||||||
# @alias tool:create
|
# @alias tool:create
|
||||||
# @arg args~
|
# @arg args~
|
||||||
@@ -671,6 +676,16 @@ _choice_agent_action() {
|
|||||||
argc generate-declarations@agent "$1" --oneline | sed "$expr"
|
argc generate-declarations@agent "$1" --oneline | sed "$expr"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_choice_mcp_args() {
|
||||||
|
if [[ "$ARGC_COMPGEN" -eq 1 ]]; then
|
||||||
|
args=( "${argc__positionals[@]}" )
|
||||||
|
args[-1]="$ARGC_LAST_ARG"
|
||||||
|
argc --argc-compgen generic scripts/mcp.sh mcp "${args[@]}"
|
||||||
|
else
|
||||||
|
:;
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
_die() {
|
_die() {
|
||||||
echo "$*" >&2
|
echo "$*" >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# MCP-Bridge
|
||||||
|
|
||||||
|
Let MCP tools be used by LLM functions.
|
||||||
|
|
||||||
|
## Get Started
|
||||||
|
|
||||||
|
1. Create a `mpc.json` at `<llm-functions-dir>`.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"sqlite": {
|
||||||
|
"command": "uvx",
|
||||||
|
"args": [
|
||||||
|
"mcp-server-sqlite",
|
||||||
|
"--db-path",
|
||||||
|
"/tmp/foo.db"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"github": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": [
|
||||||
|
"-y",
|
||||||
|
"@modelcontextprotocol/server-github"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"GITHUB_PERSONAL_ACCESS_TOKEN": "<YOUR_TOKEN>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> MCP-Bridge will launch the server and register all the tools listed by the server. The tool identifier will be `server_toolname` to avoid clashes.
|
||||||
|
|
||||||
|
2. Run the bridge server, build mcp tool binaries, update functions.json, all with:
|
||||||
|
|
||||||
|
```
|
||||||
|
argc mcp start
|
||||||
|
```
|
||||||
|
|
||||||
|
> Run `argc mcp stop` to stop the bridge server, recover functions.json
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
import * as path from "node:path";
|
||||||
|
import * as fs from "node:fs";
|
||||||
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
||||||
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
||||||
|
import express from "express";
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
const PORT = process.env.MCP_BRIDGE_PORT || 8808;
|
||||||
|
|
||||||
|
let [rootDir] = process.argv.slice(2);
|
||||||
|
|
||||||
|
if (!rootDir) {
|
||||||
|
console.error("Usage: mcp-bridge <llm-functions-dir>");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mcpServers = {};
|
||||||
|
const mcpJsonPath = path.join(rootDir, "mcp.json");
|
||||||
|
try {
|
||||||
|
const data = await fs.promises.readFile(mcpJsonPath, "utf8");
|
||||||
|
mcpServers = JSON.parse(data)?.mcpServers;
|
||||||
|
} catch {
|
||||||
|
console.error(`Failed to read json at '${mcpJsonPath}'`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startMcpServer(id, serverConfig) {
|
||||||
|
console.log(`Starting ${id} server...`);
|
||||||
|
const capabilities = { tools: {} };
|
||||||
|
const transport = new StdioClientTransport({
|
||||||
|
...serverConfig,
|
||||||
|
});
|
||||||
|
const client = new Client(
|
||||||
|
{ name: id, version: "1.0.0" },
|
||||||
|
{ capabilities }
|
||||||
|
);
|
||||||
|
await client.connect(transport);
|
||||||
|
const { tools: toolDefinitions } = await client.listTools()
|
||||||
|
const tools = toolDefinitions.map(
|
||||||
|
({ name, description, inputSchema }) =>
|
||||||
|
({
|
||||||
|
spec: {
|
||||||
|
name: `${normalizeToolName(`${id}_${name}`)}`,
|
||||||
|
description,
|
||||||
|
parameters: inputSchema,
|
||||||
|
},
|
||||||
|
impl: async args => {
|
||||||
|
const res = await client.callTool({
|
||||||
|
name: name,
|
||||||
|
arguments: args,
|
||||||
|
});
|
||||||
|
const content = res.content;
|
||||||
|
let text = arrayify(content)?.map(c => {
|
||||||
|
switch (c.type) {
|
||||||
|
case "text":
|
||||||
|
return c.text || ""
|
||||||
|
case "image":
|
||||||
|
return c.data
|
||||||
|
case "resource":
|
||||||
|
return c.resource?.uri || ""
|
||||||
|
default:
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}).join("\n");
|
||||||
|
if (res.isError) {
|
||||||
|
text = `Tool Error\n${text}`;
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
tools,
|
||||||
|
[Symbol.asyncDispose]: async () => {
|
||||||
|
try {
|
||||||
|
console.log(`Closing ${id} server...`);
|
||||||
|
await client.close();
|
||||||
|
await transport.close();
|
||||||
|
} catch { }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runBridge() {
|
||||||
|
let hasError = false;
|
||||||
|
let runningMcpServers = await Promise.all(
|
||||||
|
Object.entries(mcpServers).map(
|
||||||
|
async ([name, serverConfig]) => {
|
||||||
|
try {
|
||||||
|
return await startMcpServer(name, serverConfig)
|
||||||
|
} catch (err) {
|
||||||
|
hasError = true;
|
||||||
|
console.error(`Failed to start ${name} server; ${err.message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
runningMcpServers = runningMcpServers.filter(s => !!s);
|
||||||
|
const stopMcpServers = () => Promise.all(runningMcpServers.map(s => s[Symbol.asyncDispose]()));
|
||||||
|
if (hasError) {
|
||||||
|
await stopMcpServers();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const definitions = runningMcpServers.flatMap(s => s.tools.map(t => t.spec));
|
||||||
|
const runTool = async (name, args) => {
|
||||||
|
for (const server of runningMcpServers) {
|
||||||
|
const tool = server.tools.find(t => t.spec.name === name);
|
||||||
|
if (tool) {
|
||||||
|
return tool.impl(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return `Not found tool '${name}'`;
|
||||||
|
};
|
||||||
|
|
||||||
|
app.use((err, _req, res, _next) => {
|
||||||
|
res.status(500).send(err?.message || err);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use(express.json());
|
||||||
|
|
||||||
|
app.get("/", (_req, res) => {
|
||||||
|
res.send(`# MCP Bridge API
|
||||||
|
|
||||||
|
- POST /tools/:name
|
||||||
|
\`\`\`
|
||||||
|
curl -X POST http://localhost:8808/tools/filesystem_write_file \\
|
||||||
|
-H 'content-type: application/json' \\
|
||||||
|
-d '{"path": "/tmp/file1", "content": "hello world"}'
|
||||||
|
\`\`\`
|
||||||
|
- GET /tools
|
||||||
|
\`\`\`
|
||||||
|
curl http://localhost:8808/tools
|
||||||
|
\`\`\`
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/tools", (_req, res) => {
|
||||||
|
res.json(definitions);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/tools/:name", async (req, res) => {
|
||||||
|
try {
|
||||||
|
const output = await runTool(req.params.name, req.body);
|
||||||
|
res.send(output);
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).send(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/pid", (_req, res) => {
|
||||||
|
res.send(process.pid.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/health", (_req, res) => {
|
||||||
|
res.send("OK");
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use((_req, res, _next) => {
|
||||||
|
res.status(404).send("Not found");
|
||||||
|
});
|
||||||
|
|
||||||
|
const server = app.listen(PORT, () => {
|
||||||
|
console.log(`Server is running on port ${PORT}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
return async () => {
|
||||||
|
server.close(() => console.log("Http server closed"));
|
||||||
|
await stopMcpServers();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function arrayify(a) {
|
||||||
|
let r;
|
||||||
|
if (a === undefined) r = [];
|
||||||
|
else if (Array.isArray(a)) r = a.slice(0);
|
||||||
|
else r = [a];
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeToolName(name) {
|
||||||
|
return name.toLowerCase().replace(/-/g, "_");
|
||||||
|
}
|
||||||
|
|
||||||
|
runBridge()
|
||||||
|
.then(stop => {
|
||||||
|
if (stop) {
|
||||||
|
process.on('SIGINT', stop);
|
||||||
|
process.on('SIGTERM', stop);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "mcp-bridge",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Let MCP tools be used by LLM functions",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "sigoden <sigoden@gmail.com>",
|
||||||
|
"homepage": "https://github.com/sigoden/llm-functions/tree/main/mcp/bridge",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/sigoden/llm-functions.git",
|
||||||
|
"directory": "mcp/bridge"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"bin": {
|
||||||
|
"mcp-bridge": "index.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@modelcontextprotocol/sdk": "^1.0.3",
|
||||||
|
"express": "^4.21.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
+176
@@ -0,0 +1,176 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )/.." &> /dev/null && pwd)"
|
||||||
|
BIN_DIR="$ROOT_DIR/bin"
|
||||||
|
MCP_DIR="$ROOT_DIR/cache/__mcp__"
|
||||||
|
MCP_JSON_PATH="$ROOT_DIR/mcp.json"
|
||||||
|
FUNCTIONS_JSON_PATH="$ROOT_DIR/functions.json"
|
||||||
|
MCP_BRIDGE_PORT="${MCP_BRIDGE_PORT:-8808}"
|
||||||
|
|
||||||
|
# @cmd Start/Restart mcp bridge server
|
||||||
|
start() {
|
||||||
|
if [[ ! -f "$MCP_JSON_PATH" ]]; then
|
||||||
|
_die "error: not found mcp.json"
|
||||||
|
fi
|
||||||
|
stop
|
||||||
|
mkdir -p "$MCP_DIR"
|
||||||
|
index_js="$ROOT_DIR/mcp/bridge/index.js"
|
||||||
|
llm_functions_dir="$ROOT_DIR"
|
||||||
|
if _is_win; then
|
||||||
|
index_js="$(cygpath -w "$index_js")"
|
||||||
|
llm_functions_dir="$(cygpath -w "$llm_functions_dir")"
|
||||||
|
fi
|
||||||
|
echo "Run MCP Bridge server"
|
||||||
|
nohup node "$index_js" "$llm_functions_dir" > "$MCP_DIR/mcp-bridge.log" 2>&1 &
|
||||||
|
wait-for-server
|
||||||
|
echo "Merge MCP tools into functions.json"
|
||||||
|
merge-functions > "$MCP_DIR/functions.json"
|
||||||
|
cp -f "$MCP_DIR/functions.json" "$FUNCTIONS_JSON_PATH"
|
||||||
|
build-bin
|
||||||
|
}
|
||||||
|
|
||||||
|
# @cmd Stop mcp bridge server
|
||||||
|
stop() {
|
||||||
|
pid="$(get-server-pid)"
|
||||||
|
if [[ -n "$pid" ]]; then
|
||||||
|
if _is_win; then
|
||||||
|
taskkill /PID "$pid" /F > /dev/null 2>&1 || true
|
||||||
|
else
|
||||||
|
kill -9 "$pid" > /dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
mkdir -p "$MCP_DIR"
|
||||||
|
unmerge-functions > "$MCP_DIR/functions.original.json"
|
||||||
|
cp -f "$MCP_DIR/functions.original.json" "$FUNCTIONS_JSON_PATH"
|
||||||
|
}
|
||||||
|
|
||||||
|
# @cmd Call mcp tool
|
||||||
|
# @arg tool![`_choice_tool`] The tool name
|
||||||
|
# @arg json The json data
|
||||||
|
call() {
|
||||||
|
if [[ -z "$argc_json" ]]; then
|
||||||
|
declaration="$(build-declarations | jq --arg tool "$argc_tool" -r '.[] | select(.name == $tool)')"
|
||||||
|
if [[ -n "$declaration" ]]; then
|
||||||
|
_ask_json_data "$declaration"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [[ -z "$argc_json" ]]; then
|
||||||
|
_die "error: no JSON data"
|
||||||
|
fi
|
||||||
|
bash "$ROOT_DIR/scripts/run-mcp-tool.sh" "$argc_tool" "$argc_json"
|
||||||
|
}
|
||||||
|
|
||||||
|
# @cmd Show logs
|
||||||
|
# @flag -f --follow Follow mode
|
||||||
|
logs() {
|
||||||
|
args=""
|
||||||
|
if [[ -n "$argc_follow" ]]; then
|
||||||
|
args="$args -f"
|
||||||
|
fi
|
||||||
|
if [[ -f "$MCP_DIR/mcp-bridge.log" ]]; then
|
||||||
|
tail $args "$MCP_DIR/mcp-bridge.log"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# @cmd Build tools to bin
|
||||||
|
build-bin() {
|
||||||
|
tools=( $(build-declarations | jq -r '.[].name') )
|
||||||
|
for tool in "${tools[@]}"; do
|
||||||
|
if _is_win; then
|
||||||
|
bin_file="$BIN_DIR/$tool.cmd"
|
||||||
|
_build_win_shim > "$bin_file"
|
||||||
|
else
|
||||||
|
bin_file="$BIN_DIR/$tool"
|
||||||
|
ln -s -f "$ROOT_DIR/scripts/run-mcp-tool.sh" "$bin_file"
|
||||||
|
fi
|
||||||
|
echo "Build bin/$tool"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# @cmd Merge mcp tools into functions.json
|
||||||
|
merge-functions() {
|
||||||
|
jq --argjson json1 "$(unmerge-functions)" --argjson json2 "$(build-declarations)" -n '($json1 + $json2)'
|
||||||
|
}
|
||||||
|
|
||||||
|
# @cmd Unmerge mcp tools from functions.json
|
||||||
|
unmerge-functions() {
|
||||||
|
functions="[]"
|
||||||
|
if [[ -f "$FUNCTIONS_JSON_PATH" ]]; then
|
||||||
|
functions="$(cat "$FUNCTIONS_JSON_PATH")"
|
||||||
|
fi
|
||||||
|
printf "%s" "$functions" | jq 'map(select(has("mcp") | not))'
|
||||||
|
}
|
||||||
|
|
||||||
|
# @cmd Build tools to bin
|
||||||
|
build-declarations() {
|
||||||
|
curl -sS http://localhost:$MCP_BRIDGE_PORT/tools | jq '.[] |= . + {mcp: true}'
|
||||||
|
}
|
||||||
|
|
||||||
|
# @cmd Wait for mcp bridge server to ready
|
||||||
|
wait-for-server() {
|
||||||
|
while true; do
|
||||||
|
if [[ "$(curl -fsS http://localhost:$MCP_BRIDGE_PORT/health 2>&1)" == "OK" ]]; then
|
||||||
|
break;
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# @cmd
|
||||||
|
get-server-pid() {
|
||||||
|
curl -fsSL http://localhost:$MCP_BRIDGE_PORT/pid 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
_ask_json_data() {
|
||||||
|
declaration="$1"
|
||||||
|
echo 'Missing the JSON data but here are its properties:'
|
||||||
|
echo "$declaration" | ./scripts/declarations-util.sh pretty-print | sed -n '2,$s/^/>/p'
|
||||||
|
echo 'Generate placeholder data:'
|
||||||
|
data="$(echo "$declaration" | _declarations_json_data)"
|
||||||
|
echo "> $data"
|
||||||
|
read -e -r -p 'JSON data (Press ENTER to use placeholder): ' res
|
||||||
|
if [[ -z "$res" ]]; then
|
||||||
|
argc_json="$data"
|
||||||
|
else
|
||||||
|
argc_json="$res"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_declarations_json_data() {
|
||||||
|
./scripts/declarations-util.sh generate-json | tail -n +2
|
||||||
|
}
|
||||||
|
|
||||||
|
_build_win_shim() {
|
||||||
|
run="\"$(argc --argc-shell-path)\" --noprofile --norc"
|
||||||
|
cat <<-EOF
|
||||||
|
@echo off
|
||||||
|
setlocal
|
||||||
|
|
||||||
|
set "bin_dir=%~dp0"
|
||||||
|
for %%i in ("%bin_dir:~0,-1%") do set "script_dir=%%~dpi"
|
||||||
|
set "script_name=%~n0"
|
||||||
|
|
||||||
|
$run "%script_dir%scripts\run-mcp-tool.sh" "%script_name%" %*
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
_is_win() {
|
||||||
|
if [[ "$OS" == "Windows_NT" ]]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_choice_tool() {
|
||||||
|
build-declarations | jq -r '.[].name'
|
||||||
|
}
|
||||||
|
|
||||||
|
_die() {
|
||||||
|
echo "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# See more details at https://github.com/sigoden/argc
|
||||||
|
eval "$(argc --argc-eval "$0" "$@")"
|
||||||
Executable
+93
@@ -0,0 +1,93 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
main() {
|
||||||
|
root_dir="$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )/.." &> /dev/null && pwd)"
|
||||||
|
self_name=run-mcp-tool.sh
|
||||||
|
parse_argv "$@"
|
||||||
|
load_env "$root_dir/.env"
|
||||||
|
run
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_argv() {
|
||||||
|
if [[ "$0" == *"$self_name" ]]; then
|
||||||
|
tool_name="$1"
|
||||||
|
tool_data="$2"
|
||||||
|
else
|
||||||
|
tool_name="$(basename "$0")"
|
||||||
|
tool_data="$1"
|
||||||
|
fi
|
||||||
|
if [[ "$tool_name" == *.sh ]]; then
|
||||||
|
tool_name="${tool_name:0:$((${#tool_name}-3))}"
|
||||||
|
fi
|
||||||
|
if [[ -z "$tool_data" ]] || [[ -z "$tool_name" ]]; then
|
||||||
|
die "usage: ./run-tool.sh <tool-name> <tool-data>"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
load_env() {
|
||||||
|
local env_file="$1" env_vars
|
||||||
|
if [[ -f "$env_file" ]]; then
|
||||||
|
while IFS='=' read -r key value; do
|
||||||
|
if [[ "$key" == $'#'* ]] || [[ -z "$key" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [[ -z "${!key+x}" ]]; then
|
||||||
|
env_vars="$env_vars $key=$value"
|
||||||
|
fi
|
||||||
|
done < <(cat "$env_file"; echo "")
|
||||||
|
if [[ -n "$env_vars" ]]; then
|
||||||
|
eval "export $env_vars"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
run() {
|
||||||
|
no_llm_output=0
|
||||||
|
if [[ -z "$LLM_OUTPUT" ]]; then
|
||||||
|
no_llm_output=1
|
||||||
|
export LLM_OUTPUT="$(mktemp)"
|
||||||
|
fi
|
||||||
|
curl -sS "http://localhost:${MCP_BRIDGE_PORT:-8808}/tools/$tool_name" \
|
||||||
|
-X POST \
|
||||||
|
-H 'content-type: application/json' \
|
||||||
|
-d "$tool_data" > "$LLM_OUTPUT"
|
||||||
|
|
||||||
|
if [[ "$no_llm_output" -eq 1 ]]; then
|
||||||
|
cat "$LLM_OUTPUT"
|
||||||
|
else
|
||||||
|
dump_result
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
dump_result() {
|
||||||
|
if [ ! -t 1 ]; then
|
||||||
|
return;
|
||||||
|
fi
|
||||||
|
|
||||||
|
local agent_env_name agent_env_value func_env_name func_env_value show_result=0
|
||||||
|
agent_env_name="LLM_AGENT_DUMP_RESULT_$(echo "$LLM_AGENT_NAME" | tr '[:lower:]' '[:upper:]' | tr '-' '_')"
|
||||||
|
agent_env_value="${!agent_env_name:-"$LLM_AGENT_DUMP_RESULT"}"
|
||||||
|
func_env_name="${agent_env_name}_$(echo "$LLM_AGENT_FUNC" | tr '[:lower:]' '[:upper:]' | tr '-' '_')"
|
||||||
|
func_env_value="${!func_env_name}"
|
||||||
|
if [[ "$agent_env_value" == "1" || "$agent_env_value" == "true" ]]; then
|
||||||
|
if [[ "$func_env_value" != "0" && "$func_env_value" != "false" ]]; then
|
||||||
|
show_result=1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [[ "$func_env_value" == "1" || "$func_env_value" == "true" ]]; then
|
||||||
|
show_result=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [[ "$show_result" -ne 1 ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
cat <<EOF
|
||||||
|
$(echo -e "\e[2m")----------------------
|
||||||
|
$(cat "$LLM_OUTPUT")
|
||||||
|
----------------------$(echo -e "\e[0m")
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
Reference in New Issue
Block a user