@craftware/crafty-sdk 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  Official JavaScript SDK for the Crafty public API (current local implementation).
4
4
 
5
+ ## AI agent skill
6
+
7
+ This package ships an agent **skill** that teaches Claude, Cursor, and other agents how to configure a Crafty site through its MCP server and build against it with this SDK. It is included in the published package under `skills/crafty-cms/`.
8
+
9
+ To activate it, copy it into your project's skills directory after installing the SDK:
10
+
11
+ ```bash
12
+ cp -r node_modules/@craftware/crafty-sdk/skills/crafty-cms .claude/skills/
13
+ ```
14
+
15
+ (Use the equivalent skills directory for your agent, e.g. `.cursor/skills/`.) The skill is self-contained — it includes `SKILL.md` plus `references/mcp-configuration.md` and `references/sdk-development.md`.
16
+
5
17
  ## Requirements
6
18
 
7
19
  - Node `>=20` (package engine)
@@ -13,8 +25,8 @@ Official JavaScript SDK for the Crafty public API (current local implementation)
13
25
  import { createCraftyClient } from "@craftware/crafty-sdk";
14
26
 
15
27
  const crafty = createCraftyClient({
16
- baseUrl: "https://tenant.crafty.test",
17
- apiKey: "SITE_API_TOKEN",
28
+ baseUrl: "https://tenant.crafty.do",
29
+ apiKey: "ENVIRONMENT_API_TOKEN",
18
30
  timeoutMs: 10_000
19
31
  });
20
32
  ```
@@ -22,12 +34,13 @@ const crafty = createCraftyClient({
22
34
  ### Client config
23
35
 
24
36
  - `baseUrl` (required): tenant base URL. The SDK appends `/api/v1` automatically if missing.
25
- - `apiKey` (optional): site API token sent as `X-API-Key`.
37
+ - `apiKey` (optional): environment API token sent as `X-API-Key`.
26
38
  - When provided, the API resolves the active site from this token.
27
39
  - `pages.*` requests use this site context automatically (you do not pass `siteSlug`).
40
+ - `siteSlug` (optional): site slug used for public form submissions (`forms.submit()`).
28
41
  - `fetch` (optional): custom fetch implementation.
29
42
  - `headers` (optional): default headers applied to all requests.
30
- - `timeoutMs` (optional): default timeout for all requests.
43
+ - `timeoutMs` (optional): default timeout (ms) for all requests. A timeout is only applied when the value is a number greater than `0`.
31
44
 
32
45
  ## Available resources
33
46
 
@@ -53,18 +66,49 @@ Supported common `query` params:
53
66
  - `crafty.pages.list(query?, options?)`
54
67
  - `crafty.pages.get(pageSlug, options?)`
55
68
 
56
- `pages.*` endpoints are scoped by the `apiKey` configured on the client.
69
+ `pages.*` endpoints are scoped by the environment resolved from `apiKey`.
57
70
 
58
71
  `pages.get()` returns a page response enriched with block helpers:
59
72
 
60
- - `page.blocks` (array alias of `page.data.blocks ?? []`)
73
+ - `page.data` => the raw `CraftyPage`
74
+ - `page.blocks` => all blocks (`CraftyBlock[]`; a non-array `data.blocks` is coerced to `[]`)
61
75
  - `page.block(type)` => first block of that type or `null`
62
- - `page.blocksOf(type)` => all blocks of that type
76
+ - `page.blocksOf(type)` => all blocks of that type (`[]` if none)
63
77
  - `page.hasBlock(type)` => boolean
64
- - `page.requireBlock(type)` => first block or throws
78
+ - `page.requireBlock(type)` => first block, or throws a plain `Error` (not a `CraftyError`)
65
79
  - `page.byType.<type>` => array of blocks for that type
66
80
  - `page.firstByType.<type>` => first block for that type or `null`
67
81
 
82
+ ### `forms`
83
+
84
+ - `crafty.forms.list(options?)` — list all forms (requires API key)
85
+ - `crafty.forms.get(slug, options?)` — get a single form by slug (requires API key)
86
+ - `crafty.forms.submit(formSlug, data, options?)` — submit a public form (no API key needed)
87
+
88
+ The `submit()` method uses a public endpoint that does not require authentication. You must provide `siteSlug` in the client config for submissions to work — calling `submit()` without it throws a synchronous `CraftyError` with code `BAD_REQUEST`:
89
+
90
+ ```ts
91
+ const crafty = createCraftyClient({
92
+ baseUrl: "https://tenant.crafty.do",
93
+ apiKey: "ENVIRONMENT_API_TOKEN",
94
+ siteSlug: "my-site",
95
+ });
96
+
97
+ // List forms (authenticated)
98
+ const forms = await crafty.forms.list();
99
+
100
+ // Get form details (authenticated)
101
+ const contactForm = await crafty.forms.get("contact");
102
+
103
+ // Submit a form (public, no API key sent)
104
+ const result = await crafty.forms.submit("contact", {
105
+ name: "Jane Doe",
106
+ email: "jane@example.com",
107
+ message: "Hello!",
108
+ });
109
+ // result => { id, form_id, submitted_at }
110
+ ```
111
+
68
112
  ### `mediaCollections`
69
113
 
70
114
  - `crafty.mediaCollections.list(query?, options?)`
@@ -72,7 +116,7 @@ Supported common `query` params:
72
116
  Supported `query.include` values:
73
117
  - `"stats"`
74
118
  - `"directories"`
75
- - Array form is supported and serialized as comma-separated values
119
+ - Array form is supported, de-duplicated, and serialized as comma-separated values; a raw string is passed through unchanged
76
120
 
77
121
  ### `mediaFiles`
78
122
 
@@ -108,13 +152,16 @@ const posts = await crafty.entries.list(
108
152
  - `entries.list()` => paginated `{ data, links, meta }`
109
153
  - `entries.get()` => `{ data: CraftyEntry }`
110
154
  - `pages.list()` => paginated `{ data, links, meta }`
111
- - `pages.get()` => `{ data: CraftyPage }`
155
+ - `pages.get()` => `{ data: CraftyPage }` plus the block helpers listed above
112
156
  - `mediaCollections.list()` => `{ data, meta: { includes } }`
113
157
  - `mediaFiles.list()` => `{ data, links, meta }` (`meta` includes `ttl_minutes`)
158
+ - `forms.list()` => `{ data: CraftyForm[] }`
159
+ - `forms.get()` => `{ data: CraftyForm }`
160
+ - `forms.submit()` => `{ id, form_id, submitted_at }`
114
161
 
115
162
  ## Errors
116
163
 
117
- The SDK throws `CraftyError` for HTTP, timeout, abort, and network failures.
164
+ The SDK throws `CraftyError` for HTTP, timeout, abort, network, and configuration failures (e.g. no `fetch` implementation available at construction).
118
165
 
119
166
  ```ts
120
167
  import { CraftyError } from "@craftware/crafty-sdk";
@@ -129,17 +176,17 @@ try {
129
176
  }
