@1mcp/agent 0.13.1 → 0.14.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 +28 -20
- package/build/constants.d.ts +1 -1
- package/build/constants.js +1 -1
- package/build/core/client/clientManager.d.ts +55 -39
- package/build/core/client/clientManager.d.ts.map +1 -1
- package/build/core/client/clientManager.js +180 -171
- package/build/core/client/clientManager.js.map +1 -1
- package/build/core/server/agentConfig.d.ts +13 -0
- package/build/core/server/agentConfig.d.ts.map +1 -1
- package/build/core/server/agentConfig.js +16 -0
- package/build/core/server/agentConfig.js.map +1 -1
- package/build/core/server/serverManager.d.ts +11 -4
- package/build/core/server/serverManager.d.ts.map +1 -1
- package/build/core/server/serverManager.js +47 -23
- package/build/core/server/serverManager.js.map +1 -1
- package/build/core/types/server.d.ts +17 -0
- package/build/core/types/server.d.ts.map +1 -1
- package/build/core/types/server.js +14 -1
- package/build/core/types/server.js.map +1 -1
- package/build/handlers/notificationHandlers.d.ts +2 -2
- package/build/handlers/notificationHandlers.d.ts.map +1 -1
- package/build/handlers/notificationHandlers.js +15 -10
- package/build/handlers/notificationHandlers.js.map +1 -1
- package/build/handlers/requestHandlers.d.ts +1 -1
- package/build/handlers/requestHandlers.d.ts.map +1 -1
- package/build/handlers/requestHandlers.js +68 -67
- package/build/handlers/requestHandlers.js.map +1 -1
- package/build/index.js +7 -0
- package/build/index.js.map +1 -1
- package/build/logger/mcpLoggingEnhancer.d.ts.map +1 -1
- package/build/logger/mcpLoggingEnhancer.js +18 -1
- package/build/logger/mcpLoggingEnhancer.js.map +1 -1
- package/build/server.d.ts.map +1 -1
- package/build/server.js +3 -2
- package/build/server.js.map +1 -1
- package/build/services/configReloadService.d.ts.map +1 -1
- package/build/services/configReloadService.js +3 -2
- package/build/services/configReloadService.js.map +1 -1
- package/build/transport/http/middlewares/httpRequestLogger.d.ts +16 -0
- package/build/transport/http/middlewares/httpRequestLogger.d.ts.map +1 -0
- package/build/transport/http/middlewares/httpRequestLogger.js +41 -0
- package/build/transport/http/middlewares/httpRequestLogger.js.map +1 -0
- package/build/transport/http/middlewares/scopeAuthMiddleware.d.ts.map +1 -1
- package/build/transport/http/middlewares/scopeAuthMiddleware.js +1 -0
- package/build/transport/http/middlewares/scopeAuthMiddleware.js.map +1 -1
- package/build/transport/http/routes/oauthRoutes.d.ts.map +1 -1
- package/build/transport/http/routes/oauthRoutes.js +3 -3
- package/build/transport/http/routes/oauthRoutes.js.map +1 -1
- package/build/transport/http/routes/sseRoutes.d.ts +1 -2
- package/build/transport/http/routes/sseRoutes.d.ts.map +1 -1
- package/build/transport/http/routes/sseRoutes.js +12 -7
- package/build/transport/http/routes/sseRoutes.js.map +1 -1
- package/build/transport/http/routes/streamableHttpRoutes.d.ts +1 -2
- package/build/transport/http/routes/streamableHttpRoutes.d.ts.map +1 -1
- package/build/transport/http/routes/streamableHttpRoutes.js +12 -12
- package/build/transport/http/routes/streamableHttpRoutes.js.map +1 -1
- package/build/transport/http/server.d.ts +1 -0
- package/build/transport/http/server.d.ts.map +1 -1
- package/build/transport/http/server.js +11 -5
- package/build/transport/http/server.js.map +1 -1
- package/build/transport/transportFactory.js +2 -2
- package/build/transport/transportFactory.js.map +1 -1
- package/build/utils/operationExecution.d.ts +10 -0
- package/build/utils/operationExecution.d.ts.map +1 -0
- package/build/utils/operationExecution.js +36 -0
- package/build/utils/operationExecution.js.map +1 -0
- package/build/utils/pagination.d.ts.map +1 -1
- package/build/utils/pagination.js +22 -13
- package/build/utils/pagination.js.map +1 -1
- package/package.json +1 -1
- package/build/core/client/clientFactory.d.ts +0 -25
- package/build/core/client/clientFactory.d.ts.map +0 -1
- package/build/core/client/clientFactory.js +0 -12
- package/build/core/client/clientFactory.js.map +0 -1
package/README.md
CHANGED
|
@@ -58,31 +58,33 @@ npx -y @1mcp/agent --config ~/Library/Application\ Support/Claude/claude_desktop
|
|
|
58
58
|
# Use stdio transport instead of SSE
|
|
59
59
|
npx -y @1mcp/agent --transport stdio
|
|
60
60
|
|
|
61
|
+
# Use external URL for reverse proxy setup (nginx, etc.)
|
|
62
|
+
npx -y @1mcp/agent --external-url https://example.com
|
|
63
|
+
|
|
61
64
|
# Show all available options
|
|
62
65
|
npx -y @1mcp/agent --help
|
|
63
66
|
```
|
|
64
67
|
|
|
65
68
|
Available options:
|
|
66
69
|
|
|
67
|
-
| Option (CLI) | Environment Variable | Description
|
|
68
|
-
| :--------------------------- | :--------------------------------- |
|
|
69
|
-
| `--transport`, `-t` | `ONE_MCP_TRANSPORT` | Choose transport type ("stdio", "http", or "sse")
|
|
70
|
-
| `--config`, `-c` | `ONE_MCP_CONFIG` | Use a specific config file
|
|
71
|
-
| `--port`, `-P` | `ONE_MCP_PORT` | Change HTTP port
|
|
72
|
-
| `--host`, `-H` | `ONE_MCP_HOST` | Change HTTP host
|
|
73
|
-
| `--
|
|
74
|
-
| `--
|
|
75
|
-
| `--
|
|
76
|
-
| `--
|
|
77
|
-
| `--enable-
|
|
78
|
-
| `--enable-
|
|
79
|
-
| `--
|
|
80
|
-
| `--session-
|
|
81
|
-
| `--
|
|
82
|
-
| `--rate-limit-
|
|
83
|
-
| `--
|
|
84
|
-
|
|
85
|
-
> 💡 **Note:** Deprecated options are provided for backward compatibility. Prefer using the non-deprecated options for new configurations.
|
|
70
|
+
| Option (CLI) | Environment Variable | Description | Default |
|
|
71
|
+
| :--------------------------- | :--------------------------------- | :--------------------------------------------------------------------------- | :-------: |
|
|
72
|
+
| `--transport`, `-t` | `ONE_MCP_TRANSPORT` | Choose transport type ("stdio", "http", or "sse") | "http" |
|
|
73
|
+
| `--config`, `-c` | `ONE_MCP_CONFIG` | Use a specific config file | |
|
|
74
|
+
| `--port`, `-P` | `ONE_MCP_PORT` | Change HTTP port | 3050 |
|
|
75
|
+
| `--host`, `-H` | `ONE_MCP_HOST` | Change HTTP host | localhost |
|
|
76
|
+
| `--external-url`, `-u` | `ONE_MCP_EXTERNAL_URL` | External URL for OAuth callbacks and public URLs (e.g., https://example.com) | |
|
|
77
|
+
| `--tags`, `-g` | `ONE_MCP_TAGS` | Filter servers by tags | |
|
|
78
|
+
| `--pagination`, `-p` | `ONE_MCP_PAGINATION` | Enable pagination for client/server lists (boolean) | false |
|
|
79
|
+
| `--auth` | `ONE_MCP_AUTH` | Enable authentication (OAuth 2.1) - **Deprecated** | false |
|
|
80
|
+
| `--enable-auth` | `ONE_MCP_ENABLE_AUTH` | Enable authentication (OAuth 2.1) | false |
|
|
81
|
+
| `--enable-scope-validation` | `ONE_MCP_ENABLE_SCOPE_VALIDATION` | Enable tag-based scope validation (boolean) | true |
|
|
82
|
+
| `--enable-enhanced-security` | `ONE_MCP_ENABLE_ENHANCED_SECURITY` | Enable enhanced security middleware (boolean) | false |
|
|
83
|
+
| `--session-ttl` | `ONE_MCP_SESSION_TTL` | Session expiry time in minutes (number) | 1440 |
|
|
84
|
+
| `--session-storage-path` | `ONE_MCP_SESSION_STORAGE_PATH` | Custom session storage directory path (string) | |
|
|
85
|
+
| `--rate-limit-window` | `ONE_MCP_RATE_LIMIT_WINDOW` | OAuth rate limit window in minutes (number) | 15 |
|
|
86
|
+
| `--rate-limit-max` | `ONE_MCP_RATE_LIMIT_MAX` | Maximum requests per OAuth rate limit window (number) | 100 |
|
|
87
|
+
| `--help`, `-h` | | Show help | |
|
|
86
88
|
|
|
87
89
|
## Docker
|
|
88
90
|
|
|
@@ -108,13 +110,19 @@ Available image tags:
|
|
|
108
110
|
- `vX.Y.Z`: Specific version (e.g. `v1.0.0`)
|
|
109
111
|
- `sha-<commit>`: Specific commit
|
|
110
112
|
|
|
111
|
-
|
|
113
|
+
Examples:
|
|
112
114
|
|
|
113
115
|
```bash
|
|
116
|
+
# Custom port and tags
|
|
114
117
|
docker run -p 3051:3051 \
|
|
115
118
|
-e ONE_MCP_PORT=3051 \
|
|
116
119
|
-e ONE_MCP_TAGS=network,filesystem \
|
|
117
120
|
ghcr.io/1mcp-app/agent
|
|
121
|
+
|
|
122
|
+
# With external URL for reverse proxy
|
|
123
|
+
docker run -p 3050:3050 \
|
|
124
|
+
-e ONE_MCP_EXTERNAL_URL=https://mcp.example.com \
|
|
125
|
+
ghcr.io/1mcp-app/agent
|
|
118
126
|
```
|
|
119
127
|
|
|
120
128
|
### Understanding Tags
|
package/build/constants.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export declare const MESSAGES_ENDPOINT = "/messages";
|
|
|
9
9
|
export declare const STREAMABLE_HTTP_ENDPOINT = "/mcp";
|
|
10
10
|
export declare const MCP_CONFIG_FILE = "mcp.json";
|
|
11
11
|
export declare const MCP_SERVER_NAME = "1mcp";
|
|
12
|
-
export declare const MCP_SERVER_VERSION = "0.
|
|
12
|
+
export declare const MCP_SERVER_VERSION = "0.14.0";
|
|
13
13
|
export declare const MCP_URI_SEPARATOR = "_1mcp_";
|
|
14
14
|
export declare const CONFIG_DIR_NAME = "1mcp";
|
|
15
15
|
export declare const DEFAULT_CONFIG: {
|
package/build/constants.js
CHANGED
|
@@ -11,7 +11,7 @@ export const STREAMABLE_HTTP_ENDPOINT = '/mcp';
|
|
|
11
11
|
// MCP constants
|
|
12
12
|
export const MCP_CONFIG_FILE = 'mcp.json';
|
|
13
13
|
export const MCP_SERVER_NAME = '1mcp';
|
|
14
|
-
export const MCP_SERVER_VERSION = '0.
|
|
14
|
+
export const MCP_SERVER_VERSION = '0.14.0';
|
|
15
15
|
export const MCP_URI_SEPARATOR = '_1mcp_';
|
|
16
16
|
// Global config paths
|
|
17
17
|
export const CONFIG_DIR_NAME = '1mcp';
|
|
@@ -1,43 +1,59 @@
|
|
|
1
1
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
-
import { OutboundConnection, OutboundConnections, OperationOptions,
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
2
|
+
import { OutboundConnection, OutboundConnections, OperationOptions, ServerCapability, AuthProviderTransport } from '../types/index.js';
|
|
3
|
+
export declare class ClientManager {
|
|
4
|
+
private static instance;
|
|
5
|
+
private outboundConns;
|
|
6
|
+
private transports;
|
|
7
|
+
private connectionSemaphore;
|
|
8
|
+
private constructor();
|
|
9
|
+
static getOrCreateInstance(): ClientManager;
|
|
10
|
+
static get current(): ClientManager;
|
|
11
|
+
static resetInstance(): void;
|
|
12
|
+
/**
|
|
13
|
+
* Creates a new MCP client instance
|
|
14
|
+
* @returns A new Client instance
|
|
15
|
+
*/
|
|
16
|
+
private createClient;
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new MCP client instance for external use (e.g., OAuth testing)
|
|
19
|
+
* @returns A new Client instance
|
|
20
|
+
*/
|
|
21
|
+
createClientInstance(): Client;
|
|
22
|
+
/**
|
|
23
|
+
* Creates client instances for all transports with retry logic
|
|
24
|
+
* @param transports Record of transport instances
|
|
25
|
+
* @returns Record of client instances
|
|
26
|
+
*/
|
|
27
|
+
createClients(transports: Record<string, AuthProviderTransport>): Promise<OutboundConnections>;
|
|
28
|
+
/**
|
|
29
|
+
* Connects a client to its transport with retry logic and OAuth support
|
|
30
|
+
* @param client The client to connect
|
|
31
|
+
* @param transport The transport to connect to
|
|
32
|
+
* @param name The name of the client for logging
|
|
33
|
+
* @returns The connected client (may be a new instance after retries)
|
|
34
|
+
*/
|
|
35
|
+
private connectWithRetry;
|
|
36
|
+
/**
|
|
37
|
+
* Gets a client by name with error handling
|
|
38
|
+
* @param clientName The name of the client to get
|
|
39
|
+
* @returns The client instance
|
|
40
|
+
* @throws ClientNotFoundError if the client is not found
|
|
41
|
+
*/
|
|
42
|
+
getClient(clientName: string): OutboundConnection;
|
|
43
|
+
/**
|
|
44
|
+
* Gets all outbound connections
|
|
45
|
+
* @returns Map of all outbound connections
|
|
46
|
+
*/
|
|
47
|
+
getClients(): OutboundConnections;
|
|
48
|
+
/**
|
|
49
|
+
* Executes a client operation with error handling and retry logic
|
|
50
|
+
* @param clientName The name of the client to use
|
|
51
|
+
* @param operation The operation to execute
|
|
52
|
+
* @param options Operation options including timeout and retry settings
|
|
53
|
+
* @param requiredCapability The capability required for this operation
|
|
54
|
+
*/
|
|
55
|
+
executeClientOperation<T>(clientName: string, operation: (clientInfo: OutboundConnection) => Promise<T>, options?: OperationOptions, requiredCapability?: ServerCapability): Promise<T>;
|
|
56
|
+
}
|
|
41
57
|
/**
|
|
42
58
|
* Custom error class for OAuth authorization required
|
|
43
59
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clientManager.d.ts","sourceRoot":"","sources":["../../../src/core/client/clientManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;
|
|
1
|
+
{"version":3,"file":"clientManager.d.ts","sourceRoot":"","sources":["../../../src/core/client/clientManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAMnE,OAAO,EAEL,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACtB,MAAM,mBAAmB,CAAC;AAI3B,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAgB;IACvC,OAAO,CAAC,aAAa,CAAkC;IACvD,OAAO,CAAC,UAAU,CAA6C;IAC/D,OAAO,CAAC,mBAAmB,CAAyC;IAEpE,OAAO;WAEO,mBAAmB,IAAI,aAAa;IAOlD,WAAkB,OAAO,IAAI,aAAa,CAEzC;WAGa,aAAa,IAAI,IAAI;IAInC;;;OAGG;IACH,OAAO,CAAC,YAAY;IAYpB;;;OAGG;IACI,oBAAoB,IAAI,MAAM;IAIrC;;;;OAIG;IACU,aAAa,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAyE3G;;;;;;OAMG;YACW,gBAAgB;IA+C9B;;;;;OAKG;IACI,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB;IAQxD;;;OAGG;IACI,UAAU,IAAI,mBAAmB;IAIxC;;;;;;OAMG;IACU,sBAAsB,CAAC,CAAC,EACnC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,CAAC,UAAU,EAAE,kBAAkB,KAAK,OAAO,CAAC,CAAC,CAAC,EACzD,OAAO,GAAE,gBAAqB,EAC9B,kBAAkB,CAAC,EAAE,gBAAgB,GACpC,OAAO,CAAC,CAAC,CAAC;CAad;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAElC,UAAU,EAAE,MAAM;IAClB,MAAM,EAAE,MAAM;gBADd,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM;CAKxB"}
|
|
@@ -1,197 +1,206 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
2
|
import { UnauthorizedError } from '@modelcontextprotocol/sdk/client/auth.js';
|
|
3
|
-
import createClient from './clientFactory.js';
|
|
4
3
|
import logger from '../../logger/logger.js';
|
|
5
|
-
import { CONNECTION_RETRY, MCP_SERVER_NAME } from '../../constants.js';
|
|
6
|
-
import { ClientConnectionError, ClientNotFoundError,
|
|
4
|
+
import { CONNECTION_RETRY, MCP_SERVER_NAME, MCP_SERVER_VERSION, MCP_CLIENT_CAPABILITIES } from '../../constants.js';
|
|
5
|
+
import { ClientConnectionError, ClientNotFoundError, CapabilityError } from '../../utils/errorTypes.js';
|
|
7
6
|
import { ClientStatus, } from '../types/index.js';
|
|
8
7
|
import { AgentConfigManager } from '../server/agentConfig.js';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const client = await createClient();
|
|
20
|
-
// Connect with retry logic
|
|
21
|
-
const connectedClient = await connectWithRetry(client, transport, name);
|
|
22
|
-
clients.set(name, {
|
|
23
|
-
name,
|
|
24
|
-
transport,
|
|
25
|
-
client: connectedClient,
|
|
26
|
-
status: ClientStatus.Connected,
|
|
27
|
-
lastConnected: new Date(),
|
|
28
|
-
});
|
|
29
|
-
logger.info(`Client created for ${name}`);
|
|
30
|
-
connectedClient.onclose = () => {
|
|
31
|
-
const clientInfo = clients.get(name);
|
|
32
|
-
if (clientInfo) {
|
|
33
|
-
clientInfo.status = ClientStatus.Disconnected;
|
|
34
|
-
}
|
|
35
|
-
logger.info(`Client ${name} disconnected`);
|
|
36
|
-
};
|
|
8
|
+
import { executeOperation } from '../../utils/operationExecution.js';
|
|
9
|
+
export class ClientManager {
|
|
10
|
+
static instance;
|
|
11
|
+
outboundConns = new Map();
|
|
12
|
+
transports = {};
|
|
13
|
+
connectionSemaphore = new Map();
|
|
14
|
+
constructor() { }
|
|
15
|
+
static getOrCreateInstance() {
|
|
16
|
+
if (!ClientManager.instance) {
|
|
17
|
+
ClientManager.instance = new ClientManager();
|
|
37
18
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
19
|
+
return ClientManager.instance;
|
|
20
|
+
}
|
|
21
|
+
static get current() {
|
|
22
|
+
return ClientManager.instance;
|
|
23
|
+
}
|
|
24
|
+
// Test utility method to reset singleton state
|
|
25
|
+
static resetInstance() {
|
|
26
|
+
ClientManager.instance = undefined;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Creates a new MCP client instance
|
|
30
|
+
* @returns A new Client instance
|
|
31
|
+
*/
|
|
32
|
+
createClient() {
|
|
33
|
+
return new Client({
|
|
34
|
+
name: MCP_SERVER_NAME,
|
|
35
|
+
version: MCP_SERVER_VERSION,
|
|
36
|
+
}, {
|
|
37
|
+
capabilities: MCP_CLIENT_CAPABILITIES,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Creates a new MCP client instance for external use (e.g., OAuth testing)
|
|
42
|
+
* @returns A new Client instance
|
|
43
|
+
*/
|
|
44
|
+
createClientInstance() {
|
|
45
|
+
return this.createClient();
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Creates client instances for all transports with retry logic
|
|
49
|
+
* @param transports Record of transport instances
|
|
50
|
+
* @returns Record of client instances
|
|
51
|
+
*/
|
|
52
|
+
async createClients(transports) {
|
|
53
|
+
this.transports = transports;
|
|
54
|
+
this.outboundConns.clear();
|
|
55
|
+
for (const [name, transport] of Object.entries(transports)) {
|
|
56
|
+
logger.info(`Creating client for ${name}`);
|
|
57
|
+
try {
|
|
58
|
+
const client = this.createClient();
|
|
59
|
+
// Connect with retry logic
|
|
60
|
+
const connectedClient = await this.connectWithRetry(client, transport, name);
|
|
61
|
+
this.outboundConns.set(name, {
|
|
55
62
|
name,
|
|
56
63
|
transport,
|
|
57
|
-
client:
|
|
58
|
-
status: ClientStatus.
|
|
59
|
-
|
|
60
|
-
oauthStartTime: new Date(),
|
|
64
|
+
client: connectedClient,
|
|
65
|
+
status: ClientStatus.Connected,
|
|
66
|
+
lastConnected: new Date(),
|
|
61
67
|
});
|
|
68
|
+
logger.info(`Client created for ${name}`);
|
|
69
|
+
connectedClient.onclose = () => {
|
|
70
|
+
const clientInfo = this.outboundConns.get(name);
|
|
71
|
+
if (clientInfo) {
|
|
72
|
+
clientInfo.status = ClientStatus.Disconnected;
|
|
73
|
+
}
|
|
74
|
+
logger.info(`Client ${name} disconnected`);
|
|
75
|
+
};
|
|
76
|
+
connectedClient.onerror = (error) => {
|
|
77
|
+
logger.error(`Client ${name} error: ${error}`);
|
|
78
|
+
};
|
|
62
79
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
name
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (error instanceof OAuthRequiredError) {
|
|
82
|
+
// Handle OAuth required - set client to AwaitingOAuth status
|
|
83
|
+
logger.info(`OAuth authorization required for ${name}`);
|
|
84
|
+
// Try to get authorization URL from OAuth provider
|
|
85
|
+
let authorizationUrl;
|
|
86
|
+
try {
|
|
87
|
+
// Extract OAuth provider from transport if available
|
|
88
|
+
const oauthProvider = transport.oauthProvider;
|
|
89
|
+
if (oauthProvider && typeof oauthProvider.getAuthorizationUrl === 'function') {
|
|
90
|
+
authorizationUrl = oauthProvider.getAuthorizationUrl();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (urlError) {
|
|
94
|
+
logger.warn(`Could not extract authorization URL for ${name}:`, urlError);
|
|
95
|
+
}
|
|
96
|
+
this.outboundConns.set(name, {
|
|
97
|
+
name,
|
|
98
|
+
transport,
|
|
99
|
+
client: error.client,
|
|
100
|
+
status: ClientStatus.AwaitingOAuth,
|
|
101
|
+
authorizationUrl,
|
|
102
|
+
oauthStartTime: new Date(),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
logger.error(`Failed to create client for ${name}: ${error}`);
|
|
107
|
+
this.outboundConns.set(name, {
|
|
108
|
+
name,
|
|
109
|
+
transport,
|
|
110
|
+
client: this.createClient(),
|
|
111
|
+
status: ClientStatus.Error,
|
|
112
|
+
lastError: error instanceof Error ? error : new Error(String(error)),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
72
115
|
}
|
|
73
116
|
}
|
|
117
|
+
return this.outboundConns;
|
|
74
118
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
logger.info(`Successfully connected to ${name} with server ${sv?.name} version ${sv?.version}`);
|
|
95
|
-
return currentClient;
|
|
96
|
-
}
|
|
97
|
-
catch (error) {
|
|
98
|
-
// Handle OAuth authorization flow (managed by SDK)
|
|
99
|
-
if (error instanceof UnauthorizedError) {
|
|
100
|
-
const serverConfig = AgentConfigManager.getInstance().getConfig();
|
|
101
|
-
logger.info(`OAuth authorization required for ${name}. Visit http://localhost:${serverConfig.port}/oauth to authorize`);
|
|
102
|
-
// Throw special error that includes OAuth info
|
|
103
|
-
throw new OAuthRequiredError(name, currentClient);
|
|
119
|
+
/**
|
|
120
|
+
* Connects a client to its transport with retry logic and OAuth support
|
|
121
|
+
* @param client The client to connect
|
|
122
|
+
* @param transport The transport to connect to
|
|
123
|
+
* @param name The name of the client for logging
|
|
124
|
+
* @returns The connected client (may be a new instance after retries)
|
|
125
|
+
*/
|
|
126
|
+
async connectWithRetry(client, transport, name) {
|
|
127
|
+
let retryDelay = CONNECTION_RETRY.INITIAL_DELAY_MS;
|
|
128
|
+
let currentClient = client;
|
|
129
|
+
for (let i = 0; i < CONNECTION_RETRY.MAX_ATTEMPTS; i++) {
|
|
130
|
+
try {
|
|
131
|
+
await currentClient.connect(transport);
|
|
132
|
+
const sv = await currentClient.getServerVersion();
|
|
133
|
+
if (sv?.name === MCP_SERVER_NAME) {
|
|
134
|
+
throw new ClientConnectionError(name, new Error('Aborted to prevent circular dependency'));
|
|
135
|
+
}
|
|
136
|
+
logger.info(`Successfully connected to ${name} with server ${sv?.name} version ${sv?.version}`);
|
|
137
|
+
return currentClient;
|
|
104
138
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
retryDelay *= 2; // Exponential backoff
|
|
113
|
-
// Create a new client for retry to avoid "already started" errors
|
|
114
|
-
currentClient = await createClient();
|
|
139
|
+
catch (error) {
|
|
140
|
+
// Handle OAuth authorization flow (managed by SDK)
|
|
141
|
+
if (error instanceof UnauthorizedError) {
|
|
142
|
+
const configManager = AgentConfigManager.getInstance();
|
|
143
|
+
logger.info(`OAuth authorization required for ${name}. Visit ${configManager.getUrl()}/oauth to authorize`);
|
|
144
|
+
// Throw special error that includes OAuth info
|
|
145
|
+
throw new OAuthRequiredError(name, currentClient);
|
|
115
146
|
}
|
|
147
|
+
// Handle other connection errors
|
|
116
148
|
else {
|
|
117
|
-
|
|
149
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
150
|
+
logger.error(`Failed to connect to ${name}: ${errorMessage}`);
|
|
151
|
+
if (i < CONNECTION_RETRY.MAX_ATTEMPTS - 1) {
|
|
152
|
+
logger.info(`Retrying in ${retryDelay}ms...`);
|
|
153
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
154
|
+
retryDelay *= 2; // Exponential backoff
|
|
155
|
+
// Create a new client for retry to avoid "already started" errors
|
|
156
|
+
currentClient = this.createClient();
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
throw new ClientConnectionError(name, error instanceof Error ? error : new Error(String(error)));
|
|
160
|
+
}
|
|
118
161
|
}
|
|
119
162
|
}
|
|
120
163
|
}
|
|
164
|
+
// This should never be reached due to the throw in the else block above
|
|
165
|
+
throw new ClientConnectionError(name, new Error('Max retries exceeded'));
|
|
121
166
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
*/
|
|
133
|
-
export function getClient(clients, clientName) {
|
|
134
|
-
const client = clients.get(clientName);
|
|
135
|
-
if (!client) {
|
|
136
|
-
throw new ClientNotFoundError(clientName);
|
|
137
|
-
}
|
|
138
|
-
return client;
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Executes an operation with error handling and retry logic
|
|
142
|
-
* @param operation The operation to execute
|
|
143
|
-
* @param context The execution context (client info or server info)
|
|
144
|
-
* @param options Operation options including timeout and retry settings
|
|
145
|
-
* @returns The result of the operation
|
|
146
|
-
*/
|
|
147
|
-
export async function executeOperation(operation, contextName, options = {}) {
|
|
148
|
-
const { retryCount = 0, retryDelay = 1000 } = options;
|
|
149
|
-
let lastError;
|
|
150
|
-
for (let i = 0; i <= retryCount; i++) {
|
|
151
|
-
try {
|
|
152
|
-
return await operation();
|
|
153
|
-
}
|
|
154
|
-
catch (error) {
|
|
155
|
-
lastError = error instanceof Error ? error : new Error(String(error));
|
|
156
|
-
if (i < retryCount) {
|
|
157
|
-
logger.info(`Retrying operation ${operation.name} on ${contextName} after ${retryDelay}ms`);
|
|
158
|
-
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
159
|
-
}
|
|
167
|
+
/**
|
|
168
|
+
* Gets a client by name with error handling
|
|
169
|
+
* @param clientName The name of the client to get
|
|
170
|
+
* @returns The client instance
|
|
171
|
+
* @throws ClientNotFoundError if the client is not found
|
|
172
|
+
*/
|
|
173
|
+
getClient(clientName) {
|
|
174
|
+
const client = this.outboundConns.get(clientName);
|
|
175
|
+
if (!client) {
|
|
176
|
+
throw new ClientNotFoundError(clientName);
|
|
160
177
|
}
|
|
178
|
+
return client;
|
|
161
179
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
180
|
+
/**
|
|
181
|
+
* Gets all outbound connections
|
|
182
|
+
* @returns Map of all outbound connections
|
|
183
|
+
*/
|
|
184
|
+
getClients() {
|
|
185
|
+
return this.outboundConns;
|
|
166
186
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
throw new CapabilityError(clientName, String(requiredCapability));
|
|
187
|
+
/**
|
|
188
|
+
* Executes a client operation with error handling and retry logic
|
|
189
|
+
* @param clientName The name of the client to use
|
|
190
|
+
* @param operation The operation to execute
|
|
191
|
+
* @param options Operation options including timeout and retry settings
|
|
192
|
+
* @param requiredCapability The capability required for this operation
|
|
193
|
+
*/
|
|
194
|
+
async executeClientOperation(clientName, operation, options = {}, requiredCapability) {
|
|
195
|
+
const outboundConn = this.getClient(clientName);
|
|
196
|
+
if (outboundConn.status !== ClientStatus.Connected || !outboundConn.client.transport) {
|
|
197
|
+
throw new ClientConnectionError(clientName, new Error('Client not connected'));
|
|
198
|
+
}
|
|
199
|
+
if (requiredCapability && !outboundConn.capabilities?.[requiredCapability]) {
|
|
200
|
+
throw new CapabilityError(clientName, String(requiredCapability));
|
|
201
|
+
}
|
|
202
|
+
return executeOperation(() => operation(outboundConn), `client ${clientName}`, options);
|
|
184
203
|
}
|
|
185
|
-
return executeOperation(() => operation(clientInfo), `client ${clientName}`, options);
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Executes a server operation with error handling and retry logic
|
|
189
|
-
* @param server The server to execute the operation on
|
|
190
|
-
* @param operation The operation to execute
|
|
191
|
-
* @param options Operation options including timeout and retry settings
|
|
192
|
-
*/
|
|
193
|
-
export async function executeServerOperation(server, operation, options = {}) {
|
|
194
|
-
return executeOperation(() => operation(server), 'server', options);
|
|
195
204
|
}
|
|
196
205
|
/**
|
|
197
206
|
* Custom error class for OAuth authorization required
|