cellium-mcp-client 1.0.3 → 1.1.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 +18 -27
- package/dist/cli.js +1 -1
- package/dist/client.d.ts +1 -3
- package/dist/client.js +30 -49
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# @cellium/mcp-client
|
|
2
2
|
|
|
3
|
-
A Model Context Protocol (MCP)
|
|
3
|
+
A Model Context Protocol (MCP) server that proxies requests to remote Cellium processor servers.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **
|
|
7
|
+
- **HTTP Proxy**: Proxies MCP requests to remote Cellium servers via HTTP
|
|
8
8
|
- **Authentication**: Token-based authentication with format `user:username:hash`
|
|
9
9
|
- **Robust Connection Management**: Automatic reconnection with configurable retry logic
|
|
10
10
|
- **MCP Protocol Compliance**: Uses official `@modelcontextprotocol/sdk`
|
|
@@ -38,7 +38,7 @@ cellium-mcp-client --token "user:your-username:your-hash"
|
|
|
38
38
|
# With custom endpoint and verbose logging
|
|
39
39
|
cellium-mcp-client \
|
|
40
40
|
--token "user:your-username:your-hash" \
|
|
41
|
-
--endpoint "https://mcp.cellium.dev/
|
|
41
|
+
--endpoint "https://mcp.cellium.dev/mcp" \
|
|
42
42
|
--verbose \
|
|
43
43
|
--retry-attempts 5 \
|
|
44
44
|
--retry-delay 2000
|
|
@@ -66,7 +66,7 @@ const logger = pino();
|
|
|
66
66
|
|
|
67
67
|
const client = new CelliumMCPClient({
|
|
68
68
|
token: 'user:your-username:your-hash',
|
|
69
|
-
endpoint: 'https://mcp.cellium.dev/
|
|
69
|
+
endpoint: 'https://mcp.cellium.dev/mcp',
|
|
70
70
|
logger,
|
|
71
71
|
retryAttempts: 3,
|
|
72
72
|
retryDelay: 1000
|
|
@@ -87,7 +87,7 @@ process.on('SIGINT', async () => {
|
|
|
87
87
|
| Option | Environment Variable | Description | Default |
|
|
88
88
|
|--------|---------------------|-------------|---------|
|
|
89
89
|
| `--token` | `CELLIUM_MCP_TOKEN` | Authentication token (required) | - |
|
|
90
|
-
| `--endpoint` | - | Server endpoint URL | `https://mcp.cellium.dev/
|
|
90
|
+
| `--endpoint` | - | Server endpoint URL | `https://mcp.cellium.dev/mcp` |
|
|
91
91
|
| `--verbose` | - | Enable verbose logging | `false` |
|
|
92
92
|
| `--retry-attempts` | - | Number of retry attempts | `3` |
|
|
93
93
|
| `--retry-delay` | - | Delay between retries (ms) | `1000` |
|
|
@@ -106,29 +106,20 @@ Example: `user:john-doe:a1b2c3d4e5f6...`
|
|
|
106
106
|
## Architecture
|
|
107
107
|
|
|
108
108
|
```
|
|
109
|
-
┌─────────────────┐
|
|
110
|
-
│ │◄──────────►│ │
|
|
111
|
-
│
|
|
112
|
-
│ (
|
|
113
|
-
│ │ │ │
|
|
114
|
-
└─────────────────┘ └─────────────────┘
|
|
115
|
-
▲ │
|
|
116
|
-
│ stdio │
|
|
117
|
-
│ MCP Protocol │
|
|
118
|
-
▼ │
|
|
119
|
-
┌─────────────────┐ │
|
|
120
|
-
│ │ │
|
|
121
|
-
│ Code Editor / │ │
|
|
122
|
-
│ AI Assistant │ │
|
|
123
|
-
│ (Cody, etc.) │ │
|
|
124
|
-
└─────────────────┘ │
|
|
109
|
+
┌─────────────────┐ stdio ┌─────────────────┐ HTTPS ┌─────────────────┐
|
|
110
|
+
│ │◄──────────►│ │◄──────────►│ │
|
|
111
|
+
│ Code Editor / │ MCP │ cellium-mcp- │ HTTP │ Cellium Server │
|
|
112
|
+
│ AI Assistant │ Protocol │ client (Proxy) │ Requests │ (Remote) │
|
|
113
|
+
│ (Cody, etc.) │ │ │ │ │
|
|
114
|
+
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
125
115
|
```
|
|
126
116
|
|
|
127
|
-
The client acts as
|
|
128
|
-
-
|
|
129
|
-
-
|
|
130
|
-
-
|
|
131
|
-
-
|
|
117
|
+
The client acts as an **MCP server proxy** between local MCP clients (like Cody) and the remote Cellium processor server, handling:
|
|
118
|
+
- **MCP Protocol**: Full stdio-based MCP server implementation
|
|
119
|
+
- **Authentication**: Token-based authentication with remote server
|
|
120
|
+
- **Request Proxying**: Converts MCP requests to HTTP requests to remote server
|
|
121
|
+
- **Error Handling**: Graceful handling of connection failures
|
|
122
|
+
- **Connection Management**: Automatic retry and reconnection logic
|
|
132
123
|
|
|
133
124
|
## Development
|
|
134
125
|
|
|
@@ -153,7 +144,7 @@ npm run dev
|
|
|
153
144
|
### Connection Issues
|
|
154
145
|
|
|
155
146
|
1. **Invalid token format**: Ensure your token follows the `user:username:hash` format
|
|
156
|
-
2. **Network connectivity**: Check if `https://mcp.cellium.dev/
|
|
147
|
+
2. **Network connectivity**: Check if `https://mcp.cellium.dev/mcp` is accessible
|
|
157
148
|
3. **Authentication failed**: Verify your token is valid and not expired
|
|
158
149
|
|
|
159
150
|
### Verbose Logging
|
package/dist/cli.js
CHANGED
|
@@ -11,7 +11,7 @@ const program = new commander_1.Command();
|
|
|
11
11
|
program
|
|
12
12
|
.name('cellium-mcp-client')
|
|
13
13
|
.description('MCP client for connecting to remote Cellium processor server')
|
|
14
|
-
.version('1.0
|
|
14
|
+
.version('1.1.0')
|
|
15
15
|
.option('-t, --token <token>', 'Authentication token (format: user:username:hash)')
|
|
16
16
|
.option('-e, --endpoint <url>', 'Server endpoint URL', 'http://localhost:3000/mcp')
|
|
17
17
|
.option('-v, --verbose', 'Enable verbose logging')
|
package/dist/client.d.ts
CHANGED
|
@@ -11,14 +11,12 @@ export declare class CelliumMCPClient {
|
|
|
11
11
|
private localServer;
|
|
12
12
|
private isConnected;
|
|
13
13
|
private reconnectTimer?;
|
|
14
|
-
private currentRetryCount;
|
|
15
14
|
constructor(config: CelliumMCPClientConfig);
|
|
16
15
|
private setupServer;
|
|
17
16
|
private makeHttpRequest;
|
|
18
17
|
connect(): Promise<void>;
|
|
18
|
+
private testConnectionInBackground;
|
|
19
19
|
serve(): Promise<void>;
|
|
20
20
|
private testConnection;
|
|
21
|
-
private handleConnectionError;
|
|
22
|
-
private reconnect;
|
|
23
21
|
disconnect(): Promise<void>;
|
|
24
22
|
}
|
package/dist/client.js
CHANGED
|
@@ -35,7 +35,6 @@ class CelliumMCPClient {
|
|
|
35
35
|
localServer;
|
|
36
36
|
isConnected = false;
|
|
37
37
|
reconnectTimer;
|
|
38
|
-
currentRetryCount = 0;
|
|
39
38
|
constructor(config) {
|
|
40
39
|
this.config = {
|
|
41
40
|
retryAttempts: 3,
|
|
@@ -65,47 +64,44 @@ class CelliumMCPClient {
|
|
|
65
64
|
// Override the underlying server's tool request handlers to proxy to remote
|
|
66
65
|
this.localServer.server.setRequestHandler(ToolsListSchema, async () => {
|
|
67
66
|
this.config.logger.debug('Proxying tools/list to remote server');
|
|
68
|
-
if (!this.isConnected) {
|
|
69
|
-
throw new Error('Not connected to remote server');
|
|
70
|
-
}
|
|
71
67
|
const result = await this.makeHttpRequest('tools/list', {});
|
|
72
68
|
return result;
|
|
73
69
|
});
|
|
74
70
|
this.localServer.server.setRequestHandler(ToolsCallSchema, async (request) => {
|
|
75
71
|
this.config.logger.debug({ toolName: request.params?.name }, 'Proxying tool call to remote server');
|
|
76
|
-
if (!this.isConnected) {
|
|
77
|
-
throw new Error('Not connected to remote server');
|
|
78
|
-
}
|
|
79
72
|
const result = await this.makeHttpRequest('tools/call', request.params);
|
|
80
73
|
return result;
|
|
81
74
|
});
|
|
82
75
|
// Handle resources as well
|
|
83
76
|
this.localServer.server.setRequestHandler(ResourcesListSchema, async () => {
|
|
84
77
|
this.config.logger.debug('Proxying resources/list to remote server');
|
|
85
|
-
if (!this.isConnected) {
|
|
86
|
-
throw new Error('Not connected to remote server');
|
|
87
|
-
}
|
|
88
78
|
const result = await this.makeHttpRequest('resources/list', {});
|
|
89
79
|
return result;
|
|
90
80
|
});
|
|
91
81
|
this.localServer.server.setRequestHandler(ResourcesReadSchema, async (request) => {
|
|
92
82
|
this.config.logger.debug({ uri: request.params?.uri }, 'Proxying resources/read to remote server');
|
|
93
|
-
if (!this.isConnected) {
|
|
94
|
-
throw new Error('Not connected to remote server');
|
|
95
|
-
}
|
|
96
83
|
const result = await this.makeHttpRequest('resources/read', request.params);
|
|
97
84
|
return result;
|
|
98
85
|
});
|
|
99
86
|
// Handle ping
|
|
100
87
|
this.localServer.server.setRequestHandler(PingSchema, async () => {
|
|
101
|
-
if (!this.isConnected) {
|
|
102
|
-
throw new Error('Not connected to remote server');
|
|
103
|
-
}
|
|
104
88
|
const result = await this.makeHttpRequest('ping', {});
|
|
105
89
|
return result;
|
|
106
90
|
});
|
|
107
91
|
}
|
|
108
92
|
async makeHttpRequest(method, params) {
|
|
93
|
+
// If not connected, try to connect first
|
|
94
|
+
if (!this.isConnected) {
|
|
95
|
+
try {
|
|
96
|
+
await this.testConnection();
|
|
97
|
+
this.isConnected = true;
|
|
98
|
+
this.config.logger.info('Connected to remote Cellium server');
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
this.config.logger.error({ error }, 'Failed to connect to remote server');
|
|
102
|
+
throw new Error('Cannot connect to remote Cellium server');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
109
105
|
const mcpEndpoint = this.config.endpoint.replace('/sse', '/mcp');
|
|
110
106
|
const requestBody = {
|
|
111
107
|
jsonrpc: '2.0',
|
|
@@ -134,31 +130,38 @@ class CelliumMCPClient {
|
|
|
134
130
|
}
|
|
135
131
|
catch (error) {
|
|
136
132
|
this.config.logger.error({ error, method }, 'HTTP request to remote server failed');
|
|
133
|
+
this.isConnected = false; // Mark as disconnected on error
|
|
137
134
|
throw error;
|
|
138
135
|
}
|
|
139
136
|
}
|
|
140
137
|
async connect() {
|
|
141
138
|
try {
|
|
142
|
-
this.config.logger.info({ endpoint: this.config.endpoint }, '
|
|
143
|
-
//
|
|
139
|
+
this.config.logger.info({ endpoint: this.config.endpoint }, 'Starting Cellium MCP Server');
|
|
140
|
+
// Set up the stdio transport for local MCP server immediately
|
|
141
|
+
const stdioTransport = new stdio_js_1.StdioServerTransport();
|
|
142
|
+
await this.localServer.connect(stdioTransport);
|
|
143
|
+
this.config.logger.info('MCP Server connected and ready');
|
|
144
|
+
// Test connection to remote server in background, but don't block startup
|
|
145
|
+
this.testConnectionInBackground();
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
this.config.logger.error({ error }, 'Failed to start MCP server');
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async testConnectionInBackground() {
|
|
153
|
+
try {
|
|
144
154
|
await this.testConnection();
|
|
145
155
|
this.isConnected = true;
|
|
146
|
-
this.
|
|
156
|
+
this.config.logger.info('Connected to remote Cellium server');
|
|
147
157
|
if (this.reconnectTimer) {
|
|
148
158
|
clearTimeout(this.reconnectTimer);
|
|
149
159
|
this.reconnectTimer = undefined;
|
|
150
160
|
}
|
|
151
|
-
this.config.logger.info('Connected to remote Cellium server');
|
|
152
|
-
// Set up the stdio transport for local MCP server
|
|
153
|
-
const stdioTransport = new stdio_js_1.StdioServerTransport();
|
|
154
|
-
await this.localServer.connect(stdioTransport);
|
|
155
|
-
this.config.logger.info('MCP Client connected and ready');
|
|
156
161
|
}
|
|
157
162
|
catch (error) {
|
|
158
|
-
this.config.logger.
|
|
163
|
+
this.config.logger.warn({ error }, 'Failed to connect to remote server, will retry on first request');
|
|
159
164
|
this.isConnected = false;
|
|
160
|
-
this.handleConnectionError();
|
|
161
|
-
throw error;
|
|
162
165
|
}
|
|
163
166
|
}
|
|
164
167
|
async serve() {
|
|
@@ -192,28 +195,6 @@ class CelliumMCPClient {
|
|
|
192
195
|
}
|
|
193
196
|
this.config.logger.debug('Connection test successful');
|
|
194
197
|
}
|
|
195
|
-
handleConnectionError() {
|
|
196
|
-
if (this.currentRetryCount < this.config.retryAttempts) {
|
|
197
|
-
this.currentRetryCount++;
|
|
198
|
-
this.config.logger.warn(`Connection failed, retrying in ${this.config.retryDelay}ms (attempt ${this.currentRetryCount}/${this.config.retryAttempts})`);
|
|
199
|
-
this.reconnectTimer = setTimeout(() => {
|
|
200
|
-
this.reconnect();
|
|
201
|
-
}, this.config.retryDelay);
|
|
202
|
-
}
|
|
203
|
-
else {
|
|
204
|
-
this.config.logger.error('Max retry attempts reached, giving up');
|
|
205
|
-
process.exit(1);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
async reconnect() {
|
|
209
|
-
try {
|
|
210
|
-
await this.connect();
|
|
211
|
-
}
|
|
212
|
-
catch (error) {
|
|
213
|
-
this.config.logger.error({ error }, 'Reconnection failed');
|
|
214
|
-
this.handleConnectionError();
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
198
|
async disconnect() {
|
|
218
199
|
this.config.logger.info('Disconnecting from Cellium MCP Server');
|
|
219
200
|
this.isConnected = false;
|