uiplug-mcp 1.1.1 → 1.2.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/dist/index.js +80 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28,12 +28,8 @@ async function validateApiKey(apiKey) {
|
|
|
28
28
|
if (error || !data) {
|
|
29
29
|
return { userId: null, error: "Invalid or revoked API key." };
|
|
30
30
|
}
|
|
31
|
-
// Fire-and-forget: update usage stats
|
|
32
|
-
supabase
|
|
33
|
-
.from("api_keys")
|
|
34
|
-
.update({ last_used_at: new Date().toISOString(), usage_total: data.usage_total + 1 })
|
|
35
|
-
.eq("id", data.id)
|
|
36
|
-
.then(() => { });
|
|
31
|
+
// Fire-and-forget: update usage stats via SECURITY DEFINER RPC (bypasses RLS)
|
|
32
|
+
supabase.rpc("increment_api_key_usage", { p_key_id: data.id }).then(() => { });
|
|
37
33
|
return { userId: data.user_id, error: null };
|
|
38
34
|
}
|
|
39
35
|
// ── MCP Server ────────────────────────────────────────────────────────────────
|
|
@@ -100,13 +96,59 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
100
96
|
},
|
|
101
97
|
},
|
|
102
98
|
},
|
|
99
|
+
{
|
|
100
|
+
name: "create_component",
|
|
101
|
+
description: "Submit a new UI component to the UIPlug marketplace. " +
|
|
102
|
+
"The component will be submitted for review (status: pending) and visible in your dashboard at uiplug.com/dashboard/components. " +
|
|
103
|
+
"Use this after building a component to share it with the community.",
|
|
104
|
+
inputSchema: {
|
|
105
|
+
type: "object",
|
|
106
|
+
required: ["name", "description", "framework", "category", "code"],
|
|
107
|
+
properties: {
|
|
108
|
+
name: {
|
|
109
|
+
type: "string",
|
|
110
|
+
description: "Component name (e.g. 'Gradient Button').",
|
|
111
|
+
},
|
|
112
|
+
description: {
|
|
113
|
+
type: "string",
|
|
114
|
+
description: "A clear description of what the component does and its key features.",
|
|
115
|
+
},
|
|
116
|
+
framework: {
|
|
117
|
+
type: "string",
|
|
118
|
+
description: "Framework: React | Vue | Svelte | Angular | HTML / CSS | " +
|
|
119
|
+
"Jetpack Compose | Compose Multiplatform | Flutter | SwiftUI | React Native",
|
|
120
|
+
},
|
|
121
|
+
category: {
|
|
122
|
+
type: "string",
|
|
123
|
+
description: "Category: Layout | Navigation | Input | Data Display | Feedback | Sensors",
|
|
124
|
+
},
|
|
125
|
+
code: {
|
|
126
|
+
type: "string",
|
|
127
|
+
description: "Full component source code.",
|
|
128
|
+
},
|
|
129
|
+
installation: {
|
|
130
|
+
type: "string",
|
|
131
|
+
description: "Optional dependency/installation snippet (e.g. 'npm install some-package').",
|
|
132
|
+
},
|
|
133
|
+
tags: {
|
|
134
|
+
type: "array",
|
|
135
|
+
items: { type: "string" },
|
|
136
|
+
description: "Optional tag names (e.g. ['button', 'gradient', 'animation']).",
|
|
137
|
+
},
|
|
138
|
+
model: {
|
|
139
|
+
type: "string",
|
|
140
|
+
description: "Optional — AI model used to build this (e.g. 'Claude Sonnet 4.6').",
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
},
|
|
103
145
|
],
|
|
104
146
|
}));
|
|
105
147
|
// ── Tool handlers ─────────────────────────────────────────────────────────────
|
|
106
148
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
107
149
|
const { name, arguments: args } = request.params;
|
|
108
150
|
// ── Validate API key ─────────────────────────────────────────────────────────
|
|
109
|
-
const { error: authError } = await validateApiKey(process.env.UIPLUG_API_KEY);
|
|
151
|
+
const { userId, error: authError } = await validateApiKey(process.env.UIPLUG_API_KEY);
|
|
110
152
|
if (authError) {
|
|
111
153
|
return { content: [{ type: "text", text: authError }], isError: true };
|
|
112
154
|
}
|
|
@@ -286,6 +328,37 @@ ${c.code_component}
|
|
|
286
328
|
`;
|
|
287
329
|
return { content: [{ type: "text", text: output }] };
|
|
288
330
|
}
|
|
331
|
+
// ── create_component ────────────────────────────────────────────────────────
|
|
332
|
+
if (name === "create_component") {
|
|
333
|
+
const { name: compName, description, framework, category, code, installation, tags, model, } = (args ?? {});
|
|
334
|
+
const { data: componentId, error } = await supabase.rpc("create_component", {
|
|
335
|
+
p_user_id: userId,
|
|
336
|
+
p_name: compName,
|
|
337
|
+
p_description: description,
|
|
338
|
+
p_framework: framework,
|
|
339
|
+
p_category: category,
|
|
340
|
+
p_code: code,
|
|
341
|
+
p_installation: installation ?? null,
|
|
342
|
+
p_tags: tags ?? [],
|
|
343
|
+
p_model: model ?? null,
|
|
344
|
+
});
|
|
345
|
+
if (error) {
|
|
346
|
+
return { content: [{ type: "text", text: `Error submitting component: ${error.message}` }], isError: true };
|
|
347
|
+
}
|
|
348
|
+
return {
|
|
349
|
+
content: [
|
|
350
|
+
{
|
|
351
|
+
type: "text",
|
|
352
|
+
text: `Component submitted for review!\n\n` +
|
|
353
|
+
`**Name:** ${compName}\n` +
|
|
354
|
+
`**Framework:** ${framework}\n` +
|
|
355
|
+
`**Category:** ${category}\n` +
|
|
356
|
+
`**ID:** ${componentId}\n\n` +
|
|
357
|
+
`View and manage it at: https://uiplug.com/dashboard/components`,
|
|
358
|
+
},
|
|
359
|
+
],
|
|
360
|
+
};
|
|
361
|
+
}
|
|
289
362
|
return {
|
|
290
363
|
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
291
364
|
isError: true,
|