mcpico 0.1.1 → 0.2.0

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
@@ -24,9 +24,12 @@ Group related tools under a single entry point. The model sees 9 groups instead
24
24
  - **Tool bundling** — Groups tools by prefix (configurable separator), collapsing flat tool lists
25
25
  - **Auto-generated help** — Each group's `help` subcommand is built from upstream tool schemas
26
26
  - **Multi-server aggregation** — Proxy multiple upstream MCP servers through one interface
27
- - **Dual transport** — Supports both stdio and Streamable HTTP (SSE) upstream servers
27
+ - **Dual upstream transport** — Supports both stdio and Streamable HTTP (SSE) upstream servers
28
+ - **Dual listen transport** — MCPico itself listens via stdio or HTTP/SSE (configurable port)
28
29
  - **Configurable timeouts** — Per-server connection timeout with sensible default (30s)
29
30
  - **Resource & prompt passthrough** — Namespaced to avoid collisions across servers
31
+ - **Authentication** — Bearer, custom header, and OAuth2 client_credentials with automatic token refresh
32
+ - **Listen endpoint auth** — Protect the SSE endpoint with bearer token validation
30
33
 
31
34
  ## Usage
32
35
 
@@ -132,6 +135,25 @@ Groups from different servers are merged if they share a prefix. Otherwise each
132
135
  | `servers` | `ServerConfig[]` | **required** | Upstream MCP servers to proxy |
133
136
  | `separator` | `string` | `"_"` | Separator for prefix-based tool grouping |
134
137
  | `groups` | `object` | `{}` | Explicit group overrides (`{ "group": ["tool1","tool2"] }`) |
138
+ | `listen` | `ListenConfig` | `{"type":"stdio"}` | How MCPico exposes itself to MCP clients |
139
+
140
+ ### ListenConfig
141
+
142
+ | Field | Type | Required | Description |
143
+ |-------|------|----------|-------------|
144
+ | `type` | `"stdio"` | yes | Standard stdio transport |
145
+ | `type` | `"sse"` | yes | HTTP/SSE — specify `port` and optional `host` |
146
+
147
+ ```json
148
+ // SSE listen mode — MCPico as an HTTP endpoint
149
+ {
150
+ "servers": [...],
151
+ "listen": {
152
+ "type": "sse",
153
+ "port": 3000
154
+ }
155
+ }
156
+ ```
135
157
 
136
158
  ### ServerConfig
137
159
 
@@ -158,12 +180,111 @@ Groups from different servers are merged if they share a prefix. Otherwise each
158
180
  | `type` | `"sse"` | yes | Transport type |
159
181
  | `url` | `string` | yes | Full URL to MCP Streamable HTTP endpoint |
160
182
 