130
177
  ```
131
178
 
132
- Current error codes:
179
+ Current error codes (with their HTTP-status mapping):
133
180
 
134
- - `NETWORK_ERROR`
135
- - `TIMEOUT`
136
- - `BAD_REQUEST`
137
- - `UNAUTHORIZED`
138
- - `FORBIDDEN`
139
- - `NOT_FOUND`
140
- - `RATE_LIMITED`
141
- - `SERVER_ERROR`
142
- - `UNKNOWN_ERROR`
181
+ - `NETWORK_ERROR` — network failure, or no `fetch` available at construction
182
+ - `TIMEOUT` — request exceeded `timeoutMs`
183
+ - `BAD_REQUEST` — HTTP 400 or 422
184
+ - `UNAUTHORIZED` — HTTP 401
185
+ - `FORBIDDEN` — HTTP 403
186
+ - `NOT_FOUND` — HTTP 404
187
+ - `RATE_LIMITED` — HTTP 429
188
+ - `SERVER_ERROR` — HTTP 5xx
189
+ - `UNKNOWN_ERROR` — any other / unmapped status
143
190
 
144
191
  ## End-to-end example
145
192
 
@@ -147,8 +194,9 @@ Current error codes:
147
194
  import { createCraftyClient } from "@craftware/crafty-sdk";
148
195
 
149
196
  const crafty = createCraftyClient({
150
- baseUrl: "https://twocare.crafty.test",
197
+ baseUrl: "https://tenant.crafty.do",
151
198
  apiKey: process.env.CRAFTY_API_KEY,
199
+ siteSlug: "my-site",
152
200
  timeoutMs: 10_000
153
201
  });
154
202
 
@@ -157,17 +205,19 @@ const posts = await crafty.entries.list("posts", { per_page: 15 });
157
205
  const mediaCollections = await crafty.mediaCollections.list({ include: ["stats", "directories"] });
158
206
  const mediaFiles = await crafty.mediaFiles.list({ collection: "library", per_page: 24 });
159
207
  const homePage = await crafty.pages.get("home");
208
+ const forms = await crafty.forms.list();
209
+ const submission = await crafty.forms.submit("contact", { name: "Jane", email: "jane@test.com" });
160
210
  ```
161
211
 
162
- ## Pages scoping (site via API key)
212
+ ## Pages scoping (site+environment via API key)
163
213
 
164
214
  `pages` requests no longer require a `siteSlug` parameter. The site is inferred server-side from the
165
215
  `X-API-Key` header (set from `apiKey` in `createCraftyClient()`).
166
216
 
