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 +123 -2
- package/dist/auth-types.d.ts +54 -0
- package/dist/auth-types.js +9 -0
- package/dist/auth-types.js.map +1 -0
- package/dist/auth.d.ts +38 -0
- package/dist/auth.js +122 -0
- package/dist/auth.js.map +1 -0
- package/dist/config.d.ts +16 -0
- package/dist/config.js.map +1 -1
- package/dist/discoverer.d.ts +2 -1
- package/dist/discoverer.js +15 -3
- package/dist/discoverer.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/oauth-provider.d.ts +19 -0
- package/dist/oauth-provider.js +79 -0
- package/dist/oauth-provider.js.map +1 -0
- package/dist/server.d.ts +31 -0
- package/dist/server.js +132 -57
- package/dist/server.js.map +1 -1
- package/dist/token-store.d.ts +11 -0
- package/dist/token-store.js +94 -0
- package/dist/token-store.js.map +1 -0
- package/package.json +2 -1
- package/src/auth-types.ts +71 -0
- package/src/auth.test.ts +218 -0
- package/src/auth.ts +159 -0
- package/src/config.ts +14 -0
- package/src/discoverer.ts +25 -5
- package/src/help.test.ts +40 -0
- package/src/index.ts +1 -1
- package/src/oauth-provider.ts +110 -0
- package/src/server.test.ts +324 -0
- package/src/server.ts +165 -75
- package/src/token-store.test.ts +154 -0
- package/src/token-store.ts +109 -0
- package/vitest.config.ts +7 -4
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 (
|
|
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 @@
|
|
|
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
|
package/dist/auth.js.map
ADDED
|
@@ -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.
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"
|
|
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"}
|
package/dist/discoverer.d.ts
CHANGED
|
@@ -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
|
*/
|
package/dist/discoverer.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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
|
package/dist/discoverer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discoverer.js","sourceRoot":"","sources":["../src/discoverer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;
|
|
1
|
+
{"version":3,"file":"discoverer.js","sourceRoot":"","sources":["../src/discoverer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAGnG,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,+BAA+B,EAAE,MAAM,qBAAqB,CAAC;AAyCtE;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAAY,EACZ,eAAgC,EAChC,mBAA2B,MAAM,EACjC,IAA2B;IAE3B,IAAI,SAAS,CAAC;IAEd,IAAI,eAAe,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QACrC,SAAS,GAAG,IAAI,oBAAoB,CAAC;YACnC,OAAO,EAAE,eAAe,CAAC,OAAO;YAChC,IAAI,EAAE,eAAe,CAAC,IAAI;YAC1B,GAAG,EAAE,eAAe,CAAC,GAAG;YACxB,GAAG,EAAE,eAAe,CAAC,GAAG;SACzB,CAAC,CAAC;IACL,CAAC;SAAM,IAAI,eAAe,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC1C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QACzC,MAAM,IAAI,GAGN,EAAE,CAAC;QAEP,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBAC1B,IAAI,CAAC,YAAY,GAAG,+BAA+B,CACjD,IAAI,CAAC,SAAS,EACd,IAAI,CAAC,aAAa,EAClB,eAAe,CAAC,GAAG,CACpB,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,WAAW,GAAG,EAAE,OAAO,EAAE,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5D,CAAC;QACH,CAAC;QAED,SAAS,GAAG,IAAI,6BAA6B,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC3D,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,KAAK,CAAC,+BAAgC,eAAmC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9F,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,EAAE,IAAI,EAAE,UAAU,IAAI,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,EAC5C,EAAE,YAAY,EAAE,EAAE,EAAE,CACrB,CAAC;IAEF,uBAAuB;IACvB,MAAM,WAAW,CACf,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EACzB,gBAAgB,EAChB,kBAAkB,IAAI,qBAAqB,gBAAgB,IAAI,CAChE,CAAC;IAEF,iBAAiB;IACjB,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;IAC7C,MAAM,KAAK,GAAmB,CAAC,WAAW,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAClE,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,WAAW,EAAG,CAAC,CAAC,WAAuC,IAAI,EAAE;KAC9D,CAAC,CAAC,CAAC;IAEJ,oCAAoC;IACpC,IAAI,SAAS,GAAuB,EAAE,CAAC;IACvC,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC;QAC/C,SAAS,GAAG,CAAC,SAAS,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAClD,GAAG,EAAE,CAAC,CAAC,GAAG;YACV,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,QAAQ,EAAE,CAAC,CAAC,QAAQ;SACrB,CAAC,CAAC,CAAC;IACN,CAAC;IAAC,MAAM,CAAC;QACP,gDAAgD;IAClD,CAAC;IAED,kCAAkC;IAClC,IAAI,OAAO,GAAqB,EAAE,CAAC;IACnC,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC;QAChD,OAAO,GAAG,CAAC,YAAY,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACjD,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACzC,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,QAAQ,EAAE,CAAC,CAAC,QAAQ;aACrB,CAAC,CAAC;SACJ,CAAC,CAAC,CAAC;IACN,CAAC;IAAC,MAAM,CAAC;QACP,8CAA8C;IAChD,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;AACrD,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAAwB;IAC7D,MAAM,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;AAC9B,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,WAAW,CACxB,OAAmB,EACnB,SAAiB,EACjB,YAAoB;IAEpB,OAAO,OAAO,CAAC,IAAI,CAAC;QAClB,OAAO;QACP,IAAI,OAAO,CAAI,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAC3B,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC,EAAE,SAAS,CAAC,CAC7D;KACF,CAAC,CAAC;AACL,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -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
|
*
|