@appaflytech/wappa-mcp 0.0.10 → 0.0.12
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 +40 -0
- package/dist/auth.d.ts +25 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +35 -0
- package/dist/auth.js.map +1 -1
- package/dist/client.d.ts +39 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +159 -6
- package/dist/client.js.map +1 -1
- package/dist/factory.d.ts +32 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/factory.js +286 -0
- package/dist/factory.js.map +1 -0
- package/dist/http/auth.d.ts +33 -0
- package/dist/http/auth.d.ts.map +1 -0
- package/dist/http/auth.js +55 -0
- package/dist/http/auth.js.map +1 -0
- package/dist/http/session.d.ts +30 -0
- package/dist/http/session.d.ts.map +1 -0
- package/dist/http/session.js +56 -0
- package/dist/http/session.js.map +1 -0
- package/dist/http/transport.d.ts +21 -0
- package/dist/http/transport.d.ts.map +1 -0
- package/dist/http/transport.js +101 -0
- package/dist/http/transport.js.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -262
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +25 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +94 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/ai-chat-sessions.d.ts +143 -0
- package/dist/tools/ai-chat-sessions.d.ts.map +1 -0
- package/dist/tools/ai-chat-sessions.js +144 -0
- package/dist/tools/ai-chat-sessions.js.map +1 -0
- package/dist/tools/app-users.d.ts +440 -0
- package/dist/tools/app-users.d.ts.map +1 -0
- package/dist/tools/app-users.js +347 -0
- package/dist/tools/app-users.js.map +1 -0
- package/dist/tools/component-categories.d.ts +157 -0
- package/dist/tools/component-categories.d.ts.map +1 -0
- package/dist/tools/component-categories.js +162 -0
- package/dist/tools/component-categories.js.map +1 -0
- package/dist/tools/countries.d.ts +148 -0
- package/dist/tools/countries.d.ts.map +1 -0
- package/dist/tools/countries.js +142 -0
- package/dist/tools/countries.js.map +1 -0
- package/dist/tools/db-routines.d.ts +229 -0
- package/dist/tools/db-routines.d.ts.map +1 -0
- package/dist/tools/db-routines.js +233 -0
- package/dist/tools/db-routines.js.map +1 -0
- package/dist/tools/dynamic-entities.d.ts +166 -0
- package/dist/tools/dynamic-entities.d.ts.map +1 -1
- package/dist/tools/dynamic-entities.js +156 -0
- package/dist/tools/dynamic-entities.js.map +1 -1
- package/dist/tools/entities.d.ts +119 -0
- package/dist/tools/entities.d.ts.map +1 -1
- package/dist/tools/entities.js +413 -4
- package/dist/tools/entities.js.map +1 -1
- package/dist/tools/error-logs.d.ts +67 -0
- package/dist/tools/error-logs.d.ts.map +1 -0
- package/dist/tools/error-logs.js +74 -0
- package/dist/tools/error-logs.js.map +1 -0
- package/dist/tools/general.d.ts.map +1 -1
- package/dist/tools/general.js +218 -0
- package/dist/tools/general.js.map +1 -1
- package/dist/tools/layouts.d.ts +21 -0
- package/dist/tools/layouts.d.ts.map +1 -1
- package/dist/tools/layouts.js +20 -2
- package/dist/tools/layouts.js.map +1 -1
- package/dist/tools/operations.d.ts +184 -0
- package/dist/tools/operations.d.ts.map +1 -0
- package/dist/tools/operations.js +217 -0
- package/dist/tools/operations.js.map +1 -0
- package/dist/tools/organizations.d.ts +178 -0
- package/dist/tools/organizations.d.ts.map +1 -0
- package/dist/tools/organizations.js +158 -0
- package/dist/tools/organizations.js.map +1 -0
- package/dist/tools/page-entities.d.ts +522 -0
- package/dist/tools/page-entities.d.ts.map +1 -0
- package/dist/tools/page-entities.js +535 -0
- package/dist/tools/page-entities.js.map +1 -0
- package/dist/tools/pages.d.ts +226 -0
- package/dist/tools/pages.d.ts.map +1 -1
- package/dist/tools/pages.js +473 -17
- package/dist/tools/pages.js.map +1 -1
- package/dist/tools/plans.d.ts +293 -0
- package/dist/tools/plans.d.ts.map +1 -0
- package/dist/tools/plans.js +213 -0
- package/dist/tools/plans.js.map +1 -0
- package/dist/tools/plugins.d.ts +230 -0
- package/dist/tools/plugins.d.ts.map +1 -0
- package/dist/tools/plugins.js +218 -0
- package/dist/tools/plugins.js.map +1 -0
- package/dist/tools/push-notifications.d.ts +261 -0
- package/dist/tools/push-notifications.d.ts.map +1 -0
- package/dist/tools/push-notifications.js +246 -0
- package/dist/tools/push-notifications.js.map +1 -0
- package/dist/tools/queries.d.ts +274 -0
- package/dist/tools/queries.d.ts.map +1 -1
- package/dist/tools/queries.js +319 -17
- package/dist/tools/queries.js.map +1 -1
- package/dist/tools/query-categories.d.ts +192 -0
- package/dist/tools/query-categories.d.ts.map +1 -0
- package/dist/tools/query-categories.js +204 -0
- package/dist/tools/query-categories.js.map +1 -0
- package/dist/tools/regions.d.ts +148 -0
- package/dist/tools/regions.d.ts.map +1 -0
- package/dist/tools/regions.js +148 -0
- package/dist/tools/regions.js.map +1 -0
- package/dist/tools/roles.d.ts +284 -0
- package/dist/tools/roles.d.ts.map +1 -0
- package/dist/tools/roles.js +291 -0
- package/dist/tools/roles.js.map +1 -0
- package/dist/tools/settings.d.ts +160 -0
- package/dist/tools/settings.d.ts.map +1 -0
- package/dist/tools/settings.js +187 -0
- package/dist/tools/settings.js.map +1 -0
- package/dist/tools/showcases.d.ts +184 -0
- package/dist/tools/showcases.d.ts.map +1 -0
- package/dist/tools/showcases.js +179 -0
- package/dist/tools/showcases.js.map +1 -0
- package/dist/tools/storage.d.ts +297 -0
- package/dist/tools/storage.d.ts.map +1 -0
- package/dist/tools/storage.js +302 -0
- package/dist/tools/storage.js.map +1 -0
- package/dist/tools/subscriptions.d.ts +166 -0
- package/dist/tools/subscriptions.d.ts.map +1 -0
- package/dist/tools/subscriptions.js +144 -0
- package/dist/tools/subscriptions.js.map +1 -0
- package/dist/tools/system-tools.d.ts +124 -0
- package/dist/tools/system-tools.d.ts.map +1 -0
- package/dist/tools/system-tools.js +147 -0
- package/dist/tools/system-tools.js.map +1 -0
- package/dist/tools/system-variables.d.ts +167 -0
- package/dist/tools/system-variables.d.ts.map +1 -0
- package/dist/tools/system-variables.js +176 -0
- package/dist/tools/system-variables.js.map +1 -0
- package/dist/tools/users.d.ts +266 -0
- package/dist/tools/users.d.ts.map +1 -0
- package/dist/tools/users.js +235 -0
- package/dist/tools/users.js.map +1 -0
- package/dist/tools/workflows.d.ts +278 -0
- package/dist/tools/workflows.d.ts.map +1 -0
- package/dist/tools/workflows.js +267 -0
- package/dist/tools/workflows.js.map +1 -0
- package/package.json +12 -3
package/dist/index.js
CHANGED
|
@@ -1,283 +1,47 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* WAPPA Admin API MCP Server
|
|
3
|
+
* WAPPA Admin API MCP Server — stdio entry point
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Supports two auth modes controlled by environment variables:
|
|
6
|
+
* 1. Email/password — WAP_EMAIL + WAP_PASSWORD (legacy, default)
|
|
7
|
+
* 2. API Key — WAP_API_KEY (Organisation API Key, no sign-in needed)
|
|
7
8
|
*
|
|
9
|
+
* Provides Claude Code / Cursor with direct access to WAPPA Admin API tools.
|
|
8
10
|
*/
|
|
9
|
-
import { McpServer, ResourceTemplate, } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10
11
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
11
|
-
import { z } from "zod";
|
|
12
12
|
import { config as loadDotenv } from "dotenv";
|
|
13
|
-
import {
|
|
14
|
-
import { WapClient } from "./client.js";
|
|
15
|
-
import { getComponentTools } from "./tools/components.js";
|
|
16
|
-
import { getPageTools } from "./tools/pages.js";
|
|
17
|
-
import { getWidgetTools } from "./tools/widgets.js";
|
|
18
|
-
import { getGeneralTools } from "./tools/general.js";
|
|
19
|
-
import { getSiteTools } from "./tools/sites.js";
|
|
20
|
-
import { getEntityTools } from "./tools/entities.js";
|
|
21
|
-
import { getDynamicEntityTools } from "./tools/dynamic-entities.js";
|
|
22
|
-
import { getLayoutTools } from "./tools/layouts.js";
|
|
23
|
-
import { getQueryTools } from "./tools/queries.js";
|
|
24
|
-
import { getMenuTools } from "./tools/menus.js";
|
|
25
|
-
import { getThemeTools } from "./tools/themes.js";
|
|
26
|
-
import { getLanguageTools } from "./tools/languages.js";
|
|
27
|
-
// ─── Configuration ─────────────────────────────────────────
|
|
13
|
+
import { buildMcpServerForSession } from "./factory.js";
|
|
28
14
|
// Load .env file when running locally (no-op if vars already set by MCP client)
|
|
29
15
|
// quiet:true prevents dotenvx from writing to stdout (which breaks MCP stdio protocol)
|
|
30
16
|
loadDotenv({ quiet: true, override: false });
|
|
31
17
|
function getConfig() {
|
|
32
18
|
const required = (key) => {
|
|
33
19
|
const value = process.env[key];
|
|
34
|
-
if (!value)
|
|
20
|
+
if (!value)
|
|
35
21
|
throw new Error(`Missing required environment variable: ${key}`);
|
|
36
|
-
}
|
|
37
22
|
return value;
|
|
38
23
|
};
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
// ─── Server Setup ──────────────────────────────────────────
|
|
48
|
-
async function main() {
|
|
49
|
-
const config = getConfig();
|
|
50
|
-
const tokenManager = new TokenManager({
|
|
51
|
-
adminApiUrl: config.adminApiUrl,
|
|
52
|
-
email: config.email,
|
|
53
|
-
password: config.password,
|
|
54
|
-
});
|
|
55
|
-
const client = new WapClient({
|
|
56
|
-
adminApiUrl: config.adminApiUrl,
|
|
57
|
-
siteKey: config.siteKey,
|
|
58
|
-
language: config.language,
|
|
59
|
-
tokenManager,
|
|
60
|
-
});
|
|
61
|
-
// Collect all tools
|
|
62
|
-
const componentTools = getComponentTools(client);
|
|
63
|
-
const pageTools = getPageTools(client);
|
|
64
|
-
const widgetTools = getWidgetTools(client);
|
|
65
|
-
const generalTools = getGeneralTools(client);
|
|
66
|
-
const siteTools = getSiteTools(client);
|
|
67
|
-
const entityTools = getEntityTools(client);
|
|
68
|
-
const dynamicEntityTools = getDynamicEntityTools(client);
|
|
69
|
-
const layoutTools = getLayoutTools(client);
|
|
70
|
-
const queryTools = getQueryTools(client);
|
|
71
|
-
const menuTools = getMenuTools(client);
|
|
72
|
-
const themeTools = getThemeTools(client);
|
|
73
|
-
const languageTools = getLanguageTools(client);
|
|
74
|
-
const allTools = {
|
|
75
|
-
...componentTools,
|
|
76
|
-
...pageTools,
|
|
77
|
-
...widgetTools,
|
|
78
|
-
...generalTools,
|
|
79
|
-
...siteTools,
|
|
80
|
-
...entityTools,
|
|
81
|
-
...dynamicEntityTools,
|
|
82
|
-
...layoutTools,
|
|
83
|
-
...queryTools,
|
|
84
|
-
...menuTools,
|
|
85
|
-
...themeTools,
|
|
86
|
-
...languageTools,
|
|
87
|
-
};
|
|
88
|
-
// Create MCP Server
|
|
89
|
-
const server = new McpServer({
|
|
90
|
-
name: "wappa-mcp",
|
|
91
|
-
version: "1.0.0",
|
|
92
|
-
});
|
|
93
|
-
// Register each tool with the MCP server
|
|
94
|
-
for (const [toolName, toolDef] of Object.entries(allTools)) {
|
|
95
|
-
const { description, inputSchema, handler } = toolDef;
|
|
96
|
-
// Convert JSON Schema properties to Zod schema
|
|
97
|
-
const zodShape = {};
|
|
98
|
-
const props = inputSchema.properties || {};
|
|
99
|
-
const required = inputSchema.required || [];
|
|
100
|
-
for (const [propName, propDef] of Object.entries(props)) {
|
|
101
|
-
let zodType;
|
|
102
|
-
switch (propDef.type) {
|
|
103
|
-
case "string":
|
|
104
|
-
zodType = propDef.enum ? z.enum(propDef.enum) : z.string();
|
|
105
|
-
break;
|
|
106
|
-
case "number":
|
|
107
|
-
zodType = z.number();
|
|
108
|
-
break;
|
|
109
|
-
case "boolean":
|
|
110
|
-
zodType = z.boolean();
|
|
111
|
-
break;
|
|
112
|
-
case "object":
|
|
113
|
-
zodType = z.record(z.string(), z.unknown());
|
|
114
|
-
break;
|
|
115
|
-
case "array":
|
|
116
|
-
zodType = z.array(z.unknown());
|
|
117
|
-
break;
|
|
118
|
-
default:
|
|
119
|
-
zodType = z.unknown();
|
|
120
|
-
}
|
|
121
|
-
if (propDef.description) {
|
|
122
|
-
zodType = zodType.describe(propDef.description);
|
|
123
|
-
}
|
|
124
|
-
if (!required.includes(propName)) {
|
|
125
|
-
zodType = zodType.optional();
|
|
126
|
-
}
|
|
127
|
-
zodShape[propName] = zodType;
|
|
128
|
-
}
|
|
129
|
-
server.tool(`wappa_${toolName}`, description, zodShape, async (args) => {
|
|
130
|
-
try {
|
|
131
|
-
return await handler(args);
|
|
132
|
-
}
|
|
133
|
-
catch (error) {
|
|
134
|
-
return {
|
|
135
|
-
content: [
|
|
136
|
-
{
|
|
137
|
-
type: "text",
|
|
138
|
-
text: `Error: ${error.message}`,
|
|
139
|
-
},
|
|
140
|
-
],
|
|
141
|
-
isError: true,
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
});
|
|
24
|
+
const adminApiUrl = required("WAP_ADMIN_API_URL").replace(/\/+$/, "");
|
|
25
|
+
const siteKey = process.env.WAP_SITE_KEY || process.env.WAP_SITE_ID || "";
|
|
26
|
+
const language = process.env.WAP_LANGUAGE || "en-us";
|
|
27
|
+
let auth;
|
|
28
|
+
if (process.env.WAP_API_KEY) {
|
|
29
|
+
auth = { kind: "apiKey", apiKey: process.env.WAP_API_KEY };
|
|
145
30
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
contents: [
|
|
153
|
-
{
|
|
154
|
-
uri: uri.href,
|
|
155
|
-
mimeType: "application/json",
|
|
156
|
-
text: JSON.stringify({
|
|
157
|
-
adminApiUrl: config.adminApiUrl,
|
|
158
|
-
siteKey: config.siteKey,
|
|
159
|
-
language: config.language,
|
|
160
|
-
}, null, 2),
|
|
161
|
-
},
|
|
162
|
-
],
|
|
163
|
-
}));
|
|
164
|
-
// Dynamic: site listesi
|
|
165
|
-
server.resource("wappa-sites", "wappa://sites", {
|
|
166
|
-
description: "WAPPA'daki tüm site listesi (canlı veri)",
|
|
167
|
-
mimeType: "application/json",
|
|
168
|
-
}, async (uri) => {
|
|
169
|
-
const result = await client.getSites({});
|
|
170
|
-
return {
|
|
171
|
-
contents: [
|
|
172
|
-
{
|
|
173
|
-
uri: uri.href,
|
|
174
|
-
mimeType: "application/json",
|
|
175
|
-
text: JSON.stringify(result, null, 2),
|
|
176
|
-
},
|
|
177
|
-
],
|
|
178
|
-
};
|
|
179
|
-
});
|
|
180
|
-
// Dynamic: aktif sitenin sayfaları
|
|
181
|
-
server.resource("wappa-pages", "wappa://pages", {
|
|
182
|
-
description: "Aktif sitedeki tüm sayfalar (canlı veri)",
|
|
183
|
-
mimeType: "application/json",
|
|
184
|
-
}, async (uri) => {
|
|
185
|
-
const result = await client.getPages({});
|
|
186
|
-
return {
|
|
187
|
-
contents: [
|
|
188
|
-
{
|
|
189
|
-
uri: uri.href,
|
|
190
|
-
mimeType: "application/json",
|
|
191
|
-
text: JSON.stringify(result, null, 2),
|
|
192
|
-
},
|
|
193
|
-
],
|
|
194
|
-
};
|
|
195
|
-
});
|
|
196
|
-
// Dynamic: bileşenler
|
|
197
|
-
server.resource("wappa-components", "wappa://components", {
|
|
198
|
-
description: "Aktif sitedeki tüm bileşenler (canlı veri)",
|
|
199
|
-
mimeType: "application/json",
|
|
200
|
-
}, async (uri) => {
|
|
201
|
-
const result = await client.getComponents({});
|
|
202
|
-
return {
|
|
203
|
-
contents: [
|
|
204
|
-
{
|
|
205
|
-
uri: uri.href,
|
|
206
|
-
mimeType: "application/json",
|
|
207
|
-
text: JSON.stringify(result, null, 2),
|
|
208
|
-
},
|
|
209
|
-
],
|
|
210
|
-
};
|
|
211
|
-
});
|
|
212
|
-
// Dynamic: entityler
|
|
213
|
-
server.resource("wappa-entities", "wappa://entities", {
|
|
214
|
-
description: "Tanımlı tüm entity şemaları (canlı veri)",
|
|
215
|
-
mimeType: "application/json",
|
|
216
|
-
}, async (uri) => {
|
|
217
|
-
const result = await client.getEntities({});
|
|
218
|
-
return {
|
|
219
|
-
contents: [
|
|
220
|
-
{
|
|
221
|
-
uri: uri.href,
|
|
222
|
-
mimeType: "application/json",
|
|
223
|
-
text: JSON.stringify(result, null, 2),
|
|
224
|
-
},
|
|
225
|
-
],
|
|
31
|
+
else {
|
|
32
|
+
auth = {
|
|
33
|
+
kind: "emailPwd",
|
|
34
|
+
adminApiUrl,
|
|
35
|
+
email: required("WAP_EMAIL"),
|
|
36
|
+
password: required("WAP_PASSWORD"),
|
|
226
37
|
};
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
return {
|
|
235
|
-
contents: [
|
|
236
|
-
{
|
|
237
|
-
uri: uri.href,
|
|
238
|
-
mimeType: "application/json",
|
|
239
|
-
text: JSON.stringify(result, null, 2),
|
|
240
|
-
},
|
|
241
|
-
],
|
|
242
|
-
};
|
|
243
|
-
});
|
|
244
|
-
// Dynamic: sorgu şablonları
|
|
245
|
-
server.resource("wappa-queries", "wappa://queries", {
|
|
246
|
-
description: "Kayıtlı tüm query/sorgu şablonları (canlı veri)",
|
|
247
|
-
mimeType: "application/json",
|
|
248
|
-
}, async (uri) => {
|
|
249
|
-
const result = await client.getQueries({});
|
|
250
|
-
return {
|
|
251
|
-
contents: [
|
|
252
|
-
{
|
|
253
|
-
uri: uri.href,
|
|
254
|
-
mimeType: "application/json",
|
|
255
|
-
text: JSON.stringify(result, null, 2),
|
|
256
|
-
},
|
|
257
|
-
],
|
|
258
|
-
};
|
|
259
|
-
});
|
|
260
|
-
// Dynamic Resource Template: belirli bir entity'nin kayıtları
|
|
261
|
-
server.resource("wappa-entity-records", new ResourceTemplate("wappa://entities/{entityId}/records", {
|
|
262
|
-
list: undefined,
|
|
263
|
-
}), {
|
|
264
|
-
description: "Belirli bir entity'nin tüm kayıtları. URI: wappa://entities/{entityId}/records",
|
|
265
|
-
mimeType: "application/json",
|
|
266
|
-
}, async (uri, { entityId }) => {
|
|
267
|
-
const result = await client.getDynamicEntities(entityId, {});
|
|
268
|
-
return {
|
|
269
|
-
contents: [
|
|
270
|
-
{
|
|
271
|
-
uri: uri.href,
|
|
272
|
-
mimeType: "application/json",
|
|
273
|
-
text: JSON.stringify(result, null, 2),
|
|
274
|
-
},
|
|
275
|
-
],
|
|
276
|
-
};
|
|
277
|
-
});
|
|
278
|
-
// Start the server
|
|
279
|
-
const transport = new StdioServerTransport();
|
|
280
|
-
await server.connect(transport);
|
|
38
|
+
}
|
|
39
|
+
return { adminApiUrl, siteKey, language, auth };
|
|
40
|
+
}
|
|
41
|
+
async function main() {
|
|
42
|
+
const cfg = getConfig();
|
|
43
|
+
const { server } = buildMcpServerForSession(cfg);
|
|
44
|
+
await server.connect(new StdioServerTransport());
|
|
281
45
|
console.error("WAPPA MCP Server running on stdio");
|
|
282
46
|
}
|
|
283
47
|
main().catch((error) => {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;GAQG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,wBAAwB,EAAoB,MAAM,cAAc,CAAC;AAE1E,gFAAgF;AAChF,uFAAuF;AACvF,UAAU,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;AAE7C,SAAS,SAAS;IAChB,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAU,EAAE;QACvC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,CAAC,KAAK;YACR,MAAM,IAAI,KAAK,CAAC,0CAA0C,GAAG,EAAE,CAAC,CAAC;QACnE,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,QAAQ,CAAC,mBAAmB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACtE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC;IAC1E,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,OAAO,CAAC;IAErD,IAAI,IAAiB,CAAC;IACtB,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;QAC5B,IAAI,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;IAC7D,CAAC;SAAM,CAAC;QACN,IAAI,GAAG;YACL,IAAI,EAAE,UAAU;YAChB,WAAW;YACX,KAAK,EAAE,QAAQ,CAAC,WAAW,CAAC;YAC5B,QAAQ,EAAE,QAAQ,CAAC,cAAc,CAAC;SACnC,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAClD,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;IACxB,MAAM,EAAE,MAAM,EAAE,GAAG,wBAAwB,CAAC,GAAG,CAAC,CAAC;IACjD,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;IACjD,OAAO,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;AACrD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,KAAK,CAAC,CAAC;IAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* WAPPA MCP HTTP Server
|
|
4
|
+
*
|
|
5
|
+
* Exposes the same MCP tools as the stdio entry point but over HTTP using
|
|
6
|
+
* the Streamable-HTTP transport (MCP spec §HTTP Transport).
|
|
7
|
+
*
|
|
8
|
+
* Admin UI and external clients can use this server by:
|
|
9
|
+
* 1. Sending POST /mcp with an initialize request + auth headers
|
|
10
|
+
* 2. Continuing the session with the returned Mcp-Session-Id header
|
|
11
|
+
*
|
|
12
|
+
* Auth headers (required on every request):
|
|
13
|
+
* Authorization: Bearer <jwt> — Admin UI passthrough
|
|
14
|
+
* X-API-Key: <key> — Backend Organisation API Key
|
|
15
|
+
* X-Wappa-Site-Key: <slug> — target site slug
|
|
16
|
+
* X-Wappa-Admin-Api: <url> — backend Admin API base URL
|
|
17
|
+
* X-Wappa-Language: <locale> — optional, default "en-us"
|
|
18
|
+
*
|
|
19
|
+
* Environment variables:
|
|
20
|
+
* PORT — HTTP port (default 4100)
|
|
21
|
+
* ALLOWED_ORIGINS — comma-separated CORS origins (default "*")
|
|
22
|
+
* RATE_LIMIT_RPM — requests per minute per IP (default 120)
|
|
23
|
+
*/
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;GAqBG"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* WAPPA MCP HTTP Server
|
|
4
|
+
*
|
|
5
|
+
* Exposes the same MCP tools as the stdio entry point but over HTTP using
|
|
6
|
+
* the Streamable-HTTP transport (MCP spec §HTTP Transport).
|
|
7
|
+
*
|
|
8
|
+
* Admin UI and external clients can use this server by:
|
|
9
|
+
* 1. Sending POST /mcp with an initialize request + auth headers
|
|
10
|
+
* 2. Continuing the session with the returned Mcp-Session-Id header
|
|
11
|
+
*
|
|
12
|
+
* Auth headers (required on every request):
|
|
13
|
+
* Authorization: Bearer <jwt> — Admin UI passthrough
|
|
14
|
+
* X-API-Key: <key> — Backend Organisation API Key
|
|
15
|
+
* X-Wappa-Site-Key: <slug> — target site slug
|
|
16
|
+
* X-Wappa-Admin-Api: <url> — backend Admin API base URL
|
|
17
|
+
* X-Wappa-Language: <locale> — optional, default "en-us"
|
|
18
|
+
*
|
|
19
|
+
* Environment variables:
|
|
20
|
+
* PORT — HTTP port (default 4100)
|
|
21
|
+
* ALLOWED_ORIGINS — comma-separated CORS origins (default "*")
|
|
22
|
+
* RATE_LIMIT_RPM — requests per minute per IP (default 120)
|
|
23
|
+
*/
|
|
24
|
+
import express from "express";
|
|
25
|
+
import helmet from "helmet";
|
|
26
|
+
import cors from "cors";
|
|
27
|
+
import rateLimit from "express-rate-limit";
|
|
28
|
+
import { config as loadDotenv } from "dotenv";
|
|
29
|
+
import { wappaAuthMiddleware } from "./http/auth.js";
|
|
30
|
+
import { handleMcpPost, handleMcpGet, handleMcpDelete, } from "./http/transport.js";
|
|
31
|
+
import { closeAllSessions } from "./http/session.js";
|
|
32
|
+
loadDotenv({ quiet: true, override: false });
|
|
33
|
+
const PORT = Number(process.env.PORT ?? 4100);
|
|
34
|
+
const allowedOrigins = process.env.ALLOWED_ORIGINS
|
|
35
|
+
? process.env.ALLOWED_ORIGINS.split(",").map((s) => s.trim())
|
|
36
|
+
: ["*"];
|
|
37
|
+
const rateLimitRpm = Number(process.env.RATE_LIMIT_RPM ?? 120);
|
|
38
|
+
// ─── App setup ──────────────────────────────────────────────
|
|
39
|
+
const app = express();
|
|
40
|
+
// Security headers
|
|
41
|
+
app.use(helmet());
|
|
42
|
+
// CORS
|
|
43
|
+
app.use(cors({
|
|
44
|
+
origin: allowedOrigins.includes("*") ? "*" : allowedOrigins,
|
|
45
|
+
exposedHeaders: ["Mcp-Session-Id"],
|
|
46
|
+
}));
|
|
47
|
+
// Rate limiting
|
|
48
|
+
app.use(rateLimit({
|
|
49
|
+
windowMs: 60_000,
|
|
50
|
+
max: rateLimitRpm,
|
|
51
|
+
standardHeaders: true,
|
|
52
|
+
legacyHeaders: false,
|
|
53
|
+
}));
|
|
54
|
+
// JSON body (2 MB limit)
|
|
55
|
+
app.use(express.json({ limit: "2mb" }));
|
|
56
|
+
// ─── Routes ─────────────────────────────────────────────────
|
|
57
|
+
app.get("/health", (_req, res) => {
|
|
58
|
+
res.json({ status: "ok", transport: "streamable-http" });
|
|
59
|
+
});
|
|
60
|
+
// MCP endpoint — auth middleware applied only to /mcp
|
|
61
|
+
app.post("/mcp", wappaAuthMiddleware, (req, res) => {
|
|
62
|
+
handleMcpPost(req, res).catch((err) => {
|
|
63
|
+
console.error("MCP POST error:", err);
|
|
64
|
+
if (!res.headersSent)
|
|
65
|
+
res.status(500).json({ error: "Internal server error" });
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
app.get("/mcp", wappaAuthMiddleware, (req, res) => {
|
|
69
|
+
handleMcpGet(req, res).catch((err) => {
|
|
70
|
+
console.error("MCP GET error:", err);
|
|
71
|
+
if (!res.headersSent)
|
|
72
|
+
res.status(500).json({ error: "Internal server error" });
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
app.delete("/mcp", wappaAuthMiddleware, (req, res) => {
|
|
76
|
+
handleMcpDelete(req, res).catch((err) => {
|
|
77
|
+
console.error("MCP DELETE error:", err);
|
|
78
|
+
if (!res.headersSent)
|
|
79
|
+
res.status(500).json({ error: "Internal server error" });
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
// ─── Start ──────────────────────────────────────────────────
|
|
83
|
+
const httpServer = app.listen(PORT, () => {
|
|
84
|
+
console.log(`WAPPA MCP HTTP Server listening on port ${PORT}`);
|
|
85
|
+
});
|
|
86
|
+
// Graceful shutdown
|
|
87
|
+
async function shutdown(signal) {
|
|
88
|
+
console.log(`Received ${signal} — shutting down gracefully`);
|
|
89
|
+
await closeAllSessions();
|
|
90
|
+
httpServer.close(() => process.exit(0));
|
|
91
|
+
}
|
|
92
|
+
process.on("SIGTERM", () => void shutdown("SIGTERM"));
|
|
93
|
+
process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
94
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tools for WAP AI Chat Sessions CRUD operations
|
|
3
|
+
* AI Chat Sessions = stored AI assistant conversations (site-scoped),
|
|
4
|
+
* optionally linked to a query and grouped by session type.
|
|
5
|
+
*/
|
|
6
|
+
import { WapClient } from "../client.js";
|
|
7
|
+
export declare function getAiChatSessionTools(client: WapClient): {
|
|
8
|
+
list_ai_chat_sessions: {
|
|
9
|
+
description: string;
|
|
10
|
+
inputSchema: {
|
|
11
|
+
type: "object";
|
|
12
|
+
properties: {
|
|
13
|
+
queryId: {
|
|
14
|
+
type: string;
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
sessionType: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
handler: (args: {
|
|
24
|
+
queryId?: string;
|
|
25
|
+
sessionType?: number;
|
|
26
|
+
}) => Promise<{
|
|
27
|
+
content: {
|
|
28
|
+
type: "text";
|
|
29
|
+
text: string;
|
|
30
|
+
}[];
|
|
31
|
+
}>;
|
|
32
|
+
};
|
|
33
|
+
get_ai_chat_session: {
|
|
34
|
+
description: string;
|
|
35
|
+
inputSchema: {
|
|
36
|
+
type: "object";
|
|
37
|
+
properties: {
|
|
38
|
+
id: {
|
|
39
|
+
type: string;
|
|
40
|
+
description: string;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
required: string[];
|
|
44
|
+
};
|
|
45
|
+
handler: (args: {
|
|
46
|
+
id: string;
|
|
47
|
+
}) => Promise<{
|
|
48
|
+
content: {
|
|
49
|
+
type: "text";
|
|
50
|
+
text: string;
|
|
51
|
+
}[];
|
|
52
|
+
}>;
|
|
53
|
+
};
|
|
54
|
+
create_ai_chat_session: {
|
|
55
|
+
description: string;
|
|
56
|
+
inputSchema: {
|
|
57
|
+
type: "object";
|
|
58
|
+
properties: {
|
|
59
|
+
title: {
|
|
60
|
+
type: string;
|
|
61
|
+
description: string;
|
|
62
|
+
};
|
|
63
|
+
messages: {
|
|
64
|
+
type: string;
|
|
65
|
+
description: string;
|
|
66
|
+
};
|
|
67
|
+
queryId: {
|
|
68
|
+
type: string;
|
|
69
|
+
description: string;
|
|
70
|
+
};
|
|
71
|
+
sessionType: {
|
|
72
|
+
type: string;
|
|
73
|
+
description: string;
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
required: string[];
|
|
77
|
+
};
|
|
78
|
+
handler: (args: {
|
|
79
|
+
title: string;
|
|
80
|
+
messages: string;
|
|
81
|
+
queryId?: string;
|
|
82
|
+
sessionType?: number;
|
|
83
|
+
}) => Promise<{
|
|
84
|
+
content: {
|
|
85
|
+
type: "text";
|
|
86
|
+
text: string;
|
|
87
|
+
}[];
|
|
88
|
+
}>;
|
|
89
|
+
};
|
|
90
|
+
update_ai_chat_session: {
|
|
91
|
+
description: string;
|
|
92
|
+
inputSchema: {
|
|
93
|
+
type: "object";
|
|
94
|
+
properties: {
|
|
95
|
+
id: {
|
|
96
|
+
type: string;
|
|
97
|
+
description: string;
|
|
98
|
+
};
|
|
99
|
+
title: {
|
|
100
|
+
type: string;
|
|
101
|
+
description: string;
|
|
102
|
+
};
|
|
103
|
+
messages: {
|
|
104
|
+
type: string;
|
|
105
|
+
description: string;
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
required: string[];
|
|
109
|
+
};
|
|
110
|
+
handler: (args: {
|
|
111
|
+
id: string;
|
|
112
|
+
title?: string;
|
|
113
|
+
messages?: string;
|
|
114
|
+
}) => Promise<{
|
|
115
|
+
content: {
|
|
116
|
+
type: "text";
|
|
117
|
+
text: string;
|
|
118
|
+
}[];
|
|
119
|
+
}>;
|
|
120
|
+
};
|
|
121
|
+
delete_ai_chat_session: {
|
|
122
|
+
description: string;
|
|
123
|
+
inputSchema: {
|
|
124
|
+
type: "object";
|
|
125
|
+
properties: {
|
|
126
|
+
id: {
|
|
127
|
+
type: string;
|
|
128
|
+
description: string;
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
required: string[];
|
|
132
|
+
};
|
|
133
|
+
handler: (args: {
|
|
134
|
+
id: string;
|
|
135
|
+
}) => Promise<{
|
|
136
|
+
content: {
|
|
137
|
+
type: "text";
|
|
138
|
+
text: string;
|
|
139
|
+
}[];
|
|
140
|
+
}>;
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
//# sourceMappingURL=ai-chat-sessions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-chat-sessions.d.ts","sourceRoot":"","sources":["../../src/tools/ai-chat-sessions.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS;;;;;;;;;;;;;;;;wBAoB3B;YAAE,OAAO,CAAC,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE;;;;;;;;;;;;;;;;;;;wBA8B1C;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAwCd;YACpB,KAAK,EAAE,MAAM,CAAC;YACd,QAAQ,EAAE,MAAM,CAAC;YACjB,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,WAAW,CAAC,EAAE,MAAM,CAAC;SACtB;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAkCqB;YACpB,EAAE,EAAE,MAAM,CAAC;YACX,KAAK,CAAC,EAAE,MAAM,CAAC;YACf,QAAQ,CAAC,EAAE,MAAM,CAAC;SACnB;;;;;;;;;;;;;;;;;;;wBA6BqB;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE;;;;;;;EAazC"}
|