@aiwerk/mcp-bridge 1.0.0 → 1.0.2

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.
Files changed (63) hide show
  1. package/dist/bin/mcp-bridge.d.ts +2 -0
  2. package/dist/bin/mcp-bridge.js +320 -0
  3. package/dist/src/config.d.ts +19 -0
  4. package/dist/src/config.js +145 -0
  5. package/{src/index.ts → dist/src/index.d.ts} +1 -30
  6. package/dist/src/index.js +21 -0
  7. package/dist/src/mcp-router.d.ts +65 -0
  8. package/dist/src/mcp-router.js +271 -0
  9. package/dist/src/protocol.d.ts +4 -0
  10. package/dist/src/protocol.js +58 -0
  11. package/dist/src/schema-convert.d.ts +11 -0
  12. package/dist/src/schema-convert.js +150 -0
  13. package/dist/src/standalone-server.d.ts +30 -0
  14. package/dist/src/standalone-server.js +312 -0
  15. package/dist/src/tool-naming.d.ts +3 -0
  16. package/dist/src/tool-naming.js +38 -0
  17. package/dist/src/transport-base.d.ts +76 -0
  18. package/dist/src/transport-base.js +163 -0
  19. package/dist/src/transport-sse.d.ts +16 -0
  20. package/dist/src/transport-sse.js +207 -0
  21. package/dist/src/transport-stdio.d.ts +20 -0
  22. package/dist/src/transport-stdio.js +281 -0
  23. package/dist/src/transport-streamable-http.d.ts +11 -0
  24. package/dist/src/transport-streamable-http.js +164 -0
  25. package/dist/src/types.d.ts +72 -0
  26. package/dist/src/types.js +4 -0
  27. package/dist/src/update-checker.d.ts +25 -0
  28. package/dist/src/update-checker.js +132 -0
  29. package/package.json +19 -4
  30. package/scripts/install-server.ps1 +25 -58
  31. package/scripts/install-server.sh +37 -90
  32. package/servers/apify/README.md +6 -6
  33. package/servers/github/README.md +6 -6
  34. package/servers/google-maps/README.md +6 -6
  35. package/servers/hetzner/README.md +6 -6
  36. package/servers/hostinger/README.md +6 -6
  37. package/servers/linear/README.md +6 -6
  38. package/servers/miro/README.md +6 -6
  39. package/servers/notion/README.md +6 -6
  40. package/servers/stripe/README.md +6 -6
  41. package/servers/tavily/README.md +6 -6
  42. package/servers/todoist/README.md +6 -6
  43. package/servers/wise/README.md +6 -6
  44. package/bin/mcp-bridge.js +0 -9
  45. package/bin/mcp-bridge.ts +0 -335
  46. package/src/config.ts +0 -168
  47. package/src/mcp-router.ts +0 -366
  48. package/src/protocol.ts +0 -69
  49. package/src/schema-convert.ts +0 -178
  50. package/src/standalone-server.ts +0 -385
  51. package/src/tool-naming.ts +0 -51
  52. package/src/transport-base.ts +0 -199
  53. package/src/transport-sse.ts +0 -230
  54. package/src/transport-stdio.ts +0 -312
  55. package/src/transport-streamable-http.ts +0 -188
  56. package/src/types.ts +0 -88
  57. package/src/update-checker.ts +0 -155
  58. package/tests/collision.test.ts +0 -60
  59. package/tests/env-resolve.test.ts +0 -68
  60. package/tests/mcp-router.test.ts +0 -301
  61. package/tests/schema-convert.test.ts +0 -70
  62. package/tests/transport-base.test.ts +0 -214
  63. package/tsconfig.json +0 -15
