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 +21 -0
- package/README.md +181 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +83 -0
- package/dist/client.d.ts +23 -0
- package/dist/client.js +221 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -0
- package/package.json +48 -0
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
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
|
+
});
|
package/dist/client.d.ts
ADDED
|
@@ -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;
|
package/dist/index.d.ts
ADDED
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
|
+
}
|