cellium-mcp-client 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Cellium Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,181 @@
1
+ # @cellium/mcp-client
2
+
3
+ A Model Context Protocol (MCP) client for connecting to the remote Cellium processor server via Server-Sent Events (SSE).
4
+
5
+ ## Features
6
+
7
+ - **SSE Transport**: Connects to remote Cellium server using Server-Sent Events
8
+ - **Authentication**: Token-based authentication with format `user:username:hash`
9
+ - **Robust Connection Management**: Automatic reconnection with configurable retry logic
10
+ - **MCP Protocol Compliance**: Uses official `@modelcontextprotocol/sdk`
11
+ - **CLI Interface**: Ready-to-use command-line interface
12
+ - **TypeScript Support**: Full TypeScript types and definitions
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install -g @cellium/mcp-client
18
+ ```
19
+
20
+ Or use directly with npx:
21
+
22
+ ```bash
23
+ npx @cellium/mcp-client
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ ### Command Line Interface
29
+
30
+ ```bash
31
+ # Using environment variable for token
32
+ export CELLIUM_MCP_TOKEN="user:your-username:your-hash"
33
+ cellium-mcp-client
34
+
35
+ # Using command line option
36
+ cellium-mcp-client --token "user:your-username:your-hash"
37
+
38
+ # With custom endpoint and verbose logging
39
+ cellium-mcp-client \
40
+ --token "user:your-username:your-hash" \
41
+ --endpoint "https://mcp.cellium.dev/sse" \
42
+ --verbose \
43
+ --retry-attempts 5 \
44
+ --retry-delay 2000
45
+ ```
46
+
47
+ ### Configuration with Cody/Other MCP Clients
48
+
49
+ Add to your MCP client configuration:
50
+
51
+ ```toml
52
+ [mcp_servers.cellium]
53
+ command = "npx"
54
+ args = ["-y", "@cellium/mcp-client"]
55
+ env = { CELLIUM_MCP_TOKEN = "user:your-username:your-hash" }
56
+ enabled = true
57
+ ```
58
+
59
+ ### Programmatic Usage
60
+
61
+ ```typescript
62
+ import { CelliumMCPClient } from '@cellium/mcp-client';
63
+ import pino from 'pino';
64
+
65
+ const logger = pino();
66
+
67
+ const client = new CelliumMCPClient({
68
+ token: 'user:your-username:your-hash',
69
+ endpoint: 'https://mcp.cellium.dev/sse',
70
+ logger,
71
+ retryAttempts: 3,
72
+ retryDelay: 1000
73
+ });
74
+
75
+ await client.connect();
76
+
77
+ // The client will now proxy MCP requests to the remote server
78
+ // Handle shutdown gracefully
79
+ process.on('SIGINT', async () => {
80
+ await client.disconnect();
81
+ process.exit(0);
82
+ });
83
+ ```
84
+
85
+ ## Configuration Options
86
+
87
+ | Option | Environment Variable | Description | Default |
88
+ |--------|---------------------|-------------|---------|
89
+ | `--token` | `CELLIUM_MCP_TOKEN` | Authentication token (required) | - |
90
+ | `--endpoint` | - | Server endpoint URL | `https://mcp.cellium.dev/sse` |
91
+ | `--verbose` | - | Enable verbose logging | `false` |
92
+ | `--retry-attempts` | - | Number of retry attempts | `3` |
93
+ | `--retry-delay` | - | Delay between retries (ms) | `1000` |
94
+
95
+ ## Authentication Token Format
96
+
97
+ The authentication token must follow the format: `user:username:hash`
98
+
99
+ Where:
100
+ - `user`: Literal string "user"
101
+ - `username`: Your Cellium username
102
+ - `hash`: Authentication hash provided by Cellium
103
+
104
+ Example: `user:john-doe:a1b2c3d4e5f6...`
105
+
106
+ ## Architecture
107
+
108
+ ```
109
+ ┌─────────────────┐ SSE ┌─────────────────┐
110
+ │ │◄──────────►│ │
111
+ │ MCP Client │ HTTPS │ Cellium Server │
112
+ │ (This Package) │ │ (Remote) │
113
+ │ │ │ │
114
+ └─────────────────┘ └─────────────────┘
115
+ ▲ │
116
+ │ stdio │
117
+ │ MCP Protocol │
118
+ ▼ │
119
+ ┌─────────────────┐ │
120
+ │ │ │
121
+ │ Code Editor / │ │
122
+ │ AI Assistant │ │
123
+ │ (Cody, etc.) │ │
124
+ └─────────────────┘ │
125
+ ```
126
+
127
+ The client acts as a bridge between local MCP clients (like Cody) and the remote Cellium processor server, handling:
128
+ - Authentication and connection management
129
+ - Protocol translation between stdio MCP and SSE
130
+ - Error handling and reconnection logic
131
+ - Request/response proxying
132
+
133
+ ## Development
134
+
135
+ ```bash
136
+ # Clone and install dependencies
137
+ git clone <repo-url>
138
+ cd cellium-mcp-client
139
+ npm install
140
+
141
+ # Build
142
+ npm run build
143
+
144
+ # Run in development mode
145
+ npm run dev
146
+
147
+ # Test locally
148
+ ./dist/cli.js --help
149
+ ```
150
+
151
+ ## Troubleshooting
152
+
153
+ ### Connection Issues
154
+
155
+ 1. **Invalid token format**: Ensure your token follows the `user:username:hash` format
156
+ 2. **Network connectivity**: Check if `https://mcp.cellium.dev/sse` is accessible
157
+ 3. **Authentication failed**: Verify your token is valid and not expired
158
+
159
+ ### Verbose Logging
160
+
161
+ Use `--verbose` flag to enable detailed logging for debugging:
162
+
163
+ ```bash
164
+ cellium-mcp-client --verbose --token "your-token"
165
+ ```
166
+
167
+ ### Common Error Messages
168
+
169
+ - `Authentication token required`: Set `CELLIUM_MCP_TOKEN` or use `--token`
170
+ - `Invalid token format`: Check token follows `user:username:hash` pattern
171
+ - `Not connected to remote server`: Connection to SSE endpoint failed
172
+ - `Request timeout`: Remote server didn't respond within 30 seconds
173
+
174
+ ## License
175
+
176
+ MIT
177
+
178
+ ## Support
179
+
180
+ For issues and questions, please open an issue on the GitHub repository.
181
+
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const client_1 = require("./client");
8
+ const commander_1 = require("commander");
9
+ const pino_1 = __importDefault(require("pino"));
10
+ const program = new commander_1.Command();
11
+ program
12
+ .name('cellium-mcp-client')
13
+ .description('MCP client for connecting to remote Cellium processor server')
14
+ .version('1.0.0')
15
+ .option('-t, --token <token>', 'Authentication token (format: user:username:hash)')
16
+ .option('-e, --endpoint <url>', 'Server endpoint URL', 'https://mcp.cellium.dev/sse')
17
+ .option('-v, --verbose', 'Enable verbose logging')
18
+ .option('-r, --retry-attempts <num>', 'Number of retry attempts on connection failure', '3')
19
+ .option('--retry-delay <ms>', 'Delay between retry attempts in milliseconds', '1000')
20
+ .parse(process.argv);
21
+ const options = program.opts();
22
+ // Configure logging
23
+ const logger = (0, pino_1.default)({
24
+ level: options.verbose ? 'debug' : 'info',
25
+ transport: {
26
+ target: 'pino-pretty',
27
+ options: {
28
+ colorize: true,
29
+ translateTime: 'HH:MM:ss',
30
+ ignore: 'pid,hostname'
31
+ }
32
+ }
33
+ });
34
+ async function main() {
35
+ try {
36
+ // Get token from environment or command line
37
+ const token = options.token || process.env.CELLIUM_MCP_TOKEN;
38
+ if (!token) {
39
+ logger.error('Authentication token required. Use --token option or CELLIUM_MCP_TOKEN environment variable');
40
+ process.exit(1);
41
+ }
42
+ // Validate token format
43
+ if (!token.match(/^user:[^:]+:[a-f0-9]+$/)) {
44
+ logger.error('Invalid token format. Expected: user:username:hash');
45
+ process.exit(1);
46
+ }
47
+ logger.info('Starting Cellium MCP Client');
48
+ logger.debug({
49
+ endpoint: options.endpoint,
50
+ retryAttempts: parseInt(options.retryAttempts),
51
+ retryDelay: parseInt(options.retryDelay)
52
+ }, 'Configuration');
53
+ const client = new client_1.CelliumMCPClient({
54
+ token,
55
+ endpoint: options.endpoint,
56
+ logger,
57
+ retryAttempts: parseInt(options.retryAttempts),
58
+ retryDelay: parseInt(options.retryDelay)
59
+ });
60
+ // Handle graceful shutdown
61
+ process.on('SIGINT', async () => {
62
+ logger.info('Received SIGINT, shutting down gracefully');
63
+ await client.disconnect();
64
+ process.exit(0);
65
+ });
66
+ process.on('SIGTERM', async () => {
67
+ logger.info('Received SIGTERM, shutting down gracefully');
68
+ await client.disconnect();
69
+ process.exit(0);
70
+ });
71
+ await client.connect();
72
+ // Keep the process alive
73
+ process.stdin.resume();
74
+ }
75
+ catch (error) {
76
+ logger.error({ error }, 'Fatal error');
77
+ process.exit(1);
78
+ }
79
+ }
80
+ main().catch((error) => {
81
+ console.error('Unhandled error:', error);
82
+ process.exit(1);
83
+ });
@@ -0,0 +1,23 @@
1
+ import type { Logger } from 'pino';
2
+ export interface CelliumMCPClientConfig {
3
+ token: string;
4
+ endpoint: string;
5
+ logger: Logger;
6
+ retryAttempts?: number;
7
+ retryDelay?: number;
8
+ }
9
+ export declare class CelliumMCPClient {
10
+ private config;
11
+ private localServer;
12
+ private isConnected;
13
+ private reconnectTimer?;
14
+ private currentRetryCount;
15
+ constructor(config: CelliumMCPClientConfig);
16
+ private setupServer;
17
+ private makeHttpRequest;
18
+ connect(): Promise<void>;
19
+ private testConnection;
20
+ private handleConnectionError;
21
+ private reconnect;
22
+ disconnect(): Promise<void>;
23
+ }
package/dist/client.js ADDED
@@ -0,0 +1,221 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CelliumMCPClient = void 0;
4
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
5
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
6
+ const zod_1 = require("zod");
7
+ // Define schemas for MCP requests
8
+ const ToolsListSchema = zod_1.z.object({
9
+ method: zod_1.z.literal('tools/list'),
10
+ params: zod_1.z.object({}).optional()
11
+ });
12
+ const ToolsCallSchema = zod_1.z.object({
13
+ method: zod_1.z.literal('tools/call'),
14
+ params: zod_1.z.object({
15
+ name: zod_1.z.string(),
16
+ arguments: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional()
17
+ })
18
+ });
19
+ const ResourcesListSchema = zod_1.z.object({
20
+ method: zod_1.z.literal('resources/list'),
21
+ params: zod_1.z.object({}).optional()
22
+ });
23
+ const ResourcesReadSchema = zod_1.z.object({
24
+ method: zod_1.z.literal('resources/read'),
25
+ params: zod_1.z.object({
26
+ uri: zod_1.z.string()
27
+ })
28
+ });
29
+ const PingSchema = zod_1.z.object({
30
+ method: zod_1.z.literal('ping'),
31
+ params: zod_1.z.object({}).optional()
32
+ });
33
+ class CelliumMCPClient {
34
+ config;
35
+ localServer;
36
+ isConnected = false;
37
+ reconnectTimer;
38
+ currentRetryCount = 0;
39
+ constructor(config) {
40
+ this.config = {
41
+ retryAttempts: 3,
42
+ retryDelay: 1000,
43
+ ...config
44
+ };
45
+ // Local server that interfaces with AI assistants via stdio
46
+ this.localServer = new mcp_js_1.McpServer({
47
+ name: 'cellium-mcp-client',
48
+ version: '1.0.0'
49
+ }, {
50
+ capabilities: {
51
+ tools: {},
52
+ resources: {}
53
+ }
54
+ });
55
+ this.setupServer();
56
+ }
57
+ setupServer() {
58
+ // Register a dynamic tool handler that forwards all tool calls to remote server
59
+ this.localServer.registerTool('cellium-proxy-tool', {
60
+ description: 'Proxy tool for Cellium server - handles all tool calls dynamically'
61
+ }, async (_args) => {
62
+ // This won't actually be called since we override the request handlers directly
63
+ return { content: [{ type: 'text', text: 'Proxy tool' }] };
64
+ });
65
+ // Override the underlying server's tool request handlers to proxy to remote
66
+ this.localServer.server.setRequestHandler(ToolsListSchema, async () => {
67
+ 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
+ const result = await this.makeHttpRequest('tools/list', {});
72
+ return result;
73
+ });
74
+ this.localServer.server.setRequestHandler(ToolsCallSchema, async (request) => {
75
+ 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
+ const result = await this.makeHttpRequest('tools/call', request.params);
80
+ return result;
81
+ });
82
+ // Handle resources as well
83
+ this.localServer.server.setRequestHandler(ResourcesListSchema, async () => {
84
+ 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
+ const result = await this.makeHttpRequest('resources/list', {});
89
+ return result;
90
+ });
91
+ this.localServer.server.setRequestHandler(ResourcesReadSchema, async (request) => {
92
+ 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
+ const result = await this.makeHttpRequest('resources/read', request.params);
97
+ return result;
98
+ });
99
+ // Handle ping
100
+ this.localServer.server.setRequestHandler(PingSchema, async () => {
101
+ if (!this.isConnected) {
102
+ throw new Error('Not connected to remote server');
103
+ }
104
+ const result = await this.makeHttpRequest('ping', {});
105
+ return result;
106
+ });
107
+ }
108
+ async makeHttpRequest(method, params) {
109
+ const mcpEndpoint = this.config.endpoint.replace('/sse', '/mcp');
110
+ const requestBody = {
111
+ jsonrpc: '2.0',
112
+ id: Math.random().toString(36).substring(2, 15),
113
+ method,
114
+ params
115
+ };
116
+ this.config.logger.debug({ method, endpoint: mcpEndpoint }, 'Making HTTP request to remote server');
117
+ try {
118
+ const response = await fetch(mcpEndpoint, {
119
+ method: 'POST',
120
+ headers: {
121
+ 'Authorization': `Bearer ${this.config.token}`,
122
+ 'Content-Type': 'application/json'
123
+ },
124
+ body: JSON.stringify(requestBody)
125
+ });
126
+ if (!response.ok) {
127
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
128
+ }
129
+ const jsonResponse = await response.json();
130
+ if (jsonResponse.error) {
131
+ throw new Error(`Remote server error: ${jsonResponse.error.message}`);
132
+ }
133
+ return jsonResponse.result;
134
+ }
135
+ catch (error) {
136
+ this.config.logger.error({ error, method }, 'HTTP request to remote server failed');
137
+ throw error;
138
+ }
139
+ }
140
+ async connect() {
141
+ try {
142
+ this.config.logger.info({ endpoint: this.config.endpoint }, 'Connecting to Cellium MCP Server');
143
+ // Test connection with a ping
144
+ await this.testConnection();
145
+ this.isConnected = true;
146
+ this.currentRetryCount = 0;
147
+ if (this.reconnectTimer) {
148
+ clearTimeout(this.reconnectTimer);
149
+ this.reconnectTimer = undefined;
150
+ }
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
+ }
157
+ catch (error) {
158
+ this.config.logger.error({ error }, 'Failed to connect');
159
+ this.isConnected = false;
160
+ this.handleConnectionError();
161
+ throw error;
162
+ }
163
+ }
164
+ async testConnection() {
165
+ const mcpEndpoint = this.config.endpoint.replace('/sse', '/mcp');
166
+ const response = await fetch(mcpEndpoint, {
167
+ method: 'POST',
168
+ headers: {
169
+ 'Authorization': `Bearer ${this.config.token}`,
170
+ 'Content-Type': 'application/json'
171
+ },
172
+ body: JSON.stringify({
173
+ jsonrpc: '2.0',
174
+ id: 'test-connection',
175
+ method: 'ping',
176
+ params: {}
177
+ })
178
+ });
179
+ if (!response.ok) {
180
+ throw new Error(`Connection test failed: HTTP ${response.status}`);
181
+ }
182
+ const result = await response.json();
183
+ if (result.error) {
184
+ throw new Error(`Connection test failed: ${result.error.message}`);
185
+ }
186
+ this.config.logger.debug('Connection test successful');
187
+ }
188
+ handleConnectionError() {
189
+ if (this.currentRetryCount < this.config.retryAttempts) {
190
+ this.currentRetryCount++;
191
+ this.config.logger.warn(`Connection failed, retrying in ${this.config.retryDelay}ms (attempt ${this.currentRetryCount}/${this.config.retryAttempts})`);
192
+ this.reconnectTimer = setTimeout(() => {
193
+ this.reconnect();
194
+ }, this.config.retryDelay);
195
+ }
196
+ else {
197
+ this.config.logger.error('Max retry attempts reached, giving up');
198
+ process.exit(1);
199
+ }
200
+ }
201
+ async reconnect() {
202
+ try {
203
+ await this.connect();
204
+ }
205
+ catch (error) {
206
+ this.config.logger.error({ error }, 'Reconnection failed');
207
+ this.handleConnectionError();
208
+ }
209
+ }
210
+ async disconnect() {
211
+ this.config.logger.info('Disconnecting from Cellium MCP Server');
212
+ this.isConnected = false;
213
+ if (this.reconnectTimer) {
214
+ clearTimeout(this.reconnectTimer);
215
+ this.reconnectTimer = undefined;
216
+ }
217
+ await this.localServer.close();
218
+ this.config.logger.info('Disconnected successfully');
219
+ }
220
+ }
221
+ exports.CelliumMCPClient = CelliumMCPClient;
@@ -0,0 +1,2 @@
1
+ export { CelliumMCPClient } from './client';
2
+ export type { CelliumMCPClientConfig } from './client';
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CelliumMCPClient = void 0;
4
+ var client_1 = require("./client");
5
+ Object.defineProperty(exports, "CelliumMCPClient", { enumerable: true, get: function () { return client_1.CelliumMCPClient; } });
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "cellium-mcp-client",
3
+ "version": "1.0.0",
4
+ "description": "MCP client for connecting to remote Cellium processor server",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "cellium-mcp-client": "dist/cli.js"
8
+ },
9
+ "types": "dist/index.d.ts",
10
+ "files": [
11
+ "dist/**/*",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "dev": "tsc --watch",
17
+ "test": "echo \"No tests yet\"",
18
+ "prepublishOnly": "npm run build"
19
+ },
20
+ "keywords": [
21
+ "mcp",
22
+ "model-context-protocol",
23
+ "cellium",
24
+ "client"
25
+ ],
26
+ "author": "Cellium Team",
27
+ "license": "MIT",
28
+ "dependencies": {
29
+ "@modelcontextprotocol/sdk": "^1.26.0",
30
+ "commander": "^12.1.0",
31
+ "eventsource": "^2.0.2",
32
+ "pino": "^9.6.0",
33
+ "pino-pretty": "^13.1.3",
34
+ "zod": "^4.3.6"
35
+ },
36
+ "devDependencies": {
37
+ "@types/eventsource": "^1.1.15",
38
+ "@types/node": "^20.17.10",
39
+ "typescript": "^5.7.2"
40
+ },
41
+ "engines": {
42
+ "node": ">=18.0.0"
43
+ },
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "https://github.com/cellium/mcp-client.git"
47
+ }
48
+ }