@@ -0,0 +1,72 @@
1
+ export interface Logger {
2
+ info: (...args: unknown[]) => void;
3
+ warn: (...args: unknown[]) => void;
4
+ error: (...args: unknown[]) => void;
5
+ debug: (...args: unknown[]) => void;
6
+ }
7
+ export interface McpServerConfig {
8
+ transport: "sse" | "stdio" | "streamable-http";
9
+ /** Human-readable description for router tool description generation */
10
+ description?: string;
11
+ url?: string;
12
+ headers?: Record<string, string>;
13
+ command?: string;
14
+ args?: string[];
15
+ env?: Record<string, string>;
16
+ framing?: "auto" | "lsp" | "newline";
17
+ }
18
+ export interface McpClientConfig {
19
+ servers: Record<string, McpServerConfig>;
20
+ mode?: "direct" | "router";
21
+ toolPrefix?: boolean | "auto";
22
+ reconnectIntervalMs?: number;
23
+ connectionTimeoutMs?: number;
24
+ requestTimeoutMs?: number;
25
+ routerIdleTimeoutMs?: number;
26
+ routerMaxConcurrent?: number;
27
+ }
28
+ export interface McpTool {
29
+ name: string;
30
+ description: string;
31
+ inputSchema: any;
32
+ }
33
+ export interface McpRequest {
34
+ jsonrpc: "2.0";
35
+ id?: number;
36
+ method: string;
37
+ params?: any;
38
+ }
39
+ export declare function nextRequestId(): number;
40
+ export interface McpResponse {
41
+ jsonrpc: "2.0";
42
+ id: number;
43
+ result?: any;
44
+ error?: {
45
+ code: number;
46
+ message: string;
47
+ data?: any;
48
+ };
49
+ }
50
+ export interface McpTransport {
51
+ connect(): Promise<void>;
52
+ disconnect(): Promise<void>;
53
+ sendRequest(request: McpRequest): Promise<McpResponse>;
54
+ sendNotification(notification: any): Promise<void>;
55
+ isConnected(): boolean;
56
+ }
57
+ export interface McpServerConnection {
58
+ name: string;
59
+ transport: McpTransport;
60
+ tools: McpTool[];
61
+ isInitialized: boolean;
62
+ registeredToolNames: string[];
63
+ }
64
+ /** Bridge-level config loaded from ~/.mcp-bridge/config.json */
65
+ export interface BridgeConfig extends McpClientConfig {
66
+ http?: {
67
+ auth?: {
68
+ type: "bearer";
69
+ token: string;
70
+ };
71
+ };
72
+ }
@@ -0,0 +1,4 @@
1
+ let globalRequestId = 1;
2
+ export function nextRequestId() {
3
+ return globalRequestId++;
4
+ }
@@ -0,0 +1,25 @@
1
+ import { Logger } from "./types.js";
2
+ export interface UpdateInfo {
3
+ currentVersion: string;
4
+ latestVersion: string;
5
+ updateAvailable: boolean;
6
+ updateCommand: string;
7
+ }
8
+ /**
9
+ * Check npm registry for a newer version. Non-blocking, best-effort.
10
+ * Caches result for the lifetime of the process.
11
+ */
12
+ export declare function checkForUpdate(logger: Logger): Promise<UpdateInfo>;
13
+ /**
14
+ * Build the notice string to inject into the first tool response.
15
+ * Returns empty string if no update or already delivered.
16
+ */
17
+ export declare function getUpdateNotice(): string;
18
+ /**
19
+ * Reset the notice flag (for testing).
20
+ */
21
+ export declare function resetNoticeFlag(): void;
22
+ /**
23
+ * Execute the actual npm update. Returns a result message.
24
+ */
25
+ export declare function runUpdate(logger: Logger): Promise<string>;
@@ -0,0 +1,132 @@
1
+ import { execSync, exec as execCb } from "child_process";
2
+ import { PACKAGE_VERSION } from "./protocol.js";
3
+ const PACKAGE_NAME = "@aiwerk/mcp-bridge";
4
+ let cachedUpdateInfo = null;
5
+ let noticeDelivered = false;
6
+ /**
7
+ * Check npm registry for a newer version. Non-blocking, best-effort.
8
+ * Caches result for the lifetime of the process.
9
+ */
10
+ export async function checkForUpdate(logger) {
11
+ if (cachedUpdateInfo)
12
+ return cachedUpdateInfo;
13
+ const current = PACKAGE_VERSION;
14
+ const updateCmd = `npm update -g ${PACKAGE_NAME}`;
15
+ try {
16
+ const latest = await npmViewVersion(logger);
17
+ const updateAvailable = latest !== current && isNewer(latest, current);
18
+ cachedUpdateInfo = {
19
+ currentVersion: current,
20
+ latestVersion: latest,
21
+ updateAvailable,
22
+ updateCommand: updateCmd,
23
+ };
24
+ if (updateAvailable) {
25
+ logger.info(`[mcp-bridge] Update available: ${current} → ${latest}`);
26
+ }
27
+ else {
28
+ logger.info(`[mcp-bridge] Version ${current} is up to date`);
29
+ }
30
+ }
31
+ catch (err) {
32
+ logger.warn(`[mcp-bridge] Version check failed: ${err instanceof Error ? err.message : err}`);
33
+ cachedUpdateInfo = {
34
+ currentVersion: current,
35
+ latestVersion: current,
36
+ updateAvailable: false,
37
+ updateCommand: updateCmd,
38
+ };
39
+ }
40
+ return cachedUpdateInfo;
41
+ }
42
+ /**
43
+ * Build the notice string to inject into the first tool response.
44
+ * Returns empty string if no update or already delivered.
45
+ */
46
+ export function getUpdateNotice() {
47
+ if (noticeDelivered || !cachedUpdateInfo?.updateAvailable)
48
+ return "";
49
+ noticeDelivered = true;
50
+ return (`\n\n---\nUpdate available: ${cachedUpdateInfo.currentVersion} → ${cachedUpdateInfo.latestVersion}\n` +
51
+ `Run: ${cachedUpdateInfo.updateCommand}`);
52
+ }
53
+ /**
54
+ * Reset the notice flag (for testing).
55
+ */
56
+ export function resetNoticeFlag() {
57
+ noticeDelivered = false;
58
+ }
59
+ /**
60
+ * Execute the actual npm update. Returns a result message.
61
+ */
62
+ export async function runUpdate(logger) {
63
+ const info = cachedUpdateInfo ?? await checkForUpdate(logger);
64
+ if (!info.updateAvailable) {
65
+ return `MCP Bridge is already up to date (v${info.currentVersion}).`;
66
+ }
67
+ logger.info(`[mcp-bridge] Running update: ${info.updateCommand}`);
68
+ try {
69
+ const output = await execAsync(info.updateCommand, 60_000);
70
+ // Invalidate cache so next check re-fetches
71
+ cachedUpdateInfo = null;
72
+ noticeDelivered = false;
73
+ // Verify new version
74
+ const newVersion = npmViewVersionSync(logger);
75
+ return (`MCP Bridge updated: ${info.currentVersion} → ${newVersion}\n` +
76
+ `A restart is needed to load the new version.\n\n` +
77
+ `npm output:\n${output.trim()}`);
78
+ }
79
+ catch (err) {
80
+ const msg = err instanceof Error ? err.message : String(err);
81
+ logger.error(`[mcp-bridge] Update failed: ${msg}`);
82
+ return (`Update failed. You can try manually:\n` +
83
+ `${info.updateCommand}\n\nError: ${msg}`);
84
+ }
85
+ }
86
+ // --- helpers ---
87
+ function npmViewVersion(_logger) {
88
+ return new Promise((resolve, reject) => {
89
+ const timeout = setTimeout(() => reject(new Error("npm view timed out")), 10_000);
90
+ execCb(`npm view ${PACKAGE_NAME} version`, { encoding: "utf-8" }, (err, stdout) => {
91
+ clearTimeout(timeout);
92
+ if (err)
93
+ return reject(err);
94
+ const ver = (stdout ?? "").trim();
95
+ if (!ver)
96
+ return reject(new Error("empty version from npm"));
97
+ resolve(ver);
98
+ });
99
+ });
100
+ }
101
+ function npmViewVersionSync(_logger) {
102
+ try {
103
+ return execSync(`npm view ${PACKAGE_NAME} version`, { encoding: "utf-8", timeout: 10_000 }).trim();
104
+ }
105
+ catch {
106
+ return "unknown";
107
+ }
108
+ }
109
+ function execAsync(cmd, timeoutMs) {
110
+ return new Promise((resolve, reject) => {
111
+ const timeout = setTimeout(() => reject(new Error(`Command timed out after ${timeoutMs}ms`)), timeoutMs);
112
+ execCb(cmd, { encoding: "utf-8", timeout: timeoutMs }, (err, stdout, stderr) => {
113
+ clearTimeout(timeout);
114
+ if (err)
115
+ return reject(new Error(`${err.message}\n${stderr ?? ""}`));
116
+ resolve(stdout ?? "");
117
+ });
118
+ });
119
+ }
120
+ function isNewer(latest, current) {
121
+ const l = latest.split(".").map(Number);
122
+ const c = current.split(".").map(Number);
123
+ for (let i = 0; i < Math.max(l.length, c.length); i++) {
124
+ const lv = l[i] ?? 0;
125
+ const cv = c[i] ?? 0;
126
+ if (lv > cv)
127
+ return true;
128
+ if (lv < cv)
129
+ return false;
130
+ }
131
+ return false;
132
+ }
package/package.json CHANGED
@@ -1,12 +1,26 @@
1
1
  {
2
2
  "name": "@aiwerk/mcp-bridge",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Standalone MCP server that multiplexes multiple MCP servers into one interface",
5
5
  "type": "module",
6
- "main": "./src/index.ts",
6
+ "main": "./dist/src/index.js",
7
+ "types": "./dist/src/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/src/index.d.ts",
11
+ "import": "./dist/src/index.js"
12
+ }
13
+ },
7
14
  "bin": {
8
- "mcp-bridge": "./bin/mcp-bridge.js"
15
+ "mcp-bridge": "./dist/bin/mcp-bridge.js"
9
16
  },