183
+ ## Authentication
184
+
185
+ MCPico supports two layers of authentication:
186
+
187
+ ### Layer 1: Protecting the listen endpoint
188
+
189
+ When MCPico exposes an SSE endpoint, you can require a bearer token from clients:
190
+
191
+ ```json
192
+ {
193
+ "servers": [...],
194
+ "listen": {
195
+ "type": "sse",
196
+ "port": 3000,
197
+ "auth": {
198
+ "type": "bearer",
199
+ "token": "${MCPICO_API_KEY}"
200
+ }
201
+ }
202
+ }
203
+ ```
204
+
205
+ Clients must include `Authorization: Bearer <token>` in requests. Invalid or missing tokens receive a 401 response.
206
+
207
+ ### Layer 2: Authenticating to upstream servers
208
+
209
+ Upstream servers can require authentication. MCPico supports three methods:
210
+
211
+ **Bearer token** — standard `Authorization: Bearer <token>` header:
212
+
213
+ ```json
214
+ {
215
+ "servers": [
216
+ {
217
+ "name": "internal-api",
218
+ "transport": {
219
+ "type": "sse",
220
+ "url": "https://api.internal/mcp"
221
+ },
222
+ "auth": {
223
+ "type": "bearer",
224
+ "token": "${INTERNAL_KEY}"
225
+ }
226
+ }
227
+ ]
228
+ }
229
+ ```
230
+
231
+ **Custom header** — arbitrary headers (e.g. `X-API-Key`):
232
+
233
+ ```json
234
+ {
235
+ "auth": {
236
+ "type": "header",
237
+ "name": "X-API-Key",
238
+ "value": "${WIDGET_KEY}"
239
+ }
240
+ }
241
+ ```
242
+
243
+ **OAuth 2.0 client credentials** — machine-to-machine authentication with automatic token refresh:
244
+
245
+ ```json
246
+ {
247
+ "auth": {
248
+ "type": "oauth",
249
+ "grant_type": "client_credentials",
250
+ "client_id": "${PROVIDER_CLIENT_ID}",
251
+ "client_secret": "${PROVIDER_CLIENT_SECRET}",
252
+ "token_url": "https://auth.example.com/oauth/token",
253
+ "scopes": ["read", "write"]
254
+ }
255
+ }
256
+ ```
257
+
258
+ MCPico handles the full OAuth flow:
259
+ - Fetches initial access token on startup
260
+ - Caches tokens in `~/.mcplico/credentials.json`
261
+ - Automatically refreshes before expiry
262
+ - Retries on 401 with fresh tokens
263
+
264
+ All auth fields support `${ENV_VAR}` interpolation — never hardcode secrets.
265
+
266
+ ### Auth config reference
267
+
268
+ | Field | Type | Required | Description |
269
+ |-------|------|----------|-------------|
270
+ | `auth.type` | `"bearer"` \| `"header"` \| `"oauth"` | yes | Auth method |
271
+ | `auth.token` | `string` | for `bearer` | Bearer token value |
272
+ | `auth.name` | `string` | for `header` | Header name |
273
+ | `auth.value` | `string` | for `header` | Header value |
274
+ | `auth.grant_type` | `"client_credentials"` | for `oauth` | OAuth grant type |
275
+ | `auth.client_id` | `string` | for `oauth` | OAuth client ID |
276
+ | `auth.client_secret` | `string` | for `oauth` | OAuth client secret |
277
+ | `auth.token_url` | `string` | for `oauth` | Token endpoint URL |
278
+ | `auth.scopes` | `string[]` | no | OAuth scopes to request |
279
+ | `auth.authorization_server_url` | `string` | no | Auth server URL (if different from token_url issuer) |
280
+
281
+ ## Development
161
282
  ## Development
162
283
 
163
284
  ```bash
164
285
  npm install
165
286
  npm run build # TypeScript compilation
166
- npm test # Run tests (77 tests, vitest)
287
+ npm test # Run tests (138 tests, vitest)
167
288
  npm run dev # Run directly with tsx
168
289
  ```
