@jahia/agentic 0.1.1 → 0.3.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 (174) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +28 -0
  3. package/dist/claude/.claude/rules/jahia.md +3 -1
  4. package/dist/claude/.claude/skills/jahia/SKILL.md +18 -10
  5. package/dist/claude/.claude/skills/jahia-content/SKILL.md +102 -84
  6. package/dist/claude/.claude/skills/jahia-content-create-content/SKILL.md +255 -280
  7. package/dist/claude/.claude/skills/jahia-content-explore-structure/SKILL.md +187 -96
  8. package/dist/claude/.claude/skills/jahia-content-media-upload/SKILL.md +197 -0
  9. package/dist/claude/.claude/skills/jahia-content-move-content/SKILL.md +160 -165
  10. package/dist/claude/.claude/skills/jahia-content-organize/SKILL.md +209 -0
  11. package/dist/claude/.claude/skills/jahia-content-publish/SKILL.md +181 -0
  12. package/dist/claude/.claude/skills/jahia-content-query-content/SKILL.md +122 -92
  13. package/dist/claude/.claude/skills/jahia-content-translate-content/SKILL.md +154 -225
  14. package/dist/claude/.claude/skills/jahia-dev-build-component/SKILL.md +7 -22
  15. package/dist/claude/.claude/skills/jahia-dev-create-view/SKILL.md +58 -0
  16. package/dist/claude/.claude/skills/jahia-dev-cypress/SKILL.md +150 -330
  17. package/dist/claude/.claude/skills/jahia-dev-java/SKILL.md +7 -2
  18. package/dist/claude/.claude/skills/jahia-dev-query-content/SKILL.md +93 -296
  19. package/dist/claude/.claude/skills/jahia-java-concurrency/SKILL.md +308 -0
  20. package/dist/claude/.claude/skills/jahia-java-jcr/SKILL.md +153 -0
  21. package/dist/claude/.claude/skills/jahia-java-osgi/SKILL.md +134 -0
  22. package/dist/claude/.claude/skills/jahia-java-persistence/SKILL.md +177 -0
  23. package/dist/claude/.claude/skills/jahia-java-security/SKILL.md +84 -0
  24. package/dist/claude/.claude/skills/jahia-jcr-sql2/SKILL.md +258 -0
  25. package/dist/claude/.claude/skills/jahia-review-java/SKILL.md +131 -0
  26. package/dist/claude/.claude/skills/jahia-review-java/references/code-review-output.md +121 -0
  27. package/dist/claude/CLAUDE.md +4 -8
  28. package/dist/codex/.agents/skills/jahia/SKILL.md +18 -10
  29. package/dist/codex/.agents/skills/jahia-content/SKILL.md +102 -84
  30. package/dist/codex/.agents/skills/jahia-content-create-content/SKILL.md +255 -280
  31. package/dist/codex/.agents/skills/jahia-content-explore-structure/SKILL.md +187 -96
  32. package/dist/codex/.agents/skills/jahia-content-media-upload/SKILL.md +197 -0
  33. package/dist/codex/.agents/skills/jahia-content-move-content/SKILL.md +160 -165
  34. package/dist/codex/.agents/skills/jahia-content-organize/SKILL.md +209 -0
  35. package/dist/codex/.agents/skills/jahia-content-publish/SKILL.md +181 -0
  36. package/dist/codex/.agents/skills/jahia-content-query-content/SKILL.md +122 -92
  37. package/dist/codex/.agents/skills/jahia-content-translate-content/SKILL.md +154 -225
  38. package/dist/codex/.agents/skills/jahia-dev-build-component/SKILL.md +7 -22
  39. package/dist/codex/.agents/skills/jahia-dev-create-view/SKILL.md +58 -0
  40. package/dist/codex/.agents/skills/jahia-dev-cypress/SKILL.md +150 -330
  41. package/dist/codex/.agents/skills/jahia-dev-java/SKILL.md +7 -2
  42. package/dist/codex/.agents/skills/jahia-dev-query-content/SKILL.md +93 -296
  43. package/dist/codex/.agents/skills/jahia-java-concurrency/SKILL.md +308 -0
  44. package/dist/codex/.agents/skills/jahia-java-jcr/SKILL.md +153 -0
  45. package/dist/codex/.agents/skills/jahia-java-osgi/SKILL.md +134 -0
  46. package/dist/codex/.agents/skills/jahia-java-persistence/SKILL.md +177 -0
  47. package/dist/codex/.agents/skills/jahia-java-security/SKILL.md +84 -0
  48. package/dist/codex/.agents/skills/jahia-jcr-sql2/SKILL.md +258 -0
  49. package/dist/codex/.agents/skills/jahia-review-java/SKILL.md +131 -0
  50. package/dist/codex/.agents/skills/jahia-review-java/references/code-review-output.md +121 -0
  51. package/dist/codex/AGENTS.md +5 -5
  52. package/dist/copilot/.agents/skills/jahia/SKILL.md +18 -10
  53. package/dist/copilot/.agents/skills/jahia-content/SKILL.md +102 -84
  54. package/dist/copilot/.agents/skills/jahia-content-create-content/SKILL.md +255 -280
  55. package/dist/copilot/.agents/skills/jahia-content-explore-structure/SKILL.md +187 -96
  56. package/dist/copilot/.agents/skills/jahia-content-media-upload/SKILL.md +197 -0
  57. package/dist/copilot/.agents/skills/jahia-content-move-content/SKILL.md +160 -165
  58. package/dist/copilot/.agents/skills/jahia-content-organize/SKILL.md +209 -0
  59. package/dist/copilot/.agents/skills/jahia-content-publish/SKILL.md +181 -0
  60. package/dist/copilot/.agents/skills/jahia-content-query-content/SKILL.md +122 -92
  61. package/dist/copilot/.agents/skills/jahia-content-translate-content/SKILL.md +154 -225
  62. package/dist/copilot/.agents/skills/jahia-dev-build-component/SKILL.md +7 -22
  63. package/dist/copilot/.agents/skills/jahia-dev-create-view/SKILL.md +58 -0
  64. package/dist/copilot/.agents/skills/jahia-dev-cypress/SKILL.md +150 -330
  65. package/dist/copilot/.agents/skills/jahia-dev-java/SKILL.md +7 -2
  66. package/dist/copilot/.agents/skills/jahia-dev-query-content/SKILL.md +93 -296
  67. package/dist/copilot/.agents/skills/jahia-java-concurrency/SKILL.md +308 -0
  68. package/dist/copilot/.agents/skills/jahia-java-jcr/SKILL.md +153 -0
  69. package/dist/copilot/.agents/skills/jahia-java-osgi/SKILL.md +134 -0
  70. package/dist/copilot/.agents/skills/jahia-java-persistence/SKILL.md +177 -0
  71. package/dist/copilot/.agents/skills/jahia-java-security/SKILL.md +84 -0
  72. package/dist/copilot/.agents/skills/jahia-jcr-sql2/SKILL.md +258 -0
  73. package/dist/copilot/.agents/skills/jahia-review-java/SKILL.md +131 -0
  74. package/dist/copilot/.agents/skills/jahia-review-java/references/code-review-output.md +121 -0
  75. package/dist/copilot/AGENTS.md +5 -5
  76. package/dist/cursor/.agents/skills/jahia/SKILL.md +18 -10
  77. package/dist/cursor/.agents/skills/jahia-content/SKILL.md +102 -84
  78. package/dist/cursor/.agents/skills/jahia-content-create-content/SKILL.md +255 -280
  79. package/dist/cursor/.agents/skills/jahia-content-explore-structure/SKILL.md +187 -96
  80. package/dist/cursor/.agents/skills/jahia-content-media-upload/SKILL.md +197 -0
  81. package/dist/cursor/.agents/skills/jahia-content-move-content/SKILL.md +160 -165
  82. package/dist/cursor/.agents/skills/jahia-content-organize/SKILL.md +209 -0
  83. package/dist/cursor/.agents/skills/jahia-content-publish/SKILL.md +181 -0
  84. package/dist/cursor/.agents/skills/jahia-content-query-content/SKILL.md +122 -92
  85. package/dist/cursor/.agents/skills/jahia-content-translate-content/SKILL.md +154 -225
  86. package/dist/cursor/.agents/skills/jahia-dev-build-component/SKILL.md +7 -22
  87. package/dist/cursor/.agents/skills/jahia-dev-create-view/SKILL.md +58 -0
  88. package/dist/cursor/.agents/skills/jahia-dev-cypress/SKILL.md +150 -330
  89. package/dist/cursor/.agents/skills/jahia-dev-java/SKILL.md +7 -2
  90. package/dist/cursor/.agents/skills/jahia-dev-query-content/SKILL.md +93 -296
  91. package/dist/cursor/.agents/skills/jahia-java-concurrency/SKILL.md +308 -0
  92. package/dist/cursor/.agents/skills/jahia-java-jcr/SKILL.md +153 -0
  93. package/dist/cursor/.agents/skills/jahia-java-osgi/SKILL.md +134 -0
  94. package/dist/cursor/.agents/skills/jahia-java-persistence/SKILL.md +177 -0
  95. package/dist/cursor/.agents/skills/jahia-java-security/SKILL.md +84 -0
  96. package/dist/cursor/.agents/skills/jahia-jcr-sql2/SKILL.md +258 -0
  97. package/dist/cursor/.agents/skills/jahia-review-java/SKILL.md +131 -0
  98. package/dist/cursor/.agents/skills/jahia-review-java/references/code-review-output.md +121 -0
  99. package/dist/cursor/.cursor/rules/jahia.mdc +3 -1
  100. package/dist/gemini/.agents/skills/jahia/SKILL.md +18 -10
  101. package/dist/gemini/.agents/skills/jahia-content/SKILL.md +102 -84
  102. package/dist/gemini/.agents/skills/jahia-content-create-content/SKILL.md +255 -280
  103. package/dist/gemini/.agents/skills/jahia-content-explore-structure/SKILL.md +187 -96
  104. package/dist/gemini/.agents/skills/jahia-content-media-upload/SKILL.md +197 -0
  105. package/dist/gemini/.agents/skills/jahia-content-move-content/SKILL.md +160 -165
  106. package/dist/gemini/.agents/skills/jahia-content-organize/SKILL.md +209 -0
  107. package/dist/gemini/.agents/skills/jahia-content-publish/SKILL.md +181 -0
  108. package/dist/gemini/.agents/skills/jahia-content-query-content/SKILL.md +122 -92
  109. package/dist/gemini/.agents/skills/jahia-content-translate-content/SKILL.md +154 -225
  110. package/dist/gemini/.agents/skills/jahia-dev-build-component/SKILL.md +7 -22
  111. package/dist/gemini/.agents/skills/jahia-dev-create-view/SKILL.md +58 -0
  112. package/dist/gemini/.agents/skills/jahia-dev-cypress/SKILL.md +150 -330
  113. package/dist/gemini/.agents/skills/jahia-dev-java/SKILL.md +7 -2
  114. package/dist/gemini/.agents/skills/jahia-dev-query-content/SKILL.md +93 -296
  115. package/dist/gemini/.agents/skills/jahia-java-concurrency/SKILL.md +308 -0
  116. package/dist/gemini/.agents/skills/jahia-java-jcr/SKILL.md +153 -0
  117. package/dist/gemini/.agents/skills/jahia-java-osgi/SKILL.md +134 -0
  118. package/dist/gemini/.agents/skills/jahia-java-persistence/SKILL.md +177 -0
  119. package/dist/gemini/.agents/skills/jahia-java-security/SKILL.md +84 -0
  120. package/dist/gemini/.agents/skills/jahia-jcr-sql2/SKILL.md +258 -0
  121. package/dist/gemini/.agents/skills/jahia-review-java/SKILL.md +131 -0
  122. package/dist/gemini/.agents/skills/jahia-review-java/references/code-review-output.md +121 -0
  123. package/dist/gemini/AGENTS.md +5 -5
  124. package/dist/gemini/GEMINI.md +2 -2
  125. package/dist/opencode/.agents/skills/jahia/SKILL.md +18 -10
  126. package/dist/opencode/.agents/skills/jahia-content/SKILL.md +102 -84
  127. package/dist/opencode/.agents/skills/jahia-content-create-content/SKILL.md +255 -280
  128. package/dist/opencode/.agents/skills/jahia-content-explore-structure/SKILL.md +187 -96
  129. package/dist/opencode/.agents/skills/jahia-content-media-upload/SKILL.md +197 -0
  130. package/dist/opencode/.agents/skills/jahia-content-move-content/SKILL.md +160 -165
  131. package/dist/opencode/.agents/skills/jahia-content-organize/SKILL.md +209 -0
  132. package/dist/opencode/.agents/skills/jahia-content-publish/SKILL.md +181 -0
  133. package/dist/opencode/.agents/skills/jahia-content-query-content/SKILL.md +122 -92
  134. package/dist/opencode/.agents/skills/jahia-content-translate-content/SKILL.md +154 -225
  135. package/dist/opencode/.agents/skills/jahia-dev-build-component/SKILL.md +7 -22
  136. package/dist/opencode/.agents/skills/jahia-dev-create-view/SKILL.md +58 -0
  137. package/dist/opencode/.agents/skills/jahia-dev-cypress/SKILL.md +150 -330
  138. package/dist/opencode/.agents/skills/jahia-dev-java/SKILL.md +7 -2
  139. package/dist/opencode/.agents/skills/jahia-dev-query-content/SKILL.md +93 -296
  140. package/dist/opencode/.agents/skills/jahia-java-concurrency/SKILL.md +308 -0
  141. package/dist/opencode/.agents/skills/jahia-java-jcr/SKILL.md +153 -0
  142. package/dist/opencode/.agents/skills/jahia-java-osgi/SKILL.md +134 -0
  143. package/dist/opencode/.agents/skills/jahia-java-persistence/SKILL.md +177 -0
  144. package/dist/opencode/.agents/skills/jahia-java-security/SKILL.md +84 -0
  145. package/dist/opencode/.agents/skills/jahia-jcr-sql2/SKILL.md +258 -0
  146. package/dist/opencode/.agents/skills/jahia-review-java/SKILL.md +131 -0
  147. package/dist/opencode/.agents/skills/jahia-review-java/references/code-review-output.md +121 -0
  148. package/dist/opencode/AGENTS.md +5 -5
  149. package/dist/windsurf/.windsurf/rules/jahia.md +3 -1
  150. package/dist/windsurf/.windsurf/skills/jahia/SKILL.md +18 -10
  151. package/dist/windsurf/.windsurf/skills/jahia-content/SKILL.md +102 -84
  152. package/dist/windsurf/.windsurf/skills/jahia-content-create-content/SKILL.md +255 -280
  153. package/dist/windsurf/.windsurf/skills/jahia-content-explore-structure/SKILL.md +187 -96
  154. package/dist/windsurf/.windsurf/skills/jahia-content-media-upload/SKILL.md +197 -0
  155. package/dist/windsurf/.windsurf/skills/jahia-content-move-content/SKILL.md +160 -165
  156. package/dist/windsurf/.windsurf/skills/jahia-content-organize/SKILL.md +209 -0
  157. package/dist/windsurf/.windsurf/skills/jahia-content-publish/SKILL.md +181 -0
  158. package/dist/windsurf/.windsurf/skills/jahia-content-query-content/SKILL.md +122 -92
  159. package/dist/windsurf/.windsurf/skills/jahia-content-translate-content/SKILL.md +154 -225
  160. package/dist/windsurf/.windsurf/skills/jahia-dev-build-component/SKILL.md +7 -22
  161. package/dist/windsurf/.windsurf/skills/jahia-dev-create-view/SKILL.md +58 -0
  162. package/dist/windsurf/.windsurf/skills/jahia-dev-cypress/SKILL.md +150 -330
  163. package/dist/windsurf/.windsurf/skills/jahia-dev-java/SKILL.md +7 -2
  164. package/dist/windsurf/.windsurf/skills/jahia-dev-query-content/SKILL.md +93 -296
  165. package/dist/windsurf/.windsurf/skills/jahia-java-concurrency/SKILL.md +308 -0
  166. package/dist/windsurf/.windsurf/skills/jahia-java-jcr/SKILL.md +153 -0
  167. package/dist/windsurf/.windsurf/skills/jahia-java-osgi/SKILL.md +134 -0
  168. package/dist/windsurf/.windsurf/skills/jahia-java-persistence/SKILL.md +177 -0
  169. package/dist/windsurf/.windsurf/skills/jahia-java-security/SKILL.md +84 -0
  170. package/dist/windsurf/.windsurf/skills/jahia-jcr-sql2/SKILL.md +258 -0
  171. package/dist/windsurf/.windsurf/skills/jahia-review-java/SKILL.md +131 -0
  172. package/dist/windsurf/.windsurf/skills/jahia-review-java/references/code-review-output.md +121 -0
  173. package/dist/windsurf/AGENTS.md +5 -5
  174. package/package.json +7 -3
