strapi-plugin-ai-sdk 0.7.4 → 0.7.7
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/_chunks/{App-DCV7o6Hc.mjs → App-DMdQymB3.mjs} +3 -3
- package/dist/_chunks/{App-CEEsJsKL.js → App-joEmdpxi.js} +3 -3
- package/dist/_chunks/{index-Cw2aiQ8K.js → index-B7qLITWV.js} +1 -1
- package/dist/_chunks/{index-BMrDQVQl.mjs → index-DrLcqX__.mjs} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +92 -55
- package/dist/server/index.mjs +92 -55
- package/dist/server/src/tool-logic/create-content.d.ts +27 -0
- package/dist/server/src/tool-logic/index.d.ts +4 -2
- package/dist/server/src/tool-logic/update-content.d.ts +29 -0
- package/dist/server/src/tool-logic/upload-media.d.ts +1 -1
- package/dist/server/src/tools/definitions/{write-content.d.ts → create-content.d.ts} +1 -1
- package/dist/server/src/tools/definitions/update-content.d.ts +2 -0
- package/package.json +1 -1
- package/dist/server/src/tool-logic/write-content.d.ts +0 -34
|
@@ -4,7 +4,7 @@ import { Link, useNavigate, Routes, Route } from "react-router-dom";
|
|
|
4
4
|
import { Box, Typography, TextInput, Button, Main, SearchForm, Searchbar, Table, Thead, Tr, Th, Tbody, Td, Flex, Pagination, Modal, Field, Textarea, SingleSelect, SingleSelectOption } from "@strapi/design-system";
|
|
5
5
|
import { useState, useEffect, useCallback, useMemo, useRef, forwardRef } from "react";
|
|
6
6
|
import styled from "styled-components";
|
|
7
|
-
import { P as PLUGIN_ID } from "./index-
|
|
7
|
+
import { P as PLUGIN_ID } from "./index-DrLcqX__.mjs";
|
|
8
8
|
import { Plus, Trash, Sparkle, ArrowLeft, Pencil } from "@strapi/icons";
|
|
9
9
|
import Markdown from "react-markdown";
|
|
10
10
|
import remarkGfm from "remark-gfm";
|
|
@@ -1099,14 +1099,14 @@ function extractContentLinks(toolCall) {
|
|
|
1099
1099
|
}
|
|
1100
1100
|
return links;
|
|
1101
1101
|
}
|
|
1102
|
-
if (toolCall.toolName === "
|
|
1102
|
+
if (toolCall.toolName === "createContent" || toolCall.toolName === "updateContent") {
|
|
1103
1103
|
const doc = output.document;
|
|
1104
1104
|
const docId = doc?.documentId;
|
|
1105
1105
|
if (docId) {
|
|
1106
1106
|
const title = doc?.title || doc?.name || docId;
|
|
1107
1107
|
return [
|
|
1108
1108
|
{
|
|
1109
|
-
label: `${
|
|
1109
|
+
label: `${toolCall.toolName === "createContent" ? "Created" : "Updated"}: ${title}`,
|
|
1110
1110
|
to: buildContentManagerUrl(contentType, docId)
|
|
1111
1111
|
}
|
|
1112
1112
|
];
|
|
@@ -6,7 +6,7 @@ const reactRouterDom = require("react-router-dom");
|
|
|
6
6
|
const designSystem = require("@strapi/design-system");
|
|
7
7
|
const react = require("react");
|
|
8
8
|
const styled = require("styled-components");
|
|
9
|
-
const index = require("./index-
|
|
9
|
+
const index = require("./index-B7qLITWV.js");
|
|
10
10
|
const icons = require("@strapi/icons");
|
|
11
11
|
const Markdown = require("react-markdown");
|
|
12
12
|
const remarkGfm = require("remark-gfm");
|
|
@@ -1105,14 +1105,14 @@ function extractContentLinks(toolCall) {
|
|
|
1105
1105
|
}
|
|
1106
1106
|
return links;
|
|
1107
1107
|
}
|
|
1108
|
-
if (toolCall.toolName === "
|
|
1108
|
+
if (toolCall.toolName === "createContent" || toolCall.toolName === "updateContent") {
|
|
1109
1109
|
const doc = output.document;
|
|
1110
1110
|
const docId = doc?.documentId;
|
|
1111
1111
|
if (docId) {
|
|
1112
1112
|
const title = doc?.title || doc?.name || docId;
|
|
1113
1113
|
return [
|
|
1114
1114
|
{
|
|
1115
|
-
label: `${
|
|
1115
|
+
label: `${toolCall.toolName === "createContent" ? "Created" : "Updated"}: ${title}`,
|
|
1116
1116
|
to: buildContentManagerUrl(contentType, docId)
|
|
1117
1117
|
}
|
|
1118
1118
|
];
|
|
@@ -37,7 +37,7 @@ const index = {
|
|
|
37
37
|
defaultMessage: PLUGIN_ID
|
|
38
38
|
},
|
|
39
39
|
Component: async () => {
|
|
40
|
-
const { App } = await Promise.resolve().then(() => require("./App-
|
|
40
|
+
const { App } = await Promise.resolve().then(() => require("./App-joEmdpxi.js"));
|
|
41
41
|
return App;
|
|
42
42
|
}
|
|
43
43
|
});
|
package/dist/admin/index.js
CHANGED
package/dist/admin/index.mjs
CHANGED
package/dist/server/index.js
CHANGED
|
@@ -387,33 +387,41 @@ async function searchContent(strapi, params) {
|
|
|
387
387
|
}
|
|
388
388
|
};
|
|
389
389
|
}
|
|
390
|
-
const
|
|
390
|
+
const createContentSchema = zod.z.object({
|
|
391
391
|
contentType: zod.z.string().describe('Content type UID, e.g. "api::article.article"'),
|
|
392
|
-
action: zod.z.enum(["create", "update"]).describe("Whether to create a new document or update an existing one"),
|
|
393
|
-
documentId: zod.z.string().optional().describe("Required for update — the document ID to update"),
|
|
394
392
|
data: zod.z.record(zod.z.string(), zod.z.unknown()).describe("The field values to set. Must match the content type schema."),
|
|
395
393
|
status: zod.z.enum(["draft", "published"]).optional().describe("Document status. Defaults to draft."),
|
|
396
394
|
locale: zod.z.string().optional().describe('Locale code for i18n content, e.g. "en" or "fr"')
|
|
397
395
|
});
|
|
398
|
-
const
|
|
399
|
-
async function
|
|
400
|
-
const { contentType,
|
|
396
|
+
const createContentDescription = "Create a new document in any Strapi content type (articles, blog posts, pages, etc.). Use listContentTypes first to discover available content types and their fields.";
|
|
397
|
+
async function createContent(strapi, params) {
|
|
398
|
+
const { contentType, data, status, locale } = params;
|
|
401
399
|
if (!strapi.contentTypes[contentType]) {
|
|
402
400
|
throw new Error(`Content type "${contentType}" does not exist.`);
|
|
403
401
|
}
|
|
404
|
-
if (action === "update" && !documentId) {
|
|
405
|
-
throw new Error("documentId is required for update actions.");
|
|
406
|
-
}
|
|
407
402
|
const docs = strapi.documents(contentType);
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
403
|
+
const document = await docs.create({
|
|
404
|
+
data,
|
|
405
|
+
...status ? { status } : {},
|
|
406
|
+
...locale ? { locale } : {},
|
|
407
|
+
populate: "*"
|
|
408
|
+
});
|
|
409
|
+
return { action: "create", document };
|
|
410
|
+
}
|
|
411
|
+
const updateContentSchema = zod.z.object({
|
|
412
|
+
contentType: zod.z.string().describe('Content type UID, e.g. "api::article.article"'),
|
|
413
|
+
documentId: zod.z.string().describe("The document ID to update"),
|
|
414
|
+
data: zod.z.record(zod.z.string(), zod.z.unknown()).describe("The field values to set. Must match the content type schema."),
|
|
415
|
+
status: zod.z.enum(["draft", "published"]).optional().describe("Document status. Defaults to draft."),
|
|
416
|
+
locale: zod.z.string().optional().describe('Locale code for i18n content, e.g. "en" or "fr"')
|
|
417
|
+
});
|
|
418
|
+
const updateContentDescription = "Update an existing document in any Strapi content type. Use searchContent to find the document ID first.";
|
|
419
|
+
async function updateContent(strapi, params) {
|
|
420
|
+
const { contentType, documentId, data, status, locale } = params;
|
|
421
|
+
if (!strapi.contentTypes[contentType]) {
|
|
422
|
+
throw new Error(`Content type "${contentType}" does not exist.`);
|
|
416
423
|
}
|
|
424
|
+
const docs = strapi.documents(contentType);
|
|
417
425
|
const existing = await docs.findOne({
|
|
418
426
|
documentId,
|
|
419
427
|
...locale ? { locale } : {}
|
|
@@ -574,7 +582,7 @@ const uploadMediaSchema = zod.z.object({
|
|
|
574
582
|
caption: zod.z.string().optional().describe("Caption for the media file"),
|
|
575
583
|
alternativeText: zod.z.string().optional().describe("Alternative text for accessibility")
|
|
576
584
|
});
|
|
577
|
-
const uploadMediaDescription = "Upload a media file from a URL to the Strapi media library. Returns the uploaded file data. To link media to a content type field, use
|
|
585
|
+
const uploadMediaDescription = "Upload a media file from a URL to the Strapi media library. Returns the uploaded file data. To link media to a content type field, use createContent or updateContent with the file ID.";
|
|
578
586
|
async function uploadMedia(strapi, params) {
|
|
579
587
|
const { url, name, caption, alternativeText } = params;
|
|
580
588
|
let parsedUrl;
|
|
@@ -642,7 +650,7 @@ async function uploadMedia(strapi, params) {
|
|
|
642
650
|
return {
|
|
643
651
|
file: uploadedFile,
|
|
644
652
|
message: `File "${uploadedFile.name}" uploaded successfully (ID: ${uploadedFile.id}).`,
|
|
645
|
-
usage: `To link this file to a content type field, use
|
|
653
|
+
usage: `To link this file to a content type field, use createContent or updateContent with: { "fieldName": ${uploadedFile.id} }`
|
|
646
654
|
};
|
|
647
655
|
}
|
|
648
656
|
const CONTENT_TYPE$5 = "plugin::ai-sdk.public-memory";
|
|
@@ -1143,13 +1151,24 @@ async function sanitizeInput(strapi, uid, data, auth) {
|
|
|
1143
1151
|
throw error;
|
|
1144
1152
|
}
|
|
1145
1153
|
}
|
|
1146
|
-
const
|
|
1147
|
-
name: "
|
|
1148
|
-
description:
|
|
1149
|
-
schema:
|
|
1154
|
+
const createContentTool = {
|
|
1155
|
+
name: "createContent",
|
|
1156
|
+
description: createContentDescription,
|
|
1157
|
+
schema: createContentSchema,
|
|
1158
|
+
execute: async (args, strapi) => {
|
|
1159
|
+
const sanitizedData = await sanitizeInput(strapi, args.contentType, args.data);
|
|
1160
|
+
const result = await createContent(strapi, { ...args, data: sanitizedData });
|
|
1161
|
+
const sanitizedDoc = await sanitizeOutput(strapi, args.contentType, result.document);
|
|
1162
|
+
return { ...result, document: sanitizedDoc };
|
|
1163
|
+
}
|
|
1164
|
+
};
|
|
1165
|
+
const updateContentTool = {
|
|
1166
|
+
name: "updateContent",
|
|
1167
|
+
description: updateContentDescription,
|
|
1168
|
+
schema: updateContentSchema,
|
|
1150
1169
|
execute: async (args, strapi) => {
|
|
1151
1170
|
const sanitizedData = await sanitizeInput(strapi, args.contentType, args.data);
|
|
1152
|
-
const result = await
|
|
1171
|
+
const result = await updateContent(strapi, { ...args, data: sanitizedData });
|
|
1153
1172
|
const sanitizedDoc = await sanitizeOutput(strapi, args.contentType, result.document);
|
|
1154
1173
|
return { ...result, document: sanitizedDoc };
|
|
1155
1174
|
}
|
|
@@ -1217,7 +1236,8 @@ const manageTaskTool = {
|
|
|
1217
1236
|
const builtInTools = [
|
|
1218
1237
|
listContentTypesTool,
|
|
1219
1238
|
searchContentTool,
|
|
1220
|
-
|
|
1239
|
+
createContentTool,
|
|
1240
|
+
updateContentTool,
|
|
1221
1241
|
findOneContentTool,
|
|
1222
1242
|
uploadMediaTool,
|
|
1223
1243
|
sendEmailTool,
|
|
@@ -1231,6 +1251,14 @@ const PLUGIN_ID$2 = "ai-sdk";
|
|
|
1231
1251
|
const bootstrap = ({ strapi }) => {
|
|
1232
1252
|
const plugin = strapi.plugin(PLUGIN_ID$2);
|
|
1233
1253
|
const config2 = strapi.config.get(`plugin::${PLUGIN_ID$2}`);
|
|
1254
|
+
initializeProvider(strapi, plugin, config2);
|
|
1255
|
+
const registry = initializeToolRegistry(plugin);
|
|
1256
|
+
discoverPluginTools(strapi, registry);
|
|
1257
|
+
plugin.createMcpServer = () => createMcpServer(strapi);
|
|
1258
|
+
plugin.mcpSessions = /* @__PURE__ */ new Map();
|
|
1259
|
+
strapi.log.info(`[${PLUGIN_ID$2}] MCP endpoint available at: /api/${PLUGIN_ID$2}/mcp`);
|
|
1260
|
+
};
|
|
1261
|
+
function initializeProvider(strapi, plugin, config2) {
|
|
1234
1262
|
AIProvider.registerProvider("anthropic", ({ apiKey, baseURL }) => {
|
|
1235
1263
|
const provider = anthropic.createAnthropic({ apiKey, baseURL });
|
|
1236
1264
|
return (modelId) => provider(modelId);
|
|
@@ -1243,27 +1271,22 @@ const bootstrap = ({ strapi }) => {
|
|
|
1243
1271
|
} else {
|
|
1244
1272
|
strapi.log.warn(`[${PLUGIN_ID$2}] anthropicApiKey not configured, AI provider will not be available`);
|
|
1245
1273
|
}
|
|
1274
|
+
}
|
|
1275
|
+
function initializeToolRegistry(plugin) {
|
|
1246
1276
|
const toolRegistry = new ToolRegistry();
|
|
1247
1277
|
for (const tool of builtInTools) {
|
|
1248
1278
|
toolRegistry.register(tool);
|
|
1249
1279
|
}
|
|
1250
1280
|
plugin.toolRegistry = toolRegistry;
|
|
1281
|
+
return toolRegistry;
|
|
1282
|
+
}
|
|
1283
|
+
function discoverPluginTools(strapi, registry) {
|
|
1251
1284
|
const pluginNames = Object.keys(strapi.plugins).filter((n) => n !== PLUGIN_ID$2);
|
|
1252
1285
|
strapi.log.info(`[${PLUGIN_ID$2}] Scanning ${pluginNames.length} plugins for ai-tools: [${pluginNames.join(", ")}]`);
|
|
1253
1286
|
for (const [pluginName, pluginInstance] of Object.entries(strapi.plugins)) {
|
|
1254
1287
|
if (pluginName === PLUGIN_ID$2) continue;
|
|
1255
1288
|
try {
|
|
1256
|
-
|
|
1257
|
-
try {
|
|
1258
|
-
aiToolsService = strapi.plugin(pluginName)?.service?.("ai-tools");
|
|
1259
|
-
} catch {
|
|
1260
|
-
}
|
|
1261
|
-
if (!aiToolsService) {
|
|
1262
|
-
try {
|
|
1263
|
-
aiToolsService = pluginInstance.service?.("ai-tools");
|
|
1264
|
-
} catch {
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1289
|
+
const aiToolsService = resolveAiToolsService(strapi, pluginName, pluginInstance);
|
|
1267
1290
|
if (!aiToolsService?.getTools) {
|
|
1268
1291
|
strapi.log.debug(`[${PLUGIN_ID$2}] No ai-tools service on plugin: ${pluginName}`);
|
|
1269
1292
|
continue;
|
|
@@ -1271,21 +1294,7 @@ const bootstrap = ({ strapi }) => {
|
|
|
1271
1294
|
strapi.log.info(`[${PLUGIN_ID$2}] Found ai-tools service on plugin: ${pluginName}`);
|
|
1272
1295
|
const contributed = aiToolsService.getTools();
|
|
1273
1296
|
if (!Array.isArray(contributed)) continue;
|
|
1274
|
-
|
|
1275
|
-
for (const tool of contributed) {
|
|
1276
|
-
if (!tool.name || !tool.execute || !tool.schema) {
|
|
1277
|
-
strapi.log.warn(`[${PLUGIN_ID$2}] Invalid tool from ${pluginName}: ${tool.name || "unnamed"}`);
|
|
1278
|
-
continue;
|
|
1279
|
-
}
|
|
1280
|
-
const safeName = pluginName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1281
|
-
const namespacedName = `${safeName}__${tool.name}`;
|
|
1282
|
-
if (toolRegistry.has(namespacedName)) {
|
|
1283
|
-
strapi.log.warn(`[${PLUGIN_ID$2}] Duplicate tool: ${namespacedName}`);
|
|
1284
|
-
continue;
|
|
1285
|
-
}
|
|
1286
|
-
toolRegistry.register({ ...tool, name: namespacedName });
|
|
1287
|
-
count++;
|
|
1288
|
-
}
|
|
1297
|
+
const count = registerContributedTools(strapi, registry, pluginName, contributed);
|
|
1289
1298
|
if (count > 0) {
|
|
1290
1299
|
strapi.log.info(`[${PLUGIN_ID$2}] Registered ${count} tools from plugin: ${pluginName}`);
|
|
1291
1300
|
}
|
|
@@ -1293,10 +1302,38 @@ const bootstrap = ({ strapi }) => {
|
|
|
1293
1302
|
strapi.log.warn(`[${PLUGIN_ID$2}] Tool discovery failed for ${pluginName}: ${err}`);
|
|
1294
1303
|
}
|
|
1295
1304
|
}
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1305
|
+
}
|
|
1306
|
+
function registerContributedTools(strapi, registry, pluginName, tools) {
|
|
1307
|
+
const safeName = pluginName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1308
|
+
let count = 0;
|
|
1309
|
+
for (const tool of tools) {
|
|
1310
|
+
if (!tool.name || !tool.execute || !tool.schema) {
|
|
1311
|
+
strapi.log.warn(`[${PLUGIN_ID$2}] Invalid tool from ${pluginName}: ${tool.name || "unnamed"}`);
|
|
1312
|
+
continue;
|
|
1313
|
+
}
|
|
1314
|
+
const namespacedName = `${safeName}__${tool.name}`;
|
|
1315
|
+
if (registry.has(namespacedName)) {
|
|
1316
|
+
strapi.log.warn(`[${PLUGIN_ID$2}] Duplicate tool: ${namespacedName}`);
|
|
1317
|
+
continue;
|
|
1318
|
+
}
|
|
1319
|
+
registry.register({ ...tool, name: namespacedName });
|
|
1320
|
+
count++;
|
|
1321
|
+
}
|
|
1322
|
+
return count;
|
|
1323
|
+
}
|
|
1324
|
+
function resolveAiToolsService(strapi, pluginName, pluginInstance) {
|
|
1325
|
+
try {
|
|
1326
|
+
const svc = strapi.plugin(pluginName)?.service?.("ai-tools");
|
|
1327
|
+
if (svc) return svc;
|
|
1328
|
+
} catch {
|
|
1329
|
+
}
|
|
1330
|
+
try {
|
|
1331
|
+
const svc = pluginInstance.service?.("ai-tools");
|
|
1332
|
+
if (svc) return svc;
|
|
1333
|
+
} catch {
|
|
1334
|
+
}
|
|
1335
|
+
return null;
|
|
1336
|
+
}
|
|
1300
1337
|
const PLUGIN_ID$1 = "ai-sdk";
|
|
1301
1338
|
async function closeSession(strapi, sessionId, session) {
|
|
1302
1339
|
try {
|
package/dist/server/index.mjs
CHANGED
|
@@ -367,33 +367,41 @@ async function searchContent(strapi, params) {
|
|
|
367
367
|
}
|
|
368
368
|
};
|
|
369
369
|
}
|
|
370
|
-
const
|
|
370
|
+
const createContentSchema = z.object({
|
|
371
371
|
contentType: z.string().describe('Content type UID, e.g. "api::article.article"'),
|
|
372
|
-
action: z.enum(["create", "update"]).describe("Whether to create a new document or update an existing one"),
|
|
373
|
-
documentId: z.string().optional().describe("Required for update — the document ID to update"),
|
|
374
372
|
data: z.record(z.string(), z.unknown()).describe("The field values to set. Must match the content type schema."),
|
|
375
373
|
status: z.enum(["draft", "published"]).optional().describe("Document status. Defaults to draft."),
|
|
376
374
|
locale: z.string().optional().describe('Locale code for i18n content, e.g. "en" or "fr"')
|
|
377
375
|
});
|
|
378
|
-
const
|
|
379
|
-
async function
|
|
380
|
-
const { contentType,
|
|
376
|
+
const createContentDescription = "Create a new document in any Strapi content type (articles, blog posts, pages, etc.). Use listContentTypes first to discover available content types and their fields.";
|
|
377
|
+
async function createContent(strapi, params) {
|
|
378
|
+
const { contentType, data, status, locale } = params;
|
|
381
379
|
if (!strapi.contentTypes[contentType]) {
|
|
382
380
|
throw new Error(`Content type "${contentType}" does not exist.`);
|
|
383
381
|
}
|
|
384
|
-
if (action === "update" && !documentId) {
|
|
385
|
-
throw new Error("documentId is required for update actions.");
|
|
386
|
-
}
|
|
387
382
|
const docs = strapi.documents(contentType);
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
383
|
+
const document = await docs.create({
|
|
384
|
+
data,
|
|
385
|
+
...status ? { status } : {},
|
|
386
|
+
...locale ? { locale } : {},
|
|
387
|
+
populate: "*"
|
|
388
|
+
});
|
|
389
|
+
return { action: "create", document };
|
|
390
|
+
}
|
|
391
|
+
const updateContentSchema = z.object({
|
|
392
|
+
contentType: z.string().describe('Content type UID, e.g. "api::article.article"'),
|
|
393
|
+
documentId: z.string().describe("The document ID to update"),
|
|
394
|
+
data: z.record(z.string(), z.unknown()).describe("The field values to set. Must match the content type schema."),
|
|
395
|
+
status: z.enum(["draft", "published"]).optional().describe("Document status. Defaults to draft."),
|
|
396
|
+
locale: z.string().optional().describe('Locale code for i18n content, e.g. "en" or "fr"')
|
|
397
|
+
});
|
|
398
|
+
const updateContentDescription = "Update an existing document in any Strapi content type. Use searchContent to find the document ID first.";
|
|
399
|
+
async function updateContent(strapi, params) {
|
|
400
|
+
const { contentType, documentId, data, status, locale } = params;
|
|
401
|
+
if (!strapi.contentTypes[contentType]) {
|
|
402
|
+
throw new Error(`Content type "${contentType}" does not exist.`);
|
|
396
403
|
}
|
|
404
|
+
const docs = strapi.documents(contentType);
|
|
397
405
|
const existing = await docs.findOne({
|
|
398
406
|
documentId,
|
|
399
407
|
...locale ? { locale } : {}
|
|
@@ -554,7 +562,7 @@ const uploadMediaSchema = z.object({
|
|
|
554
562
|
caption: z.string().optional().describe("Caption for the media file"),
|
|
555
563
|
alternativeText: z.string().optional().describe("Alternative text for accessibility")
|
|
556
564
|
});
|
|
557
|
-
const uploadMediaDescription = "Upload a media file from a URL to the Strapi media library. Returns the uploaded file data. To link media to a content type field, use
|
|
565
|
+
const uploadMediaDescription = "Upload a media file from a URL to the Strapi media library. Returns the uploaded file data. To link media to a content type field, use createContent or updateContent with the file ID.";
|
|
558
566
|
async function uploadMedia(strapi, params) {
|
|
559
567
|
const { url, name, caption, alternativeText } = params;
|
|
560
568
|
let parsedUrl;
|
|
@@ -622,7 +630,7 @@ async function uploadMedia(strapi, params) {
|
|
|
622
630
|
return {
|
|
623
631
|
file: uploadedFile,
|
|
624
632
|
message: `File "${uploadedFile.name}" uploaded successfully (ID: ${uploadedFile.id}).`,
|
|
625
|
-
usage: `To link this file to a content type field, use
|
|
633
|
+
usage: `To link this file to a content type field, use createContent or updateContent with: { "fieldName": ${uploadedFile.id} }`
|
|
626
634
|
};
|
|
627
635
|
}
|
|
628
636
|
const CONTENT_TYPE$5 = "plugin::ai-sdk.public-memory";
|
|
@@ -1123,13 +1131,24 @@ async function sanitizeInput(strapi, uid, data, auth) {
|
|
|
1123
1131
|
throw error;
|
|
1124
1132
|
}
|
|
1125
1133
|
}
|
|
1126
|
-
const
|
|
1127
|
-
name: "
|
|
1128
|
-
description:
|
|
1129
|
-
schema:
|
|
1134
|
+
const createContentTool = {
|
|
1135
|
+
name: "createContent",
|
|
1136
|
+
description: createContentDescription,
|
|
1137
|
+
schema: createContentSchema,
|
|
1138
|
+
execute: async (args, strapi) => {
|
|
1139
|
+
const sanitizedData = await sanitizeInput(strapi, args.contentType, args.data);
|
|
1140
|
+
const result = await createContent(strapi, { ...args, data: sanitizedData });
|
|
1141
|
+
const sanitizedDoc = await sanitizeOutput(strapi, args.contentType, result.document);
|
|
1142
|
+
return { ...result, document: sanitizedDoc };
|
|
1143
|
+
}
|
|
1144
|
+
};
|
|
1145
|
+
const updateContentTool = {
|
|
1146
|
+
name: "updateContent",
|
|
1147
|
+
description: updateContentDescription,
|
|
1148
|
+
schema: updateContentSchema,
|
|
1130
1149
|
execute: async (args, strapi) => {
|
|
1131
1150
|
const sanitizedData = await sanitizeInput(strapi, args.contentType, args.data);
|
|
1132
|
-
const result = await
|
|
1151
|
+
const result = await updateContent(strapi, { ...args, data: sanitizedData });
|
|
1133
1152
|
const sanitizedDoc = await sanitizeOutput(strapi, args.contentType, result.document);
|
|
1134
1153
|
return { ...result, document: sanitizedDoc };
|
|
1135
1154
|
}
|
|
@@ -1197,7 +1216,8 @@ const manageTaskTool = {
|
|
|
1197
1216
|
const builtInTools = [
|
|
1198
1217
|
listContentTypesTool,
|
|
1199
1218
|
searchContentTool,
|
|
1200
|
-
|
|
1219
|
+
createContentTool,
|
|
1220
|
+
updateContentTool,
|
|
1201
1221
|
findOneContentTool,
|
|
1202
1222
|
uploadMediaTool,
|
|
1203
1223
|
sendEmailTool,
|
|
@@ -1211,6 +1231,14 @@ const PLUGIN_ID$2 = "ai-sdk";
|
|
|
1211
1231
|
const bootstrap = ({ strapi }) => {
|
|
1212
1232
|
const plugin = strapi.plugin(PLUGIN_ID$2);
|
|
1213
1233
|
const config2 = strapi.config.get(`plugin::${PLUGIN_ID$2}`);
|
|
1234
|
+
initializeProvider(strapi, plugin, config2);
|
|
1235
|
+
const registry = initializeToolRegistry(plugin);
|
|
1236
|
+
discoverPluginTools(strapi, registry);
|
|
1237
|
+
plugin.createMcpServer = () => createMcpServer(strapi);
|
|
1238
|
+
plugin.mcpSessions = /* @__PURE__ */ new Map();
|
|
1239
|
+
strapi.log.info(`[${PLUGIN_ID$2}] MCP endpoint available at: /api/${PLUGIN_ID$2}/mcp`);
|
|
1240
|
+
};
|
|
1241
|
+
function initializeProvider(strapi, plugin, config2) {
|
|
1214
1242
|
AIProvider.registerProvider("anthropic", ({ apiKey, baseURL }) => {
|
|
1215
1243
|
const provider = createAnthropic({ apiKey, baseURL });
|
|
1216
1244
|
return (modelId) => provider(modelId);
|
|
@@ -1223,27 +1251,22 @@ const bootstrap = ({ strapi }) => {
|
|
|
1223
1251
|
} else {
|
|
1224
1252
|
strapi.log.warn(`[${PLUGIN_ID$2}] anthropicApiKey not configured, AI provider will not be available`);
|
|
1225
1253
|
}
|
|
1254
|
+
}
|
|
1255
|
+
function initializeToolRegistry(plugin) {
|
|
1226
1256
|
const toolRegistry = new ToolRegistry();
|
|
1227
1257
|
for (const tool2 of builtInTools) {
|
|
1228
1258
|
toolRegistry.register(tool2);
|
|
1229
1259
|
}
|
|
1230
1260
|
plugin.toolRegistry = toolRegistry;
|
|
1261
|
+
return toolRegistry;
|
|
1262
|
+
}
|
|
1263
|
+
function discoverPluginTools(strapi, registry) {
|
|
1231
1264
|
const pluginNames = Object.keys(strapi.plugins).filter((n) => n !== PLUGIN_ID$2);
|
|
1232
1265
|
strapi.log.info(`[${PLUGIN_ID$2}] Scanning ${pluginNames.length} plugins for ai-tools: [${pluginNames.join(", ")}]`);
|
|
1233
1266
|
for (const [pluginName, pluginInstance] of Object.entries(strapi.plugins)) {
|
|
1234
1267
|
if (pluginName === PLUGIN_ID$2) continue;
|
|
1235
1268
|
try {
|
|
1236
|
-
|
|
1237
|
-
try {
|
|
1238
|
-
aiToolsService = strapi.plugin(pluginName)?.service?.("ai-tools");
|
|
1239
|
-
} catch {
|
|
1240
|
-
}
|
|
1241
|
-
if (!aiToolsService) {
|
|
1242
|
-
try {
|
|
1243
|
-
aiToolsService = pluginInstance.service?.("ai-tools");
|
|
1244
|
-
} catch {
|
|
1245
|
-
}
|
|
1246
|
-
}
|
|
1269
|
+
const aiToolsService = resolveAiToolsService(strapi, pluginName, pluginInstance);
|
|
1247
1270
|
if (!aiToolsService?.getTools) {
|
|
1248
1271
|
strapi.log.debug(`[${PLUGIN_ID$2}] No ai-tools service on plugin: ${pluginName}`);
|
|
1249
1272
|
continue;
|
|
@@ -1251,21 +1274,7 @@ const bootstrap = ({ strapi }) => {
|
|
|
1251
1274
|
strapi.log.info(`[${PLUGIN_ID$2}] Found ai-tools service on plugin: ${pluginName}`);
|
|
1252
1275
|
const contributed = aiToolsService.getTools();
|
|
1253
1276
|
if (!Array.isArray(contributed)) continue;
|
|
1254
|
-
|
|
1255
|
-
for (const tool2 of contributed) {
|
|
1256
|
-
if (!tool2.name || !tool2.execute || !tool2.schema) {
|
|
1257
|
-
strapi.log.warn(`[${PLUGIN_ID$2}] Invalid tool from ${pluginName}: ${tool2.name || "unnamed"}`);
|
|
1258
|
-
continue;
|
|
1259
|
-
}
|
|
1260
|
-
const safeName = pluginName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1261
|
-
const namespacedName = `${safeName}__${tool2.name}`;
|
|
1262
|
-
if (toolRegistry.has(namespacedName)) {
|
|
1263
|
-
strapi.log.warn(`[${PLUGIN_ID$2}] Duplicate tool: ${namespacedName}`);
|
|
1264
|
-
continue;
|
|
1265
|
-
}
|
|
1266
|
-
toolRegistry.register({ ...tool2, name: namespacedName });
|
|
1267
|
-
count++;
|
|
1268
|
-
}
|
|
1277
|
+
const count = registerContributedTools(strapi, registry, pluginName, contributed);
|
|
1269
1278
|
if (count > 0) {
|
|
1270
1279
|
strapi.log.info(`[${PLUGIN_ID$2}] Registered ${count} tools from plugin: ${pluginName}`);
|
|
1271
1280
|
}
|
|
@@ -1273,10 +1282,38 @@ const bootstrap = ({ strapi }) => {
|
|
|
1273
1282
|
strapi.log.warn(`[${PLUGIN_ID$2}] Tool discovery failed for ${pluginName}: ${err}`);
|
|
1274
1283
|
}
|
|
1275
1284
|
}
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1285
|
+
}
|
|
1286
|
+
function registerContributedTools(strapi, registry, pluginName, tools) {
|
|
1287
|
+
const safeName = pluginName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1288
|
+
let count = 0;
|
|
1289
|
+
for (const tool2 of tools) {
|
|
1290
|
+
if (!tool2.name || !tool2.execute || !tool2.schema) {
|
|
1291
|
+
strapi.log.warn(`[${PLUGIN_ID$2}] Invalid tool from ${pluginName}: ${tool2.name || "unnamed"}`);
|
|
1292
|
+
continue;
|
|
1293
|
+
}
|
|
1294
|
+
const namespacedName = `${safeName}__${tool2.name}`;
|
|
1295
|
+
if (registry.has(namespacedName)) {
|
|
1296
|
+
strapi.log.warn(`[${PLUGIN_ID$2}] Duplicate tool: ${namespacedName}`);
|
|
1297
|
+
continue;
|
|
1298
|
+
}
|
|
1299
|
+
registry.register({ ...tool2, name: namespacedName });
|
|
1300
|
+
count++;
|
|
1301
|
+
}
|
|
1302
|
+
return count;
|
|
1303
|
+
}
|
|
1304
|
+
function resolveAiToolsService(strapi, pluginName, pluginInstance) {
|
|
1305
|
+
try {
|
|
1306
|
+
const svc = strapi.plugin(pluginName)?.service?.("ai-tools");
|
|
1307
|
+
if (svc) return svc;
|
|
1308
|
+
} catch {
|
|
1309
|
+
}
|
|
1310
|
+
try {
|
|
1311
|
+
const svc = pluginInstance.service?.("ai-tools");
|
|
1312
|
+
if (svc) return svc;
|
|
1313
|
+
} catch {
|
|
1314
|
+
}
|
|
1315
|
+
return null;
|
|
1316
|
+
}
|
|
1280
1317
|
const PLUGIN_ID$1 = "ai-sdk";
|
|
1281
1318
|
async function closeSession(strapi, sessionId, session) {
|
|
1282
1319
|
try {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Core } from '@strapi/strapi';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export declare const createContentSchema: z.ZodObject<{
|
|
4
|
+
contentType: z.ZodString;
|
|
5
|
+
data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
6
|
+
status: z.ZodOptional<z.ZodEnum<{
|
|
7
|
+
draft: "draft";
|
|
8
|
+
published: "published";
|
|
9
|
+
}>>;
|
|
10
|
+
locale: z.ZodOptional<z.ZodString>;
|
|
11
|
+
}, z.core.$strip>;
|
|
12
|
+
export declare const createContentDescription = "Create a new document in any Strapi content type (articles, blog posts, pages, etc.). Use listContentTypes first to discover available content types and their fields.";
|
|
13
|
+
export interface CreateContentParams {
|
|
14
|
+
contentType: string;
|
|
15
|
+
data: Record<string, unknown>;
|
|
16
|
+
status?: 'draft' | 'published';
|
|
17
|
+
locale?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface CreateContentResult {
|
|
20
|
+
action: 'create';
|
|
21
|
+
document: any;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Core logic for creating content.
|
|
25
|
+
* Shared between AI SDK tool and MCP tool.
|
|
26
|
+
*/
|
|
27
|
+
export declare function createContent(strapi: Core.Strapi, params: CreateContentParams): Promise<CreateContentResult>;
|
|
@@ -2,8 +2,10 @@ export { listContentTypes, listContentTypesSchema, listContentTypesDescription }
|
|
|
2
2
|
export type { ContentTypeSummary, ComponentSummary, RelationSummary, ListContentTypesResult } from './list-content-types';
|
|
3
3
|
export { searchContent, searchContentSchema, searchContentDescription } from './search-content';
|
|
4
4
|
export type { SearchContentParams, SearchContentResult } from './search-content';
|
|
5
|
-
export {
|
|
6
|
-
export type {
|
|
5
|
+
export { createContent, createContentSchema, createContentDescription } from './create-content';
|
|
6
|
+
export type { CreateContentParams, CreateContentResult } from './create-content';
|
|
7
|
+
export { updateContent, updateContentSchema, updateContentDescription } from './update-content';
|
|
8
|
+
export type { UpdateContentParams, UpdateContentResult } from './update-content';
|
|
7
9
|
export { sendEmail, sendEmailSchema, sendEmailDescription } from './send-email';
|
|
8
10
|
export type { SendEmailParams, SendEmailResult } from './send-email';
|
|
9
11
|
export { saveMemory, saveMemorySchema, saveMemoryDescription } from './save-memory';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Core } from '@strapi/strapi';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export declare const updateContentSchema: z.ZodObject<{
|
|
4
|
+
contentType: z.ZodString;
|
|
5
|
+
documentId: z.ZodString;
|
|
6
|
+
data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
7
|
+
status: z.ZodOptional<z.ZodEnum<{
|
|
8
|
+
draft: "draft";
|
|
9
|
+
published: "published";
|
|
10
|
+
}>>;
|
|
11
|
+
locale: z.ZodOptional<z.ZodString>;
|
|
12
|
+
}, z.core.$strip>;
|
|
13
|
+
export declare const updateContentDescription = "Update an existing document in any Strapi content type. Use searchContent to find the document ID first.";
|
|
14
|
+
export interface UpdateContentParams {
|
|
15
|
+
contentType: string;
|
|
16
|
+
documentId: string;
|
|
17
|
+
data: Record<string, unknown>;
|
|
18
|
+
status?: 'draft' | 'published';
|
|
19
|
+
locale?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface UpdateContentResult {
|
|
22
|
+
action: 'update';
|
|
23
|
+
document: any;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Core logic for updating content.
|
|
27
|
+
* Shared between AI SDK tool and MCP tool.
|
|
28
|
+
*/
|
|
29
|
+
export declare function updateContent(strapi: Core.Strapi, params: UpdateContentParams): Promise<UpdateContentResult>;
|
|
@@ -6,7 +6,7 @@ export declare const uploadMediaSchema: z.ZodObject<{
|
|
|
6
6
|
caption: z.ZodOptional<z.ZodString>;
|
|
7
7
|
alternativeText: z.ZodOptional<z.ZodString>;
|
|
8
8
|
}, z.core.$strip>;
|
|
9
|
-
export declare const uploadMediaDescription = "Upload a media file from a URL to the Strapi media library. Returns the uploaded file data. To link media to a content type field, use
|
|
9
|
+
export declare const uploadMediaDescription = "Upload a media file from a URL to the Strapi media library. Returns the uploaded file data. To link media to a content type field, use createContent or updateContent with the file ID.";
|
|
10
10
|
export interface UploadMediaParams {
|
|
11
11
|
url: string;
|
|
12
12
|
name?: string;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { ToolDefinition } from '../../lib/tool-registry';
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const createContentTool: ToolDefinition;
|
package/package.json
CHANGED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { Core } from '@strapi/strapi';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
export declare const writeContentSchema: z.ZodObject<{
|
|
4
|
-
contentType: z.ZodString;
|
|
5
|
-
action: z.ZodEnum<{
|
|
6
|
-
create: "create";
|
|
7
|
-
update: "update";
|
|
8
|
-
}>;
|
|
9
|
-
documentId: z.ZodOptional<z.ZodString>;
|
|
10
|
-
data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
11
|
-
status: z.ZodOptional<z.ZodEnum<{
|
|
12
|
-
draft: "draft";
|
|
13
|
-
published: "published";
|
|
14
|
-
}>>;
|
|
15
|
-
locale: z.ZodOptional<z.ZodString>;
|
|
16
|
-
}, z.core.$strip>;
|
|
17
|
-
export declare const writeContentDescription = "Create or update a document in any Strapi content type. Use listContentTypes first to discover the schema, and searchContent to find existing documents for updates.";
|
|
18
|
-
export interface WriteContentParams {
|
|
19
|
-
contentType: string;
|
|
20
|
-
action: 'create' | 'update';
|
|
21
|
-
documentId?: string;
|
|
22
|
-
data: Record<string, unknown>;
|
|
23
|
-
status?: 'draft' | 'published';
|
|
24
|
-
locale?: string;
|
|
25
|
-
}
|
|
26
|
-
export interface WriteContentResult {
|
|
27
|
-
action: 'create' | 'update';
|
|
28
|
-
document: any;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Core logic for creating/updating content.
|
|
32
|
-
* Shared between AI SDK tool and MCP tool.
|
|
33
|
-
*/
|
|
34
|
-
export declare function writeContent(strapi: Core.Strapi, params: WriteContentParams): Promise<WriteContentResult>;
|