@aiwerk/mcp-bridge 2.1.0 → 2.1.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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @aiwerk/mcp-bridge
2
2
 
3
- [![Tests](https://github.com/AIWerk/mcp-bridge/actions/workflows/test.yml/badge.svg)](https://github.com/AIWerk/mcp-bridge/actions/workflows/test.yml)
3
+ [![CI](https://github.com/AIWerk/mcp-bridge/actions/workflows/ci.yml/badge.svg)](https://github.com/AIWerk/mcp-bridge/actions/workflows/ci.yml)
4
4
  [![npm version](https://img.shields.io/npm/v/@aiwerk/mcp-bridge.svg)](https://www.npmjs.com/package/@aiwerk/mcp-bridge)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
 
@@ -17,6 +17,12 @@ Most AI agents connect to MCP servers one-by-one. With 10+ servers, that's 10+ c
17
17
  - **Intent routing**: say what you need in plain language, the bridge finds the right tool
18
18
  - **Schema compression**: tool descriptions compressed ~57%, full schema on demand
19
19
  - **Security layer**: trust levels, tool deny/allow lists, result size limits
20
+ - **HTTP auth**: bearer token, custom headers, and **OAuth2 Client Credentials** with automatic token management
21
+ - **Result caching**: LRU cache with per-tool TTL overrides
22
+ - **Batch calls**: parallel multi-tool execution via `action=batch`
23
+ - **Multi-server resolution**: automatic tool disambiguation when multiple servers provide the same tool
24
+ - **Configurable retries**: exponential backoff for transient errors
25
+ - **Graceful shutdown**: clean process termination and connection cleanup
20
26
  - **Direct mode**: all tools registered individually with automatic prefixing
21
27
  - **3 transports**: stdio, SSE, streamable-http
22
28
  - **Built-in catalog**: 14 pre-configured servers, install with one command
@@ -363,6 +369,35 @@ When `action="call"` is used without `server=`, mcp-bridge can resolve collision
363
369
  | `sse` | `url`, `headers` | Remote SSE servers |
364
370
  | `streamable-http` | `url`, `headers` | Modern HTTP-based servers |
365
371
 
372
+ ### Authentication
373
+
374
+ SSE and streamable-HTTP transports support three auth methods:
375
+
376
+ **Bearer token:**
377
+ ```json
378
+ { "auth": { "type": "bearer", "token": "${MY_API_TOKEN}" } }
379
+ ```
380
+
381
+ **Custom headers:**
382
+ ```json
383
+ { "auth": { "type": "header", "headers": { "X-API-Key": "${MY_KEY}" } } }
384
+ ```
385
+
386
+ **OAuth2 Client Credentials** (automatic token management):
387
+ ```json
388
+ {
389
+ "auth": {
390
+ "type": "oauth2",
391
+ "clientId": "${CLIENT_ID}",
392
+ "clientSecret": "${CLIENT_SECRET}",
393
+ "tokenUrl": "https://provider.com/oauth/token",
394
+ "scopes": ["read", "write"]
395
+ }
396
+ }
397
+ ```
398
+
399
+ OAuth2 features: automatic token acquisition, caching with expiry-aware refresh, single-attempt 401 retry, env var substitution in credentials.
400
+
366
401
  ### Environment variables
367
402
 
368
403
  Secrets go in `~/.mcp-bridge/.env` (chmod 600 on init):
@@ -459,6 +494,36 @@ const result = await router.dispatch("todoist", "call", "find-tasks", { query: "
459
494
  └──────────────────────────────────────────────┘
460
495
  ```
461
496
 
497
+ ## Security Limitations
498
+
499
+ The built-in security layer (trust levels, tool filters, result sanitization) provides **baseline protection** for common threats:
500
+
501
+ - Prompt injection patterns (known strings)
502
+ - Oversized responses
503
+ - Unauthorized tool access
504
+
505
+ **What it does NOT cover:**
506
+ - Unicode obfuscation / homoglyph attacks
507
+ - Sophisticated multi-step injection chains
508
+ - Content-level PII detection
509
+
510
+ For production deployments with high security requirements, consider adding an external content filtering layer (e.g., guardrails, PII redaction service) between the bridge and your application.
511
+
512
+ ## Roadmap
513
+
514
+ | Status | Feature | Version |
515
+ |--------|---------|---------|
516
+ | ✅ | Smart Router v2 (intent, cache, batch, resolution) | 1.9.0 |
517
+ | ✅ | HTTP auth (bearer, headers) | 2.0.0 |
518
+ | ✅ | Configurable retries + graceful shutdown | 2.0.0 |
519
+ | ✅ | OAuth2 Client Credentials | 2.1.0 |
520
+ | 🔜 | Hosted bridge (bridge.aiwerk.ch) | planned |
521
+ | 🔜 | Remote catalog integration | planned |
522
+ | 🔜 | OpenTelemetry / Prometheus metrics | planned |
523
+ | 🔜 | PII redaction | planned |
524
+
525
+ See [docs/hosted-bridge-spec.md](docs/hosted-bridge-spec.md) for the hosted bridge architecture.
526
+
462
527
  ## Related
463
528
 
464
529
  - **[@aiwerk/openclaw-mcp-bridge](https://github.com/AIWerk/openclaw-mcp-bridge)** — OpenClaw plugin wrapper (uses this package as core)
File without changes
@@ -17,6 +17,6 @@ export type { Logger, McpServerConfig, McpClientConfig, HttpAuthConfig, RetryCon
17
17
  export { nextRequestId } from "./types.js";
18
18
  export { pickRegisteredToolName } from "./tool-naming.js";
19
19
  export { StandaloneServer } from "./standalone-server.js";
20
- export { checkForUpdate, getUpdateNotice, runUpdate, resetNoticeFlag } from "./update-checker.js";
21
- export type { UpdateInfo } from "./update-checker.js";
20
+ export { checkForUpdate, checkPluginUpdate, getUpdateNotice, runUpdate, resetNoticeFlag } from "./update-checker.js";
21
+ export type { UpdateInfo, PluginUpdateInfo } from "./update-checker.js";
22
22
  export { filterServers, buildFilteredDescription } from "./smart-filter.js";
package/dist/src/index.js CHANGED
@@ -22,6 +22,6 @@ export { pickRegisteredToolName } from "./tool-naming.js";
22
22
  // Standalone server
23
23
  export { StandaloneServer } from "./standalone-server.js";
24
24
  // Update checker
25
- export { checkForUpdate, getUpdateNotice, runUpdate, resetNoticeFlag } from "./update-checker.js";
25
+ export { checkForUpdate, checkPluginUpdate, getUpdateNotice, runUpdate, resetNoticeFlag } from "./update-checker.js";
26
26
  // Smart filter
27
27
  export { filterServers, buildFilteredDescription } from "./smart-filter.js";
@@ -6,11 +6,22 @@ export interface UpdateInfo {
6
6
  updateCommand: string;
7
7
  updateCommandParts: string[];
8
8
  }
9
+ export interface PluginUpdateInfo {
10
+ pluginName: string;
11
+ currentVersion: string;
12
+ latestVersion: string;
13
+ updateAvailable: boolean;
14
+ }
9
15
  /**
10
16
  * Check npm registry for a newer version. Non-blocking, best-effort.
11
17
  * Caches result for the lifetime of the process.
12
18
  */
13
19
  export declare function checkForUpdate(logger: Logger): Promise<UpdateInfo>;
20
+ /**
21
+ * Check if a wrapper plugin (e.g. openclaw-mcp-bridge) has an update.
22
+ * The caller passes the installed plugin version; we check npm for the latest.
23
+ */
24
+ export declare function checkPluginUpdate(pluginName: string, installedVersion: string, logger: Logger): Promise<PluginUpdateInfo>;
14
25
  /**
15
26
  * Build the notice string to inject into the first tool response.
16
27
  * Returns empty string if no update or already delivered.
@@ -1,7 +1,9 @@
1
1
  import { execFileSync, execFile } from "child_process";
2
2
  import { PACKAGE_VERSION } from "./protocol.js";
3
3
  const PACKAGE_NAME = "@aiwerk/mcp-bridge";
4
+ const PLUGIN_PACKAGE_NAME = "@aiwerk/openclaw-mcp-bridge";
4
5
  let cachedUpdateInfo = null;
6
+ let cachedPluginUpdateInfo = null;
5
7
  let noticeDelivered = false;
6
8
  /**
7
9
  * Check npm registry for a newer version. Non-blocking, best-effort.
@@ -42,16 +44,59 @@ export async function checkForUpdate(logger) {
42
44
  }
43
45
  return cachedUpdateInfo;
44
46
  }
47
+ /**
48
+ * Check if a wrapper plugin (e.g. openclaw-mcp-bridge) has an update.
49
+ * The caller passes the installed plugin version; we check npm for the latest.
50
+ */
51
+ export async function checkPluginUpdate(pluginName, installedVersion, logger) {
52
+ if (cachedPluginUpdateInfo)
53
+ return cachedPluginUpdateInfo;
54
+ try {
55
+ const latest = await npmViewVersionOf(pluginName, logger);
56
+ const updateAvailable = latest !== installedVersion && isNewer(latest, installedVersion);
57
+ cachedPluginUpdateInfo = {
58
+ pluginName,
59
+ currentVersion: installedVersion,
60
+ latestVersion: latest,
61
+ updateAvailable,
62
+ };
63
+ if (updateAvailable) {
64
+ logger.info(`[mcp-bridge] Plugin update available: ${pluginName} ${installedVersion} → ${latest}`);
65
+ }
66
+ }
67
+ catch (err) {
68
+ logger.warn(`[mcp-bridge] Plugin version check failed: ${err instanceof Error ? err.message : err}`);
69
+ cachedPluginUpdateInfo = {
70
+ pluginName,
71
+ currentVersion: installedVersion,
72
+ latestVersion: installedVersion,
73
+ updateAvailable: false,
74
+ };
75
+ }
76
+ return cachedPluginUpdateInfo;
77
+ }
45
78
  /**
46
79
  * Build the notice string to inject into the first tool response.
47
80
  * Returns empty string if no update or already delivered.
48
81
  */
49
82
  export function getUpdateNotice() {
50
- if (noticeDelivered || !cachedUpdateInfo?.updateAvailable)
83
+ const coreUpdate = cachedUpdateInfo?.updateAvailable;
84
+ const pluginUpdate = cachedPluginUpdateInfo?.updateAvailable;
85
+ if (noticeDelivered || (!coreUpdate && !pluginUpdate))
51
86
  return "";
52
87
  noticeDelivered = true;
53
- return (`\n\n---\nUpdate available: ${cachedUpdateInfo.currentVersion} → ${cachedUpdateInfo.latestVersion}\n` +
54
- `Run: ${cachedUpdateInfo.updateCommand}`);
88
+ const lines = ["\n\n---"];
89
+ if (coreUpdate) {
90
+ lines.push(`Core update: ${cachedUpdateInfo.currentVersion} → ${cachedUpdateInfo.latestVersion}`);
91
+ }
92
+ if (pluginUpdate) {
93
+ lines.push(`Plugin update: ${cachedPluginUpdateInfo.pluginName} ${cachedPluginUpdateInfo.currentVersion} → ${cachedPluginUpdateInfo.latestVersion}`);
94
+ }
95
+ const installCmd = cachedPluginUpdateInfo?.pluginName
96
+ ? `openclaw plugins install ${cachedPluginUpdateInfo.pluginName}`
97
+ : cachedUpdateInfo.updateCommand;
98
+ lines.push(`Run: ${installCmd}`);
99
+ return lines.join("\n");
55
100
  }
56
101
  /**
57
102
  * Reset the notice flag (for testing).
@@ -89,8 +134,11 @@ export async function runUpdate(logger) {
89
134
  }
90
135
  // --- helpers ---
91
136
  function npmViewVersion(_logger) {
137
+ return npmViewVersionOf(PACKAGE_NAME, _logger);
138
+ }
139
+ function npmViewVersionOf(packageName, _logger) {
92
140
  return new Promise((resolve, reject) => {
93
- execFile("npm", ["view", PACKAGE_NAME, "version"], { encoding: "utf-8", timeout: 10_000 }, (err, stdout) => {
141
+ execFile("npm", ["view", packageName, "version"], { encoding: "utf-8", timeout: 10_000 }, (err, stdout) => {
94
142
  if (err)
95
143
  return reject(err);
96
144
  const ver = (stdout ?? "").trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiwerk/mcp-bridge",
3
- "version": "2.1.0",
3
+ "version": "2.1.2",
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",
@@ -45,13 +45,21 @@
45
45
  "test": "node --import tsx --test tests/*.test.ts",
46
46
  "typecheck": "tsc --noEmit",
47
47
  "prepublishOnly": "npm run build",
48
- "validate-recipe": "npx tsx bin/validate-recipe.ts"
48
+ "validate-recipe": "npx tsx bin/validate-recipe.ts",
49
+ "lint": "eslint src/",
50
+ "format": "prettier --write src/",
51
+ "format:check": "prettier --check src/"
49
52
  },
50
53
  "dependencies": {
51
54
  "@sinclair/typebox": "^0.34.0"
52
55
  },
53
56
  "devDependencies": {
57
+ "@eslint/js": "^10.0.1",
54
58
  "@types/node": "^22.0.0",
59
+ "@typescript-eslint/eslint-plugin": "^8.57.0",
60
+ "@typescript-eslint/parser": "^8.57.0",
61
+ "eslint": "^10.0.3",
62
+ "prettier": "^3.8.1",
55
63
  "tsx": "^4.0.0",
56
64
  "typescript": "^5.7.0"
57
65
  }
@@ -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]"
@@ -160,11 +178,15 @@ if [[ "$REMOVE" == "true" ]]; then
160
178
  import json, sys
161
179
  server_name = sys.argv[1]
162
180
  config_path = sys.argv[2]
181
+ config_mode = sys.argv[3]
163
182
  with open(config_path) as f:
164
183
  cfg = json.load(f)
165
- servers = cfg.get('servers',{})
184
+ if config_mode == 'openclaw':
185
+ servers = cfg.get('plugins',{}).get('entries',{}).get('openclaw-mcp-bridge',{}).get('config',{}).get('servers',{})
186
+ else:
187
+ servers = cfg.get('servers',{})
166
188
  print('yes' if server_name in servers else 'no')
167
- " "$SERVER_NAME" "$MCP_BRIDGE_JSON" 2>/dev/null)
189
+ " "$SERVER_NAME" "$MCP_BRIDGE_JSON" "$CONFIG_MODE" 2>/dev/null)
168
190
 
169
191
  if [[ "$HAS_SERVER" != "yes" ]]; then
170
192
  echo "ℹ️ Server '$SERVER_NAME' not found in config. Nothing to remove."
@@ -181,16 +203,20 @@ print('yes' if server_name in servers else 'no')
181
203
  import json, sys
182
204
  server_name = sys.argv[1]
183
205
  config_path = sys.argv[2]
206
+ config_mode = sys.argv[3]
184
207
  with open(config_path) as f:
185
208
  cfg = json.load(f)
186
- servers = cfg.get('servers', {})
209
+ if config_mode == 'openclaw':
210
+ servers = cfg['plugins']['entries']['openclaw-mcp-bridge']['config']['servers']
211
+ else:
212
+ servers = cfg.get('servers', {})
187
213
  del servers[server_name]
188
214
  with open(config_path, 'w') as f:
189
215
  json.dump(cfg, f, indent=2)
190
216
  f.write('\n')
191
217
  print(f'✅ Removed {server_name} from config')
192
218
  print(f'ℹ️ Server recipe kept in servers/{server_name}/ (reinstall anytime)')
193
- " "$SERVER_NAME" "$MCP_BRIDGE_JSON" 2>/dev/null
219
+ " "$SERVER_NAME" "$MCP_BRIDGE_JSON" "$CONFIG_MODE" 2>/dev/null
194
220
 
195
221
  # Remove env var from .env if exists
196
222
  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",
@@ -0,0 +1,45 @@
1
+ {
2
+ "id": "imap-email",
3
+ "schemaVersion": 2,
4
+ "name": "IMAP Email",
5
+ "description": "Email tools for any IMAP/SMTP provider - list, read, search, send, manage emails",
6
+ "recipeVersion": "1.0.0",
7
+ "metadata": {
8
+ "category": "communication",
9
+ "tags": ["email", "imap", "smtp", "inbox"],
10
+ "country": "global",
11
+ "language": "en",
12
+ "homepage": "https://github.com/AIWerk/mcp-server-imap",
13
+ "license": "MIT",
14
+ "pricing": "free",
15
+ "maturity": "beta"
16
+ },
17
+ "transports": [
18
+ {
19
+ "type": "stdio",
20
+ "command": "npx",
21
+ "args": ["-y", "@aiwerk/mcp-server-imap"],
22
+ "env": {
23
+ "IMAP_HOST": "${IMAP_HOST}",
24
+ "IMAP_PORT": "${IMAP_PORT}",
25
+ "IMAP_USER": "${IMAP_USER}",
26
+ "IMAP_PASS": "${IMAP_PASS}",
27
+ "SMTP_HOST": "${SMTP_HOST}",
28
+ "SMTP_PORT": "${SMTP_PORT}",
29
+ "SMTP_SEND_ENABLED": "${SMTP_SEND_ENABLED}"
30
+ }
31
+ }
32
+ ],
33
+ "auth": {
34
+ "required": true,
35
+ "type": "api-key",
36
+ "envVars": ["IMAP_HOST", "IMAP_USER", "IMAP_PASS"],
37
+ "instructions": "Set your IMAP server hostname, username (email), and password. For Gmail use an App Password. Set SMTP_SEND_ENABLED=true to enable email sending.",
38
+ "bootstrap": "env-only"
39
+ },
40
+ "capabilities": {
41
+ "toolCount": 10,
42
+ "toolNames": ["email_list", "email_read", "email_search", "email_folders", "email_move", "email_flag", "email_delete", "email_send", "email_reply", "email_attachment"],
43
+ "sideEffects": "external-write"
44
+ }
45
+ }
@@ -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",