@@ -1,313 +1,340 @@
1
1
  ---
2
2
  name: jahia-content-create-content
3
- description: Creates and publishes JCR content nodes in a running Jahia instance via the GraphQL API. Use when asked to populate a site with content, create articles, tutorials, or any JCR node programmatically.
3
+ description: Creates Jahia sites, pages, and content nodes via MCP tools. Use when asked to stand up a new site, create pages, populate areas, or build structured content trees.
4
4
  ---
5
5
 
6
6
  # Skill: jahia-content-create-content
7
7
 
8
- Creates content nodes in a running Jahia instance using the GraphQL JCR mutation API, then publishes them.
8
+ Creates sites, pages, and content in a running Jahia instance using MCP tools via the `jahia` MCP server.
9
+
10
+ > **Never call Jahia's GraphQL API directly.** Use only MCP tools. If a capability is missing, report it — do not work around with curl/GraphQL.
9
11
 
10
12
  ---
11
13
 
12
14
  ## Prerequisites
13
15
 
14
- - Jahia running at `http://localhost:8080`
15
- - Credentials: `root` / `root1234` (default)
16
- - GraphQL endpoint: `http://localhost:8080/modules/graphql`
17
-
18
- **Auth pattern — always use both flags:**
19
- ```bash
20
- curl -u root:root1234 \
21
- -H "Content-Type: application/json" \
22
- -H "Origin: http://localhost:8080" \
23
- ...
24
- ```
25
-
26
- > ⚠️ The `Origin: http://localhost:8080` header is **required**. Requests without it return `Permission denied` even with correct credentials.
16
+ - MCP server `jahia` connected with a valid API token
17
+ - Know the target `siteKey` if the site already exists (call `site.list` if unsure)
18
+ - Know the content `locale` (for example `en` or `fr`)
27
19
 
