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 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
- - Aggregate multiple local stdio servers behind a single HTTP endpoint
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 (e.g., `github__create_issue`)
12
+ - Namespaces with `servername__` prefix to avoid collisions
13
13
  - OAuth or API key authentication
14
14
 
15
- ![mcpbox diagram](assets/diagram.excalidraw.png)
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** — choose one:
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 `storage.type: "sqlite"` for persistence across restarts
82
- - [ ] Set `auth.issuer` to your public URL when using OAuth
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
- "users": [{ "username": "admin", "password": "${MCPBOX_PASSWORD}" }],
104
- "dynamic_registration": true
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 `dynamic_registration: true` in your config.
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 `dynamic_registration: true` in your config.
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
- ### MCP clients with JSON config
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"
@@ -1,4 +1,5 @@
1
1
  /**
2
2
  * Timing-safe string comparison to prevent timing attacks.
3
+ * @package
3
4
  */
4
5
  export declare function safeCompare(a: string, b: string): boolean;
@@ -1,6 +1,7 @@
1
1
  import { timingSafeEqual } from "node:crypto";
2
2
  /**
3
3
  * Timing-safe string comparison to prevent timing attacks.
4
+ * @package
4
5
  */
5
6
  export function safeCompare(a, b) {
6
7
  try {
@@ -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;
@@ -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.redirect_uris?.includes(redirectUri) ?? false;
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) {
@@ -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
- users?: Array<{
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;