mcp-controller 0.4.0 → 0.4.1

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
@@ -5,7 +5,7 @@ A Model Context Protocol (MCP) server that acts as a proxy between MCP clients a
5
5
  ## Key Features
6
6
 
7
7
  - **🔧 Tool Filtering**: Selectively enable or disable specific tools from target MCP servers
8
- - **🔍 Transparent Proxying**: Forwards all other MCP protocol messages without modification
8
+ - **🔍 Transparent Proxying**: Forwards all other MCP protocol messages without modification
9
9
  - **⚡ Zero Configuration**: Works with any existing MCP server without changes
10
10
  - **🛡️ Access Control**: Control which tools clients can access for security and usability
11
11
  - **📦 Command Line Interface**: Start any MCP server through command arguments
@@ -15,8 +15,13 @@ A Model Context Protocol (MCP) server that acts as a proxy between MCP clients a
15
15
  ## Installation
16
16
 
17
17
  ```bash
18
- bun install
19
- bun run build
18
+ curl -fsSL https://raw.githubusercontent.com/eli0shin/mcp-controller/main/install.sh | bash
19
+ ```
20
+
21
+ Or install via npm:
22
+
23
+ ```bash
24
+ npm install -g mcp-controller
20
25
  ```
21
26
 
22
27
  ## Usage
@@ -25,16 +30,16 @@ bun run build
25
30
 
26
31
  ```bash
27
32
  # Proxy to a local MCP server
28
- ./mcp-controller bun run my-server.ts
33
+ mcp-controller bun run my-server.ts
29
34
 
30
35
  # Proxy to an npm-distributed MCP server
31
- ./mcp-controller @modelcontextprotocol/server-sequential-thinking
36
+ mcp-controller @modelcontextprotocol/server-sequential-thinking
32
37
 
33
38
  # Proxy to a Python MCP server
34
- ./mcp-controller python -m my_mcp_server
39
+ mcp-controller python -m my_mcp_server
35
40
 
36
41
  # Proxy to any executable MCP server
37
- ./mcp-controller node server.js --port 3000
42
+ mcp-controller node server.js --port 3000
38
43
  ```
39
44
 
40
45
  ### Tool Filtering
@@ -43,13 +48,13 @@ Control which tools from the target server are exposed to clients:
43
48
 
44
49
  ```bash
45
50
  # Only allow specific tools (whitelist mode)
46
- ./mcp-controller --enabled-tools file-read,file-write,search bun run my-server.ts
51
+ mcp-controller --enabled-tools file-read,file-write,search bun run my-server.ts
47
52
 
48
- # Block specific tools (blacklist mode)
49
- ./mcp-controller --disabled-tools dangerous-tool,admin-commands python -m my_server
53
+ # Block specific tools (blacklist mode)
54
+ mcp-controller --disabled-tools dangerous-tool,admin-commands python -m my_server
50
55
 
51
56
  # Multiple tools (comma-separated, no spaces around commas)
52
- ./mcp-controller --enabled-tools tool1,tool2,tool3 node server.js
57
+ mcp-controller --enabled-tools tool1,tool2,tool3 node server.js
53
58
  ```
54
59
 
55
60
  ### Filtering Rules
@@ -63,30 +68,33 @@ Control which tools from the target server are exposed to clients:
63
68
  ## Use Cases
64
69
 
65
70
  ### Security & Access Control
71
+
66
72
  ```bash
67
73
  # Production environment - only allow safe read-only tools
68
- ./mcp-controller --enabled-tools read-file,search,list-files my-server
74
+ mcp-controller --enabled-tools read-file,search,list-files my-server
69
75
 
70
76
  # Development environment - block dangerous operations
71
- ./mcp-controller --disabled-tools delete-file,format-disk,restart-system my-server
77
+ mcp-controller --disabled-tools delete-file,format-disk,restart-system my-server
72
78
  ```
73
79
 
74
80
  ### Client-Specific Customization