28
20
  ---
29
21
 
30
- ## Minimum-call workflow
31
-
32
- Use these patterns to minimise the number of API round-trips:
22
+ ## Minimum-call workflows
33
23
 
34
- ### 1. Batch site exploration one call instead of four
24
+ ### 1 Create a brand-new site, then its first page
35
25
 
36
- Use GraphQL aliases to retrieve site metadata, page structure, files, and available content types in a **single request**:
26
+ Use this flow when the site does not exist yet:
37
27
 
38
- ```bash
39
- curl -s -u root:root1234 \
40
- -H "Content-Type: application/json" -H "Origin: http://localhost:8080" \
41
- -X POST http://localhost:8080/modules/graphql \
42
- -d '{"query":"{ jcr { site: nodeByPath(path: \"/sites/SITE_KEY\") { properties(names: [\"j:templatesSet\",\"j:defaultLanguage\"]) { name value } } home: nodeByPath(path: \"/sites/SITE_KEY/home\") { children { nodes { name primaryNodeType { name } children { nodes { name primaryNodeType { name } } } } } } files: nodeByPath(path: \"/sites/SITE_KEY/files\") { children { nodes { name uuid } } } contentTypes: nodeTypes(filter: {siteKey: \"SITE_KEY\", includeMixins: false, includeAbstract: false}) { nodes { name systemId } } } }"}'
43
28
  ```
