@leonailtd/priority-mcp-client 0.5.3 → 0.6.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/README.md CHANGED
@@ -7,18 +7,33 @@ MCP client for Priority ERP that connects Claude Desktop to a remote Priority MC
7
7
  No installation required! Use with `npx`:
8
8
 
9
9
  ```bash
10
- npx @leonailtd/priority-mcp-client --url https://priority-mcp.leonai.io --token sk-cust-xxx ...
10
+ npx @leonailtd/priority-mcp-client --token sk-cust-YOUR_KEY
11
11
  ```
12
12
 
13
13
  ## Usage
14
14
 
15
- ### With Claude Desktop
15
+ ### With Claude Desktop (credentials stored server-side)
16
16
 
17
- Add this configuration to your `claude_desktop_config.json`:
17
+ If you registered via the self-service form, your Priority credentials are already stored on the server. You only need your API key:
18
18
 
19
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
20
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
21
- **Linux:** `~/.config/Claude/claude_desktop_config.json`
19
+ ```json
20
+ {
21
+ "mcpServers": {
22
+ "priority-erp": {
23
+ "command": "npx",
24
+ "args": [
25
+ "@leonailtd/priority-mcp-client",
26
+ "--token",
27
+ "sk-cust-YOUR_API_KEY_HERE"
28
+ ]
29
+ }
30
+ }
31
+ }
32
+ ```
33
+
34
+ ### With Claude Desktop (credentials via CLI)
35
+
36
+ If your credentials are NOT stored server-side, pass them as arguments:
22
37
 
23
38
  ```json
24
39
  {
@@ -27,8 +42,6 @@ Add this configuration to your `claude_desktop_config.json`:
27
42
  "command": "npx",
28
43
  "args": [
29
44
  "@leonailtd/priority-mcp-client",
30
- "--url",
31
- "https://priority-mcp.leonai.io",
32
45
  "--token",
33
46
  "sk-cust-YOUR_API_KEY_HERE",
34
47
  "--priority-url",
@@ -45,21 +58,25 @@ Add this configuration to your `claude_desktop_config.json`:
45
58
  }
46
59
  ```
47
60
 
61
+ ### Config file location
62
+
63
+ **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
64
+ **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
65
+ **Linux:** `~/.config/Claude/claude_desktop_config.json`
66
+
48
67
  ### Command-Line Options
49
68
 
50
69
  | Option | Required | Description |
51
70
  |--------|----------|-------------|
