@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.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": "npm run build",
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
- MCP_BRIDGE_DIR="${HOME}/.mcp-bridge"
6
- MCP_BRIDGE_JSON="${MCP_BRIDGE_DIR}/config.json"
7
- ENV_FILE="${MCP_BRIDGE_DIR}/.env"
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
- local clone_dir="$SERVER_DIR/mcp-server"
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
- local clone_dir="$SERVER_DIR/mcp-server"
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 "$SERVER_DIR/mcp-server/dist/cli.js" ;;
140
- hetzner) echo "$SERVER_DIR/mcp-server/dist/index.js" ;;
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
- servers = cfg.get('servers',{})
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
- servers = cfg.get('servers', {})
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/valerius21/hetzner-mcp",
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/valerius21/hetzner-mcp",
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": "valerius21",
36
+ "author": "dkruyt",
37
37
  "tags": ["cloud", "infrastructure", "servers", "vps", "hetzner"],
38
38
  "category": "infrastructure",
39
39
  "pricing": "byok",
@@ -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/kstam/wise-mcp",
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/kstam/wise-mcp",
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": "kstam",
36
+ "author": "Szotasz",
37
37
  "tags": ["payments", "transfers", "international", "currency", "fintech"],
38
38
  "category": "finance",
39
39
  "pricing": "byok",