29
+ # 1. Discover which template sets are installed for site creation
30
+ tool: site.templateSets
31
+ args: {}
32
+
33
+ # 2. Create the site
34
+ tool: site.create
35
+ args: {
36
+ "siteKey": "brandSite",
37
+ "title": "Brand Site",
38
+ "templateSet": "digitall",
39
+ "defaultLanguage": "en",
40
+ "languages": ["en", "fr"],
41
+ "serverName": "brand.local"
42
+ }
44
43
 
45
- Then fetch all needed type definitions in one more call using `nodeTypesByNames` (see `/jahia-content-explore-structure`).
46
-
47
- ### 2. Upload images in parallel
44
+ # 3. Discover page templates available on the new site
45
+ tool: page.templates
46
+ args: { "siteKey": "brandSite" }
47
+
48
+ # 4. Create the first page
49
+ tool: page.create
50
+ args: {
51
+ "parentPath": "/sites/brandSite/home",
52
+ "name": "about",
53
+ "title": "About",
54
+ "templateName": "simple",
55
+ "locale": "en"
56
+ }
48
57
 
49
- Run all uploads simultaneously using background processes:
58
+ # 5. Discover allowed content types in an area
59
+ tool: content.list_definitions
60
+ args: {
61
+ "siteKey": "brandSite",
62
+ "nodePath": "/sites/brandSite/home/about/main"
63
+ }
50
64
 
51
- ```bash
52
- for f in /path/to/img1.jpg /path/to/img2.jpg /path/to/img3.jpg; do
53
- name=$(basename "$f")
54
- curl -s -u root:root1234 \
55
- -H "Origin: http://localhost:8080" \
56
- -X POST http://localhost:8080/modules/graphql \
57
- -F "operations={\"query\":\"mutation { jcr { addNode(name: \\\"${name}\\\", parentPathOrId: \\\"/sites/SITE_KEY/files\\\", primaryNodeType: \\\"jnt:file\\\", mixins: [\\\"jmix:image\\\"]) { addChild(name: \\\"jcr:content\\\", primaryNodeType: \\\"jnt:resource\\\") { content: mutateProperty(name: \\\"jcr:data\\\") { setValue(type: BINARY, value: \\\"fc\\\") } contentType: mutateProperty(name: \\\"jcr:mimeType\\\") { setValue(value: \\\"image/jpeg\\\") } } uuid } } }\"}" \
58
- -F 'map={"fc":["variables.f"]}' \
59
- -F "fc=@${f};type=image/jpeg" &
60
- done
61
- wait # all uploads complete in parallel
65
+ # 6. Create the first content item
66
+ tool: content.create
67
+ args: {
68
+ "parentPath": "/sites/brandSite/home/about/main",
69
+ "nodeType": "jnt:bigText",
70
+ "locale": "en",
71
+ "properties": {
72
+ "text": "<h2>Welcome</h2><p>Hello world.</p>"
73
+ }
74
+ }
62
75
  ```
63
76
 
64
- > ⚠️ Always include `mixins: ["jmix:image"]` in the upload. Without it, the file node **cannot be used as a WEAKREFERENCE** in image properties.
77
+ ### 2 Create a page with content on an existing site
65
78
 
66
- To collect UUIDs after parallel uploads, query them in one batch:
67
- ```bash
68
- curl -s -u root:root1234 -H "Content-Type: application/json" -H "Origin: http://localhost:8080" \
69
- -X POST http://localhost:8080/modules/graphql \
70
- -d '{"query":"{ jcr { nodesByQuery(query: \"SELECT * FROM [jnt:file] WHERE ISDESCENDANTNODE(\u0027/sites/SITE_KEY/files/FOLDER\u0027)\", queryLanguage: SQL2) { nodes { name uuid } } } }"}'
71
79
  ```
80
+ # 1. Discover templates
81
+ tool: page.templates
82
+ args: { "siteKey": "SITE_KEY" }
83
+
84
+ # 2. Create the page
85
+ tool: page.create
86
+ args: {
87
+ "parentPath": "/sites/SITE_KEY/home",
88
+ "name": "my-page",
89
+ "title": "My Page",
90
+ "templateName": "simple",
91
+ "locale": "en"
92
+ }
72
93
 
73
- ### 3. Create an entire content tree in one mutation
74
-
75
- Use nested `addChild` calls inside a single `addNode` mutation to build a complete page hierarchy without sequential round-trips:
94
+ # 3. Check which content types and properties are allowed in the area
95
+ tool: content.list_definitions
96
+ args: {
97
+ "siteKey": "SITE_KEY",
98
+ "nodePath": "/sites/SITE_KEY/home/my-page/main"
99
+ }
76
100
 