169
290
 
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Authentication types for MCPico.
3
+ *
4
+ * Two layers:
5
+ * Layer 1 — Auth for the listen endpoint (protecting MCPico from clients)
6
+ * Layer 2 — Auth passthrough to upstream MCP servers
7
+ */
8
+ export interface BearerAuth {
9
+ type: "bearer";
10
+ /** Bearer token value. Supports ${ENV_VAR} interpolation. */
11
+ token: string;
12
+ }
13
+ export interface HeaderAuth {
14
+ type: "header";
15
+ /** Header name (e.g. "X-API-Key", "X-Auth-Token") */
16
+ name: string;
17
+ /** Header value. Supports ${ENV_VAR} interpolation. */
18
+ value: string;
19
+ }
20
+ export interface OAuthClientCredentials {
21
+ type: "oauth";
22
+ /** OAuth 2.0 grant type */
23
+ grant_type: "client_credentials";
24
+ /** Client ID for the OAuth provider */
25
+ client_id: string;
26
+ /** Client secret for the OAuth provider */
27
+ client_secret: string;
28
+ /** Token endpoint URL */
29
+ token_url: string;
30
+ /** Optional scopes to request */
31
+ scopes?: string[];
32
+ /** Optional authorization server metadata URL (if different from token_url issuer) */
33
+ authorization_server_url?: string;
34
+ }
35
+ export type UpstreamAuth = BearerAuth | HeaderAuth | OAuthClientCredentials;
36
+ export interface ListenBearerAuth {
37
+ type: "bearer";
38
+ /** Bearer token that clients must provide. Supports ${ENV_VAR} interpolation. */
39
+ token: string;
40
+ }
41
+ export type ListenAuth = ListenBearerAuth;
42
+ export interface ResolvedBearerAuth {
43
+ type: "bearer";
44
+ token: string;
45
+ }
46
+ export interface ResolvedHeaderAuth {
47
+ type: "header";
48
+ name: string;
49
+ value: string;
50
+ }
51
+ export type ResolvedUpstreamAuth = ResolvedBearerAuth | ResolvedHeaderAuth | (OAuthClientCredentials & {
52
+ type: "oauth";
53
+ });
54
+ export type ResolvedListenAuth = ResolvedBearerAuth;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Authentication types for MCPico.
3
+ *
4
+ * Two layers:
5
+ * Layer 1 — Auth for the listen endpoint (protecting MCPico from clients)
6
+ * Layer 2 — Auth passthrough to upstream MCP servers
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=auth-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-types.js","sourceRoot":"","sources":["../src/auth-types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
package/dist/auth.d.ts ADDED
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Auth utilities: env var resolution, header generation, listen token validation.
3
+ */
4
+ import type { IncomingMessage, ServerResponse } from "node:http";
5
+ import type { UpstreamAuth, ListenAuth, ResolvedUpstreamAuth, ResolvedListenAuth } from "./auth-types.js";
6
+ /**
7
+ * Replace ${VAR} patterns with process.env values.
8
+ * Throws if a referenced env var is not set.
9
+ */
10
+ export declare function resolveEnvVars(value: string): string;
11
+ /**
12
+ * Resolve an UpstreamAuth config to a ResolvedUpstreamAuth with env vars interpolated.
13
+ */
14
+ export declare function resolveUpstreamAuth(auth: UpstreamAuth): ResolvedUpstreamAuth;
15
+ /**
16
+ * Resolve a ListenAuth config to ResolvedListenAuth.
17
+ */
18
+ export declare function resolveListenAuth(auth: ListenAuth): ResolvedListenAuth;
19
+ /**
20
+ * Generate HTTP headers from a resolved upstream auth config.
21
+ * For bearer/header types, returns headers to attach to requests.
22
+ * For OAuth, returns empty — the SDK authProvider handles it.
23
+ */
24
+ export declare function upstreamAuthHeaders(auth: ResolvedUpstreamAuth): Record<string, string>;
25
+ /**
26
+ * Extract bearer token from an Authorization header.
27
+ * Returns undefined if no Bearer token found.
28
+ */
29
+ export declare function extractBearerToken(req: IncomingMessage): string | undefined;
30
+ /**
31
+ * Validate a bearer token against the configured token.
32
+ * Returns true if token is valid, false otherwise.
33
+ */
34
+ export declare function validateBearerToken(providedToken: string | undefined, expectedToken: string): boolean;
35
+ /**
36
+ * Send a 401 Unauthorized response with a WWW-Authenticate header.
37
+ */
38
+ export declare function sendUnauthorized(res: ServerResponse): void;
package/dist/auth.js ADDED
@@ -0,0 +1,122 @@
1
+ // ── Env var interpolation ──
2
+ /**
3
+ * Replace ${VAR} patterns with process.env values.
4
+ * Throws if a referenced env var is not set.
5
+ */
6
+ export function resolveEnvVars(value) {
7
+ return value.replace(/\$\{(\w+)\}/g, (_match, varName) => {
8
+ const envValue = process.env[varName];
9
+ if (envValue === undefined) {
10
+ throw new Error(`Environment variable "${varName}" is not set. ` +
11
+ `Referenced in auth config. Set it or remove the auth block.`);
12
+ }
13
+ return envValue;
14
+ });
15
+ }
16
+ // ── Auth resolution ──
17
+ /**
18
+ * Resolve an UpstreamAuth config to a ResolvedUpstreamAuth with env vars interpolated.
19
+ */
20
+ export function resolveUpstreamAuth(auth) {
21
+ switch (auth.type) {
22
+ case "bearer":
23
+ return {
24
+ type: "bearer",
25
+ token: resolveEnvVars(auth.token),
26
+ };
27
+ case "header":
28
+ return {
29
+ type: "header",
30
+ name: resolveEnvVars(auth.name),
31
+ value: resolveEnvVars(auth.value),
32
+ };
33
+ case "oauth":
34
+ // OAuth credentials are resolved at request time by the provider
35
+ return {
36
+ type: "oauth",
37
+ grant_type: auth.grant_type,
38
+ client_id: resolveEnvVars(auth.client_id),
39
+ client_secret: resolveEnvVars(auth.client_secret),
40
+ token_url: resolveEnvVars(auth.token_url),
41
+ scopes: auth.scopes,
42
+ authorization_server_url: auth.authorization_server_url
43
+ ? resolveEnvVars(auth.authorization_server_url)
44
+ : undefined,
45
+ };
46
+ }
47
+ }
48
+ /**
49
+ * Resolve a ListenAuth config to ResolvedListenAuth.
50
+ */
51
+ export function resolveListenAuth(auth) {
52
+ return {
53
+ type: "bearer",
54
+ token: resolveEnvVars(auth.token),
55
+ };
56
+ }
57
+ // ── Header generation for upstream requests ──
58
+ /**
59
+ * Generate HTTP headers from a resolved upstream auth config.
60
+ * For bearer/header types, returns headers to attach to requests.
61
+ * For OAuth, returns empty — the SDK authProvider handles it.
62
+ */
63
+ export function upstreamAuthHeaders(auth) {
64
+ if (auth.type === "bearer") {
65
+ return { Authorization: `Bearer ${auth.token}` };
66
+ }
67
+ if (auth.type === "header") {
68
+ return { [auth.name]: auth.value };
69
+ }
70
+ // OAuth is handled by the MCP SDK's authProvider — no static headers
71
+ return {};
72
+ }
73
+ // ── Listen endpoint auth middleware ──
74
+ /**
75
+ * Extract bearer token from an Authorization header.
76
+ * Returns undefined if no Bearer token found.
77
+ */
78
+ export function extractBearerToken(req) {
79
+ const auth = req.headers.authorization;
80
+ if (!auth)
81
+ return undefined;
82
+ const match = auth.match(/^Bearer\s+(.+)$/i);
83
+ return match ? match[1] : undefined;
84
+ }
85
+ /**
86
+ * Validate a bearer token against the configured token.
87
+ * Returns true if token is valid, false otherwise.
88
+ */
89
+ export function validateBearerToken(providedToken, expectedToken) {
90
+ if (!providedToken)
91
+ return false;
92
+ // Constant-time comparison to prevent timing attacks
93
+ return timingSafeEqual(providedToken, expectedToken);
94
+ }
95
+ /**
96
+ * Send a 401 Unauthorized response with a WWW-Authenticate header.
97
+ */
98
+ export function sendUnauthorized(res) {
99
+ res.writeHead(401, {
100
+ "Content-Type": "application/json",
101
+ "WWW-Authenticate": 'Bearer realm="mcplico"',
102
+ });
103
+ res.end(JSON.stringify({ error: "Unauthorized", message: "Bearer token required" }));
104
+ }
105
+ // ── Constant-time string comparison ──
106
+ function timingSafeEqual(a, b) {
107
+ if (a.length !== b.length) {
108
+ // Still compare to avoid leaking length via timing
109
+ const maxLen = Math.max(a.length, b.length);
110
+ let result = 0;
111
+ for (let i = 0; i < maxLen; i++) {
112
+ result |= a.charCodeAt(i % a.length) ^ b.charCodeAt(i % b.length);
113
+ }
114
+ return false;
115
+ }
116
+ let result = 0;
117
+ for (let i = 0; i < a.length; i++) {
118
+ result |= a.charCodeAt(i) ^ b.charCodeAt(i);
119
+ }
120
+ return result === 0;
121
+ }
122
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
package/dist/config.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Transport configuration for connecting to an upstream MCP server.
3
3
  */
