@homespunapps/mcp 1.6.35 → 1.6.37
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/dist/tools.js +34 -10
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/server.json +2 -2
package/dist/tools.js
CHANGED
|
@@ -162,7 +162,7 @@ const deployAppShape = {
|
|
|
162
162
|
.string()
|
|
163
163
|
.min(1)
|
|
164
164
|
.optional()
|
|
165
|
-
.describe("The app's UI as a complete HTML document (single file, up to the relay's size cap), sent inline. Provide either this or `html_path`. Inline is required for a hosted or remote connector that has no filesystem. If both are given, inline `html` wins."),
|
|
165
|
+
.describe("The app's UI as a complete HTML document (single file, up to the relay's size cap), sent inline. Provide either this or `html_path`. Inline is required for a hosted or remote connector that has no filesystem. If both are given, inline `html` wins. ON REDEPLOY, omit it entirely to keep the live document: a manifest-only change (adding a collection, widening externalHosts) then costs nothing in HTML."),
|
|
166
166
|
html_path: z
|
|
167
167
|
.string()
|
|
168
168
|
.optional()
|
|
@@ -172,7 +172,20 @@ const deployAppShape = {
|
|
|
172
172
|
.optional()
|
|
173
173
|
.describe("Validate only: run the full manifest + asset-shape validation, the compat gate (for a redeploy), and the schedule-timezone advisory, then return { ok, warnings, compat?, breaks? } WITHOUT creating a version or mutating anything. An invalid manifest returns the SAME error a real deploy would; a redeploy the compat gate would refuse reports the break instead of applying it. `check` is an accepted alias."),
|
|
174
174
|
check: z.boolean().optional().describe("Alias for `dry_run`."),
|
|
175
|
-
|
|
175
|
+
// Optional at the SCHEMA level because a redeploy inherits an omitted
|
|
176
|
+
// manifest (#1272); the handler still refuses a create without one.
|
|
177
|
+
//
|
|
178
|
+
// `z.preprocess` rather than `.nullish()` on purpose. MCP clients routinely
|
|
179
|
+
// send `manifest: null` instead of dropping the key, and the SDK validates
|
|
180
|
+
// arguments against this shape before the handler ever runs, so null has to
|
|
181
|
+
// be acceptable here. But `.nullish()` emits `anyOf:[{type:"object"},
|
|
182
|
+
// {type:"null"}]`, which loses the plain top-level `type: "object"` that
|
|
183
|
+
// stops a harness from stringifying the manifest (the reported bug the
|
|
184
|
+
// advertised-schema test guards). Preprocessing keeps the emitted schema
|
|
185
|
+
// exactly `type: "object"` AND turns a null into an omission.
|
|
186
|
+
manifest: z
|
|
187
|
+
.preprocess((v) => (v === null ? undefined : v), jsonObjectSchema.optional())
|
|
188
|
+
.describe("The x-homespun-manifest capability document (a JSON object). REQUIRED to create; ON REDEPLOY, omit it to keep the live manifest, which is what most redeploys want (the manifest was byte-identical to the previous version in 71% of real redeploys). Eight extension keys: app metadata; collections (+ per-collection write/update/read/delete role lists, where write gates creates and also updates unless the optional update list is declared); externalHosts (fetch allowlist); cdn (allow CDN scripts/styles); capabilities (Permissions-Policy opt-ins); embeds (iframe frame-src allowlist); notify (email-on-row rules); webhooks (signed HTTP POST on-row rules). Call get_skill for the full grammar before authoring one from scratch."),
|
|
176
189
|
visibility: z
|
|
177
190
|
.enum(["private", "link", "public"])
|
|
178
191
|
.optional()
|
|
@@ -209,7 +222,7 @@ const deployAppShape = {
|
|
|
209
222
|
}),
|
|
210
223
|
]))
|
|
211
224
|
.optional()
|
|
212
|
-
.describe(
|
|
225
|
+
.describe('Optional bundle of files shipped WITH the app in ONE deploy: images, fonts, audio/video, data. Each asset either carries its bytes inline as `content_base64` OR references an already-uploaded attachment by `attachment_id` (prefer the reference form for real images/media: upload once via `attachments fetch` or presign, then bind it here with NO base64 in the deploy body). Each asset is validated + stored app-scoped exactly like a normal attachment (byte-sniff, allowlist, size cap, quota, scan) and served at its `path` on the app\'s OWN origin, so the page references it by a stable same-origin path (`<img src="frames/000.jpg">`, `<video src="media/intro.mp4">`; media/font paths support HTTP Range). The whole deploy is rejected atomically if any asset fails validation. ON REDEPLOY, assets you send REPLACE the previous version\'s set, omitting `assets` keeps the live set (no re-upload, no re-encoding), and `assets: []` is the explicit way to clear it. Bounded by the relay\'s per-deploy asset-count cap; total bytes by the per-app blob quota.'),
|
|
213
226
|
};
|
|
214
227
|
const listRowsShape = {
|
|
215
228
|
app_id: z.string().min(1).describe("The app id."),
|
|
@@ -764,7 +777,7 @@ export const TOOLS = [
|
|
|
764
777
|
// ----- v2 app lifecycle + data (discrete, hot-path) -----------------------
|
|
765
778
|
{
|
|
766
779
|
name: "deploy_app",
|
|
767
|
-
description: "Deploy a v2 app: an HTML document plus a capability manifest, hosted at its own URL.
|
|
780
|
+
description: "Deploy a v2 app: an HTML document plus a capability manifest, hosted at its own URL.\n\nON REDEPLOY, SEND ONLY WHAT CHANGES. Every content field is optional when `app_id` is given, and an omitted one keeps what is live: omit `manifest` for an HTML-only change, omit `html` for a manifest-only change, omit `assets` to keep the current files. This is the cheap path and it is the one to reach for by default, because the omitted field costs no output tokens at all: a one-line colour change should not resend the whole document, and a manifest edit should not resend it either. Send a field only when its content is different from what is live. `assets: []` is the explicit way to clear the asset set, and omitting all three is refused, since there would be nothing to change.\n\nThe manifest carries eight extension keys: app metadata; collections, with per-collection write, update, read and delete role lists, where write gates creates and also gates updates unless an update list is declared; externalHosts, a fetch allowlist; cdn, to allow CDN scripts and styles; capabilities, for Permissions-Policy opt-ins; embeds, an iframe frame-src allowlist; notify, for email-on-row rules; and webhooks, for signed HTTP POST on-row rules. The manifest grammar is documented in the Homespun guide that get_skill returns.\n\nPass no `app_id` to create, which mints a slug and URL and requires both `html` and `manifest`, or pass `app_id` to redeploy an existing app. Supply the HTML inline as `html`, or as `html_path`, an absolute path read on the MCP-server host, which is the relay for a hosted connector or the CLI host for a locally-run one, and not the remote agent's machine; it avoids retransmitting a large HTML file on every deploy, only a locally-run connector can read it, and inline `html` wins if both are given. `dry_run:true` (alias `check`) validates only: it runs the full manifest and asset validation, the redeploy compat gate and the schedule-timezone advisory, then returns { ok, warnings, compat?, breaks? } without creating a version or mutating anything, and it resolves omitted fields the same way a real deploy would, so it reports on exactly the deploy that would run.\n\nA redeploy is refused with manifest_incompatible_redeploy, unless force:true, when it would strand rows already written (dropping a collection, tightening a schema, flipping appendOnly), or when it would widen what the app's install screen discloses, such as a collection's read reaching further than the live manifest. The break quotes the sentence a user would now be asked to approve. Taking access away never prompts: dropping a role, or adding update:[\\\"creator\\\"] to a write:[\\\"anyone\\\"] collection, redeploys clean. A removed collection is detached rather than deleted.\n\nImages, fonts, audio, video and data files ship with the app in the same call via `assets[]`. Each is validated and stored app-scoped and served at its `path` on the app's own origin, so the HTML references it by a stable same-origin path such as `<img src=\\\"frames/000.jpg\\\">`; media and font paths support HTTP Range for seeking. A redeploy's assets replace the previous version's set when sent, carry over when omitted, and are cleared by `assets: []`.\n\nReturns { app_id, slug, url, version, visibility, created } on create, or { app_id, version, compat, breaks? } on redeploy.",
|
|
768
781
|
inputSchema: deployAppShape,
|
|
769
782
|
annotations: {
|
|
770
783
|
title: "Deploy App",
|
|
@@ -803,14 +816,21 @@ export const TOOLS = [
|
|
|
803
816
|
const dryRun = args["dry_run"] === true || args["check"] === true;
|
|
804
817
|
const assets = args["assets"];
|
|
805
818
|
const appId = str(args, "app_id");
|
|
819
|
+
// `manifest: null` is how several MCP clients express "not sending
|
|
820
|
+
// this" rather than dropping the key, so it means the same as omitted
|
|
821
|
+
// here (the relay's resolver applies the same rule).
|
|
822
|
+
const manifestValue = manifest.value === null ? undefined : manifest.value;
|
|
806
823
|
if (appId === undefined) {
|
|
807
824
|
if (html === undefined) {
|
|
808
825
|
return invalidArgs("create requires `html` or `html_path`");
|
|
809
826
|
}
|
|
827
|
+
if (manifestValue === undefined) {
|
|
828
|
+
return invalidArgs("create requires `manifest` (there is nothing to inherit on a first deploy; inherit-on-omit applies to a redeploy, which passes `app_id`)");
|
|
829
|
+
}
|
|
810
830
|
if (dryRun) {
|
|
811
831
|
return jsonResult(await client.checkDeploy({
|
|
812
832
|
html,
|
|
813
|
-
manifest:
|
|
833
|
+
manifest: manifestValue,
|
|
814
834
|
assets,
|
|
815
835
|
}));
|
|
816
836
|
}
|
|
@@ -821,20 +841,24 @@ export const TOOLS = [
|
|
|
821
841
|
}
|
|
822
842
|
return jsonResult(await client.deployApp({
|
|
823
843
|
html,
|
|
824
|
-
manifest:
|
|
844
|
+
manifest: manifestValue,
|
|
825
845
|
visibility,
|
|
826
846
|
slug,
|
|
827
847
|
assets,
|
|
828
848
|
}));
|
|
829
849
|
}
|
|
830
|
-
|
|
831
|
-
|
|
850
|
+
// REDEPLOY. Nothing is required individually: an omitted html,
|
|
851
|
+
// manifest or assets keeps what is live (issue #1272). Only the empty
|
|
852
|
+
// body is refused, and locally, so the caller gets the reason rather
|
|
853
|
+
// than a round trip that says the same thing.
|
|
854
|
+
if (html === undefined && manifestValue === undefined && !assets) {
|
|
855
|
+
return invalidArgs("a redeploy must change something: send `html`, `manifest` or `assets` (an omitted field keeps what is live; `assets: []` clears the asset set)");
|
|
832
856
|
}
|
|
833
857
|
if (dryRun) {
|
|
834
858
|
return jsonResult(await client.checkDeploy({
|
|
835
859
|
app_id: appId,
|
|
836
860
|
html,
|
|
837
|
-
manifest:
|
|
861
|
+
manifest: manifestValue,
|
|
838
862
|
force: args["force"],
|
|
839
863
|
assets,
|
|
840
864
|
}));
|
|
@@ -844,7 +868,7 @@ export const TOOLS = [
|
|
|
844
868
|
}
|
|
845
869
|
const redeployed = await client.redeployApp(appId, {
|
|
846
870
|
html,
|
|
847
|
-
manifest:
|
|
871
|
+
manifest: manifestValue,
|
|
848
872
|
force: args["force"],
|
|
849
873
|
assets,
|
|
850
874
|
});
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "1.6.
|
|
1
|
+
export declare const VERSION = "1.6.37";
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@homespunapps/mcp",
|
|
3
3
|
"mcpName": "dev.homespun/homespun",
|
|
4
|
-
"version": "1.6.
|
|
4
|
+
"version": "1.6.37",
|
|
5
5
|
"description": "Model Context Protocol (stdio) server for Homespun: lets any MCP client (Claude Desktop, Cursor, …) deploy a multi-user web app with hosting, auth, a shared database and permissions included.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@modelcontextprotocol/sdk": "^1.20.0",
|
|
49
|
-
"@homespunapps/core": "^1.6.
|
|
49
|
+
"@homespunapps/core": "^1.6.37",
|
|
50
50
|
"zod": "^4.4.3"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
package/server.json
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
"name": "dev.homespun/homespun",
|
|
4
4
|
"title": "Homespun",
|
|
5
5
|
"description": "Deploy a multi-user web app from your agent: hosting, auth, database, and permissions.",
|
|
6
|
-
"version": "1.6.
|
|
6
|
+
"version": "1.6.37",
|
|
7
7
|
"websiteUrl": "https://docs.homespun.dev",
|
|
8
8
|
"packages": [
|
|
9
9
|
{
|
|
10
10
|
"registryType": "npm",
|
|
11
11
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
12
12
|
"identifier": "@homespunapps/mcp",
|
|
13
|
-
"version": "1.6.
|
|
13
|
+
"version": "1.6.37",
|
|
14
14
|
"transport": {
|
|
15
15
|
"type": "stdio"
|
|
16
16
|
},
|