77
- ```bash
78
- curl -s -u root:root1234 -H "Content-Type: application/json" -H "Origin: http://localhost:8080" \
79
- -X POST http://localhost:8080/modules/graphql \
80
- -d '{"query":"mutation { jcr { addNode(parentPathOrId: \"/sites/SITE_KEY/home\", name: \"my-page\", primaryNodeType: \"jnt:page\", properties: [{name: \"j:templateName\", value: \"TEMPLATE\"}, {name: \"jcr:title\", value: \"Page Title\", language: \"en\"}]) { uuid addChild(name: \"AREA_NAME\", primaryNodeType: \"AREA_TYPE\") { addChild(name: \"section-1\", primaryNodeType: \"NAMESPACE:section\", properties: [{name: \"jcr:title\", value: \"Section 1\", language: \"en\"}]) { uuid addChild(name: \"item-1\", primaryNodeType: \"NAMESPACE:item\", properties: [{name: \"jcr:title\", value: \"Item 1\", language: \"en\"}, {name: \"body\", value: \"<p>Content</p>\", language: \"en\"}]) { uuid } } } } } }"}'
101
+ # 4. Create content inside the area
102
+ tool: content.create
103
+ args: {
104
+ "parentPath": "/sites/SITE_KEY/home/my-page/main",
105
+ "nodeType": "jnt:bigText",
106
+ "locale": "en",
107
+ "properties": {
108
+ "text": "<h2>Welcome</h2><p>Hello world.</p>"
109
+ }
110
+ }
81
111
  ```
82
112
 
83
- > This creates the full `page area → section → item` hierarchy atomically.
113
+ ### 3 Create a content tree atomically
84
114
 
85
- ### 4. Publish the entire page in one call
115
+ Use `children` to build nested content in one call:
86
116
 
87
- ```bash
88
- curl -s -u root:root1234 -H "Content-Type: application/json" -H "Origin: http://localhost:8080" \
89
- -X POST http://localhost:8080/modules/graphql \
90
- -d '{"query":"mutation { jcr { mutateNode(pathOrId: \"/sites/SITE_KEY/home/my-page\") { publish(languages: [\"en\"]) } } }"}'
117
+ ```
118
+ tool: content.create
119
+ args: {
120
+ "parentPath": "/sites/SITE_KEY/home/my-page/main",
121
+ "nodeType": "mymodule:section",
122
+ "locale": "en",
123
+ "properties": {
124
+ "jcr:title": "Features Section"
125
+ },
126
+ "children": [
127
+ {
128
+ "name": "card-1",
129
+ "nodeType": "mymodule:card",
130
+ "properties": {
131
+ "jcr:title": "Feature One",
132
+ "body": "<p>Description here.</p>"
133
+ }
134
+ },
135
+ {
136
+ "name": "card-2",
137
+ "nodeType": "mymodule:card",
138
+ "properties": {
139
+ "jcr:title": "Feature Two",
140
+ "body": "<p>Another description.</p>"
141
+ }
142
+ }
143
+ ]
144
+ }
91
145
  ```
92
146
 
147
+ The entire tree is created atomically — all or nothing.
148
+
93
149
  ---
94
150
 
95
- ## Uploading image files
151
+ ## Step-by-step workflow
152
+
153
+ ### Step 1 — Check whether the site already exists
96
154
 
97
- Use the GraphQL API with a **multipart request** to upload files.
155
+ ```
156
+ tool: site.list
157
+ ```
98
158
 
99
- > ⚠️ Do **not** use `Content-Type: application/json` for uploads — use multipart form-data.
100
- > Do **not** use `type: BINARY` inside the `properties: [...]` array of `addNode` or `addChild` — it won't work. Always use the separate `mutateProperty.setValue` step.
159
+ - If the site already exists, continue with `page.templates`.
160
+ - If it does not exist yet, do **site creation first**.
101
161
 
102
- ### Upload a single file
162
+ ### Step 2 If needed, create the site first
103
163
 
104
- > ⚠️ Always include `mixins: ["jmix:image"]` when uploading images. Without this mixin, the file node **cannot be used as a WEAKREFERENCE** in image properties — you will get a constraint error.
164
+ Discover what template sets are installed for site creation:
105
165
 
106
- ```bash
107
- curl -s -u root:root1234 \
108
- -H "Origin: http://localhost:8080" \
109
- -X POST http://localhost:8080/modules/graphql \
110
- -F 'operations={"query":"mutation { jcr { addNode(name: \"image.jpg\", parentPathOrId: \"/sites/SITE_KEY/files\", primaryNodeType: \"jnt:file\", mixins: [\"jmix:image\"]) { addChild(name: \"jcr:content\", primaryNodeType: \"jnt:resource\") { content: mutateProperty(name: \"jcr:data\") { setValue(type: BINARY, value: \"fc\") } contentType: mutateProperty(name: \"jcr:mimeType\") { setValue(value: \"image/jpeg\") } } uuid } } }"}' \
111
- -F 'map={"fc":["variables.f"]}' \
112
- -F "fc=@/absolute/path/to/image.jpg;type=image/jpeg"
166
+ ```
167
+ tool: site.templateSets
168
+ args: {}
113
169
  ```
114
170
 
