@floomhq/floom-mcp-sync 1.0.0 → 1.0.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
@@ -6,7 +6,7 @@ Tiny MCP server for Floom skills. This package is part of the Floom Version 1 sy
6
6
  npx -y @floomhq/floom-mcp-sync
7
7
  ```
8
8
 
9
- On startup it reads `~/.floom/config.json`, fetches your own published Floom skills, and writes missing files to `~/.claude/skills/`. The background sync behavior is a Version 1 preview path.
9
+ On startup it reads `~/.floom/config.json`, fetches published, saved, and subscribed library skills, and writes missing files to `~/.claude/skills/`. The background sync behavior is a Version 1 preview path.
10
10
 
11
11
  Sync stores a machine-local manifest next to the Floom CLI config at `~/.floom/sync-manifest.json`.
12
12
  Version 1 sync does not replace existing local Markdown files. Remote updates, existing untracked
@@ -25,4 +25,9 @@ Tools:
25
25
  - `installs_as`: `claude_skill`, `memory`, `rule`, `codex_instruction`, `opencode_instruction`, `cursor_rule`, or `other` (default `claude_skill`)
26
26
  - `version`: optional label like `1.0.0` or `v1-preview`
27
27
 
28
- Library management and share-invite tools are planned for a later Floom version.
28
+ - `floom_list_libraries()` lists public Floom libraries.
29
+ - `floom_subscribe_library(slug)` subscribes the signed-in user so the library syncs locally.
30
+ - `floom_unsubscribe_library(slug)` removes that subscription.
31
+ - `floom_move_skill(slug, folder, tags?)` sets the signed-in user's local folder/tags override.
32
+
33
+ Team workspaces, share invites, and role-based library access are planned for a later Floom version.
package/dist/server.js CHANGED
@@ -4,12 +4,14 @@ import { stdin, stdout } from "node:process";
4
4
  import { autoSync } from "./auto-sync.js";
5
5
  import { installSkill } from "./tools/install.js";
6
6
  import { publishSkill } from "./tools/publish.js";
7
- const SERVER_VERSION = "1.0.0";
7
+ import { listLibraries, moveSkill, subscribeLibrary, unsubscribeLibrary, } from "./tools/libraries.js";
8
+ const SERVER_VERSION = "1.0.1";
8
9
  const DEFAULT_INTERVAL_MS = 60_000;
9
10
  const MIN_INTERVAL_MS = 10_000;
10
11
  const VERSION_RE = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,63}$/;
11
12
  const VISIBILITIES = new Set(["unlisted", "public", "private"]);
12
13
  const ASSET_TYPES = new Set(["knowledge", "instruction", "workflow", "skill"]);
14
+ const FOLDER_RE = /^[a-z0-9][a-z0-9._-]*(\/[a-z0-9][a-z0-9._-]*)*$/;
13
15
  const INSTALL_TARGETS = new Set([
14
16
  "claude_skill",
15
17
  "memory",
@@ -29,7 +31,7 @@ Usage
29
31
 
30
32
  Behavior
31
33
  Starts a stdio MCP server.
32
- Syncs your own published Floom skills into ~/.claude/skills/ on startup.
34
+ Syncs published, saved, and subscribed library skills into ~/.claude/skills/.
33
35
  Polls for updates while the MCP process is running.
34
36
 
35
37
  Options
@@ -120,6 +122,57 @@ function toolList() {
120
122
  additionalProperties: false,
121
123
  },
122
124
  },
125
+ {
126
+ name: "floom_list_libraries",
127
+ description: "List public Floom libraries.",
128
+ inputSchema: {
129
+ type: "object",
130
+ properties: {},
131
+ additionalProperties: false,
132
+ },
133
+ },
134
+ {
135
+ name: "floom_subscribe_library",
136
+ description: "Subscribe the signed-in user to a Floom library so it syncs locally.",
137
+ inputSchema: {
138
+ type: "object",
139
+ properties: {
140
+ slug: { type: "string", minLength: 1, maxLength: 64 },
141
+ },
142
+ required: ["slug"],
143
+ additionalProperties: false,
144
+ },
145
+ },
146
+ {
147
+ name: "floom_unsubscribe_library",
148
+ description: "Unsubscribe the signed-in user from a Floom library.",
149
+ inputSchema: {
150
+ type: "object",
151
+ properties: {
152
+ slug: { type: "string", minLength: 1, maxLength: 64 },
153
+ },
154
+ required: ["slug"],
155
+ additionalProperties: false,
156
+ },
157
+ },
158
+ {
159
+ name: "floom_move_skill",
160
+ description: "Set a signed-in user's local folder/tags override for a Floom skill.",
161
+ inputSchema: {
162
+ type: "object",
163
+ properties: {
164
+ slug: { type: "string", minLength: 1, maxLength: 128 },
165
+ folder: { type: ["string", "null"], maxLength: 256 },
166
+ tags: {
167
+ type: "array",
168
+ items: { type: "string", minLength: 1, maxLength: 64 },
169
+ maxItems: 32,
170
+ },
171
+ },
172
+ required: ["slug", "folder"],
173
+ additionalProperties: false,
174
+ },
175
+ },
123
176
  ],
124
177
  };
125
178
  }
@@ -157,6 +210,21 @@ function nullableEnumValue(value, label, allowed, fallback) {
157
210
  throw new Error(`Invalid ${label}.`);
158
211
  return value;
159
212
  }
213
+ function optionalStringArray(value, label, maxItems, maxLength) {
214
+ if (value === undefined)
215
+ return [];
216
+ if (!Array.isArray(value) || value.length > maxItems)
217
+ throw new Error(`Invalid ${label}.`);
218
+ return value.map((item) => asString(item, label, 1, maxLength));
219
+ }
220
+ function nullableFolder(value) {
221
+ if (value === null)
222
+ return null;
223
+ const folder = asString(value, "folder", 1, 256);
224
+ if (!FOLDER_RE.test(folder))
225
+ throw new Error("Invalid folder.");
226
+ return folder;
227
+ }
160
228
  async function callTool(params) {
161
229
  const parsed = asObject(params);
162
230
  const name = asString(parsed.name, "tool name", 1, 200);
@@ -170,6 +238,18 @@ async function callTool(params) {
170
238
  throw new Error("Invalid version.");
171
239
  return ok(await publishSkill(asString(args.name, "name", 1, 200), asString(args.content, "content", 1, 500_000), optionalString(args.description, "description", 1000), enumValue(args.visibility, "visibility", VISIBILITIES, "unlisted"), enumValue(args.asset_type, "asset_type", ASSET_TYPES, "skill"), nullableEnumValue(args.installs_as, "installs_as", INSTALL_TARGETS, "claude_skill"), version));
172
240
  }
241
+ if (name === "floom_list_libraries") {
242
+ return ok(await listLibraries());
243
+ }
244
+ if (name === "floom_subscribe_library") {
245
+ return ok(await subscribeLibrary(asString(args.slug, "slug", 1, 64)));
246
+ }
247
+ if (name === "floom_unsubscribe_library") {
248
+ return ok(await unsubscribeLibrary(asString(args.slug, "slug", 1, 64)));
249
+ }
250
+ if (name === "floom_move_skill") {
251
+ return ok(await moveSkill(asString(args.slug, "slug", 1, 128), nullableFolder(args.folder), optionalStringArray(args.tags, "tags", 32, 64)));
252
+ }
173
253
  throw new Error(`Unknown tool: ${name}`);
174
254
  }
175
255
  function response(id, result) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floomhq/floom-mcp-sync",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Lightweight Floom MCP server for installing, publishing, and startup-syncing skills.",
5
5
  "license": "MIT",
6
6
  "type": "module",