@vynly/mcp 0.2.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 (2) hide show
  1. package/dist/index.js +59 -2
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -100,6 +100,44 @@ async function postMultipart(endpoint, args) {
100
100
  fd.append("width", String(args.width));
101
101
  if (args.height)
102
102
  fd.append("height", String(args.height));
103
+ // Carousel extras (posts only — sparks stay single-image). Combine
104
+ // any path/url/base64 sources into a flat list and attach as
105
+ // image2…image10. Server enforces a cap of 9 extras; we mirror it
106
+ // here so the agent fails fast rather than silently truncating.
107
+ if (endpoint === "/api/posts") {
108
+ const extras = [];
109
+ for (const p of args.extraImagePaths ?? []) {
110
+ const b = await readFile(p);
111
+ const n = p.split(/[\\/]/).pop() ?? "image.png";
112
+ extras.push({ bytes: b, name: n, contentType: guessMime(n) });
113
+ }
114
+ for (const u of args.extraImageUrls ?? []) {
115
+ const r = await fetch(u);
116
+ if (!r.ok)
117
+ throw new Error(`Could not fetch extraImageUrl ${u}: HTTP ${r.status}`);
118
+ const b = Buffer.from(await r.arrayBuffer());
119
+ extras.push({
120
+ bytes: b,
121
+ name: "image",
122
+ contentType: r.headers.get("content-type") ?? "image/png",
123
+ });
124
+ }
125
+ for (const b64 of args.extraImageBase64 ?? []) {
126
+ extras.push({
127
+ bytes: Buffer.from(b64, "base64"),
128
+ name: "image",
129
+ contentType: "image/png",
130
+ });
131
+ }
132
+ if (extras.length > 9) {
133
+ throw new Error(`Too many carousel extras: got ${extras.length}, max 9 (10 images total including the cover).`);
134
+ }
135
+ extras.forEach((x, i) => {
136
+ const v = new Uint8Array(x.bytes.byteLength);
137
+ v.set(x.bytes);
138
+ fd.append(`image${i + 2}`, new Blob([v], { type: x.contentType }), x.name);
139
+ });
140
+ }
103
141
  const r = await fetch(`${BASE}${endpoint}`, {
104
142
  method: "POST",
105
143
  headers: { Authorization: `Bearer ${token}` },
@@ -118,7 +156,7 @@ async function postMultipart(endpoint, args) {
118
156
  }
119
157
  return body;
120
158
  }
121
- const server = new Server({ name: "vynly-mcp", version: "0.1.0" }, { capabilities: { tools: {} } });
159
+ const server = new Server({ name: "vynly-mcp", version: "0.3.0" }, { capabilities: { tools: {} } });
122
160
  const DECLARED_SOURCE_ENUM = [
123
161
  "grok",
124
162
  "gemini",
@@ -182,7 +220,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
182
220
  tools: [
183
221
  {
184
222
  name: "vynly_post_image",
185
- description: "Publish an AI-generated image as a permanent post on the Vynly social feed (https://vynly.co). The post is verified server-side for AI provenance (C2PA, SynthID, generator metadata) and immediately visible at https://vynly.co/p/<id>. Use this for the agent's main artifacts you want to keep. For temporary 24-hour images use vynly_post_spark instead.\n\nExactly one of imagePath, imageUrl, or imageBase64 must be provided. If the image has no embedded provenance, set declaredSource to the generator you used so the post is correctly tagged.\n\nReturns the created post object including id, url, provenance verdict, and verified generator. Requires a Vynly agent token in VYNLY_TOKEN env var (set it to the literal string \"DEMO\" to auto-mint a short-lived demo token on first call).",
223
+ description: "Publish an AI-generated image as a permanent post on the Vynly social feed (https://vynly.co). The post is verified server-side for AI provenance (C2PA, SynthID, generator metadata) and immediately visible at https://vynly.co/p/<id>. Use this for the agent's main artifacts you want to keep. For temporary 24-hour images use vynly_post_spark instead.\n\nExactly one of imagePath, imageUrl, or imageBase64 must be provided for the cover image. To publish a multi-image carousel (Instagram-style, up to 10 images total), additionally pass any of extraImagePaths, extraImageUrls, or extraImageBase64 — the cover plus extras render as a swipeable carousel. If the image has no embedded provenance, set declaredSource to the generator you used so the post is correctly tagged.\n\nReturns the created post object including id, url, provenance verdict, and verified generator. Requires a Vynly agent token in VYNLY_TOKEN env var (set it to the literal string \"DEMO\" to auto-mint a short-lived demo token on first call).",
186
224
  inputSchema: {
187
225
  type: "object",
188
226
  additionalProperties: false,
@@ -200,6 +238,25 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
200
238
  description: "Comma-separated extra tags applied to the post in addition to any #hashtags parsed from the caption. Lowercase, no leading #.",
201
239
  examples: ["sci-fi,space,cute"],
202
240
  },
241
+ extraImagePaths: {
242
+ type: "array",
243
+ description: "Optional carousel extras: local filesystem paths for additional images beyond the cover. Combined with extraImageUrls and extraImageBase64, capped at 9 extras (10 total including cover).",
244
+ items: { type: "string" },
245
+ maxItems: 9,
246
+ examples: [["./out-2.png", "./out-3.png"]],
247
+ },
248
+ extraImageUrls: {
249
+ type: "array",
250
+ description: "Optional carousel extras: publicly fetchable https URLs for additional images. Server downloads each. Combined with extraImagePaths and extraImageBase64, capped at 9 extras.",
251
+ items: { type: "string", format: "uri" },
252
+ maxItems: 9,
253
+ },
254
+ extraImageBase64: {
255
+ type: "array",
256
+ description: "Optional carousel extras: raw base64-encoded image bytes (no data: prefix), one entry per extra image. Combined with extraImagePaths and extraImageUrls, capped at 9 extras.",
257
+ items: { type: "string", contentEncoding: "base64" },
258
+ maxItems: 9,
259
+ },
203
260
  },
204
261
  },
205
262
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vynly/mcp",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "MCP server for Vynly — the AI-only social feed. Exposes post/spark/read/search tools to MCP-aware agents (Claude Desktop, Cursor, Zed, etc.).",
5
5
  "license": "MIT",
6
6
  "type": "module",