cellium-mcp-client 1.1.7 → 2.0.2

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
@@ -1,10 +1,11 @@
1
1
  # @cellium/mcp-client
2
2
 
3
- A Model Context Protocol (MCP) server that proxies requests to remote Cellium processor servers.
3
+ A Model Context Protocol (MCP) server that provides both HTTP streaming and stdio transports to connect remote Cellium processor servers with MCP clients like Codex.
4
4
 
5
5
  ## Features
6
6
 
7
- - **HTTP Proxy**: Proxies MCP requests to remote Cellium servers via HTTP
7
+ - **Dual Transport Support**: Both HTTP streaming and stdio transport for maximum compatibility
8
+ - **Auto-Detection**: Automatically chooses appropriate transport based on execution context
8
9
  - **Authentication**: Token-based authentication with format `user:username:hash`
9
10
  - **Robust Connection Management**: Automatic reconnection with configurable retry logic
10
11
  - **MCP Protocol Compliance**: Uses official `@modelcontextprotocol/sdk`
@@ -27,6 +28,9 @@ npx @cellium/mcp-client
27
28
 
28
29
  ### Command Line Interface
29
30
 
31
+ #### HTTP Streaming Mode (Default)
32
+ Perfect for direct connections to the MCP server:
33
+
30
34
  ```bash
31
35
  # Using environment variable for token
32
36
  export CELLIUM_MCP_TOKEN="user:your-username:your-hash"
@@ -35,19 +39,46 @@ cellium-mcp-client
35
39
  # Using command line option
36
40
  cellium-mcp-client --token "user:your-username:your-hash"
37
41
 
38
- # With custom endpoint and verbose logging
42
+ # With custom endpoint, port, and verbose logging
39
43
  cellium-mcp-client \
40
44
  --token "user:your-username:your-hash" \
41
- --endpoint "https://mcp.cellium.dev/mcp" \
45
+ --endpoint "https://mcp.cellium.dev/http-stream" \
46
+ --port 3001 \
42
47
  --verbose \
43
48
  --retry-attempts 5 \
44
49
  --retry-delay 2000
45
50
  ```
46
51
 
47
- ### Configuration with Cody/Other MCP Clients
52
+ #### Stdio Mode (Command-Based MCP Clients)
53
+ Perfect for command-based MCP clients like Codex:
54
+
55
+ ```bash
56
+ # Explicit stdio mode
57
+ cellium-mcp-client --stdio --token "user:your-username:your-hash"
58
+
59
+ # Auto-detection when run by MCP clients (stdin is piped)
60
+ # This happens automatically when launched via MCP client configuration
61
+ ```
62
+
63
+ ### Configuration with Codex/Other MCP Clients
64
+
65
+ #### Option 1: Direct HTTP Connection (Recommended for modern clients)
66
+ ```toml
67
+ [mcp_servers.cellium]
68
+ url = "http://localhost:3001"
69
+ enabled = true
70
+ ```
48
71
 
49
- Add to your MCP client configuration:
72
+ #### Option 2: Command-Based with Stdio Transport (Universal compatibility)
73
+ ```toml
74
+ [mcp_servers.cellium]
75
+ command = "npx"
76
+ args = ["-y", "@cellium/mcp-client", "--stdio"]
77
+ env = { CELLIUM_MCP_TOKEN = "user:your-username:your-hash" }
78
+ enabled = true
79
+ ```
50
80
 