81
+
75
82
  ```bash
76
83
  # For a documentation client - only text processing tools
77
- ./mcp-controller --enabled-tools text-search,summarize,translate content-server
84
+ mcp-controller --enabled-tools text-search,summarize,translate content-server
78
85
 
79
- # For an admin interface - block user-facing tools
80
- ./mcp-controller --disabled-tools user-chat,send-email,post-social admin-server
86
+ # For an admin interface - block user-facing tools
87
+ mcp-controller --disabled-tools user-chat,send-email,post-social admin-server
81
88
  ```
82
89
 
83
90
  ### Testing & Development
91
+
84
92
  ```bash
85
93
  # Test specific functionality by isolating tools
86
- ./mcp-controller --enabled-tools database-query,cache-get test-server
94
+ mcp-controller --enabled-tools database-query,cache-get test-server
87
95
 
88
96
  # Debug by excluding problematic tools
89
- ./mcp-controller --disabled-tools flaky-api,slow-process debug-server
97
+ mcp-controller --disabled-tools flaky-api,slow-process debug-server
90
98
  ```
91
99
 
92
100
  ## How it Works
@@ -107,7 +115,7 @@ MCP Client ↔ MCP Controller ↔ Target MCP Server
107
115
  ### Message Flow
108
116
 
109
117
  1. **Client → Controller → Target**: All requests forwarded transparently
110
- 2. **Target → Controller → Client**:
118
+ 2. **Target → Controller → Client**:
111
119
  - `tools/list` responses are filtered based on configuration
112
120
  - All other responses pass through unchanged
113
121
 
@@ -115,7 +123,7 @@ MCP Client ↔ MCP Controller ↔ Target MCP Server
115
123
 
116
124
  - ✅ **`tools/list` responses** - Tool arrays are filtered according to your settings