167
217
  ```ts
168
218
  const crafty = createCraftyClient({
169
- baseUrl: "https://tenant.crafty.test",
170
- apiKey: "SITE_API_TOKEN",
219
+ baseUrl: "https://tenant.crafty.do",
220
+ apiKey: "ENVIRONMENT_API_TOKEN",
171
221
  });
172
222
 
173
223
  await crafty.pages.list({ per_page: 12 });
package/dist/client.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { CollectionsResource } from "./resources/collections";
2
2
  import { EntriesResource } from "./resources/entries";
3
+ import { FormsResource } from "./resources/forms";
3
4
  import { MediaCollectionsResource } from "./resources/mediaCollections";
4
5
  import { MediaFilesResource } from "./resources/mediaFiles";
5
6
  import { PagesResource } from "./resources/pages";
@@ -7,6 +8,7 @@ import type { CraftyClientConfig } from "./types/public";
7
8
  export type CraftyClient = {
8
9
  collections: CollectionsResource;
9
10
  entries: EntriesResource;
11
+ forms: FormsResource;
10
12
  mediaCollections: MediaCollectionsResource;
11
13
  mediaFiles: MediaFilesResource;
12
14
  pages: PagesResource;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,MAAM,MAAM,YAAY,GAAG;IACzB,WAAW,EAAE,mBAAmB,CAAC;IACjC,OAAO,EAAE,eAAe,CAAC;IACzB,gBAAgB,EAAE,wBAAwB,CAAC;IAC3C,UAAU,EAAE,kBAAkB,CAAC;IAC/B,KAAK,EAAE,aAAa,CAAC;CACtB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CAU3E"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,MAAM,MAAM,YAAY,GAAG;IACzB,WAAW,EAAE,mBAAmB,CAAC;IACjC,OAAO,EAAE,eAAe,CAAC;IACzB,KAAK,EAAE,aAAa,CAAC;IACrB,gBAAgB,EAAE,wBAAwB,CAAC;IAC3C,UAAU,EAAE,kBAAkB,CAAC;IAC/B,KAAK,EAAE,aAAa,CAAC;CACtB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CAW3E"}
package/dist/client.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { HttpClient } from "./http";
2
2
  import { CollectionsResource } from "./resources/collections";
3
3
  import { EntriesResource } from "./resources/entries";
4
+ import { FormsResource } from "./resources/forms";
4
5
  import { MediaCollectionsResource } from "./resources/mediaCollections";
5
6
  import { MediaFilesResource } from "./resources/mediaFiles";
6
7
  import { PagesResource } from "./resources/pages";
@@ -9,6 +10,7 @@ export function createCraftyClient(config) {
9
10
  return {
10
11
  collections: new CollectionsResource(http),
11
12
  entries: new EntriesResource(http),
13
+ forms: new FormsResource(http),
12
14
  mediaCollections: new MediaCollectionsResource(http),
13
15
  mediaFiles: new MediaFilesResource(http),
14
16
  pages: new PagesResource(http)
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAWlD,MAAM,UAAU,kBAAkB,CAAC,MAA0B;IAC3D,MAAM,IAAI,GAAG,UAAU,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAEjD,OAAO;QACL,WAAW,EAAE,IAAI,mBAAmB,CAAC,IAAI,CAAC;QAC1C,OAAO,EAAE,IAAI,eAAe,CAAC,IAAI,CAAC;QAClC,gBAAgB,EAAE,IAAI,wBAAwB,CAAC,IAAI,CAAC;QACpD,UAAU,EAAE,IAAI,kBAAkB,CAAC,IAAI,CAAC;QACxC,KAAK,EAAE,IAAI,aAAa,CAAC,IAAI,CAAC;KAC/B,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAYlD,MAAM,UAAU,kBAAkB,CAAC,MAA0B;IAC3D,MAAM,IAAI,GAAG,UAAU,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAEjD,OAAO;QACL,WAAW,EAAE,IAAI,mBAAmB,CAAC,IAAI,CAAC;QAC1C,OAAO,EAAE,IAAI,eAAe,CAAC,IAAI,CAAC;QAClC,KAAK,EAAE,IAAI,aAAa,CAAC,IAAI,CAAC;QAC9B,gBAAgB,EAAE,IAAI,wBAAwB,CAAC,IAAI,CAAC;QACpD,UAAU,EAAE,IAAI,kBAAkB,CAAC,IAAI,CAAC;QACxC,KAAK,EAAE,IAAI,aAAa,CAAC,IAAI,CAAC;KAC/B,CAAC;AACJ,CAAC"}
package/dist/http.d.ts CHANGED
@@ -9,6 +9,7 @@ type RequiredTransportConfig = {
9
9
  fetchImpl: typeof fetch;
10
10
  defaultHeaders: HeadersInit | undefined;
11
11
  apiKey: string | undefined;
12
+ siteSlug: string | undefined;
12
13
  timeoutMs: number | undefined;
13
14
  };
14
15
  export declare class HttpClient {
@@ -16,12 +17,19 @@ export declare class HttpClient {
16
17
  private readonly fetchImpl;
17
18
  private readonly defaultHeaders;
18
19
  private readonly apiKey;
20
+ private readonly siteSlug;
19
21
  private readonly defaultTimeoutMs;
20
22
  constructor(config: RequiredTransportConfig);
21
23
  static fromClientConfig(config: CraftyClientConfig): HttpClient;
24
+ getSiteSlug(): string | undefined;
22
25
  get<T>(path: string, options?: HttpRequestOptions): Promise<T>;
26
+ post<T>(path: string, body: unknown, options?: HttpRequestOptions & {
27
+ skipApiKey?: boolean;
28
+ }): Promise<T>;
23
29
  request<T>(path: string, options: HttpRequestOptions & {
24
30
  method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
31
+ body?: unknown;
32
+ skipApiKey?: boolean;
25
33
  }): Promise<T>;
26
34
  private buildUrl;
27
35
  }
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAc,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErF,KAAK,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;AAC/D,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAE9C,KAAK,kBAAkB,GAAG,cAAc,GAAG;IACzC,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,CAAC;AAEF,KAAK,uBAAuB,GAAG;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,KAAK,CAAC;IACxB,cAAc,EAAE,WAAW,GAAG,SAAS,CAAC;IACxC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B,CAAC;AAEF,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA0B;IACzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;gBAEnC,MAAM,EAAE,uBAAuB;WAQpC,gBAAgB,CAAC,MAAM,EAAE,kBAAkB,GAAG,UAAU;IAwBzD,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC;IAI9D,OAAO,CAAC,CAAC,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,kBAAkB,GAAG;QAAE,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAA;KAAE,GACpF,OAAO,CAAC,CAAC,CAAC;IAkFb,OAAO,CAAC,QAAQ;CAejB"}
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAc,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErF,KAAK,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;AAC/D,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAE9C,KAAK,kBAAkB,GAAG,cAAc,GAAG;IACzC,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,CAAC;AAEF,KAAK,uBAAuB,GAAG;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,KAAK,CAAC;IACxB,cAAc,EAAE,WAAW,GAAG,SAAS,CAAC;IACxC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B,CAAC;AAEF,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA0B;IACzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;gBAEnC,MAAM,EAAE,uBAAuB;WASpC,gBAAgB,CAAC,MAAM,EAAE,kBAAkB,GAAG,UAAU;IAyB/D,WAAW,IAAI,MAAM,GAAG,SAAS;IAI3B,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC;IAI9D,IAAI,CAAC,CAAC,EACjB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,EACb,OAAO,CAAC,EAAE,kBAAkB,GAAG;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,GACtD,OAAO,CAAC,CAAC,CAAC;IAcA,OAAO,CAAC,CAAC,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,kBAAkB,GAAG;QAC5B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;QACpD,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,GACA,OAAO,CAAC,CAAC,CAAC;IA2Fb,OAAO,CAAC,QAAQ;CAejB"}
package/dist/http.js CHANGED
@@ -4,12 +4,14 @@ export class HttpClient {
4
4
  fetchImpl;
5
5
  defaultHeaders;
6
6
  apiKey;
7
+ siteSlug;
7
8
  defaultTimeoutMs;
8
9
  constructor(config) {
9
10
  this.apiBaseUrl = config.apiBaseUrl;
10
11
  this.fetchImpl = config.fetchImpl;
11
12
  this.defaultHeaders = config.defaultHeaders;
12
13
  this.apiKey = config.apiKey;
14
+ this.siteSlug = config.siteSlug;
13
15
  this.defaultTimeoutMs = config.timeoutMs;
14
16
  }
15
17
  static fromClientConfig(config) {
@@ -29,12 +31,27 @@ export class HttpClient {
29
31
  fetchImpl,
30
32
  defaultHeaders: config.headers,
31
33
  apiKey: config.apiKey,
34
+ siteSlug: config.siteSlug,
32
35
  timeoutMs: config.timeoutMs
33
36
  });
34
37
  }
38
+ getSiteSlug() {
39
+ return this.siteSlug;
40
+ }
35
41
  async get(path, options) {
36
42
  return this.request(path, { ...options, method: "GET" });
37
43
  }
44
+ async post(path, body, options) {
45
+ const requestOptions = {
46
+ ...options,
47
+ method: "POST",
48
+ body
49
+ };
50
+ if (options?.skipApiKey) {
51
+ requestOptions.skipApiKey = true;
52
+ }
53
+ return this.request(path, requestOptions);
54
+ }
38
55
  async request(path, options) {
39
56
  const url = this.buildUrl(path, options.query);
40
57
  const headers = new Headers(this.defaultHeaders);
@@ -45,9 +62,12 @@ export class HttpClient {
45
62
  if (!headers.has("Accept")) {
46
63
  headers.set("Accept", "application/json");
47
64
  }
48
- if (this.apiKey && !headers.has("X-API-Key")) {
65
+ if (this.apiKey && !headers.has("X-API-Key") && !options.skipApiKey) {
49
66
  headers.set("X-API-Key", this.apiKey);
50
67
  }
68
+ if (options.body !== undefined && !headers.has("Content-Type")) {
69
+ headers.set("Content-Type", "application/json");
70
+ }
51
71
  const timeoutMs = options.timeoutMs ?? this.defaultTimeoutMs;
52
72
  const timeoutController = typeof timeoutMs === "number" && timeoutMs > 0
53
73
  ? new AbortController()
@@ -58,11 +78,15 @@ export class HttpClient {
58
78
  timeoutHandle = setTimeout(() => timeoutController.abort("timeout"), timeoutMs);
59
79
  }
60
80
  try {
61
- const response = await this.fetchImpl(url, {
81
+ const fetchInit = {
62
82
  method: options.method,
63
83
  headers,
64
84
  signal: signal ?? null
65
- });
85
+ };
86
+ if (options.body !== undefined) {
87
+ fetchInit.body = JSON.stringify(options.body);
88
+ }
89
+ const response = await this.fetchImpl(url, fetchInit);
66
90
  const payload = await parseResponsePayload(response);
67
91
  if (!response.ok) {
68
92
  const requestId = response.headers.get("x-request-id") ?? undefined;
package/dist/http.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { HttpClient } from "../http";
2
+ import type { FormListResponse, FormShowResponse, FormSubmitData, FormSubmitResponse, RequestOptions } from "../types/public";
3
+ export declare class FormsResource {
4
+ private readonly http;
5
+ constructor(http: HttpClient);
6
+ list(options?: RequestOptions): Promise<FormListResponse>;
7
+ get(slug: string, options?: RequestOptions): Promise<FormShowResponse>;
8
+ submit(formSlug: string, data: FormSubmitData, options?: RequestOptions): Promise<FormSubmitResponse>;
9
+ }
10
+ //# sourceMappingURL=forms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forms.d.ts","sourceRoot":"","sources":["../../src/resources/forms.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,cAAc,EACf,MAAM,iBAAiB,CAAC;AAEzB,qBAAa,aAAa;IACL,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,IAAI,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAIzD,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAItE,MAAM,CACX,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,cAAc,EACpB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,kBAAkB,CAAC;CAgB/B"}
@@ -0,0 +1,24 @@
1
+ import { CraftyError } from "../errors";
2
+ export class FormsResource {
3
+ http;
4
+ constructor(http) {
5
+ this.http = http;
6
+ }
7
+ list(options) {
8
+ return this.http.get("/forms", options);
9
+ }
10
+ get(slug, options) {
11
+ return this.http.get(`/forms/${encodeURIComponent(slug)}`, options);
12
+ }
13
+ submit(formSlug, data, options) {
14
+ const siteSlug = this.http.getSiteSlug();
15
+ if (!siteSlug) {
16
+ throw new CraftyError({
17
+ code: "BAD_REQUEST",
18
+ message: "siteSlug is required in client config to submit forms."
19
+ });
20
+ }
21
+ return this.http.post(`/sites/${encodeURIComponent(siteSlug)}/forms/${encodeURIComponent(formSlug)}/submit`, data, { ...options, skipApiKey: true });
22
+ }
23
+ }
24
+ //# sourceMappingURL=forms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forms.js","sourceRoot":"","sources":["../../src/resources/forms.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAUxC,MAAM,OAAO,aAAa;IACY;IAApC,YAAoC,IAAgB;QAAhB,SAAI,GAAJ,IAAI,CAAY;IAAG,CAAC;IAEjD,IAAI,CAAC,OAAwB;QAClC,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAmB,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC5D,CAAC;IAEM,GAAG,CAAC,IAAY,EAAE,OAAwB;QAC/C,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAmB,UAAU,kBAAkB,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IACxF,CAAC;IAEM,MAAM,CACX,QAAgB,EAChB,IAAoB,EACpB,OAAwB;QAExB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QAEzC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,WAAW,CAAC;gBACpB,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,wDAAwD;aAClE,CAAC,CAAC;QACL,CAAC;QAED,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CACnB,UAAU,kBAAkB,CAAC,QAAQ,CAAC,UAAU,kBAAkB,CAAC,QAAQ,CAAC,SAAS,EACrF,IAAI,EACJ,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CACjC,CAAC;IACJ,CAAC;CACF"}
@@ -141,12 +141,45 @@ export type MediaFilesListQuery = {
141
141
  per_page?: number;
142
142
  page?: number;
143
143
  };
144
+ export type CraftyForm = {
145
+ id: number;
146
+ name: string;
147
+ slug: string;
148
+ description: string | null;
149
+ fields: Record<string, CraftyFormField>;
150
+ status: "draft" | "published";
151
+ hcaptcha_enabled: boolean;
152
+ created_at: string;
153
+ updated_at: string;
154
+ };
155
+ export type CraftyFormField = {
156
+ label: string;
157
+ type: "text" | "textarea" | "email" | "tel" | "number" | "select" | "checkbox" | "radio" | "date";
158
+ required: boolean;
159
+ options?: string[];
160
+ order: number;
161
+ };
162
+ export type FormListResponse = {
163
+ data: CraftyForm[];
164
+ };
165
+ export type FormShowResponse = SingleResponse<CraftyForm>;
166
+ export type FormSubmitData = Record<string, unknown>;
167
+ export type FormSubmitResponse = {
168
+ id: number;
169
+ form_id: number;
170
+ submitted_at: string;
171
+ };
144
172
  export type CraftyClientConfig = {
145
173
  baseUrl: string;
146
174
  /**
147
- * Site API token. The SDK sends it as `X-API-Key: <token>`.
175
+ * Environment API token. The SDK sends it as `X-API-Key: <token>`.
176
+ * The backend resolves site + environment from this key.
148
177
  */
149
178
  apiKey?: string;
179
+ /**
180
+ * Site slug, required for public endpoints like form submissions.
181
+ */
182
+ siteSlug?: string;
150
183
  fetch?: typeof fetch;
151
184
  headers?: HeadersInit;
152
185
  timeoutMs?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/types/public.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/types/public.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@craftware/crafty-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -18,7 +18,8 @@
18
18
  "module": "./dist/index.js",
19
19
  "types": "./dist/index.d.ts",
20
20
  "files": [
21
- "dist"
21
+ "dist",
22
+ "skills"
22
23
  ],
23
24
  "engines": {
24
25
  "node": ">=20"
@@ -0,0 +1,139 @@
1
+ ---
2
+ name: crafty-cms
3
+ description: "Build on Crafty CMS — configure sites, pages, blocks, collections, entries, forms, and media through the Crafty MCP server, then develop a frontend with the @craftware/crafty-sdk JavaScript SDK. Use when connecting an agent to a Crafty MCP server; calling Crafty MCP tools like list_sites, create_page, publish_form, or upload_media; installing or importing @craftware/crafty-sdk or createCraftyClient; fetching Crafty pages, entries, collections, or media; rendering page blocks; or submitting Crafty forms."
4
+ license: MIT
5
+ metadata:
6
+ author: craftware
7
+ ---
8
+
9
+ # Crafty CMS (MCP + JavaScript SDK)
10
+
11
+ Crafty is a headless, multi-tenant CMS. There are two integration surfaces, and they are used at different stages:
12
+
13
+ | Surface | Purpose | Auth | Use it to |
14
+ |---|---|---|---|
15
+ | **MCP server** | Author and configure content | Bearer token (PAT or OAuth), scoped | Create sites, pages, blocks, collections, entries, forms; upload media; publish |
16
+ | **`@craftware/crafty-sdk`** | Read published content into an app | `X-API-Key` environment token | Fetch pages/blocks, entries, collections, media; submit forms from a site |
17
+
18
+ The normal flow is **configure with the MCP server → build a frontend with the SDK**.
19
+
20
+ ## When to Use
21
+
22
+ - Connecting Claude, Cursor, VS Code, Codex, or ChatGPT to a Crafty tenant's MCP server.
23
+ - Driving Crafty MCP tools (`list_sites`, `create_page`, `create_entry`, `create_form`, `publish_page`, `upload_media`, …).
24
+ - Installing or importing `@craftware/crafty-sdk` / `createCraftyClient`.
25
+ - Fetching Crafty pages, entries, collections, or media in app code, or submitting a Crafty form.
26
+
27
+ ## Scope
28
+
29
+ - **In scope:** using a *deployed* Crafty tenant via its MCP server and the published SDK.
30
+ - **Out of scope:** building or modifying the Crafty backend itself, the SDK's internals, or the publish pipeline (that's the `publish-sdk` skill).
31
+
32
+ ## Domain model
33
+
34
+ - **Site** — top-level tenant resource. Everything else belongs to a site.
35
+ - **Page** — a web page in a site; `draft` or `published`; composed of ordered **Blocks**.
36
+ - **Block** — a content unit on a page; its **BlockType** defines the data schema.
37
+ - **Collection** — a structured content schema (e.g. "Blog Posts"); defines the fields **Entries** must have.
38
+ - **Entry** — one record in a collection (e.g. a single post). Belongs to a site and a collection.
39
+ - **Form** — a contact/feedback form in a site; `draft` or `published`. Published forms accept **public** submissions without auth.
40
+ - **Form Submission** — visitor-submitted data; read-only after creation.
41
+ - **Media** — files attached to a site via the Spatie Media Library.
42
+
43
+ ## Phase 1 — Configure content via the MCP server
44
+
45
+ Connect once, then drive the tools. Minimal connect with a Personal Access Token (Claude Code shown):
46
+
47
+ ```bash
48
+ claude mcp add crafty \
49
+ --transport http \
50
+ --url https://<your-tenant-domain>/mcp \
51
+ --header "Authorization: Bearer YOUR_PERSONAL_ACCESS_TOKEN"
52
+ ```
53
+
54
+ The server URL is **per-tenant**: `https://<your-tenant-domain>/mcp`. Tokens carry scopes — `crafty:read`, `crafty:write`, `crafty:publish`, and `crafty:submissions` (reading form submissions, which may contain personal data) — and they are **enforced per tool** (a tool without its scope is hidden and errors if called). Request the scopes the agent needs. **Full connection guide for every client (PAT and OAuth), scopes, and the complete 25-tool catalog: read `references/mcp-configuration.md`.**
55
+
56
+ Canonical authoring workflow (exact tool names):
57
+
58
+ 1. `list_sites` → pick a `site_id` (or `create_site`).
59
+ 2. `list_block_types` → learn the available block types and their data schemas.
60
+ 3. `create_page` with `site_id`, `title`, and optional `blocks: [{ block_type_id, data }]`.
61
+ 4. `publish_page`.
62
+
63
+ Collections/entries: `list_collections` → `create_entry` (`collection_slug`, `site_id`, `slug`, `data`) → `update_entry` (snapshots a revision).
64
+ Forms: `create_form` (`site_id`, `name`, `slug`, `fields`) → `publish_form` → `list_form_submissions`.
65
+ Media: `upload_media` (`url`, `site_id`, optional `collection_name`).
66
+
67
+ > `update_page` **replaces all blocks** when `blocks` is supplied. `update_entry` creates a revision. `create_page` always starts as `draft`.
68
+
69
+ ## Phase 2 — Build a frontend with the SDK
70
+
71
+ ```bash
72
+ npm install @craftware/crafty-sdk
73
+ ```
74
+
75
+ ```ts
76
+ import { createCraftyClient } from "@craftware/crafty-sdk";
77
+
78
+ const crafty = createCraftyClient({
79
+ baseUrl: "https://<your-tenant>.crafty.do", // SDK appends /api/v1
80
+ apiKey: process.env.CRAFTY_API_KEY, // environment API token (sent as X-API-Key)
81
+ siteSlug: "main-site", // required ONLY for forms.submit()
82
+ });
83
+
84
+ // pages are scoped by the site the apiKey resolves to — no siteSlug argument
85
+ const page = await crafty.pages.get("home");
86
+ const hero = page.block("hero"); // first block of that type, or null
87
+ const posts = await crafty.entries.list("posts", { per_page: 15 });
88
+ ```
89
+
90
+ `pages.get()` returns the page enriched with block helpers (`page.block(type)`, `page.blocksOf(type)`, `page.byType`, …). **Full client config, every resource and return shape, block helpers, errors, and an end-to-end example: read `references/sdk-development.md`.**
91
+
92
+ ## Phase 3 — Deploy a static site with the CLI
93
+
94
+ ```bash
95
+ npm install -D @craftware/crafty-cli
96
+ ```
97
+
98
+ Add to `.env` (the CLI reads this at deploy time, separate from `VITE_*` vars):
99
+
100
+ ```
101
+ CRAFTY_API_URL=https://<your-tenant>.crafty.do/api/v1
102
+ ```
103
+
104
+ Add `crafty.json` at the project root:
105
+
106
+ ```json
107
+ { "site": "<site-slug>", "environment": "production", "buildDir": "dist" }
108
+ ```
109
+
110
+ Then build and deploy:
111
+
112
+ ```bash
113
+ npm run build # produces dist/
114
+ crafty deploy # uploads dist/ to the configured environment
115
+ ```
116
+
117
+ > `crafty.json` is safe to commit. `.env` (which contains your API key) must be gitignored — use `.env.example` with placeholder values.
118
+
119
+ ## Key distinction: MCP auth vs SDK API key
120
+
121
+ These are two different credentials — do not mix them:
122
+
123
+ - **MCP** uses a **Bearer** token (Personal Access Token or OAuth access token) with `crafty:*` scopes. Used by an agent/editor to *author* content.
124
+ - **SDK** uses an **environment API token** sent as the `X-API-Key` header. It resolves the site + environment server-side, so `pages.*` need no `siteSlug`. Used by app code to *read* published content.
125
+
126
+ ## Common pitfalls
127
+
128
+ - **Wrong tenant host** → the MCP endpoint won't resolve. Use the exact `https://<tenant-domain>/mcp`; each tenant has its own endpoint.
129
+ - **401 from the MCP server** → the Bearer token is missing, invalid, or expired; reconnect or regenerate it.
130
+ - **A tool is missing or fails with "Insufficient OAuth scope"** → the token lacks that tool's scope; re-issue it with `crafty:write` / `crafty:publish` / `crafty:submissions` as needed.
131
+ - **`forms.submit()` throws `BAD_REQUEST`** → `siteSlug` was not set in `createCraftyClient()`. Submissions hit the public endpoint and deliberately omit the API key.
132
+ - **`update_entry` / `update_page` wipe fields you don't send** → both MCP tools do a **full replace**, not a merge. If you send only the new fields, all existing fields are deleted in that revision. Always fetch the current entry first (`get_entry`), merge your changes into the full field map, then call `update_entry` with the complete merged object.
133
+ - **Content not visible via the SDK** → it isn't published. Pages/forms must be published (`publish_page` / `publish_form`).
134
+ - **Confusing the two tokens** → the SDK `apiKey` (X-API-Key) is not an MCP Bearer token.
135
+
136
+ ## References
137
+
138
+ - `references/mcp-configuration.md` — connecting every client, scopes, and the full MCP tool catalog with workflows.
139
+ - `references/sdk-development.md` — the complete SDK surface, block helpers, error handling, and examples.
@@ -0,0 +1,183 @@
1
+ # Configuring Crafty via the MCP server
2
+
3
+ Use this when an agent should **author and configure** content in a Crafty tenant: sites, pages/blocks, collections/entries, forms, media, and publishing.
4
+
5
+ ## Server URL
6
+
7
+ The MCP endpoint is **per-tenant** and lives at the `/mcp` path on the tenant's own domain:
8
+
9
+ ```
10
+ https://<your-tenant-domain>/mcp
11
+ ```
12
+
13
+ The tenant is identified by the request **host**, so each tenant has its own endpoint. Hitting the wrong host fails to resolve the tenant. Every request must be authenticated (Bearer token).
14
+
15
+ ## Authentication
16
+
17
+ Two families, both carrying a **Bearer** token on every request:
18
+
19
+ ### Personal Access Token (PAT) — simplest
20
+
21
+ Generate a long-lived Personal Access Token from your Crafty profile, choosing the scopes you need, then paste it into the editor config as an `Authorization: Bearer …` header. Best for CLIs and local setups. Regenerate and update the config if it stops working.
22
+
23
+ ### OAuth 2.0 — via an MCP client
24
+
25
+ Create an "MCP client" in your Crafty profile and pick a grant type:
26
+
27
+ - **Authorization Code** — standard browser-redirect flow for tools that open a browser (Claude.ai, Cursor, VS Code). Select the redirect URI matching your tool.
28
+ - **Device Code (RFC 8628)** — for CLIs that can't open a browser; the editor shows a code in the terminal that you approve in the browser.
29
+
30
+ When you create the client manually in your profile you are shown the `client_id` and a **one-time** `client_secret` — copy it immediately (PATs likewise show the token once). Clients that **auto-register** through discovery (e.g. Claude.ai, Cursor) are registered as public clients and receive no client secret.
31
+
32
+ OAuth clients that support MCP auto-discovery (Claude.ai, Cursor) can find everything from the server URL alone: discovery is published at `/.well-known/oauth-protected-resource` and `/.well-known/oauth-authorization-server`, with `authorization_endpoint` `/oauth/authorize`, `token_endpoint` `/oauth/token`, dynamic `registration_endpoint` `/oauth/register`, PKCE (`S256`), and grants `authorization_code` + `refresh_token`. These OAuth endpoints live at the tenant **root**, not under `/mcp`.
33
+
34
+ ## Scopes
35
+
36
+ When you create a token (PAT or OAuth client) you grant one or more scopes. Scopes are **enforced per tool**: a tool is only advertised when your token carries its required scope, and invoking one without the scope fails with an MCP error (`Insufficient OAuth scope`). Grant the scopes the agent needs:
37
+
38
+ | Scope | Unlocks | Tools |
39
+ |---|---|---|
40
+ | `crafty:read` | Read sites, pages, block types, collections, entries, media, and form definitions | all `list_*` / `get_*` except form submissions |
41
+ | `crafty:write` | Create and update sites, pages, entries, media, and forms | all `create_*` / `update_*`, `upload_media` |
42
+ | `crafty:publish` | Publish and unpublish pages and forms | `publish_page`, `unpublish_page`, `publish_form`, `unpublish_form` |
43
+ | `crafty:submissions` | Read form submissions (may contain personal data) | `list_form_submissions`, `get_form_submission` |
44
+
45
+ Request all four for full authoring plus submission access. Scopes are also advertised in OAuth discovery; the `/mcp` endpoint additionally requires a valid authenticated token on every request.
46
+
47
+ ## Connecting each client
48
+
49
+ Replace `https://<tenant>/mcp` with your tenant URL and `YOUR_PAT` with a Personal Access Token.
50
+
51
+ **Claude.ai (Authorization Code):** create an Authorization Code MCP client with redirect URI `https://claude.ai/api/mcp/auth_callback`; in Claude.ai go to **Settings → Integrations → Add integration**, enter the server URL, and authorize in the browser.
52
+
53
+ **Claude Code (CLI):**
54
+
55
+ ```bash
56
+ # Option A — PAT
57
+ claude mcp add crafty --transport http \
58
+ --url https://<tenant>/mcp \
59
+ --header "Authorization: Bearer YOUR_PAT"
60
+
61
+ # Option B — Device Code (create a Device Code MCP client first)
62
+ claude mcp add crafty --transport http \
63
+ --url https://<tenant>/mcp \
64
+ --oauth2 device_code \
65
+ --client-id YOUR_CLIENT_ID \
66
+ --client-secret YOUR_CLIENT_SECRET
67
+ ```
68
+
69
+ **Cursor (Authorization Code):** create an Authorization Code client with redirect URI `cursor://anysphere.cursor-deeplink/mcp/auth_callback`, then add to `~/.cursor/mcp.json` and authorize when prompted:
70
+
71
+ ```json
72
+ { "mcpServers": { "crafty": { "url": "https://<tenant>/mcp" } } }
73
+ ```
74
+
75
+ **VS Code (GitHub Copilot, PAT):** add to `.vscode/mcp.json`, then open Copilot Chat → **Agent** mode and select the Crafty tools. (OAuth alternative: Authorization Code client with redirect URI `vscode://github.copilot-chat/mcp/auth_callback`.)
76
+
77
+ ```json
78
+ { "servers": { "crafty": { "type": "http", "url": "https://<tenant>/mcp",
79
+ "headers": { "Authorization": "Bearer YOUR_PAT" } } } }
80
+ ```
81
+
82
+ **Codex CLI (`~/.codex/config.yaml`):**
83
+
84
+ ```yaml
85
+ # Option A — PAT
86
+ mcp_servers:
87
+ crafty:
88
+ type: sse
89
+ url: https://<tenant>/mcp
90
+ headers:
91
+ Authorization: "Bearer YOUR_PAT"
92
+ # Option B — Device Code: add
93
+ # auth: { type: device_code, client_id: ..., client_secret: ...,
94
+ # token_url: https://<tenant>/oauth/token,
95
+ # device_authorization_url: https://<tenant>/oauth/device/code }
96
+ ```
97
+
98
+ **ChatGPT (Authorization Code, Plus/Pro/Enterprise):** create an Authorization Code client with redirect URI `https://chatgpt.com/aip/g-*/oauth/callback`; in **Explore GPTs → Create → Configure → Actions** enter the server URL and configure OAuth with Authorization URL `/oauth/authorize` and Token URL `/oauth/token`.
99
+
100
+ ## Tool catalog
101
+
102
+ All tools return a JSON text response. IDs are integers; slugs are strings. Pagination defaults to 25 per page (`per_page` max 100 where supported).
103
+
104
+ ### Read (`crafty:read`)
105
+
106
+ | Tool | Required | Optional | Returns |
107
+ |---|---|---|---|
108
+ | `list_sites` | — | — | all sites (id, name, slug, domain, is_active) |
109
+ | `get_site` | `site_id` | — | one site |
110
+ | `list_pages` | `site_id` | `status` (`draft`/`published`) | paginated pages, ordered by title |
111
+ | `get_page` | `page_id` | — | page with its blocks (incl. block types) |
112
+ | `list_block_types` | — | — | all block types and their schemas |
113
+ | `list_collections` | — | `site_id` | active collections |
114
+ | `list_entries` | `collection_slug` | `site_id`, `per_page` | paginated entries (newest id first) |
115
+ | `get_entry` | `entry_id` | — | one entry with its collection |
116
+ | `list_media` | `site_id` | `collection`, `per_page` | paginated media for the site |
117
+ | `list_forms` | — | `site_id`, `status`, `per_page` | paginated forms, ordered by name |
118
+ | `get_form` | `form_id` | — | one form with its field definitions |
119
+
120
+ ### Submissions (`crafty:submissions`)
121
+
122
+ Form submissions may contain personal data, so they require their own scope.
123
+
124
+ | Tool | Required | Optional | Returns |
125
+ |---|---|---|---|
126
+ | `list_form_submissions` | `form_slug` | `site_id`, `per_page` | paginated submissions, newest first |
127
+ | `get_form_submission` | `form_slug`, `submission_id` | `site_id` | one submission with its data |
128
+
129
+ ### Write (`crafty:write`)
130
+
131
+ | Tool | Required | Optional | Notes |
132
+ |---|---|---|---|
133
+ | `create_site` | `name` | `slug` | slug auto-generated from name; returns the site (with a generated `api_token`) |
134
+ | `create_page` | `site_id`, `title` | `slug`, `blocks` | starts as `draft`; `blocks: [{ block_type_id, data }]` created in order |
135
+ | `update_page` | `page_id` | `title`, `slug`, `blocks` | **if `blocks` is supplied, ALL existing blocks are replaced** |
136
+ | `create_entry` | `collection_slug`, `site_id`, `slug` | `data` | `data` = field key/value pairs |
137
+ | `update_entry` | `entry_id`, `data` | `slug` | snapshots the prior data into a new revision |
138
+ | `upload_media` | `url`, `site_id` | `collection_name` (default `library`), `file_name` | downloads the file from the URL |
139
+ | `create_form` | `site_id`, `name`, `slug` | `description`, `fields` | `fields: [{ key, type, label, required? }]`; starts as `draft` |
140
+ | `update_form` | `form_id` | `name`, `slug`, `description`, `fields`, `notification_emails` | only supplied fields change; `fields`/`notification_emails` replace |
141
+
142
+ ### Publish (`crafty:publish`)
143
+
144
+ | Tool | Required | Effect |
145
+ |---|---|---|
146
+ | `publish_page` | `page_id` | status → `published`, sets `published_at` |
147
+ | `unpublish_page` | `page_id` | status → `draft`, clears `published_at` |
148
+ | `publish_form` | `form_id` | status → `published` (now accepts public submissions) |
149
+ | `unpublish_form` | `form_id` | status → `draft` (stops accepting submissions) |
150
+
151
+ ## Canonical workflows
152
+
153
+ **Build and publish a page**
154
+
155
+ 1. `list_sites` → choose a `site_id` (or `create_site`).
156
+ 2. `list_block_types` → note each `id` and its data schema.
157
+ 3. `create_page` with `site_id`, `title`, and `blocks: [{ block_type_id, data }, …]`.
158
+ 4. `publish_page` with the returned `page_id`.
159
+
160
+ **Structured content**
161
+
162
+ 1. `list_collections` (optionally `site_id`) → pick a `collection_slug`.
163
+ 2. `create_entry` with `collection_slug`, `site_id`, `slug`, and `data`.
164
+ 3. `update_entry` to edit (each edit stores a revision).
165
+
166
+ **Forms**
167
+
168
+ 1. `create_form` with `site_id`, `name`, `slug`, and `fields`.
169
+ 2. `publish_form` so it accepts public submissions.
170
+ 3. `list_form_submissions` / `get_form_submission` to read what visitors sent.
171
+
172
+ **Media**
173
+
174
+ 1. `upload_media` with `url` and `site_id` (optionally `collection_name`).
175
+ 2. `list_media` to confirm and retrieve URLs.
176
+
177
+ ## Troubleshooting
178
+
179
+ - **401 / authentication error** — the Bearer token is missing, invalid, or expired; reconnect or regenerate it. Every `/mcp` request must carry a valid token.
180
+ - **A tool is missing, or fails with `Insufficient OAuth scope`** — the token lacks that tool's scope. Tools are only advertised when the token carries the matching scope (`crafty:write` to create/edit, `crafty:publish` to publish, `crafty:submissions` to read form submissions). Re-issue the token with the scopes you need.
181
+ - **Tenant not found** — wrong host; use the exact tenant domain. OAuth token endpoints are at the tenant root (`/oauth/...`), not under `/mcp`.
182
+ - **Device code never appears** — the MCP client must be created as type **Device Code**, with matching `client_id`/`client_secret`.
183
+ - **Token/secret lost** — `client_secret` and PATs are shown only once at creation; regenerate if lost.
@@ -0,0 +1,198 @@
1
+ # Developing with `@craftware/crafty-sdk`
2
+
3
+ Use this when writing application code that **reads published Crafty content** (pages/blocks, entries, collections, media) or **submits forms**. Authoring/configuration is done via the MCP server (see `mcp-configuration.md`), not the SDK.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @craftware/crafty-sdk
9
+ ```
10
+
11
+ Requires Node `>=20`, or any runtime with `fetch` (browser, Bun, Deno, modern edge runtimes).
12
+
13
+ ## Create a client
14
+
15
+ ```ts
16
+ import { createCraftyClient } from "@craftware/crafty-sdk";
17
+
18
+ const crafty = createCraftyClient({
19
+ baseUrl: "https://<your-tenant>.crafty.do",
20
+ apiKey: process.env.CRAFTY_API_KEY,
21
+ siteSlug: "main-site",
22
+ timeoutMs: 10_000,
23
+ });
24
+ ```
25
+
26
+ `createCraftyClient(config)` returns a plain object with six resources: `collections`, `entries`, `pages`, `forms`, `mediaCollections`, `mediaFiles`.
27
+
28
+ ### Config options
29
+
30
+ | Option | Required | Notes |
31
+ |---|---|---|
32
+ | `baseUrl` | **yes** | Tenant base URL. Trailing slashes are stripped; `/api/v1` is appended automatically unless already present. |
33
+ | `apiKey` | no | Environment API token. Sent as `X-API-Key`. Resolves the site + environment server-side, so `pages.*` need no `siteSlug`. |
34
+ | `siteSlug` | no | Site slug. **Required for `forms.submit()`** (the public submit path is `/sites/{siteSlug}/forms/{formSlug}/submit`). |
35
+ | `fetch` | no | Custom fetch. Defaults to `globalThis.fetch`. If none is available, construction throws `CraftyError` (`NETWORK_ERROR`). |
36
+ | `headers` | no | Default headers merged into every request. |
37
+ | `timeoutMs` | no | Default per-request timeout. A timeout is only armed when the value is a number `> 0`. |
38
+
39
+ ## Per-call options
40
+
41
+ Every method takes an optional final `options` argument:
42
+
43
+ - `signal: AbortSignal` — merged with the internal timeout signal.
44
+ - `headers: HeadersInit` — merged over the client's default headers.
45
+ - `timeoutMs: number` — overrides the client timeout for this call.
46
+
47
+ ```ts
48
+ const posts = await crafty.entries.list(
49
+ "posts",
50
+ { per_page: 15, page: 1, sort_by: "updated_at", sort_order: "desc" },
51
+ { timeoutMs: 5_000 }
52
+ );
53
+ ```
54
+
55
+ ## Resources
56
+
57
+ ### `collections`
58
+
59
+ ```ts
60
+ crafty.collections.list(options?) // => { data: CraftyCollection[] }
61
+ crafty.collections.get(slug, options?) // => CraftyCollection (BARE object, not wrapped)
62
+ ```
63
+
64
+ ### `entries`
65
+
66
+ ```ts
67
+ crafty.entries.list(collectionSlug, query?, options?) // => { data: CraftyEntry[], links, meta } (paginated)
68
+ crafty.entries.get(collectionSlug, id, options?) // => { data: CraftyEntry }
69
+ ```
70
+
71
+ `query`: `sort_by`, `sort_order` (`asc`/`desc`), `per_page`, `page`, plus any extra key/value filters passed through to the API.
72
+
73
+ ### `pages`
74
+
75
+ ```ts
76
+ crafty.pages.list(query?, options?) // => { data: CraftyPage[], links, meta } (paginated)
77
+ crafty.pages.get(pageSlug, options?) // => enriched page (see below)
78
+ ```
79
+
80
+ Pages are scoped server-side by the site the `apiKey` resolves to — there is **no `siteSlug` argument**. `query` accepts `per_page` and `page`.
81
+
82
+ `pages.get()` resolves to `{ data: CraftyPage }` **plus** block helpers:
83
+
84
+ | Helper | Returns |
85
+ |---|---|
86
+ | `page.blocks` | `CraftyBlock[]` (all blocks; non-array data coerced to `[]`) |
87
+ | `page.block(type)` | first block of `type`, or `null` |
88
+ | `page.blocksOf(type)` | all blocks of `type` (`[]` if none) |
89
+ | `page.hasBlock(type)` | `boolean` |
90
+ | `page.requireBlock(type)` | first block, or **throws a plain `Error`** (`Crafty page block not found: …`) — not a `CraftyError` |
91
+ | `page.byType` | `Record<type, CraftyBlock[]>` |
92
+ | `page.firstByType` | `Record<type, CraftyBlock \| null>` |
93
+ | `page.data` | the raw `CraftyPage` |
94
+
95
+ ```ts
96
+ const landing = await crafty.pages.get("landing");
97
+ const hero = landing.block("hero"); // CraftyBlock | null
98
+ const features = landing.blocksOf("feature"); // CraftyBlock[]
99
+ const cta = landing.requireBlock("cta"); // throws plain Error if absent
100
+ ```
101
+
102
+ ### `forms`
103
+
104
+ ```ts
105
+ crafty.forms.list(options?) // => { data: CraftyForm[] } (requires apiKey)
106
+ crafty.forms.get(slug, options?) // => { data: CraftyForm } (requires apiKey)
107
+ crafty.forms.submit(formSlug, data, options?) // => { id, form_id, submitted_at } (public, no apiKey)
108
+ ```
109
+
110
+ `forms.submit()` uses a **public** endpoint: it sends no `X-API-Key`, but it **requires `siteSlug` in the client config** — calling it without one throws a synchronous `CraftyError` with code `BAD_REQUEST`.
111
+
112
+ ```ts
113
+ const crafty = createCraftyClient({
114
+ baseUrl: "https://<your-tenant>.crafty.do",
115
+ siteSlug: "main-site", // required for submit()
116
+ });
117
+
118
+ const result = await crafty.forms.submit("contact", {
119
+ name: "Jane Doe",
120
+ email: "jane@example.com",
121
+ message: "Hello!",
122
+ });
123
+ // => { id, form_id, submitted_at }
124
+ ```
125
+
126
+ > Note the asymmetry: `collections.get()` returns the bare object, while `forms.get()` and `entries.get()` are wrapped in `{ data }`.
127
+
128
+ ### `mediaCollections`
129
+
130
+ ```ts
131
+ crafty.mediaCollections.list(query?, options?) // => { data: CraftyMediaCollection[], meta: { includes } }
132
+ ```
133
+
134
+ `query.include` accepts `"stats"`, `"directories"`, or an array of them (de-duplicated and serialized comma-separated).
135
+
136
+ ### `mediaFiles`
137
+
138
+ ```ts
139
+ crafty.mediaFiles.list(query?, options?) // => { data: CraftyMediaFile[], links, meta: { ...pagination, ttl_minutes } }
140
+ ```
141
+
142
+ `query`: `collection`, `directory_id`, `ttl_minutes` (signed-URL lifetime), `per_page`, `page`.
143
+
144
+ ## Error handling
145
+
146
+ The SDK throws `CraftyError` for HTTP, timeout, abort, network, and config failures.
147
+
148
+ ```ts
149
+ import { CraftyError } from "@craftware/crafty-sdk";
150
+
151
+ try {
152
+ await crafty.pages.get("home");
153
+ } catch (error) {
154
+ if (error instanceof CraftyError) {
155
+ console.error(error.code, error.status, error.message, error.requestId);
156
+ }
157
+ }
158
+ ```
159
+
160
+ Codes: `NETWORK_ERROR`, `TIMEOUT`, `BAD_REQUEST` (HTTP 400 **and** 422), `UNAUTHORIZED` (401), `FORBIDDEN` (403), `NOT_FOUND` (404), `RATE_LIMITED` (429), `SERVER_ERROR` (5xx), `UNKNOWN_ERROR` (any other / unmapped status). `error.requestId` is populated when the API returns an `x-request-id` header.
161
+
162
+ > `page.requireBlock()` throws a plain `Error`, not a `CraftyError` — handle it separately.
163
+
164
+ ## End-to-end example
165
+
166
+ ```ts
167
+ import { createCraftyClient, CraftyError } from "@craftware/crafty-sdk";
168
+
169
+ const crafty = createCraftyClient({
170
+ baseUrl: "https://<your-tenant>.crafty.do",
171
+ apiKey: process.env.CRAFTY_API_KEY,
172
+ siteSlug: "main-site",
173
+ timeoutMs: 10_000,
174
+ });
175
+
176
+ const home = await crafty.pages.get("home");
177
+ const hero = home.block("hero");
178
+
179
+ const { data: posts } = await crafty.entries.list("posts", { per_page: 15, sort_order: "desc" });
180
+ const { data: collections } = await crafty.collections.list();
181
+
182
+ const media = await crafty.mediaCollections.list({ include: ["stats", "directories"] });
183
+ const files = await crafty.mediaFiles.list({ collection: "library", per_page: 24 });
184
+
185
+ try {
186
+ await crafty.forms.submit("contact", { name: "Jane", email: "jane@test.com" });
187
+ } catch (e) {
188
+ if (e instanceof CraftyError && e.code === "BAD_REQUEST") {
189
+ // missing siteSlug, or validation failed
190
+ }
191
+ }
192
+ ```
193
+
194
+ ## Notes
195
+
196
+ - `Accept: application/json` is set automatically unless overridden.
197
+ - `baseUrl` is normalized so `/api/v1` is never duplicated.
198
+ - The `apiKey` (`X-API-Key`) is an **environment** token and is distinct from the **Bearer** tokens used for the MCP server.