115
- The response contains the UUID:
116
- ```json
117
- {"data":{"jcr":{"addNode":{"addChild":{"content":{"setValue":true},"contentType":{"setValue":true}},"uuid":"xxxxxxxx-..."}}}}
171
+ Then create the site:
172
+
173
+ ```
174
+ tool: site.create
175
+ args: {
176
+ "siteKey": "SITE_KEY",
177
+ "title": "My Site",
178
+ "templateSet": "digitall",
179
+ "defaultLanguage": "en",
180
+ "languages": ["en", "fr"],
181
+ "serverName": "mysite.local"
182
+ }
118
183
  ```
119
184
 
120
- ### Use a file UUID as an image property
185
+ Only after the site exists should you continue to page-template discovery and page creation.
121
186
 
122
- Image properties in CND have `requiredType: WEAKREFERENCE`. Pass the UUID with `type: WEAKREFERENCE`:
187
+ ### Step 3 Discover available page templates
123
188
 
124
- ```graphql
125
- properties: [
126
- {name: "image", value: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", type: WEAKREFERENCE}
127
- ]
189
+ ```
190
+ tool: page.templates
191
+ args: { "siteKey": "SITE_KEY" }
128
192
  ```
129
193
 
130
- > After uploading, publish the files folder so images are accessible on the live site:
131
- > ```bash
132
- > curl -s -u root:root1234 -H "Content-Type: application/json" -H "Origin: http://localhost:8080" \
133
- > -X POST http://localhost:8080/modules/graphql \
134
- > -d '{"query":"mutation { jcr { mutateNode(pathOrId: \"/sites/SITE_KEY/files\") { publish(languages: [\"en\"]) } } }"}'
135
- > ```
194
+ Pick the template that matches the page's purpose.
136
195
 
137
- ---
196
+ ### Step 4 — Create the page
138
197
 
139
- ## Creating pages (jnt:page) — Area structure is mandatory
198
+ ```
199
+ tool: page.create
200
+ args: {
201
+ "parentPath": "/sites/SITE_KEY/home",
202
+ "name": "my-page",
203
+ "title": "My Page",
204
+ "templateName": "simple",
205
+ "locale": "en"
206
+ }
207
+ ```
140
208
 
141
- > ⚠️ If your task involves creating a **page**, read this section first.
209
+ `page.create` returns the page structure: areas, paths, allowed types, and constraints. If you need to re-check the structure later, call `page.structure`.
142
210
 
143
- In Jahia, a `jnt:page` uses a template that declares named **Areas**. Content added as **direct children of the page node** is silently ignored by the renderer it will never appear on the page.
211
+ ### Step 5Understand what content goes where
144
212
 
145
- Content must be created as children of the **Area sub-node** (e.g. `/sites/mySite/home/my-page/AREA_NAME/hero`).
213
+ The page structure shows each area:
146
214
 
147
- ### Step A Discover the correct Area structure from an existing page
215
+ - area path, for example `/sites/SITE_KEY/home/my-page/main`
216
+ - allowed node types
217
+ - existing children
148
218
 
149
- Pick any working sibling page and inspect its children:
219
+ For a full type definition:
150
220
 
151
- ```bash
152
- curl -s -u root:root1234 \
153
- -H "Content-Type: application/json" \
154
- -H "Origin: http://localhost:8080" \
155
- -X POST http://localhost:8080/modules/graphql \
156
- -d '{"query":"{ jcr { nodeByPath(path: \"/sites/SITE_KEY/home\") { children { nodes { name primaryNodeType { name } children { nodes { name primaryNodeType { name } } } } } } } }"}'
221
+ ```
222
+ tool: content.type
223
+ args: { "name": "jnt:bigText" }
157
224
  ```
158
225
 
159
- Look for a child node that is a content list or area type (e.g. `jnt:contentList`, `jnt:area`, or a custom area type). Note its **name** — that is your Area node name.
160
-
161
- ### Step B — Check the page template
226
+ To discover all types available on the site:
162
227
 
163
- ```bash
164
- curl -s -u root:root1234 \
165
- -H "Content-Type: application/json" \
166
- -H "Origin: http://localhost:8080" \
167
- -X POST http://localhost:8080/modules/graphql \
168
- -d '{"query":"{ jcr { nodeByPath(path: \"/sites/SITE_KEY/home/EXISTING_PAGE\") { properties(names: [\"j:templateName\"]) { name value } } } }"}'
228
+ ```
229
+ tool: site.types
230
+ args: { "siteKey": "SITE_KEY" }
169
231
  ```
170
232
 
171
- Use this exact template name for your new page.
233
+ ### Step 6 Check what properties a type requires
172
234
 
173
- ### Step C — Create the page, then add content inside the Area
235
+ ```
236
+ tool: content.list_definitions
237
+ args: {
238
+ "siteKey": "SITE_KEY",
239
+ "nodePath": "/sites/SITE_KEY/home/my-page/main"
240
+ }
241
+ ```
174
242
 
175
- ```bash
176
- # 1. Create the page
177
- curl -s -u root:root1234 -H "Content-Type: application/json" -H "Origin: http://localhost:8080" \
178
- -X POST http://localhost:8080/modules/graphql \
179
- -d '{"query":"mutation { jcr { addNode(parentPathOrId: \"/sites/SITE_KEY/home\", name: \"my-page\", primaryNodeType: \"jnt:page\", properties: [{name: \"jcr:title\", value: \"My Page\", language: \"en\"}, {name: \"j:templateName\", value: \"TEMPLATE_NAME\"}]) { uuid node { path } } } }"}'
243
+ This returns allowed content types, mandatory and optional properties, property types, choicelist values, and i18n flags.
180
244
 
181
- # 2. Create the Area sub-node (same type and name as the sibling page's area)
182
- curl -s -u root:root1234 -H "Content-Type: application/json" -H "Origin: http://localhost:8080" \
183
- -X POST http://localhost:8080/modules/graphql \
184
- -d '{"query":"mutation { jcr { addNode(parentPathOrId: \"/sites/SITE_KEY/home/my-page\", name: \"AREA_NAME\", primaryNodeType: \"AREA_TYPE\") { uuid node { path } } } }"}'
245
+ ### Step 7 Create content in an area
185
246
 
186
- # 3. Add content INSIDE the area (not on the page directly)
187
- curl -s -u root:root1234 -H "Content-Type: application/json" -H "Origin: http://localhost:8080" \
188
- -X POST http://localhost:8080/modules/graphql \
189
- -d '{"query":"mutation { jcr { addNode(parentPathOrId: \"/sites/SITE_KEY/home/my-page/AREA_NAME\", name: \"hero\", primaryNodeType: \"jnt:text\", properties: [{name: \"text\", value: \"<h1>Hello<\\/h1>\", language: \"en\"}]) { uuid node { path } } } }"}'
190
247
  ```
191
-
192
- ### Step D — Publish the page
193
-
194
- ```bash
195
- curl -s -u root:root1234 -H "Content-Type: application/json" -H "Origin: http://localhost:8080" \
196
- -X POST http://localhost:8080/modules/graphql \
197
- -d '{"query":"mutation { jcr { mutateNode(pathOrId: \"/sites/SITE_KEY/home/my-page\") { publish(languages: [\"en\"]) } } }"}'
248
+ tool: content.create
249
+ args: {
250
+ "parentPath": "/sites/SITE_KEY/home/my-page/main",
251
+ "nodeType": "jnt:bigText",
252
+ "locale": "en",
253
+ "properties": {
254
+ "text": "<h2>Welcome</h2><p>This is the intro paragraph.</p>"
255
+ }
256
+ }
198
257
  ```
199
258
 
200
- ---
201
-
202
- ## Step 1 — Identify target site and content folder
259
+ ### Step 8 — Update existing content
203
260
 
204
- Standard content folder paths:
205
- - `/sites/<siteKey>/contents/articles/` — for article nodes
206
- - `/sites/<siteKey>/contents/tutorials/` — for tutorial nodes
207
- - `/sites/<siteKey>/contents/` — for any other content folder
208
-
209
- ---
261
+ ```
262
+ tool: content.update
263
+ args: {
264
+ "path": "/sites/SITE_KEY/home/my-page/main/intro-text",
265
+ "locale": "en",
266
+ "properties": {
267
+ "text": "<h2>Updated Title</h2><p>New content.</p>"
268
+ }
269
+ }
270
+ ```
210
271
 
211
- ## Step 2 Look up the content type's properties
272
+ Also supports `addMixins`, `removeMixins`, and `removeProperties`.
212
273
 
213
- > 💡 **If the site is unfamiliar**, use **`/jahia-content-explore-structure`** first.
274
+ ### Step 9 Preview and verify
214
275
 
215
- ```bash
216
- curl -s -u root:root1234 \
217
- -H "Content-Type: application/json" \
218
- -H "Origin: http://localhost:8080" \
219
- -X POST http://localhost:8080/modules/graphql \
220
- -d '{
221
- "query": "{ jcr { nodeTypeByName(name: \"NAMESPACE:typeName\") { properties { name requiredType internationalized mandatory constraints } } } }"
222
- }'
276
+ ```
277
+ tool: page.preview
278
+ args: { "path": "/sites/SITE_KEY/home/my-page" }
223
279
  ```
224
280
 
225
- ---
281
+ Use this to verify the rendered HTML before publishing.
226
282
 
227
- ## Step 3Create a node
283
+ ### Step 10Publish the result
228
284
 
229
- ```bash
230
- curl -s -u root:root1234 \
231
- -H "Content-Type: application/json" \
232
- -H "Origin: http://localhost:8080" \
233
- -X POST http://localhost:8080/modules/graphql \
234
- -d '{
235
- "query": "mutation { jcr { addNode(parentPathOrId: \"/sites/mySite/contents/articles\", name: \"my-article\", primaryNodeType: \"namespace:docArticle\", properties: [{name: \"jcr:title\", value: \"My Article\", language: \"en\"}, {name: \"body\", value: \"<p>Content here</p>\", language: \"en\"}]) { uuid node { path } } } }"
236
- }'
237
285
  ```
238
-
239
- ### Property rules
240
-
241
- | Situation | GraphQL syntax |
242
- |-----------|---------------|
243
- | i18n property (declared `i18n` in CND) | `{name: "body", value: "...", language: "en"}` |
244
- | Non-i18n property | `{name: "product", value: "jahia"}` |
245
- | Title (from `mix:title`) | `{name: "jcr:title", value: "...", language: "en"}` |
246
- | Date property | `{name: "updatedAt", value: "2024-01-15T00:00:00.000Z", type: DATE}` |
247
- | Multiple values | `{name: "tags", values: ["a", "b"]}` |
248
-
249
- ### Node name rules
250
- - Use lowercase kebab-case: `my-article`, `getting-started`
251
- - No spaces, no special characters
252
- - Must be unique within the parent folder
253
- - Use `useAvailableNodeName: true` to auto-suffix if name is taken
286
+ tool: publication.publish
287
+ args: {
288
+ "path": "/sites/SITE_KEY/home/my-page",
289
+ "languages": ["en"]
290
+ }
291
+ ```
254
292
 
