@lynkow/mcp-server 1.0.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/LICENSE +6 -0
- package/README.md +104 -0
- package/bin/lynkow-mcp.js +8 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +2041 -0
- package/dist/index.js.map +1 -0
- package/package.json +65 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2041 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
|
|
6
|
+
// src/index.ts
|
|
7
|
+
var DEFAULT_API_URL = "https://api.lynkow.com";
|
|
8
|
+
var configSchema = z.object({
|
|
9
|
+
apiUrl: z.string().url().default(DEFAULT_API_URL),
|
|
10
|
+
apiToken: z.string().min(1, "LYNKOW_API_TOKEN is required"),
|
|
11
|
+
locale: z.string().default("fr"),
|
|
12
|
+
timeout: z.coerce.number().default(3e4),
|
|
13
|
+
debug: z.coerce.boolean().default(false)
|
|
14
|
+
});
|
|
15
|
+
function loadConfig() {
|
|
16
|
+
return configSchema.parse({
|
|
17
|
+
apiUrl: process.env.LYNKOW_API_URL || DEFAULT_API_URL,
|
|
18
|
+
apiToken: process.env.LYNKOW_API_TOKEN,
|
|
19
|
+
locale: process.env.LYNKOW_LOCALE,
|
|
20
|
+
timeout: process.env.LYNKOW_TIMEOUT,
|
|
21
|
+
debug: process.env.LYNKOW_DEBUG
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/client/api-client.ts
|
|
26
|
+
var LynkowApiClient = class {
|
|
27
|
+
baseUrl;
|
|
28
|
+
token;
|
|
29
|
+
timeout;
|
|
30
|
+
debug;
|
|
31
|
+
constructor(config) {
|
|
32
|
+
this.baseUrl = config.apiUrl.replace(/\/$/, "");
|
|
33
|
+
this.token = config.apiToken;
|
|
34
|
+
this.timeout = config.timeout;
|
|
35
|
+
this.debug = config.debug;
|
|
36
|
+
}
|
|
37
|
+
async get(path, params) {
|
|
38
|
+
const url = new URL(`/v1${path}`, this.baseUrl);
|
|
39
|
+
if (params) {
|
|
40
|
+
for (const [k, v] of Object.entries(params)) {
|
|
41
|
+
if (v !== void 0 && v !== null) url.searchParams.set(k, String(v));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return this.request("GET", url);
|
|
45
|
+
}
|
|
46
|
+
async post(path, body) {
|
|
47
|
+
const url = new URL(`/v1${path}`, this.baseUrl);
|
|
48
|
+
return this.request("POST", url, body);
|
|
49
|
+
}
|
|
50
|
+
async put(path, body) {
|
|
51
|
+
const url = new URL(`/v1${path}`, this.baseUrl);
|
|
52
|
+
return this.request("PUT", url, body);
|
|
53
|
+
}
|
|
54
|
+
async delete(path, params) {
|
|
55
|
+
const url = new URL(`/v1${path}`, this.baseUrl);
|
|
56
|
+
if (params) {
|
|
57
|
+
for (const [k, v] of Object.entries(params)) {
|
|
58
|
+
if (v !== void 0 && v !== null) url.searchParams.set(k, String(v));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return this.request("DELETE", url);
|
|
62
|
+
}
|
|
63
|
+
async request(method, url, body) {
|
|
64
|
+
if (this.debug) {
|
|
65
|
+
console.error(`[lynkow-mcp] ${method} ${url.pathname}${url.search}`);
|
|
66
|
+
}
|
|
67
|
+
const controller = new AbortController();
|
|
68
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
69
|
+
try {
|
|
70
|
+
const response = await fetch(url.toString(), {
|
|
71
|
+
method,
|
|
72
|
+
headers: {
|
|
73
|
+
"Authorization": `Bearer ${this.token}`,
|
|
74
|
+
"Content-Type": "application/json",
|
|
75
|
+
"Accept": "application/json"
|
|
76
|
+
},
|
|
77
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
78
|
+
signal: controller.signal
|
|
79
|
+
});
|
|
80
|
+
if (!response.ok) {
|
|
81
|
+
const error = await response.json().catch(() => ({}));
|
|
82
|
+
throw new ApiError(
|
|
83
|
+
response.status,
|
|
84
|
+
error.error || response.statusText,
|
|
85
|
+
error
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
if (response.status === 204) return {};
|
|
89
|
+
return response.json();
|
|
90
|
+
} finally {
|
|
91
|
+
clearTimeout(timeoutId);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
var ApiError = class extends Error {
|
|
96
|
+
constructor(status, message, details) {
|
|
97
|
+
super(message);
|
|
98
|
+
this.status = status;
|
|
99
|
+
this.details = details;
|
|
100
|
+
this.name = "ApiError";
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
function handleApiError(error) {
|
|
104
|
+
if (error instanceof ApiError) {
|
|
105
|
+
switch (error.status) {
|
|
106
|
+
case 400:
|
|
107
|
+
throw new McpError(ErrorCode.InvalidParams, error.message);
|
|
108
|
+
case 401:
|
|
109
|
+
throw new McpError(ErrorCode.InvalidRequest, "Invalid or expired API token");
|
|
110
|
+
case 403:
|
|
111
|
+
throw new McpError(
|
|
112
|
+
ErrorCode.InvalidRequest,
|
|
113
|
+
`Permission denied: ${error.message}. Check your API token permissions.`
|
|
114
|
+
);
|
|
115
|
+
case 404:
|
|
116
|
+
throw new McpError(ErrorCode.InvalidParams, `Not found: ${error.message}`);
|
|
117
|
+
case 422:
|
|
118
|
+
throw new McpError(
|
|
119
|
+
ErrorCode.InvalidParams,
|
|
120
|
+
`Validation error: ${JSON.stringify(error.details)}`
|
|
121
|
+
);
|
|
122
|
+
case 429:
|
|
123
|
+
throw new McpError(ErrorCode.InvalidRequest, "Rate limit exceeded. Please wait.");
|
|
124
|
+
default:
|
|
125
|
+
throw new McpError(ErrorCode.InternalError, `API error ${error.status}: ${error.message}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
throw new McpError(
|
|
129
|
+
ErrorCode.InternalError,
|
|
130
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/utils/formatting.ts
|
|
135
|
+
function jsonResponse(data) {
|
|
136
|
+
return {
|
|
137
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/tools/contents.ts
|
|
142
|
+
function registerContentTools(server, client) {
|
|
143
|
+
server.tool(
|
|
144
|
+
"list_contents",
|
|
145
|
+
"List content items (blog posts, articles) with filtering, search, and pagination",
|
|
146
|
+
{
|
|
147
|
+
page: z.number().optional().describe("Page number (default: 1)"),
|
|
148
|
+
perPage: z.number().optional().describe("Items per page (default: 15)"),
|
|
149
|
+
status: z.enum(["draft", "published", "archived", "scheduled"]).optional(),
|
|
150
|
+
type: z.string().optional().describe("Content type filter"),
|
|
151
|
+
search: z.string().optional().describe("Search in title and content"),
|
|
152
|
+
locale: z.string().optional().describe("Filter by locale"),
|
|
153
|
+
category: z.string().optional().describe("Category ID filter"),
|
|
154
|
+
tag: z.string().optional().describe("Tag ID filter"),
|
|
155
|
+
sort: z.string().optional().describe('Sort field (e.g., "created_at", "title")'),
|
|
156
|
+
order: z.enum(["asc", "desc"]).optional()
|
|
157
|
+
},
|
|
158
|
+
async (params) => {
|
|
159
|
+
try {
|
|
160
|
+
return jsonResponse(await client.get("/contents", params));
|
|
161
|
+
} catch (error) {
|
|
162
|
+
handleApiError(error);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
);
|
|
166
|
+
server.tool(
|
|
167
|
+
"get_content",
|
|
168
|
+
"Get a single content item by ID with full details",
|
|
169
|
+
{ id: z.number().describe("Content ID") },
|
|
170
|
+
async ({ id }) => {
|
|
171
|
+
try {
|
|
172
|
+
return jsonResponse(await client.get(`/contents/${id}`));
|
|
173
|
+
} catch (error) {
|
|
174
|
+
handleApiError(error);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
server.tool(
|
|
179
|
+
"search_contents",
|
|
180
|
+
"Full-text search across all content",
|
|
181
|
+
{
|
|
182
|
+
q: z.string().describe("Search query"),
|
|
183
|
+
page: z.number().optional(),
|
|
184
|
+
perPage: z.number().optional(),
|
|
185
|
+
locale: z.string().optional()
|
|
186
|
+
},
|
|
187
|
+
async ({ q, ...params }) => {
|
|
188
|
+
try {
|
|
189
|
+
return jsonResponse(await client.get("/contents/search", { q, ...params }));
|
|
190
|
+
} catch (error) {
|
|
191
|
+
handleApiError(error);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
server.tool(
|
|
196
|
+
"create_content",
|
|
197
|
+
"Create a new content item (blog post, article, etc.)",
|
|
198
|
+
{
|
|
199
|
+
title: z.string().describe("Content title"),
|
|
200
|
+
slug: z.string().optional().describe("URL slug (auto-generated if omitted)"),
|
|
201
|
+
type: z.string().optional().describe('Content type (default: "post")'),
|
|
202
|
+
excerpt: z.string().optional().describe("Short excerpt/summary"),
|
|
203
|
+
body: z.any().optional().describe("Content body (rich text blocks)"),
|
|
204
|
+
status: z.enum(["draft", "published"]).optional().describe("Initial status (default: draft)"),
|
|
205
|
+
locale: z.string().optional().describe("Content locale"),
|
|
206
|
+
categoryIds: z.array(z.number()).optional().describe("Category IDs to assign"),
|
|
207
|
+
tagIds: z.array(z.number()).optional().describe("Tag IDs to assign"),
|
|
208
|
+
meta: z.record(z.any()).optional().describe("SEO and custom fields")
|
|
209
|
+
},
|
|
210
|
+
async (params) => {
|
|
211
|
+
try {
|
|
212
|
+
return jsonResponse(await client.post("/contents", params));
|
|
213
|
+
} catch (error) {
|
|
214
|
+
handleApiError(error);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
);
|
|
218
|
+
server.tool(
|
|
219
|
+
"update_content",
|
|
220
|
+
"Update an existing content item",
|
|
221
|
+
{
|
|
222
|
+
id: z.number().describe("Content ID"),
|
|
223
|
+
title: z.string().optional(),
|
|
224
|
+
slug: z.string().optional(),
|
|
225
|
+
excerpt: z.string().optional(),
|
|
226
|
+
body: z.any().optional(),
|
|
227
|
+
status: z.string().optional(),
|
|
228
|
+
meta: z.record(z.any()).optional(),
|
|
229
|
+
categoryIds: z.array(z.number()).optional(),
|
|
230
|
+
tagIds: z.array(z.number()).optional()
|
|
231
|
+
},
|
|
232
|
+
async ({ id, ...body }) => {
|
|
233
|
+
try {
|
|
234
|
+
return jsonResponse(await client.put(`/contents/${id}`, body));
|
|
235
|
+
} catch (error) {
|
|
236
|
+
handleApiError(error);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
);
|
|
240
|
+
server.tool(
|
|
241
|
+
"delete_content",
|
|
242
|
+
"Delete a content item permanently",
|
|
243
|
+
{ id: z.number().describe("Content ID") },
|
|
244
|
+
async ({ id }) => {
|
|
245
|
+
try {
|
|
246
|
+
return jsonResponse(await client.delete(`/contents/${id}`));
|
|
247
|
+
} catch (error) {
|
|
248
|
+
handleApiError(error);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
);
|
|
252
|
+
server.tool(
|
|
253
|
+
"publish_content",
|
|
254
|
+
"Publish a draft content item, making it visible on the site",
|
|
255
|
+
{ id: z.number().describe("Content ID") },
|
|
256
|
+
async ({ id }) => {
|
|
257
|
+
try {
|
|
258
|
+
return jsonResponse(await client.post(`/contents/${id}/publish`));
|
|
259
|
+
} catch (error) {
|
|
260
|
+
handleApiError(error);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
);
|
|
264
|
+
server.tool(
|
|
265
|
+
"archive_content",
|
|
266
|
+
"Archive a content item (removes from public view but keeps data)",
|
|
267
|
+
{ id: z.number().describe("Content ID") },
|
|
268
|
+
async ({ id }) => {
|
|
269
|
+
try {
|
|
270
|
+
return jsonResponse(await client.post(`/contents/${id}/archive`));
|
|
271
|
+
} catch (error) {
|
|
272
|
+
handleApiError(error);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
);
|
|
276
|
+
server.tool(
|
|
277
|
+
"duplicate_content",
|
|
278
|
+
"Create a copy of a content item as a new draft",
|
|
279
|
+
{ id: z.number().describe("Content ID") },
|
|
280
|
+
async ({ id }) => {
|
|
281
|
+
try {
|
|
282
|
+
return jsonResponse(await client.post(`/contents/${id}/duplicate`));
|
|
283
|
+
} catch (error) {
|
|
284
|
+
handleApiError(error);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
);
|
|
288
|
+
server.tool(
|
|
289
|
+
"bulk_publish_contents",
|
|
290
|
+
"Publish multiple content items at once",
|
|
291
|
+
{ ids: z.array(z.number()).describe("Array of content IDs to publish") },
|
|
292
|
+
async ({ ids }) => {
|
|
293
|
+
try {
|
|
294
|
+
return jsonResponse(await client.post("/contents/bulk/publish", { ids }));
|
|
295
|
+
} catch (error) {
|
|
296
|
+
handleApiError(error);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
);
|
|
300
|
+
server.tool(
|
|
301
|
+
"bulk_archive_contents",
|
|
302
|
+
"Archive multiple content items at once",
|
|
303
|
+
{ ids: z.array(z.number()).describe("Array of content IDs to archive") },
|
|
304
|
+
async ({ ids }) => {
|
|
305
|
+
try {
|
|
306
|
+
return jsonResponse(await client.post("/contents/bulk/archive", { ids }));
|
|
307
|
+
} catch (error) {
|
|
308
|
+
handleApiError(error);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
);
|
|
312
|
+
server.tool(
|
|
313
|
+
"bulk_delete_contents",
|
|
314
|
+
"Delete multiple content items at once",
|
|
315
|
+
{ ids: z.array(z.number()).describe("Array of content IDs to delete") },
|
|
316
|
+
async ({ ids }) => {
|
|
317
|
+
try {
|
|
318
|
+
return jsonResponse(await client.post("/contents/bulk/delete", { ids }));
|
|
319
|
+
} catch (error) {
|
|
320
|
+
handleApiError(error);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
);
|
|
324
|
+
server.tool(
|
|
325
|
+
"get_content_revisions",
|
|
326
|
+
"Get version history of a content item",
|
|
327
|
+
{
|
|
328
|
+
id: z.number().describe("Content ID"),
|
|
329
|
+
page: z.number().optional(),
|
|
330
|
+
perPage: z.number().optional()
|
|
331
|
+
},
|
|
332
|
+
async ({ id, ...params }) => {
|
|
333
|
+
try {
|
|
334
|
+
return jsonResponse(await client.get(`/contents/${id}/revisions`, params));
|
|
335
|
+
} catch (error) {
|
|
336
|
+
handleApiError(error);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
function registerSiteBlockTools(server, client) {
|
|
342
|
+
server.tool(
|
|
343
|
+
"list_site_blocks",
|
|
344
|
+
"List all site blocks (pages and global blocks) with optional filtering",
|
|
345
|
+
{
|
|
346
|
+
type: z.enum(["block", "page"]).optional(),
|
|
347
|
+
tag: z.string().optional(),
|
|
348
|
+
locale: z.string().optional(),
|
|
349
|
+
status: z.string().optional(),
|
|
350
|
+
search: z.string().optional(),
|
|
351
|
+
page: z.number().optional(),
|
|
352
|
+
perPage: z.number().optional()
|
|
353
|
+
},
|
|
354
|
+
async (params) => {
|
|
355
|
+
try {
|
|
356
|
+
return jsonResponse(await client.get("/site-blocks", params));
|
|
357
|
+
} catch (error) {
|
|
358
|
+
handleApiError(error);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
);
|
|
362
|
+
server.tool(
|
|
363
|
+
"get_site_block",
|
|
364
|
+
"Get a site block by its slug",
|
|
365
|
+
{
|
|
366
|
+
slug: z.string().describe("Site block slug"),
|
|
367
|
+
locale: z.string().optional()
|
|
368
|
+
},
|
|
369
|
+
async ({ slug, ...params }) => {
|
|
370
|
+
try {
|
|
371
|
+
return jsonResponse(await client.get(`/site-blocks/${slug}`, params));
|
|
372
|
+
} catch (error) {
|
|
373
|
+
handleApiError(error);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
);
|
|
377
|
+
server.tool(
|
|
378
|
+
"create_site_block",
|
|
379
|
+
"Create a new site block (page or global block) with schema and optional data",
|
|
380
|
+
{
|
|
381
|
+
name: z.string().describe("Block name"),
|
|
382
|
+
slug: z.string().describe("URL slug"),
|
|
383
|
+
type: z.enum(["block", "page"]).describe("Block type"),
|
|
384
|
+
schema: z.any().describe("Field schema: { fields: [...] }"),
|
|
385
|
+
data: z.any().optional().describe("Initial content data"),
|
|
386
|
+
path: z.string().optional().describe("URL path (for pages)"),
|
|
387
|
+
tags: z.array(z.string()).optional()
|
|
388
|
+
},
|
|
389
|
+
async (params) => {
|
|
390
|
+
try {
|
|
391
|
+
return jsonResponse(await client.post("/site-blocks", params));
|
|
392
|
+
} catch (error) {
|
|
393
|
+
handleApiError(error);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
);
|
|
397
|
+
server.tool(
|
|
398
|
+
"update_site_block_schema",
|
|
399
|
+
"Update the field schema of a site block (affects all translations)",
|
|
400
|
+
{
|
|
401
|
+
slug: z.string().describe("Site block slug"),
|
|
402
|
+
schema: z.any().describe("New field schema")
|
|
403
|
+
},
|
|
404
|
+
async ({ slug, ...body }) => {
|
|
405
|
+
try {
|
|
406
|
+
return jsonResponse(await client.put(`/site-blocks/${slug}/schema`, body));
|
|
407
|
+
} catch (error) {
|
|
408
|
+
handleApiError(error);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
);
|
|
412
|
+
server.tool(
|
|
413
|
+
"update_site_block_data",
|
|
414
|
+
"Update the content data of a site block for a specific locale",
|
|
415
|
+
{
|
|
416
|
+
slug: z.string().describe("Site block slug"),
|
|
417
|
+
data: z.any().describe("Content data object"),
|
|
418
|
+
locale: z.string().optional()
|
|
419
|
+
},
|
|
420
|
+
async ({ slug, ...body }) => {
|
|
421
|
+
try {
|
|
422
|
+
return jsonResponse(await client.put(`/site-blocks/${slug}/data`, body));
|
|
423
|
+
} catch (error) {
|
|
424
|
+
handleApiError(error);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
);
|
|
428
|
+
server.tool(
|
|
429
|
+
"delete_site_block",
|
|
430
|
+
"Delete a site block. Use locale param to delete only a translation.",
|
|
431
|
+
{
|
|
432
|
+
slug: z.string().describe("Site block slug"),
|
|
433
|
+
locale: z.string().optional().describe("Delete only this locale translation")
|
|
434
|
+
},
|
|
435
|
+
async ({ slug, locale }) => {
|
|
436
|
+
try {
|
|
437
|
+
return jsonResponse(await client.delete(`/site-blocks/${slug}`, locale ? { locale } : void 0));
|
|
438
|
+
} catch (error) {
|
|
439
|
+
handleApiError(error);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
);
|
|
443
|
+
server.tool(
|
|
444
|
+
"publish_site_block",
|
|
445
|
+
"Publish a site block, making it live on the website",
|
|
446
|
+
{
|
|
447
|
+
slug: z.string().describe("Site block slug"),
|
|
448
|
+
locale: z.string().optional()
|
|
449
|
+
},
|
|
450
|
+
async ({ slug, ...body }) => {
|
|
451
|
+
try {
|
|
452
|
+
return jsonResponse(await client.post(`/site-blocks/${slug}/publish`, body));
|
|
453
|
+
} catch (error) {
|
|
454
|
+
handleApiError(error);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
);
|
|
458
|
+
server.tool(
|
|
459
|
+
"unpublish_site_block",
|
|
460
|
+
"Unpublish a site block, reverting it to draft",
|
|
461
|
+
{
|
|
462
|
+
slug: z.string().describe("Site block slug"),
|
|
463
|
+
locale: z.string().optional()
|
|
464
|
+
},
|
|
465
|
+
async ({ slug, ...body }) => {
|
|
466
|
+
try {
|
|
467
|
+
return jsonResponse(await client.post(`/site-blocks/${slug}/unpublish`, body));
|
|
468
|
+
} catch (error) {
|
|
469
|
+
handleApiError(error);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
);
|
|
473
|
+
server.tool(
|
|
474
|
+
"get_site_block_revisions",
|
|
475
|
+
"Get version history of a site block",
|
|
476
|
+
{
|
|
477
|
+
slug: z.string().describe("Site block slug"),
|
|
478
|
+
locale: z.string().optional(),
|
|
479
|
+
page: z.number().optional(),
|
|
480
|
+
perPage: z.number().optional()
|
|
481
|
+
},
|
|
482
|
+
async ({ slug, ...params }) => {
|
|
483
|
+
try {
|
|
484
|
+
return jsonResponse(await client.get(`/site-blocks/${slug}/revisions`, params));
|
|
485
|
+
} catch (error) {
|
|
486
|
+
handleApiError(error);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
);
|
|
490
|
+
server.tool(
|
|
491
|
+
"restore_site_block_revision",
|
|
492
|
+
"Restore a previous version of a site block",
|
|
493
|
+
{
|
|
494
|
+
slug: z.string().describe("Site block slug"),
|
|
495
|
+
revisionId: z.number().describe("Revision ID to restore"),
|
|
496
|
+
locale: z.string().optional()
|
|
497
|
+
},
|
|
498
|
+
async ({ slug, revisionId, ...body }) => {
|
|
499
|
+
try {
|
|
500
|
+
return jsonResponse(await client.post(`/site-blocks/${slug}/revisions/${revisionId}/restore`, body));
|
|
501
|
+
} catch (error) {
|
|
502
|
+
handleApiError(error);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
function registerCategoryTools(server, client) {
|
|
508
|
+
server.tool(
|
|
509
|
+
"list_categories",
|
|
510
|
+
"List categories with pagination",
|
|
511
|
+
{
|
|
512
|
+
page: z.number().optional(),
|
|
513
|
+
perPage: z.number().optional(),
|
|
514
|
+
search: z.string().optional(),
|
|
515
|
+
locale: z.string().optional()
|
|
516
|
+
},
|
|
517
|
+
async (params) => {
|
|
518
|
+
try {
|
|
519
|
+
return jsonResponse(await client.get("/categories", params));
|
|
520
|
+
} catch (error) {
|
|
521
|
+
handleApiError(error);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
);
|
|
525
|
+
server.tool(
|
|
526
|
+
"get_categories_tree",
|
|
527
|
+
"Get the full category hierarchy as a tree structure",
|
|
528
|
+
{ locale: z.string().optional() },
|
|
529
|
+
async (params) => {
|
|
530
|
+
try {
|
|
531
|
+
return jsonResponse(await client.get("/categories/tree", params));
|
|
532
|
+
} catch (error) {
|
|
533
|
+
handleApiError(error);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
);
|
|
537
|
+
server.tool(
|
|
538
|
+
"get_category",
|
|
539
|
+
"Get a single category by ID",
|
|
540
|
+
{ id: z.number().describe("Category ID") },
|
|
541
|
+
async ({ id }) => {
|
|
542
|
+
try {
|
|
543
|
+
return jsonResponse(await client.get(`/categories/${id}`));
|
|
544
|
+
} catch (error) {
|
|
545
|
+
handleApiError(error);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
);
|
|
549
|
+
server.tool(
|
|
550
|
+
"create_category",
|
|
551
|
+
"Create a new category",
|
|
552
|
+
{
|
|
553
|
+
name: z.string().describe("Category name"),
|
|
554
|
+
slug: z.string().optional(),
|
|
555
|
+
description: z.string().optional(),
|
|
556
|
+
parentId: z.number().optional().describe("Parent category ID for nesting"),
|
|
557
|
+
image: z.string().optional()
|
|
558
|
+
},
|
|
559
|
+
async (params) => {
|
|
560
|
+
try {
|
|
561
|
+
return jsonResponse(await client.post("/categories", params));
|
|
562
|
+
} catch (error) {
|
|
563
|
+
handleApiError(error);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
);
|
|
567
|
+
server.tool(
|
|
568
|
+
"update_category",
|
|
569
|
+
"Update an existing category",
|
|
570
|
+
{
|
|
571
|
+
id: z.number().describe("Category ID"),
|
|
572
|
+
name: z.string().optional(),
|
|
573
|
+
slug: z.string().optional(),
|
|
574
|
+
description: z.string().optional(),
|
|
575
|
+
parentId: z.number().optional(),
|
|
576
|
+
image: z.string().optional()
|
|
577
|
+
},
|
|
578
|
+
async ({ id, ...body }) => {
|
|
579
|
+
try {
|
|
580
|
+
return jsonResponse(await client.put(`/categories/${id}`, body));
|
|
581
|
+
} catch (error) {
|
|
582
|
+
handleApiError(error);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
);
|
|
586
|
+
server.tool(
|
|
587
|
+
"delete_category",
|
|
588
|
+
"Delete a category",
|
|
589
|
+
{ id: z.number().describe("Category ID") },
|
|
590
|
+
async ({ id }) => {
|
|
591
|
+
try {
|
|
592
|
+
return jsonResponse(await client.delete(`/categories/${id}`));
|
|
593
|
+
} catch (error) {
|
|
594
|
+
handleApiError(error);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
);
|
|
598
|
+
server.tool(
|
|
599
|
+
"copy_category_to_locale",
|
|
600
|
+
"Copy a category to another language. Automatically resolves parent in target locale.",
|
|
601
|
+
{
|
|
602
|
+
id: z.number().describe("Category ID"),
|
|
603
|
+
targetLocale: z.string().describe('Target locale code (e.g., "en", "es")')
|
|
604
|
+
},
|
|
605
|
+
async ({ id, ...body }) => {
|
|
606
|
+
try {
|
|
607
|
+
return jsonResponse(await client.post(`/categories/${id}/copy-to-locale`, body));
|
|
608
|
+
} catch (error) {
|
|
609
|
+
handleApiError(error);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
function registerTagTools(server, client) {
|
|
615
|
+
server.tool(
|
|
616
|
+
"list_tags",
|
|
617
|
+
"List tags with pagination",
|
|
618
|
+
{
|
|
619
|
+
page: z.number().optional(),
|
|
620
|
+
perPage: z.number().optional(),
|
|
621
|
+
search: z.string().optional(),
|
|
622
|
+
locale: z.string().optional()
|
|
623
|
+
},
|
|
624
|
+
async (params) => {
|
|
625
|
+
try {
|
|
626
|
+
return jsonResponse(await client.get("/tags", params));
|
|
627
|
+
} catch (error) {
|
|
628
|
+
handleApiError(error);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
);
|
|
632
|
+
server.tool(
|
|
633
|
+
"get_tag",
|
|
634
|
+
"Get a single tag by ID",
|
|
635
|
+
{ id: z.number().describe("Tag ID") },
|
|
636
|
+
async ({ id }) => {
|
|
637
|
+
try {
|
|
638
|
+
return jsonResponse(await client.get(`/tags/${id}`));
|
|
639
|
+
} catch (error) {
|
|
640
|
+
handleApiError(error);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
);
|
|
644
|
+
server.tool(
|
|
645
|
+
"create_tag",
|
|
646
|
+
"Create a new tag",
|
|
647
|
+
{
|
|
648
|
+
name: z.string().describe("Tag name"),
|
|
649
|
+
slug: z.string().optional()
|
|
650
|
+
},
|
|
651
|
+
async (params) => {
|
|
652
|
+
try {
|
|
653
|
+
return jsonResponse(await client.post("/tags", params));
|
|
654
|
+
} catch (error) {
|
|
655
|
+
handleApiError(error);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
);
|
|
659
|
+
server.tool(
|
|
660
|
+
"update_tag",
|
|
661
|
+
"Update an existing tag",
|
|
662
|
+
{
|
|
663
|
+
id: z.number().describe("Tag ID"),
|
|
664
|
+
name: z.string().optional(),
|
|
665
|
+
slug: z.string().optional()
|
|
666
|
+
},
|
|
667
|
+
async ({ id, ...body }) => {
|
|
668
|
+
try {
|
|
669
|
+
return jsonResponse(await client.put(`/tags/${id}`, body));
|
|
670
|
+
} catch (error) {
|
|
671
|
+
handleApiError(error);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
);
|
|
675
|
+
server.tool(
|
|
676
|
+
"delete_tag",
|
|
677
|
+
"Delete a tag",
|
|
678
|
+
{ id: z.number().describe("Tag ID") },
|
|
679
|
+
async ({ id }) => {
|
|
680
|
+
try {
|
|
681
|
+
return jsonResponse(await client.delete(`/tags/${id}`));
|
|
682
|
+
} catch (error) {
|
|
683
|
+
handleApiError(error);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
);
|
|
687
|
+
server.tool(
|
|
688
|
+
"get_tag_translations",
|
|
689
|
+
"Get translation status for a tag across all enabled locales",
|
|
690
|
+
{ id: z.number().describe("Tag ID") },
|
|
691
|
+
async ({ id }) => {
|
|
692
|
+
try {
|
|
693
|
+
return jsonResponse(await client.get(`/tags/${id}/translations`));
|
|
694
|
+
} catch (error) {
|
|
695
|
+
handleApiError(error);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
);
|
|
699
|
+
server.tool(
|
|
700
|
+
"copy_tag_to_locale",
|
|
701
|
+
"Copy a tag to another language",
|
|
702
|
+
{
|
|
703
|
+
id: z.number().describe("Tag ID"),
|
|
704
|
+
targetLocale: z.string().describe('Target locale code (e.g., "en", "es")')
|
|
705
|
+
},
|
|
706
|
+
async ({ id, ...body }) => {
|
|
707
|
+
try {
|
|
708
|
+
return jsonResponse(await client.post(`/tags/${id}/copy-to-locale`, body));
|
|
709
|
+
} catch (error) {
|
|
710
|
+
handleApiError(error);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
);
|
|
714
|
+
}
|
|
715
|
+
function registerVariableTools(server, client) {
|
|
716
|
+
server.tool(
|
|
717
|
+
"list_variables",
|
|
718
|
+
"List all site variables (reusable key-value pairs like company name, phone, etc.)",
|
|
719
|
+
{
|
|
720
|
+
page: z.number().optional(),
|
|
721
|
+
perPage: z.number().optional(),
|
|
722
|
+
search: z.string().optional()
|
|
723
|
+
},
|
|
724
|
+
async (params) => {
|
|
725
|
+
try {
|
|
726
|
+
return jsonResponse(await client.get("/variables", params));
|
|
727
|
+
} catch (error) {
|
|
728
|
+
handleApiError(error);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
);
|
|
732
|
+
server.tool(
|
|
733
|
+
"get_variable",
|
|
734
|
+
"Get a single variable by ID",
|
|
735
|
+
{ id: z.number().describe("Variable ID") },
|
|
736
|
+
async ({ id }) => {
|
|
737
|
+
try {
|
|
738
|
+
return jsonResponse(await client.get(`/variables/${id}`));
|
|
739
|
+
} catch (error) {
|
|
740
|
+
handleApiError(error);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
);
|
|
744
|
+
server.tool(
|
|
745
|
+
"create_variable",
|
|
746
|
+
"Create a new site variable",
|
|
747
|
+
{
|
|
748
|
+
key: z.string().describe("Variable key"),
|
|
749
|
+
value: z.string().describe("Variable value"),
|
|
750
|
+
description: z.string().optional()
|
|
751
|
+
},
|
|
752
|
+
async (params) => {
|
|
753
|
+
try {
|
|
754
|
+
return jsonResponse(await client.post("/variables", params));
|
|
755
|
+
} catch (error) {
|
|
756
|
+
handleApiError(error);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
);
|
|
760
|
+
server.tool(
|
|
761
|
+
"update_variable",
|
|
762
|
+
"Update a variable value or description",
|
|
763
|
+
{
|
|
764
|
+
id: z.number().describe("Variable ID"),
|
|
765
|
+
key: z.string().optional(),
|
|
766
|
+
value: z.string().optional(),
|
|
767
|
+
description: z.string().optional()
|
|
768
|
+
},
|
|
769
|
+
async ({ id, ...body }) => {
|
|
770
|
+
try {
|
|
771
|
+
return jsonResponse(await client.put(`/variables/${id}`, body));
|
|
772
|
+
} catch (error) {
|
|
773
|
+
handleApiError(error);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
);
|
|
777
|
+
server.tool(
|
|
778
|
+
"delete_variable",
|
|
779
|
+
"Delete a variable",
|
|
780
|
+
{ id: z.number().describe("Variable ID") },
|
|
781
|
+
async ({ id }) => {
|
|
782
|
+
try {
|
|
783
|
+
return jsonResponse(await client.delete(`/variables/${id}`));
|
|
784
|
+
} catch (error) {
|
|
785
|
+
handleApiError(error);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
);
|
|
789
|
+
server.tool(
|
|
790
|
+
"get_variable_translations",
|
|
791
|
+
"Get translation status for a variable",
|
|
792
|
+
{ id: z.number().describe("Variable ID") },
|
|
793
|
+
async ({ id }) => {
|
|
794
|
+
try {
|
|
795
|
+
return jsonResponse(await client.get(`/variables/${id}/translations`));
|
|
796
|
+
} catch (error) {
|
|
797
|
+
handleApiError(error);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
);
|
|
801
|
+
server.tool(
|
|
802
|
+
"copy_variable_to_locale",
|
|
803
|
+
"Copy a variable to another language",
|
|
804
|
+
{
|
|
805
|
+
id: z.number().describe("Variable ID"),
|
|
806
|
+
targetLocale: z.string().describe('Target locale code (e.g., "en", "es")')
|
|
807
|
+
},
|
|
808
|
+
async ({ id, ...body }) => {
|
|
809
|
+
try {
|
|
810
|
+
return jsonResponse(await client.post(`/variables/${id}/copy-to-locale`, body));
|
|
811
|
+
} catch (error) {
|
|
812
|
+
handleApiError(error);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
function registerMediaTools(server, client) {
|
|
818
|
+
server.tool(
|
|
819
|
+
"list_media",
|
|
820
|
+
"List media files with optional folder filtering",
|
|
821
|
+
{
|
|
822
|
+
page: z.number().optional(),
|
|
823
|
+
perPage: z.number().optional(),
|
|
824
|
+
search: z.string().optional(),
|
|
825
|
+
type: z.enum(["image", "video", "document"]).optional(),
|
|
826
|
+
folderId: z.number().optional().describe("Filter by folder ID")
|
|
827
|
+
},
|
|
828
|
+
async (params) => {
|
|
829
|
+
try {
|
|
830
|
+
return jsonResponse(await client.get("/media", params));
|
|
831
|
+
} catch (error) {
|
|
832
|
+
handleApiError(error);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
);
|
|
836
|
+
server.tool(
|
|
837
|
+
"get_media",
|
|
838
|
+
"Get a single media file details (URL, dimensions, metadata)",
|
|
839
|
+
{ id: z.number().describe("Media ID") },
|
|
840
|
+
async ({ id }) => {
|
|
841
|
+
try {
|
|
842
|
+
return jsonResponse(await client.get(`/media/${id}`));
|
|
843
|
+
} catch (error) {
|
|
844
|
+
handleApiError(error);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
);
|
|
848
|
+
server.tool(
|
|
849
|
+
"upload_media",
|
|
850
|
+
"Upload a media file from a URL. The file is downloaded by the server.",
|
|
851
|
+
{
|
|
852
|
+
url: z.string().url().describe("URL of the file to upload"),
|
|
853
|
+
name: z.string().optional(),
|
|
854
|
+
alt: z.string().optional().describe("Alt text for accessibility"),
|
|
855
|
+
folderId: z.number().optional().describe("Target folder ID")
|
|
856
|
+
},
|
|
857
|
+
async (params) => {
|
|
858
|
+
try {
|
|
859
|
+
return jsonResponse(await client.post("/media", params));
|
|
860
|
+
} catch (error) {
|
|
861
|
+
handleApiError(error);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
);
|
|
865
|
+
server.tool(
|
|
866
|
+
"update_media",
|
|
867
|
+
"Update media metadata (name, alt text, description)",
|
|
868
|
+
{
|
|
869
|
+
id: z.number().describe("Media ID"),
|
|
870
|
+
name: z.string().optional(),
|
|
871
|
+
alt: z.string().optional(),
|
|
872
|
+
description: z.string().optional()
|
|
873
|
+
},
|
|
874
|
+
async ({ id, ...body }) => {
|
|
875
|
+
try {
|
|
876
|
+
return jsonResponse(await client.put(`/media/${id}`, body));
|
|
877
|
+
} catch (error) {
|
|
878
|
+
handleApiError(error);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
);
|
|
882
|
+
server.tool(
|
|
883
|
+
"delete_media",
|
|
884
|
+
"Move a media file to trash",
|
|
885
|
+
{ id: z.number().describe("Media ID") },
|
|
886
|
+
async ({ id }) => {
|
|
887
|
+
try {
|
|
888
|
+
return jsonResponse(await client.delete(`/media/${id}`));
|
|
889
|
+
} catch (error) {
|
|
890
|
+
handleApiError(error);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
);
|
|
894
|
+
server.tool(
|
|
895
|
+
"get_media_references",
|
|
896
|
+
"Find all content items that use this media file",
|
|
897
|
+
{ id: z.number().describe("Media ID") },
|
|
898
|
+
async ({ id }) => {
|
|
899
|
+
try {
|
|
900
|
+
return jsonResponse(await client.get(`/media/${id}/references`));
|
|
901
|
+
} catch (error) {
|
|
902
|
+
handleApiError(error);
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
);
|
|
906
|
+
server.tool(
|
|
907
|
+
"list_media_folders",
|
|
908
|
+
"List media folders (root or children of a parent folder)",
|
|
909
|
+
{ parentId: z.number().optional().describe("Parent folder ID (omit for root)") },
|
|
910
|
+
async (params) => {
|
|
911
|
+
try {
|
|
912
|
+
return jsonResponse(await client.get("/media-folders", params));
|
|
913
|
+
} catch (error) {
|
|
914
|
+
handleApiError(error);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
);
|
|
918
|
+
server.tool(
|
|
919
|
+
"get_media_folder_tree",
|
|
920
|
+
"Get the complete folder hierarchy as a tree",
|
|
921
|
+
{},
|
|
922
|
+
async () => {
|
|
923
|
+
try {
|
|
924
|
+
return jsonResponse(await client.get("/media-folders/tree"));
|
|
925
|
+
} catch (error) {
|
|
926
|
+
handleApiError(error);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
);
|
|
930
|
+
server.tool(
|
|
931
|
+
"create_media_folder",
|
|
932
|
+
"Create a new media folder (max 3 levels deep)",
|
|
933
|
+
{
|
|
934
|
+
name: z.string().describe("Folder name"),
|
|
935
|
+
parentId: z.number().optional().describe("Parent folder ID")
|
|
936
|
+
},
|
|
937
|
+
async (params) => {
|
|
938
|
+
try {
|
|
939
|
+
return jsonResponse(await client.post("/media-folders", params));
|
|
940
|
+
} catch (error) {
|
|
941
|
+
handleApiError(error);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
);
|
|
945
|
+
server.tool(
|
|
946
|
+
"rename_media_folder",
|
|
947
|
+
"Rename a media folder",
|
|
948
|
+
{
|
|
949
|
+
id: z.number().describe("Folder ID"),
|
|
950
|
+
name: z.string().describe("New folder name")
|
|
951
|
+
},
|
|
952
|
+
async ({ id, ...body }) => {
|
|
953
|
+
try {
|
|
954
|
+
return jsonResponse(await client.put(`/media-folders/${id}`, body));
|
|
955
|
+
} catch (error) {
|
|
956
|
+
handleApiError(error);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
);
|
|
960
|
+
server.tool(
|
|
961
|
+
"delete_media_folder",
|
|
962
|
+
"Delete a media folder. Choose whether to delete files or move them to root.",
|
|
963
|
+
{
|
|
964
|
+
id: z.number().describe("Folder ID"),
|
|
965
|
+
action: z.enum(["delete_files", "move_to_root"]).describe("What to do with files inside")
|
|
966
|
+
},
|
|
967
|
+
async ({ id, action }) => {
|
|
968
|
+
try {
|
|
969
|
+
return jsonResponse(await client.delete(`/media-folders/${id}`, { action }));
|
|
970
|
+
} catch (error) {
|
|
971
|
+
handleApiError(error);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
);
|
|
975
|
+
server.tool(
|
|
976
|
+
"move_media_to_folder",
|
|
977
|
+
"Move one or more media files into a folder (or back to root with null folderId)",
|
|
978
|
+
{
|
|
979
|
+
mediaIds: z.array(z.number()).describe("Array of media IDs to move"),
|
|
980
|
+
folderId: z.number().nullable().describe("Target folder ID (null for root)")
|
|
981
|
+
},
|
|
982
|
+
async (params) => {
|
|
983
|
+
try {
|
|
984
|
+
return jsonResponse(await client.post("/media-folders/move-media", params));
|
|
985
|
+
} catch (error) {
|
|
986
|
+
handleApiError(error);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
);
|
|
990
|
+
}
|
|
991
|
+
function registerFormTools(server, client) {
|
|
992
|
+
server.tool(
|
|
993
|
+
"list_forms",
|
|
994
|
+
"List all forms (contact, newsletter, etc.)",
|
|
995
|
+
{
|
|
996
|
+
page: z.number().optional(),
|
|
997
|
+
perPage: z.number().optional(),
|
|
998
|
+
status: z.string().optional(),
|
|
999
|
+
search: z.string().optional()
|
|
1000
|
+
},
|
|
1001
|
+
async (params) => {
|
|
1002
|
+
try {
|
|
1003
|
+
return jsonResponse(await client.get("/forms", params));
|
|
1004
|
+
} catch (error) {
|
|
1005
|
+
handleApiError(error);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
);
|
|
1009
|
+
server.tool(
|
|
1010
|
+
"get_form",
|
|
1011
|
+
"Get a form with its schema definition",
|
|
1012
|
+
{ id: z.number().describe("Form ID") },
|
|
1013
|
+
async ({ id }) => {
|
|
1014
|
+
try {
|
|
1015
|
+
return jsonResponse(await client.get(`/forms/${id}`));
|
|
1016
|
+
} catch (error) {
|
|
1017
|
+
handleApiError(error);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
);
|
|
1021
|
+
server.tool(
|
|
1022
|
+
"create_form",
|
|
1023
|
+
"Create a new form with field schema",
|
|
1024
|
+
{
|
|
1025
|
+
name: z.string().describe("Form name"),
|
|
1026
|
+
slug: z.string().optional(),
|
|
1027
|
+
schema: z.any().describe("Form field schema"),
|
|
1028
|
+
settings: z.record(z.any()).optional(),
|
|
1029
|
+
status: z.string().optional()
|
|
1030
|
+
},
|
|
1031
|
+
async (params) => {
|
|
1032
|
+
try {
|
|
1033
|
+
return jsonResponse(await client.post("/forms", params));
|
|
1034
|
+
} catch (error) {
|
|
1035
|
+
handleApiError(error);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
);
|
|
1039
|
+
server.tool(
|
|
1040
|
+
"update_form",
|
|
1041
|
+
"Update a form schema or settings",
|
|
1042
|
+
{
|
|
1043
|
+
id: z.number().describe("Form ID"),
|
|
1044
|
+
name: z.string().optional(),
|
|
1045
|
+
schema: z.any().optional(),
|
|
1046
|
+
settings: z.record(z.any()).optional(),
|
|
1047
|
+
status: z.string().optional()
|
|
1048
|
+
},
|
|
1049
|
+
async ({ id, ...body }) => {
|
|
1050
|
+
try {
|
|
1051
|
+
return jsonResponse(await client.put(`/forms/${id}`, body));
|
|
1052
|
+
} catch (error) {
|
|
1053
|
+
handleApiError(error);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
);
|
|
1057
|
+
server.tool(
|
|
1058
|
+
"delete_form",
|
|
1059
|
+
"Delete a form and all its submissions",
|
|
1060
|
+
{ id: z.number().describe("Form ID") },
|
|
1061
|
+
async ({ id }) => {
|
|
1062
|
+
try {
|
|
1063
|
+
return jsonResponse(await client.delete(`/forms/${id}`));
|
|
1064
|
+
} catch (error) {
|
|
1065
|
+
handleApiError(error);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
);
|
|
1069
|
+
server.tool(
|
|
1070
|
+
"list_form_submissions",
|
|
1071
|
+
"List submissions for a specific form",
|
|
1072
|
+
{
|
|
1073
|
+
id: z.number().describe("Form ID"),
|
|
1074
|
+
page: z.number().optional(),
|
|
1075
|
+
perPage: z.number().optional(),
|
|
1076
|
+
status: z.enum(["new", "read", "archived", "spam"]).optional()
|
|
1077
|
+
},
|
|
1078
|
+
async ({ id, ...params }) => {
|
|
1079
|
+
try {
|
|
1080
|
+
return jsonResponse(await client.get(`/forms/${id}/submissions`, params));
|
|
1081
|
+
} catch (error) {
|
|
1082
|
+
handleApiError(error);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
);
|
|
1086
|
+
server.tool(
|
|
1087
|
+
"get_form_submission",
|
|
1088
|
+
"Get a single submission with all submitted data",
|
|
1089
|
+
{
|
|
1090
|
+
id: z.number().describe("Form ID"),
|
|
1091
|
+
submissionId: z.number().describe("Submission ID")
|
|
1092
|
+
},
|
|
1093
|
+
async ({ id, submissionId }) => {
|
|
1094
|
+
try {
|
|
1095
|
+
return jsonResponse(await client.get(`/forms/${id}/submissions/${submissionId}`));
|
|
1096
|
+
} catch (error) {
|
|
1097
|
+
handleApiError(error);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
);
|
|
1101
|
+
server.tool(
|
|
1102
|
+
"update_submission_status",
|
|
1103
|
+
"Change the status of a form submission (mark as read, archive, spam)",
|
|
1104
|
+
{
|
|
1105
|
+
id: z.number().describe("Form ID"),
|
|
1106
|
+
submissionId: z.number().describe("Submission ID"),
|
|
1107
|
+
status: z.enum(["new", "read", "archived", "spam"]).describe("New status")
|
|
1108
|
+
},
|
|
1109
|
+
async ({ id, submissionId, status }) => {
|
|
1110
|
+
try {
|
|
1111
|
+
return jsonResponse(await client.put(`/forms/${id}/submissions/${submissionId}/status`, { status }));
|
|
1112
|
+
} catch (error) {
|
|
1113
|
+
handleApiError(error);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
);
|
|
1117
|
+
server.tool(
|
|
1118
|
+
"delete_form_submission",
|
|
1119
|
+
"Delete a form submission",
|
|
1120
|
+
{
|
|
1121
|
+
id: z.number().describe("Form ID"),
|
|
1122
|
+
submissionId: z.number().describe("Submission ID")
|
|
1123
|
+
},
|
|
1124
|
+
async ({ id, submissionId }) => {
|
|
1125
|
+
try {
|
|
1126
|
+
return jsonResponse(await client.delete(`/forms/${id}/submissions/${submissionId}`));
|
|
1127
|
+
} catch (error) {
|
|
1128
|
+
handleApiError(error);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
);
|
|
1132
|
+
server.tool(
|
|
1133
|
+
"export_form_submissions",
|
|
1134
|
+
"Export all submissions for a form as CSV",
|
|
1135
|
+
{ id: z.number().describe("Form ID") },
|
|
1136
|
+
async ({ id }) => {
|
|
1137
|
+
try {
|
|
1138
|
+
return jsonResponse(await client.get(`/forms/${id}/submissions/export`));
|
|
1139
|
+
} catch (error) {
|
|
1140
|
+
handleApiError(error);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
);
|
|
1144
|
+
}
|
|
1145
|
+
function registerReviewTools(server, client) {
|
|
1146
|
+
server.tool(
|
|
1147
|
+
"list_reviews",
|
|
1148
|
+
"List customer reviews with filtering",
|
|
1149
|
+
{
|
|
1150
|
+
page: z.number().optional(),
|
|
1151
|
+
perPage: z.number().optional(),
|
|
1152
|
+
status: z.enum(["pending", "approved", "rejected"]).optional(),
|
|
1153
|
+
rating: z.number().optional(),
|
|
1154
|
+
search: z.string().optional()
|
|
1155
|
+
},
|
|
1156
|
+
async (params) => {
|
|
1157
|
+
try {
|
|
1158
|
+
return jsonResponse(await client.get("/reviews", params));
|
|
1159
|
+
} catch (error) {
|
|
1160
|
+
handleApiError(error);
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
);
|
|
1164
|
+
server.tool(
|
|
1165
|
+
"get_review",
|
|
1166
|
+
"Get a single review with all details",
|
|
1167
|
+
{ id: z.number().describe("Review ID") },
|
|
1168
|
+
async ({ id }) => {
|
|
1169
|
+
try {
|
|
1170
|
+
return jsonResponse(await client.get(`/reviews/${id}`));
|
|
1171
|
+
} catch (error) {
|
|
1172
|
+
handleApiError(error);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
);
|
|
1176
|
+
server.tool(
|
|
1177
|
+
"create_review",
|
|
1178
|
+
"Create a new review manually",
|
|
1179
|
+
{
|
|
1180
|
+
authorName: z.string().describe("Reviewer name"),
|
|
1181
|
+
content: z.string().describe("Review text"),
|
|
1182
|
+
rating: z.number().min(1).max(5).describe("Rating (1-5)"),
|
|
1183
|
+
email: z.string().optional(),
|
|
1184
|
+
status: z.enum(["pending", "approved", "rejected"]).optional()
|
|
1185
|
+
},
|
|
1186
|
+
async (params) => {
|
|
1187
|
+
try {
|
|
1188
|
+
return jsonResponse(await client.post("/reviews", params));
|
|
1189
|
+
} catch (error) {
|
|
1190
|
+
handleApiError(error);
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
);
|
|
1194
|
+
server.tool(
|
|
1195
|
+
"update_review",
|
|
1196
|
+
"Update a review (content, rating, status)",
|
|
1197
|
+
{
|
|
1198
|
+
id: z.number().describe("Review ID"),
|
|
1199
|
+
content: z.string().optional(),
|
|
1200
|
+
rating: z.number().min(1).max(5).optional(),
|
|
1201
|
+
status: z.enum(["pending", "approved", "rejected"]).optional(),
|
|
1202
|
+
response: z.string().optional().describe("Owner response to the review")
|
|
1203
|
+
},
|
|
1204
|
+
async ({ id, ...body }) => {
|
|
1205
|
+
try {
|
|
1206
|
+
return jsonResponse(await client.put(`/reviews/${id}`, body));
|
|
1207
|
+
} catch (error) {
|
|
1208
|
+
handleApiError(error);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
);
|
|
1212
|
+
server.tool(
|
|
1213
|
+
"delete_review",
|
|
1214
|
+
"Delete a review",
|
|
1215
|
+
{ id: z.number().describe("Review ID") },
|
|
1216
|
+
async ({ id }) => {
|
|
1217
|
+
try {
|
|
1218
|
+
return jsonResponse(await client.delete(`/reviews/${id}`));
|
|
1219
|
+
} catch (error) {
|
|
1220
|
+
handleApiError(error);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
);
|
|
1224
|
+
server.tool(
|
|
1225
|
+
"get_review_stats",
|
|
1226
|
+
"Get review statistics (average rating, count by status, distribution)",
|
|
1227
|
+
{},
|
|
1228
|
+
async () => {
|
|
1229
|
+
try {
|
|
1230
|
+
return jsonResponse(await client.get("/reviews/stats"));
|
|
1231
|
+
} catch (error) {
|
|
1232
|
+
handleApiError(error);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
);
|
|
1236
|
+
server.tool(
|
|
1237
|
+
"bulk_moderate_reviews",
|
|
1238
|
+
"Approve or reject multiple reviews at once",
|
|
1239
|
+
{
|
|
1240
|
+
ids: z.array(z.number()).describe("Array of review IDs"),
|
|
1241
|
+
status: z.enum(["pending", "approved", "rejected"]).describe("New status")
|
|
1242
|
+
},
|
|
1243
|
+
async (params) => {
|
|
1244
|
+
try {
|
|
1245
|
+
return jsonResponse(await client.post("/reviews/bulk-status", params));
|
|
1246
|
+
} catch (error) {
|
|
1247
|
+
handleApiError(error);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
);
|
|
1251
|
+
server.tool(
|
|
1252
|
+
"bulk_delete_reviews",
|
|
1253
|
+
"Delete multiple reviews at once",
|
|
1254
|
+
{ ids: z.array(z.number()).describe("Array of review IDs") },
|
|
1255
|
+
async (params) => {
|
|
1256
|
+
try {
|
|
1257
|
+
return jsonResponse(await client.post("/reviews/bulk-delete", params));
|
|
1258
|
+
} catch (error) {
|
|
1259
|
+
handleApiError(error);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
);
|
|
1263
|
+
server.tool(
|
|
1264
|
+
"get_review_settings",
|
|
1265
|
+
"Get review moderation settings (auto-approve, require rating, etc.)",
|
|
1266
|
+
{},
|
|
1267
|
+
async () => {
|
|
1268
|
+
try {
|
|
1269
|
+
return jsonResponse(await client.get("/reviews/settings"));
|
|
1270
|
+
} catch (error) {
|
|
1271
|
+
handleApiError(error);
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
);
|
|
1275
|
+
server.tool(
|
|
1276
|
+
"update_review_settings",
|
|
1277
|
+
"Update review moderation settings",
|
|
1278
|
+
{
|
|
1279
|
+
autoApprove: z.boolean().optional(),
|
|
1280
|
+
requireModeration: z.boolean().optional(),
|
|
1281
|
+
allowAnonymous: z.boolean().optional(),
|
|
1282
|
+
ratingRequired: z.boolean().optional()
|
|
1283
|
+
},
|
|
1284
|
+
async (params) => {
|
|
1285
|
+
try {
|
|
1286
|
+
return jsonResponse(await client.put("/reviews/settings", params));
|
|
1287
|
+
} catch (error) {
|
|
1288
|
+
handleApiError(error);
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
);
|
|
1292
|
+
}
|
|
1293
|
+
function registerSeoTools(server, client) {
|
|
1294
|
+
server.tool(
|
|
1295
|
+
"get_seo_settings",
|
|
1296
|
+
"Get global SEO settings (default meta, robots.txt config)",
|
|
1297
|
+
{},
|
|
1298
|
+
async () => {
|
|
1299
|
+
try {
|
|
1300
|
+
return jsonResponse(await client.get("/seo/settings"));
|
|
1301
|
+
} catch (error) {
|
|
1302
|
+
handleApiError(error);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
);
|
|
1306
|
+
server.tool(
|
|
1307
|
+
"update_seo_settings",
|
|
1308
|
+
"Update global SEO settings",
|
|
1309
|
+
{
|
|
1310
|
+
defaultTitle: z.string().optional(),
|
|
1311
|
+
defaultDescription: z.string().optional(),
|
|
1312
|
+
robotsTxt: z.string().optional(),
|
|
1313
|
+
indexNowEnabled: z.boolean().optional()
|
|
1314
|
+
},
|
|
1315
|
+
async (params) => {
|
|
1316
|
+
try {
|
|
1317
|
+
return jsonResponse(await client.put("/seo/settings", params));
|
|
1318
|
+
} catch (error) {
|
|
1319
|
+
handleApiError(error);
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
);
|
|
1323
|
+
server.tool(
|
|
1324
|
+
"get_seo_sitemap_json",
|
|
1325
|
+
"Get the generated sitemap as JSON (for analysis/review)",
|
|
1326
|
+
{},
|
|
1327
|
+
async () => {
|
|
1328
|
+
try {
|
|
1329
|
+
return jsonResponse(await client.get("/seo/sitemap.json"));
|
|
1330
|
+
} catch (error) {
|
|
1331
|
+
handleApiError(error);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
);
|
|
1335
|
+
server.tool(
|
|
1336
|
+
"test_indexnow",
|
|
1337
|
+
"Test IndexNow submission for a specific URL",
|
|
1338
|
+
{ url: z.string().describe("URL to submit to IndexNow") },
|
|
1339
|
+
async (params) => {
|
|
1340
|
+
try {
|
|
1341
|
+
return jsonResponse(await client.get("/seo/indexnow/test", params));
|
|
1342
|
+
} catch (error) {
|
|
1343
|
+
handleApiError(error);
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
);
|
|
1347
|
+
server.tool(
|
|
1348
|
+
"get_indexnow_logs",
|
|
1349
|
+
"Get logs of IndexNow submissions to search engines",
|
|
1350
|
+
{
|
|
1351
|
+
page: z.number().optional(),
|
|
1352
|
+
perPage: z.number().optional()
|
|
1353
|
+
},
|
|
1354
|
+
async (params) => {
|
|
1355
|
+
try {
|
|
1356
|
+
return jsonResponse(await client.get("/seo/indexnow/logs", params));
|
|
1357
|
+
} catch (error) {
|
|
1358
|
+
handleApiError(error);
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
);
|
|
1362
|
+
server.tool(
|
|
1363
|
+
"get_indexnow_stats",
|
|
1364
|
+
"Get IndexNow submission statistics (success/error counts)",
|
|
1365
|
+
{},
|
|
1366
|
+
async () => {
|
|
1367
|
+
try {
|
|
1368
|
+
return jsonResponse(await client.get("/seo/indexnow/stats"));
|
|
1369
|
+
} catch (error) {
|
|
1370
|
+
handleApiError(error);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
);
|
|
1374
|
+
}
|
|
1375
|
+
function registerSitemapTools(server, client) {
|
|
1376
|
+
server.tool(
|
|
1377
|
+
"list_sitemap_entries",
|
|
1378
|
+
"List custom sitemap entries",
|
|
1379
|
+
{
|
|
1380
|
+
page: z.number().optional(),
|
|
1381
|
+
perPage: z.number().optional()
|
|
1382
|
+
},
|
|
1383
|
+
async (params) => {
|
|
1384
|
+
try {
|
|
1385
|
+
return jsonResponse(await client.get("/sitemap/entries", params));
|
|
1386
|
+
} catch (error) {
|
|
1387
|
+
handleApiError(error);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
);
|
|
1391
|
+
server.tool(
|
|
1392
|
+
"create_sitemap_entry",
|
|
1393
|
+
"Add a custom entry to the sitemap",
|
|
1394
|
+
{
|
|
1395
|
+
loc: z.string().describe("URL path"),
|
|
1396
|
+
changefreq: z.string().optional().describe("Change frequency (daily, weekly, monthly, etc.)"),
|
|
1397
|
+
priority: z.number().optional().describe("Priority (0.0 to 1.0)")
|
|
1398
|
+
},
|
|
1399
|
+
async (params) => {
|
|
1400
|
+
try {
|
|
1401
|
+
return jsonResponse(await client.post("/sitemap/entries", params));
|
|
1402
|
+
} catch (error) {
|
|
1403
|
+
handleApiError(error);
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
);
|
|
1407
|
+
server.tool(
|
|
1408
|
+
"update_sitemap_entry",
|
|
1409
|
+
"Update a sitemap entry",
|
|
1410
|
+
{
|
|
1411
|
+
id: z.number().describe("Entry ID"),
|
|
1412
|
+
loc: z.string().optional(),
|
|
1413
|
+
changefreq: z.string().optional(),
|
|
1414
|
+
priority: z.number().optional()
|
|
1415
|
+
},
|
|
1416
|
+
async ({ id, ...body }) => {
|
|
1417
|
+
try {
|
|
1418
|
+
return jsonResponse(await client.put(`/sitemap/entries/${id}`, body));
|
|
1419
|
+
} catch (error) {
|
|
1420
|
+
handleApiError(error);
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
);
|
|
1424
|
+
server.tool(
|
|
1425
|
+
"delete_sitemap_entry",
|
|
1426
|
+
"Remove a custom entry from the sitemap",
|
|
1427
|
+
{ id: z.number().describe("Entry ID") },
|
|
1428
|
+
async ({ id }) => {
|
|
1429
|
+
try {
|
|
1430
|
+
return jsonResponse(await client.delete(`/sitemap/entries/${id}`));
|
|
1431
|
+
} catch (error) {
|
|
1432
|
+
handleApiError(error);
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
);
|
|
1436
|
+
server.tool(
|
|
1437
|
+
"preview_sitemap",
|
|
1438
|
+
"Preview the generated sitemap XML",
|
|
1439
|
+
{},
|
|
1440
|
+
async () => {
|
|
1441
|
+
try {
|
|
1442
|
+
return jsonResponse(await client.get("/sitemap/preview"));
|
|
1443
|
+
} catch (error) {
|
|
1444
|
+
handleApiError(error);
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
);
|
|
1448
|
+
server.tool(
|
|
1449
|
+
"export_sitemap_entries",
|
|
1450
|
+
"Export all sitemap entries as CSV",
|
|
1451
|
+
{},
|
|
1452
|
+
async () => {
|
|
1453
|
+
try {
|
|
1454
|
+
return jsonResponse(await client.get("/sitemap/entries/export"));
|
|
1455
|
+
} catch (error) {
|
|
1456
|
+
handleApiError(error);
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
);
|
|
1460
|
+
server.tool(
|
|
1461
|
+
"import_sitemap_entries",
|
|
1462
|
+
"Import sitemap entries from JSON",
|
|
1463
|
+
{
|
|
1464
|
+
entries: z.array(z.object({
|
|
1465
|
+
loc: z.string(),
|
|
1466
|
+
changefreq: z.string().optional(),
|
|
1467
|
+
priority: z.number().optional()
|
|
1468
|
+
})).describe("Array of sitemap entries to import")
|
|
1469
|
+
},
|
|
1470
|
+
async (params) => {
|
|
1471
|
+
try {
|
|
1472
|
+
return jsonResponse(await client.post("/sitemap/entries/import", params));
|
|
1473
|
+
} catch (error) {
|
|
1474
|
+
handleApiError(error);
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
);
|
|
1478
|
+
}
|
|
1479
|
+
function registerRedirectTools(server, client) {
|
|
1480
|
+
server.tool(
|
|
1481
|
+
"list_redirects",
|
|
1482
|
+
"List all URL redirections",
|
|
1483
|
+
{
|
|
1484
|
+
page: z.number().optional(),
|
|
1485
|
+
perPage: z.number().optional(),
|
|
1486
|
+
search: z.string().optional(),
|
|
1487
|
+
statusCode: z.number().optional().describe("Filter by status code (301, 302, 307, 308)"),
|
|
1488
|
+
isActive: z.boolean().optional()
|
|
1489
|
+
},
|
|
1490
|
+
async (params) => {
|
|
1491
|
+
try {
|
|
1492
|
+
return jsonResponse(await client.get("/redirects", params));
|
|
1493
|
+
} catch (error) {
|
|
1494
|
+
handleApiError(error);
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
);
|
|
1498
|
+
server.tool(
|
|
1499
|
+
"get_redirect",
|
|
1500
|
+
"Get a single redirect rule",
|
|
1501
|
+
{ id: z.number().describe("Redirect ID") },
|
|
1502
|
+
async ({ id }) => {
|
|
1503
|
+
try {
|
|
1504
|
+
return jsonResponse(await client.get(`/redirects/${id}`));
|
|
1505
|
+
} catch (error) {
|
|
1506
|
+
handleApiError(error);
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
);
|
|
1510
|
+
server.tool(
|
|
1511
|
+
"create_redirect",
|
|
1512
|
+
"Create a new URL redirection rule",
|
|
1513
|
+
{
|
|
1514
|
+
sourcePath: z.string().describe("Source path to redirect from"),
|
|
1515
|
+
targetPath: z.string().describe("Target path to redirect to"),
|
|
1516
|
+
statusCode: z.number().optional().describe("HTTP status code (default: 301)"),
|
|
1517
|
+
isRegex: z.boolean().optional().describe("Treat sourcePath as regex"),
|
|
1518
|
+
preserveQueryString: z.boolean().optional(),
|
|
1519
|
+
note: z.string().optional()
|
|
1520
|
+
},
|
|
1521
|
+
async (params) => {
|
|
1522
|
+
try {
|
|
1523
|
+
return jsonResponse(await client.post("/redirects", params));
|
|
1524
|
+
} catch (error) {
|
|
1525
|
+
handleApiError(error);
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
);
|
|
1529
|
+
server.tool(
|
|
1530
|
+
"update_redirect",
|
|
1531
|
+
"Update a redirect rule",
|
|
1532
|
+
{
|
|
1533
|
+
id: z.number().describe("Redirect ID"),
|
|
1534
|
+
sourcePath: z.string().optional(),
|
|
1535
|
+
targetPath: z.string().optional(),
|
|
1536
|
+
statusCode: z.number().optional(),
|
|
1537
|
+
isActive: z.boolean().optional(),
|
|
1538
|
+
note: z.string().optional()
|
|
1539
|
+
},
|
|
1540
|
+
async ({ id, ...body }) => {
|
|
1541
|
+
try {
|
|
1542
|
+
return jsonResponse(await client.put(`/redirects/${id}`, body));
|
|
1543
|
+
} catch (error) {
|
|
1544
|
+
handleApiError(error);
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
);
|
|
1548
|
+
server.tool(
|
|
1549
|
+
"delete_redirect",
|
|
1550
|
+
"Delete a redirect rule",
|
|
1551
|
+
{ id: z.number().describe("Redirect ID") },
|
|
1552
|
+
async ({ id }) => {
|
|
1553
|
+
try {
|
|
1554
|
+
return jsonResponse(await client.delete(`/redirects/${id}`));
|
|
1555
|
+
} catch (error) {
|
|
1556
|
+
handleApiError(error);
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
);
|
|
1560
|
+
server.tool(
|
|
1561
|
+
"bulk_delete_redirects",
|
|
1562
|
+
"Delete multiple redirect rules at once",
|
|
1563
|
+
{ ids: z.array(z.number()).describe("Array of redirect IDs") },
|
|
1564
|
+
async (params) => {
|
|
1565
|
+
try {
|
|
1566
|
+
return jsonResponse(await client.post("/redirects/bulk-delete", params));
|
|
1567
|
+
} catch (error) {
|
|
1568
|
+
handleApiError(error);
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
);
|
|
1572
|
+
server.tool(
|
|
1573
|
+
"import_redirects",
|
|
1574
|
+
"Import redirect rules from JSON",
|
|
1575
|
+
{
|
|
1576
|
+
redirects: z.array(z.object({
|
|
1577
|
+
sourcePath: z.string(),
|
|
1578
|
+
targetPath: z.string(),
|
|
1579
|
+
statusCode: z.number().optional(),
|
|
1580
|
+
isRegex: z.boolean().optional(),
|
|
1581
|
+
preserveQueryString: z.boolean().optional()
|
|
1582
|
+
})).describe("Array of redirect rules to import")
|
|
1583
|
+
},
|
|
1584
|
+
async (params) => {
|
|
1585
|
+
try {
|
|
1586
|
+
return jsonResponse(await client.post("/redirects/import", params));
|
|
1587
|
+
} catch (error) {
|
|
1588
|
+
handleApiError(error);
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
);
|
|
1592
|
+
server.tool(
|
|
1593
|
+
"export_redirects",
|
|
1594
|
+
"Export all redirect rules as CSV",
|
|
1595
|
+
{ isActive: z.boolean().optional() },
|
|
1596
|
+
async (params) => {
|
|
1597
|
+
try {
|
|
1598
|
+
return jsonResponse(await client.get("/redirects/export", params));
|
|
1599
|
+
} catch (error) {
|
|
1600
|
+
handleApiError(error);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
);
|
|
1604
|
+
}
|
|
1605
|
+
function registerAnalyticsTools(server, client) {
|
|
1606
|
+
server.tool(
|
|
1607
|
+
"get_analytics_overview",
|
|
1608
|
+
"Get analytics overview (visitors, pageviews, bounce rate, top pages)",
|
|
1609
|
+
{
|
|
1610
|
+
period: z.enum(["7d", "30d", "90d"]).optional(),
|
|
1611
|
+
startDate: z.string().optional().describe("Start date (YYYY-MM-DD)"),
|
|
1612
|
+
endDate: z.string().optional().describe("End date (YYYY-MM-DD)")
|
|
1613
|
+
},
|
|
1614
|
+
async (params) => {
|
|
1615
|
+
try {
|
|
1616
|
+
return jsonResponse(await client.get("/analytics/overview", params));
|
|
1617
|
+
} catch (error) {
|
|
1618
|
+
handleApiError(error);
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
);
|
|
1622
|
+
server.tool(
|
|
1623
|
+
"get_analytics_pages",
|
|
1624
|
+
"Get page-level analytics (views, avg time, bounce rate per page)",
|
|
1625
|
+
{
|
|
1626
|
+
period: z.enum(["7d", "30d", "90d"]).optional(),
|
|
1627
|
+
startDate: z.string().optional(),
|
|
1628
|
+
endDate: z.string().optional(),
|
|
1629
|
+
page: z.number().optional(),
|
|
1630
|
+
perPage: z.number().optional()
|
|
1631
|
+
},
|
|
1632
|
+
async (params) => {
|
|
1633
|
+
try {
|
|
1634
|
+
return jsonResponse(await client.get("/analytics/pages", params));
|
|
1635
|
+
} catch (error) {
|
|
1636
|
+
handleApiError(error);
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
);
|
|
1640
|
+
server.tool(
|
|
1641
|
+
"get_analytics_sources",
|
|
1642
|
+
"Get traffic sources (referrers, search engines, social, direct)",
|
|
1643
|
+
{
|
|
1644
|
+
period: z.enum(["7d", "30d", "90d"]).optional(),
|
|
1645
|
+
startDate: z.string().optional(),
|
|
1646
|
+
endDate: z.string().optional()
|
|
1647
|
+
},
|
|
1648
|
+
async (params) => {
|
|
1649
|
+
try {
|
|
1650
|
+
return jsonResponse(await client.get("/analytics/referrers", params));
|
|
1651
|
+
} catch (error) {
|
|
1652
|
+
handleApiError(error);
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
);
|
|
1656
|
+
server.tool(
|
|
1657
|
+
"get_analytics_realtime",
|
|
1658
|
+
"Get real-time analytics snapshot (active visitors, current pages)",
|
|
1659
|
+
{},
|
|
1660
|
+
async () => {
|
|
1661
|
+
try {
|
|
1662
|
+
return jsonResponse(await client.get("/analytics/realtime"));
|
|
1663
|
+
} catch (error) {
|
|
1664
|
+
handleApiError(error);
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
);
|
|
1668
|
+
server.tool(
|
|
1669
|
+
"get_analytics_engagement",
|
|
1670
|
+
"Get engagement metrics (avg time on page, scroll depth, bounce rate)",
|
|
1671
|
+
{
|
|
1672
|
+
period: z.enum(["7d", "30d", "90d"]).optional(),
|
|
1673
|
+
startDate: z.string().optional(),
|
|
1674
|
+
endDate: z.string().optional()
|
|
1675
|
+
},
|
|
1676
|
+
async (params) => {
|
|
1677
|
+
try {
|
|
1678
|
+
return jsonResponse(await client.get("/analytics/engagement", params));
|
|
1679
|
+
} catch (error) {
|
|
1680
|
+
handleApiError(error);
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
);
|
|
1684
|
+
server.tool(
|
|
1685
|
+
"get_analytics_funnels",
|
|
1686
|
+
"Get conversion funnel data",
|
|
1687
|
+
{
|
|
1688
|
+
period: z.enum(["7d", "30d", "90d"]).optional(),
|
|
1689
|
+
funnelId: z.string().optional()
|
|
1690
|
+
},
|
|
1691
|
+
async (params) => {
|
|
1692
|
+
try {
|
|
1693
|
+
return jsonResponse(await client.get("/analytics/funnels", params));
|
|
1694
|
+
} catch (error) {
|
|
1695
|
+
handleApiError(error);
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
);
|
|
1699
|
+
server.tool(
|
|
1700
|
+
"get_analytics_settings",
|
|
1701
|
+
"Get analytics tracking configuration",
|
|
1702
|
+
{},
|
|
1703
|
+
async () => {
|
|
1704
|
+
try {
|
|
1705
|
+
return jsonResponse(await client.get("/analytics/settings"));
|
|
1706
|
+
} catch (error) {
|
|
1707
|
+
handleApiError(error);
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
);
|
|
1711
|
+
server.tool(
|
|
1712
|
+
"update_analytics_settings",
|
|
1713
|
+
"Update analytics tracking configuration",
|
|
1714
|
+
{
|
|
1715
|
+
trackingEnabled: z.boolean().optional(),
|
|
1716
|
+
anonymizeIp: z.boolean().optional(),
|
|
1717
|
+
sessionTimeout: z.number().optional(),
|
|
1718
|
+
botFiltering: z.boolean().optional(),
|
|
1719
|
+
excludedPaths: z.array(z.string()).optional()
|
|
1720
|
+
},
|
|
1721
|
+
async (params) => {
|
|
1722
|
+
try {
|
|
1723
|
+
return jsonResponse(await client.put("/analytics/settings", params));
|
|
1724
|
+
} catch (error) {
|
|
1725
|
+
handleApiError(error);
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
);
|
|
1729
|
+
server.tool(
|
|
1730
|
+
"export_analytics",
|
|
1731
|
+
"Export analytics data as CSV",
|
|
1732
|
+
{
|
|
1733
|
+
period: z.enum(["7d", "30d", "90d"]).optional(),
|
|
1734
|
+
startDate: z.string().optional(),
|
|
1735
|
+
endDate: z.string().optional(),
|
|
1736
|
+
type: z.enum(["overview", "pages", "sources"]).optional()
|
|
1737
|
+
},
|
|
1738
|
+
async (params) => {
|
|
1739
|
+
try {
|
|
1740
|
+
return jsonResponse(await client.get("/analytics/export", params));
|
|
1741
|
+
} catch (error) {
|
|
1742
|
+
handleApiError(error);
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
);
|
|
1746
|
+
}
|
|
1747
|
+
function registerSettingsTools(server, client) {
|
|
1748
|
+
server.tool(
|
|
1749
|
+
"get_site_info",
|
|
1750
|
+
"Get basic site information (name, domain, locales, timezone)",
|
|
1751
|
+
{},
|
|
1752
|
+
async () => {
|
|
1753
|
+
try {
|
|
1754
|
+
return jsonResponse(await client.get("/site/info"));
|
|
1755
|
+
} catch (error) {
|
|
1756
|
+
handleApiError(error);
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
);
|
|
1760
|
+
server.tool(
|
|
1761
|
+
"get_site_settings",
|
|
1762
|
+
"Get all site settings",
|
|
1763
|
+
{},
|
|
1764
|
+
async () => {
|
|
1765
|
+
try {
|
|
1766
|
+
return jsonResponse(await client.get("/site/settings"));
|
|
1767
|
+
} catch (error) {
|
|
1768
|
+
handleApiError(error);
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
);
|
|
1772
|
+
server.tool(
|
|
1773
|
+
"update_site_settings",
|
|
1774
|
+
"Update general site settings (name, domain, timezone, etc.)",
|
|
1775
|
+
{
|
|
1776
|
+
name: z.string().optional(),
|
|
1777
|
+
domain: z.string().optional(),
|
|
1778
|
+
description: z.string().optional(),
|
|
1779
|
+
timezone: z.string().optional(),
|
|
1780
|
+
dateFormat: z.string().optional()
|
|
1781
|
+
},
|
|
1782
|
+
async (params) => {
|
|
1783
|
+
try {
|
|
1784
|
+
return jsonResponse(await client.put("/site/settings", params));
|
|
1785
|
+
} catch (error) {
|
|
1786
|
+
handleApiError(error);
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
);
|
|
1790
|
+
server.tool(
|
|
1791
|
+
"get_language_settings",
|
|
1792
|
+
"Get multi-language configuration (enabled locales, default locale)",
|
|
1793
|
+
{},
|
|
1794
|
+
async () => {
|
|
1795
|
+
try {
|
|
1796
|
+
return jsonResponse(await client.get("/site/settings/language"));
|
|
1797
|
+
} catch (error) {
|
|
1798
|
+
handleApiError(error);
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
);
|
|
1802
|
+
server.tool(
|
|
1803
|
+
"update_language_settings",
|
|
1804
|
+
"Enable/disable multi-language, add/remove locales, change default locale",
|
|
1805
|
+
{
|
|
1806
|
+
defaultLocale: z.string().describe('Default locale code (e.g., "fr")'),
|
|
1807
|
+
enabledLocales: z.array(z.string()).describe("Array of enabled locale codes"),
|
|
1808
|
+
multiLanguageEnabled: z.boolean().describe("Whether multi-language is enabled")
|
|
1809
|
+
},
|
|
1810
|
+
async (params) => {
|
|
1811
|
+
try {
|
|
1812
|
+
return jsonResponse(await client.put("/site/settings/language", params));
|
|
1813
|
+
} catch (error) {
|
|
1814
|
+
handleApiError(error);
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
);
|
|
1818
|
+
server.tool(
|
|
1819
|
+
"get_spam_settings",
|
|
1820
|
+
"Get anti-spam protection settings (honeypot, reCAPTCHA)",
|
|
1821
|
+
{},
|
|
1822
|
+
async () => {
|
|
1823
|
+
try {
|
|
1824
|
+
return jsonResponse(await client.get("/spam-settings"));
|
|
1825
|
+
} catch (error) {
|
|
1826
|
+
handleApiError(error);
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
);
|
|
1830
|
+
server.tool(
|
|
1831
|
+
"update_spam_settings",
|
|
1832
|
+
"Update anti-spam configuration",
|
|
1833
|
+
{
|
|
1834
|
+
protectionMethod: z.string().optional(),
|
|
1835
|
+
honeypotEnabled: z.boolean().optional(),
|
|
1836
|
+
minSubmissionTimeMs: z.number().optional(),
|
|
1837
|
+
recaptchaEnabled: z.boolean().optional(),
|
|
1838
|
+
recaptchaSiteKey: z.string().optional()
|
|
1839
|
+
},
|
|
1840
|
+
async (params) => {
|
|
1841
|
+
try {
|
|
1842
|
+
return jsonResponse(await client.put("/spam-settings", params));
|
|
1843
|
+
} catch (error) {
|
|
1844
|
+
handleApiError(error);
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
);
|
|
1848
|
+
server.tool(
|
|
1849
|
+
"get_cookie_consent_settings",
|
|
1850
|
+
"Get cookie consent banner configuration",
|
|
1851
|
+
{},
|
|
1852
|
+
async () => {
|
|
1853
|
+
try {
|
|
1854
|
+
return jsonResponse(await client.get("/cookie-consent/settings"));
|
|
1855
|
+
} catch (error) {
|
|
1856
|
+
handleApiError(error);
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
);
|
|
1860
|
+
server.tool(
|
|
1861
|
+
"update_cookie_consent_settings",
|
|
1862
|
+
"Update cookie consent banner configuration",
|
|
1863
|
+
{
|
|
1864
|
+
enabled: z.boolean().optional(),
|
|
1865
|
+
position: z.string().optional(),
|
|
1866
|
+
theme: z.string().optional(),
|
|
1867
|
+
categories: z.any().optional()
|
|
1868
|
+
},
|
|
1869
|
+
async (params) => {
|
|
1870
|
+
try {
|
|
1871
|
+
return jsonResponse(await client.put("/cookie-consent/settings", params));
|
|
1872
|
+
} catch (error) {
|
|
1873
|
+
handleApiError(error);
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
);
|
|
1877
|
+
server.tool(
|
|
1878
|
+
"get_notification_settings",
|
|
1879
|
+
"Get email notification settings (form submissions, reviews, etc.)",
|
|
1880
|
+
{},
|
|
1881
|
+
async () => {
|
|
1882
|
+
try {
|
|
1883
|
+
return jsonResponse(await client.get("/settings/notifications"));
|
|
1884
|
+
} catch (error) {
|
|
1885
|
+
handleApiError(error);
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
);
|
|
1889
|
+
}
|
|
1890
|
+
function registerTranslationTools(server, client) {
|
|
1891
|
+
server.tool(
|
|
1892
|
+
"get_content_translations",
|
|
1893
|
+
"Get translation status for a content item across all enabled locales",
|
|
1894
|
+
{ id: z.number().describe("Content ID") },
|
|
1895
|
+
async ({ id }) => {
|
|
1896
|
+
try {
|
|
1897
|
+
return jsonResponse(await client.get(`/contents/${id}/translations`));
|
|
1898
|
+
} catch (error) {
|
|
1899
|
+
handleApiError(error);
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
);
|
|
1903
|
+
server.tool(
|
|
1904
|
+
"copy_content_to_locale",
|
|
1905
|
+
"Copy a content item to another language, creating a linked translation",
|
|
1906
|
+
{
|
|
1907
|
+
id: z.number().describe("Content ID"),
|
|
1908
|
+
targetLocale: z.string().describe('Target locale code (e.g., "en", "es")')
|
|
1909
|
+
},
|
|
1910
|
+
async ({ id, ...body }) => {
|
|
1911
|
+
try {
|
|
1912
|
+
return jsonResponse(await client.post(`/contents/${id}/copy-to-locale`, body));
|
|
1913
|
+
} catch (error) {
|
|
1914
|
+
handleApiError(error);
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
);
|
|
1918
|
+
server.tool(
|
|
1919
|
+
"get_site_block_translations",
|
|
1920
|
+
"Get translation status for a site block (page/global block)",
|
|
1921
|
+
{ slug: z.string().describe("Site block slug") },
|
|
1922
|
+
async ({ slug }) => {
|
|
1923
|
+
try {
|
|
1924
|
+
return jsonResponse(await client.get(`/site-blocks/${slug}/translations`));
|
|
1925
|
+
} catch (error) {
|
|
1926
|
+
handleApiError(error);
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
);
|
|
1930
|
+
server.tool(
|
|
1931
|
+
"copy_site_block_to_locale",
|
|
1932
|
+
"Copy a site block to another language",
|
|
1933
|
+
{
|
|
1934
|
+
slug: z.string().describe("Site block slug"),
|
|
1935
|
+
targetLocale: z.string().describe('Target locale code (e.g., "en", "es")')
|
|
1936
|
+
},
|
|
1937
|
+
async ({ slug, ...body }) => {
|
|
1938
|
+
try {
|
|
1939
|
+
return jsonResponse(await client.post(`/site-blocks/${slug}/copy-to-locale`, body));
|
|
1940
|
+
} catch (error) {
|
|
1941
|
+
handleApiError(error);
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
);
|
|
1945
|
+
server.tool(
|
|
1946
|
+
"get_available_locales",
|
|
1947
|
+
"Get all available locales with labels and flags (reference data)",
|
|
1948
|
+
{},
|
|
1949
|
+
async () => {
|
|
1950
|
+
try {
|
|
1951
|
+
return jsonResponse(await client.get("/site/settings/locales"));
|
|
1952
|
+
} catch (error) {
|
|
1953
|
+
handleApiError(error);
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
);
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
// src/tools/index.ts
|
|
1960
|
+
function registerAllTools(server, client, _config) {
|
|
1961
|
+
registerContentTools(server, client);
|
|
1962
|
+
registerSiteBlockTools(server, client);
|
|
1963
|
+
registerCategoryTools(server, client);
|
|
1964
|
+
registerTagTools(server, client);
|
|
1965
|
+
registerVariableTools(server, client);
|
|
1966
|
+
registerMediaTools(server, client);
|
|
1967
|
+
registerFormTools(server, client);
|
|
1968
|
+
registerReviewTools(server, client);
|
|
1969
|
+
registerSeoTools(server, client);
|
|
1970
|
+
registerSitemapTools(server, client);
|
|
1971
|
+
registerRedirectTools(server, client);
|
|
1972
|
+
registerAnalyticsTools(server, client);
|
|
1973
|
+
registerSettingsTools(server, client);
|
|
1974
|
+
registerTranslationTools(server, client);
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
// src/resources/site.ts
|
|
1978
|
+
function registerSiteResources(server, client) {
|
|
1979
|
+
server.resource(
|
|
1980
|
+
"site-info",
|
|
1981
|
+
"lynkow://site/info",
|
|
1982
|
+
{
|
|
1983
|
+
description: "Basic info about the connected Lynkow site (name, domain, locales)",
|
|
1984
|
+
mimeType: "application/json"
|
|
1985
|
+
},
|
|
1986
|
+
async (uri) => {
|
|
1987
|
+
const data = await client.get("/site/info");
|
|
1988
|
+
return {
|
|
1989
|
+
contents: [{
|
|
1990
|
+
uri: uri.href,
|
|
1991
|
+
mimeType: "application/json",
|
|
1992
|
+
text: JSON.stringify(data, null, 2)
|
|
1993
|
+
}]
|
|
1994
|
+
};
|
|
1995
|
+
}
|
|
1996
|
+
);
|
|
1997
|
+
server.resource(
|
|
1998
|
+
"language-settings",
|
|
1999
|
+
"lynkow://site/languages",
|
|
2000
|
+
{
|
|
2001
|
+
description: "Multi-language configuration: enabled locales, default locale",
|
|
2002
|
+
mimeType: "application/json"
|
|
2003
|
+
},
|
|
2004
|
+
async (uri) => {
|
|
2005
|
+
const data = await client.get("/site/settings/language");
|
|
2006
|
+
return {
|
|
2007
|
+
contents: [{
|
|
2008
|
+
uri: uri.href,
|
|
2009
|
+
mimeType: "application/json",
|
|
2010
|
+
text: JSON.stringify(data, null, 2)
|
|
2011
|
+
}]
|
|
2012
|
+
};
|
|
2013
|
+
}
|
|
2014
|
+
);
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
// src/resources/index.ts
|
|
2018
|
+
function registerAllResources(server, client) {
|
|
2019
|
+
registerSiteResources(server, client);
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
// src/index.ts
|
|
2023
|
+
async function startServer() {
|
|
2024
|
+
const config = loadConfig();
|
|
2025
|
+
const client = new LynkowApiClient(config);
|
|
2026
|
+
const server = new McpServer({
|
|
2027
|
+
name: "lynkow",
|
|
2028
|
+
version: "1.0.0"
|
|
2029
|
+
});
|
|
2030
|
+
registerAllTools(server, client);
|
|
2031
|
+
registerAllResources(server, client);
|
|
2032
|
+
const transport = new StdioServerTransport();
|
|
2033
|
+
await server.connect(transport);
|
|
2034
|
+
if (config.debug) {
|
|
2035
|
+
console.error("[lynkow-mcp] Server started successfully");
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
export { LynkowApiClient, startServer };
|
|
2040
|
+
//# sourceMappingURL=index.js.map
|
|
2041
|
+
//# sourceMappingURL=index.js.map
|