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.
@@ -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-BMrDQVQl.mjs";
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 === "writeContent") {
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: `${input.action === "create" ? "Created" : "Updated"}: ${title}`,
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-Cw2aiQ8K.js");
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 === "writeContent") {
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: `${input.action === "create" ? "Created" : "Updated"}: ${title}`,
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-CEEsJsKL.js"));
40
+ const { App } = await Promise.resolve().then(() => require("./App-joEmdpxi.js"));
41
41
  return App;
42
42
  }
43
43
  });
@@ -36,7 +36,7 @@ const index = {
36
36
  defaultMessage: PLUGIN_ID
37
37
  },
38
38
  Component: async () => {
39
- const { App } = await import("./App-DCV7o6Hc.mjs");
39
+ const { App } = await import("./App-DMdQymB3.mjs");
40
40
  return App;
41
41
  }
42
42
  });
@@ -1,3 +1,3 @@
1
1
  "use strict";
2
- const index = require("../_chunks/index-Cw2aiQ8K.js");
2
+ const index = require("../_chunks/index-B7qLITWV.js");
3
3
  module.exports = index.index;
@@ -1,4 +1,4 @@
1
- import { i } from "../_chunks/index-BMrDQVQl.mjs";
1
+ import { i } from "../_chunks/index-DrLcqX__.mjs";
2
2
  export {
3
3
  i as default
4
4
  };
@@ -387,33 +387,41 @@ async function searchContent(strapi, params) {
387
387
  }
388
388
  };
389
389
  }
390
- const writeContentSchema = zod.z.object({
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 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.";
399
- async function writeContent(strapi, params) {
400
- const { contentType, action, documentId, data, status, locale } = params;
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
- if (action === "create") {
409
- const document2 = await docs.create({
410
- data,
411
- ...status ? { status } : {},
412
- ...locale ? { locale } : {},
413
- populate: "*"
414
- });
415
- return { action: "create", document: document2 };
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 writeContent with the file ID.";
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 writeContent with: { "fieldName": ${uploadedFile.id} }`
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 writeContentTool = {
1147
- name: "writeContent",
1148
- description: writeContentDescription,
1149
- schema: writeContentSchema,
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 writeContent(strapi, { ...args, data: sanitizedData });
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
- writeContentTool,
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
- let aiToolsService = null;
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
- let count = 0;
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
- plugin.createMcpServer = () => createMcpServer(strapi);
1297
- plugin.mcpSessions = /* @__PURE__ */ new Map();
1298
- strapi.log.info(`[${PLUGIN_ID$2}] MCP endpoint available at: /api/${PLUGIN_ID$2}/mcp`);
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 {
@@ -367,33 +367,41 @@ async function searchContent(strapi, params) {
367
367
  }
368
368
  };
369
369
  }
370
- const writeContentSchema = z.object({
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 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.";
379
- async function writeContent(strapi, params) {
380
- const { contentType, action, documentId, data, status, locale } = params;
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
- if (action === "create") {
389
- const document2 = await docs.create({
390
- data,
391
- ...status ? { status } : {},
392
- ...locale ? { locale } : {},
393
- populate: "*"
394
- });
395
- return { action: "create", document: document2 };
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 writeContent with the file ID.";
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 writeContent with: { "fieldName": ${uploadedFile.id} }`
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 writeContentTool = {
1127
- name: "writeContent",
1128
- description: writeContentDescription,
1129
- schema: writeContentSchema,
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 writeContent(strapi, { ...args, data: sanitizedData });
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
- writeContentTool,
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
- let aiToolsService = null;
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
- let count = 0;
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
- plugin.createMcpServer = () => createMcpServer(strapi);
1277
- plugin.mcpSessions = /* @__PURE__ */ new Map();
1278
- strapi.log.info(`[${PLUGIN_ID$2}] MCP endpoint available at: /api/${PLUGIN_ID$2}/mcp`);
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 { writeContent, writeContentSchema, writeContentDescription } from './write-content';
6
- export type { WriteContentParams, WriteContentResult } from './write-content';
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 writeContent with the file ID.";
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 writeContentTool: ToolDefinition;
2
+ export declare const createContentTool: ToolDefinition;
@@ -0,0 +1,2 @@
1
+ import type { ToolDefinition } from '../../lib/tool-registry';
2
+ export declare const updateContentTool: ToolDefinition;
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.7.4",
2
+ "version": "0.7.7",
3
3
  "keywords": [
4
4
  "strapi",
5
5
  "strapi-plugin",
@@ -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>;