255
293
  ---
256
294
 
257
- ## Step 4 — Publish the node
258
-
259
- ```bash
260
- curl -s -u root:root1234 \
261
- -H "Content-Type: application/json" \
262
- -H "Origin: http://localhost:8080" \
263
- -X POST http://localhost:8080/modules/graphql \
264
- -d '{
265
- "query": "mutation { jcr { mutateNode(pathOrId: \"/sites/mySite/contents/articles/my-article\") { publish(languages: [\"en\"]) } } }"
266
- }'
267
- ```
295
+ ## Property rules
268
296
 
269
- Expected response: `{"data": {"jcr": {"mutateNode": {"publish": true}}}}`
297
+ | Situation | How to handle |
298
+ |-----------|---------------|
299
+ | i18n property | Pass `locale` and set the translated value in `properties` |
300
+ | Rich text | Use HTML such as `"text": "<h2>Title</h2><p>Body</p>"` |
301
+ | Date property | Use ISO-8601 such as `"2026-01-15T00:00:00.000Z"` |
302
+ | Reference property | Pass a UUID or absolute JCR path |
303
+ | Multi-valued property | Pass an array such as `"tags": ["a", "b"]` |
304
+ | Node name | Optional — auto-derived from `jcr:title` or `title` if omitted |
270
305
 
271
306
  ---
272
307
 
273
- ## Step 5 — Batch creation
308
+ ## Common patterns
274
309
 
275
- To create multiple nodes efficiently, use `addNodesBatch`:
310
+ ### Create a page under a sub-page
276
311
 
277
- ```bash
278
- curl -s -u root:root1234 \
279
- -H "Content-Type: application/json" \
280
- -H "Origin: http://localhost:8080" \
281
- -X POST http://localhost:8080/modules/graphql \
282
- -d '{
283
- "query": "mutation { jcr { addNodesBatch(nodes: [{parentPathOrId: \"/sites/mySite/contents/articles\", name: \"article-1\", primaryNodeType: \"namespace:docArticle\", properties: [{name: \"jcr:title\", value: \"Article One\", language: \"en\"}, {name: \"body\", value: \"<p>Body 1</p>\", language: \"en\"}]}, {parentPathOrId: \"/sites/mySite/contents/articles\", name: \"article-2\", primaryNodeType: \"namespace:docArticle\", properties: [{name: \"jcr:title\", value: \"Article Two\", language: \"en\"}, {name: \"body\", value: \"<p>Body 2</p>\", language: \"en\"}]}]) { uuid node { path } } } }"
284
- }'
285
312
  ```
