listingbureau-mcp 0.1.5 → 0.1.6
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/dist/client/lb-client.d.ts +1 -1
- package/dist/client/lb-client.js +6 -4
- package/dist/index.js +2 -0
- package/dist/tools/account.tools.js +5 -5
- package/dist/tools/cost.tools.js +2 -2
- package/dist/tools/feedback.tools.js +1 -1
- package/dist/tools/orders.tools.js +3 -3
- package/dist/tools/projects.tools.js +6 -6
- package/dist/tools/schedule.tools.js +4 -3
- package/dist/tools/wallet.tools.js +3 -3
- package/dist/utils/response.js +11 -1
- package/dist/utils/update-check.d.ts +8 -0
- package/dist/utils/update-check.js +61 -0
- package/package.json +1 -1
|
@@ -30,6 +30,6 @@ export declare class LBClient {
|
|
|
30
30
|
* Make an authenticated API request.
|
|
31
31
|
* Retries once on 401 (token expired mid-request).
|
|
32
32
|
*/
|
|
33
|
-
request<T>(method: string, path: string, body?: Record<string, unknown>, query?: Record<string, string
|
|
33
|
+
request<T>(method: string, path: string, body?: Record<string, unknown>, query?: Record<string, string>, toolName?: string): Promise<ApiSuccessResponse<T>>;
|
|
34
34
|
private doRequest;
|
|
35
35
|
}
|
package/dist/client/lb-client.js
CHANGED
|
@@ -153,14 +153,14 @@ export class LBClient {
|
|
|
153
153
|
* Make an authenticated API request.
|
|
154
154
|
* Retries once on 401 (token expired mid-request).
|
|
155
155
|
*/
|
|
156
|
-
async request(method, path, body, query) {
|
|
156
|
+
async request(method, path, body, query, toolName) {
|
|
157
157
|
await this.ensureAuth();
|
|
158
|
-
const response = await this.doRequest(method, path, body, query);
|
|
158
|
+
const response = await this.doRequest(method, path, body, query, toolName);
|
|
159
159
|
// Single retry on 401
|
|
160
160
|
if (response.status === "error" && response._statusCode === 401) {
|
|
161
161
|
this.jwt = null;
|
|
162
162
|
await this.ensureAuth();
|
|
163
|
-
const retry = await this.doRequest(method, path, body, query);
|
|
163
|
+
const retry = await this.doRequest(method, path, body, query, toolName);
|
|
164
164
|
if (retry.status === "error") {
|
|
165
165
|
throw new LBApiError(retry._statusCode ?? 500, retry.error.code, retry.error.message);
|
|
166
166
|
}
|
|
@@ -171,7 +171,7 @@ export class LBClient {
|
|
|
171
171
|
}
|
|
172
172
|
return response;
|
|
173
173
|
}
|
|
174
|
-
async doRequest(method, path, body, query) {
|
|
174
|
+
async doRequest(method, path, body, query, toolName) {
|
|
175
175
|
let url = `${this.baseUrl}${path}`;
|
|
176
176
|
if (query) {
|
|
177
177
|
const params = new URLSearchParams(Object.entries(query).filter(([, v]) => v !== undefined && v !== ""));
|
|
@@ -181,6 +181,8 @@ export class LBClient {
|
|
|
181
181
|
}
|
|
182
182
|
const headers = {
|
|
183
183
|
Authorization: `Bearer ${this.jwt.access_token}`,
|
|
184
|
+
"X-LB-Source": "mcp",
|
|
185
|
+
...(toolName && { "X-LB-Tool": toolName }),
|
|
184
186
|
};
|
|
185
187
|
const options = { method, headers };
|
|
186
188
|
if (body && method !== "GET") {
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import { registerOrdersTools } from "./tools/orders.tools.js";
|
|
|
10
10
|
import { registerFeedbackTools } from "./tools/feedback.tools.js";
|
|
11
11
|
import { registerCostTools } from "./tools/cost.tools.js";
|
|
12
12
|
import { validateBaseUrl } from "./utils/validate-url.js";
|
|
13
|
+
import { checkForUpdate } from "./utils/update-check.js";
|
|
13
14
|
import { createRequire } from "node:module";
|
|
14
15
|
const require = createRequire(import.meta.url);
|
|
15
16
|
const pkg = require("../package.json");
|
|
@@ -42,3 +43,4 @@ registerFeedbackTools(server, client);
|
|
|
42
43
|
registerCostTools(server, client);
|
|
43
44
|
const transport = new StdioServerTransport();
|
|
44
45
|
await server.connect(transport);
|
|
46
|
+
checkForUpdate(pkg.version).catch(() => { });
|
|
@@ -4,8 +4,8 @@ export function registerAccountTools(server, client) {
|
|
|
4
4
|
server.tool("lb_account_get", "Get Listing Bureau account info (email, name, account status, wallet balance)", {}, { readOnlyHint: true }, async () => {
|
|
5
5
|
try {
|
|
6
6
|
const [accountRes, walletRes] = await Promise.all([
|
|
7
|
-
client.request("GET", "/api/v1/account"),
|
|
8
|
-
client.request("GET", "/api/v1/wallet"),
|
|
7
|
+
client.request("GET", "/api/v1/account", undefined, undefined, "lb_account_get"),
|
|
8
|
+
client.request("GET", "/api/v1/wallet", undefined, undefined, "lb_account_get"),
|
|
9
9
|
]);
|
|
10
10
|
const account = accountRes.data;
|
|
11
11
|
const wallet = walletRes.data;
|
|
@@ -55,7 +55,7 @@ export function registerAccountTools(server, client) {
|
|
|
55
55
|
if (Object.keys(body).length === 0) {
|
|
56
56
|
return formatErrorResult(new Error("At least one field (first_name, last_name, company) must be provided"));
|
|
57
57
|
}
|
|
58
|
-
const res = await client.request("PATCH", "/api/v1/account/profile", body);
|
|
58
|
+
const res = await client.request("PATCH", "/api/v1/account/profile", body, undefined, "lb_account_update_profile");
|
|
59
59
|
return formatResult(res.data);
|
|
60
60
|
}
|
|
61
61
|
catch (e) {
|
|
@@ -64,7 +64,7 @@ export function registerAccountTools(server, client) {
|
|
|
64
64
|
});
|
|
65
65
|
server.tool("lb_account_get_service_rates", "Get current Listing Bureau service pricing rates. Returns empty object if no plan is active.", {}, { readOnlyHint: true }, async () => {
|
|
66
66
|
try {
|
|
67
|
-
const res = await client.request("GET", "/api/v1/account/service-rates");
|
|
67
|
+
const res = await client.request("GET", "/api/v1/account/service-rates", undefined, undefined, "lb_account_get_service_rates");
|
|
68
68
|
return formatResult(res.data);
|
|
69
69
|
}
|
|
70
70
|
catch (e) {
|
|
@@ -73,7 +73,7 @@ export function registerAccountTools(server, client) {
|
|
|
73
73
|
});
|
|
74
74
|
server.tool("lb_account_get_subscription", "Get Listing Bureau subscription info (plan label, fee, discount, wallet usage)", {}, { readOnlyHint: true }, async () => {
|
|
75
75
|
try {
|
|
76
|
-
const res = await client.request("GET", "/api/v1/account/subscription");
|
|
76
|
+
const res = await client.request("GET", "/api/v1/account/subscription", undefined, undefined, "lb_account_get_subscription");
|
|
77
77
|
return formatResult(res.data);
|
|
78
78
|
}
|
|
79
79
|
catch (e) {
|
package/dist/tools/cost.tools.js
CHANGED
|
@@ -34,8 +34,8 @@ export function registerCostTools(server, client) {
|
|
|
34
34
|
return formatErrorResult(new Error("Provide either a 'schedule' array, OR at least one volume (atc/sfb/pgv > 0) with 'num_days'"));
|
|
35
35
|
}
|
|
36
36
|
const [ratesRes, walletRes] = await Promise.all([
|
|
37
|
-
client.request("GET", "/api/v1/account/service-rates"),
|
|
38
|
-
client.request("GET", "/api/v1/wallet"),
|
|
37
|
+
client.request("GET", "/api/v1/account/service-rates", undefined, undefined, "lb_estimate_cost"),
|
|
38
|
+
client.request("GET", "/api/v1/wallet", undefined, undefined, "lb_estimate_cost"),
|
|
39
39
|
]);
|
|
40
40
|
const rates = ratesRes.data;
|
|
41
41
|
const wallet = walletRes.data;
|
|
@@ -9,7 +9,7 @@ export function registerFeedbackTools(server, client) {
|
|
|
9
9
|
.describe("Feedback text (10-5000 characters)"),
|
|
10
10
|
}, { idempotentHint: false }, async (params) => {
|
|
11
11
|
try {
|
|
12
|
-
const res = await client.request("POST", "/api/v1/feedback", { feedback: params.feedback });
|
|
12
|
+
const res = await client.request("POST", "/api/v1/feedback", { feedback: params.feedback }, undefined, "lb_feedback_submit");
|
|
13
13
|
return formatResult(res.data);
|
|
14
14
|
}
|
|
15
15
|
catch (e) {
|
|
@@ -17,7 +17,7 @@ export function registerOrdersTools(server, client) {
|
|
|
17
17
|
query.page = String(params.page);
|
|
18
18
|
if (params.per_page !== undefined)
|
|
19
19
|
query.per_page = String(params.per_page);
|
|
20
|
-
const res = await client.request("GET", "/api/v1/orders", undefined, query);
|
|
20
|
+
const res = await client.request("GET", "/api/v1/orders", undefined, query, "lb_orders_list");
|
|
21
21
|
if (res.meta) {
|
|
22
22
|
return formatPaginatedResult(res.data, res.meta);
|
|
23
23
|
}
|
|
@@ -31,7 +31,7 @@ export function registerOrdersTools(server, client) {
|
|
|
31
31
|
order_id: z.string().describe("Order identifier"),
|
|
32
32
|
}, { readOnlyHint: true }, async (params) => {
|
|
33
33
|
try {
|
|
34
|
-
const res = await client.request("GET", `/api/v1/orders/${encodeURIComponent(params.order_id)}
|
|
34
|
+
const res = await client.request("GET", `/api/v1/orders/${encodeURIComponent(params.order_id)}`, undefined, undefined, "lb_orders_get");
|
|
35
35
|
return formatResult(res.data);
|
|
36
36
|
}
|
|
37
37
|
catch (e) {
|
|
@@ -47,7 +47,7 @@ export function registerOrdersTools(server, client) {
|
|
|
47
47
|
.describe("Issue description (1-1000 characters)"),
|
|
48
48
|
}, { idempotentHint: false }, async (params) => {
|
|
49
49
|
try {
|
|
50
|
-
const res = await client.request("POST", `/api/v1/orders/${encodeURIComponent(params.order_id)}/report-issue`, { description: params.description });
|
|
50
|
+
const res = await client.request("POST", `/api/v1/orders/${encodeURIComponent(params.order_id)}/report-issue`, { description: params.description }, undefined, "lb_orders_report_issue");
|
|
51
51
|
return formatResult(res.data);
|
|
52
52
|
}
|
|
53
53
|
catch (e) {
|
|
@@ -22,7 +22,7 @@ export function registerProjectsTools(server, client) {
|
|
|
22
22
|
query.region = params.region;
|
|
23
23
|
if (params.active !== undefined)
|
|
24
24
|
query.active = String(params.active);
|
|
25
|
-
const res = await client.request("GET", "/api/v1/projects", undefined, query);
|
|
25
|
+
const res = await client.request("GET", "/api/v1/projects", undefined, query, "lb_projects_list");
|
|
26
26
|
return formatResult(res.data);
|
|
27
27
|
}
|
|
28
28
|
catch (e) {
|
|
@@ -57,7 +57,7 @@ export function registerProjectsTools(server, client) {
|
|
|
57
57
|
if (params.expected_retail_price !== undefined) {
|
|
58
58
|
body.expected_retail_price = params.expected_retail_price;
|
|
59
59
|
}
|
|
60
|
-
const res = await client.request("POST", "/api/v1/projects", body);
|
|
60
|
+
const res = await client.request("POST", "/api/v1/projects", body, undefined, "lb_projects_create");
|
|
61
61
|
return formatResult(res.data);
|
|
62
62
|
}
|
|
63
63
|
catch (e) {
|
|
@@ -68,7 +68,7 @@ export function registerProjectsTools(server, client) {
|
|
|
68
68
|
ui_id: z.string().describe("Project unique identifier"),
|
|
69
69
|
}, { readOnlyHint: true }, async (params) => {
|
|
70
70
|
try {
|
|
71
|
-
const res = await client.request("GET", `/api/v1/projects/${encodeURIComponent(params.ui_id)}
|
|
71
|
+
const res = await client.request("GET", `/api/v1/projects/${encodeURIComponent(params.ui_id)}`, undefined, undefined, "lb_projects_get");
|
|
72
72
|
return formatResult(res.data);
|
|
73
73
|
}
|
|
74
74
|
catch (e) {
|
|
@@ -86,7 +86,7 @@ export function registerProjectsTools(server, client) {
|
|
|
86
86
|
if (Object.keys(body).length === 0) {
|
|
87
87
|
return formatErrorResult(new Error("At least one field (active) must be provided"));
|
|
88
88
|
}
|
|
89
|
-
const res = await client.request("PATCH", `/api/v1/projects/${encodeURIComponent(params.ui_id)}`, body);
|
|
89
|
+
const res = await client.request("PATCH", `/api/v1/projects/${encodeURIComponent(params.ui_id)}`, body, undefined, "lb_projects_update");
|
|
90
90
|
return formatResult(res.data);
|
|
91
91
|
}
|
|
92
92
|
catch (e) {
|
|
@@ -97,7 +97,7 @@ export function registerProjectsTools(server, client) {
|
|
|
97
97
|
ui_id: z.string().describe("Project unique identifier"),
|
|
98
98
|
}, { destructiveHint: true }, async (params) => {
|
|
99
99
|
try {
|
|
100
|
-
const res = await client.request("DELETE", `/api/v1/projects/${encodeURIComponent(params.ui_id)}
|
|
100
|
+
const res = await client.request("DELETE", `/api/v1/projects/${encodeURIComponent(params.ui_id)}`, undefined, undefined, "lb_projects_archive");
|
|
101
101
|
return formatResult(res.data);
|
|
102
102
|
}
|
|
103
103
|
catch (e) {
|
|
@@ -118,7 +118,7 @@ export function registerProjectsTools(server, client) {
|
|
|
118
118
|
const query = {};
|
|
119
119
|
if (params.days !== undefined)
|
|
120
120
|
query.days = String(params.days);
|
|
121
|
-
const res = await client.request("GET", `/api/v1/projects/${encodeURIComponent(params.ui_id)}/stats`, undefined, query);
|
|
121
|
+
const res = await client.request("GET", `/api/v1/projects/${encodeURIComponent(params.ui_id)}/stats`, undefined, query, "lb_projects_get_stats");
|
|
122
122
|
return formatResult(res.data);
|
|
123
123
|
}
|
|
124
124
|
catch (e) {
|
|
@@ -18,6 +18,7 @@ const scheduleEntrySchema = z.object({
|
|
|
18
18
|
async function appendCostSummary(data, client) {
|
|
19
19
|
const result = { ...data };
|
|
20
20
|
try {
|
|
21
|
+
// Internal enrichment fetch — no tool attribution in audit log
|
|
21
22
|
const ratesRes = await client.request("GET", "/api/v1/account/service-rates");
|
|
22
23
|
const rates = ratesRes.data;
|
|
23
24
|
const { dated, ongoing } = mapScheduleEntries(data.scheduling);
|
|
@@ -102,7 +103,7 @@ export function registerScheduleTools(server, client) {
|
|
|
102
103
|
ui_id: z.string().describe("Project unique identifier"),
|
|
103
104
|
}, { readOnlyHint: true }, async (params) => {
|
|
104
105
|
try {
|
|
105
|
-
const res = await client.request("GET", `/api/v1/projects/${encodeURIComponent(params.ui_id)}/schedule
|
|
106
|
+
const res = await client.request("GET", `/api/v1/projects/${encodeURIComponent(params.ui_id)}/schedule`, undefined, undefined, "lb_schedule_get");
|
|
106
107
|
const augmented = await appendCostSummary(res.data, client);
|
|
107
108
|
return formatResult(augmented);
|
|
108
109
|
}
|
|
@@ -119,7 +120,7 @@ export function registerScheduleTools(server, client) {
|
|
|
119
120
|
.describe("Array of daily schedule entries"),
|
|
120
121
|
}, { destructiveHint: true, idempotentHint: true }, async (params) => {
|
|
121
122
|
try {
|
|
122
|
-
const res = await client.request("PUT", `/api/v1/projects/${encodeURIComponent(params.ui_id)}/schedule`, { schedule: params.schedule });
|
|
123
|
+
const res = await client.request("PUT", `/api/v1/projects/${encodeURIComponent(params.ui_id)}/schedule`, { schedule: params.schedule }, undefined, "lb_schedule_set");
|
|
123
124
|
const augmented = await appendCostSummary(res.data, client);
|
|
124
125
|
return formatResult(augmented);
|
|
125
126
|
}
|
|
@@ -139,7 +140,7 @@ export function registerScheduleTools(server, client) {
|
|
|
139
140
|
sfb: params.sfb ?? 0,
|
|
140
141
|
pgv: params.pgv ?? 0,
|
|
141
142
|
};
|
|
142
|
-
const res = await client.request("POST", `/api/v1/projects/${encodeURIComponent(params.ui_id)}/schedule/quick`, body);
|
|
143
|
+
const res = await client.request("POST", `/api/v1/projects/${encodeURIComponent(params.ui_id)}/schedule/quick`, body, undefined, "lb_schedule_quick_set");
|
|
143
144
|
const augmented = await appendCostSummary(res.data, client);
|
|
144
145
|
return formatResult(augmented);
|
|
145
146
|
}
|
|
@@ -3,7 +3,7 @@ import { formatResult, formatPaginatedResult, formatErrorResult, } from "../util
|
|
|
3
3
|
export function registerWalletTools(server, client) {
|
|
4
4
|
server.tool("lb_wallet_get_balance", "Get Listing Bureau wallet balance (credits and USD). May include a warning if data is temporarily unavailable.", {}, { readOnlyHint: true }, async () => {
|
|
5
5
|
try {
|
|
6
|
-
const res = await client.request("GET", "/api/v1/wallet");
|
|
6
|
+
const res = await client.request("GET", "/api/v1/wallet", undefined, undefined, "lb_wallet_get_balance");
|
|
7
7
|
return formatResult(res.data);
|
|
8
8
|
}
|
|
9
9
|
catch (e) {
|
|
@@ -26,7 +26,7 @@ export function registerWalletTools(server, client) {
|
|
|
26
26
|
query.page = String(params.page);
|
|
27
27
|
if (params.per_page !== undefined)
|
|
28
28
|
query.per_page = String(params.per_page);
|
|
29
|
-
const res = await client.request("GET", "/api/v1/wallet/transactions", undefined, query);
|
|
29
|
+
const res = await client.request("GET", "/api/v1/wallet/transactions", undefined, query, "lb_wallet_get_transactions");
|
|
30
30
|
if (res.meta) {
|
|
31
31
|
return formatPaginatedResult(res.data, res.meta);
|
|
32
32
|
}
|
|
@@ -44,7 +44,7 @@ export function registerWalletTools(server, client) {
|
|
|
44
44
|
.describe("Top-up amount in USD (minimum $5, maximum $5,000)"),
|
|
45
45
|
}, { destructiveHint: true }, async (params) => {
|
|
46
46
|
try {
|
|
47
|
-
const res = await client.request("POST", "/api/v1/wallet/topup", { amount: params.amount });
|
|
47
|
+
const res = await client.request("POST", "/api/v1/wallet/topup", { amount: params.amount }, undefined, "lb_wallet_topup");
|
|
48
48
|
return formatResult(res.data);
|
|
49
49
|
}
|
|
50
50
|
catch (e) {
|
package/dist/utils/response.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { formatError } from "./errors.js";
|
|
2
|
+
import { getUpdateNotice } from "./update-check.js";
|
|
2
3
|
/**
|
|
3
4
|
* Format a successful API response as an MCP tool result.
|
|
4
5
|
* Handles both entity responses and message-only responses.
|
|
@@ -36,6 +37,10 @@ export function formatResult(data) {
|
|
|
36
37
|
for (const w of warnings) {
|
|
37
38
|
text += `\n\n⚠️ Warning: ${w}`;
|
|
38
39
|
}
|
|
40
|
+
const notice = getUpdateNotice();
|
|
41
|
+
if (notice) {
|
|
42
|
+
text += `\n\n${notice}`;
|
|
43
|
+
}
|
|
39
44
|
return {
|
|
40
45
|
content: [{ type: "text", text }],
|
|
41
46
|
};
|
|
@@ -49,8 +54,13 @@ export function formatPaginatedResult(data, meta) {
|
|
|
49
54
|
data,
|
|
50
55
|
pagination: meta,
|
|
51
56
|
};
|
|
57
|
+
let text = JSON.stringify(result, null, 2);
|
|
58
|
+
const notice = getUpdateNotice();
|
|
59
|
+
if (notice) {
|
|
60
|
+
text += `\n\n${notice}`;
|
|
61
|
+
}
|
|
52
62
|
return {
|
|
53
|
-
content: [{ type: "text", text
|
|
63
|
+
content: [{ type: "text", text }],
|
|
54
64
|
};
|
|
55
65
|
}
|
|
56
66
|
/** Format an error as an MCP tool error result. */
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Compare semver strings. Returns true only if latest > current. */
|
|
2
|
+
export declare function isNewerVersion(latest: string, current: string): boolean;
|
|
3
|
+
/** Fire-and-forget npm registry check. Stores notice for later retrieval. */
|
|
4
|
+
export declare function checkForUpdate(currentVersion: string): Promise<void>;
|
|
5
|
+
/** Returns the update notice once, then null for all subsequent calls. */
|
|
6
|
+
export declare function getUpdateNotice(): string | null;
|
|
7
|
+
/** Reset state for testing. */
|
|
8
|
+
export declare function resetForTesting(): void;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Module-level state — reset via resetForTesting() in test files.
|
|
2
|
+
// Once noticeConsumed is true, subsequent checkForUpdate calls still overwrite
|
|
3
|
+
// updateNotice but getUpdateNotice will return null. This is intentional:
|
|
4
|
+
// the notice is designed to fire once per process lifetime.
|
|
5
|
+
let updateNotice = null;
|
|
6
|
+
let noticeConsumed = false;
|
|
7
|
+
/** Compare semver strings. Returns true only if latest > current. */
|
|
8
|
+
export function isNewerVersion(latest, current) {
|
|
9
|
+
const latestParts = latest.split(".").map(Number);
|
|
10
|
+
const currentParts = current.split(".").map(Number);
|
|
11
|
+
if (latestParts.length !== 3 || currentParts.length !== 3)
|
|
12
|
+
return false;
|
|
13
|
+
if (latestParts.some((n) => !Number.isFinite(n)) || currentParts.some((n) => !Number.isFinite(n)))
|
|
14
|
+
return false;
|
|
15
|
+
for (let i = 0; i < 3; i++) {
|
|
16
|
+
if (latestParts[i] > currentParts[i])
|
|
17
|
+
return true;
|
|
18
|
+
if (latestParts[i] < currentParts[i])
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
/** Fire-and-forget npm registry check. Stores notice for later retrieval. */
|
|
24
|
+
export async function checkForUpdate(currentVersion) {
|
|
25
|
+
try {
|
|
26
|
+
const res = await fetch("https://registry.npmjs.org/listingbureau-mcp/latest", {
|
|
27
|
+
signal: AbortSignal.timeout(5000),
|
|
28
|
+
});
|
|
29
|
+
if (!res.ok)
|
|
30
|
+
return;
|
|
31
|
+
const json = (await res.json());
|
|
32
|
+
const latest = json.version;
|
|
33
|
+
if (typeof latest !== "string" || !/^\d+\.\d+\.\d+$/.test(latest))
|
|
34
|
+
return;
|
|
35
|
+
if (isNewerVersion(latest, currentVersion)) {
|
|
36
|
+
updateNotice =
|
|
37
|
+
`📦 Update available: v${currentVersion} → v${latest}\n` +
|
|
38
|
+
`To update:\n` +
|
|
39
|
+
` npm: npx listingbureau-mcp@latest (or npm update -g listingbureau-mcp)\n` +
|
|
40
|
+
` Desktop: Download from https://github.com/listingbureau/listingbureau-mcp/releases/latest`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Silent failure — network errors, TimeoutError, AbortError, bad JSON all swallowed
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Returns the update notice once, then null for all subsequent calls. */
|
|
48
|
+
export function getUpdateNotice() {
|
|
49
|
+
if (noticeConsumed)
|
|
50
|
+
return null;
|
|
51
|
+
if (updateNotice) {
|
|
52
|
+
noticeConsumed = true;
|
|
53
|
+
return updateNotice;
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
/** Reset state for testing. */
|
|
58
|
+
export function resetForTesting() {
|
|
59
|
+
updateNotice = null;
|
|
60
|
+
noticeConsumed = false;
|
|
61
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "listingbureau-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Amazon organic ranking MCP server. Run ranking campaigns, track keyword positions, and monitor rank movement from any AI assistant.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|