52
- | `--url` | Yes | Priority MCP server URL (e.g., https://priority-mcp.leonai.io) |
53
71
  | `--token` | Yes | Authentication token from tenant provisioning (sk-cust-...) |
54
- | `--priority-url` | Yes | Your Priority ERP base URL |
55
- | `--priority-company` | Yes | Your company code in Priority |
56
- | `--priority-username` | Yes | Priority API username |
57
- | `--priority-password` | Yes | Priority API password |
72
+ | `--url` | No | Server URL (default: https://priority-mcp.leonai.io) |
73
+ | `--priority-url` | No | Your Priority ERP base URL (optional if stored server-side) |
74
+ | `--priority-company` | No | Your company code in Priority (optional if stored server-side) |
75
+ | `--priority-username` | No | Priority API username (optional if stored server-side) |
76
+ | `--priority-password` | No | Priority API password (optional if stored server-side) |
77
+ | `--priority-tabulaini` | No | Tabula.ini profile (default: tabula.ini) |
58
78
  | `--debug` | No | Enable debug logging to stderr |
59
-
60
- ### Getting Your API Token
61
-
62
- Contact your Priority MCP administrator to provision a tenant and receive your API key.
79
+ | `--verbose` | No | Enable verbose logging (includes request/response details) |
63
80
 
64
81
  ## How It Works
65
82
 
@@ -67,22 +84,23 @@ This client acts as a proxy between Claude Desktop (stdio transport) and the rem
67
84
 
68
85
  ```
69
86
  Claude Desktop (stdio)
70
-
87
+ |
71
88
  Priority MCP Client (this package)
72
-
89
+ |
73
90
  Remote Priority MCP Server (HTTP/SSE)
74
-
91
+ |
75
92
  Priority ERP API
76
93
  ```
77
94
 
78
95
  ## Features
79
96
 
80
- - No installation required (uses npx)
81
- - Works with Claude Desktop out of the box
82
- - Secure authentication with API keys
83
- - Supports all MCP protocol features (tools, resources, prompts)
84
- - Debug logging available
85
- - Cross-platform (Windows, macOS, Linux)
97
+ - No installation required (uses npx)
98
+ - Works with Claude Desktop out of the box
99
+ - Secure authentication with API keys
100
+ - Server-stored credentials (no passwords in config files)
101
+ - Supports all MCP protocol features (tools, resources, prompts)
102
+ - Debug logging available
103
+ - Cross-platform (Windows, macOS, Linux)
86
104
 
87
105
  ## Troubleshooting
88
106
 
@@ -90,25 +108,22 @@ Priority ERP API
90
108
 
91
109
  If you see connection errors:
92
110
 
93
- 1. Verify the `--url` is correct
111
+ 1. Verify the server is running: `curl https://priority-mcp.leonai.io/health`
94
112
  2. Check your `--token` is valid
95
- 3. Ensure Priority credentials are correct
96
- 4. Try with `--debug` flag to see detailed logs
97
-
98
- ### stdio Communication Errors
113
+ 3. Try with `--debug` flag to see detailed logs
99
114
 
100
- If Claude Desktop can't connect:
115
+ ### Claude Desktop Can't Connect
101
116
 
102
117
  1. Restart Claude Desktop completely
103
118
  2. Verify the config file syntax (valid JSON)
104
- 3. Check Node.js is installed (`node --version`)
119
+ 3. Check Node.js is installed (`node --version`, requires >= 18)
105
120
 
106
121
  ### Authentication Errors
107
122
 
108
123
  If you get 401/403 errors:
109
124
 
110
125
  1. Verify your API key is active (not expired/revoked)
111
- 2. Check Priority credentials work directly in Priority
126
+ 2. If using CLI credentials, check they work directly in Priority
112
127
  3. Confirm Priority URL is accessible
113
128
 
114
129
  ## Support
package/dist/bin.js CHANGED
@@ -1,298 +1,2 @@
1
1
  #!/usr/bin/env node
2
-
3
- // src/bin.ts
4
- import { program } from "commander";
5
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
6
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
7
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
- import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
9
- import {
10
- ListToolsRequestSchema,
11
- CallToolRequestSchema,
12
- ListResourcesRequestSchema,
13
- ReadResourceRequestSchema,
14
- ListPromptsRequestSchema,
15
- GetPromptRequestSchema
16
- } from "@modelcontextprotocol/sdk/types.js";
17
- var MCP_SERVER_URL = "https://priority-mcp.leonai.io/";
18
- var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
19
- function isRetryableError(error) {
20
- if (!error) return false;
21
- const errorStr = String(error).toLowerCase();
22
- const retryablePatterns = [
23
- "econnreset",
24
- "econnrefused",
25
- "etimedout",
26
- "timeout",
27
- "network",
28
- "socket hang up",
29
- "502",
30
- "503",
31
- "504"
32
- ];
33
- return retryablePatterns.some((pattern) => errorStr.includes(pattern));
34
- }
35
- async function retryWithBackoff(fn, maxRetries = 3, baseDelay = 1e3, debug = false) {
36
- let lastError;
37
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
38
- try {
39
- return await fn();
40
- } catch (error) {
41
- lastError = error;
42
- if (attempt >= maxRetries) {
43
- throw error;
44
- }
45
- if (!isRetryableError(error)) {
46
- throw error;
47
- }
48
- const delay = Math.min(baseDelay * Math.pow(2, attempt), 3e4);
49
- const jitter = Math.random() * 0.25 * delay;
50
- const totalDelay = Math.floor(delay + jitter);
51
- if (debug) {
52
- console.error(`[DEBUG] Retry attempt ${attempt + 1}/${maxRetries} after ${totalDelay}ms`);
53
- }
54
- await sleep(totalDelay);
55
- }
56
- }
57
- throw lastError;
58
- }
59
- var ConnectionMonitor = class {
60
- lastSuccessfulCall = Date.now();
61
- failureCount = 0;
62
- totalCalls = 0;
63
- debug;
64
- constructor(debug = false) {
65
- this.debug = debug;
66
- }
67
- async monitorCall(operation, fn) {
68
- this.totalCalls++;
69
- const startTime = Date.now();
70
- try {
71
- const result = await fn();
72
- this.lastSuccessfulCall = Date.now();
73
- this.failureCount = 0;
74
- const duration = Date.now() - startTime;
75
- if (this.debug && duration > 5e3) {
76
- console.error(`[WARNING] Slow operation: ${operation} took ${duration}ms`);
77
- }
78
- return result;
79
- } catch (error) {
80
- this.failureCount++;
81
- const timeSinceSuccess = Date.now() - this.lastSuccessfulCall;
82
- if (timeSinceSuccess > 6e4 && this.failureCount >= 3) {
83
- console.error("[WARNING] Connection appears degraded");
84
- console.error(`[WARNING] ${this.failureCount} failures in the last ${Math.floor(timeSinceSuccess / 1e3)}s`);
85
- }
86
- throw error;
87
- }
88
- }
89
- };
90
- function getErrorHint(error, context) {
91
- const errorStr = String(error).toLowerCase();
92
- if (errorStr.includes("401") || errorStr.includes("unauthorized")) {
93
- return "Check your API token (--token sk-cust-...)";
94
- }
95
- if (errorStr.includes("403") || errorStr.includes("forbidden")) {
96
- return "Token is valid but lacks permission. Contact your administrator.";
97
- }
98
- if (errorStr.includes("404")) {
99
- return "Server endpoint not found. Contact info@leonai.io if issue persists.";
100
- }
101
- if (errorStr.includes("timeout") || errorStr.includes("etimedout")) {
102
- return "Request timed out. Server may be slow or overloaded.";
103
- }
104
- if (errorStr.includes("econnrefused")) {
105
- return "Connection refused. Verify server is running and URL is correct.";
106
- }
107
- if (errorStr.includes("econnreset") || errorStr.includes("socket hang up")) {
108
- return "Connection lost. Check network connectivity.";
109
- }
110
- if (errorStr.includes("dns") || errorStr.includes("getaddrinfo")) {
111
- return "DNS lookup failed. Check server URL spelling.";
112
- }
113
- return "See error details above";
114
- }
115
- program.name("priority-mcp-client").description("MCP client that proxies Claude Desktop to Priority MCP server").requiredOption("--token <token>", "Authentication token (sk-cust-...)").requiredOption("--priority-url <url>", "Priority ERP base URL").requiredOption("--priority-company <company>", "Priority company name").requiredOption("--priority-username <username>", "Priority API username").requiredOption("--priority-password <password>", "Priority API password").option("--priority-tabulaini <tabulaini>", "Priority tabula.ini file name (default: tabula.ini)", "tabula.ini").option("--debug", "Enable debug logging").option("--verbose", "Enable verbose logging (includes request/response details)").parse();
116
- var options = program.opts();
117
- async function main() {
118
- try {
119
- const debug = options.debug || options.verbose || false;
120
- const verbose = options.verbose || false;
121
- if (debug) {
122
- console.error("[DEBUG] Starting Priority MCP Client v1.0.0");
123
- console.error(`[DEBUG] Server URL: ${MCP_SERVER_URL}`);
124
- }
125
- if (debug) {
126
- console.error("[DEBUG] Performing health check...");
127
- }
128
- try {
129
- const healthUrl = `${MCP_SERVER_URL}/health`;
130
- const healthResponse = await retryWithBackoff(
131
- async () => {
132
- const response = await fetch(healthUrl);
133
- if (!response.ok) {
134
- throw new Error(`Health check failed: ${response.status} ${response.statusText}`);
135
- }
136
- return response.json();
137
- },
138
- 2,
139
- // 2 retries for health check
140
- 500,
141
- // 500ms base delay
142
- debug
143
- );
144
- if (debug) {
145
- console.error("[DEBUG] Health check passed:", JSON.stringify(healthResponse));
146
- }
147
- if (healthResponse.status !== "healthy") {
148
- console.error("[WARNING] Server health check returned non-healthy status");
149
- console.error("[WARNING] Service may be degraded");
150
- }
151
- } catch (error) {
152
- console.error("[ERROR] Server health check failed");
153
- console.error("[ERROR]", error);
154
- console.error("[HINT]", getErrorHint(error, "health check"));
155
- console.error("[HINT] Verify server is running: curl", `${MCP_SERVER_URL}/health`);
156
- process.exit(1);
157
- }
158
- const connectionMonitor = new ConnectionMonitor(debug);
159
- const client = new Client({
160
- name: "priority-mcp-client",
161
- version: "1.0.0"
162
- }, {
163
- capabilities: {}
164
- });
165
- const crypto = await import("crypto");
166
- const tenantIdSource = `${options.priorityCompany.toLowerCase()}@${options.priorityUrl.toLowerCase()}`;
167
- const tenantId = crypto.createHash("sha256").update(tenantIdSource).digest("hex").substring(0, 16);
168
- const headers = {
169
- "User-Agent": "leonai-priority-mcp-client",
170
- "Authorization": `Bearer ${options.token}`,
171
- "X-Tenant-ID": tenantId,
172
- // Unique identifier for rate limiting/tracking
173
- "X-Priority-URL": options.priorityUrl,
174
- "X-Priority-Company": options.priorityCompany,
175
- "X-Priority-Tabulaini": options.priorityTabulaini,
176
- "X-Priority-Username": options.priorityUsername,
177
- "X-Priority-Password": options.priorityPassword
178
- };
179
- if (debug) {
180
- console.error("[DEBUG] Headers prepared (credentials hidden)");
181
- }
182
- const mcpUrl = new URL("/mcp", MCP_SERVER_URL);
183
- const controller = new AbortController();
184
- const timeoutId = setTimeout(() => controller.abort(), 3e4);
185
- const customFetch = async (url, init) => {
186
- const mergedHeaders = { ...headers };
187
- if (init?.headers) {
188
- const sdkHeaders = init.headers instanceof Headers ? Object.fromEntries(init.headers.entries()) : init.headers;
189
- Object.assign(mergedHeaders, sdkHeaders);
190
- }
191
- const fetchInit = {
192
- ...init,
193
- headers: mergedHeaders
194
- };
195
- return fetch(url, fetchInit);
196
- };
197
- const transport = new StreamableHTTPClientTransport(mcpUrl, {
198
- requestInit: {
199
- signal: controller.signal
200
- },
201
- fetch: customFetch
202
- });
203
- try {
204
- await retryWithBackoff(
205
- () => client.connect(transport),
206
- 3,
207
- // 3 retries for connection
208
- 1e3,
209
- // 1s base delay
210
- debug
211
- );
212
- clearTimeout(timeoutId);
213
- if (debug) {
214
- console.error("[DEBUG] Connected to remote Priority MCP server");
215
- }
216
- } catch (error) {
217
- clearTimeout(timeoutId);
218
- console.error("[ERROR] Failed to connect to Priority MCP server");
219
- console.error("[ERROR]", error);
220
- console.error("[HINT]", getErrorHint(error, "connection"));
221
- throw error;
222
- }
223
- const server = new Server({
224
- name: "priority-mcp-proxy",
225
- version: "1.0.0"
226
- }, {
227
- capabilities: {
228
- tools: {},
229
- resources: {},
230
- prompts: {}
231
- }
232
- });
233
- server.setRequestHandler(ListToolsRequestSchema, async () => {
234
- if (debug) console.error("[DEBUG] Forwarding tools/list");
235
- return await connectionMonitor.monitorCall("tools/list", async () => {
236
- return await retryWithBackoff(() => client.listTools(), 2, 500, debug);
237
- });
238
- });
239
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
240
- if (debug) console.error(`[DEBUG] Forwarding tools/call: ${request.params.name}`);
241
- if (verbose) console.error(`[VERBOSE] Tool params:`, JSON.stringify(request.params));
242
- try {
243
- const result = await connectionMonitor.monitorCall(`tools/call/${request.params.name}`, async () => {
244
- return await retryWithBackoff(() => client.callTool(request.params), 3, 1e3, debug);
245
- });
246
- if (verbose) console.error(`[VERBOSE] Tool result:`, JSON.stringify(result).slice(0, 500));
247
- return result;
248
- } catch (error) {
249
- console.error(`[ERROR] Tool call failed: ${request.params.name}`);
250
- console.error(`[HINT]`, getErrorHint(error, "tool call"));
251
- throw error;
252
- }
253
- });
254
- server.setRequestHandler(ListResourcesRequestSchema, async () => {
255
- if (debug) console.error("[DEBUG] Forwarding resources/list");
256
- return await connectionMonitor.monitorCall("resources/list", async () => {
257
- return await retryWithBackoff(() => client.listResources(), 2, 500, debug);
258
- });
259
- });
260
- server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
261
- if (debug) console.error(`[DEBUG] Forwarding resources/read: ${request.params.uri}`);
262
- return await connectionMonitor.monitorCall(`resources/read/${request.params.uri}`, async () => {
263
- return await retryWithBackoff(() => client.readResource(request.params), 3, 1e3, debug);
264
- });
265
- });
266
- server.setRequestHandler(ListPromptsRequestSchema, async () => {
267
- if (debug) console.error("[DEBUG] Forwarding prompts/list");
268
- return await connectionMonitor.monitorCall("prompts/list", async () => {
269
- return await retryWithBackoff(() => client.listPrompts(), 2, 500, debug);
270
- });
271
- });
272
- server.setRequestHandler(GetPromptRequestSchema, async (request) => {
273
- if (debug) console.error(`[DEBUG] Forwarding prompts/get: ${request.params.name}`);
274
- return await connectionMonitor.monitorCall(`prompts/get/${request.params.name}`, async () => {
275
- return await retryWithBackoff(() => client.getPrompt(request.params), 2, 500, debug);
276
- });
277
- });
278
- const stdioTransport = new StdioServerTransport();
279
- await server.connect(stdioTransport);
280
- if (debug) {
281
- console.error("[DEBUG] Stdio server ready, proxy active");
282
- }
283
- process.on("SIGINT", async () => {
284
- if (debug) console.error("[DEBUG] Shutting down...");
285
- await server.close();
286
- await client.close();
287
- process.exit(0);
288
- });
289
- } catch (error) {
290
- console.error("Failed to start Priority MCP client:", error);
291
- process.exit(1);
292
- }
293
- }
294
- main().catch((error) => {
295
- console.error("Fatal error:", error);
296
- process.exit(1);
297
- });
298
- //# sourceMappingURL=bin.js.map
2
+ import{program as e}from"commander";import{Client as r}from"@modelcontextprotocol/sdk/client/index.js";import{Server as o}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as t}from"@modelcontextprotocol/sdk/server/stdio.js";import{StreamableHTTPClientTransport as s}from"@modelcontextprotocol/sdk/client/streamableHttp.js";import{ListToolsRequestSchema as i,CallToolRequestSchema as n,ListResourcesRequestSchema as a,ReadResourceRequestSchema as c,ListPromptsRequestSchema as l,GetPromptRequestSchema as d}from"@modelcontextprotocol/sdk/types.js";var u=e=>new Promise(r=>setTimeout(r,e));function p(e){if(!e)return!1;const r=String(e).toLowerCase();return["econnreset","econnrefused","etimedout","timeout","network","socket hang up","502","503","504"].some(e=>r.includes(e))}async function m(e,r=3,o=1e3,t=!1){let s;for(let i=0;i<=r;i++)try{return await e()}catch(e){if(s=e,i>=r)throw e;if(!p(e))throw e;const n=Math.min(o*Math.pow(2,i),3e4),a=.25*Math.random()*n,c=Math.floor(n+a);t&&console.error(`[DEBUG] Retry attempt ${i+1}/${r} after ${c}ms`),await u(c)}throw s}var y=class{lastSuccessfulCall=Date.now();failureCount=0;totalCalls=0;debug;constructor(e=!1){this.debug=e}async monitorCall(e,r){this.totalCalls++;const o=Date.now();try{const t=await r();this.lastSuccessfulCall=Date.now(),this.failureCount=0;const s=Date.now()-o;return this.debug&&s>5e3&&console.error(`[WARNING] Slow operation: ${e} took ${s}ms`),t}catch(e){this.failureCount++;const r=Date.now()-this.lastSuccessfulCall;throw r>6e4&&this.failureCount>=3&&(console.error("[WARNING] Connection appears degraded"),console.error(`[WARNING] ${this.failureCount} failures in the last ${Math.floor(r/1e3)}s`)),e}}};function h(e,r){const o=String(e).toLowerCase();return o.includes("401")||o.includes("unauthorized")?"Check your API token (--token sk-cust-...)":o.includes("403")||o.includes("forbidden")?"Token is valid but lacks permission. Contact your administrator.":o.includes("404")?"Server endpoint not found. Contact info@leonai.io if issue persists.":o.includes("timeout")||o.includes("etimedout")?"Request timed out. Server may be slow or overloaded.":o.includes("econnrefused")?"Connection refused. Verify server is running and URL is correct.":o.includes("econnreset")||o.includes("socket hang up")?"Connection lost. Check network connectivity.":o.includes("dns")||o.includes("getaddrinfo")?"DNS lookup failed. Check server URL spelling.":"See error details above"}e.name("priority-mcp-client").description("MCP client that proxies Claude Desktop to Priority MCP server").requiredOption("--token <token>","Authentication token (sk-cust-...)").option("--url <url>","Priority MCP server URL","https://priority-mcp.leonai.io").option("--priority-url <url>","Priority ERP base URL (optional if stored server-side)").option("--priority-company <company>","Priority company name (optional if stored server-side)").option("--priority-username <username>","Priority API username (optional if stored server-side)").option("--priority-password <password>","Priority API password (optional if stored server-side)").option("--priority-tabulaini <tabulaini>","Priority tabula.ini profile","tabula.ini").option("--debug","Enable debug logging").option("--verbose","Enable verbose logging (includes request/response details)").parse();var w=e.opts();(async function(){try{const e=w.debug||w.verbose||!1,u=w.verbose||!1,p=w.url.replace(/\/+$/,"");e&&(console.error("[DEBUG] Starting Priority MCP Client v0.6.0"),console.error(`[DEBUG] Server URL: ${p}`),w.priorityUrl?console.error("[DEBUG] Priority credentials provided via CLI args"):console.error("[DEBUG] No Priority credentials in args — using server-stored credentials")),e&&console.error("[DEBUG] Performing health check...");try{const r=`${p}/health`,o=await m(async()=>{const e=await fetch(r);if(!e.ok)throw new Error(`Health check failed: ${e.status} ${e.statusText}`);return e.json()},2,500,e);e&&console.error("[DEBUG] Health check passed:",JSON.stringify(o)),"healthy"!==o.status&&(console.error("[WARNING] Server health check returned non-healthy status"),console.error("[WARNING] Service may be degraded"))}catch(e){console.error("[ERROR] Server health check failed"),console.error("[ERROR]",e),console.error("[HINT]",h(e)),console.error("[HINT] Verify server is running: curl",`${p}/health`),process.exit(1)}const f=new y(e),g=new r({name:"priority-mcp-client",version:"0.6.0"},{capabilities:{}}),C={"User-Agent":"leonai-priority-mcp-client",Authorization:`Bearer ${w.token}`};if(w.priorityUrl){const e=await import("crypto"),r=`${w.priorityCompany?.toLowerCase()||""}@${w.priorityUrl.toLowerCase()}`,o=e.createHash("sha256").update(r).digest("hex").substring(0,16);C["X-Tenant-ID"]=o,C["X-Priority-URL"]=w.priorityUrl,C["X-Priority-Company"]=w.priorityCompany||"",C["X-Priority-Tabulaini"]=w.priorityTabulaini,C["X-Priority-Username"]=w.priorityUsername||"",C["X-Priority-Password"]=w.priorityPassword||""}e&&console.error("[DEBUG] Headers prepared (credentials hidden)");const R=new URL("/mcp",p),v=new AbortController,P=setTimeout(()=>v.abort(),3e4),b=async(e,r)=>{const o={...C};if(r?.headers){const e=r.headers instanceof Headers?Object.fromEntries(r.headers.entries()):r.headers;Object.assign(o,e)}const t={...r,headers:o};return fetch(e,t)},k=new s(R,{requestInit:{signal:v.signal},fetch:b});try{await m(()=>g.connect(k),3,1e3,e),clearTimeout(P),e&&console.error("[DEBUG] Connected to remote Priority MCP server")}catch(e){throw clearTimeout(P),console.error("[ERROR] Failed to connect to Priority MCP server"),console.error("[ERROR]",e),console.error("[HINT]",h(e)),e}const E=new o({name:"priority-mcp-proxy",version:"0.6.0"},{capabilities:{tools:{},resources:{},prompts:{}}});E.setRequestHandler(i,async()=>(e&&console.error("[DEBUG] Forwarding tools/list"),await f.monitorCall("tools/list",async()=>await m(()=>g.listTools(),2,500,e)))),E.setRequestHandler(n,async r=>{e&&console.error(`[DEBUG] Forwarding tools/call: ${r.params.name}`),u&&console.error("[VERBOSE] Tool params:",JSON.stringify(r.params));try{const o=await f.monitorCall(`tools/call/${r.params.name}`,async()=>await m(()=>g.callTool(r.params),3,1e3,e));return u&&console.error("[VERBOSE] Tool result:",JSON.stringify(o).slice(0,500)),o}catch(e){throw console.error(`[ERROR] Tool call failed: ${r.params.name}`),console.error("[HINT]",h(e)),e}}),E.setRequestHandler(a,async()=>(e&&console.error("[DEBUG] Forwarding resources/list"),await f.monitorCall("resources/list",async()=>await m(()=>g.listResources(),2,500,e)))),E.setRequestHandler(c,async r=>(e&&console.error(`[DEBUG] Forwarding resources/read: ${r.params.uri}`),await f.monitorCall(`resources/read/${r.params.uri}`,async()=>await m(()=>g.readResource(r.params),3,1e3,e)))),E.setRequestHandler(l,async()=>(e&&console.error("[DEBUG] Forwarding prompts/list"),await f.monitorCall("prompts/list",async()=>await m(()=>g.listPrompts(),2,500,e)))),E.setRequestHandler(d,async r=>(e&&console.error(`[DEBUG] Forwarding prompts/get: ${r.params.name}`),await f.monitorCall(`prompts/get/${r.params.name}`,async()=>await m(()=>g.getPrompt(r.params),2,500,e))));const U=new t;await E.connect(U),e&&console.error("[DEBUG] Stdio server ready, proxy active"),process.on("SIGINT",async()=>{e&&console.error("[DEBUG] Shutting down..."),await E.close(),await g.close(),process.exit(0)})}catch(e){console.error("Failed to start Priority MCP client:",e),process.exit(1)}})().catch(e=>{console.error("Fatal error:",e),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leonailtd/priority-mcp-client",
3
- "version": "0.5.3",
3
+ "version": "0.6.0",
4
4
  "description": "MCP client for Priority ERP - connects Claude Desktop to remote Priority MCP server",
5
5
  "type": "module",
6
6
  "main": "./dist/bin.js",
@@ -43,11 +43,12 @@
43
43
  "registry": "https://registry.npmjs.org/"
44
44
  },
45
45
  "dependencies": {
46
- "@modelcontextprotocol/sdk": "^1.22.0",
46
+ "@modelcontextprotocol/sdk": "^1.26.0",
47
47
  "commander": "^12.1.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/node": "^22.10.5",
51
+ "terser": "^5.46.0",
51
52
  "tsup": "^8.3.5",
52
53
  "typescript": "^5.7.3"
53
54
  },
package/dist/bin.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/bin.ts"],"sourcesContent":["#!/usr/bin/env node\r\n\r\nimport { program } from 'commander';\r\nimport { Client } from '@modelcontextprotocol/sdk/client/index.js';\r\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\r\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\r\nimport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';\r\nimport {\r\n ListToolsRequestSchema,\r\n CallToolRequestSchema,\r\n ListResourcesRequestSchema,\r\n ReadResourceRequestSchema,\r\n ListPromptsRequestSchema,\r\n GetPromptRequestSchema\r\n} from '@modelcontextprotocol/sdk/types.js';\r\n\r\n// MCP Server URL (hardcoded - customers don't need to know backend infrastructure)\r\nconst MCP_SERVER_URL = 'https://priority-mcp.leonai.io/';\r\n\r\n// Helper: Sleep for specified milliseconds\r\nconst sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));\r\n\r\n// Helper: Check if error is retryable\r\nfunction isRetryableError(error: any): boolean {\r\n if (!error) return false;\r\n\r\n const errorStr = String(error).toLowerCase();\r\n const retryablePatterns = [\r\n 'econnreset',\r\n 'econnrefused',\r\n 'etimedout',\r\n 'timeout',\r\n 'network',\r\n 'socket hang up',\r\n '502',\r\n '503',\r\n '504'\r\n ];\r\n\r\n return retryablePatterns.some(pattern => errorStr.includes(pattern));\r\n}\r\n\r\n// Helper: Retry with exponential backoff\r\nasync function retryWithBackoff<T>(\r\n fn: () => Promise<T>,\r\n maxRetries: number = 3,\r\n baseDelay: number = 1000,\r\n debug: boolean = false\r\n): Promise<T> {\r\n let lastError: any;\r\n\r\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\r\n try {\r\n return await fn();\r\n } catch (error) {\r\n lastError = error;\r\n\r\n if (attempt >= maxRetries) {\r\n throw error;\r\n }\r\n\r\n if (!isRetryableError(error)) {\r\n throw error;\r\n }\r\n\r\n const delay = Math.min(baseDelay * Math.pow(2, attempt), 30000); // Max 30s\r\n const jitter = Math.random() * 0.25 * delay;\r\n const totalDelay = Math.floor(delay + jitter);\r\n\r\n if (debug) {\r\n console.error(`[DEBUG] Retry attempt ${attempt + 1}/${maxRetries} after ${totalDelay}ms`);\r\n }\r\n\r\n await sleep(totalDelay);\r\n }\r\n }\r\n\r\n throw lastError;\r\n}\r\n\r\n// Helper: Connection monitor\r\nclass ConnectionMonitor {\r\n private lastSuccessfulCall: number = Date.now();\r\n private failureCount: number = 0;\r\n private totalCalls: number = 0;\r\n private debug: boolean;\r\n\r\n constructor(debug: boolean = false) {\r\n this.debug = debug;\r\n }\r\n\r\n async monitorCall<T>(operation: string, fn: () => Promise<T>): Promise<T> {\r\n this.totalCalls++;\r\n const startTime = Date.now();\r\n\r\n try {\r\n const result = await fn();\r\n this.lastSuccessfulCall = Date.now();\r\n this.failureCount = 0;\r\n\r\n const duration = Date.now() - startTime;\r\n if (this.debug && duration > 5000) {\r\n console.error(`[WARNING] Slow operation: ${operation} took ${duration}ms`);\r\n }\r\n\r\n return result;\r\n } catch (error) {\r\n this.failureCount++;\r\n\r\n const timeSinceSuccess = Date.now() - this.lastSuccessfulCall;\r\n if (timeSinceSuccess > 60000 && this.failureCount >= 3) {\r\n console.error('[WARNING] Connection appears degraded');\r\n console.error(`[WARNING] ${this.failureCount} failures in the last ${Math.floor(timeSinceSuccess / 1000)}s`);\r\n }\r\n\r\n throw error;\r\n }\r\n }\r\n}\r\n\r\n// Helper: Enhanced error message\r\nfunction getErrorHint(error: any, context: string): string {\r\n const errorStr = String(error).toLowerCase();\r\n\r\n if (errorStr.includes('401') || errorStr.includes('unauthorized')) {\r\n return 'Check your API token (--token sk-cust-...)';\r\n }\r\n if (errorStr.includes('403') || errorStr.includes('forbidden')) {\r\n return 'Token is valid but lacks permission. Contact your administrator.';\r\n }\r\n if (errorStr.includes('404')) {\r\n return 'Server endpoint not found. Contact info@leonai.io if issue persists.';\r\n }\r\n if (errorStr.includes('timeout') || errorStr.includes('etimedout')) {\r\n return 'Request timed out. Server may be slow or overloaded.';\r\n }\r\n if (errorStr.includes('econnrefused')) {\r\n return 'Connection refused. Verify server is running and URL is correct.';\r\n }\r\n if (errorStr.includes('econnreset') || errorStr.includes('socket hang up')) {\r\n return 'Connection lost. Check network connectivity.';\r\n }\r\n if (errorStr.includes('dns') || errorStr.includes('getaddrinfo')) {\r\n return 'DNS lookup failed. Check server URL spelling.';\r\n }\r\n\r\n return 'See error details above';\r\n}\r\n\r\n// Parse command-line arguments\r\nprogram\r\n .name('priority-mcp-client')\r\n .description('MCP client that proxies Claude Desktop to Priority MCP server')\r\n .requiredOption('--token <token>', 'Authentication token (sk-cust-...)')\r\n .requiredOption('--priority-url <url>', 'Priority ERP base URL')\r\n .requiredOption('--priority-company <company>', 'Priority company name')\r\n .requiredOption('--priority-username <username>', 'Priority API username')\r\n .requiredOption('--priority-password <password>', 'Priority API password')\r\n .option('--priority-tabulaini <tabulaini>', 'Priority tabula.ini file name (default: tabula.ini)', 'tabula.ini')\r\n .option('--debug', 'Enable debug logging')\r\n .option('--verbose', 'Enable verbose logging (includes request/response details)')\r\n .parse();\r\n\r\nconst options = program.opts();\r\n\r\nasync function main() {\r\n try {\r\n const debug = options.debug || options.verbose || false;\r\n const verbose = options.verbose || false;\r\n\r\n if (debug) {\r\n console.error('[DEBUG] Starting Priority MCP Client v1.0.0');\r\n console.error(`[DEBUG] Server URL: ${MCP_SERVER_URL}`);\r\n }\r\n\r\n // Health check on startup\r\n if (debug) {\r\n console.error('[DEBUG] Performing health check...');\r\n }\r\n\r\n try {\r\n const healthUrl = `${MCP_SERVER_URL}/health`;\r\n const healthResponse = await retryWithBackoff(\r\n async () => {\r\n const response = await fetch(healthUrl);\r\n if (!response.ok) {\r\n throw new Error(`Health check failed: ${response.status} ${response.statusText}`);\r\n }\r\n return response.json();\r\n },\r\n 2, // 2 retries for health check\r\n 500, // 500ms base delay\r\n debug\r\n );\r\n\r\n if (debug) {\r\n console.error('[DEBUG] Health check passed:', JSON.stringify(healthResponse));\r\n }\r\n\r\n if (healthResponse.status !== 'healthy') {\r\n console.error('[WARNING] Server health check returned non-healthy status');\r\n console.error('[WARNING] Service may be degraded');\r\n }\r\n } catch (error) {\r\n console.error('[ERROR] Server health check failed');\r\n console.error('[ERROR]', error);\r\n console.error('[HINT]', getErrorHint(error, 'health check'));\r\n console.error('[HINT] Verify server is running: curl', `${MCP_SERVER_URL}/health`);\r\n process.exit(1);\r\n }\r\n\r\n // Initialize connection monitor\r\n const connectionMonitor = new ConnectionMonitor(debug);\r\n\r\n // Create MCP client that will connect to remote Priority server\r\n const client = new Client({\r\n name: 'priority-mcp-client',\r\n version: '1.0.0'\r\n }, {\r\n capabilities: {}\r\n });\r\n\r\n // Generate tenant ID from company+url for uniqueness\r\n // This allows server to track/rate-limit per customer without storing credentials\r\n const crypto = await import('crypto');\r\n const tenantIdSource = `${options.priorityCompany.toLowerCase()}@${options.priorityUrl.toLowerCase()}`;\r\n const tenantId = crypto.createHash('sha256').update(tenantIdSource).digest('hex').substring(0, 16);\r\n\r\n // Prepare headers for authentication\r\n const headers = {\r\n 'User-Agent': 'leonai-priority-mcp-client',\r\n 'Authorization': `Bearer ${options.token}`,\r\n 'X-Tenant-ID': tenantId, // Unique identifier for rate limiting/tracking\r\n 'X-Priority-URL': options.priorityUrl,\r\n 'X-Priority-Company': options.priorityCompany,\r\n 'X-Priority-Tabulaini': options.priorityTabulaini,\r\n 'X-Priority-Username': options.priorityUsername,\r\n 'X-Priority-Password': options.priorityPassword\r\n };\r\n\r\n if (debug) {\r\n console.error('[DEBUG] Headers prepared (credentials hidden)');\r\n }\r\n\r\n // Connect to remote Priority MCP server via Streamable HTTP with timeout\r\n const mcpUrl = new URL('/mcp', MCP_SERVER_URL);\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), 30000); // 30 second timeout\r\n\r\n // Create custom fetch that ensures User-Agent and auth headers are set\r\n // without interfering with MCP SDK's Content-Type headers\r\n const customFetch = async (url: string | URL | Request, init?: RequestInit) => {\r\n // Properly merge headers: SDK headers take precedence over our custom headers\r\n const mergedHeaders: Record<string, string> = { ...headers };\r\n\r\n // If SDK provides headers, ensure they override ours (especially Content-Type)\r\n if (init?.headers) {\r\n const sdkHeaders = init.headers instanceof Headers\r\n ? Object.fromEntries(init.headers.entries())\r\n : init.headers;\r\n Object.assign(mergedHeaders, sdkHeaders);\r\n }\r\n\r\n const fetchInit: RequestInit = {\r\n ...init,\r\n headers: mergedHeaders\r\n };\r\n return fetch(url, fetchInit);\r\n };\r\n\r\n const transport = new StreamableHTTPClientTransport(mcpUrl, {\r\n requestInit: {\r\n signal: controller.signal\r\n },\r\n fetch: customFetch as any\r\n });\r\n\r\n try {\r\n await retryWithBackoff(\r\n () => client.connect(transport),\r\n 3, // 3 retries for connection\r\n 1000, // 1s base delay\r\n debug\r\n );\r\n clearTimeout(timeoutId);\r\n\r\n if (debug) {\r\n console.error('[DEBUG] Connected to remote Priority MCP server');\r\n }\r\n } catch (error) {\r\n clearTimeout(timeoutId);\r\n console.error('[ERROR] Failed to connect to Priority MCP server');\r\n console.error('[ERROR]', error);\r\n console.error('[HINT]', getErrorHint(error, 'connection'));\r\n throw error;\r\n }\r\n\r\n // Create stdio server for Claude Desktop\r\n const server = new Server({\r\n name: 'priority-mcp-proxy',\r\n version: '1.0.0'\r\n }, {\r\n capabilities: {\r\n tools: {},\r\n resources: {},\r\n prompts: {}\r\n }\r\n });\r\n\r\n // Set up request handlers with retry logic and monitoring\r\n\r\n // List tools\r\n server.setRequestHandler(ListToolsRequestSchema, async () => {\r\n if (debug) console.error('[DEBUG] Forwarding tools/list');\r\n return await connectionMonitor.monitorCall('tools/list', async () => {\r\n return await retryWithBackoff(() => client.listTools(), 2, 500, debug);\r\n });\r\n });\r\n\r\n // Call tool (with retries for transient failures)\r\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\r\n if (debug) console.error(`[DEBUG] Forwarding tools/call: ${request.params.name}`);\r\n if (verbose) console.error(`[VERBOSE] Tool params:`, JSON.stringify(request.params));\r\n\r\n try {\r\n const result = await connectionMonitor.monitorCall(`tools/call/${request.params.name}`, async () => {\r\n return await retryWithBackoff(() => client.callTool(request.params), 3, 1000, debug);\r\n });\r\n\r\n if (verbose) console.error(`[VERBOSE] Tool result:`, JSON.stringify(result).slice(0, 500));\r\n return result;\r\n } catch (error) {\r\n console.error(`[ERROR] Tool call failed: ${request.params.name}`);\r\n console.error(`[HINT]`, getErrorHint(error, 'tool call'));\r\n throw error;\r\n }\r\n });\r\n\r\n // List resources\r\n server.setRequestHandler(ListResourcesRequestSchema, async () => {\r\n if (debug) console.error('[DEBUG] Forwarding resources/list');\r\n return await connectionMonitor.monitorCall('resources/list', async () => {\r\n return await retryWithBackoff(() => client.listResources(), 2, 500, debug);\r\n });\r\n });\r\n\r\n // Read resource\r\n server.setRequestHandler(ReadResourceRequestSchema, async (request) => {\r\n if (debug) console.error(`[DEBUG] Forwarding resources/read: ${request.params.uri}`);\r\n return await connectionMonitor.monitorCall(`resources/read/${request.params.uri}`, async () => {\r\n return await retryWithBackoff(() => client.readResource(request.params), 3, 1000, debug);\r\n });\r\n });\r\n\r\n // List prompts\r\n server.setRequestHandler(ListPromptsRequestSchema, async () => {\r\n if (debug) console.error('[DEBUG] Forwarding prompts/list');\r\n return await connectionMonitor.monitorCall('prompts/list', async () => {\r\n return await retryWithBackoff(() => client.listPrompts(), 2, 500, debug);\r\n });\r\n });\r\n\r\n // Get prompt\r\n server.setRequestHandler(GetPromptRequestSchema, async (request) => {\r\n if (debug) console.error(`[DEBUG] Forwarding prompts/get: ${request.params.name}`);\r\n return await connectionMonitor.monitorCall(`prompts/get/${request.params.name}`, async () => {\r\n return await retryWithBackoff(() => client.getPrompt(request.params), 2, 500, debug);\r\n });\r\n });\r\n\r\n // Connect server to stdio for Claude Desktop\r\n const stdioTransport = new StdioServerTransport();\r\n await server.connect(stdioTransport);\r\n\r\n if (debug) {\r\n console.error('[DEBUG] Stdio server ready, proxy active');\r\n }\r\n\r\n // Keep process running\r\n process.on('SIGINT', async () => {\r\n if (debug) console.error('[DEBUG] Shutting down...');\r\n await server.close();\r\n await client.close();\r\n process.exit(0);\r\n });\r\n\r\n } catch (error) {\r\n console.error('Failed to start Priority MCP client:', error);\r\n process.exit(1);\r\n }\r\n}\r\n\r\nmain().catch((error) => {\r\n console.error('Fatal error:', error);\r\n process.exit(1);\r\n});\r\n"],"mappings":"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","names":[]}