@sparkerp/plugin-sdk 0.1.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.
Files changed (142) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +61 -0
  3. package/bundle/blocks.json +12229 -0
  4. package/bundle/catalog.json +32475 -0
  5. package/bundle/docs/applications/hcm/employee-profile.md +144 -0
  6. package/bundle/docs/concepts/artifact-types.md +80 -0
  7. package/bundle/docs/concepts/glossary.md +124 -0
  8. package/bundle/docs/concepts/overview.md +88 -0
  9. package/bundle/docs/concepts/page-data-flow.md +84 -0
  10. package/bundle/docs/concepts/plugin-lifecycle.md +81 -0
  11. package/bundle/docs/concepts/sdk-modes.md +71 -0
  12. package/bundle/docs/docs.json +109 -0
  13. package/bundle/docs/guides/add-a-cadence-job.md +134 -0
  14. package/bundle/docs/guides/add-a-compliance-job.md +139 -0
  15. package/bundle/docs/guides/add-a-create-edit-form.md +167 -0
  16. package/bundle/docs/guides/add-a-custom-block.md +225 -0
  17. package/bundle/docs/guides/add-a-data-provider.md +216 -0
  18. package/bundle/docs/guides/add-a-kpi.md +154 -0
  19. package/bundle/docs/guides/add-a-reminder-job.md +181 -0
  20. package/bundle/docs/guides/add-an-aggregation-job.md +153 -0
  21. package/bundle/docs/guides/add-an-approval-workflow.md +222 -0
  22. package/bundle/docs/guides/add-an-entity.md +170 -0
  23. package/bundle/docs/guides/add-business-rules.md +147 -0
  24. package/bundle/docs/guides/add-i18n.md +121 -0
  25. package/bundle/docs/guides/add-menus.md +149 -0
  26. package/bundle/docs/guides/build-a-code-plugin.md +141 -0
  27. package/bundle/docs/guides/build-a-page.md +213 -0
  28. package/bundle/docs/guides/build-a-polyglot-service-plugin.md +201 -0
  29. package/bundle/docs/guides/build-a-tenant-extension-service.md +149 -0
  30. package/bundle/docs/guides/create-a-plugin.md +172 -0
  31. package/bundle/docs/guides/expose-a-plugin-operation-as-an-ai-tool.md +114 -0
  32. package/bundle/docs/guides/index.md +53 -0
  33. package/bundle/docs/guides/publish-and-upgrade.md +152 -0
  34. package/bundle/docs/guides/responsive-plugin.md +117 -0
  35. package/bundle/docs/guides/seed-data-on-install.md +127 -0
  36. package/bundle/docs/guides/set-up-the-sdk.md +120 -0
  37. package/bundle/docs/guides/use-the-mcp-server.md +104 -0
  38. package/bundle/docs/guides/validate-and-test.md +119 -0
  39. package/bundle/docs/guides/wire-a-pages-data.md +153 -0
  40. package/bundle/docs/quickstart/index.md +118 -0
  41. package/bundle/docs/recipes/condition-based-compliance-flag.md +70 -0
  42. package/bundle/docs/recipes/cross-plugin-offboarding-fanout.md +68 -0
  43. package/bundle/docs/recipes/cross-row-aggregation.md +67 -0
  44. package/bundle/docs/recipes/entity-with-approval-workflow.md +72 -0
  45. package/bundle/docs/recipes/index.md +40 -0
  46. package/bundle/docs/recipes/install-lifecycle-data-seeding.md +76 -0
  47. package/bundle/docs/recipes/kpi-dashboard-page.md +92 -0
  48. package/bundle/docs/recipes/master-detail-page.md +84 -0
  49. package/bundle/docs/recipes/n-days-before-reminder.md +72 -0
  50. package/bundle/docs/recipes/per-row-document-generator.md +65 -0
  51. package/bundle/docs/recipes/related-record-lookup-field.md +74 -0
  52. package/bundle/docs/recipes/resolved-reference-grid-column.md +63 -0
  53. package/bundle/docs/recipes/scheduled-record-generation.md +68 -0
  54. package/bundle/docs/reference/action.md +18 -0
  55. package/bundle/docs/reference/block-definition.md +35 -0
  56. package/bundle/docs/reference/block-instance.md +33 -0
  57. package/bundle/docs/reference/composite-pack-manifest.md +33 -0
  58. package/bundle/docs/reference/connector-definition.md +36 -0
  59. package/bundle/docs/reference/data-service-definition.md +22 -0
  60. package/bundle/docs/reference/data-view-definition.md +26 -0
  61. package/bundle/docs/reference/entity-aggregation-config.md +38 -0
  62. package/bundle/docs/reference/entity-cadence-config.md +33 -0
  63. package/bundle/docs/reference/entity-compliance-config.md +37 -0
  64. package/bundle/docs/reference/entity-cross-plugin-action-config.md +37 -0
  65. package/bundle/docs/reference/entity-definition.md +18 -0
  66. package/bundle/docs/reference/entity-document-generator-config.md +35 -0
  67. package/bundle/docs/reference/entity-rule-definition.md +29 -0
  68. package/bundle/docs/reference/entity-status-date-sweep-config.md +27 -0
  69. package/bundle/docs/reference/form.md +35 -0
  70. package/bundle/docs/reference/index.md +37 -0
  71. package/bundle/docs/reference/menu.md +26 -0
  72. package/bundle/docs/reference/mobile-nav.md +22 -0
  73. package/bundle/docs/reference/page-template.md +29 -0
  74. package/bundle/docs/reference/page.md +44 -0
  75. package/bundle/docs/reference/plugin-manifest.md +54 -0
  76. package/bundle/docs/reference/plugin-seed-data.md +21 -0
  77. package/bundle/docs/reference/print-template-definition.md +22 -0
  78. package/bundle/docs/reference/provider-definition.md +22 -0
  79. package/bundle/docs/reference/report-definition.md +22 -0
  80. package/bundle/docs/reference/rule.md +30 -0
  81. package/bundle/docs/reference/workflow-definition.md +28 -0
  82. package/bundle/docs/troubleshooting/grid-shows-zero-rows.md +82 -0
  83. package/bundle/docs/troubleshooting/job-never-runs.md +89 -0
  84. package/bundle/docs/troubleshooting/known-gotchas.md +193 -0
  85. package/bundle/docs/troubleshooting/publishing-errors.md +119 -0
  86. package/bundle/docs/tutorial/01-create-the-plugin.md +157 -0
  87. package/bundle/docs/tutorial/02-category-entity-and-seed.md +94 -0
  88. package/bundle/docs/tutorial/03-equipment-entity.md +121 -0
  89. package/bundle/docs/tutorial/04-equipment-register-page.md +153 -0
  90. package/bundle/docs/tutorial/05-checkout-entity-and-page.md +116 -0
  91. package/bundle/docs/tutorial/06-checkout-approval-workflow.md +139 -0
  92. package/bundle/docs/tutorial/07-return-due-reminder-job.md +129 -0
  93. package/bundle/docs/tutorial/08-menus-i18n-publish.md +123 -0
  94. package/bundle/docs/tutorial/index.md +72 -0
  95. package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-aggregation-configs.json +18 -0
  96. package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-cadence-configs.json +9 -0
  97. package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-compliance-configs.json +74 -0
  98. package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-cross-plugin-configs.json +9 -0
  99. package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-document-configs.json +9 -0
  100. package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-sweep-configs.json +260 -0
  101. package/bundle/examples/backend/modules/hcm-compensation/spk-assembly/metadata/entities/hcm_salary_planning_entry.json +41 -0
  102. package/bundle/examples/backend/modules/hcm-compensation/spk-assembly/metadata/page/salary-planning.json +1 -0
  103. package/bundle/examples/backend/modules/hcm-employee/spk-assembly/metadata/page/promotions.json +10944 -0
  104. package/bundle/examples/backend/modules/hcm-employee/spk-assembly/metadata/workflow/employee.movement.json +44 -0
  105. package/bundle/examples/backend/modules/hcm-employee/spk-assembly/metadata/workflow/employee.promotion-acknowledge.json +19 -0
  106. package/bundle/examples/backend/modules/hcm-employee/spk-assembly/metadata/workflow/employee.resignation.json +29 -0
  107. package/bundle/examples/example-patterns.mjs +94 -0
  108. package/bundle/manifest.json +14 -0
  109. package/bundle/schemas/action.schema.json +102 -0
  110. package/bundle/schemas/block-definition.schema.json +222 -0
  111. package/bundle/schemas/block-instance.schema.json +139 -0
  112. package/bundle/schemas/composite-pack-manifest.schema.json +160 -0
  113. package/bundle/schemas/connector-definition.schema.json +29 -0
  114. package/bundle/schemas/data-service-definition.schema.json +64 -0
  115. package/bundle/schemas/data-view-definition.schema.json +148 -0
  116. package/bundle/schemas/entity-aggregation-config.schema.json +32 -0
  117. package/bundle/schemas/entity-cadence-config.schema.json +27 -0
  118. package/bundle/schemas/entity-compliance-config.schema.json +31 -0
  119. package/bundle/schemas/entity-cross-plugin-action-config.schema.json +31 -0
  120. package/bundle/schemas/entity-definition.schema.json +126 -0
  121. package/bundle/schemas/entity-document-generator-config.schema.json +29 -0
  122. package/bundle/schemas/entity-rule-definition.schema.json +75 -0
  123. package/bundle/schemas/entity-status-date-sweep-config.schema.json +21 -0
  124. package/bundle/schemas/form.schema.json +281 -0
  125. package/bundle/schemas/menu.schema.json +106 -0
  126. package/bundle/schemas/mobile-nav.schema.json +53 -0
  127. package/bundle/schemas/page-template.schema.json +46 -0
  128. package/bundle/schemas/page.schema.json +292 -0
  129. package/bundle/schemas/plugin-manifest.schema.json +139 -0
  130. package/bundle/schemas/plugin-seed-data.schema.json +24 -0
  131. package/bundle/schemas/print-template-definition.schema.json +90 -0
  132. package/bundle/schemas/provider-definition.schema.json +27 -0
  133. package/bundle/schemas/report-definition.schema.json +96 -0
  134. package/bundle/schemas/rule.schema.json +82 -0
  135. package/bundle/schemas/workflow-definition.schema.json +42 -0
  136. package/bundle/validators/block-engine.mjs +23222 -0
  137. package/bundle/validators/page-engine.mjs +21645 -0
  138. package/erp-cli/authoring-root.mjs +344 -0
  139. package/erp-cli/erp.mjs +4065 -0
  140. package/erp-cli/example-patterns.mjs +94 -0
  141. package/erp-mcp-server/index.mjs +818 -0
  142. package/package.json +27 -0
