@aiwerk/mcp-bridge 2.1.1 → 2.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiwerk/mcp-bridge",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.3",
|
|
4
4
|
"description": "Standalone MCP server that multiplexes multiple MCP servers into one interface",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/src/index.js",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"build": "tsc",
|
|
45
45
|
"test": "node --import tsx --test tests/*.test.ts",
|
|
46
46
|
"typecheck": "tsc --noEmit",
|
|
47
|
-
"prepublishOnly": "
|
|
47
|
+
"prepublishOnly": "bash scripts/validate-recipes.sh",
|
|
48
48
|
"validate-recipe": "npx tsx bin/validate-recipe.ts",
|
|
49
49
|
"lint": "eslint src/",
|
|
50
50
|
"format": "prettier --write src/",
|
|
@@ -2,9 +2,27 @@
|
|
|
2
2
|
set -e
|
|
3
3
|
|
|
4
4
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
|
|
6
|
+
# Detect OpenClaw plugin or standalone mode
|
|
7
|
+
OPENCLAW_JSON="${HOME}/.openclaw/openclaw.json"
|
|
8
|
+
OPENCLAW_PLUGIN_DIR="${HOME}/.openclaw/extensions/openclaw-mcp-bridge"
|
|
9
|
+
if [[ -f "$OPENCLAW_JSON" ]] && python3 -c "
|
|
10
|
+
import json
|
|
11
|
+
with open('$OPENCLAW_JSON') as f:
|
|
12
|
+
c = json.load(f)
|
|
13
|
+
assert 'openclaw-mcp-bridge' in c.get('plugins',{}).get('entries',{})
|
|
14
|
+
" 2>/dev/null; then
|
|
15
|
+
CONFIG_MODE="openclaw"
|
|
16
|
+
MCP_BRIDGE_DIR="$OPENCLAW_PLUGIN_DIR"
|
|
17
|
+
MCP_BRIDGE_JSON="$OPENCLAW_JSON"
|
|
18
|
+
ENV_FILE="${HOME}/.openclaw/.env"
|
|
19
|
+
echo "[mcp-bridge] Detected OpenClaw plugin mode"
|
|
20
|
+
else
|
|
21
|
+
CONFIG_MODE="standalone"
|
|
22
|
+
MCP_BRIDGE_DIR="${HOME}/.mcp-bridge"
|
|
23
|
+
MCP_BRIDGE_JSON="${MCP_BRIDGE_DIR}/config.json"
|
|
24
|
+
ENV_FILE="${MCP_BRIDGE_DIR}/.env"
|
|
25
|
+
fi
|
|
8
26
|
|
|
9
27
|
usage() {
|
|
10
28
|
echo "Usage: $0 <server-name> [--dry-run] [--remove]"
|
|
@@ -107,7 +125,9 @@ install_dependencies() {
|
|
|
107
125
|
echo "Installing @anthropic-pb/linear-mcp-server globally..."
|
|
108
126
|
npm install -g @anthropic-pb/linear-mcp-server ;;
|
|
109
127
|
wise)
|
|
110
|
-
|
|
128
|
+
# Clone outside plugin tree to avoid nested node_modules TS path conflicts
|
|
129
|
+
local clone_dir="$HOME/.openclaw/mcp-servers/wise-mcp"
|
|
130
|
+
mkdir -p "$HOME/.openclaw/mcp-servers"
|
|
111
131
|
if [ -d "$clone_dir/.git" ]; then
|
|
112
132
|
echo "Updating wise mcp-server..."; git -C "$clone_dir" pull --ff-only
|
|
113
133
|
else
|
|
@@ -116,7 +136,9 @@ install_dependencies() {
|
|
|
116
136
|
echo "Building wise mcp-server..."
|
|
117
137
|
(cd "$clone_dir" && npm install && npm run build) ;;
|
|
118
138
|
hetzner)
|
|
119
|
-
|
|
139
|
+
# Clone outside plugin tree to avoid nested node_modules TS path conflicts
|
|
140
|
+
local clone_dir="$HOME/.openclaw/mcp-servers/mcp-hetzner"
|
|
141
|
+
mkdir -p "$HOME/.openclaw/mcp-servers"
|
|
120
142
|
if [ -d "$clone_dir/.git" ]; then
|
|
121
143
|
echo "Updating hetzner mcp-server..."; git -C "$clone_dir" pull --ff-only
|
|
122
144
|
else
|
|
@@ -136,8 +158,8 @@ resolve_path_override() {
|
|
|
136
158
|
else
|
|
137
159
|
echo "$npm_root/@anthropic-pb/linear-mcp-server/build/index.js"
|
|
138
160
|
fi ;;
|
|
139
|
-
wise) echo "$
|
|
140
|
-
hetzner) echo "$
|
|
161
|
+
wise) echo "$HOME/.openclaw/mcp-servers/wise-mcp/dist/cli.js" ;;
|
|
162
|
+
hetzner) echo "$HOME/.openclaw/mcp-servers/mcp-hetzner/dist/index.js" ;;
|
|
141
163
|
*) echo "" ;;
|
|
142
164
|
esac
|
|
143
165
|
}
|
|
@@ -160,11 +182,15 @@ if [[ "$REMOVE" == "true" ]]; then
|
|
|
160
182
|
import json, sys
|
|
161
183
|
server_name = sys.argv[1]
|
|
162
184
|
config_path = sys.argv[2]
|
|
185
|
+
config_mode = sys.argv[3]
|
|
163
186
|
with open(config_path) as f:
|
|
164
187
|
cfg = json.load(f)
|
|
165
|
-
|
|
188
|
+
if config_mode == 'openclaw':
|
|
189
|
+
servers = cfg.get('plugins',{}).get('entries',{}).get('openclaw-mcp-bridge',{}).get('config',{}).get('servers',{})
|
|
190
|
+
else:
|
|
191
|
+
servers = cfg.get('servers',{})
|
|
166
192
|
print('yes' if server_name in servers else 'no')
|
|
167
|
-
" "$SERVER_NAME" "$MCP_BRIDGE_JSON" 2>/dev/null)
|
|
193
|
+
" "$SERVER_NAME" "$MCP_BRIDGE_JSON" "$CONFIG_MODE" 2>/dev/null)
|
|
168
194
|
|
|
169
195
|
if [[ "$HAS_SERVER" != "yes" ]]; then
|
|
170
196
|
echo "ℹ️ Server '$SERVER_NAME' not found in config. Nothing to remove."
|
|
@@ -181,16 +207,20 @@ print('yes' if server_name in servers else 'no')
|
|
|
181
207
|
import json, sys
|
|
182
208
|
server_name = sys.argv[1]
|
|
183
209
|
config_path = sys.argv[2]
|
|
210
|
+
config_mode = sys.argv[3]
|
|
184
211
|
with open(config_path) as f:
|
|
185
212
|
cfg = json.load(f)
|
|
186
|
-
|
|
213
|
+
if config_mode == 'openclaw':
|
|
214
|
+
servers = cfg['plugins']['entries']['openclaw-mcp-bridge']['config']['servers']
|
|
215
|
+
else:
|
|
216
|
+
servers = cfg.get('servers', {})
|
|
187
217
|
del servers[server_name]
|
|
188
218
|
with open(config_path, 'w') as f:
|
|
189
219
|
json.dump(cfg, f, indent=2)
|
|
190
220
|
f.write('\n')
|
|
191
221
|
print(f'✅ Removed {server_name} from config')
|
|
192
222
|
print(f'ℹ️ Server recipe kept in servers/{server_name}/ (reinstall anytime)')
|
|
193
|
-
" "$SERVER_NAME" "$MCP_BRIDGE_JSON" 2>/dev/null
|
|
223
|
+
" "$SERVER_NAME" "$MCP_BRIDGE_JSON" "$CONFIG_MODE" 2>/dev/null
|
|
194
224
|
|
|
195
225
|
# Remove env var from .env if exists
|
|
196
226
|
REMOVE_ENV_VAR=""
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# validate-recipes.sh — Verify all recipe.json URLs are reachable (no hallucinated repos!)
|
|
3
|
+
# Usage: ./scripts/validate-recipes.sh [--ci]
|
|
4
|
+
# Exit code 1 if any URL is broken.
|
|
5
|
+
|
|
6
|
+
set -uo pipefail
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
+
SERVERS_DIR="$SCRIPT_DIR/../servers"
|
|
10
|
+
CI_MODE="${1:-}"
|
|
11
|
+
ERRORS=0
|
|
12
|
+
CHECKED=0
|
|
13
|
+
|
|
14
|
+
RED='\033[0;31m'
|
|
15
|
+
GREEN='\033[0;32m'
|
|
16
|
+
NC='\033[0m'
|
|
17
|
+
|
|
18
|
+
echo "Validating recipe URLs..."
|
|
19
|
+
echo ""
|
|
20
|
+
|
|
21
|
+
for recipe in "$SERVERS_DIR"/*/recipe.json; do
|
|
22
|
+
server=$(basename "$(dirname "$recipe")")
|
|
23
|
+
|
|
24
|
+
# Extract all URLs from recipe.json
|
|
25
|
+
urls=$(python3 -c "
|
|
26
|
+
import json, sys
|
|
27
|
+
d = json.load(open('$recipe'))
|
|
28
|
+
urls = set()
|
|
29
|
+
if d.get('repository'): urls.add(d['repository'])
|
|
30
|
+
if d.get('install', {}).get('repository'): urls.add(d['install']['repository'])
|
|
31
|
+
if d.get('metadata', {}).get('homepage'): urls.add(d['metadata']['homepage'])
|
|
32
|
+
if d.get('auth', {}).get('credentialsUrl'): urls.add(d['auth']['credentialsUrl'])
|
|
33
|
+
for u in sorted(urls): print(u)
|
|
34
|
+
" 2>/dev/null)
|
|
35
|
+
|
|
36
|
+
# Cross-check git repos against install-server.sh
|
|
37
|
+
install_script="$SCRIPT_DIR/install-server.sh"
|
|
38
|
+
if [ -f "$install_script" ]; then
|
|
39
|
+
# Match server name in the clone context (e.g. "wise)" or "Cloning wise")
|
|
40
|
+
script_repo=$(grep -B1 -A0 -i "${server})" "$install_script" 2>/dev/null | grep -oP "git clone \Khttps://[^ ]+" | head -1 | sed 's/\.git$//')
|
|
41
|
+
recipe_repo=$(python3 -c "
|
|
42
|
+
import json
|
|
43
|
+
d = json.load(open('$recipe'))
|
|
44
|
+
r = d.get('install', {}).get('repository', d.get('repository', ''))
|
|
45
|
+
print(r.rstrip('/').removesuffix('.git'))
|
|
46
|
+
" 2>/dev/null)
|
|
47
|
+
|
|
48
|
+
if [ -n "$script_repo" ] && [ -n "$recipe_repo" ]; then
|
|
49
|
+
script_repo_clean=$(echo "$script_repo" | sed 's/\.git$//')
|
|
50
|
+
if [ "$recipe_repo" != "$script_repo_clean" ]; then
|
|
51
|
+
echo -e "${RED}❌ MISMATCH [$server]${NC}"
|
|
52
|
+
echo " recipe.json: $recipe_repo"
|
|
53
|
+
echo " install-server.sh: $script_repo_clean"
|
|
54
|
+
ERRORS=$((ERRORS + 1))
|
|
55
|
+
fi
|
|
56
|
+
fi
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# HTTP check all URLs
|
|
60
|
+
for url in $urls; do
|
|
61
|
+
CHECKED=$((CHECKED + 1))
|
|
62
|
+
status=$(curl -sI -o /dev/null -w "%{http_code}" --max-time 10 "$url" 2>/dev/null || echo "000")
|
|
63
|
+
# 401/403 are OK for credentialsUrl (login-protected pages)
|
|
64
|
+
if [ "$status" = "000" ] || [ "$status" -ge 404 ]; then
|
|
65
|
+
echo -e "${RED}❌ HTTP $status [$server] $url${NC}"
|
|
66
|
+
ERRORS=$((ERRORS + 1))
|
|
67
|
+
else
|
|
68
|
+
echo -e "${GREEN}✓${NC} [$server] $url"
|
|
69
|
+
fi
|
|
70
|
+
done
|
|
71
|
+
done
|
|
72
|
+
|
|
73
|
+
echo ""
|
|
74
|
+
echo "Checked $CHECKED URLs across $(ls -d "$SERVERS_DIR"/*/recipe.json 2>/dev/null | wc -l) recipes."
|
|
75
|
+
|
|
76
|
+
if [ $ERRORS -gt 0 ]; then
|
|
77
|
+
echo -e "${RED}$ERRORS error(s) found!${NC}"
|
|
78
|
+
exit 1
|
|
79
|
+
else
|
|
80
|
+
echo -e "${GREEN}All URLs valid.${NC}"
|
|
81
|
+
exit 0
|
|
82
|
+
fi
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"id": "hetzner",
|
|
4
4
|
"name": "Hetzner Cloud",
|
|
5
5
|
"description": "Manage Hetzner Cloud infrastructure — servers, networks, load balancers, volumes, firewalls, and more via the Hetzner Cloud API",
|
|
6
|
-
"repository": "https://github.com/
|
|
6
|
+
"repository": "https://github.com/dkruyt/mcp-hetzner",
|
|
7
7
|
|
|
8
8
|
"transports": [
|
|
9
9
|
{
|
|
@@ -26,14 +26,14 @@
|
|
|
26
26
|
|
|
27
27
|
"install": {
|
|
28
28
|
"method": "git",
|
|
29
|
-
"repository": "https://github.com/
|
|
29
|
+
"repository": "https://github.com/dkruyt/mcp-hetzner",
|
|
30
30
|
"buildCommand": "npm install && npm run build",
|
|
31
31
|
"version": "latest"
|
|
32
32
|
},
|
|
33
33
|
|
|
34
34
|
"metadata": {
|
|
35
35
|
"homepage": "https://www.hetzner.com/cloud",
|
|
36
|
-
"author": "
|
|
36
|
+
"author": "dkruyt",
|
|
37
37
|
"tags": ["cloud", "infrastructure", "servers", "vps", "hetzner"],
|
|
38
38
|
"category": "infrastructure",
|
|
39
39
|
"pricing": "byok",
|
package/servers/wise/recipe.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"id": "wise",
|
|
4
4
|
"name": "Wise",
|
|
5
5
|
"description": "International money transfers and multi-currency account management via the Wise (TransferWise) API",
|
|
6
|
-
"repository": "https://github.com/
|
|
6
|
+
"repository": "https://github.com/Szotasz/wise-mcp",
|
|
7
7
|
|
|
8
8
|
"transports": [
|
|
9
9
|
{
|
|
@@ -26,14 +26,14 @@
|
|
|
26
26
|
|
|
27
27
|
"install": {
|
|
28
28
|
"method": "git",
|
|
29
|
-
"repository": "https://github.com/
|
|
29
|
+
"repository": "https://github.com/Szotasz/wise-mcp",
|
|
30
30
|
"buildCommand": "npm install && npm run build",
|
|
31
31
|
"version": "latest"
|
|
32
32
|
},
|
|
33
33
|
|
|
34
34
|
"metadata": {
|
|
35
35
|
"homepage": "https://wise.com/",
|
|
36
|
-
"author": "
|
|
36
|
+
"author": "Szotasz",
|
|
37
37
|
"tags": ["payments", "transfers", "international", "currency", "fintech"],
|
|
38
38
|
"category": "finance",
|
|
39
39
|
"pricing": "byok",
|