4
+ import type { UpstreamAuth, ListenAuth } from "./auth-types.js";
4
5
  export type TransportConfig = {
5
6
  type: "stdio";
6
7
  command: string;
@@ -22,6 +23,8 @@ export interface ServerConfig {
22
23
  transport: TransportConfig;
23
24
  /** Connection timeout in milliseconds (default: 30000) */
24
25
  connectTimeoutMs?: number;
26
+ /** Authentication config for this upstream server */
27
+ auth?: UpstreamAuth;
25
28
  }
26
29
  /**
27
30
  * Tool grouping override — map group names to explicit tool lists.
@@ -29,6 +32,17 @@ export interface ServerConfig {
29
32
  export interface GroupOverrides {
30
33
  [groupName: string]: string[];
31
34
  }
35
+ /**
36
+ * Transport to expose MCPico itself to clients.
37
+ */
38
+ export type ListenConfig = {
39
+ type: "stdio";
40
+ } | {
41
+ type: "sse";
42
+ port: number;
43
+ host?: string;
44
+ auth?: ListenAuth;
45
+ };
32
46
  /**
33
47
  * Full MCPico configuration.
34
48
  */
@@ -39,6 +53,8 @@ export interface MCPicoConfig {
39
53
  separator?: string;
40
54
  /** Explicit group overrides — tools not listed here are auto-grouped */
41
55
  groups?: GroupOverrides;
56
+ /** How MCPico exposes itself to clients (default: stdio) */
57
+ listen?: ListenConfig;
42
58
  }
43
59
  /**
44
60
  * Validate server config and return a user-friendly error message, or null if valid.
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAgDA;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAoB;IACvD,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACpD,OAAO,mDAAmD,CAAC;IAC7D,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACtB,OAAO,WAAW,MAAM,CAAC,IAAI,wBAAwB,CAAC;IACxD,CAAC;IACD,IAAI,MAAM,CAAC,SAAS,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QACtC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;YAC9B,OAAO,WAAW,MAAM,CAAC,IAAI,uCAAuC,CAAC;QACvE,CAAC;IACH,CAAC;SAAM,IAAI,MAAM,CAAC,SAAS,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC3C,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC;YAC1B,OAAO,WAAW,MAAM,CAAC,IAAI,iCAAiC,CAAC;QACjE,CAAC;QACD,IAAI,CAAC;YACH,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,WAAW,MAAM,CAAC,IAAI,+CAA+C,MAAM,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC;QACtG,CAAC;IACH,CAAC;SAAM,CAAC;QACN,OAAO,WAAW,MAAM,CAAC,IAAI,8BAA+B,MAAM,CAAC,SAAoC,CAAC,IAAI,0BAA0B,CAAC;IACzI,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AA8DA;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAoB;IACvD,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACpD,OAAO,mDAAmD,CAAC;IAC7D,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACtB,OAAO,WAAW,MAAM,CAAC,IAAI,wBAAwB,CAAC;IACxD,CAAC;IACD,IAAI,MAAM,CAAC,SAAS,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QACtC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;YAC9B,OAAO,WAAW,MAAM,CAAC,IAAI,uCAAuC,CAAC;QACvE,CAAC;IACH,CAAC;SAAM,IAAI,MAAM,CAAC,SAAS,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC3C,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC;YAC1B,OAAO,WAAW,MAAM,CAAC,IAAI,iCAAiC,CAAC;QACjE,CAAC;QACD,IAAI,CAAC;YACH,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,WAAW,MAAM,CAAC,IAAI,+CAA+C,MAAM,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC;QACtG,CAAC;IACH,CAAC;SAAM,CAAC;QACN,OAAO,WAAW,MAAM,CAAC,IAAI,8BAA+B,MAAM,CAAC,SAAoC,CAAC,IAAI,0BAA0B,CAAC;IACzI,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
2
  import type { TransportConfig } from "./config.js";
3
+ import type { ResolvedUpstreamAuth } from "./auth-types.js";
3
4
  /**
4
5
  * Tool metadata from an upstream MCP server.
5
6
  */
@@ -45,7 +46,7 @@ export interface DiscoveredServer {
45
46
  * Supports stdio and streamable HTTP transports.
46
47
  * Default connection timeout is 30 seconds.
47
48
  */
48
- export declare function discoverServer(name: string, transportConfig: TransportConfig, connectTimeoutMs?: number): Promise<DiscoveredServer>;
49
+ export declare function discoverServer(name: string, transportConfig: TransportConfig, connectTimeoutMs?: number, auth?: ResolvedUpstreamAuth): Promise<DiscoveredServer>;
49
50
  /**
50
51
  * Disconnect from an upstream server.
51
52
  */
@@ -1,13 +1,15 @@
1
1
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
2
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
3
3
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
4
+ import { upstreamAuthHeaders } from "./auth.js";
5
+ import { createClientCredentialsProvider } from "./oauth-provider.js";
4
6
  /**
5
7
  * Connect to an upstream MCP server and discover its tools.
6
8
  *
7
9
  * Supports stdio and streamable HTTP transports.
8
10
  * Default connection timeout is 30 seconds.
9
11
  */
10
- export async function discoverServer(name, transportConfig, connectTimeoutMs = 30_000) {
12
+ export async function discoverServer(name, transportConfig, connectTimeoutMs = 30_000, auth) {
11
13
  let transport;
12
14
  if (transportConfig.type === "stdio") {
13
15
  transport = new StdioClientTransport({
@@ -18,12 +20,22 @@ export async function discoverServer(name, transportConfig, connectTimeoutMs = 3
18
20
  });
19
21
  }
20
22
  else if (transportConfig.type === "sse") {
21
- transport = new StreamableHTTPClientTransport(new URL(transportConfig.url));
23
+ const url = new URL(transportConfig.url);
24
+ const opts = {};
25
+ if (auth) {
26
+ if (auth.type === "oauth") {
27
+ opts.authProvider = createClientCredentialsProvider(auth.client_id, auth.client_secret, transportConfig.url);
28
+ }
29
+ else {
30
+ opts.requestInit = { headers: upstreamAuthHeaders(auth) };
31
+ }
32
+ }
33
+ transport = new StreamableHTTPClientTransport(url, opts);
22
34
  }
23
35
  else {
24
36
  throw new Error(`Unsupported transport type: ${transportConfig.type}`);
25
37
  }
26
- const client = new Client({ name: `MCPico-${name}`, version: "0.1.0" }, { capabilities: {} });
38
+ const client = new Client({ name: `MCPico-${name}`, version: "0.2.0" }, { capabilities: {} });
27
39
  // Connect with timeout
28
40
  await withTimeout(client.connect(transport), connectTimeoutMs, `Connection to "${name}" timed out after ${connectTimeoutMs}ms`);
29
41
  // Discover tools
@@ -1 +1 @@
1
- {"version":3,"file":"discoverer.js","sourceRoot":"","sources":["../src/discoverer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"discoverer.js","sourceRoot":"","sources":["../src/discoverer.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ async function main() {
20
20
  i++;
21
21
  }
22
22
  else if (args[i] === "--version" || args[i] === "-v") {
23
- console.log("MCPico v0.1.0");
23
+ console.log("MCPico v0.2.0");
24
24
  process.exit(0);
25
25
  }
26
26
  else if (args[i] === "--help" || args[i] === "-h") {
@@ -0,0 +1,19 @@
1
+ /**
2
+ * OAuth client_credentials provider for MCPico.
3
+ *
4
+ * Implements the MCP SDK's OAuthClientProvider interface so that
5
+ * StreamableHTTPClientTransport handles auth discovery, token exchange,
6
+ * and refresh automatically.
7
+ */
8
+ import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
9
+ /**
10
+ * Create an OAuthClientProvider for client_credentials grant.
11
+ *
12
+ * Uses a fixed server URL for token storage key and provides
13
+ * client metadata from config. The MCP SDK handles:
14
+ * - RFC 9728 resource metadata discovery
15
+ * - RFC 8414 authorization server metadata discovery
16
+ * - Token exchange
17
+ * - Token refresh via refreshAuthorization()
18
+ */
19
+ export declare function createClientCredentialsProvider(clientId: string, clientSecret: string, serverUrl: string): OAuthClientProvider;
@@ -0,0 +1,79 @@
1
+ import { loadTokens, saveTokens, clearTokens } from "./token-store.js";
2
+ /**
3
+ * Create an OAuthClientProvider for client_credentials grant.
4
+ *
5
+ * Uses a fixed server URL for token storage key and provides
6
+ * client metadata from config. The MCP SDK handles:
7
+ * - RFC 9728 resource metadata discovery
8
+ * - RFC 8414 authorization server metadata discovery
9
+ * - Token exchange
10
+ * - Token refresh via refreshAuthorization()
11
+ */
12
+ export function createClientCredentialsProvider(clientId, clientSecret, serverUrl) {
13
+ return new ClientCredentialsProvider(clientId, clientSecret, serverUrl);
14
+ }
15
+ class ClientCredentialsProvider {
16
+ clientId;
17
+ clientSecret;
18
+ serverUrl;
19
+ _tokens;
20
+ constructor(clientId, clientSecret, serverUrl) {
21
+ this.clientId = clientId;
22
+ this.clientSecret = clientSecret;
23
+ this.serverUrl = serverUrl;
24
+ // Load any cached tokens on creation
25
+ this._tokens = loadTokens(serverUrl);
26
+ }
27
+ get redirectUrl() {
28
+ // client_credentials is non-interactive — no redirect URL
29
+ return undefined;
30
+ }
31
+ get clientMetadata() {
32
+ return {
33
+ redirect_uris: ["http://localhost:0/mcplico-callback"],
34
+ grant_types: ["client_credentials"],
35
+ client_name: "MCPico",
36
+ };
37
+ }
38
+ // Client information for token exchange
39
+ clientInformation() {
40
+ return {
41
+ client_id: this.clientId,
42
+ client_secret: this.clientSecret,
43
+ client_secret_expires_at: 0, // Never expires
44
+ };
45
+ }
46
+ // Return cached tokens (or undefined for initial auth)
47
+ tokens() {
48
+ return this._tokens;
49
+ }
50
+ // Persist tokens after successful auth/refresh
51
+ saveTokens(tokens) {
52
+ this._tokens = tokens;
53
+ saveTokens(this.serverUrl, tokens);
54
+ }
55
+ // No redirect needed for client_credentials
56
+ redirectToAuthorization(_authorizationUrl) {
57
+ // Non-interactive — no redirect
58
+ }
59
+ // PKCE not needed for client_credentials
60
+ saveCodeVerifier(_codeVerifier) {
61
+ // No-op for client_credentials
62
+ }
63
+ codeVerifier() {
64
+ return "";
65
+ }
66
+ // Use client_credentials grant
67
+ prepareTokenRequest(_scope) {
68
+ const params = new URLSearchParams({
69
+ grant_type: "client_credentials",
70
+ });
71
+ return params;
72
+ }
73
+ // Clear tokens on invalidation
74
+ invalidateCredentials(_scope) {
75
+ this._tokens = undefined;
76
+ clearTokens(this.serverUrl);
77
+ }
78
+ }
79
+ //# sourceMappingURL=oauth-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-provider.js","sourceRoot":"","sources":["../src/oauth-provider.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEvE;;;;;;;;;GASG;AACH,MAAM,UAAU,+BAA+B,CAC7C,QAAgB,EAChB,YAAoB,EACpB,SAAiB;IAEjB,OAAO,IAAI,yBAAyB,CAAC,QAAQ,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;AAC1E,CAAC;AAED,MAAM,yBAAyB;IAInB;IACA;IACA;IALF,OAAO,CAA0B;IAEzC,YACU,QAAgB,EAChB,YAAoB,EACpB,SAAiB;QAFjB,aAAQ,GAAR,QAAQ,CAAQ;QAChB,iBAAY,GAAZ,YAAY,CAAQ;QACpB,cAAS,GAAT,SAAS,CAAQ;QAEzB,qCAAqC;QACrC,IAAI,CAAC,OAAO,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC;IAED,IAAI,WAAW;QACb,0DAA0D;QAC1D,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,cAAc;QAChB,OAAO;YACL,aAAa,EAAE,CAAC,qCAAqC,CAAC;YACtD,WAAW,EAAE,CAAC,oBAAoB,CAAC;YACnC,WAAW,EAAE,QAAQ;SACtB,CAAC;IACJ,CAAC;IAED,wCAAwC;IACxC,iBAAiB;QACf,OAAO;YACL,SAAS,EAAE,IAAI,CAAC,QAAQ;YACxB,aAAa,EAAE,IAAI,CAAC,YAAY;YAChC,wBAAwB,EAAE,CAAC,EAAE,gBAAgB;SAC9C,CAAC;IACJ,CAAC;IAED,uDAAuD;IACvD,MAAM;QACJ,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,+CAA+C;IAC/C,UAAU,CAAC,MAAmB;QAC5B,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACrC,CAAC;IAED,4CAA4C;IAC5C,uBAAuB,CAAC,iBAAsB;QAC5C,gCAAgC;IAClC,CAAC;IAED,yCAAyC;IACzC,gBAAgB,CAAC,aAAqB;QACpC,+BAA+B;IACjC,CAAC;IAED,YAAY;QACV,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,+BAA+B;IAC/B,mBAAmB,CACjB,MAAe;QAEf,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;YACjC,UAAU,EAAE,oBAAoB;SACjC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,+BAA+B;IAC/B,qBAAqB,CAAC,MAA8D;QAClF,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QACzB,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC9B,CAAC;CACF"}
package/dist/server.d.ts CHANGED
@@ -1,4 +1,35 @@
1
+ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
1
2
  import type { MCPicoConfig } from "./config.js";
3
+ import { type DiscoveredServer } from "./discoverer.js";
4
+ import { type ToolGroup } from "./grouper.js";
5
+ /** Helper: create a simple text content result */
6
+ export declare function textResult(text: string): CallToolResult;
7
+ /**
8
+ * Merge tool groups from multiple upstream servers.
9
+ *
10
+ * Groups with the same name are merged:
11
+ * - Tools are concatenated
12
+ * - serverName is joined with " + "
13
+ */
14
+ export declare function mergeGroups(allGroups: ToolGroup[]): Map<string, ToolGroup>;
15
+ /**
16
+ * Handle a tool call command for a given group.
17
+ *
18
+ * Dispatches: help → error → unknown subcommand → forward to upstream server.
19
+ *
20
+ * Exported for testing; allows injecting a mock forwardFn to avoid
21
+ * connecting to real upstream servers.
22
+ */
23
+ export declare function handleToolCall(command: string, group: ToolGroup, servers: DiscoveredServer[], helpText: string, forwardFn?: (server: DiscoveredServer, toolName: string, args: Record<string, unknown>) => Promise<CallToolResult>): Promise<CallToolResult>;
24
+ /**
25
+ * Build the description string for a tool group.
26
+ */
27
+ export declare function buildGroupDescription(group: ToolGroup): string;
28
+ /**
29
+ * Validate all server configs, returning an array of error messages.
30
+ * Returns empty array if all configs are valid.
31
+ */
32
+ export declare function validateAllServerConfigs(servers: MCPicoConfig["servers"]): string[];
2
33
  /**
3
34
  * Start the MCPico proxy server.
4
35
  *