@@ -0,0 +1,117 @@
1
+ ---
2
+ title: Make a plugin work on desktop, tablet, and mobile
3
+ audience: tenant
4
+ ---
5
+
6
+ # Make a plugin work on desktop, tablet, and mobile
7
+
8
+ ## What you're doing
9
+
10
+ The platform is **mobile-first**: a page you author with no responsive settings
11
+ renders as a single stacked column and already works on a phone. This guide is
12
+ about the deliberate adjustments — hiding a column on small screens, a
13
+ desktop-only side panel, a device-restricted menu item.
14
+
15
+ ## The default you get for free
16
+
17
+ - Rows and columns collapse to a single column below the `sm` breakpoint.
18
+ - A `core.grid` becomes horizontally scrollable inside its own container.
19
+ - KPI rows wrap.
20
+
21
+ So the tutorial pages — header, KPI row, filter row, grid, dialogs — are usable
22
+ on mobile with zero extra work. **Verify that's true before adding overrides.**
23
+
24
+ ## The breakpoint model
25
+
26
+ - The base breakpoint is **`xs`** (mobile). It has no key inside
27
+ `breakpointOverrides` — its own hidden state is the sibling property
28
+ **`hiddenAtXs`** on the row / column / item.
29
+ - Wider breakpoints are **`sm`, `md`, `lg`, `xl`**, set under `breakpoints`:
30
+
31
+ ```json
32
+ {
33
+ "id": "item-3",
34
+ "kind": "block",
35
+ "hiddenAtXs": true,
36
+ "breakpoints": {
37
+ "md": { "hidden": false },
38
+ "lg": { "span": 4 }
39
+ },
40
+ "block": { "...": "..." }
41
+ }
42
+ ```
43
+
44
+ Read that as: hidden on mobile, shown again from `md` up, and 4/12 columns wide
45
+ from `lg` up. This is how you express "desktop-only content" — author it hidden
46
+ at `xs`, re-show it at a wider breakpoint.
47
+
48
+ - **`spanAtXs`** (on a column) is an independent 1–12 width for mobile, not a
49
+ mirror of `sm`.
50
+
51
+ ## The complete example — a detail panel that's a dialog on mobile, a column on desktop
52
+
53
+ The tutorial keeps it simple: detail is always a `core.dialog`, which works
54
+ everywhere. If you want a side-by-side layout on desktop, put the detail content
55
+ in a second column that is `hiddenAtXs: true` with `breakpoints.lg.hidden:
56
+ false`, and keep the dialog for `xs`–`md`.
57
+
58
+ ```json
59
+ "rows": [
60
+ { "id": "row-main", "columns": [
61
+ { "id": "col-list", "items": [ /* grid */ ], "spanAtXs": 12, "breakpoints": { "lg": { "span": 7 } } },
62
+ { "id": "col-detail", "hiddenAtXs": true, "breakpoints": { "lg": { "hidden": false, "span": 5 } },
63
+ "items": [ /* detail card bound to page.detailRecord */ ] }
64
+ ] }
65
+ ]
66
+ ```
67
+
68
+ ## Device-restricted menu items
69
+
70
+ A menu node's `visibility.devices`:
71
+
72
+ ```json
73
+ "visibility": { "visible": true, "enabled": true, "devices": ["desktop", "tablet"] }
74
+ ```
75
+
76
+ `[]` = every device. Use this for a bulk-admin screen you don't want on phones.
77
+
78
+ ## Page variants (advanced)
79
+
80
+ A page can carry `variants` — alternate layouts selected by role, device class,
81
+ or a route param:
82
+
83
+ ```json
84
+ "variants": [
85
+ { "id": "mobile", "rule": { "deviceClass": "mobile" }, "rows": [ /* a trimmed layout */ ] }
86
+ ]
87
+ ```
88
+
89
+ Use this only when the mobile experience is genuinely a different screen, not
90
+ just a reflow. For most list pages the default stacking plus a couple of
91
+ `hiddenAtXs` flags is enough.
92
+
93
+ ## How to verify it worked
94
+
95
+ `erp plugin test` validates the breakpoint shape. For the real check, publish and
96
+ open the page in the ERP, then use your browser's device-emulation to switch
97
+ between phone, tablet, and desktop widths. Confirm:
98
+
99
+ - nothing overflows the viewport horizontally (the page body must never
100
+ scroll sideways — only inner containers);
101
+ - every action reachable on desktop is reachable on mobile (even if in a
102
+ different place);
103
+ - text stays legible (no fixed pixel widths forcing tiny text).
104
+
105
+ ## Common mistakes
106
+
107
+ | Symptom | Cause | Fix |
108
+ | --- | --- | --- |
109
+ | page scrolls sideways on mobile | a fixed-width block or `minWidth` on a container | use relative units; let the grid scroll inside its own container |
110
+ | `xs` override ignored | put an `xs` key inside `breakpointOverrides` | there is no `xs` key there — use `hiddenAtXs` / `spanAtXs` |
111
+ | desktop-only panel shows on mobile | set `breakpoints.lg.hidden:false` without `hiddenAtXs:true` | you must hide at `xs` first, then re-show |
112
+ | a dialog is unusable on a phone | fixed `minWidth: 520px` on the dialog content | use `max-width: 100%` / flexible widths |
113
+
114
+ ## What to read next
115
+
116
+ - [Build a page](./build-a-page.md)
117
+ - [Add menus](./add-menus.md)
@@ -0,0 +1,127 @@
1
+ ---
2
+ title: Seed config and reference data on install
3
+ audience: tenant
4
+ ---
5
+
6
+ # Seed config and reference data on install
7
+
8
+ ## What you're doing
9
+
10
+ Making data rows travel **with** the `.spk` instead of a manual per-tenant
11
+ `POST`. Two things need seeding:
12
+
13
+ 1. **reference data** you own — equipment categories, status pick-lists, default
14
+ settings.
15
+ 2. **rows in shared platform config tables** — `entity_status_date_sweep_config`,
16
+ `entity_aggregation_config`, `entity_cadence_config`, etc. — that make your
17
+ scheduled jobs exist.
18
+
19
+ One file per data set under `spk-assembly/metadata/seed-data/`. The
20
+ `PluginDataSeedInstaller` upserts them on every install, upgrade, and
21
+ auto-provision — **idempotently**, keyed by the fields you nominate.
22
+
23
+ ## The complete example
24
+
25
+ `spk-assembly/metadata/seed-data/office-equipment-categories.json` —
26
+ [real file](../tutorial/example-plugin/spk-assembly/metadata/seed-data/office-equipment-categories.json):
27
+
28
+ ```json
29
+ {
30
+ "entity": "oeq_category",
31
+ "keyFields": ["category_code"],
32
+ "source": "office-equipment",
33
+ "rows": [
34
+ { "category_code": "LAPTOP", "category_name": "Laptops", "description": "Portable computers", "status": "ACTIVE", "seeded_by": "office-equipment" },
35
+ { "category_code": "MONITOR", "category_name": "Monitors", "description": "External displays", "status": "ACTIVE", "seeded_by": "office-equipment" },
36
+ { "category_code": "PHONE", "category_name": "Phones", "description": "Mobile handsets", "status": "ACTIVE", "seeded_by": "office-equipment" },
37
+ { "category_code": "HEADSET", "category_name": "Headsets", "description": "Audio headsets", "status": "ACTIVE", "seeded_by": "office-equipment" }
38
+ ]
39
+ }
40
+ ```
41
+
42
+ ## Line by line
43
+
44
+ - **`entity`** — the target entity `name`. It must already exist — ship it in
45
+ `metadata/entities/` if your plugin owns it.
46
+ - **`keyFields`** — the natural key. On re-install, a row matching **all**
47
+ `keyFields` is *updated only if a non-key value changed*, otherwise inserted.
48
+ Never duplicated. Choose a key that is genuinely unique (`category_code`, not
49
+ `category_name`).
50
+ - **`source`** — your plugin id. Written into a `seeded_by` field if the entity
51
+ has one — handy for `WHERE seeded_by = 'office-equipment'` cleanup later.
52
+ - **`rows`** — plain field maps. **No tenant id** — the installer supplies it.
53
+
54
+ ## Idempotency in practice
55
+
56
+ Publish the plugin, then publish it again with a fifth category added. The
57
+ installer log shows:
58
+
59
+ ```
60
+ PluginDataSeedInstaller: office-equipment-categories.json -> entity oeq_category (tenant 2): 1 created, 0 updated, 4 unchanged
61
+ ```
62
+
63
+ The four existing rows are left alone; only the new one is inserted. Verified
64
+ against the tutorial module — the first install logged `4 created`, and no
65
+ re-run ever duplicates them.
66
+
67
+ ## Seeding shared platform config (the important use)
68
+
69
+ The job guides ([reminder](./add-a-reminder-job.md),
70
+ [aggregation](./add-an-aggregation-job.md), [cadence](./add-a-cadence-job.md),
71
+ [compliance](./add-a-compliance-job.md)) all seed a row into a shared config
72
+ entity. For those you also ship:
73
+
74
+ 1. **the shared config entity file** (`entity_status_date_sweep_config.json`
75
+ etc.) so a fresh environment that predates it gains the table. The installer
76
+ is idempotent-additive: an existing table just gains any missing columns.
77
+ Ship only the additive fields your rows need — see the tutorial's
78
+ [`entity_status_date_sweep_config.json`](../tutorial/example-plugin/spk-assembly/metadata/entities/entity_status_date_sweep_config.json).
79
+ 2. **an `AFTER_CREATE` register rule** on the config entity, calling
80
+ `ensure<Job>Registered`, so the first seeded row auto-registers the job. (The
81
+ status-date-sweep job's register rule ships with the platform; the others you
82
+ ship yourself — see each job guide.)
83
+
84
+ ## Ground yourself first
85
+
86
+ ```bash
87
+ erp schema pull plugin-seed-data
88
+ erp examples patterns --kind jobs # "install-lifecycle-data-seeding"
89
+ ```
90
+
91
+ ## How to verify it worked
92
+
93
+ ```bash
94
+ erp schema validate spk-assembly/metadata/seed-data/office-equipment-categories.json --schema plugin-seed-data
95
+ ```
96
+
97
+ ```
98
+ OK — ... matches schema "plugin-seed-data"
99
+ ```
100
+
101
+ After publishing:
102
+
103
+ ```bash
104
+ erp api get "/api/v1/entities/oeq_category/records/query?size=10"
105
+ ```
106
+
107
+ ```json
108
+ { "rows": [
109
+ { "id": 1, "category_code": "LAPTOP", "category_name": "Laptops", "status": "ACTIVE", "seeded_by": "office-equipment" },
110
+ { "id": 2, "category_code": "MONITOR", "category_name": "Monitors", "status": "ACTIVE", "seeded_by": "office-equipment" }
111
+ ], "total": 4 }
112
+ ```
113
+
114
+ ## Common mistakes
115
+
116
+ | Symptom | Cause | Fix |
117
+ | --- | --- | --- |
118
+ | re-install duplicates rows | `keyFields` not actually unique | pick a real key |
119
+ | seed fails: entity not found | didn't ship the `metadata/entities/*.json` | ship it |
120
+ | job doesn't register despite seeding | no `AFTER_CREATE` register rule (for aggregation/cadence/compliance) | ship it |
121
+ | a `null` in `keyFields` matches nothing / everything | key field is optional and unset in some rows | keep key fields non-null in every row |
122
+
123
+ ## What to read next
124
+
125
+ - [Add a scheduled reminder job](./add-a-reminder-job.md)
126
+ - Recipe: [install-lifecycle data seeding](../recipes/install-lifecycle-data-seeding.md)
127
+ - Tutorial chapter 2: [the category entity + seeded data](../tutorial/02-category-entity-and-seed.md)
@@ -0,0 +1,120 @@
1
+ ---
2
+ title: Set up the SDK and connect to an environment
3
+ audience: tenant
4
+ ---
5
+
6
+ # Set up the SDK and connect to an environment
7
+
8
+ ## What you're doing
9
+
10
+ Before you can build anything you need the **plugin SDK** talking to **your ERP**.
11
+ The SDK is two programs:
12
+
13
+ - **the `erp` CLI** — scaffolds plugins, validates them, and publishes them to your ERP.
14
+ - **the `erp-plugin-language` MCP server** — the thing your AI coding agent (Claude
15
+ Code, Cursor, Codex) calls to look up schemas, widgets, and worked examples so
16
+ it doesn't have to guess.
17
+
18
+ An **environment** (or *env*) is one named ERP you talk to — `dev`, `staging`,
19
+ `prod`. The CLI remembers a base URL and a login token per env in
20
+ `~/.erp/config.json`, so you can switch between them with one command.
21
+
22
+ You do **not** need access to the ERP's source code. Everything the tools need to
23
+ ground themselves — JSON Schemas, the catalog of platform capabilities, curated
24
+ examples, these docs — ships in an **authoring bundle** that the SDK reads
25
+ offline and can refresh from your ERP.
26
+
27
+ ## The complete sequence
28
+
29
+ ```bash
30
+ # 1. point the CLI at your ERP and give the env a name
31
+ erp env use dev --base-url https://erp.example.com
32
+
33
+ # 2. log in (opens your browser; RFC 8628 device-authorization grant)
34
+ erp login
35
+
36
+ # 3. confirm who you are and which tenant you're on
37
+ erp whoami
38
+
39
+ # 4. pull this env's authoring bundle into ~/.erp/cache/
40
+ erp env sync
41
+
42
+ # 5. install the MCP server config into the current project folder
43
+ erp mcp install
44
+ ```
45
+
46
+ > Until the SDK is published to npm you run it directly. Everywhere these docs
47
+ > say `erp`, substitute `node <sdk>/tools/erp-cli/erp.mjs`. Set a shell alias:
48
+ > `alias erp='node /path/to/tools/erp-cli/erp.mjs'`.
49
+
50
+ ## Line by line
51
+
52
+ **`erp env use dev --base-url https://erp.example.com`** creates an env named
53
+ `dev` and makes it current. Run it again with a different name to add another
54
+ env; `erp env list` shows them all with a `*` next to the current one. `erp env
55
+ use staging` (no `--base-url`) just switches to an env you already defined.
56
+
57
+ **`erp login`** opens your browser, you approve the CLI, and an access token +
58
+ refresh token are written to `~/.erp/config.json` (file mode `0600`). Networked
59
+ commands auto-refresh an expired access token, so you rarely log in again. In CI,
60
+ skip the browser: set `ERP_TOKEN=<access token>` and the CLI uses that instead.
61
+
62
+ **`erp whoami`** prints the env, base URL, tenant id, your user id, and your
63
+ roles. This is the fastest check that your session is live and pointed where you
64
+ think.
65
+
66
+ **`erp env sync`** downloads the env's authoring bundle (schemas, the frozen
67
+ capability catalog, blocks, validators, docs, plus the live list of entity and
68
+ theme names in your tenant) into `~/.erp/cache/<env>/`. If your ERP doesn't
69
+ expose the bundle endpoint yet this is a no-op and the SDK falls back to the
70
+ bundle shipped inside the SDK package — you can keep going. Re-run it whenever
71
+ your ERP is upgraded. `erp env sync --check` reports drift without downloading.
72
+
73
+ **`erp mcp install`** writes a `.mcp.json` in the current directory pointing at
74
+ the `erp-plugin-language` MCP server. Your AI agent picks it up automatically the
75
+ next time it opens this folder.
76
+
77
+ ## How to verify it worked
78
+
79
+ ```bash
80
+ erp whoami
81
+ ```
82
+
83
+ Expected — a JSON blob like this (your values differ):
84
+
85
+ ```json
86
+ {
87
+ "env": "dev",
88
+ "baseUrl": "https://erp.example.com",
89
+ "tenantId": 2,
90
+ "valid": true,
91
+ "userId": "you@example.com",
92
+ "roles": ["HR Administrator", "Studio Staff"]
93
+ }
94
+ ```
95
+
96
+ `"valid": true` means the token is good. Then check the MCP wiring:
97
+
98
+ ```bash
99
+ cat .mcp.json
100
+ ```
101
+
102
+ You should see a server entry named `erp-plugin-language`. Restart your AI agent
103
+ in this folder and ask it to call `erp_list_schemas` — it should return a list
104
+ of schema names, not an error.
105
+
106
+ ## Common mistakes
107
+
108
+ | Symptom | Cause | Fix |
109
+ | --- | --- | --- |
110
+ | `erp whoami` says `"valid": false` | token expired and no refresh token | `erp login` again |
111
+ | `could not reach <url>` | wrong base URL, or `/` mangled by Git Bash on Windows | check `erp env list`; on Git Bash set `MSYS_NO_PATHCONV=1` |
112
+ | `erp env sync` downloads nothing | your ERP has no `/api/v1/dev/bundle` endpoint yet | fine — the SDK's built-in bundle is used |
113
+ | AI agent can't see the `erp_*` tools | `.mcp.json` written in a different folder, or agent not restarted | run `erp mcp install` in your project root, restart the agent |
114
+ | `--tenant 5` "does not re-authenticate you" errors | `--tenant` only overrides the header; your token must already be valid for that tenant | log in as a user who has access to that tenant |
115
+
116
+ ## What to read next
117
+
118
+ - [Create a plugin from scratch](./create-a-plugin.md)
119
+ - [Use the MCP server with an AI agent](./use-the-mcp-server.md)
120
+ - [The tutorial](../tutorial/index.md) builds a real module end to end.
@@ -0,0 +1,104 @@
1
+ ---
2
+ title: Use the MCP server with an AI agent
3
+ audience: tenant
4
+ ---
5
+
6
+ # Use the MCP server with an AI agent
7
+
8
+ ## What you're doing
9
+
10
+ Letting your AI coding agent (Claude Code, Cursor, Codex) build plugins without
11
+ guessing. The `erp-plugin-language` **MCP server** exposes tools the agent calls
12
+ to fetch the real schemas, the real widget catalog, real worked examples, and to
13
+ validate what it wrote — all grounded in *your* ERP's authoring bundle.
14
+
15
+ ## Setup
16
+
17
+ ```bash
18
+ erp mcp install # writes .mcp.json in the current folder
19
+ ```
20
+
21
+ Restart your agent in that folder. It now has the `erp_*` tools.
22
+
23
+ ## The tools, and when to reach for each
24
+
25
+ ### Grounding — before writing an artifact
26
+
27
+ | Tool | Returns | Use when |
28
+ | --- | --- | --- |
29
+ | `erp_list_schemas` | every schema name | "what artifact types exist?" |
30
+ | `erp_get_schema {"name":"entity-definition"}` | one full JSON Schema | before writing any entity / page / provider / rule / workflow / seed file |
31
+ | `erp_list_blocks` / `erp_list_blocks {"type":"core.grid"}` | the widget catalog; one widget's full property + event list | before adding a block to a page |
32
+ | `erp_list_grammar_rules` | the page/action-chain grammar | when an action chain isn't behaving |
33
+ | `erp_platform_catalog` / `erp_platform_describe {"className":"..."}` | the live inventory of platform engines, REST mappings, tables; real method signatures for extension points | before assuming "there's no built-in X" |
34
+
35
+ ### Examples — copy a proven shape
36
+
37
+ | Tool | Returns |
38
+ | --- | --- |
39
+ | `erp_examples_patterns` | curated **named shapes** (`single-stage-self-decide`, `related-record-lookup-field`, `n-days-before-reminder-sweep`, …), each pointing at a real shipped file |
40
+ | `erp_search_examples {"pattern":"employee-search","kind":"data-service"}` | real shipped metadata files matched by name |
41
+ | `erp_search_docs {"query":"..."}` | these docs + the live engine catalog + Java contracts |
42
+
43
+ ### Validation — after writing
44
+
45
+ | Tool | Checks |
46
+ | --- | --- |
47
+ | `erp_validate_page` | one page against the contract + live block registry |
48
+ | `erp_validate_plugin_pages` | every page in a plugin dir |
49
+ | `erp_validate_connector_definition` | a connector JSON |
50
+ | `erp_lint_plugin_semantics {"pluginDir":"..."}` | PAGE-DS-001 / DS-001 / I18N-001 / SEED-001 / JOB-001 across the whole plugin |
51
+ | `erp_plugin_scaffold` | a starting skeleton for a plugin or extension point |
52
+
53
+ ### Diagnostics
54
+
55
+ | Tool | Returns |
56
+ | --- | --- |
57
+ | `erp_tail_logs {"grep":"<X-Correlation-Id>"}` | the server-side stack trace for a failed call (join on the response's `X-Correlation-Id` header) |
58
+
59
+ ## A worked interaction
60
+
61
+ You: *"Add a category lookup field to the equipment form."*
62
+
63
+ A well-behaved agent will:
64
+
65
+ 1. `erp_examples_patterns` → finds `related-record-lookup-field`, reads the real
66
+ `promotions.json` it points at;
67
+ 2. `erp_get_schema {"name":"data-service-definition"}` and
68
+ `erp_search_examples {"pattern":"employee-search","kind":"data-service"}` →
69
+ learns the `search` + `dataView` recipe;
70
+ 3. writes `oeq-category-select-options-view.json`, `oeq-category-search.json`,
71
+ and the `core.lookup` block;
72
+ 4. `erp_validate_plugin_pages` and `erp_lint_plugin_semantics` → confirms clean.
73
+
74
+ Every step is grounded, nothing is guessed.
75
+
76
+ ## The verification-you-can-run trap
77
+
78
+ Ask the agent to prove each claim with a command whose output you can see —
79
+ `erp plugin test`, `erp api get ...`, `erp api post .../execute` — not "it should
80
+ work now". If the agent can't show you the output, the step isn't done.
81
+
82
+ ## How to verify the MCP wiring itself
83
+
84
+ Ask the agent to call `erp_list_schemas`. It should return a list of ~25 schema
85
+ names. If it errors or says the tool doesn't exist:
86
+
87
+ - `cat .mcp.json` — is there an `erp-plugin-language` entry?
88
+ - did you restart the agent in this folder?
89
+ - run `erp mcp install` again in the project root.
90
+
91
+ ## Common mistakes
92
+
93
+ | Symptom | Cause | Fix |
94
+ | --- | --- | --- |
95
+ | agent invents block properties | didn't call `erp_list_blocks` for that type | tell it to ground first |
96
+ | agent's page fails install on `metadata.dataSource` | skipped `erp_lint_plugin_semantics` | run it before publish |
97
+ | agent "can't find" a tool | `.mcp.json` in wrong folder / agent not restarted | `erp mcp install` in root, restart |
98
+ | schemas look stale | bundle not synced | `erp env sync` |
99
+
100
+ ## What to read next
101
+
102
+ - [Set up the SDK](./set-up-the-sdk.md)
103
+ - [Validate and test a plugin](./validate-and-test.md)
104
+ - [Expose a plugin operation as an AI tool](./expose-a-plugin-operation-as-an-ai-tool.md) — the *other* MCP concept: the ERP product's own embedded AI assistant calling your plugin at runtime, not your coding agent while you build
@@ -0,0 +1,119 @@
1
+ ---
2
+ title: Validate and test a plugin
3
+ audience: tenant
4
+ ---
5
+
6
+ # Validate and test a plugin
7
+
8
+ ## What you're doing
9
+
10
+ Catching mistakes before you publish. The SDK runs the **same checks the
11
+ platform runs at install time**, so a clean local run means a clean install.
12
+ Three commands, cheapest first.
13
+
14
+ ## The complete sequence
15
+
16
+ ```bash
17
+ erp schema validate spk-assembly/metadata/entities/oeq_equipment.json --schema entity-definition
18
+ erp plugin validate spk-assembly
19
+ erp plugin test spk-assembly
20
+ ```
21
+
22
+ Plus, over MCP, the semantic linter directly:
23
+
24
+ ```
25
+ erp_lint_plugin_semantics {"pluginDir": "developer-docs/tutorial/example-plugin/spk-assembly"}
26
+ ```
27
+
28
+ ## Line by line
29
+
30
+ ### `erp schema validate <file> --schema <name>`
31
+
32
+ Checks **one** JSON file against **one** schema. Use it while authoring a single
33
+ artifact. Fast, no backend needed.
34
+
35
+ ```
36
+ OK — spk-assembly/metadata/entities/oeq_equipment.json matches schema "entity-definition"
37
+ ```
38
+
39
+ ### `erp plugin validate <dir>`
40
+
41
+ Validates every **page** in the plugin against the page contract *and* the live
42
+ block registry (does each `blockType` exist, does each property it sets actually
43
+ belong to that block).
44
+
45
+ ```
46
+ validated 3 page(s) — clean
47
+ ```
48
+
49
+ ### `erp plugin test <dir>`
50
+
51
+ Runs `plugin validate` **plus** the semantic lints — the checks that catch the
52
+ "looks fine, silently broken" class of bug:
53
+
54
+ ```
55
+ Plugin Tests
56
+ ────────────────────────────────────────
57
+ ✓ page:equipment-catalog.json
58
+ ✓ page:equipment-checkout.json
59
+ ✓ page:equipment-register.json
60
+ ✓ plugin.json:valid-json
61
+ 1 passed, 0 failed, 0 semantic warning(s), 3 skipped
62
+ ```
63
+
64
+ ## The semantic lint codes — what each one catches
65
+
66
+ | Code | Catches | Fix |
67
+ | --- | --- | --- |
68
+ | **PAGE-DS-001** | a page has a `core.grid` but `metadata.dataSource` is missing or doesn't match any `metadata/provider/*.json` `name` — the "0 rows, no error" bug | see [Wire a page's data](./wire-a-pages-data.md) |
69
+ | **DS-001** | a Data Service `filter` compares a value of the wrong type (e.g. a string against a numeric column) | fix the filter, or set `valueType` |
70
+ | **I18N-001** | a page references an i18n key with no entry in `metadata/i18n/en.json` (or a placeholder value) | add the key — see [Add i18n](./add-i18n.md) |
71
+ | **SEED-001** | a `metadata/seed-data/*.json` targets an entity the plugin doesn't ship and that isn't a known shared config entity, or `keyFields` look non-unique | ship the entity, or fix the key |
72
+ | **JOB-001** | a new bespoke `ErpJobContribution` Java class where a generic sweep engine + JSON config would do | delete the Java, add a config row — see the job guides |
73
+ | page-semantic-lint | a foreign-key field bound to a plain text/number input instead of `core.lookup`; decorative filters that aren't wired | use `core.lookup`; wire the filter to `externalFilter` |
74
+
75
+ These are **warnings**, not hard failures — review each. But the platform's
76
+ install-time equivalents of PAGE-DS-001 / I18N-001 / SEED-001 **do** block a bad
77
+ install, so treat them as errors.
78
+
79
+ ## Wire it into CI
80
+
81
+ The docs in this tree keep every example honest with a runner that validates
82
+ every artifact and runs `erp plugin test`:
83
+
84
+ ```bash
85
+ node developer-docs/examples/test-examples.mjs
86
+ ```
87
+
88
+ ```
89
+ validating tutorial module artifacts against live schemas...
90
+ ok tutorial/entities/oeq_equipment.json
91
+ ...
92
+ running `erp plugin test` on the tutorial module...
93
+ 4 passed, 0 failed, 0 semantic warning(s), 3 skipped
94
+
95
+ All examples valid.
96
+ ```
97
+
98
+ Run it in CI on every change to `developer-docs/`. A broken example fails the
99
+ build.
100
+
101
+ ## How to verify it worked
102
+
103
+ A green `erp plugin test` and a green `test-examples.mjs`. That's the bar for
104
+ "safe to publish".
105
+
106
+ ## Common mistakes
107
+
108
+ | Symptom | Cause | Fix |
109
+ | --- | --- | --- |
110
+ | `erp plugin validate` says "no page JSON found" | you pointed it at the plugin root, not `spk-assembly` | pass `<plugin>/spk-assembly` |
111
+ | `erp schema validate` fails on a page file | page files are wrapped; `--schema page` checks the inner contract | rely on `erp plugin validate` / `test` for pages |
112
+ | lint clean locally, install still fails | a check the platform runs that the SDK doesn't (rare) | read the install error; report it |
113
+ | `erp plugin test` skips `security-rule-scan` etc. | those need extra inputs | fine for a JSON-only plugin |
114
+
115
+ ## What to read next
116
+
117
+ - [Publish and upgrade a plugin](./publish-and-upgrade.md)
118
+ - [Wire a page's data](./wire-a-pages-data.md)
119
+ - Troubleshooting: [known gotchas](../troubleshooting/known-gotchas.md)