@centrali-io/centrali-mcp 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/README.md +94 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +62 -0
- package/dist/resources/structures.d.ts +3 -0
- package/dist/resources/structures.js +69 -0
- package/dist/tools/compute.d.ts +3 -0
- package/dist/tools/compute.js +110 -0
- package/dist/tools/records.d.ts +3 -0
- package/dist/tools/records.js +180 -0
- package/dist/tools/search.d.ts +3 -0
- package/dist/tools/search.js +51 -0
- package/dist/tools/smart-queries.d.ts +3 -0
- package/dist/tools/smart-queries.js +74 -0
- package/dist/tools/structures.d.ts +3 -0
- package/dist/tools/structures.js +64 -0
- package/package.json +33 -0
- package/src/index.ts +58 -0
- package/src/resources/structures.ts +71 -0
- package/src/tools/compute.ts +120 -0
- package/src/tools/records.ts +209 -0
- package/src/tools/search.ts +50 -0
- package/src/tools/smart-queries.ts +82 -0
- package/src/tools/structures.ts +64 -0
- package/tsconfig.json +14 -0
package/README.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# @centrali-io/centrali-mcp
|
|
2
|
+
|
|
3
|
+
MCP (Model Context Protocol) server for the Centrali platform. Lets AI assistants (Claude, Cursor, etc.) interact with Centrali workspaces — query data, manage records, search, trigger compute functions, and more.
|
|
4
|
+
|
|
5
|
+
Built on `@centrali-io/centrali-sdk` v3.1.0. Authenticates via service account client credentials.
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
Add to your MCP client configuration (e.g., Claude Desktop, Cursor):
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"mcpServers": {
|
|
14
|
+
"centrali": {
|
|
15
|
+
"command": "npx",
|
|
16
|
+
"args": ["@centrali-io/centrali-mcp"],
|
|
17
|
+
"env": {
|
|
18
|
+
"CENTRALI_URL": "https://dev.centrali.io",
|
|
19
|
+
"CENTRALI_CLIENT_ID": "<service-account-client-id>",
|
|
20
|
+
"CENTRALI_CLIENT_SECRET": "<service-account-secret>",
|
|
21
|
+
"CENTRALI_WORKSPACE": "my-workspace"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Environment Variables
|
|
29
|
+
|
|
30
|
+
| Variable | Required | Description |
|
|
31
|
+
|----------|----------|-------------|
|
|
32
|
+
| `CENTRALI_URL` | Yes | Centrali instance URL (e.g., `https://dev.centrali.io`) |
|
|
33
|
+
| `CENTRALI_CLIENT_ID` | Yes | Service account client ID |
|
|
34
|
+
| `CENTRALI_CLIENT_SECRET` | Yes | Service account client secret |
|
|
35
|
+
| `CENTRALI_WORKSPACE` | Yes | Workspace slug to operate in |
|
|
36
|
+
|
|
37
|
+
## Available Tools
|
|
38
|
+
|
|
39
|
+
### Structures
|
|
40
|
+
| Tool | Description |
|
|
41
|
+
|------|-------------|
|
|
42
|
+
| `list_structures` | List all data structures (schemas) in the workspace |
|
|
43
|
+
| `get_structure` | Get full schema for a structure by record slug |
|
|
44
|
+
|
|
45
|
+
### Records
|
|
46
|
+
| Tool | Description |
|
|
47
|
+
|------|-------------|
|
|
48
|
+
| `query_records` | Query records with filters, sorting, pagination |
|
|
49
|
+
| `get_record` | Get a single record by ID |
|
|
50
|
+
| `create_record` | Create a new record |
|
|
51
|
+
| `update_record` | Update an existing record |
|
|
52
|
+
| `delete_record` | Soft or hard delete a record |
|
|
53
|
+
|
|
54
|
+
### Search
|
|
55
|
+
| Tool | Description |
|
|
56
|
+
|------|-------------|
|
|
57
|
+
| `search_records` | Full-text search across workspace records |
|
|
58
|
+
|
|
59
|
+
### Compute
|
|
60
|
+
| Tool | Description |
|
|
61
|
+
|------|-------------|
|
|
62
|
+
| `list_functions` | List compute functions |
|
|
63
|
+
| `list_triggers` | List function triggers (on-demand, event-driven, scheduled, webhook) |
|
|
64
|
+
| `invoke_trigger` | Invoke an on-demand trigger with optional payload |
|
|
65
|
+
|
|
66
|
+
### Smart Queries
|
|
67
|
+
| Tool | Description |
|
|
68
|
+
|------|-------------|
|
|
69
|
+
| `list_smart_queries` | List smart queries, optionally filtered by structure |
|
|
70
|
+
| `execute_smart_query` | Execute a smart query with optional variables |
|
|
71
|
+
|
|
72
|
+
## Resources
|
|
73
|
+
|
|
74
|
+
| URI | Description |
|
|
75
|
+
|-----|-------------|
|
|
76
|
+
| `centrali://structures` | List of all structures with name, slug, description |
|
|
77
|
+
| `centrali://structures/{slug}` | Full schema for a specific structure |
|
|
78
|
+
|
|
79
|
+
## Development
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Install dependencies
|
|
83
|
+
npm install
|
|
84
|
+
|
|
85
|
+
# Build
|
|
86
|
+
npm run build
|
|
87
|
+
|
|
88
|
+
# Test with MCP inspector
|
|
89
|
+
CENTRALI_URL=https://dev.centrali.io \
|
|
90
|
+
CENTRALI_CLIENT_ID=... \
|
|
91
|
+
CENTRALI_CLIENT_SECRET=... \
|
|
92
|
+
CENTRALI_WORKSPACE=my-workspace \
|
|
93
|
+
npx @modelcontextprotocol/inspector node dist/index.js
|
|
94
|
+
```
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
4
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
5
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
6
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
7
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
8
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
9
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
14
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
15
|
+
const centrali_sdk_1 = require("@centrali-io/centrali-sdk");
|
|
16
|
+
const structures_js_1 = require("./tools/structures.js");
|
|
17
|
+
const records_js_1 = require("./tools/records.js");
|
|
18
|
+
const search_js_1 = require("./tools/search.js");
|
|
19
|
+
const compute_js_1 = require("./tools/compute.js");
|
|
20
|
+
const smart_queries_js_1 = require("./tools/smart-queries.js");
|
|
21
|
+
const structures_js_2 = require("./resources/structures.js");
|
|
22
|
+
function getRequiredEnv(name) {
|
|
23
|
+
const value = process.env[name];
|
|
24
|
+
if (!value) {
|
|
25
|
+
console.error(`Missing required environment variable: ${name}`);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
function main() {
|
|
31
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
32
|
+
const baseUrl = getRequiredEnv("CENTRALI_URL");
|
|
33
|
+
const clientId = getRequiredEnv("CENTRALI_CLIENT_ID");
|
|
34
|
+
const clientSecret = getRequiredEnv("CENTRALI_CLIENT_SECRET");
|
|
35
|
+
const workspaceId = getRequiredEnv("CENTRALI_WORKSPACE");
|
|
36
|
+
const sdk = new centrali_sdk_1.CentraliSDK({
|
|
37
|
+
baseUrl,
|
|
38
|
+
workspaceId,
|
|
39
|
+
clientId,
|
|
40
|
+
clientSecret,
|
|
41
|
+
});
|
|
42
|
+
const server = new mcp_js_1.McpServer({
|
|
43
|
+
name: "centrali",
|
|
44
|
+
version: "1.0.0",
|
|
45
|
+
});
|
|
46
|
+
// Register all tools
|
|
47
|
+
(0, structures_js_1.registerStructureTools)(server, sdk);
|
|
48
|
+
(0, records_js_1.registerRecordTools)(server, sdk);
|
|
49
|
+
(0, search_js_1.registerSearchTools)(server, sdk);
|
|
50
|
+
(0, compute_js_1.registerComputeTools)(server, sdk);
|
|
51
|
+
(0, smart_queries_js_1.registerSmartQueryTools)(server, sdk);
|
|
52
|
+
// Register resources
|
|
53
|
+
(0, structures_js_2.registerStructureResources)(server, sdk);
|
|
54
|
+
// Start stdio transport
|
|
55
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
56
|
+
yield server.connect(transport);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
main().catch((error) => {
|
|
60
|
+
console.error("Fatal error:", error);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.registerStructureResources = registerStructureResources;
|
|
13
|
+
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
14
|
+
function registerStructureResources(server, sdk) {
|
|
15
|
+
// Static resource: list all structures (gives LLM workspace context)
|
|
16
|
+
server.resource("structures-list", "centrali://structures", {
|
|
17
|
+
description: "List of all data structures in the workspace with name, slug, and description",
|
|
18
|
+
mimeType: "application/json",
|
|
19
|
+
}, (uri) => __awaiter(this, void 0, void 0, function* () {
|
|
20
|
+
const result = yield sdk.structures.list({ limit: 100 });
|
|
21
|
+
const summary = (result.data || []).map((s) => {
|
|
22
|
+
var _a;
|
|
23
|
+
return ({
|
|
24
|
+
name: s.name,
|
|
25
|
+
recordSlug: s.recordSlug,
|
|
26
|
+
description: s.description || "",
|
|
27
|
+
propertyCount: ((_a = s.properties) === null || _a === void 0 ? void 0 : _a.length) || 0,
|
|
28
|
+
status: s.status,
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
contents: [
|
|
33
|
+
{
|
|
34
|
+
uri: uri.href,
|
|
35
|
+
mimeType: "application/json",
|
|
36
|
+
text: JSON.stringify(summary, null, 2),
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
}));
|
|
41
|
+
// Resource template: full schema for a specific structure
|
|
42
|
+
server.resource("structure-schema", new mcp_js_1.ResourceTemplate("centrali://structures/{slug}", {
|
|
43
|
+
list: () => __awaiter(this, void 0, void 0, function* () {
|
|
44
|
+
const result = yield sdk.structures.list({ limit: 100 });
|
|
45
|
+
return {
|
|
46
|
+
resources: (result.data || []).map((s) => ({
|
|
47
|
+
uri: `centrali://structures/${s.recordSlug}`,
|
|
48
|
+
name: s.name,
|
|
49
|
+
description: s.description || `Schema for ${s.name}`,
|
|
50
|
+
mimeType: "application/json",
|
|
51
|
+
})),
|
|
52
|
+
};
|
|
53
|
+
}),
|
|
54
|
+
}), {
|
|
55
|
+
description: "Full schema definition for a specific structure",
|
|
56
|
+
mimeType: "application/json",
|
|
57
|
+
}, (uri_1, _a) => __awaiter(this, [uri_1, _a], void 0, function* (uri, { slug }) {
|
|
58
|
+
const result = yield sdk.structures.getBySlug(slug);
|
|
59
|
+
return {
|
|
60
|
+
contents: [
|
|
61
|
+
{
|
|
62
|
+
uri: uri.href,
|
|
63
|
+
mimeType: "application/json",
|
|
64
|
+
text: JSON.stringify(result.data, null, 2),
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.registerComputeTools = registerComputeTools;
|
|
13
|
+
const zod_1 = require("zod");
|
|
14
|
+
function registerComputeTools(server, sdk) {
|
|
15
|
+
server.tool("list_functions", "List all compute functions in the workspace. Compute functions are JavaScript code blocks that run server-side.", {
|
|
16
|
+
page: zod_1.z.number().optional().describe("Page number"),
|
|
17
|
+
limit: zod_1.z.number().optional().describe("Results per page"),
|
|
18
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ page, limit }) {
|
|
19
|
+
try {
|
|
20
|
+
const options = {};
|
|
21
|
+
if (page !== undefined)
|
|
22
|
+
options.page = page;
|
|
23
|
+
if (limit !== undefined)
|
|
24
|
+
options.limit = limit;
|
|
25
|
+
const result = yield sdk.functions.list(Object.keys(options).length > 0 ? options : undefined);
|
|
26
|
+
return {
|
|
27
|
+
content: [
|
|
28
|
+
{ type: "text", text: JSON.stringify(result.data, null, 2) },
|
|
29
|
+
],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
return {
|
|
34
|
+
content: [
|
|
35
|
+
{
|
|
36
|
+
type: "text",
|
|
37
|
+
text: `Error listing functions: ${error.message}`,
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
isError: true,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}));
|
|
44
|
+
server.tool("list_triggers", "List function triggers in the workspace. Triggers define how and when compute functions are executed (on-demand, event-driven, scheduled, webhook).", {
|
|
45
|
+
executionType: zod_1.z
|
|
46
|
+
.enum(["on-demand", "event-driven", "scheduled", "webhook"])
|
|
47
|
+
.optional()
|
|
48
|
+
.describe("Filter by trigger execution type"),
|
|
49
|
+
page: zod_1.z.number().optional().describe("Page number"),
|
|
50
|
+
limit: zod_1.z.number().optional().describe("Results per page"),
|
|
51
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ executionType, page, limit }) {
|
|
52
|
+
try {
|
|
53
|
+
const options = {};
|
|
54
|
+
if (executionType)
|
|
55
|
+
options.executionType = executionType;
|
|
56
|
+
if (page !== undefined)
|
|
57
|
+
options.page = page;
|
|
58
|
+
if (limit !== undefined)
|
|
59
|
+
options.limit = limit;
|
|
60
|
+
const result = yield sdk.triggers.listAll(Object.keys(options).length > 0 ? options : undefined);
|
|
61
|
+
return {
|
|
62
|
+
content: [
|
|
63
|
+
{ type: "text", text: JSON.stringify(result.data, null, 2) },
|
|
64
|
+
],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
return {
|
|
69
|
+
content: [
|
|
70
|
+
{
|
|
71
|
+
type: "text",
|
|
72
|
+
text: `Error listing triggers: ${error.message}`,
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
isError: true,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}));
|
|
79
|
+
server.tool("invoke_trigger", "Invoke an on-demand function trigger by ID. Optionally pass a custom payload. Returns the queued job ID.", {
|
|
80
|
+
triggerId: zod_1.z.string().describe("The trigger ID (UUID) to invoke"),
|
|
81
|
+
payload: zod_1.z
|
|
82
|
+
.record(zod_1.z.string(), zod_1.z.any())
|
|
83
|
+
.optional()
|
|
84
|
+
.describe("Custom payload to pass to the function execution"),
|
|
85
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ triggerId, payload }) {
|
|
86
|
+
try {
|
|
87
|
+
const options = payload ? { payload } : undefined;
|
|
88
|
+
const result = yield sdk.triggers.invoke(triggerId, options);
|
|
89
|
+
return {
|
|
90
|
+
content: [
|
|
91
|
+
{
|
|
92
|
+
type: "text",
|
|
93
|
+
text: JSON.stringify({ jobId: result.data, message: "Trigger invoked successfully" }, null, 2),
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
return {
|
|
100
|
+
content: [
|
|
101
|
+
{
|
|
102
|
+
type: "text",
|
|
103
|
+
text: `Error invoking trigger '${triggerId}': ${error.message}`,
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
isError: true,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.registerRecordTools = registerRecordTools;
|
|
13
|
+
const zod_1 = require("zod");
|
|
14
|
+
function registerRecordTools(server, sdk) {
|
|
15
|
+
server.tool("query_records", "Query records from a structure with optional filters, sorting, and pagination. Filters use 'data.' prefix for custom fields and bracket notation for operators (e.g., 'data.status': 'active', 'data.price[lte]': 100).", {
|
|
16
|
+
recordSlug: zod_1.z
|
|
17
|
+
.string()
|
|
18
|
+
.describe("The structure's record slug (e.g., 'orders')"),
|
|
19
|
+
filters: zod_1.z
|
|
20
|
+
.record(zod_1.z.string(), zod_1.z.any())
|
|
21
|
+
.optional()
|
|
22
|
+
.describe("Filter object with keys like 'data.fieldName' or 'data.fieldName[operator]'. Operators: eq, ne, gt, gte, lt, lte, in, nin, contains, startswith, endswith"),
|
|
23
|
+
sort: zod_1.z
|
|
24
|
+
.string()
|
|
25
|
+
.optional()
|
|
26
|
+
.describe("Sort field with optional '-' prefix for descending (e.g., '-createdAt')"),
|
|
27
|
+
page: zod_1.z.number().optional().describe("Page number (1-indexed, default: 1)"),
|
|
28
|
+
pageSize: zod_1.z
|
|
29
|
+
.number()
|
|
30
|
+
.optional()
|
|
31
|
+
.describe("Records per page (default: 50, max: 500)"),
|
|
32
|
+
expand: zod_1.z
|
|
33
|
+
.string()
|
|
34
|
+
.optional()
|
|
35
|
+
.describe("Comma-separated reference fields to expand (e.g., 'customer,product')"),
|
|
36
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ recordSlug, filters, sort, page, pageSize, expand }) {
|
|
37
|
+
try {
|
|
38
|
+
const params = Object.assign(Object.assign({}, filters), { sort,
|
|
39
|
+
page,
|
|
40
|
+
pageSize,
|
|
41
|
+
expand });
|
|
42
|
+
// Remove undefined values
|
|
43
|
+
Object.keys(params).forEach((key) => params[key] === undefined && delete params[key]);
|
|
44
|
+
const result = yield sdk.queryRecords(recordSlug, params);
|
|
45
|
+
return {
|
|
46
|
+
content: [
|
|
47
|
+
{ type: "text", text: JSON.stringify(result, null, 2) },
|
|
48
|
+
],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
return {
|
|
53
|
+
content: [
|
|
54
|
+
{
|
|
55
|
+
type: "text",
|
|
56
|
+
text: `Error querying records from '${recordSlug}': ${error.message}`,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
isError: true,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}));
|
|
63
|
+
server.tool("get_record", "Get a single record by its ID from a structure. Optionally expand reference fields.", {
|
|
64
|
+
recordSlug: zod_1.z.string().describe("The structure's record slug"),
|
|
65
|
+
id: zod_1.z.string().describe("The record ID (UUID)"),
|
|
66
|
+
expand: zod_1.z
|
|
67
|
+
.string()
|
|
68
|
+
.optional()
|
|
69
|
+
.describe("Comma-separated reference fields to expand"),
|
|
70
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ recordSlug, id, expand }) {
|
|
71
|
+
try {
|
|
72
|
+
const options = expand ? { expand } : undefined;
|
|
73
|
+
const result = yield sdk.getRecord(recordSlug, id, options);
|
|
74
|
+
return {
|
|
75
|
+
content: [
|
|
76
|
+
{ type: "text", text: JSON.stringify(result.data, null, 2) },
|
|
77
|
+
],
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
return {
|
|
82
|
+
content: [
|
|
83
|
+
{
|
|
84
|
+
type: "text",
|
|
85
|
+
text: `Error getting record '${id}' from '${recordSlug}': ${error.message}`,
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
isError: true,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}));
|
|
92
|
+
server.tool("create_record", "Create a new record in a structure. Pass the record data as a JSON object with field names matching the structure's properties.", {
|
|
93
|
+
recordSlug: zod_1.z
|
|
94
|
+
.string()
|
|
95
|
+
.describe("The structure's record slug (e.g., 'orders')"),
|
|
96
|
+
data: zod_1.z
|
|
97
|
+
.record(zod_1.z.string(), zod_1.z.any())
|
|
98
|
+
.describe("The record data object (field names as keys)"),
|
|
99
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ recordSlug, data }) {
|
|
100
|
+
try {
|
|
101
|
+
const result = yield sdk.createRecord(recordSlug, data);
|
|
102
|
+
return {
|
|
103
|
+
content: [
|
|
104
|
+
{ type: "text", text: JSON.stringify(result.data, null, 2) },
|
|
105
|
+
],
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
return {
|
|
110
|
+
content: [
|
|
111
|
+
{
|
|
112
|
+
type: "text",
|
|
113
|
+
text: `Error creating record in '${recordSlug}': ${error.message}`,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
isError: true,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}));
|
|
120
|
+
server.tool("update_record", "Update an existing record by ID. Only include the fields you want to change.", {
|
|
121
|
+
recordSlug: zod_1.z.string().describe("The structure's record slug"),
|
|
122
|
+
id: zod_1.z.string().describe("The record ID (UUID) to update"),
|
|
123
|
+
data: zod_1.z
|
|
124
|
+
.record(zod_1.z.string(), zod_1.z.any())
|
|
125
|
+
.describe("Object with fields to update (partial update)"),
|
|
126
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ recordSlug, id, data }) {
|
|
127
|
+
try {
|
|
128
|
+
const result = yield sdk.updateRecord(recordSlug, id, data);
|
|
129
|
+
return {
|
|
130
|
+
content: [
|
|
131
|
+
{ type: "text", text: JSON.stringify(result.data, null, 2) },
|
|
132
|
+
],
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
return {
|
|
137
|
+
content: [
|
|
138
|
+
{
|
|
139
|
+
type: "text",
|
|
140
|
+
text: `Error updating record '${id}' in '${recordSlug}': ${error.message}`,
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
isError: true,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}));
|
|
147
|
+
server.tool("delete_record", "Delete a record by ID. Performs a soft delete by default (can be restored). Set hard=true for permanent deletion.", {
|
|
148
|
+
recordSlug: zod_1.z.string().describe("The structure's record slug"),
|
|
149
|
+
id: zod_1.z.string().describe("The record ID (UUID) to delete"),
|
|
150
|
+
hard: zod_1.z
|
|
151
|
+
.boolean()
|
|
152
|
+
.optional()
|
|
153
|
+
.describe("If true, permanently delete. Default: false (soft delete, can be restored)"),
|
|
154
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ recordSlug, id, hard }) {
|
|
155
|
+
try {
|
|
156
|
+
const options = hard !== undefined ? { hard } : undefined;
|
|
157
|
+
yield sdk.deleteRecord(recordSlug, id, options);
|
|
158
|
+
const deleteType = hard ? "permanently deleted" : "soft-deleted";
|
|
159
|
+
return {
|
|
160
|
+
content: [
|
|
161
|
+
{
|
|
162
|
+
type: "text",
|
|
163
|
+
text: `Record '${id}' ${deleteType} from '${recordSlug}'.`,
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
return {
|
|
170
|
+
content: [
|
|
171
|
+
{
|
|
172
|
+
type: "text",
|
|
173
|
+
text: `Error deleting record '${id}' from '${recordSlug}': ${error.message}`,
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
isError: true,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.registerSearchTools = registerSearchTools;
|
|
13
|
+
const zod_1 = require("zod");
|
|
14
|
+
function registerSearchTools(server, sdk) {
|
|
15
|
+
server.tool("search_records", "Full-text search across records in the workspace. Powered by Meilisearch. Optionally filter by structure(s) and limit results.", {
|
|
16
|
+
query: zod_1.z.string().describe("The search query string"),
|
|
17
|
+
structures: zod_1.z
|
|
18
|
+
.union([zod_1.z.string(), zod_1.z.array(zod_1.z.string())])
|
|
19
|
+
.optional()
|
|
20
|
+
.describe("Filter by structure slug(s). Single slug string or array of slugs"),
|
|
21
|
+
limit: zod_1.z
|
|
22
|
+
.number()
|
|
23
|
+
.optional()
|
|
24
|
+
.describe("Maximum results to return (default: 20, max: 100)"),
|
|
25
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ query, structures, limit }) {
|
|
26
|
+
try {
|
|
27
|
+
const options = {};
|
|
28
|
+
if (structures)
|
|
29
|
+
options.structures = structures;
|
|
30
|
+
if (limit)
|
|
31
|
+
options.limit = limit;
|
|
32
|
+
const result = yield sdk.search(query, Object.keys(options).length > 0 ? options : undefined);
|
|
33
|
+
return {
|
|
34
|
+
content: [
|
|
35
|
+
{ type: "text", text: JSON.stringify(result.data, null, 2) },
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
return {
|
|
41
|
+
content: [
|
|
42
|
+
{
|
|
43
|
+
type: "text",
|
|
44
|
+
text: `Error searching records: ${error.message}`,
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
isError: true,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.registerSmartQueryTools = registerSmartQueryTools;
|
|
13
|
+
const zod_1 = require("zod");
|
|
14
|
+
function registerSmartQueryTools(server, sdk) {
|
|
15
|
+
server.tool("list_smart_queries", "List smart queries. Smart queries are reusable, parameterized queries defined in the Centrali console. Optionally filter by structure slug.", {
|
|
16
|
+
recordSlug: zod_1.z
|
|
17
|
+
.string()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("Filter by structure slug. If omitted, lists all smart queries in the workspace"),
|
|
20
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ recordSlug }) {
|
|
21
|
+
try {
|
|
22
|
+
const result = recordSlug
|
|
23
|
+
? yield sdk.smartQueries.list(recordSlug)
|
|
24
|
+
: yield sdk.smartQueries.listAll();
|
|
25
|
+
return {
|
|
26
|
+
content: [
|
|
27
|
+
{ type: "text", text: JSON.stringify(result.data, null, 2) },
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
return {
|
|
33
|
+
content: [
|
|
34
|
+
{
|
|
35
|
+
type: "text",
|
|
36
|
+
text: `Error listing smart queries: ${error.message}`,
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
isError: true,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}));
|
|
43
|
+
server.tool("execute_smart_query", "Execute a smart query by ID and return the results. Smart queries can have parameterized variables using {{variableName}} syntax.", {
|
|
44
|
+
recordSlug: zod_1.z
|
|
45
|
+
.string()
|
|
46
|
+
.describe("The structure's record slug the query belongs to"),
|
|
47
|
+
queryId: zod_1.z.string().describe("The smart query ID (UUID) to execute"),
|
|
48
|
+
variables: zod_1.z
|
|
49
|
+
.record(zod_1.z.string(), zod_1.z.string())
|
|
50
|
+
.optional()
|
|
51
|
+
.describe("Variables to substitute in the query (key-value pairs matching {{variableName}} placeholders)"),
|
|
52
|
+
}, (_a) => __awaiter(this, [_a], void 0, function* ({ recordSlug, queryId, variables }) {
|
|
53
|
+
try {
|
|
54
|
+
const options = variables ? { variables } : undefined;
|
|
55
|
+
const result = yield sdk.smartQueries.execute(recordSlug, queryId, options);
|
|
56
|
+
return {
|
|
57
|
+
content: [
|
|
58
|
+
{ type: "text", text: JSON.stringify(result.data, null, 2) },
|
|
59
|
+
],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
return {
|
|
64
|
+
content: [
|
|
65
|
+
{
|
|
66
|
+
type: "text",
|
|
67
|
+
text: `Error executing smart query '${queryId}': ${error.message}`,
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
isError: true,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}));
|
|
74
|
+
}
|