81
+ #### Option 3: Command-Based with HTTP Auto-Start
51
82
  ```toml
52
83
  [mcp_servers.cellium]
53
84
  command = "npx"
@@ -64,17 +95,30 @@ import pino from 'pino';
64
95
 
65
96
  const logger = pino();
66
97
 
67
- const client = new CelliumMCPClient({
98
+ // HTTP Streaming Mode
99
+ const httpClient = new CelliumMCPClient({
100
+ token: 'user:your-username:your-hash',
101
+ endpoint: 'https://mcp.cellium.dev/http-stream',
102
+ httpPort: 3001,
103
+ logger,
104
+ retryAttempts: 3,
105
+ retryDelay: 1000,
106
+ useStdio: false
107
+ });
108
+
109
+ // Stdio Mode
110
+ const stdioClient = new CelliumMCPClient({
68
111
  token: 'user:your-username:your-hash',
69
- endpoint: 'https://mcp.cellium.dev/mcp',
112
+ endpoint: 'https://mcp.cellium.dev/http-stream',
70
113
  logger,
71
114
  retryAttempts: 3,
72
- retryDelay: 1000
115
+ retryDelay: 1000,
116
+ useStdio: true
73
117
  });
74
118
 
75
119
  await client.connect();
120
+ await client.serve();
76
121
 
77
- // The client will now proxy MCP requests to the remote server
78
122
  // Handle shutdown gracefully
79
123
  process.on('SIGINT', async () => {
80
124
  await client.disconnect();
@@ -87,11 +131,35 @@ process.on('SIGINT', async () => {
87
131
  | Option | Environment Variable | Description | Default |
88
132
  |--------|---------------------|-------------|---------|
89
133
  | `--token` | `CELLIUM_MCP_TOKEN` | Authentication token (required) | - |
90
- | `--endpoint` | - | Server endpoint URL | `https://mcp.cellium.dev/mcp` |
134
+ | `--endpoint` | - | Server endpoint URL | `http://localhost:3000/http-stream` |
135
+ | `--port` | - | HTTP streaming port | `3001` |
136
+ | `--stdio` | - | Force stdio transport | Auto-detected |
91
137
  | `--verbose` | - | Enable verbose logging | `false` |
138
+ | `--debug` | - | Enable debug mode with extensive logging | `false` |
92
139
  | `--retry-attempts` | - | Number of retry attempts | `3` |
93
140
  | `--retry-delay` | - | Delay between retries (ms) | `1000` |
94
141
 
142
+ ## Transport Modes
143
+
144
+ ### HTTP Streaming Transport
145
+ - **Use case**: Direct connections from modern MCP clients
146
+ - **Benefits**: Persistent connections, multiple concurrent sessions, better scalability
147
+ - **Connection**: Client connects directly to `http://localhost:3001` (or configured port)
148
+ - **Communication**: Server-Sent Events (SSE) for real-time streaming
149
+
150
+ ### Stdio Transport
151
+ - **Use case**: Command-based MCP clients (like traditional Codex configurations)
152
+ - **Benefits**: Universal compatibility, no port conflicts, standard MCP pattern
153
+ - **Connection**: MCP client launches process and communicates via stdin/stdout
154
+ - **Communication**: Standard input/output pipes
155
+
156
+ ### Auto-Detection Logic
157
+ The client automatically chooses the appropriate transport:
158
+
159
+ 1. **Explicit stdio**: `--stdio` flag forces stdio transport
160
+ 2. **Auto-detected stdio**: When stdin is piped (command-based execution)
161
+ 3. **Default HTTP**: All other cases use HTTP streaming transport
162
+
95
163
  ## Authentication Token Format
96
164
 
97
165
  The authentication token must follow the format: `user:username:hash`
@@ -106,16 +174,17 @@ Example: `user:john-doe:a1b2c3d4e5f6...`
106
174
  ## Architecture
107
175
 
108
176
  ```
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
- └─────────────────┘ └─────────────────┘ └─────────────────┘
177
+ ┌─────────────────┐ HTTP/Stdio ┌─────────────────┐ HTTPS ┌─────────────────┐
178
+ │◄──────────────►│ │◄──────────►│ │
179
+ Codex / Streaming │ cellium-mcp- │ HTTP │ Cellium Server │
180
+ │ AI Assistant │ or Pipes │ client (Proxy) │ Requests │ (Remote) │
181
+ │ (Cody, etc.) │ MCP Protocol │ │ │ │
182
+ └─────────────────┘ └─────────────────┘ └─────────────────┘
115
183
  ```
116
184
 
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
185
+ The client acts as a **dual-transport MCP server** that proxies requests between MCP clients and the remote Cellium processor server, handling:
186
+ - **HTTP Streaming Transport**: Provides streamable HTTP interface for direct client connection
187
+ - **Stdio Transport**: Traditional stdin/stdout communication for command-based clients
119
188
  - **Authentication**: Token-based authentication with remote server
120
189
  - **Request Proxying**: Converts MCP requests to HTTP requests to remote server
121
190
  - **Error Handling**: Graceful handling of connection failures
@@ -144,8 +213,9 @@ npm run dev
144
213
  ### Connection Issues
