workfullcircle-mcp-local 1.3.1 → 1.4.1
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/index.js +68 -32
- package/package.json +2 -2
- package/test.js +0 -98
package/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const fetch = require('node-fetch');
|
|
4
|
+
const https = require('https');
|
|
5
|
+
const { URL } = require('url');
|
|
4
6
|
|
|
5
7
|
// Parse command line arguments
|
|
6
8
|
const args = process.argv.slice(2);
|
|
7
9
|
|
|
8
10
|
function printUsage() {
|
|
9
|
-
process.stderr.write('Usage: workfullcircle-mcp-local --token <api-token> [--url <api-url>]\n');
|
|
10
|
-
process.stderr.write('Environment variables: WFC_API_URL (default: https://workfullcircle.com)\n');
|
|
11
11
|
process.exit(1);
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -33,7 +33,6 @@ function parseArgs(argv) {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
if (!parsed.token) {
|
|
36
|
-
process.stderr.write('Error: --token argument or WFC_TOKEN environment variable is required\n');
|
|
37
36
|
printUsage();
|
|
38
37
|
}
|
|
39
38
|
|
|
@@ -42,6 +41,58 @@ function parseArgs(argv) {
|
|
|
42
41
|
|
|
43
42
|
const { token: API_TOKEN, url: API_URL } = parseArgs(args);
|
|
44
43
|
|
|
44
|
+
// Create HTTPS agent with keepAlive disabled to prevent stale connections
|
|
45
|
+
const httpsAgent = new https.Agent({
|
|
46
|
+
keepAlive: false, // Force fresh connection for each request
|
|
47
|
+
timeout: 10000 // 10 second timeout
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Helper function to make HTTP requests with retry logic
|
|
51
|
+
async function makeRequest(url, payload, retryCount = 0) {
|
|
52
|
+
const controller = new AbortController();
|
|
53
|
+
const timeout = setTimeout(() => controller.abort(), 15000); // 15s timeout
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
const response = await fetch(url, {
|
|
57
|
+
method: 'POST',
|
|
58
|
+
headers: {
|
|
59
|
+
'Content-Type': 'application/json',
|
|
60
|
+
'Authorization': `Bearer ${API_TOKEN}`,
|
|
61
|
+
'Connection': 'close' // Explicitly request connection close
|
|
62
|
+
},
|
|
63
|
+
body: JSON.stringify(payload),
|
|
64
|
+
agent: httpsAgent,
|
|
65
|
+
signal: controller.signal
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
clearTimeout(timeout);
|
|
69
|
+
|
|
70
|
+
if (!response.ok) {
|
|
71
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const responseText = await response.text();
|
|
75
|
+
|
|
76
|
+
// Validate response is valid JSON
|
|
77
|
+
try {
|
|
78
|
+
JSON.parse(responseText);
|
|
79
|
+
return responseText;
|
|
80
|
+
} catch (jsonError) {
|
|
81
|
+
throw new Error('Invalid JSON response from server');
|
|
82
|
+
}
|
|
83
|
+
} catch (error) {
|
|
84
|
+
clearTimeout(timeout);
|
|
85
|
+
|
|
86
|
+
// If it's a timeout or connection error, retry once
|
|
87
|
+
if ((error.name === 'AbortError' || error.code === 'ECONNRESET' || error.message.includes('timeout')) && retryCount === 0) {
|
|
88
|
+
console.error(`Request failed, retrying... (${error.message})`);
|
|
89
|
+
return makeRequest(url, payload, retryCount + 1);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
45
96
|
// Forward all MCP requests directly to the server's MCP endpoint
|
|
46
97
|
async function handleStdio() {
|
|
47
98
|
let inputBuffer = '';
|
|
@@ -63,46 +114,31 @@ async function handleStdio() {
|
|
|
63
114
|
try {
|
|
64
115
|
payload = JSON.parse(line);
|
|
65
116
|
} catch (error) {
|
|
66
|
-
process.stderr.write(`[WFC-Local] Invalid JSON input: ${error.message}\n`);
|
|
67
117
|
continue;
|
|
68
118
|
}
|
|
69
119
|
|
|
70
120
|
try {
|
|
71
|
-
// Forward the MCP request directly to the server
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
headers: {
|
|
75
|
-
'Content-Type': 'application/json',
|
|
76
|
-
'Authorization': `Bearer ${API_TOKEN}`
|
|
77
|
-
},
|
|
78
|
-
body: JSON.stringify(payload)
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
const responseText = await response.text();
|
|
82
|
-
|
|
83
|
-
if (!response.ok) {
|
|
84
|
-
process.stderr.write(`[WFC-Local] Remote error ${response.status}: ${responseText}\n`);
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Validate response is valid JSON before outputting
|
|
89
|
-
try {
|
|
90
|
-
JSON.parse(responseText);
|
|
91
|
-
process.stdout.write(`${responseText}\n`);
|
|
92
|
-
} catch (jsonError) {
|
|
93
|
-
process.stderr.write(`[WFC-Local] Invalid JSON response: ${jsonError.message}\n`);
|
|
94
|
-
process.stderr.write(`[WFC-Local] Response was: ${responseText.substring(0, 200)}...\n`);
|
|
95
|
-
}
|
|
121
|
+
// Forward the MCP request directly to the server using the robust request function
|
|
122
|
+
const responseText = await makeRequest(API_URL, payload);
|
|
123
|
+
process.stdout.write(`${responseText}\n`);
|
|
96
124
|
} catch (error) {
|
|
97
|
-
|
|
125
|
+
console.error(`MCP request failed: ${error.message}`);
|
|
126
|
+
// Send error response back to MCP client
|
|
127
|
+
const errorResponse = JSON.stringify({
|
|
128
|
+
jsonrpc: "2.0",
|
|
129
|
+
id: payload.id || null,
|
|
130
|
+
error: {
|
|
131
|
+
code: -32603,
|
|
132
|
+
message: `Request failed: ${error.message}`
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
process.stdout.write(`${errorResponse}\n`);
|
|
98
136
|
}
|
|
99
137
|
}
|
|
100
138
|
});
|
|
101
139
|
}
|
|
102
140
|
|
|
103
141
|
async function main() {
|
|
104
|
-
process.stderr.write(`[WFC-Local] Connected to MCP server at ${API_URL}\n`);
|
|
105
|
-
process.stderr.write(`[WFC-Local] Using token: ${API_TOKEN.substring(0, 10)}...\n`);
|
|
106
142
|
await handleStdio();
|
|
107
143
|
}
|
|
108
144
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package",
|
|
3
3
|
"name": "workfullcircle-mcp-local",
|
|
4
|
-
"version": "1.
|
|
5
|
-
"description": "Local STDIO MCP server for WorkFullCircle API with
|
|
4
|
+
"version": "1.4.1",
|
|
5
|
+
"description": "Local STDIO MCP server for WorkFullCircle API with connection leak fixes",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"workfullcircle-mcp-local": "index.js"
|
package/test.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
const { spawn } = require('child_process');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
|
|
4
|
-
// Test configuration
|
|
5
|
-
const TEST_API_KEY = process.env.TEST_API_KEY || 'uam_test_key';
|
|
6
|
-
const TEST_API_URL = process.env.TEST_API_URL || process.env.WFC_API_URL || 'https://workfullcircle.com/api';
|
|
7
|
-
|
|
8
|
-
console.log('Testing @workfullcircle/mcp-local...');
|
|
9
|
-
console.log('API URL:', TEST_API_URL);
|
|
10
|
-
console.log('API Key:', TEST_API_KEY.substring(0, 10) + '...');
|
|
11
|
-
|
|
12
|
-
// Spawn the MCP server
|
|
13
|
-
const mcpServer = spawn('node', [path.join(__dirname, 'index.js')], {
|
|
14
|
-
env: {
|
|
15
|
-
...process.env,
|
|
16
|
-
WFC_API_URL: TEST_API_URL,
|
|
17
|
-
WFC_API_KEY: TEST_API_KEY
|
|
18
|
-
},
|
|
19
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
let responseBuffer = '';
|
|
23
|
-
|
|
24
|
-
mcpServer.stdout.on('data', (data) => {
|
|
25
|
-
responseBuffer += data.toString();
|
|
26
|
-
|
|
27
|
-
// Try to parse complete JSON responses
|
|
28
|
-
const lines = responseBuffer.split('\n');
|
|
29
|
-
for (let i = 0; i < lines.length - 1; i++) {
|
|
30
|
-
const line = lines[i].trim();
|
|
31
|
-
if (line) {
|
|
32
|
-
try {
|
|
33
|
-
const response = JSON.parse(line);
|
|
34
|
-
console.log('Response:', JSON.stringify(response, null, 2));
|
|
35
|
-
} catch (e) {
|
|
36
|
-
console.log('Non-JSON output:', line);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
responseBuffer = lines[lines.length - 1];
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
mcpServer.stderr.on('data', (data) => {
|
|
44
|
-
console.error('Error:', data.toString());
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
mcpServer.on('close', (code) => {
|
|
48
|
-
console.log('Server exited with code:', code);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
// Send test requests
|
|
52
|
-
setTimeout(() => {
|
|
53
|
-
console.log('Sending initialize request...');
|
|
54
|
-
mcpServer.stdin.write(JSON.stringify({
|
|
55
|
-
jsonrpc: '2.0',
|
|
56
|
-
id: 1,
|
|
57
|
-
method: 'initialize',
|
|
58
|
-
params: {
|
|
59
|
-
protocolVersion: '2024-11-05',
|
|
60
|
-
capabilities: {},
|
|
61
|
-
clientInfo: {
|
|
62
|
-
name: 'test-client',
|
|
63
|
-
version: '1.0.0'
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}) + '\n');
|
|
67
|
-
}, 1000);
|
|
68
|
-
|
|
69
|
-
setTimeout(() => {
|
|
70
|
-
console.log('Sending tools/list request...');
|
|
71
|
-
mcpServer.stdin.write(JSON.stringify({
|
|
72
|
-
jsonrpc: '2.0',
|
|
73
|
-
id: 2,
|
|
74
|
-
method: 'tools/list'
|
|
75
|
-
}) + '\n');
|
|
76
|
-
}, 2000);
|
|
77
|
-
|
|
78
|
-
setTimeout(() => {
|
|
79
|
-
console.log('Sending tools/call request...');
|
|
80
|
-
mcpServer.stdin.write(JSON.stringify({
|
|
81
|
-
jsonrpc: '2.0',
|
|
82
|
-
id: 3,
|
|
83
|
-
method: 'tools/call',
|
|
84
|
-
params: {
|
|
85
|
-
name: 'save_memory',
|
|
86
|
-
arguments: {
|
|
87
|
-
content: 'Test memory from MCP local',
|
|
88
|
-
sector: 'Semantic',
|
|
89
|
-
tags: ['test', 'mcp-local']
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}) + '\n');
|
|
93
|
-
}, 3000);
|
|
94
|
-
|
|
95
|
-
setTimeout(() => {
|
|
96
|
-
console.log('Terminating...');
|
|
97
|
-
mcpServer.kill();
|
|
98
|
-
}, 5000);
|