genudo-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 +2 -0
- package/index.js +92 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -185,6 +185,8 @@ node index.js
|
|
|
185
185
|
| `GENUDO_API_KEY` | **Yes** | - | Your Genudo API key from account settings |
|
|
186
186
|
| `GENUDO_BASE_URL` | No | `https://api.genudo.ai` | Base URL for self-hosted Genudo instances |
|
|
187
187
|
| `GENUDO_ALLOW_INSECURE_SSL` | No | `false` | Allow self-signed SSL certificates (local development only) |
|
|
188
|
+
| `GENUDO_REQUEST_TIMEOUT` | No | `8000` | Per-attempt request timeout in ms before retrying |
|
|
189
|
+
| `GENUDO_REQUEST_RETRIES` | No | `4` | Number of attempts for a request before giving up |
|
|
188
190
|
|
|
189
191
|
## Development
|
|
190
192
|
|
package/index.js
CHANGED
|
@@ -11,6 +11,36 @@ const SSE_URL = `${BASE_URL}/api/user/mcp/sse`;
|
|
|
11
11
|
const API_KEY = process.env.GENUDO_API_KEY;
|
|
12
12
|
const ALLOW_INSECURE_SSL = process.env.GENUDO_ALLOW_INSECURE_SSL === 'true';
|
|
13
13
|
|
|
14
|
+
// The Genudo backend can be slow to answer the first request after connecting
|
|
15
|
+
// (cold start — observed 1s to 30s+). Rather than let one slow attempt hang the
|
|
16
|
+
// whole MCP handshake, we time out each attempt fast and retry.
|
|
17
|
+
const REQUEST_TIMEOUT = parseInt(process.env.GENUDO_REQUEST_TIMEOUT || '8000', 10);
|
|
18
|
+
const REQUEST_RETRIES = parseInt(process.env.GENUDO_REQUEST_RETRIES || '4', 10);
|
|
19
|
+
|
|
20
|
+
// Server-level guidance returned in the `initialize` handshake. MCP clients
|
|
21
|
+
// (Claude Code, Codex, Cursor, ...) inject this into the model's context, so it
|
|
22
|
+
// teaches any client how to get value fast and avoid the common failure modes —
|
|
23
|
+
// without the user having to write prompts. Keep it concise and high-signal.
|
|
24
|
+
const SERVER_INSTRUCTIONS = [
|
|
25
|
+
'Genudo MCP: control + analytics for AI sales/support pipelines (pipelines, stages, actions/webhooks, variables, contacts, opportunities, messages, analytics). 21 tools, read + write.',
|
|
26
|
+
'',
|
|
27
|
+
'PICK A PATTERN:',
|
|
28
|
+
'- Audit a pipeline: list_pipelines -> list_pipeline_stages -> list_variables -> list_opportunities -> list_contacts -> list_messages',
|
|
29
|
+
'- Build from scratch: start_pipeline_journey (ALWAYS first) -> get_pipeline_options (valid IDs) -> create_pipeline -> create_stage (xN) -> create_variable -> create_action',
|
|
30
|
+
'- Add an integration: list_pipelines -> list_pipeline_stages -> list_variables -> create_variable -> create_action',
|
|
31
|
+
'- Report activity: get_account_summary -> get_ai_performance -> list_opportunities -> get_messaging_stats',
|
|
32
|
+
'',
|
|
33
|
+
'RULES THAT PREVENT FAILURES:',
|
|
34
|
+
'1. Discover IDs before writing: get_pipeline_options (agent_type_id, ai_model_id, language_id, channel_id); list_pipelines (pipeline_id); list_pipeline_stages (stage_id); list_variables (variable_id).',
|
|
35
|
+
'2. Actions CANNOT use raw system placeholders. Never put {{opportunity.contact_email}} in an action url/headers/payload. Instead create_variable {type:"from_system", value:"opportunity.contact_email"} and reference {{its_name}}. Variable types: fixed | from_system | from_action | from_ai.',
|
|
36
|
+
'3. create_pipeline: if is_model_routing_enabled=true, model_pool is required with exactly 4 tiers (router, simple, moderate, complex). persona + instructions drive quality — ask the user for a 1-2 sentence business description, then offer to write them.',
|
|
37
|
+
'4. create_stage nature in {neutral, won, lost}. create_action fixed_trigger in {stage_started, on_any_message, on_user_message, custom}; omit stage_id for a pipeline-wide action.',
|
|
38
|
+
'5. Immutable after create: pipeline agent_type; action fixed_trigger can only change to on_user_message/custom on update; update_opportunities stage moves must stay within the same pipeline.',
|
|
39
|
+
'6. Not exposed (do not attempt): listing actions, deleting actions/stages/pipelines, KB management, sending manual messages, reading plan limits.',
|
|
40
|
+
'',
|
|
41
|
+
'Confirm before bulk writes (update_opportunities is bulk). The backend can be slow on the first call — retries are automatic.'
|
|
42
|
+
].join('\n');
|
|
43
|
+
|
|
14
44
|
// HTTPS agent configuration
|
|
15
45
|
// For local development with self-signed certificates, set GENUDO_ALLOW_INSECURE_SSL=true
|
|
16
46
|
const httpsAgent = new https.Agent({
|
|
@@ -80,26 +110,46 @@ async function forwardRequest(jsonRpcRequest) {
|
|
|
80
110
|
|
|
81
111
|
debug('Forwarding request:', jsonRpcRequest.method, 'id:', jsonRpcRequest.id);
|
|
82
112
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
113
|
+
let lastError;
|
|
114
|
+
for (let attempt = 1; attempt <= REQUEST_RETRIES; attempt++) {
|
|
115
|
+
const controller = new AbortController();
|
|
116
|
+
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT);
|
|
117
|
+
try {
|
|
118
|
+
const response = await fetch(messageEndpoint, {
|
|
119
|
+
method: 'POST',
|
|
120
|
+
headers: {
|
|
121
|
+
'Content-Type': 'application/json',
|
|
122
|
+
'Api-Key': API_KEY
|
|
123
|
+
},
|
|
124
|
+
body: JSON.stringify(jsonRpcRequest),
|
|
125
|
+
agent: httpsAgent,
|
|
126
|
+
signal: controller.signal
|
|
127
|
+
});
|
|
128
|
+
clearTimeout(timer);
|
|
92
129
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
130
|
+
if (!response.ok) {
|
|
131
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
132
|
+
}
|
|
96
133
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
134
|
+
// Handle 204 No Content (for notifications)
|
|
135
|
+
if (response.status === 204) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
101
138
|
|
|
102
|
-
|
|
139
|
+
return await response.json();
|
|
140
|
+
} catch (error) {
|
|
141
|
+
clearTimeout(timer);
|
|
142
|
+
lastError = error;
|
|
143
|
+
const reason = error.name === 'AbortError'
|
|
144
|
+
? `timeout after ${REQUEST_TIMEOUT}ms`
|
|
145
|
+
: error.message;
|
|
146
|
+
debug(`Attempt ${attempt}/${REQUEST_RETRIES} failed (${reason})`);
|
|
147
|
+
if (attempt < REQUEST_RETRIES) {
|
|
148
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
throw lastError;
|
|
103
153
|
}
|
|
104
154
|
|
|
105
155
|
/**
|
|
@@ -110,6 +160,31 @@ async function processInput(line) {
|
|
|
110
160
|
const request = JSON.parse(line);
|
|
111
161
|
debug('Received request:', request.method);
|
|
112
162
|
|
|
163
|
+
// Answer the MCP handshake locally so Claude's startup never blocks on
|
|
164
|
+
// backend cold-start latency. The Genudo server accepts tool calls without a
|
|
165
|
+
// forwarded initialize (verified), so we synthesize the response and warm the
|
|
166
|
+
// backend in the background for the first real request.
|
|
167
|
+
if (request.method === 'initialize') {
|
|
168
|
+
console.log(JSON.stringify({
|
|
169
|
+
jsonrpc: '2.0',
|
|
170
|
+
id: request.id,
|
|
171
|
+
result: {
|
|
172
|
+
protocolVersion: (request.params && request.params.protocolVersion) || '2024-11-05',
|
|
173
|
+
capabilities: { tools: {} },
|
|
174
|
+
serverInfo: { name: 'Genudo', version: '1.0.2' },
|
|
175
|
+
instructions: SERVER_INSTRUCTIONS
|
|
176
|
+
}
|
|
177
|
+
}));
|
|
178
|
+
forwardRequest({ jsonrpc: '2.0', id: 'warmup', method: 'tools/list', params: {} })
|
|
179
|
+
.catch(() => {});
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Handshake is handled locally, so there is no server session to notify.
|
|
184
|
+
if (request.method === 'notifications/initialized') {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
113
188
|
const response = await forwardRequest(request);
|
|
114
189
|
|
|
115
190
|
// Only write response if there is one (notifications don't get responses)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genudo-mcp-client",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "A lightweight bridge connecting Claude Code to Genudo's Model Context Protocol (MCP) server for AI-powered workflow automation",
|
|
5
5
|
"mcpName": "io.github.genudo-ai/genudo_mcp",
|
|
6
6
|
"main": "index.js",
|