145
214
 
146
215
  1. **Invalid token format**: Ensure your token follows the `user:username:hash` format
147
- 2. **Network connectivity**: Check if `https://mcp.cellium.dev/mcp` is accessible
216
+ 2. **Network connectivity**: Check if `https://mcp.cellium.dev/http-stream` is accessible
148
217
  3. **Authentication failed**: Verify your token is valid and not expired
218
+ 4. **Port already in use**: Change the `--port` option if port 3001 is already in use
149
219
 
150
220
  ### Verbose Logging
151
221
 
@@ -159,8 +229,18 @@ cellium-mcp-client --verbose --token "your-token"
159
229
 
160
230
  - `Authentication token required`: Set `CELLIUM_MCP_TOKEN` or use `--token`
161
231
  - `Invalid token format`: Check token follows `user:username:hash` pattern
162
- - `Not connected to remote server`: Connection to SSE endpoint failed
232
+ - `Not connected to remote server`: Connection to HTTP streaming endpoint failed
163
233
  - `Request timeout`: Remote server didn't respond within 30 seconds
234
+ - `EADDRINUSE`: Port is already in use, try a different `--port`
235
+
236
+ ### Connecting with Codex
237
+
238
+ Make sure to connect your MCP client to the HTTP streaming server at:
239
+ ```
240
+ http://localhost:3001
241
+ ```
242
+
243
+ The server provides Server-Sent Events (SSE) streaming for real-time communication.
164
244
 
165
245
  ## License
166
246
 
package/dist/cli.js CHANGED
@@ -11,9 +11,11 @@ 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.1.3')
14
+ .version('2.0.0')
15
15
  .option('-t, --token <token>', 'Authentication token (format: user:username:hash)')
16
- .option('-e, --endpoint <url>', 'Server endpoint URL', 'http://localhost:3000/mcp')
16
+ .option('-e, --endpoint <url>', 'Server endpoint URL', 'http://localhost:3000/http-stream')
17
+ .option('-p, --port <number>', 'HTTP streaming port for MCP client', '3001')
18
+ .option('--stdio', 'Use stdio transport (for command-based MCP clients)')
17
19
  .option('-v, --verbose', 'Enable verbose logging')
18
20
  .option('-d, --debug', 'Enable debug mode with extensive logging')
19
21
  .option('-r, --retry-attempts <num>', 'Number of retry attempts on connection failure', '3')
@@ -50,24 +52,33 @@ async function main() {
50
52
  logger.info('Example: user:myusername:abc123def456...');
51
53
  process.exit(1);
52
54
  }
55
+ // Auto-detect transport mode
56
+ // Use stdio if explicitly requested or if stdin is piped (indicating command-based usage)
57
+ const useStdio = options.stdio || (!process.stdin.isTTY && process.stdout.isTTY);
58
+ const transportMode = useStdio ? 'stdio' : 'http-streaming';
53
59
  logger.info({
54
- version: '1.1.3',
60
+ version: '2.0.0',
55
61
  debugMode: !!options.debug,
56
- verboseMode: !!options.verbose
62
+ verboseMode: !!options.verbose,
63
+ transportMode
57
64
  }, 'Starting Cellium MCP Client');
58
65
  logger.debug({
59
66
  endpoint: options.endpoint,
67
+ httpPort: parseInt(options.port),
60
68
  retryAttempts: parseInt(options.retryAttempts),
61
69
  retryDelay: parseInt(options.retryDelay),
70
+ useStdio,
62
71
  tokenPreview: `${token.split(':')[0]}:${token.split(':')[1]}:${token.split(':')[2]?.substring(0, 8)}...`
63
72
  }, 'Configuration loaded');
64
73
  const client = new client_1.CelliumMCPClient({
65
74
  token,
66
75
  endpoint: options.endpoint,
76
+ httpPort: parseInt(options.port),
67
77
  logger,
68
78
  retryAttempts: parseInt(options.retryAttempts),
69
79
  retryDelay: parseInt(options.retryDelay),
70
- debugMode: !!options.debug
80
+ debugMode: !!options.debug,
81
+ useStdio
71
82
  });
72
83
  // Enhanced error handling for unhandled rejections
