mcpbox 0.2.2 → 0.3.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 +9 -112
- package/dist/config/loader.js +9 -1
- package/dist/config/schema.d.ts +8 -0
- package/dist/config/schema.js +4 -0
- package/dist/mcp/manager.js +34 -24
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
- Supports Tools, Resources & Prompts
|
|
12
12
|
- Namespaces with `servername__` prefix to avoid collisions
|
|
13
13
|
- Per-server tool filtering to limit AI access and reduce context usage
|
|
14
|
-
- OAuth
|
|
14
|
+
- OAuth 2.1, API key, or no auth
|
|
15
15
|
|
|
16
16
|
<picture>
|
|
17
17
|
<source media="(prefers-color-scheme: dark)" srcset="assets/diagram-dark.excalidraw.png">
|
|
@@ -28,28 +28,22 @@ Create `mcpbox.json`:
|
|
|
28
28
|
"memory": {
|
|
29
29
|
"command": "npx",
|
|
30
30
|
"args": ["-y", "@modelcontextprotocol/server-memory"]
|
|
31
|
+
},
|
|
32
|
+
"sequential-thinking": {
|
|
33
|
+
"command": "npx",
|
|
34
|
+
"args": ["-y", "@modelcontextprotocol/server-sequential-thinking"]
|
|
31
35
|
}
|
|
32
36
|
}
|
|
33
37
|
}
|
|
34
38
|
```
|
|
35
39
|
|
|
36
|
-
Run
|
|
37
|
-
|
|
38
|
-
**npx**
|
|
40
|
+
Run:
|
|
39
41
|
|
|
40
42
|
```bash
|
|
41
43
|
npx mcpbox
|
|
42
44
|
```
|
|
43
|
-
*MCP server commands (e.g., `uvx`, `docker`) must be available where the box runs.*
|
|
44
|
-
|
|
45
|
-
**Docker**
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
docker run -v ./mcpbox.json:/config/config.json -p 8080:8080 ghcr.io/kandobyte/mcpbox
|
|
49
|
-
```
|
|
50
|
-
*The Docker image includes Node.js and Python, supporting MCP servers launched via `npx` and `uvx`.*
|
|
51
45
|
|
|
52
|
-
|
|
46
|
+
Add to your MCP client config:
|
|
53
47
|
|
|
54
48
|
```json
|
|
55
49
|
{
|
|
@@ -62,103 +56,6 @@ The box starts on http://localhost:8080. Connect an agent by adding this to your
|
|
|
62
56
|
}
|
|
63
57
|
```
|
|
64
58
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
## Configuration
|
|
68
|
-
|
|
69
|
-
See [`mcpbox.example.jsonc`](mcpbox.example.jsonc) for all options. All string values support `${VAR_NAME}` environment variable substitution.
|
|
70
|
-
|
|
71
|
-
**[Authentication](docs/authentication.md)** — none (default), API key, or OAuth.
|
|
72
|
-
|
|
73
|
-
## Deployment
|
|
74
|
-
|
|
75
|
-
To expose MCPBox remotely, put it behind a TLS-terminating reverse proxy.
|
|
76
|
-
|
|
77
|
-
Before deploying with OAuth:
|
|
78
|
-
- [ ] Use sqlite storage for persistence across restarts
|
|
79
|
-
- [ ] Set issuer to your public URL
|
|
80
|
-
- [ ] Use bcrypt hashes for local passwords
|
|
81
|
-
|
|
82
|
-
> [!NOTE]
|
|
83
|
-
> MCPBox is single-instance only — don't run multiple instances behind a load balancer.
|
|
84
|
-
|
|
85
|
-
### Quick remote access
|
|
86
|
-
|
|
87
|
-
Use [cloudflared](https://github.com/cloudflare/cloudflared) to expose a local instance (no account required):
|
|
88
|
-
|
|
89
|
-
```bash
|
|
90
|
-
cloudflared tunnel --url http://localhost:8080
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
Then update your config with the generated public URL:
|
|
94
|
-
|
|
95
|
-
```json
|
|
96
|
-
{
|
|
97
|
-
"auth": {
|
|
98
|
-
"type": "oauth",
|
|
99
|
-
"issuer": "https://<tunnel-id>.trycloudflare.com",
|
|
100
|
-
"identityProviders": [
|
|
101
|
-
{ "type": "local", "users": [{ "username": "admin", "password": "${MCPBOX_PASSWORD}" }] }
|
|
102
|
-
],
|
|
103
|
-
"dynamicRegistration": true
|
|
104
|
-
},
|
|
105
|
-
"storage": {
|
|
106
|
-
"type": "sqlite",
|
|
107
|
-
"path": "/data/mcpbox.db"
|
|
108
|
-
},
|
|
109
|
-
"mcpServers": { ... }
|
|
110
|
-
}
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
Run with a persistent data volume:
|
|
114
|
-
|
|
115
|
-
```bash
|
|
116
|
-
docker run -v ./mcpbox.json:/config/config.json -v ./data:/data -p 8080:8080 ghcr.io/kandobyte/mcpbox
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
## Connect Your AI
|
|
120
|
-
|
|
121
|
-
### Claude Web & Mobile
|
|
59
|
+
## Documentation
|
|
122
60
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
Requires `dynamicRegistration: true` in your config.
|
|
126
|
-
|
|
127
|
-
### Claude Code
|
|
128
|
-
|
|
129
|
-
```bash
|
|
130
|
-
claude mcp add --transport http mcpbox https://your-mcpbox-url.com
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
Requires `dynamicRegistration: true` in your config.
|
|
134
|
-
|
|
135
|
-
### Other MCP clients
|
|
136
|
-
|
|
137
|
-
**With dynamic registration (OAuth)** — just provide the URL:
|
|
138
|
-
|
|
139
|
-
```json
|
|
140
|
-
{
|
|
141
|
-
"mcpServers": {
|
|
142
|
-
"mcpbox": {
|
|
143
|
-
"type": "http",
|
|
144
|
-
"url": "https://your-mcpbox-url.com"
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
**With API key:**
|
|
151
|
-
|
|
152
|
-
```json
|
|
153
|
-
{
|
|
154
|
-
"mcpServers": {
|
|
155
|
-
"mcpbox": {
|
|
156
|
-
"type": "http",
|
|
157
|
-
"url": "https://your-mcpbox-url.com",
|
|
158
|
-
"headers": {
|
|
159
|
-
"Authorization": "Bearer YOUR_API_KEY"
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
```
|
|
61
|
+
See the [documentation](https://kandobyte.github.io/mcpbox/quick-start) for configuration, authentication, deployment, and connecting clients.
|
package/dist/config/loader.js
CHANGED
|
@@ -2,7 +2,13 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import { RawConfigSchema, } from "./schema.js";
|
|
3
3
|
function substituteEnvVars(obj) {
|
|
4
4
|
if (typeof obj === "string") {
|
|
5
|
-
return obj.replace(/\$\{(\w+)\}/g, (
|
|
5
|
+
return obj.replace(/\$\{(\w+)\}/g, (match, name) => {
|
|
6
|
+
const value = process.env[name];
|
|
7
|
+
if (value === undefined) {
|
|
8
|
+
throw new Error(`Environment variable ${name} is not set (referenced as ${match})`);
|
|
9
|
+
}
|
|
10
|
+
return value;
|
|
11
|
+
});
|
|
6
12
|
}
|
|
7
13
|
if (Array.isArray(obj)) {
|
|
8
14
|
return obj.map(substituteEnvVars);
|
|
@@ -25,6 +31,8 @@ function parseMcpServers(mcpServers) {
|
|
|
25
31
|
args: entry.args,
|
|
26
32
|
env: entry.env,
|
|
27
33
|
tools: entry.tools,
|
|
34
|
+
resources: entry.resources,
|
|
35
|
+
prompts: entry.prompts,
|
|
28
36
|
});
|
|
29
37
|
}
|
|
30
38
|
return mcps;
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export declare const McpServerEntrySchema: z.ZodObject<{
|
|
|
8
8
|
args: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
9
9
|
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
10
10
|
tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
11
|
+
resources: z.ZodOptional<z.ZodBoolean>;
|
|
12
|
+
prompts: z.ZodOptional<z.ZodBoolean>;
|
|
11
13
|
}, z.core.$strict>;
|
|
12
14
|
/**
|
|
13
15
|
* OAuth user credentials
|
|
@@ -204,6 +206,8 @@ export declare const RawConfigSchema: z.ZodObject<{
|
|
|
204
206
|
args: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
205
207
|
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
206
208
|
tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
209
|
+
resources: z.ZodOptional<z.ZodBoolean>;
|
|
210
|
+
prompts: z.ZodOptional<z.ZodBoolean>;
|
|
207
211
|
}, z.core.$strict>>>;
|
|
208
212
|
}, z.core.$strict>;
|
|
209
213
|
/**
|
|
@@ -216,6 +220,8 @@ export declare const McpConfigSchema: z.ZodObject<{
|
|
|
216
220
|
args: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
217
221
|
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
218
222
|
tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
223
|
+
resources: z.ZodOptional<z.ZodBoolean>;
|
|
224
|
+
prompts: z.ZodOptional<z.ZodBoolean>;
|
|
219
225
|
}, z.core.$strip>;
|
|
220
226
|
/**
|
|
221
227
|
* Processed config (after loader adds defaults and resolves mcpServers)
|
|
@@ -282,6 +288,8 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
282
288
|
args: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
283
289
|
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
284
290
|
tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
291
|
+
resources: z.ZodOptional<z.ZodBoolean>;
|
|
292
|
+
prompts: z.ZodOptional<z.ZodBoolean>;
|
|
285
293
|
}, z.core.$strip>>;
|
|
286
294
|
}, z.core.$strip>;
|
|
287
295
|
export type McpServerEntry = z.infer<typeof McpServerEntrySchema>;
|
package/dist/config/schema.js
CHANGED
|
@@ -17,6 +17,8 @@ export const McpServerEntrySchema = z
|
|
|
17
17
|
args: z.array(z.string()).optional(),
|
|
18
18
|
env: z.record(z.string(), z.string()).optional(),
|
|
19
19
|
tools: z.array(z.string()).optional(),
|
|
20
|
+
resources: z.boolean().optional(),
|
|
21
|
+
prompts: z.boolean().optional(),
|
|
20
22
|
})
|
|
21
23
|
.strict();
|
|
22
24
|
/**
|
|
@@ -201,6 +203,8 @@ export const McpConfigSchema = z.object({
|
|
|
201
203
|
args: z.array(z.string()).optional(),
|
|
202
204
|
env: z.record(z.string(), z.string()).optional(),
|
|
203
205
|
tools: z.array(z.string()).optional(),
|
|
206
|
+
resources: z.boolean().optional(),
|
|
207
|
+
prompts: z.boolean().optional(),
|
|
204
208
|
});
|
|
205
209
|
/**
|
|
206
210
|
* Processed config (after loader adds defaults and resolves mcpServers)
|
package/dist/mcp/manager.js
CHANGED
|
@@ -106,37 +106,47 @@ export class McpManager {
|
|
|
106
106
|
}
|
|
107
107
|
// Get resources from this MCP (namespace them if multiple servers)
|
|
108
108
|
let resources = [];
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
109
|
+
if (config.resources !== false) {
|
|
110
|
+
try {
|
|
111
|
+
const { resources: rawResources } = await client.listResources();
|
|
112
|
+
resources = this.useNamespacing
|
|
113
|
+
? rawResources.map((resource) => ({
|
|
114
|
+
...resource,
|
|
115
|
+
uri: namespaceName(config.name, resource.uri),
|
|
116
|
+
}))
|
|
117
|
+
: rawResources;
|
|
118
|
+
for (const resource of resources) {
|
|
119
|
+
this.resourceToMcp.set(resource.uri, config.name);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
logger.debug({ mcp: config.name }, "Server doesn't support resources");
|
|
119
124
|
}
|
|
120
125
|
}
|
|
121
|
-
|
|
122
|
-
logger.debug({ mcp: config.name }, "
|
|
126
|
+
else {
|
|
127
|
+
logger.debug({ mcp: config.name }, "Resources disabled by config");
|
|
123
128
|
}
|
|
124
129
|
// Get prompts from this MCP (namespace them if multiple servers)
|
|
125
130
|
let prompts = [];
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
131
|
+
if (config.prompts !== false) {
|
|
132
|
+
try {
|
|
133
|
+
const { prompts: rawPrompts } = await client.listPrompts();
|
|
134
|
+
prompts = this.useNamespacing
|
|
135
|
+
? rawPrompts.map((prompt) => ({
|
|
136
|
+
...prompt,
|
|
137
|
+
name: namespaceName(config.name, prompt.name),
|
|
138
|
+
}))
|
|
139
|
+
: rawPrompts;
|
|
140
|
+
for (const prompt of prompts) {
|
|
141
|
+
this.promptToMcp.set(prompt.name, config.name);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
logger.debug({ mcp: config.name }, "Server doesn't support prompts");
|
|
136
146
|
}
|
|
137
147
|
}
|
|
138
|
-
|
|
139
|
-
logger.debug({ mcp: config.name }, "
|
|
148
|
+
else {
|
|
149
|
+
logger.debug({ mcp: config.name }, "Prompts disabled by config");
|
|
140
150
|
}
|
|
141
151
|
this.mcps.set(config.name, {
|
|
142
152
|
name: config.name,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcpbox",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "A lightweight gateway that exposes local stdio-based MCP servers via Streamable HTTP",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -18,7 +18,10 @@
|
|
|
18
18
|
"test:conformance:oauth": "node --disable-warning=ExperimentalWarning --import tsx --test --test-concurrency=1 'test/conformance-oauth/**/*.test.ts'",
|
|
19
19
|
"test:coverage": "node --experimental-test-coverage --disable-warning=ExperimentalWarning --import tsx --test --test-concurrency=1 'test/unit/**/*.test.ts' 'test/integration/**/*.test.ts' 'test/conformance-oauth/**/*.test.ts'",
|
|
20
20
|
"format": "biome check --write .",
|
|
21
|
-
"check": "biome check ."
|
|
21
|
+
"check": "biome check .",
|
|
22
|
+
"docs:dev": "vitepress dev docs",
|
|
23
|
+
"docs:build": "vitepress build docs",
|
|
24
|
+
"docs:preview": "vitepress preview docs"
|
|
22
25
|
},
|
|
23
26
|
"keywords": [
|
|
24
27
|
"mcp",
|
|
@@ -61,6 +64,7 @@
|
|
|
61
64
|
"@biomejs/biome": "^2.3.14",
|
|
62
65
|
"@types/node": "^25.1.0",
|
|
63
66
|
"tsx": "^4.21.0",
|
|
64
|
-
"typescript": "^5.9.3"
|
|
67
|
+
"typescript": "^5.9.3",
|
|
68
|
+
"vitepress": "^1.6.4"
|
|
65
69
|
}
|
|
66
70
|
}
|