strapi-plugin-mcp-chat 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -5
- package/admin/src/components/ErrorBoundary.tsx +29 -0
- package/admin/src/components/FloatingChat.tsx +8 -0
- package/admin/src/index.tsx +39 -5
- package/dist/server/index.js +298 -184
- package/package.json +1 -1
- package/server/src/content-tools.ts +16 -4
- package/server/src/controllers/frontend.ts +7 -2
- package/server/src/index.ts +6 -1
- package/server/src/mcp/define.ts +72 -0
- package/server/src/mcp/index.ts +19 -6
- package/server/src/mcp/tools/buscar-texto.ts +18 -24
- package/server/src/mcp/tools/criar-locale.ts +20 -26
- package/server/src/mcp/tools/editar-campo.ts +29 -35
- package/server/src/mcp/tools/habilitar-i18n.ts +23 -29
- package/server/src/mcp/tools/listar-locales.ts +17 -23
- package/server/src/mcp/tools/publicar.ts +21 -27
- package/server/src/mcp/tools/traduzir.ts +26 -32
- package/server/src/mcp/types.ts +12 -9
- package/server/src/mcp-client.ts +15 -3
- package/server/src/provision/write.ts +92 -0
- package/server/src/services/chat.ts +28 -6
package/README.md
CHANGED
|
@@ -294,11 +294,16 @@ The plugin follows the documented Strapi 5 plugin APIs:
|
|
|
294
294
|
(`register` / `bootstrap` / `destroy` / `config` / `controllers` / `services` / `routes`).
|
|
295
295
|
Routes are declared with `type: 'admin'`, so the chat/STT/TTS endpoints require an
|
|
296
296
|
authenticated admin session.
|
|
297
|
-
- **MCP** — tools are
|
|
298
|
-
(
|
|
299
|
-
`
|
|
300
|
-
|
|
301
|
-
|
|
297
|
+
- **MCP** — tools are **defined** as pure objects with a local typed `defineTool` helper
|
|
298
|
+
(`server/src/mcp/define.ts`) and **registered from an array** via
|
|
299
|
+
`strapi.ai.mcp.registerTool` during `register()`, using `z` from `@strapi/utils` for the
|
|
300
|
+
schemas and `auth.policies` (content-manager read/update/publish) for RBAC. The
|
|
301
|
+
`defineTool`/`defineResource`/`definePrompt` identity functions infer each handler's
|
|
302
|
+
`args` from its input schema (no `any`) and keep the definitions side-effect-free —
|
|
303
|
+
mirroring the direction of Strapi's [PR #26603](https://github.com/strapi/strapi/pull/26603)
|
|
304
|
+
(`ai.mcp.defineTool` + the `import { ai } from "@strapi/strapi"` namespace). When that API
|
|
305
|
+
ships stable, migrating is a one-line import swap; until then the plugin stays on the
|
|
306
|
+
released `registerTool` so it runs on any Strapi ≥ 5.47 (no experimental build required).
|
|
302
307
|
- **Admin** — `register()` uses only documented APIs (`app.addMenuLink`, `app.registerPlugin`).
|
|
303
308
|
|
|
304
309
|
One intentional deviation: the **global floating chat** is mounted via its own React root
|
|
@@ -316,6 +321,27 @@ single spot.
|
|
|
316
321
|
token's permissions — scope the token to only what those clients should change.
|
|
317
322
|
- The agent can edit and publish content — give the plugin only to trusted editors.
|
|
318
323
|
|
|
324
|
+
## Reliability — never degrades or breaks the host Strapi
|
|
325
|
+
|
|
326
|
+
This plugin is built to be a good citizen: installing it must never slow down or take
|
|
327
|
+
down the host app. Concrete guarantees:
|
|
328
|
+
|
|
329
|
+
- **Can't crash boot.** `register()` degrades gracefully if the native MCP server / i18n /
|
|
330
|
+
OpenAI key are absent (logs a warning, disables the feature). MCP tools register inside a
|
|
331
|
+
per-tool `try/catch`, so a single bad tool can never abort the others or the boot.
|
|
332
|
+
`destroy()` is best-effort.
|
|
333
|
+
- **Can't blank the admin.** The global overlay (floating chat + preview) mounts inside a
|
|
334
|
+
React **error boundary** in its own root, after an SSR/double-mount guard; any render
|
|
335
|
+
error just hides the overlay, leaving the Strapi admin fully intact. Lingering preview
|
|
336
|
+
layout styles are reset on load, and screen/mic capture is stopped on unmount.
|
|
337
|
+
- **Provisioning is fail-safe.** Generated schemas are **validated before any file is
|
|
338
|
+
written** (kind/attributes/known types/relation targets) and writes are **all-or-nothing**
|
|
339
|
+
— a malformed manifest can never leave Strapi with a broken, unbootable schema. Generation
|
|
340
|
+
stays additive (never touches existing types), dev-only, with hardened zip-slip protection.
|
|
341
|
+
- **Won't become a bottleneck.** Every outbound call (OpenAI, MCP) has a **timeout** so a
|
|
342
|
+
slow upstream can't hold a request open; recursive content search has depth caps and a
|
|
343
|
+
result cap; tool outputs are size-capped before going back to the model.
|
|
344
|
+
|
|
319
345
|
## License
|
|
320
346
|
|
|
321
347
|
[MIT](./LICENSE) © Raul Balestra
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error boundary que ISOLA as sobreposições do plugin (chat + preview) do resto
|
|
3
|
+
* do admin do Strapi. Se algo dentro renderizar com erro, capturamos aqui e
|
|
4
|
+
* renderizamos `null` — o overlay some, mas o admin do Strapi continua intacto.
|
|
5
|
+
* Sem isto, um erro de render num root React próprio poderia quebrar a página.
|
|
6
|
+
*/
|
|
7
|
+
import { Component, type ReactNode } from 'react';
|
|
8
|
+
|
|
9
|
+
type Props = { children: ReactNode };
|
|
10
|
+
type State = { failed: boolean };
|
|
11
|
+
|
|
12
|
+
export class ErrorBoundary extends Component<Props, State> {
|
|
13
|
+
state: State = { failed: false };
|
|
14
|
+
|
|
15
|
+
static getDerivedStateFromError(): State {
|
|
16
|
+
return { failed: true };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
componentDidCatch(error: unknown) {
|
|
20
|
+
// Apenas loga; nunca propaga para o admin.
|
|
21
|
+
// eslint-disable-next-line no-console
|
|
22
|
+
console.error('[mcp-chat] overlay desativado após erro de render:', error);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
render() {
|
|
26
|
+
if (this.state.failed) return null;
|
|
27
|
+
return this.props.children;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -113,6 +113,14 @@ export const FloatingChat = ({ previewOn, previewUrl, onTogglePreview, onReply }
|
|
|
113
113
|
if (bodyRef.current) bodyRef.current.scrollTop = bodyRef.current.scrollHeight;
|
|
114
114
|
}, [messages, loading]);
|
|
115
115
|
|
|
116
|
+
// ── Cleanup ao desmontar: encerra captura de tela / microfone (sem vazar) ──
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
return () => {
|
|
119
|
+
try { streamRef.current?.getTracks().forEach((t) => t.stop()); } catch { /* noop */ }
|
|
120
|
+
try { recorderRef.current?.stop(); } catch { /* noop */ }
|
|
121
|
+
};
|
|
122
|
+
}, []);
|
|
123
|
+
|
|
116
124
|
// ── Screenshare ───────────────────────────────────────────────────────────
|
|
117
125
|
const startShare = async () => {
|
|
118
126
|
setError(null);
|
package/admin/src/index.tsx
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
import { createRoot } from 'react-dom/client';
|
|
2
2
|
import { PLUGIN_ID } from './pluginId';
|
|
3
3
|
import { AdminOverlays } from './components/AdminOverlays';
|
|
4
|
+
import { ErrorBoundary } from './components/ErrorBoundary';
|
|
5
|
+
|
|
6
|
+
// Flag de módulo: trava extra contra duplo-mount (além do guard por id no DOM).
|
|
7
|
+
let mounted = false;
|
|
8
|
+
|
|
9
|
+
/** Limpa estilos inline que o PreviewPanel possa ter deixado no #strapi caso uma
|
|
10
|
+
* sessão anterior tenha sido encerrada de forma abrupta — garante que o admin
|
|
11
|
+
* nunca apareça encolhido/quebrado ao carregar. */
|
|
12
|
+
const resetStrapiRootStyles = () => {
|
|
13
|
+
try {
|
|
14
|
+
const root = document.getElementById('strapi') as HTMLElement | null;
|
|
15
|
+
if (!root) return;
|
|
16
|
+
for (const p of ['width', 'maxWidth', 'transform', 'overflow'] as const) {
|
|
17
|
+
root.style[p] = '';
|
|
18
|
+
}
|
|
19
|
+
} catch {
|
|
20
|
+
/* noop */
|
|
21
|
+
}
|
|
22
|
+
};
|
|
4
23
|
|
|
5
24
|
const PluginIcon = () => (
|
|
6
25
|
<svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
@@ -39,11 +58,26 @@ export default {
|
|
|
39
58
|
// tocar na árvore do admin). É o único desvio das APIs documentadas e está
|
|
40
59
|
// contido a este único ponto. `register()` acima usa só APIs oficiais
|
|
41
60
|
// (addMenuLink + registerPlugin).
|
|
61
|
+
// Guarda de ambiente: só roda no browser (nunca em SSR/headless sem DOM).
|
|
62
|
+
if (typeof document === 'undefined' || typeof window === 'undefined') return;
|
|
42
63
|
const ID = 'mcp-chat-fab-root';
|
|
43
|
-
if (document.getElementById(ID)) return;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
64
|
+
if (mounted || document.getElementById(ID)) return;
|
|
65
|
+
mounted = true;
|
|
66
|
+
try {
|
|
67
|
+
resetStrapiRootStyles();
|
|
68
|
+
const el = document.createElement('div');
|
|
69
|
+
el.id = ID;
|
|
70
|
+
document.body.appendChild(el);
|
|
71
|
+
// ErrorBoundary garante que um erro de render do overlay nunca derrube o admin.
|
|
72
|
+
createRoot(el).render(
|
|
73
|
+
<ErrorBoundary>
|
|
74
|
+
<AdminOverlays />
|
|
75
|
+
</ErrorBoundary>
|
|
76
|
+
);
|
|
77
|
+
} catch (e) {
|
|
78
|
+
// Em último caso, falhar em montar o overlay não pode quebrar o admin.
|
|
79
|
+
// eslint-disable-next-line no-console
|
|
80
|
+
console.error('[mcp-chat] falha ao montar overlays (admin segue normal):', e);
|
|
81
|
+
}
|
|
48
82
|
},
|
|
49
83
|
};
|