17
+ "files": [
18
+ "dist/",
19
+ "servers/",
20
+ "scripts/",
21
+ "LICENSE",
22
+ "README.md"
23
+ ],
10
24
  "license": "MIT",
11
25
  "author": "AIWerk <kontakt@aiwerk.ch>",
12
26
  "homepage": "https://github.com/AIWerk/mcp-bridge#readme",
@@ -29,7 +43,8 @@
29
43
  "scripts": {
30
44
  "build": "tsc",
31
45
  "test": "node --import tsx --test tests/*.test.ts",
32
- "typecheck": "tsc --noEmit"
46
+ "typecheck": "tsc --noEmit",
47
+ "prepublishOnly": "npm run build"
33
48
  },
34
49
  "dependencies": {
35
50
  "@sinclair/typebox": "^0.34.0"
@@ -1,9 +1,9 @@
1
1
  $ErrorActionPreference = "Stop"
2
2
 
3
3
  $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
4
- $OpenclawDir = Join-Path $env:USERPROFILE ".openclaw"
5
- $EnvFile = Join-Path $OpenclawDir ".env"
6
- $OpenclawJson = Join-Path $OpenclawDir "openclaw.json"
4
+ $McpBridgeDir = Join-Path $env:USERPROFILE ".mcp-bridge"
5
+ $EnvFile = Join-Path $McpBridgeDir ".env"
6
+ $McpBridgeJson = Join-Path $McpBridgeDir "config.json"
7
7
 
8
8
  if ($args.Count -eq 0) {
9
9
  Write-Host "Usage: install-server.ps1 <server-name> [--dry-run] [--remove]"
@@ -133,26 +133,26 @@ if ($Remove) {
133
133
  Write-Host "Removing $ServerTitle MCP Server"
134
134
  Write-Host "========================================"
135
135
 
136
- if (-not (Test-Path $OpenclawJson)) {
137
- Write-Host "❌ Config not found: $OpenclawJson" -ForegroundColor Red
136
+ if (-not (Test-Path $McpBridgeJson)) {
137
+ Write-Host "❌ Config not found: $McpBridgeJson" -ForegroundColor Red
138
138
  exit 1
139
139
  }
140
140
 
141
- $cfg = Get-Content $OpenclawJson -Raw | ConvertFrom-Json
142
- $servers = $cfg.plugins.entries.'openclaw-mcp-bridge'.config.servers
141
+ $cfg = Get-Content $McpBridgeJson -Raw | ConvertFrom-Json
142
+ $servers = $cfg.servers
143
143
  if (-not ($servers.PSObject.Properties.Name -contains $ServerName)) {
144
144
  Write-Host "ℹ️ Server '$ServerName' not found in config. Nothing to remove." -ForegroundColor Yellow
145
145
  exit 0
146
146
  }
147
147
 
148
148
  # Backup
149
- $backupFile = "$OpenclawJson.bak-$(Get-Date -Format 'yyyyMMddHHmmss')"
150
- Copy-Item $OpenclawJson $backupFile
149
+ $backupFile = "$McpBridgeJson.bak-$(Get-Date -Format 'yyyyMMddHHmmss')"
150
+ Copy-Item $McpBridgeJson $backupFile
151
151
  Write-Host "Backup: $backupFile"
152
152
 
153
153
  # Remove server entry
154
154
  $servers.PSObject.Properties.Remove($ServerName)
155
- $cfg | ConvertTo-Json -Depth 10 | Set-Content $OpenclawJson -Encoding UTF8
155
+ $cfg | ConvertTo-Json -Depth 10 | Set-Content $McpBridgeJson -Encoding UTF8
156
156
  Write-Host "✅ Removed $ServerName from config" -ForegroundColor Green
157
157
  Write-Host "ℹ️ Server recipe kept in servers\$ServerName\ (reinstall anytime)" -ForegroundColor Cyan
158
158
 
@@ -168,17 +168,7 @@ if ($Remove) {
168
168
  }
169
169
  }
170
170
 
171
- $restart = Read-Host "Restart gateway now? [Y/n]"
172
- if ([string]::IsNullOrEmpty($restart) -or $restart -match '^[Yy]$') {
173
- try {
174
- Restart-Service openclaw-gateway -ErrorAction Stop
175
- Write-Host "✅ Gateway restarted. $ServerTitle removed." -ForegroundColor Green
176
- } catch {
177
- Write-Host "⚠️ Auto-restart failed. Run: Restart-Service openclaw-gateway" -ForegroundColor Yellow
178
- }
179
- } else {
180
- Write-Host "⏭️ Run manually: Restart-Service openclaw-gateway"
181
- }
171
+ Write-Host "✅ $ServerTitle removed. Restart mcp-bridge to apply." -ForegroundColor Green
182
172
  exit 0
183
173
  }
184
174
 
@@ -218,7 +208,7 @@ while ([string]::IsNullOrWhiteSpace($Token)) {
218
208
  }
219
209
 
220
210
  # 4. Write to .env
221
- New-Item -ItemType Directory -Force -Path $OpenclawDir | Out-Null
211
+ New-Item -ItemType Directory -Force -Path $McpBridgeDir | Out-Null
222
212
  if (-not (Test-Path $EnvFile)) { New-Item -ItemType File -Force -Path $EnvFile | Out-Null }
223
213
 
224
214
  $envExists = Select-String -Path $EnvFile -Pattern "^$([regex]::Escape($EnvVarName))=" -Quiet
@@ -237,14 +227,14 @@ if ($envExists) {
237
227
  Write-Host "Saved $EnvVarName to $EnvFile"
238
228
  }
239
229
 
240
- # 5. Backup and merge openclaw.json
241
- if (-not (Test-Path $OpenclawJson)) { Set-Content -Path $OpenclawJson -Value "{}" -Encoding UTF8 }
230
+ # 5. Backup and merge config.json
231
+ if (-not (Test-Path $McpBridgeJson)) { Set-Content -Path $McpBridgeJson -Value "{}" -Encoding UTF8 }
242
232
 
243
233
  $timestamp = Get-Date -Format "yyyyMMddHHmmss"
244
- Copy-Item -Path $OpenclawJson -Destination "$OpenclawJson.bak-$timestamp" -Force
245
- Write-Host "Backup: $OpenclawJson.bak-$timestamp"
234
+ Copy-Item -Path $McpBridgeJson -Destination "$McpBridgeJson.bak-$timestamp" -Force
235
+ Write-Host "Backup: $McpBridgeJson.bak-$timestamp"
246
236
 
247
- $cfgRaw = Get-Content -Path $OpenclawJson -Raw
237
+ $cfgRaw = Get-Content -Path $McpBridgeJson -Raw
248
238
  if ([string]::IsNullOrWhiteSpace($cfgRaw)) { $cfgRaw = "{}" }
249
239
  $cfg = $cfgRaw | ConvertFrom-Json
250
240
  $serverConfig = Get-Content -Path $ServerConfigFile -Raw | ConvertFrom-Json
@@ -258,43 +248,20 @@ if ($pathOverride -and $serverConfig.args -and $serverConfig.args.Count -gt 0) {
258
248
  }
259
249
  }
260
250
 
261
- $plugins = Ensure-Property -Object $cfg -Name "plugins" -DefaultValue ([PSCustomObject]@{})
262
- $allow = Ensure-Property -Object $plugins -Name "allow" -DefaultValue @()
263
- if ($allow -notcontains "openclaw-mcp-bridge") { $plugins.allow = @($allow) + "openclaw-mcp-bridge" }
264
- $entries = Ensure-Property -Object $plugins -Name "entries" -DefaultValue ([PSCustomObject]@{})
265
-
266
- if (-not ($entries.PSObject.Properties.Name -contains "openclaw-mcp-bridge")) {
267
- $entries | Add-Member -NotePropertyName "openclaw-mcp-bridge" -NotePropertyValue ([PSCustomObject]@{})
268
- }
269
- $mcpClient = $entries."openclaw-mcp-bridge"
270
- if (-not ($mcpClient.PSObject.Properties.Name -contains "enabled")) {
271
- $mcpClient | Add-Member -NotePropertyName "enabled" -NotePropertyValue $true
272
- }
273
- $mcpConfig = Ensure-Property -Object $mcpClient -Name "config" -DefaultValue ([PSCustomObject]@{})
274
- if (-not ($mcpConfig.PSObject.Properties.Name -contains "toolPrefix")) { $mcpConfig | Add-Member -NotePropertyName "toolPrefix" -NotePropertyValue $true }
275
- if (-not ($mcpConfig.PSObject.Properties.Name -contains "reconnectIntervalMs")) { $mcpConfig | Add-Member -NotePropertyName "reconnectIntervalMs" -NotePropertyValue 30000 }
276
- if (-not ($mcpConfig.PSObject.Properties.Name -contains "connectionTimeoutMs")) { $mcpConfig | Add-Member -NotePropertyName "connectionTimeoutMs" -NotePropertyValue 10000 }
277
- if (-not ($mcpConfig.PSObject.Properties.Name -contains "requestTimeoutMs")) { $mcpConfig | Add-Member -NotePropertyName "requestTimeoutMs" -NotePropertyValue 60000 }
278
- $servers = Ensure-Property -Object $mcpConfig -Name "servers" -DefaultValue ([PSCustomObject]@{})
251
+ if (-not ($cfg.PSObject.Properties.Name -contains "toolPrefix")) { $cfg | Add-Member -NotePropertyName "toolPrefix" -NotePropertyValue $true }
252
+ if (-not ($cfg.PSObject.Properties.Name -contains "reconnectIntervalMs")) { $cfg | Add-Member -NotePropertyName "reconnectIntervalMs" -NotePropertyValue 30000 }
253
+ if (-not ($cfg.PSObject.Properties.Name -contains "connectionTimeoutMs")) { $cfg | Add-Member -NotePropertyName "connectionTimeoutMs" -NotePropertyValue 10000 }
254
+ if (-not ($cfg.PSObject.Properties.Name -contains "requestTimeoutMs")) { $cfg | Add-Member -NotePropertyName "requestTimeoutMs" -NotePropertyValue 60000 }
255
+ $servers = Ensure-Property -Object $cfg -Name "servers" -DefaultValue ([PSCustomObject]@{})
279
256
 
280
257
  if ($servers.PSObject.Properties.Name -contains $ServerName) {
281
258
  $servers.PSObject.Properties.Remove($ServerName)
282
259
  }
283
260
  $servers | Add-Member -NotePropertyName $ServerName -NotePropertyValue $serverConfig
284
261
 
285
- $cfg | ConvertTo-Json -Depth 30 | Set-Content -Path $OpenclawJson -Encoding UTF8
262
+ $cfg | ConvertTo-Json -Depth 30 | Set-Content -Path $McpBridgeJson -Encoding UTF8
286
263
  Write-Host "Configuration merged for: $ServerName"
287
264
 
288
- # 6. Gateway restart
289
265
  Write-Host ""
290
- $restart = Read-Host "Restart gateway now? [Y/n]"
291
- if ([string]::IsNullOrWhiteSpace($restart) -or $restart -match "^[Yy]$") {
292
- try {
293
- openclaw gateway restart 2>$null
294
- Write-Host "Gateway restarting... Check 'openclaw gateway status' in a moment."
295
- } catch {
296
- Write-Host "Could not restart automatically. Run: openclaw gateway restart"
297
- }
298
- } else {
299
- Write-Host "Run manually: openclaw gateway restart"
300
- }
266
+ Write-Host "$ServerTitle MCP Server installed."
267
+ Write-Host "Restart mcp-bridge to pick up the new server configuration."
@@ -2,9 +2,9 @@
2
2
  set -e
3
3
 
4
4
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
- OPENCLAW_DIR="${HOME}/.openclaw"
6
- OPENCLAW_JSON="${OPENCLAW_DIR}/openclaw.json"
7
- ENV_FILE="${OPENCLAW_DIR}/.env"
5
+ MCP_BRIDGE_DIR="${HOME}/.mcp-bridge"
6
+ MCP_BRIDGE_JSON="${MCP_BRIDGE_DIR}/config.json"
7
+ ENV_FILE="${MCP_BRIDGE_DIR}/.env"
8
8
 
9
9
  usage() {
10
10
  echo "Usage: $0 <server-name> [--dry-run] [--remove]"
@@ -128,17 +128,17 @@ if [[ "$REMOVE" == "true" ]]; then
128
128
  echo "Removing ${SERVER_TITLE} MCP Server"
129
129
  echo "========================================"
130
130
 
131
- if [[ ! -f "$OPENCLAW_JSON" ]]; then
132
- echo "❌ Config not found: $OPENCLAW_JSON"
131
+ if [[ ! -f "$MCP_BRIDGE_JSON" ]]; then
132
+ echo "❌ Config not found: $MCP_BRIDGE_JSON"
133
133
  exit 1
134
134
  fi
135
135
 
136
136
  # Check if server exists in config
137
137
  HAS_SERVER=$(python3 -c "
138
138
  import json
139
- with open('$OPENCLAW_JSON') as f:
139
+ with open('$MCP_BRIDGE_JSON') as f:
140
140
  cfg = json.load(f)
141
- servers = cfg.get('plugins',{}).get('entries',{}).get('openclaw-mcp-bridge',{}).get('config',{}).get('servers',{})
141
+ servers = cfg.get('servers',{})
142
142
  print('yes' if '$SERVER_NAME' in servers else 'no')
143
143
  " 2>/dev/null)
144
144
 
@@ -148,18 +148,18 @@ print('yes' if '$SERVER_NAME' in servers else 'no')
148
148
  fi
149
149
 
150
150
  # Backup
151
- BACKUP_FILE="${OPENCLAW_JSON}.bak-$(date +%Y%m%d%H%M%S)"
152
- cp "$OPENCLAW_JSON" "$BACKUP_FILE"
151
+ BACKUP_FILE="${MCP_BRIDGE_JSON}.bak-$(date +%Y%m%d%H%M%S)"
152
+ cp "$MCP_BRIDGE_JSON" "$BACKUP_FILE"
153
153
  echo "Backup: ${BACKUP_FILE}"
154
154
 
155
155
  # Remove server entry from config (keep servers/<name>/ directory)
156
156
  python3 -c "
157
157
  import json
158
- with open('$OPENCLAW_JSON') as f:
158
+ with open('$MCP_BRIDGE_JSON') as f:
159
159
  cfg = json.load(f)
160
- servers = cfg['plugins']['entries']['openclaw-mcp-bridge']['config']['servers']
160
+ servers = cfg.get('servers', {})
161
161
  del servers['$SERVER_NAME']
162
- with open('$OPENCLAW_JSON', 'w') as f:
162
+ with open('$MCP_BRIDGE_JSON', 'w') as f:
163
163
  json.dump(cfg, f, indent=2)
164
164
  f.write('\n')
165
165
  print('✅ Removed $SERVER_NAME from config')
@@ -182,21 +182,21 @@ print('ℹ️ Server recipe kept in servers/$SERVER_NAME/ (reinstall anytime)')
182
182
  read -r -p "Restart gateway now? [Y/n]: " RESTART </dev/tty
183
183
  fi
184
184
  if [[ -z "$RESTART" || "$RESTART" =~ ^[Yy]$ ]]; then
185
- systemctl --user restart openclaw-gateway 2>/dev/null || {
186
- echo "⚠️ Auto-restart failed. Run: systemctl --user restart openclaw-gateway"
185
+ systemctl --user restart mcp-bridge 2>/dev/null || {
186
+ echo "⚠️ Auto-restart failed. Run: systemctl --user restart mcp-bridge"
187
187
  exit 0
188
188
  }
189
189
  sleep 3
190
- if systemctl --user is-active --quiet openclaw-gateway 2>/dev/null; then
191
- echo "✅ Gateway restarted. ${SERVER_TITLE} removed."
190
+ if systemctl --user is-active --quiet mcp-bridge 2>/dev/null; then
191
+ echo "✅ Service restarted. ${SERVER_TITLE} removed."
192
192
  else
193
- echo "❌ Gateway failed to start! Restoring backup..."
194
- cp "$BACKUP_FILE" "$OPENCLAW_JSON"
195
- systemctl --user restart openclaw-gateway 2>/dev/null
193
+ echo "❌ Service failed to start! Restoring backup..."
194
+ cp "$BACKUP_FILE" "$MCP_BRIDGE_JSON"
195
+ systemctl --user restart mcp-bridge 2>/dev/null
196
196
  echo "Restored from backup."
197
197
  fi
198
198
  else
199
- echo "⏭️ Run manually: systemctl --user restart openclaw-gateway"
199
+ echo "⏭️ Run manually: systemctl --user restart mcp-bridge"
200
200
  fi
201
201
  exit 0
202
202
  fi
@@ -235,7 +235,7 @@ if [[ -f "$ENV_VARS_FILE" ]] && [[ -s "$ENV_VARS_FILE" ]]; then
235
235
  done
236
236
 
237
237
  # Write to .env
238
- mkdir -p "$OPENCLAW_DIR"
238
+ mkdir -p "$MCP_BRIDGE_DIR"
239
239
  touch "$ENV_FILE"
240
240
  chmod 600 "$ENV_FILE"
241
241
 
@@ -255,22 +255,22 @@ if [[ -f "$ENV_VARS_FILE" ]] && [[ -s "$ENV_VARS_FILE" ]]; then
255
255
  fi
256
256
  fi
257
257
 
258
- # 4. Backup and merge openclaw.json
259
- mkdir -p "$(dirname "$OPENCLAW_JSON")"
260
- [[ ! -f "$OPENCLAW_JSON" ]] && echo "{}" > "$OPENCLAW_JSON"
258
+ # 4. Backup and merge config.json
259
+ mkdir -p "$(dirname "$MCP_BRIDGE_JSON")"
260
+ [[ ! -f "$MCP_BRIDGE_JSON" ]] && echo "{}" > "$MCP_BRIDGE_JSON"
261
261
 
262
- BACKUP_FILE="${OPENCLAW_JSON}.bak-$(date +%Y%m%d%H%M%S)"
263
- cp "$OPENCLAW_JSON" "$BACKUP_FILE"
262
+ BACKUP_FILE="${MCP_BRIDGE_JSON}.bak-$(date +%Y%m%d%H%M%S)"
263
+ cp "$MCP_BRIDGE_JSON" "$BACKUP_FILE"
264
264
  echo "Backup: ${BACKUP_FILE}"
265
265
 
266
266
  PATH_OVERRIDE="$(resolve_path_override)"
267
267
 
268
- python3 - "$OPENCLAW_JSON" "$SERVER_CONFIG_FILE" "$SERVER_NAME" "$PATH_OVERRIDE" <<'PY'
268
+ python3 - "$MCP_BRIDGE_JSON" "$SERVER_CONFIG_FILE" "$SERVER_NAME" "$PATH_OVERRIDE" <<'PY'
269
269
  import json, sys
270
270
 
271
- openclaw_path, server_cfg_path, server_name, path_override = sys.argv[1:5]
271
+ config_path, server_cfg_path, server_name, path_override = sys.argv[1:5]
272
272
 
273
- with open(openclaw_path, "r", encoding="utf-8") as f:
273
+ with open(config_path, "r", encoding="utf-8") as f:
274
274
  raw = f.read().strip()
275
275
  cfg = json.loads(raw) if raw else {}
276
276
 
@@ -284,22 +284,14 @@ if path_override:
284
284
  if isinstance(value, str) and value.startswith("path/to/"):
285
285
  args[idx] = path_override
286
286
 
287
- plugins = cfg.setdefault("plugins", {})
288
- allow = plugins.setdefault("allow", [])
289
- if "openclaw-mcp-bridge" not in allow:
290
- allow.append("openclaw-mcp-bridge")
291
- entries = plugins.setdefault("entries", {})
292
- mcp_client = entries.setdefault("openclaw-mcp-bridge", {})
293
- mcp_client.setdefault("enabled", True)
294
- mcp_cfg = mcp_client.setdefault("config", {})
295
- mcp_cfg.setdefault("toolPrefix", True)
296
- mcp_cfg.setdefault("reconnectIntervalMs", 30000)
297
- mcp_cfg.setdefault("connectionTimeoutMs", 10000)
298
- mcp_cfg.setdefault("requestTimeoutMs", 60000)
299
- servers = mcp_cfg.setdefault("servers", {})
287
+ cfg.setdefault("toolPrefix", True)
288
+ cfg.setdefault("reconnectIntervalMs", 30000)
289
+ cfg.setdefault("connectionTimeoutMs", 10000)
290
+ cfg.setdefault("requestTimeoutMs", 60000)
291
+ servers = cfg.setdefault("servers", {})
300
292
  servers[server_name] = server_cfg
301
293
 
302
- with open(openclaw_path, "w", encoding="utf-8") as f:
294
+ with open(config_path, "w", encoding="utf-8") as f:
303
295
  json.dump(cfg, f, indent=2)
304
296
  f.write("\n")
305
297
 
@@ -308,50 +300,5 @@ PY
308
300
 
309
301
  # 5. Gateway restart
310
302
  echo ""
311
- read -r -p "Restart gateway now? [Y/n]: " RESTART </dev/tty
312
- if [[ -z "$RESTART" || "$RESTART" =~ ^[Yy]$ ]]; then
313
- systemctl --user restart openclaw-gateway 2>/dev/null || {
314
- echo "⚠️ Auto-restart failed. Run: systemctl --user restart openclaw-gateway"
315
- exit 0
316
- }
317
- echo "Waiting for gateway startup..."
318
- CONFIRMED=false
319
- ROUTER_MODE=false
320
- for i in 1 2 3 4 5 6; do
321
- sleep 5
322
- if ! systemctl --user is-active --quiet openclaw-gateway 2>/dev/null; then
323
- echo "❌ Gateway failed to start!"
324
- journalctl --user -u openclaw-gateway --since "1 min ago" --no-pager 2>/dev/null | grep -iE "error|fail|missing" | head -5
325
- echo "Full logs: journalctl --user -u openclaw-gateway --since '1 min ago' --no-pager"
326
- exit 1
327
- fi
328
- # Router mode: servers connect lazily, just check plugin loaded
329
- if journalctl --user -u openclaw-gateway --since "1 min ago" --no-pager 2>/dev/null | grep -qi "Plugin activated with.*servers configured"; then
330
- CONFIRMED=true
331
- ROUTER_MODE=true
332
- break
333
- fi
334
- # Direct mode: server connects at boot
335
- if journalctl --user -u openclaw-gateway --since "1 min ago" --no-pager 2>/dev/null | grep -qi "Server ${SERVER_NAME} initialized"; then
336
- CONFIRMED=true
337
- break
338
- fi
339
- # Check if server explicitly failed
340
- if journalctl --user -u openclaw-gateway --since "1 min ago" --no-pager 2>/dev/null | grep -qi "Startup failed: ${SERVER_NAME}"; then
341
- echo "❌ ${SERVER_TITLE} MCP Server failed to start!"
342
- journalctl --user -u openclaw-gateway --since "1 min ago" --no-pager 2>/dev/null | grep -i "$SERVER_NAME" | tail -5
343
- exit 1
344
- fi
345
- done
346
- if $CONFIRMED; then
347
- if $ROUTER_MODE; then
348
- echo "✅ ${SERVER_TITLE} configured! (Router mode — server connects on first use)"
349
- else
350
- echo "✅ ${SERVER_TITLE} MCP Server installed and running!"
351
- fi
352
- else
353
- echo "⚠️ Gateway running but plugin not confirmed after 30s. Check: journalctl --user -u openclaw-gateway -f"
354
- fi
355
- else
356
- echo "⏭️ Run manually: systemctl --user restart openclaw-gateway"
357
- fi
303
+ echo " ${SERVER_TITLE} MCP Server installed."
304
+ echo "Restart mcp-bridge to pick up the new server configuration."
@@ -10,21 +10,21 @@ Hosted Apify MCP endpoint for actors, scraping, and docs search.
10
10
 
11
11
  ### Linux / macOS
12
12
  ```bash
13
- cd ~/.openclaw/extensions/mcp-client/servers/apify
14
- chmod +x install.sh && ./install.sh
13
+ # Using mcp-bridge CLI:
14
+ mcp-bridge install apify
15
15
  ```
16
16
 
17
17
  ### Windows (PowerShell)
18
18
  ```powershell
19
- cd $env:USERPROFILE\.openclaw\extensions\mcp-client\servers\apify
20
- .\install.ps1
19
+ # Using mcp-bridge CLI:
20
+ mcp-bridge install apify
21
21
  ```
22
22
 
23
23
  ### Manual Setup
24
24
  1. Get your token: https://console.apify.com/settings/integrations
25
25
  2. Add to .env: `APIFY_TOKEN=your_token`
26
- 3. Add config to openclaw.json (see config.json)
27
- 4. Restart gateway
26
+ 3. Add config to ~/.mcp-bridge/config.json (see config.json)
27
+ 4. Restart mcp-bridge
28
28
 
29
29
  ## What you get
30
30
  - Actor discovery and execution