mcpbox 0.1.1 → 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 +35 -21
- package/dist/auth/crypto.d.ts +1 -0
- package/dist/auth/crypto.js +1 -0
- package/dist/auth/oauth-utils.d.ts +6 -0
- package/dist/auth/oauth-utils.js +7 -1
- package/dist/auth/oauth.d.ts +5 -4
- package/dist/auth/oauth.js +323 -181
- package/dist/auth/providers/github.d.ts +18 -0
- package/dist/auth/providers/github.js +104 -0
- package/dist/auth/providers/identity-provider.d.ts +32 -0
- package/dist/auth/providers/identity-provider.js +1 -0
- package/dist/auth/providers/local.d.ts +14 -0
- package/dist/auth/providers/local.js +16 -0
- package/dist/config/loader.js +8 -4
- package/dist/config/schema.d.ts +114 -35
- package/dist/config/schema.js +67 -19
- package/dist/config/types.d.ts +1 -1
- package/dist/index.js +5 -5
- package/dist/logger.d.ts +4 -0
- package/dist/logger.js +5 -1
- package/dist/mcp/manager.js +1 -1
- package/dist/mcp/namespace.d.ts +4 -0
- package/dist/mcp/namespace.js +4 -0
- package/dist/server.js +36 -5
- package/dist/storage/memory.js +4 -4
- package/dist/storage/sqlite.d.ts +0 -1
- package/dist/storage/sqlite.js +7 -9
- package/dist/storage/types.d.ts +15 -15
- package/package.json +8 -5
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
|
|
|
@@ -62,25 +65,18 @@ For remote access with authentication, see [Deployment](#deployment) and [Connec
|
|
|
62
65
|
|
|
63
66
|
## Configuration
|
|
64
67
|
|
|
65
|
-
See [`mcpbox.example.jsonc`](mcpbox.example.jsonc) for all options.
|
|
68
|
+
See [`mcpbox.example.jsonc`](mcpbox.example.jsonc) for all options. All string values support `${VAR_NAME}` environment variable substitution.
|
|
66
69
|
|
|
67
|
-
**Authentication** —
|
|
68
|
-
- **None** — default
|
|
69
|
-
- **API Key** — `auth.type: "apikey"` with `auth.apiKey`
|
|
70
|
-
- **OAuth** — `auth.type: "oauth"` with users, clients, and optional dynamic registration
|
|
71
|
-
|
|
72
|
-
**Storage** (for OAuth):
|
|
73
|
-
- **Memory** — default, tokens lost on restart
|
|
74
|
-
- **SQLite** — `storage.type: "sqlite"` for persistence
|
|
70
|
+
**[Authentication](docs/authentication.md)** — none (default), API key, or OAuth.
|
|
75
71
|
|
|
76
72
|
## Deployment
|
|
77
73
|
|
|
78
74
|
To expose MCPBox remotely, put it behind a TLS-terminating reverse proxy.
|
|
79
75
|
|
|
80
|
-
Before deploying:
|
|
81
|
-
- [ ] Use
|
|
82
|
-
- [ ] Set
|
|
83
|
-
- [ ] 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
|
|
84
80
|
|
|
85
81
|
> [!NOTE]
|
|
86
82
|
> MCPBox is single-instance only — don't run multiple instances behind a load balancer.
|
|
@@ -100,8 +96,10 @@ Then update your config with the generated public URL:
|
|
|
100
96
|
"auth": {
|
|
101
97
|
"type": "oauth",
|
|
102
98
|
"issuer": "https://<tunnel-id>.trycloudflare.com",
|
|
103
|
-
"
|
|
104
|
-
|
|
99
|
+
"identityProviders": [
|
|
100
|
+
{ "type": "local", "users": [{ "username": "admin", "password": "${MCPBOX_PASSWORD}" }] }
|
|
101
|
+
],
|
|
102
|
+
"dynamicRegistration": true
|
|
105
103
|
},
|
|
106
104
|
"storage": {
|
|
107
105
|
"type": "sqlite",
|
|
@@ -123,7 +121,7 @@ docker run -v ./mcpbox.json:/config/config.json -v ./data:/data -p 8080:8080 ghc
|
|
|
123
121
|
|
|
124
122
|
Settings → Connectors → Add Custom Connector → enter your URL → Connect
|
|
125
123
|
|
|
126
|
-
Requires `
|
|
124
|
+
Requires `dynamicRegistration: true` in your config.
|
|
127
125
|
|
|
128
126
|
### Claude Code
|
|
129
127
|
|
|
@@ -131,14 +129,30 @@ Requires `dynamic_registration: true` in your config.
|
|
|
131
129
|
claude mcp add --transport http mcpbox https://your-mcpbox-url.com
|
|
132
130
|
```
|
|
133
131
|
|
|
134
|
-
Requires `
|
|
132
|
+
Requires `dynamicRegistration: true` in your config.
|
|
133
|
+
|
|
134
|
+
### Other MCP clients
|
|
135
|
+
|
|
136
|
+
**With dynamic registration (OAuth)** — just provide the URL:
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"mcpServers": {
|
|
141
|
+
"mcpbox": {
|
|
142
|
+
"type": "http",
|
|
143
|
+
"url": "https://your-mcpbox-url.com"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
135
148
|
|
|
136
|
-
|
|
149
|
+
**With API key:**
|
|
137
150
|
|
|
138
151
|
```json
|
|
139
152
|
{
|
|
140
153
|
"mcpServers": {
|
|
141
154
|
"mcpbox": {
|
|
155
|
+
"type": "http",
|
|
142
156
|
"url": "https://your-mcpbox-url.com",
|
|
143
157
|
"headers": {
|
|
144
158
|
"Authorization": "Bearer YOUR_API_KEY"
|
package/dist/auth/crypto.d.ts
CHANGED
package/dist/auth/crypto.js
CHANGED
|
@@ -2,28 +2,34 @@ import type { StoredClient } from "../storage/types.js";
|
|
|
2
2
|
/**
|
|
3
3
|
* Hash a secret using SHA-256.
|
|
4
4
|
* Used for storing tokens and client secrets.
|
|
5
|
+
* @package
|
|
5
6
|
*/
|
|
6
7
|
export declare function hashSecret(secret: string): string;
|
|
7
8
|
/**
|
|
8
9
|
* Verify a client secret against a stored hash using timing-safe comparison.
|
|
10
|
+
* @package
|
|
9
11
|
*/
|
|
10
12
|
export declare function verifyClientSecret(input: string, storedHash: string): boolean;
|
|
11
13
|
/**
|
|
12
14
|
* Check if a password string is a bcrypt hash.
|
|
15
|
+
* @package
|
|
13
16
|
*/
|
|
14
17
|
export declare function isBcryptHash(password: string): boolean;
|
|
15
18
|
/**
|
|
16
19
|
* Verify a password against a stored value.
|
|
17
20
|
* Supports both plain text (timing-safe) and bcrypt hashed passwords.
|
|
21
|
+
* @package
|
|
18
22
|
*/
|
|
19
23
|
export declare function verifyPassword(input: string, stored: string): boolean;
|
|
20
24
|
/**
|
|
21
25
|
* Check if a redirect URI is allowed for a client.
|
|
22
26
|
* Uses exact string matching as required by OAuth 2.0 spec.
|
|
27
|
+
* @package
|
|
23
28
|
*/
|
|
24
29
|
export declare function isRedirectUriAllowed(redirectUri: string, client: StoredClient): boolean;
|
|
25
30
|
/**
|
|
26
31
|
* Parse a Bearer token from an Authorization header.
|
|
27
32
|
* Returns null if the header is missing or malformed.
|
|
33
|
+
* @package
|
|
28
34
|
*/
|
|
29
35
|
export declare function parseBearerToken(authHeader: string | undefined): string | null;
|
package/dist/auth/oauth-utils.js
CHANGED
|
@@ -4,18 +4,21 @@ import { safeCompare } from "./crypto.js";
|
|
|
4
4
|
/**
|
|
5
5
|
* Hash a secret using SHA-256.
|
|
6
6
|
* Used for storing tokens and client secrets.
|
|
7
|
+
* @package
|
|
7
8
|
*/
|
|
8
9
|
export function hashSecret(secret) {
|
|
9
10
|
return createHash("sha256").update(secret).digest("hex");
|
|
10
11
|
}
|
|
11
12
|
/**
|
|
12
13
|
* Verify a client secret against a stored hash using timing-safe comparison.
|
|
14
|
+
* @package
|
|
13
15
|
*/
|
|
14
16
|
export function verifyClientSecret(input, storedHash) {
|
|
15
17
|
return safeCompare(hashSecret(input), storedHash);
|
|
16
18
|
}
|
|
17
19
|
/**
|
|
18
20
|
* Check if a password string is a bcrypt hash.
|
|
21
|
+
* @package
|
|
19
22
|
*/
|
|
20
23
|
export function isBcryptHash(password) {
|
|
21
24
|
return /^\$2[aby]\$\d{2}\$/.test(password);
|
|
@@ -23,6 +26,7 @@ export function isBcryptHash(password) {
|
|
|
23
26
|
/**
|
|
24
27
|
* Verify a password against a stored value.
|
|
25
28
|
* Supports both plain text (timing-safe) and bcrypt hashed passwords.
|
|
29
|
+
* @package
|
|
26
30
|
*/
|
|
27
31
|
export function verifyPassword(input, stored) {
|
|
28
32
|
if (isBcryptHash(stored)) {
|
|
@@ -33,13 +37,15 @@ export function verifyPassword(input, stored) {
|
|
|
33
37
|
/**
|
|
34
38
|
* Check if a redirect URI is allowed for a client.
|
|
35
39
|
* Uses exact string matching as required by OAuth 2.0 spec.
|
|
40
|
+
* @package
|
|
36
41
|
*/
|
|
37
42
|
export function isRedirectUriAllowed(redirectUri, client) {
|
|
38
|
-
return client.
|
|
43
|
+
return client.redirectUris?.includes(redirectUri) ?? false;
|
|
39
44
|
}
|
|
40
45
|
/**
|
|
41
46
|
* Parse a Bearer token from an Authorization header.
|
|
42
47
|
* Returns null if the header is missing or malformed.
|
|
48
|
+
* @package
|
|
43
49
|
*/
|
|
44
50
|
export function parseBearerToken(authHeader) {
|
|
45
51
|
if (!authHeader) {
|
package/dist/auth/oauth.d.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import type { Context } from "hono";
|
|
2
2
|
import type { OAuthClient } from "../config/types.js";
|
|
3
3
|
import type { StateStore } from "../storage/types.js";
|
|
4
|
+
import type { IdentityProvider } from "./providers/identity-provider.js";
|
|
4
5
|
export interface OAuthConfig {
|
|
5
6
|
issuer: string;
|
|
6
|
-
|
|
7
|
-
username: string;
|
|
8
|
-
password: string;
|
|
9
|
-
}>;
|
|
7
|
+
providers: IdentityProvider[];
|
|
10
8
|
clients?: OAuthClient[];
|
|
11
9
|
dynamicRegistration?: boolean;
|
|
12
10
|
}
|
|
@@ -22,6 +20,9 @@ export declare class OAuthServer {
|
|
|
22
20
|
handleRegister(c: Context, body: string): Promise<Response>;
|
|
23
21
|
private getClient;
|
|
24
22
|
handleAuthorize(c: Context, query: URLSearchParams, body?: string): Promise<Response>;
|
|
23
|
+
private issueAuthorizationCode;
|
|
24
|
+
private redirectToProvider;
|
|
25
|
+
handleIdPCallback(c: Context, providerId: string, query: URLSearchParams): Promise<Response>;
|
|
25
26
|
private showLoginForm;
|
|
26
27
|
handleToken(c: Context, body: string): Promise<Response>;
|
|
27
28
|
private handleClientCredentialsGrant;
|