73
84
  process.on('unhandledRejection', (reason, promise) => {
package/dist/client.d.ts CHANGED
@@ -6,11 +6,14 @@ export interface CelliumMCPClientConfig {
6
6
  retryAttempts?: number;
7
7
  retryDelay?: number;
8
8
  debugMode?: boolean;
9
+ httpPort?: number;
10
+ useStdio?: boolean;
9
11
  }
10
12
  export declare class CelliumMCPClient {
11
13
  private config;
12
14
  private localServer;
13
15
  private transport?;
16
+ private httpServer?;
14
17
  private isConnected;
15
18
  private reconnectTimer?;
16
19
  private keepAliveInterval?;
package/dist/client.js CHANGED
@@ -2,7 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CelliumMCPClient = void 0;
4
4
  const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
5
+ const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
5
6
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
7
+ const http_1 = require("http");
8
+ const crypto_1 = require("crypto");
6
9
  const zod_1 = require("zod");
7
10
  // Define schemas for MCP requests
8
11
  const ToolsListSchema = zod_1.z.object({
@@ -45,6 +48,7 @@ class CelliumMCPClient {
45
48
  config;
46
49
  localServer;
47
50
  transport;
51
+ httpServer;
48
52
  isConnected = false;
49
53
  reconnectTimer;
50
54
  keepAliveInterval;
@@ -65,16 +69,19 @@ class CelliumMCPClient {
65
69
  retryAttempts: 3,
66
70
  retryDelay: 1000,
67
71
  debugMode: false,
72
+ httpPort: 3001, // Default HTTP streaming port
73
+ useStdio: false,
68
74
  ...config
69
75
  };
70
76
  this.logDebug('Initializing CelliumMCPClient', {
71
77
  endpoint: this.config.endpoint,
78
+ httpPort: this.config.httpPort,
72
79
  debugMode: this.config.debugMode
73
80
  });
74
- // Local server that interfaces with AI assistants via stdio
81
+ // Local server that interfaces with AI assistants via HTTP streaming
75
82
  this.localServer = new mcp_js_1.McpServer({
76
83
  name: 'cellium-mcp-client',
77
- version: '1.1.3'
84
+ version: '2.0.0'
78
85
  }, {
79
86
  capabilities: {
80
87
  tools: {},
@@ -216,7 +223,7 @@ class CelliumMCPClient {
216
223
  },
217
224
  serverInfo: {
218
225
  name: 'cellium-mcp-client',
219
- version: '1.1.3'
226
+ version: '2.0.0'
220
227
  }
221
228
  };
222
229
  this.transportState.connected = true;
@@ -355,14 +362,14 @@ class CelliumMCPClient {
355
362
  throw new Error('Cannot connect to remote Cellium server');
356
363
  }
357
364
  }
358
- const mcpEndpoint = this.config.endpoint.replace('/sse', '/mcp');
365
+ const httpStreamEndpoint = this.config.endpoint.replace('/sse', '/http-stream');
359
366
  const requestBody = {
360
367
  jsonrpc: '2.0',
361
368
  id: requestId,
362
369
  method,
363
370
  params
364
371
  };
365
- this.logDebug(`Making HTTP request to ${mcpEndpoint}`, {
372
+ this.logDebug(`Making HTTP request to ${httpStreamEndpoint}`, {
366
373
  requestId,
367
374
  method,
368
375
  bodySize: JSON.stringify(requestBody).length
@@ -370,12 +377,12 @@ class CelliumMCPClient {
370
377
  let response;
371
378
  let responseText;
372
379
  try {
373
- response = await fetch(mcpEndpoint, {
380
+ response = await fetch(httpStreamEndpoint, {
374
381
  method: 'POST',
375
382
  headers: {
376
383
  'Authorization': `Bearer ${this.config.token}`,
377
384
  'Content-Type': 'application/json',
378
- 'User-Agent': 'cellium-mcp-client/1.1.3'
385
+ 'User-Agent': 'cellium-mcp-client/2.0.0'
379
386
  },
380
387
  body: JSON.stringify(requestBody)
381
388
  });
@@ -434,7 +441,7 @@ class CelliumMCPClient {
434
441
  requestId,
435
442
  method,
436
443
  duration,
437
- endpoint: mcpEndpoint
444
+ endpoint: httpStreamEndpoint
438
445
  }, 'HTTP request to remote server failed');
439
446
  this.isConnected = false; // Mark as disconnected on error
440
447
  throw error;
@@ -442,34 +449,90 @@ class CelliumMCPClient {
442
449
  }
443
450
  async connect() {
444
451
  try {
452
+ const transportMode = this.config.useStdio ? 'stdio' : 'http-streaming';
445
453
  this.config.logger.info({
446
454
  endpoint: this.config.endpoint,
455
+ httpPort: this.config.httpPort,
447
456
  debugMode: this.config.debugMode,
448
- protocolVersion: this.mcpProtocolVersion
449
- }, 'Starting Cellium MCP Server');
450
- this.logDebug('Initializing stdio transport');
451
- // Set up the stdio transport for local MCP server immediately
452
- this.transport = new stdio_js_1.StdioServerTransport();
453
- // Add transport event monitoring
454
- this.setupTransportEventListeners(this.transport);
455
- await this.localServer.connect(this.transport);
456
- this.transportState.connected = true;
457
- this.transportState.lastActivity = Date.now();
458
- this.config.logger.info('MCP Server connected and ready');
459
- this.logDebug('Transport connected successfully', {
460
- transportType: 'stdio',
461
- serverName: 'cellium-mcp-client',
462
- serverVersion: '1.1.3'
463
- });
464
- // Keep the process alive with a minimal interval
465
- // This ensures the process doesn't exit when stdin closes
466
- this.keepAliveInterval = setInterval(() => {
467
- this.logDebug('Keep-alive tick', {
468
- uptime: Date.now() - this.transportState.lastActivity,
469
- activeRequests: this.activeRequests.size
457
+ protocolVersion: this.mcpProtocolVersion,
458
+ transportMode
459
+ }, `Starting Cellium MCP Client with ${transportMode} transport`);
460
+ if (this.config.useStdio) {
461
+ // Use stdio transport for command-based MCP clients
462
+ this.logDebug('Initializing stdio transport');
463
+ this.transport = new stdio_js_1.StdioServerTransport();
464
+ // Add transport event monitoring if supported
465
+ if ('onclose' in this.transport) {
466
+ this.setupTransportEventListeners(this.transport);
467
+ }
468
+ await this.localServer.connect(this.transport);
469
+ this.transportState.connected = true;
470
+ this.transportState.lastActivity = Date.now();
471
+ this.config.logger.info('MCP stdio transport ready - communicating via stdin/stdout');
472
+ this.logDebug('Stdio transport connected successfully', {
473
+ transportType: 'stdio',
474
+ serverName: 'cellium-mcp-client',
475
+ serverVersion: '2.0.0'
476
+ });
477
+ }
478
+ else {
479
+ // Use HTTP streaming transport (original implementation)
480
+ this.logDebug('Initializing HTTP streaming transport');
481
+ // Set up the HTTP streaming transport for local MCP server
482
+ this.transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
483
+ sessionIdGenerator: () => (0, crypto_1.randomUUID)(),
484
+ enableJsonResponse: false // Use SSE streaming for compatibility with codex
485
+ });
486
+ // Add transport event monitoring
487
+ this.setupTransportEventListeners(this.transport);
488
+ await this.localServer.connect(this.transport);
489
+ this.transportState.connected = true;
490
+ this.transportState.lastActivity = Date.now();
491
+ // Create HTTP server to handle requests
492
+ this.httpServer = (0, http_1.createServer)(async (req, res) => {
493
+ try {
494
+ await this.transport.handleRequest(req, res);
495
+ }
496
+ catch (error) {
497
+ this.config.logger.error({ error }, 'Error handling HTTP request');
498
+ res.statusCode = 500;
499
+ res.end('Internal Server Error');
500
+ }
501
+ });
502
+ // Start HTTP server
503
+ await new Promise((resolve, reject) => {
504
+ this.httpServer.listen(this.config.httpPort, (err) => {
505
+ if (err) {
506
+ reject(err);
507
+ }
508
+ else {
509
+ this.config.logger.info({
510
+ port: this.config.httpPort,
511
+ url: `http://localhost:${this.config.httpPort}`
512
+ }, 'HTTP streaming server started');
513
+ resolve();
514
+ }
515
+ });
470
516
  });
471
- }, 30000); // Check every 30 seconds
472
- this.config.logger.debug('Keep-alive interval started for persistent MCP communication');
517
+ this.config.logger.info(`MCP HTTP Streaming Server ready at http://localhost:${this.config.httpPort}`);
518
+ this.logDebug('HTTP streaming transport connected successfully', {
519
+ transportType: 'streamable-http',
520
+ port: this.config.httpPort,
521
+ serverName: 'cellium-mcp-client',
522
+ serverVersion: '2.0.0'
523
+ });
524
+ }
525
+ // For HTTP streaming mode, keep the process alive with a minimal interval
526
+ // For stdio mode, the process stays alive naturally via stdin/stdout communication
527
+ if (!this.config.useStdio) {
528
+ this.keepAliveInterval = setInterval(() => {
529
+ this.logDebug('Keep-alive tick', {
530
+ uptime: Date.now() - this.transportState.lastActivity,
531
+ activeRequests: this.activeRequests.size
532
+ });
533
+ }, 30000); // Check every 30 seconds
534
+ this.config.logger.debug('Keep-alive interval started for persistent HTTP MCP communication');
535
+ }
473
536
  // Test connection to remote server and pre-fetch tools cache
474
537
  await this.initializeRemoteConnection();
475
538
  }
@@ -480,80 +543,79 @@ class CelliumMCPClient {
480
543
  message: error.message,
481
544
  stack: error.stack
482
545
  } : error
483
- }, 'Failed to start MCP server');
546
+ }, 'Failed to start MCP HTTP streaming server');
484
547
  this.transportState.connected = false;
485
548
  throw error;
486
549
  }
487
550
  }
488
551
  setupTransportEventListeners(transport) {
489
552
  this.logDebug('Setting up transport event listeners');
490
- // Monitor transport events if available
491
- try {
492
- // Try to access transport events (may not be available in all SDK versions)
493
- const transportAny = transport;
494
- if (transportAny.on && typeof transportAny.on === 'function') {
495
- transportAny.on('close', () => {
496
- this.config.logger.warn('Transport close event detected');
497
- this.transportState.connected = false;
498
- });
499
- transportAny.on('error', (error) => {
500
- this.config.logger.error({ error }, 'Transport error event');
501
- this.transportState.errorCount++;
502
- });
503
- transportAny.on('connect', () => {
504
- this.config.logger.info('Transport connect event');
505
- this.transportState.connected = true;
506
- });
507
- this.logDebug('Transport event listeners attached');
508
- }
509
- else {
510
- this.logDebug('Transport does not support event listeners');
511
- }
512
- }
513
- catch (error) {
514
- this.logDebug('Could not attach transport event listeners', { error });
515
- }
553
+ // Set up transport event handlers
554
+ transport.onclose = () => {
555
+ this.config.logger.warn('Transport close event detected');
556
+ this.transportState.connected = false;
557
+ };
558
+ transport.onerror = (error) => {
559
+ this.config.logger.error({ error }, 'Transport error event');
560
+ this.transportState.errorCount++;
561
+ };
562
+ this.logDebug('Transport event listeners attached');
516
563
  }
517
564
  async serve() {
518
- // The MCP server is already connected via stdio in connect()
519
- // Keep the process alive indefinitely for persistent MCP communication
520
- // This ensures compatibility with MCP clients like Copilot that expect long-running servers
521
- this.config.logger.debug({
522
- transportConnected: this.transportState.connected,
523
- serverName: 'cellium-mcp-client'
524
- }, 'Server is now serving and will stay alive for persistent MCP communication');
525
- this.logDebug('Entering serve mode - process will stay alive');
526
- // Set up graceful shutdown handlers
527
- process.on('SIGINT', async () => {
528
- this.config.logger.info('Received SIGINT, shutting down gracefully...');
529
- await this.disconnect();
530
- process.exit(0);
531
- });
532
- process.on('SIGTERM', async () => {
533
- this.config.logger.info('Received SIGTERM, shutting down gracefully...');
534
- await this.disconnect();
535
- process.exit(0);
536
- });
537
- // Return a promise that never resolves to keep the process alive
538
- // The process will only exit via SIGINT/SIGTERM signals
539
- return new Promise(() => {
540
- // Never resolve - keep process alive for MCP communication
541
- // Process will be terminated gracefully via signal handlers
542
- });
565
+ if (this.config.useStdio) {
566
+ // For stdio transport, the MCP communication happens automatically via stdin/stdout
567
+ // The server stays alive as long as the parent process keeps it alive
568
+ this.config.logger.debug({
569
+ transportConnected: this.transportState.connected,
570
+ transportMode: 'stdio',
571
+ serverName: 'cellium-mcp-client'
572
+ }, 'Stdio MCP server is now serving via stdin/stdout');
573
+ this.logDebug('Entering serve mode - stdio transport active');
574
+ // For stdio mode, we don't need to keep the process alive artificially
575
+ // The process will stay alive as long as stdin/stdout are connected
576
+ // No need to return a never-resolving promise
577
+ }
578
+ else {
579
+ // For HTTP streaming mode, keep the process alive indefinitely
580
+ this.config.logger.debug({
581
+ transportConnected: this.transportState.connected,
582
+ httpPort: this.config.httpPort,
583
+ transportMode: 'http-streaming',
584
+ serverName: 'cellium-mcp-client'
585
+ }, 'HTTP streaming server is now serving and will stay alive for persistent MCP communication');
586
+ this.logDebug('Entering serve mode - HTTP server will stay alive');
587
+ // Set up graceful shutdown handlers
588
+ process.on('SIGINT', async () => {
589
+ this.config.logger.info('Received SIGINT, shutting down gracefully...');
590
+ await this.disconnect();
591
+ process.exit(0);
592
+ });
593
+ process.on('SIGTERM', async () => {
594
+ this.config.logger.info('Received SIGTERM, shutting down gracefully...');
595
+ await this.disconnect();
596
+ process.exit(0);
597
+ });
598
+ // Return a promise that never resolves to keep the process alive
599
+ // The process will only exit via SIGINT/SIGTERM signals
600
+ return new Promise(() => {
601
+ // Never resolve - keep process alive for HTTP streaming MCP communication
602
+ // Process will be terminated gracefully via signal handlers
603
+ });
604
+ }
543
605
  }
544
606
  async testConnection() {
545
607
  const startTime = Date.now();
546
- const mcpEndpoint = this.config.endpoint.replace('/sse', '/mcp');
608
+ const httpStreamEndpoint = this.config.endpoint.replace('/sse', '/http-stream');
547
609
  this.logDebug('Testing connection', {
548
- endpoint: mcpEndpoint,
610
+ endpoint: httpStreamEndpoint,
549
611
  hasToken: !!this.config.token
550
612
  });
551
- const response = await fetch(mcpEndpoint, {
613
+ const response = await fetch(httpStreamEndpoint, {
552
614
  method: 'POST',
553
615
  headers: {
554
616
  'Authorization': `Bearer ${this.config.token}`,
555
617
  'Content-Type': 'application/json',
556
- 'User-Agent': 'cellium-mcp-client/1.1.3'
618
+ 'User-Agent': 'cellium-mcp-client/2.0.0'
557
619
  },
558
620
  body: JSON.stringify({
559
621
  jsonrpc: '2.0',
@@ -640,7 +702,7 @@ class CelliumMCPClient {
640
702
  }
641
703
  }
642
704
  async disconnect() {
643
- this.config.logger.info('Disconnecting from Cellium MCP Server');
705
+ this.config.logger.info('Disconnecting from Cellium MCP HTTP Streaming Server');
644
706
  this.logDebug('Starting disconnect process', {
645
707
  activeRequestCount: this.activeRequests.size,
646
708
  transportConnected: this.transportState.connected
@@ -669,6 +731,15 @@ class CelliumMCPClient {
669
731
  clearInterval(this.backgroundRefreshTimer);
670
732
  this.backgroundRefreshTimer = undefined;
671
733
  }
734
+ // Close HTTP server
735
+ if (this.httpServer) {
736
+ await new Promise((resolve) => {
737
+ this.httpServer.close(() => {
738
+ this.config.logger.info('HTTP streaming server closed');
739
+ resolve();
740
+ });
741
+ });
742
+ }
672
743
  try {
673
744
  await this.localServer.close();
674
745
  this.logDebug('Local MCP server closed successfully');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cellium-mcp-client",
3
- "version": "1.1.7",
3
+ "version": "2.0.2",
4
4
  "description": "MCP client for connecting to remote Cellium processor server",
5
5
  "main": "dist/index.js",
6
6
  "bin": {