@webiny/mcp 6.1.0-beta.1 → 6.1.0-beta.2
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/agents/claude.d.ts +2 -0
- package/agents/claude.js +6 -0
- package/agents/claude.js.map +1 -1
- package/agents/cline.d.ts +2 -0
- package/agents/cline.js +6 -0
- package/agents/cline.js.map +1 -1
- package/agents/copilot.d.ts +2 -0
- package/agents/copilot.js +7 -0
- package/agents/copilot.js.map +1 -1
- package/agents/cursor.d.ts +2 -0
- package/agents/cursor.js +6 -0
- package/agents/cursor.js.map +1 -1
- package/agents/discover.d.ts +3 -0
- package/agents/discover.js +29 -0
- package/agents/discover.js.map +1 -0
- package/agents/instructions.d.ts +3 -1
- package/agents/instructions.js +15 -2
- package/agents/instructions.js.map +1 -1
- package/agents/kiro.d.ts +2 -0
- package/agents/kiro.js +6 -0
- package/agents/kiro.js.map +1 -1
- package/agents/opencode.d.ts +2 -0
- package/agents/opencode.js +7 -0
- package/agents/opencode.js.map +1 -1
- package/agents/types.d.ts +16 -0
- package/agents/types.js +3 -0
- package/agents/types.js.map +1 -0
- package/agents/windsurf.d.ts +2 -0
- package/agents/windsurf.js +6 -0
- package/agents/windsurf.js.map +1 -1
- package/cli/ConfigureMcp.js +21 -6
- package/cli/ConfigureMcp.js.map +1 -1
- package/package.json +3 -3
- package/skills/admin/admin-architect/SKILL.md +188 -0
- package/skills/admin/admin-permissions/SKILL.md +159 -0
- package/skills/api/api-architect/SKILL.md +548 -60
- package/skills/api/event-handler-pattern/SKILL.md +191 -23
- package/skills/api/graphql-api/SKILL.md +227 -31
- package/skills/api/permissions/SKILL.md +291 -0
- package/skills/api/use-case-pattern/SKILL.md +347 -12
- package/skills/api/v5-to-v6-migration/SKILL.md +416 -0
- package/skills/generated/admin/SKILL.md +1 -1
- package/skills/generated/admin/aco/SKILL.md +1 -1
- package/skills/generated/admin/build-params/SKILL.md +1 -1
- package/skills/generated/admin/cms/SKILL.md +1 -1
- package/skills/generated/admin/configs/SKILL.md +1 -1
- package/skills/generated/admin/env-config/SKILL.md +1 -1
- package/skills/generated/admin/form/SKILL.md +1 -1
- package/skills/generated/admin/graphql-client/SKILL.md +1 -1
- package/skills/generated/admin/lexical/SKILL.md +1 -1
- package/skills/generated/admin/local-storage/SKILL.md +1 -1
- package/skills/generated/admin/router/SKILL.md +1 -1
- package/skills/generated/admin/security/SKILL.md +1 -1
- package/skills/generated/admin/tenancy/SKILL.md +1 -1
- package/skills/generated/admin/ui/SKILL.md +1 -1
- package/skills/generated/admin/website-builder/SKILL.md +1 -1
- package/skills/generated/api/SKILL.md +1 -1
- package/skills/generated/api/aco/SKILL.md +1 -1
- package/skills/generated/api/build-params/SKILL.md +1 -1
- package/skills/generated/api/cms/SKILL.md +1 -1
- package/skills/generated/api/event-publisher/SKILL.md +1 -1
- package/skills/generated/api/file-manager/SKILL.md +1 -1
- package/skills/generated/api/graphql/SKILL.md +1 -1
- package/skills/generated/api/key-value-store/SKILL.md +1 -1
- package/skills/generated/api/logger/SKILL.md +1 -1
- package/skills/generated/api/opensearch/SKILL.md +1 -1
- package/skills/generated/api/scheduler/SKILL.md +1 -1
- package/skills/generated/api/security/SKILL.md +1 -1
- package/skills/generated/api/system/SKILL.md +1 -1
- package/skills/generated/api/tasks/SKILL.md +1 -1
- package/skills/generated/api/tenancy/SKILL.md +1 -1
- package/skills/generated/api/tenant-manager/SKILL.md +1 -1
- package/skills/generated/api/website-builder/SKILL.md +1 -1
- package/skills/generated/cli/SKILL.md +1 -1
- package/skills/generated/cli/command/SKILL.md +1 -1
- package/skills/generated/extensions/SKILL.md +1 -1
- package/skills/generated/infra/SKILL.md +1 -1
|
@@ -308,6 +308,193 @@ export const DashboardFeature = createFeature({
|
|
|
308
308
|
|
|
309
309
|
> **Prefer the typical pattern.** Only use the one-off pattern when the business logic is truly presentation-specific and will never be reused by other features.
|
|
310
310
|
|
|
311
|
+
## Observable Service Implementation
|
|
312
|
+
|
|
313
|
+
For long-lived services that hold observable state (e.g., project config, feature flags):
|
|
314
|
+
|
|
315
|
+
```ts
|
|
316
|
+
import { makeAutoObservable, runInAction } from "mobx";
|
|
317
|
+
import { WcpService as ServiceAbstraction, WcpGateway } from "./abstractions.js";
|
|
318
|
+
|
|
319
|
+
class WcpServiceImpl implements ServiceAbstraction.Interface {
|
|
320
|
+
private project: ILicense | null = null;
|
|
321
|
+
|
|
322
|
+
constructor(private gateway: WcpGateway.Interface) {
|
|
323
|
+
makeAutoObservable(this);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
getProject(): ILicense {
|
|
327
|
+
return this.project;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
async loadProject(): Promise<void> {
|
|
331
|
+
const data = await this.gateway.fetchProject();
|
|
332
|
+
runInAction(() => {
|
|
333
|
+
this.project = data;
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export const WcpService = ServiceAbstraction.createImplementation({
|
|
339
|
+
implementation: WcpServiceImpl,
|
|
340
|
+
dependencies: [WcpGateway]
|
|
341
|
+
});
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
- Registered in **singleton scope** — long-lived, holds state
|
|
345
|
+
- Use `makeAutoObservable(this)` in the constructor
|
|
346
|
+
- Wrap async state mutations in `runInAction`
|
|
347
|
+
|
|
348
|
+
## Repository Implementation
|
|
349
|
+
|
|
350
|
+
Repositories own domain data and cache. They use MobX for reactivity:
|
|
351
|
+
|
|
352
|
+
```ts
|
|
353
|
+
import { makeAutoObservable, runInAction } from "mobx";
|
|
354
|
+
import {
|
|
355
|
+
NextjsConfigRepository as RepositoryAbstraction,
|
|
356
|
+
NextjsConfigGateway,
|
|
357
|
+
NextjsConfig
|
|
358
|
+
} from "./abstractions.js";
|
|
359
|
+
|
|
360
|
+
class NextjsConfigRepositoryImpl implements RepositoryAbstraction.Interface {
|
|
361
|
+
private config: NextjsConfig | undefined = undefined;
|
|
362
|
+
|
|
363
|
+
constructor(private gateway: NextjsConfigGateway.Interface) {
|
|
364
|
+
makeAutoObservable(this);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
getConfig(): NextjsConfig | undefined {
|
|
368
|
+
return this.config;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async loadConfig(): Promise<void> {
|
|
372
|
+
if (this.config) {
|
|
373
|
+
return; // Already loaded — cache hit
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const config = await this.gateway.getConfig();
|
|
377
|
+
runInAction(() => {
|
|
378
|
+
this.config = config;
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
export const NextjsConfigRepository = RepositoryAbstraction.createImplementation({
|
|
384
|
+
implementation: NextjsConfigRepositoryImpl,
|
|
385
|
+
dependencies: [NextjsConfigGateway]
|
|
386
|
+
});
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
## Gateway Implementation (GraphQL)
|
|
390
|
+
|
|
391
|
+
Gateways handle external I/O. Use `GraphQLClient` for GraphQL calls:
|
|
392
|
+
|
|
393
|
+
```ts
|
|
394
|
+
import { NextjsConfigGateway as GatewayAbstraction } from "./abstractions.js";
|
|
395
|
+
import { GraphQLClient } from "@webiny/app/features/graphqlClient";
|
|
396
|
+
|
|
397
|
+
const GET_NEXTJS_CONFIG = /* GraphQL */ `
|
|
398
|
+
query GetNextjsConfig {
|
|
399
|
+
websiteBuilder {
|
|
400
|
+
getNextjsConfig {
|
|
401
|
+
data
|
|
402
|
+
error { code message data }
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
`;
|
|
407
|
+
|
|
408
|
+
type GetNextjsConfigResponse = {
|
|
409
|
+
websiteBuilder: {
|
|
410
|
+
getNextjsConfig:
|
|
411
|
+
| { data: string; error: null }
|
|
412
|
+
| { data: null; error: { code: string; message: string; data: any } };
|
|
413
|
+
};
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
class NextjsGraphQLGateway implements GatewayAbstraction.Interface {
|
|
417
|
+
constructor(private client: GraphQLClient.Interface) {}
|
|
418
|
+
|
|
419
|
+
async getConfig(): Promise<string> {
|
|
420
|
+
const response = await this.client.execute<GetNextjsConfigResponse>({
|
|
421
|
+
query: GET_NEXTJS_CONFIG
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
const envelope = response.websiteBuilder.getNextjsConfig;
|
|
425
|
+
if (envelope.error) {
|
|
426
|
+
throw new Error(envelope.error.message);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return envelope.data;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export const NextjsConfigGateway = GatewayAbstraction.createImplementation({
|
|
434
|
+
implementation: NextjsGraphQLGateway,
|
|
435
|
+
dependencies: [GraphQLClient]
|
|
436
|
+
});
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
**Key points:**
|
|
440
|
+
- Define the GraphQL query as a string constant with `/* GraphQL */` comment for syntax highlighting
|
|
441
|
+
- Type the response shape explicitly
|
|
442
|
+
- Handle the `data`/`error` envelope pattern
|
|
443
|
+
- Inject `GraphQLClient` from `@webiny/app/features/graphqlClient`
|
|
444
|
+
|
|
445
|
+
## Composite Features (Aggregating Child Features)
|
|
446
|
+
|
|
447
|
+
When grouping related features, create a composite with no `resolve`:
|
|
448
|
+
|
|
449
|
+
```ts
|
|
450
|
+
import { createFeature } from "webiny/admin";
|
|
451
|
+
|
|
452
|
+
export const FoldersFeature = createFeature({
|
|
453
|
+
name: "Folders",
|
|
454
|
+
register(container) {
|
|
455
|
+
CreateFolderFeature.register(container);
|
|
456
|
+
UpdateFolderFeature.register(container);
|
|
457
|
+
DeleteFolderFeature.register(container);
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
## Extending Features (Decorators)
|
|
463
|
+
|
|
464
|
+
Decorators add cross-cutting concerns without modifying the core implementation:
|
|
465
|
+
|
|
466
|
+
```ts
|
|
467
|
+
class ListFoldersUseCaseWithLoading implements UseCaseAbstraction.Interface {
|
|
468
|
+
constructor(
|
|
469
|
+
private loadingRepository: FoldersLoadingRepository.Interface,
|
|
470
|
+
private decoratee: UseCaseAbstraction.Interface // decoratee is LAST
|
|
471
|
+
) {}
|
|
472
|
+
|
|
473
|
+
async execute() {
|
|
474
|
+
await this.loadingRepository.runCallBack(
|
|
475
|
+
this.decoratee.execute(),
|
|
476
|
+
LoadingActionsEnum.list
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
Register with `container.registerDecorator()`:
|
|
483
|
+
|
|
484
|
+
```ts
|
|
485
|
+
export const MyExtensionFeature = createFeature({
|
|
486
|
+
name: "MyExtension",
|
|
487
|
+
register(container) {
|
|
488
|
+
container.registerDecorator(MyPresenterDecorator);
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
**Rules:**
|
|
494
|
+
- Implements the same interface as the decorated abstraction
|
|
495
|
+
- Constructor: extra dependencies first, `decoratee` **last**
|
|
496
|
+
- The `dependencies` array does NOT include the decoratee
|
|
497
|
+
|
|
311
498
|
## Reading Admin BuildParams
|
|
312
499
|
|
|
313
500
|
There are two ways to read build parameters on the Admin side:
|
|
@@ -384,6 +571,7 @@ Creates a feature definition for the admin runtime.
|
|
|
384
571
|
|
|
385
572
|
## Related Skills
|
|
386
573
|
|
|
574
|
+
- **webiny-admin-permissions** — Admin permission UI registration (Security.Permissions schema, custom UIs)
|
|
387
575
|
- **webiny-full-stack-architect** — Top-level component, shared domain layer, BuildParam declarations
|
|
388
576
|
- **webiny-dependency-injection** — The `createImplementation` DI pattern and injectable services
|
|
389
577
|
- **webiny-admin-ui-extensions** — Admin UI customization, decorators, theming, forms, and config
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: webiny-admin-permissions
|
|
3
|
+
context: webiny-extensions
|
|
4
|
+
description: >
|
|
5
|
+
Admin-side permission UI registration using Security.Permissions. Use this skill when
|
|
6
|
+
adding permission controls to the admin UI — schema-based auto-generated forms, custom
|
|
7
|
+
permission UIs with usePermissionValue/usePermissionForm, entity dependencies, and
|
|
8
|
+
the Security.Permissions component props. Covers both simple apps and complex
|
|
9
|
+
multi-entity permission schemas.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Admin Permissions UI
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
Register permissions via `AdminConfig` + `Security.Permissions`. The framework auto-generates the UI from a schema and handles serialization. No form code needed for most apps.
|
|
17
|
+
|
|
18
|
+
## Schema-Based (Auto-Generated UI)
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import React from "react";
|
|
22
|
+
import { AdminConfig } from "@webiny/app-admin";
|
|
23
|
+
import { ReactComponent as Icon } from "@webiny/icons/shield.svg";
|
|
24
|
+
|
|
25
|
+
const { Security } = AdminConfig;
|
|
26
|
+
|
|
27
|
+
export const MyPermission = () => {
|
|
28
|
+
return (
|
|
29
|
+
<AdminConfig>
|
|
30
|
+
<Security.Permissions
|
|
31
|
+
name="store-manager"
|
|
32
|
+
title="Store Manager"
|
|
33
|
+
description="Manage Store Manager permissions."
|
|
34
|
+
icon={<Icon />}
|
|
35
|
+
schema={{
|
|
36
|
+
prefix: "sm",
|
|
37
|
+
fullAccess: { name: "sm.*" },
|
|
38
|
+
entities: [
|
|
39
|
+
{
|
|
40
|
+
id: "product",
|
|
41
|
+
title: "Products",
|
|
42
|
+
permission: "sm.product",
|
|
43
|
+
scopes: ["full", "own"],
|
|
44
|
+
actions: [
|
|
45
|
+
{ name: "rwd" },
|
|
46
|
+
{ name: "pw" },
|
|
47
|
+
{ name: "import", label: "Import products" },
|
|
48
|
+
{ name: "export", label: "Export products" }
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: "category",
|
|
53
|
+
title: "Categories",
|
|
54
|
+
permission: "sm.category",
|
|
55
|
+
scopes: ["full"],
|
|
56
|
+
actions: [{ name: "rwd" }]
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: "settings",
|
|
60
|
+
title: "Settings",
|
|
61
|
+
permission: "sm.settings",
|
|
62
|
+
scopes: ["full"]
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
}}
|
|
66
|
+
/>
|
|
67
|
+
</AdminConfig>
|
|
68
|
+
);
|
|
69
|
+
};
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Render `<MyPermission />` anywhere in your app's extension component.
|
|
73
|
+
|
|
74
|
+
## Schema Reference
|
|
75
|
+
|
|
76
|
+
| Field | Type | Required | Description |
|
|
77
|
+
| ------------ | -------------------- | -------- | -------------------------------------------------------------- |
|
|
78
|
+
| `prefix` | `string` | Yes | Permission prefix (e.g., `"sm"`) |
|
|
79
|
+
| `fullAccess` | `{ name: string }` | Yes | Permission emitted on "Full access" (e.g., `{ name: "sm.*" }`) |
|
|
80
|
+
| `entities` | `EntityDefinition[]` | No | Entity definitions. Omit for binary full/no access. |
|
|
81
|
+
|
|
82
|
+
### Entity Definition
|
|
83
|
+
|
|
84
|
+
| Field | Type | Required | Description |
|
|
85
|
+
| ------------ | -------------------------------------- | -------- | ---------------------------------------------- |
|
|
86
|
+
| `id` | `string` | Yes | Unique identifier for form field naming |
|
|
87
|
+
| `title` | `string` | No | Display title. Falls back to `id`. |
|
|
88
|
+
| `permission` | `string` | Yes | Permission name emitted (e.g., `"sm.product"`) |
|
|
89
|
+
| `scopes` | `("full" \| "own")[]` | Yes | Available access scopes |
|
|
90
|
+
| `actions` | `ActionDefinition[]` | No | Actions on this entity |
|
|
91
|
+
| `dependsOn` | `{ entity: string; requires: string }` | No | Dependency on another entity |
|
|
92
|
+
|
|
93
|
+
### Actions
|
|
94
|
+
|
|
95
|
+
- `{ name: "rwd" }` — Read/Write/Delete select dropdown. Auto-set to `"rwd"` when scope is `"own"`.
|
|
96
|
+
- `{ name: "pw" }` — Publish/Unpublish checkbox group.
|
|
97
|
+
- `{ name: "custom", label: "Label" }` — Custom boolean flag.
|
|
98
|
+
|
|
99
|
+
### Entity Dependencies
|
|
100
|
+
|
|
101
|
+
Child entities can depend on a parent. If the parent lacks the required action, the child is pruned from output. `"own"` scope cascades to dependents.
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
{
|
|
105
|
+
id: "review",
|
|
106
|
+
permission: "sm.review",
|
|
107
|
+
scopes: ["full", "own"],
|
|
108
|
+
actions: [{ name: "rwd" }],
|
|
109
|
+
dependsOn: { entity: "product", requires: "r" }
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Simple Apps (No Entities)
|
|
116
|
+
|
|
117
|
+
Omit `entities` for binary full/no access:
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
<Security.Permissions
|
|
121
|
+
name="my-app"
|
|
122
|
+
title="My App"
|
|
123
|
+
description="Manage My App access permissions."
|
|
124
|
+
schema={{ prefix: "ma", fullAccess: { name: "ma.*" } }}
|
|
125
|
+
/>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## `Security.Permissions` Props
|
|
131
|
+
|
|
132
|
+
| Prop | Type | Required | Description |
|
|
133
|
+
| ------------- | ------------------ | ------------------------- | ----------------------------------------------- |
|
|
134
|
+
| `name` | `string` | Yes | Unique identifier for this permission renderer |
|
|
135
|
+
| `title` | `string` | Yes | Display title in the accordion header |
|
|
136
|
+
| `description` | `string` | No | Description shown below the title |
|
|
137
|
+
| `icon` | `ReactElement` | No | Icon in the accordion header |
|
|
138
|
+
| `schema` | `PermissionSchema` | One of `schema`/`element` | Auto-generate UI from schema |
|
|
139
|
+
| `element` | `ReactElement` | One of `schema`/`element` | Fully custom permission UI |
|
|
140
|
+
| `system` | `boolean` | No | If `true`, renders before app-level permissions |
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Matching API-Side Permissions
|
|
145
|
+
|
|
146
|
+
The admin schema and the API-side `createPermissions` schema should use the **same prefix, entity IDs, and action names**. This ensures the permissions emitted by the admin UI are correctly evaluated by the API.
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
Admin: schema={{ prefix: "sm", entities: [{ id: "product", permission: "sm.product", ... }] }}
|
|
150
|
+
API: createPermissions({ prefix: "sm", entities: [{ id: "product", permission: "sm.product", ... }] })
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
See **webiny-api-permissions** for the API-side implementation.
|
|
154
|
+
|
|
155
|
+
## Related Skills
|
|
156
|
+
|
|
157
|
+
- **webiny-api-permissions** — API-side permission checking (canRead, canEdit, etc.)
|
|
158
|
+
- **webiny-admin-architect** — Admin architecture, headless and presentation features
|
|
159
|
+
- **webiny-admin-ui-extensions** — Admin UI customization, decorators, config
|