286
-
287
- Then publish all at once using `mutateNodesByQuery`:
288
-
289
- ```bash
290
- curl -s -u root:root1234 \
291
- -H "Content-Type: application/json" \
292
- -H "Origin: http://localhost:8080" \
293
- -X POST http://localhost:8080/modules/graphql \
294
- -d '{
295
- "query": "mutation { jcr { mutateNodesByQuery(query: \"SELECT * FROM [namespace:docArticle] WHERE ISDESCENDANTNODE(\u0027/sites/mySite/contents/articles\u0027)\", queryLanguage: SQL2) { publish(languages: [\"en\"]) } } }"
296
- }'
313
+ tool: page.create
314
+ args: {
315
+ "parentPath": "/sites/SITE_KEY/home/about",
316
+ "name": "team",
317
+ "title": "Our Team",
318
+ "templateName": "simple",
319
+ "locale": "en"
320
+ }
297
321
  ```
298
322
 
299
- ---
323
+ ### Multiple content items in the same area
324
+
325
+ Call `content.create` multiple times with the same `parentPath`. Items appear in creation order. Use `content.reorder` if you need to rearrange them later.
300
326
 
301
- ## Step 6 — Verify
327
+ ### Reference another node
302
328
 
303
- ```bash
304
- curl -s -u root:root1234 \
305
- -H "Content-Type: application/json" \
306
- -H "Origin: http://localhost:8080" \
307
- -X POST http://localhost:8080/modules/graphql \
308
- -d '{
309
- "query": "{ jcr { nodesByQuery(query: \"SELECT * FROM [namespace:docArticle] WHERE ISDESCENDANTNODE(\u0027/sites/mySite/contents/articles\u0027)\", queryLanguage: SQL2) { nodes { name path properties(names: [\"jcr:title\"], language: \"en\") { name value } } } } }"
310
- }'
329
+ ```
330
+ tool: content.create
331
+ args: {
332
+ "parentPath": "/sites/SITE_KEY/home/my-page/aside",
333
+ "nodeType": "jnt:contentReference",
334
+ "properties": {
335
+ "j:node": "UUID-OF-REFERENCED-NODE"
336
+ }
337
+ }
311
338
  ```
312
339
 
313
340
  ---
@@ -316,69 +343,17 @@ curl -s -u root:root1234 \
316
343
 
317
344
  | Error | Cause | Fix |
318
345
  |-------|-------|-----|
319
- | `Permission denied` | Missing `Origin` header | Add `-H "Origin: http://localhost:8080"` |
320
- | `Couldn't find definition for property X` | Wrong property name or non-i18n prop given with `language:` | Check CND definition; remove `language:` for non-i18n props |
321
- | `ConstraintViolationException: mandatory property` | A mandatory CND property was not provided | Provide all mandatory properties |
322
- | `ItemExistsException` | Node name already taken | Use `useAvailableNodeName: true` or choose a different name |
323
- | WEAKREFERENCE image constraint error | Uploaded file missing `jmix:image` mixin | Always include `mixins: ["jmix:image"]` in the `addNode` upload mutation |
324
- | `deletePropertiesBatch fails with missing required fields` | `language` is NON_NULL in `InputJCRDeletedProperty` — required even for non-i18n properties | Always provide `language: "en"` in every `deletePropertiesBatch` entry |
346
+ | `NODE_TYPE_NOT_ALLOWED` | Type is not droppable in that area | Call `content.list_definitions` |
347
+ | `MANDATORY_PROPERTY_MISSING` | A required property was omitted | Check `content.list_definitions` |
348
+ | `NODE_EXISTS` | Node name already exists | Use a different `nodeName` or omit it |
349
+ | `PATH_NOT_FOUND` | Parent path does not exist | Create the site or page first, then add content |
325
350
 
326
351
  ---
327
352
 
328
- ## Setting `j:linkType` links via GraphQL
329
-
330
- > 🚫 **NEVER use `j:linkType: "external"` to link to an internal Jahia page.** Always use `"internal"` with `j:linknode`. Hardcoding an internal URL as an external link will break on environment changes, language switching, vanity URLs, and live/preview workspace toggling.
331
-
332
- ### Internal link (`j:linkType: "internal"`)
333
-
334
- `j:linknode` is an **internationalized** weakreference. Add the mixin first, then set the property with `language:`.
335
-
336
- ```graphql
337
- # Step 1 — add mixin + set j:linkType
338
- mutation {
339
- jcr {
340
- mutateNode(pathOrId: "/sites/mySite/home/features/my-card") {
341
- addMixins(mixins: ["jmix:internalLink"])
342
- setPropertiesBatch(properties: [
343
- {name: "j:linkType", value: "internal"}
344
- ]) { path }
345
- }
346
- }
347
- }
348
-
349
- # Step 2 — set j:linknode (i18n weakreference — must include language)
350
- mutation {
351
- jcr {
352
- mutateNode(pathOrId: "/sites/mySite/home/features/my-card") {
353
- mutateProperty(name: "j:linknode") {
354
- setValue(value: "<target-node-uuid>", language: "en", type: WEAKREFERENCE)
355
- }
356
- }
357
- }
358
- }
359
- ```
360
-
361
- ### External link (`j:linkType: "external"`)
362
-
363
- ```graphql
364
- mutation {
365
- jcr {
366
- mutateNode(pathOrId: "/sites/mySite/home/features/my-card") {
367
- addMixins(mixins: ["jmix:externalLink"])
368
- setPropertiesBatch(properties: [
369
- {name: "j:linkType", value: "external"}
370
- {name: "j:url", value: "https://example.com", language: "en"}
371
- {name: "j:linkTitle", value: "Visit Example", language: "en"}
372
- ]) { path }
373
- }
374
- }
375
- }
376
- ```
377
-
378
- ---
353
+ ## Related skills
379
354
 
380
- ## References
355
+ - `/jahia-content-explore-structure` — map sites, pages, areas, and properties first
356
+ - `/jahia-content-media-upload` — upload images and files before referencing them
357
+ - `/jahia-content-publish` — publish pages, content, and translations
358
+ - `/jahia-content-organize` — move, rename, reorder, copy, or delete content later
381
359
 
382
- - Jahia GraphQL API playground: `http://localhost:8080/modules/graphql` (GET in browser, POST for queries)
383
- - JCR mutation docs: https://academy.jahia.com/documentation/developer/jahia/8/api-documentation/graphql-api
384
- - Native Jahia node types (CND source): https://github.com/Jahia/jahia/tree/master/war/src/main/webapp/WEB-INF/etc/repository/nodetypes