117
125
  - ❌ **Tool calls** - Individual tool invocations pass through (filtered tools simply won't be available)
118
- - ❌ **Resources** - Resource lists and access remain unchanged
126
+ - ❌ **Resources** - Resource lists and access remain unchanged
119
127
  - ❌ **Prompts** - Prompt functionality unaffected
120
128
  - ❌ **Other messages** - Initialization, capabilities, etc. pass through
121
129
 
@@ -124,6 +132,8 @@ MCP Client ↔ MCP Controller ↔ Target MCP Server
124
132
  ```bash
125
133
  Usage: mcp-controller [--enabled-tools <tool1,tool2,...>] [--disabled-tools <tool1,tool2,...>] <command> [args...]
126
134
 
135
+ mcp-controller update
136
+
127
137
  Options:
128
138
  --enabled-tools <tools> Comma-separated list of tools to allow (whitelist mode)
129
139
  --disabled-tools <tools> Comma-separated list of tools to block (blacklist mode)
@@ -140,15 +150,15 @@ The controller validates arguments at startup and will exit with helpful error m
140
150
 
141
151
  ```bash
142
152
  # Missing command
143
- $ ./mcp-controller --enabled-tools read
153
+ $ mcp-controller --enabled-tools read
144
154
  Error: No target command specified
145
155
 
146
156
  # Both filtering modes
147
- $ ./mcp-controller --enabled-tools read --disabled-tools write bun server.ts
157
+ $ mcp-controller --enabled-tools read --disabled-tools write bun server.ts
148
158
  Error: --enabled-tools and --disabled-tools are mutually exclusive
149
159
 
150
160
  # Missing tool list
151
- $ ./mcp-controller --enabled-tools bun server.ts
161
+ $ mcp-controller --enabled-tools bun server.ts
152
162
  Error: --enabled-tools requires a value
153
163
  ```
154
164
 
@@ -158,15 +168,21 @@ Error: --enabled-tools requires a value
158
168
  # Install dependencies
159
169
  bun install
160
170
 
161
- # Build the executable
171
+ # Build release binaries
162
172
  bun run build
163
173
 
164
- # Run in development mode
174
+ # Run via wrapper against the local release build
175
+ ./bin/mcp-controller bun run tests/fixtures/mcp-server.ts
176
+
177
+ # Run in development mode
165
178
  bun run dev <target-command>
166
179
 
167
180
  # Run tests (includes tool filtering tests)
168
181
  bun test
169
182
 
183
+ # Update installed binary from GitHub Releases
184
+ mcp-controller update
185
+
170
186
  # Lint and format
171
187
  bun run lint
172
188
  bun run format
@@ -177,4 +193,4 @@ bun run typecheck
177
193
 
178
194
  ## License
179
195
 
180
- MIT
196
+ MIT
@@ -0,0 +1,56 @@
1
+ #!/bin/sh
2
+ set -e
3
+
4
+ if [ -n "$MCP_CONTROLLER_BIN_PATH" ]; then
5
+ resolved="$MCP_CONTROLLER_BIN_PATH"
6
+ else
7
+ script_path="$0"
8
+ while [ -L "$script_path" ]; do
9
+ link_target="$(readlink "$script_path")"
10
+ case "$link_target" in
11
+ /*) script_path="$link_target" ;;
12
+ *) script_path="$(dirname "$script_path")/$link_target" ;;
13
+ esac
14
+ done
15
+ script_dir="$(dirname "$script_path")"
16
+ script_dir="$(cd "$script_dir" && pwd)"
17
+
18
+ case "$(uname -s)" in
19
+ Darwin) platform="darwin" ;;
20
+ Linux) platform="linux" ;;
21
+ *) platform="$(uname -s | tr '[:upper:]' '[:lower:]')" ;;
22
+ esac
23
+
24
+ case "$(uname -m)" in
25
+ x86_64|amd64) arch="x64" ;;
26
+ aarch64|arm64) arch="arm64" ;;
27
+ *) arch="$(uname -m)" ;;
28
+ esac
29
+
30
+ resolved=""
31
+
32
+ candidate="$script_dir/mcp-controller-downloaded"
33
+ if [ -f "$candidate" ]; then
34
+ resolved="$candidate"
35
+ fi
36
+
37
+ if [ -z "$resolved" ]; then
38
+ name="mcp-controller-${platform}-${arch}"
39
+ current_dir="$script_dir"
40
+ while [ "$current_dir" != "/" ]; do
41
+ candidate="$current_dir/dist/$name/bin/mcp-controller"
42
+ if [ -f "$candidate" ]; then
43
+ resolved="$candidate"
44
+ break
45
+ fi
46
+ current_dir="$(dirname "$current_dir")"
47
+ done
48
+ fi
49
+
50
+ if [ -z "$resolved" ]; then
51
+ printf "Failed to find mcp-controller binary for your platform\n" >&2
52
+ exit 1
53
+ fi
54
+ fi
55
+
56
+ exec "$resolved" "$@"
@@ -0,0 +1,50 @@
1
+ @echo off
2
+ setlocal enabledelayedexpansion
3
+
4
+ if defined MCP_CONTROLLER_BIN_PATH (
5
+ set "resolved=%MCP_CONTROLLER_BIN_PATH%"
6
+ goto :execute
7
+ )
8
+
9
+ set "script_dir=%~dp0"
10
+ set "script_dir=%script_dir:~0,-1%"
11
+
12
+ set "resolved="
13
+ set "candidate=%script_dir%\mcp-controller-downloaded.exe"
14
+ if exist "%candidate%" (
15
+ set "resolved=%candidate%"
16
+ goto :execute
17
+ )
18
+
19
+ if "%PROCESSOR_ARCHITECTURE%"=="AMD64" (
20
+ set "arch=x64"
21
+ ) else if "%PROCESSOR_ARCHITECTURE%"=="ARM64" (
22
+ set "arch=arm64"
23
+ ) else (
24
+ set "arch=x64"
25
+ )
26
+
27
+ set "name=mcp-controller-windows-!arch!"
28
+ set "current_dir=%script_dir%"
29
+
30
+ :search_loop
31
+ set "candidate=%current_dir%\dist\%name%\bin\mcp-controller.exe"
32
+ if exist "%candidate%" (
33
+ set "resolved=%candidate%"
34
+ goto :execute
35
+ )
36
+
37
+ for %%i in ("%current_dir%") do set "parent_dir=%%~dpi"
38
+ set "parent_dir=%parent_dir:~0,-1%"
39
+
40
+ if "%current_dir%"=="%parent_dir%" goto :not_found
41
+ set "current_dir=%parent_dir%"
42
+ goto :search_loop
43
+
44
+ :not_found
45
+ echo Failed to find mcp-controller binary for your platform >&2
46
+ exit /b 1
47
+
48
+ :execute
49
+ start /b /wait "" "%resolved%" %*
50
+ exit /b %ERRORLEVEL%
package/package.json CHANGED
@@ -1,53 +1,21 @@
1
1
  {
2
2
  "name": "mcp-controller",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "MCP server proxy that enables controlling availability of tools.",
5
- "type": "module",
6
- "bin": {
7
- "mcp-controller": "mcp-controller"
8
- },
9
- "files": [
10
- "README.md",
11
- "package.json",
12
- "bun.lock",
13
- "mcp-controller"
14
- ],
15
- "scripts": {
16
- "build": "bun build src/cli.ts --compile --outfile mcp-controller",
17
- "dev": "bun run src/cli.ts",
18
- "typecheck": "bun tsc --noEmit",
19
- "lint": "eslint .",
20
- "lint:fix": "eslint . --fix",
21
- "format": "prettier --check .",
22
- "format:fix": "prettier --write .",
23
- "test": "bun test",
24
- "test:watch": "bun test tests/ --watch",
25
- "prepublish": "bun test && bun run build"
26
- },
27
- "devDependencies": {
28
- "@changesets/changelog-github": "^0.5.2",
29
- "@changesets/cli": "^2.29.8",
30
- "@modelcontextprotocol/sdk": "^1.0.0",
31
- "@total-typescript/ts-reset": "^0.6.1",
32
- "@types/node": "^22.9.0",
33
- "bun-types": "^1.1.34",
34
- "eslint": "^9.15.0",
35
- "eslint-for-ai": "^1.0.8",
36
- "prettier": "^3.3.3",
37
- "typescript": "^5.6.3",
38
- "zod": "^3.23.8"
39
- },
40
- "keywords": [
41
- "mcp",
42
- "model-context-protocol",
43
- "proxy",
44
- "json-rpc",
45
- "cli"
46
- ],
47
- "author": "@eli0shin",
48
- "license": "MIT",
49
5
  "repository": {
50
6
  "type": "git",
51
7
  "url": "https://github.com/eli0shin/mcp-controller"
8
+ },
9
+ "bugs": {
10
+ "url": "https://github.com/eli0shin/mcp-controller/issues"
11
+ },
12
+ "homepage": "https://github.com/eli0shin/mcp-controller#readme",
13
+ "license": "MIT",
14
+ "bin": {
15
+ "mcp-controller": "./bin/mcp-controller"
16
+ },
17
+ "scripts": {
18
+ "preinstall": "node ./preinstall.mjs",
19
+ "postinstall": "node ./postinstall.mjs"
52
20
  }
53
21
  }
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import os from 'os';
6
+ import { fileURLToPath } from 'url';
7
+ import { createRequire } from 'module';
8
+
9
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
+ const require = createRequire(import.meta.url);
11
+ const REPO = 'eli0shin/mcp-controller';
12
+
13
+ function shouldSkipPostinstall() {
14
+ const packageRoot = path.join(__dirname, '..');
15
+ const srcDir = path.join(packageRoot, 'src');
16
+ if (fs.existsSync(srcDir)) {
17
+ console.log('Skipping postinstall (running from source)');
18
+ return true;
19
+ }
20
+ return false;
21
+ }
22
+
23
+ function detectPlatformAndArch() {
24
+ let platform;
25
+ switch (os.platform()) {
26
+ case 'darwin':
27
+ platform = 'darwin';
28
+ break;
29
+ case 'linux':
30
+ platform = 'linux';
31
+ break;
32
+ case 'win32':
33
+ platform = 'windows';
34
+ break;
35
+ default:
36
+ platform = os.platform();
37
+ }
38
+
39
+ let arch;
40
+ switch (os.arch()) {
41
+ case 'x64':
42
+ arch = 'x64';
43
+ break;
44
+ case 'arm64':
45
+ arch = 'arm64';
46
+ break;
47
+ default:
48
+ arch = os.arch();
49
+ }
50
+
51
+ return { platform, arch };
52
+ }
53
+
54
+ function getDownloadedBinaryPath() {
55
+ const { platform } = detectPlatformAndArch();
56
+ const binary =
57
+ platform === 'windows'
58
+ ? 'mcp-controller-downloaded.exe'
59
+ : 'mcp-controller-downloaded';
60
+ return path.join(__dirname, '..', 'bin', binary);
61
+ }
62
+
63
+ async function downloadLatestBinary() {
64
+ const { platform, arch } = detectPlatformAndArch();
65
+ const asset =
66
+ platform === 'windows'
67
+ ? `mcp-controller-${platform}-${arch}.exe`
68
+ : `mcp-controller-${platform}-${arch}`;
69
+ const url = `https://github.com/${REPO}/releases/latest/download/${asset}`;
70
+ const response = await fetch(url, {
71
+ headers: {
72
+ 'User-Agent': 'mcp-controller',
73
+ },
74
+ });
75
+
76
+ if (!response.ok) {
77
+ throw new Error(`Failed to download ${url}: ${response.status}`);
78
+ }
79
+
80
+ const targetPath = getDownloadedBinaryPath();
81
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
82
+ const arrayBuffer = await response.arrayBuffer();
83
+ fs.writeFileSync(targetPath, Buffer.from(arrayBuffer));
84
+
85
+ if (platform !== 'windows') {
86
+ fs.chmodSync(targetPath, 0o755);
87
+ }
88
+
89
+ console.log(`Downloaded binary: ${targetPath}`);
90
+ }
91
+
92
+ async function regenerateWindowsCmdWrappers() {
93
+ console.log('Windows + npm: Rebuilding bin links');
94
+
95
+ try {
96
+ const { execSync } = require('child_process');
97
+ const pkgPath = path.join(__dirname, '..');
98
+
99
+ const isGlobal =
100
+ process.env.npm_config_global === 'true' ||
101
+ pkgPath.includes(path.join('npm', 'node_modules'));
102
+
103
+ const cmd = `npm rebuild mcp-controller --ignore-scripts${isGlobal ? ' -g' : ''}`;
104
+ const opts = {
105
+ stdio: 'inherit',
106
+ shell: true,
107
+ ...(isGlobal ? {} : { cwd: path.join(pkgPath, '..', '..') }),
108
+ };
109
+
110
+ execSync(cmd, opts);
111
+ console.log('Successfully rebuilt npm bin links');
112
+ } catch (error) {
113
+ console.error('Error rebuilding npm links:', error.message);
114
+ }
115
+ }
116
+
117
+ async function main() {
118
+ if (shouldSkipPostinstall()) {
119
+ return;
120
+ }
121
+
122
+ try {
123
+ await downloadLatestBinary();
124
+
125
+ if (
126
+ os.platform() === 'win32' &&
127
+ process.env.npm_config_user_agent?.startsWith('npm')
128
+ ) {
129
+ await regenerateWindowsCmdWrappers();
130
+ }
131
+ } catch (error) {
132
+ console.error('Failed to install release binary:', error.message);
133
+ process.exit(1);
134
+ }
135
+ }
136
+
137
+ try {
138
+ await main();
139
+ } catch (error) {
140
+ console.error('Postinstall error:', error.message);
141
+ process.exit(0);
142
+ }
package/preinstall.mjs ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import os from 'os';
6
+ import { fileURLToPath } from 'url';
7
+
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+
10
+ function main() {
11
+ if (os.platform() !== 'win32') {
12
+ console.log('Non-Windows platform, skipping preinstall');
13
+ return;
14
+ }
15
+
16
+ console.log('Windows: Modifying package.json bin entry');
17
+
18
+ const packageJsonPath = path.join(__dirname, '..', 'package.json');
19
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
20
+
21
+ packageJson.bin = {
22
+ 'mcp-controller': './bin/mcp-controller.cmd',
23
+ };
24
+
25
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
26
+ console.log('Updated package.json bin to use mcp-controller.cmd');
27
+
28
+ const unixScript = path.join(__dirname, '..', 'bin', 'mcp-controller');
29
+ if (fs.existsSync(unixScript)) {
30
+ fs.unlinkSync(unixScript);
31
+ }
32
+ }
33
+
34
+ try {
35
+ main();
36
+ } catch (error) {
37
+ console.error('Preinstall error:', error.message);
38
+ process.exit(0);
39
+ }