mcpbox 0.2.0 → 0.2.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 +30 -9
- package/dist/auth/oauth.js +4 -4
- package/dist/index.js +5 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,12 +7,15 @@
|
|
|
7
7
|
|
|
8
8
|
**MCPBox** is a lightweight gateway that exposes local stdio-based MCP (Model Context Protocol) servers via Streamable HTTP, enabling Claude and other AI agents to connect from anywhere.
|
|
9
9
|
|
|
10
|
-
-
|
|
10
|
+
- Runs multiple MCP stdio servers behind a single HTTP endpoint
|
|
11
11
|
- Exposes Tools, Resources & Prompts
|
|
12
|
-
- Namespaces with `servername__` prefix to avoid collisions
|
|
12
|
+
- Namespaces with `servername__` prefix to avoid collisions
|
|
13
13
|
- OAuth or API key authentication
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
<picture>
|
|
16
|
+
<source media="(prefers-color-scheme: dark)" srcset="assets/diagram-dark.excalidraw.png">
|
|
17
|
+
<img src="assets/diagram.excalidraw.png" alt="mcpbox diagram">
|
|
18
|
+
</picture>
|
|
16
19
|
|
|
17
20
|
## Quick Start
|
|
18
21
|
|
|
@@ -70,10 +73,10 @@ See [`mcpbox.example.jsonc`](mcpbox.example.jsonc) for all options. All string v
|
|
|
70
73
|
|
|
71
74
|
To expose MCPBox remotely, put it behind a TLS-terminating reverse proxy.
|
|
72
75
|
|
|
73
|
-
Before deploying:
|
|
74
|
-
- [ ] Use
|
|
75
|
-
- [ ] Set
|
|
76
|
-
- [ ] Use bcrypt hashes for passwords
|
|
76
|
+
Before deploying with OAuth:
|
|
77
|
+
- [ ] Use sqlite storage for persistence across restarts
|
|
78
|
+
- [ ] Set issuer to your public URL
|
|
79
|
+
- [ ] Use bcrypt hashes for local passwords
|
|
77
80
|
|
|
78
81
|
> [!NOTE]
|
|
79
82
|
> MCPBox is single-instance only — don't run multiple instances behind a load balancer.
|
|
@@ -93,7 +96,9 @@ Then update your config with the generated public URL:
|
|
|
93
96
|
"auth": {
|
|
94
97
|
"type": "oauth",
|
|
95
98
|
"issuer": "https://<tunnel-id>.trycloudflare.com",
|
|
96
|
-
"
|
|
99
|
+
"identityProviders": [
|
|
100
|
+
{ "type": "local", "users": [{ "username": "admin", "password": "${MCPBOX_PASSWORD}" }] }
|
|
101
|
+
],
|
|
97
102
|
"dynamicRegistration": true
|
|
98
103
|
},
|
|
99
104
|
"storage": {
|
|
@@ -126,12 +131,28 @@ claude mcp add --transport http mcpbox https://your-mcpbox-url.com
|
|
|
126
131
|
|
|
127
132
|
Requires `dynamicRegistration: true` in your config.
|
|
128
133
|
|
|
129
|
-
### MCP clients
|
|
134
|
+
### Other MCP clients
|
|
135
|
+
|
|
136
|
+
**With dynamic registration (OAuth)** — just provide the URL:
|
|
130
137
|
|
|
131
138
|
```json
|
|
132
139
|
{
|
|
133
140
|
"mcpServers": {
|
|
134
141
|
"mcpbox": {
|
|
142
|
+
"type": "http",
|
|
143
|
+
"url": "https://your-mcpbox-url.com"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**With API key:**
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"mcpServers": {
|
|
154
|
+
"mcpbox": {
|
|
155
|
+
"type": "http",
|
|
135
156
|
"url": "https://your-mcpbox-url.com",
|
|
136
157
|
"headers": {
|
|
137
158
|
"Authorization": "Bearer YOUR_API_KEY"
|
package/dist/auth/oauth.js
CHANGED
|
@@ -63,7 +63,7 @@ export class OAuthServer {
|
|
|
63
63
|
return {
|
|
64
64
|
resource: this.config.issuer,
|
|
65
65
|
authorization_servers: [this.config.issuer],
|
|
66
|
-
scopes_supported: ["mcp
|
|
66
|
+
scopes_supported: ["mcp"],
|
|
67
67
|
bearer_methods_supported: ["header"],
|
|
68
68
|
// Non-standard: logo for client display
|
|
69
69
|
logo_uri: `${this.config.issuer}/logo.png`,
|
|
@@ -87,7 +87,7 @@ export class OAuthServer {
|
|
|
87
87
|
token_endpoint: `${this.config.issuer}/token`,
|
|
88
88
|
grant_types_supported: grantTypes,
|
|
89
89
|
token_endpoint_auth_methods_supported: ["none", "client_secret_post"],
|
|
90
|
-
scopes_supported: ["mcp
|
|
90
|
+
scopes_supported: ["mcp"],
|
|
91
91
|
};
|
|
92
92
|
// Only advertise authorization endpoint if identity providers are configured
|
|
93
93
|
if (hasProviders) {
|
|
@@ -656,7 +656,7 @@ export class OAuthServer {
|
|
|
656
656
|
this.store.saveAccessToken({
|
|
657
657
|
token: hashSecret(accessToken),
|
|
658
658
|
clientId,
|
|
659
|
-
scope: "mcp
|
|
659
|
+
scope: "mcp",
|
|
660
660
|
expiresAt: Date.now() + expiresIn * 1000,
|
|
661
661
|
userId: `client:${clientId}`, // Mark as client-authenticated
|
|
662
662
|
});
|
|
@@ -666,7 +666,7 @@ export class OAuthServer {
|
|
|
666
666
|
access_token: accessToken, // Return unhashed token to client
|
|
667
667
|
token_type: "Bearer",
|
|
668
668
|
expires_in: expiresIn,
|
|
669
|
-
scope: "mcp
|
|
669
|
+
scope: "mcp",
|
|
670
670
|
});
|
|
671
671
|
}
|
|
672
672
|
// RFC 6749 Section 4.1: Authorization Code Grant
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
2
3
|
import { loadConfig, resolveConfigPath, } from "./config/loader.js";
|
|
3
4
|
import { configureLogger, logger } from "./logger.js";
|
|
4
5
|
import { createServer } from "./server.js";
|
|
@@ -33,10 +34,6 @@ function parseArgs(args) {
|
|
|
33
34
|
else if (arg === "-c" || arg === "--config") {
|
|
34
35
|
result.config = args[++i];
|
|
35
36
|
}
|
|
36
|
-
else if (!arg.startsWith("-")) {
|
|
37
|
-
// Positional arg = config path (backwards compat)
|
|
38
|
-
result.config = arg;
|
|
39
|
-
}
|
|
40
37
|
}
|
|
41
38
|
return result;
|
|
42
39
|
}
|
|
@@ -50,6 +47,10 @@ if (args.version) {
|
|
|
50
47
|
process.exit(0);
|
|
51
48
|
}
|
|
52
49
|
const configPath = resolveConfigPath(args.config);
|
|
50
|
+
if (args.config && !existsSync(configPath)) {
|
|
51
|
+
console.error(`Config file not found: ${configPath}`);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
53
54
|
let config;
|
|
54
55
|
let warnings;
|
|
55
56
|
try {
|
|
@@ -57,7 +58,6 @@ try {
|
|
|
57
58
|
}
|
|
58
59
|
catch (error) {
|
|
59
60
|
const message = error instanceof Error ? error.message : String(error);
|
|
60
|
-
// Log to stderr directly since logger config isn't loaded yet
|
|
61
61
|
console.error(`Failed to load config from ${configPath}:\n${message}`);
|
|
62
62
|
process.exit(1);
|
|
63
63
|
}
|