@cloudstreamsoftware/knowledge-mcp-client 1.0.0 → 1.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 +4 -4
- package/dist/bridge.d.ts +2 -1
- package/dist/bridge.d.ts.map +1 -1
- package/dist/bridge.js +6 -21
- package/dist/index.js +61 -39
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @cloudstreamsoftware/knowledge-mcp-client
|
|
2
2
|
|
|
3
3
|
MCP client bridge for CloudStream Knowledge Server. Translates Claude Code's stdio-based MCP protocol to HTTP requests against the Cloudflare Knowledge Server.
|
|
4
4
|
|
|
@@ -26,7 +26,7 @@ Add to `~/.claude/settings.json`:
|
|
|
26
26
|
"mcpServers": {
|
|
27
27
|
"cloudstream-knowledge": {
|
|
28
28
|
"command": "npx",
|
|
29
|
-
"args": ["@
|
|
29
|
+
"args": ["@cloudstreamsoftware/knowledge-mcp-client"],
|
|
30
30
|
"env": {
|
|
31
31
|
"CLOUDSTREAM_LICENSE": "your-license-key",
|
|
32
32
|
"CLOUDSTREAM_API": "https://cloudstream-knowledge.broken-rain-0984.workers.dev"
|
|
@@ -64,7 +64,7 @@ When the Knowledge Server is unavailable, the bridge returns helpful error messa
|
|
|
64
64
|
Enable debug logging:
|
|
65
65
|
|
|
66
66
|
```bash
|
|
67
|
-
CLOUDSTREAM_DEBUG=true npx @
|
|
67
|
+
CLOUDSTREAM_DEBUG=true npx @cloudstreamsoftware/knowledge-mcp-client
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
Debug output goes to stderr (doesn't interfere with JSON-RPC protocol on stdout).
|
|
@@ -75,7 +75,7 @@ Debug output goes to stderr (doesn't interfere with JSON-RPC protocol on stdout)
|
|
|
75
75
|
# Test initialize (no network required)
|
|
76
76
|
echo '{"jsonrpc":"2.0","id":1,"method":"initialize"}' | \
|
|
77
77
|
CLOUDSTREAM_LICENSE=test CLOUDSTREAM_API=http://localhost \
|
|
78
|
-
npx @
|
|
78
|
+
npx @cloudstreamsoftware/knowledge-mcp-client
|
|
79
79
|
|
|
80
80
|
# Expected response:
|
|
81
81
|
# {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05",...}}
|
package/dist/bridge.d.ts
CHANGED
|
@@ -31,7 +31,8 @@ export declare class HttpBridge {
|
|
|
31
31
|
*/
|
|
32
32
|
private handleError;
|
|
33
33
|
/**
|
|
34
|
-
* Generate offline mode response
|
|
34
|
+
* Generate offline mode response
|
|
35
|
+
* Per JSON-RPC 2.0, failures must return error, not result
|
|
35
36
|
*/
|
|
36
37
|
private offlineModeResponse;
|
|
37
38
|
/**
|
package/dist/bridge.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAGjF;;;;;;;;GAQG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,eAAe,CAAiC;IACxD,OAAO,CAAC,SAAS,CAAsB;gBAE3B,MAAM,EAAE,YAAY;IAIhC;;OAEG;IACG,OAAO,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC;IAgBxD;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAiBxB;;OAEG;YACW,WAAW;IAwDzB;;OAEG;IACH,OAAO,CAAC,WAAW;IAoBnB
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAGjF;;;;;;;;GAQG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,eAAe,CAAiC;IACxD,OAAO,CAAC,SAAS,CAAsB;gBAE3B,MAAM,EAAE,YAAY;IAIhC;;OAEG;IACG,OAAO,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC;IAgBxD;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAiBxB;;OAEG;YACW,WAAW;IAwDzB;;OAEG;IACH,OAAO,CAAC,WAAW;IAoBnB;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IA8B3B;;OAEG;IACH,OAAO,CAAC,aAAa;IAYrB;;OAEG;IACH,kBAAkB,IAAI,eAAe;IAIrC;;OAEG;IACH,YAAY,IAAI,KAAK,GAAG,IAAI;CAG7B"}
|
package/dist/bridge.js
CHANGED
|
@@ -115,10 +115,11 @@ class HttpBridge {
|
|
|
115
115
|
`Bridge error: ${error.message}`);
|
|
116
116
|
}
|
|
117
117
|
/**
|
|
118
|
-
* Generate offline mode response
|
|
118
|
+
* Generate offline mode response
|
|
119
|
+
* Per JSON-RPC 2.0, failures must return error, not result
|
|
119
120
|
*/
|
|
120
121
|
offlineModeResponse(request) {
|
|
121
|
-
// For tools/list, return empty list
|
|
122
|
+
// For tools/list, return empty list (graceful degradation)
|
|
122
123
|
if (request.method === 'tools/list') {
|
|
123
124
|
return {
|
|
124
125
|
jsonrpc: '2.0',
|
|
@@ -128,27 +129,11 @@ class HttpBridge {
|
|
|
128
129
|
},
|
|
129
130
|
};
|
|
130
131
|
}
|
|
131
|
-
// For tool calls, return
|
|
132
|
+
// For tool calls, return proper JSON-RPC error
|
|
132
133
|
if (request.method === 'tools/call') {
|
|
133
134
|
const toolName = request.params?.name || 'unknown tool';
|
|
134
|
-
return {
|
|
135
|
-
|
|
136
|
-
id: request.id,
|
|
137
|
-
result: {
|
|
138
|
-
content: [
|
|
139
|
-
{
|
|
140
|
-
type: 'text',
|
|
141
|
-
text: `[OFFLINE] The CloudStream Knowledge Server is currently unavailable.\n\n` +
|
|
142
|
-
`Unable to process: ${toolName}\n\n` +
|
|
143
|
-
`Please check:\n` +
|
|
144
|
-
`1. Your internet connection\n` +
|
|
145
|
-
`2. Knowledge Server status at ${this.config.apiUrl}/health\n` +
|
|
146
|
-
`3. Your license key validity\n\n` +
|
|
147
|
-
`Local skills and web search remain available.`,
|
|
148
|
-
},
|
|
149
|
-
],
|
|
150
|
-
},
|
|
151
|
-
};
|
|
135
|
+
return this.errorResponse(request.id, -32003, `Knowledge Server unavailable. Unable to process: ${toolName}. ` +
|
|
136
|
+
`Check your internet connection and try again.`);
|
|
152
137
|
}
|
|
153
138
|
return this.errorResponse(request.id, -32003, 'Knowledge Server unavailable');
|
|
154
139
|
}
|
package/dist/index.js
CHANGED
|
@@ -99,56 +99,78 @@ async function main() {
|
|
|
99
99
|
}
|
|
100
100
|
// Create HTTP bridge
|
|
101
101
|
const bridge = new bridge_1.HttpBridge(config);
|
|
102
|
+
// Track pending async operations to prevent premature exit
|
|
103
|
+
const pendingRequests = new Set();
|
|
104
|
+
let stdinClosed = false;
|
|
102
105
|
// Set up readline interface for stdin
|
|
103
106
|
const rl = readline.createInterface({
|
|
104
107
|
input: process.stdin,
|
|
105
108
|
terminal: false,
|
|
106
109
|
});
|
|
107
110
|
// Process JSON-RPC requests line by line
|
|
108
|
-
rl.on('line',
|
|
111
|
+
rl.on('line', (line) => {
|
|
109
112
|
debug(`Received: ${line.slice(0, 100)}...`);
|
|
110
113
|
if (!line.trim()) {
|
|
111
114
|
return; // Ignore empty lines
|
|
112
115
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
request
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
116
|
+
// Handle request asynchronously but track the promise
|
|
117
|
+
const requestPromise = (async () => {
|
|
118
|
+
let request;
|
|
119
|
+
try {
|
|
120
|
+
request = JSON.parse(line);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// Parse error
|
|
124
|
+
sendResponse({
|
|
125
|
+
jsonrpc: '2.0',
|
|
126
|
+
id: null,
|
|
127
|
+
error: {
|
|
128
|
+
code: -32700,
|
|
129
|
+
message: 'Parse error: Invalid JSON',
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
// Forward to bridge and send response
|
|
135
|
+
try {
|
|
136
|
+
const response = await bridge.forward(request);
|
|
137
|
+
debug(`Response for ${request.method}: ${bridge.getConnectionState()}`);
|
|
138
|
+
sendResponse(response);
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
// Unexpected error - should be handled by bridge
|
|
142
|
+
debug(`Unexpected error: ${error.message}`);
|
|
143
|
+
sendResponse({
|
|
144
|
+
jsonrpc: '2.0',
|
|
145
|
+
id: request.id,
|
|
146
|
+
error: {
|
|
147
|
+
code: -32603,
|
|
148
|
+
message: `Internal error: ${error.message}`,
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
})();
|
|
153
|
+
// Track this request
|
|
154
|
+
pendingRequests.add(requestPromise);
|
|
155
|
+
requestPromise.finally(() => {
|
|
156
|
+
pendingRequests.delete(requestPromise);
|
|
157
|
+
// If stdin is closed and no pending requests, exit
|
|
158
|
+
if (stdinClosed && pendingRequests.size === 0) {
|
|
159
|
+
debug('All requests completed, exiting');
|
|
160
|
+
process.exit(0);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
147
163
|
});
|
|
148
|
-
// Handle stdin close
|
|
149
|
-
rl.on('close', () => {
|
|
150
|
-
debug(
|
|
151
|
-
|
|
164
|
+
// Handle stdin close - wait for pending requests before exiting
|
|
165
|
+
rl.on('close', async () => {
|
|
166
|
+
debug(`stdin closed, ${pendingRequests.size} pending requests`);
|
|
167
|
+
stdinClosed = true;
|
|
168
|
+
// If no pending requests, exit immediately
|
|
169
|
+
if (pendingRequests.size === 0) {
|
|
170
|
+
debug('No pending requests, exiting');
|
|
171
|
+
process.exit(0);
|
|
172
|
+
}
|
|
173
|
+
// Otherwise, requests will exit when they complete (see finally block above)
|
|
152
174
|
});
|
|
153
175
|
// Handle errors
|
|
154
176
|
process.on('uncaughtException', (error) => {
|
package/package.json
CHANGED