@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 +7 -2
- package/dist/server.js +82 -2
- package/package.json +1 -1
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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) {
|