@buildinternet/uploads 0.53.0 → 0.55.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.
@@ -263,6 +263,20 @@ export const changelogResultSchema = objectSchema({
263
263
  feed: { type: "string" },
264
264
  entries: { type: "array", items: changelogEntrySchema },
265
265
  }, ["url", "entries"]);
266
+ const docsSearchHitSchema = objectSchema({
267
+ title: { type: "string" },
268
+ url: { type: "string" },
269
+ page: { type: "string" },
270
+ snippet: { type: "string" },
271
+ body: { type: "string" },
272
+ truncated: { type: "boolean" },
273
+ }, ["title", "url", "page", "snippet"]);
274
+ export const searchDocsResultSchema = objectSchema({
275
+ url: { type: "string" },
276
+ query: { type: "string" },
277
+ results: { type: "array", items: docsSearchHitSchema },
278
+ total: { type: "number" },
279
+ }, ["url", "results", "total"]);
266
280
  export const promoteToolResultSchema = objectSchema({
267
281
  // `promotion` is optional (issue #702): a `keys`-only call (no `branch`)
268
282
  // never runs the branch sweep, so there's nothing to report under it.
@@ -323,6 +337,39 @@ export const galleryFindResultSchema = objectSchema({
323
337
  galleries: { type: "array", items: galleryResultSchema },
324
338
  nextCursor: nullableString,
325
339
  });
340
+ const feedItemSchema = objectSchema({
341
+ id: { type: "string" },
342
+ objectKey: { type: "string" },
343
+ filename: { type: "string" },
344
+ status: { type: "string" },
345
+ url: nullableString,
346
+ embedUrl: nullableString,
347
+ pageUrl: { type: "string" },
348
+ contentType: nullableString,
349
+ size: { type: ["number", "null"] },
350
+ uploaded: nullableString,
351
+ modified: nullableString,
352
+ path: nullableString,
353
+ state: nullableString,
354
+ posterUrl: { type: "string" },
355
+ videoDimensions: {
356
+ type: "object",
357
+ additionalProperties: true,
358
+ },
359
+ });
360
+ export const feedResultSchema = objectSchema({
361
+ id: { type: "string" },
362
+ url: { type: "string" },
363
+ workspace: { type: "string" },
364
+ repo: { type: "string" },
365
+ path: nullableString,
366
+ number: { type: ["number", "null"] },
367
+ kind: nullableString,
368
+ title: { type: "string" },
369
+ createdAt: { type: "string" },
370
+ updatedAt: { type: "string" },
371
+ items: { type: "array", items: feedItemSchema },
372
+ });
326
373
  /** Hosted catalog — every tool must have an entry. */
327
374
  export const hostedOutputSchemas = {
328
375
  gallery_create: galleryResultSchema,
@@ -330,6 +377,8 @@ export const hostedOutputSchemas = {
330
377
  gallery_add: galleryItemSchema,
331
378
  gallery_link: galleryReferenceSchema,
332
379
  gallery_find_by_reference: galleryFindResultSchema,
380
+ feed_create: feedResultSchema,
381
+ feed_get: feedResultSchema,
333
382
  put: putResultSchema,
334
383
  list: listResultSchema,
335
384
  delete: deleteResultSchema,
@@ -345,6 +394,7 @@ export const hostedOutputSchemas = {
345
394
  purge_expired: purgeExpiredResultSchema,
346
395
  whoami: whoamiResultSchema,
347
396
  changelog: changelogResultSchema,
397
+ search_docs: searchDocsResultSchema,
348
398
  };
349
399
  /** Shared-shape stdio tools. Hosted-only tools (`promote`, `repo_link_status`) omitted. */
350
400
  export const stdioOutputSchemas = {
@@ -353,6 +403,8 @@ export const stdioOutputSchemas = {
353
403
  gallery_add: galleryItemSchema,
354
404
  gallery_link: galleryReferenceSchema,
355
405
  gallery_find_by_reference: galleryFindResultSchema,
406
+ feed_create: feedResultSchema,
407
+ feed_get: feedResultSchema,
356
408
  put: putResultSchema,
357
409
  list: listResultSchema,
358
410
  delete: deleteResultSchema,
@@ -379,6 +431,7 @@ export const stdioOutputSchemas = {
379
431
  purge_expired: purgeExpiredResultSchema,
380
432
  whoami: whoamiResultSchema,
381
433
  changelog: changelogResultSchema,
434
+ search_docs: searchDocsResultSchema,
382
435
  report: objectSchema({
383
436
  ok: { type: "boolean" },
384
437
  id: { type: "string" },
@@ -20,6 +20,7 @@
20
20
  import { McpServer, type Implementation, type jsonSchemaValidator } from "@modelcontextprotocol/server";
21
21
  export { appProp, canonicalMetaFromArgs, METADATA_DESCRIPTION, METADATA_PATH_CUE, MCP_EXAMPLE_PNG_BASE64, metadataArgWithCanonical, metadataProp, stateProp, optBool, optPosInt, optString, optStringArray, optStringRecord, usage, type ToolArgs, } from "./args.js";
22
22
  export { ToolBatchError, batchFailureMessage } from "./batch-error.js";
23
+ export { SEARCH_DOCS_DESCRIPTION, SEARCH_DOCS_INPUT_SCHEMA, runSearchDocsTool, } from "./docs-tool.js";
23
24
  export { mapBounded } from "../async.js";
24
25
  export { McpServer, type jsonSchemaValidator };
25
26
  export { commentResultSchema, deleteResultSchema, findFilesResultSchema, galleryFindResultSchema, galleryResultSchema, hostedOutputSchemas, listResultSchema, metadataFacetsResultSchema, metadataResultSchema, promoteToolResultSchema, purgeExpiredResultSchema, putResultSchema, reconcileResultSchema, repoLinkStatusResultSchema, stdioOutputSchemas, usageResultSchema, withOutputSchemas, } from "./output-schemas.js";
@@ -23,6 +23,7 @@ import { errorCodeFromUnknown, recordEvent } from "../telemetry.js";
23
23
  import { ToolBatchError } from "./batch-error.js";
24
24
  export { appProp, canonicalMetaFromArgs, METADATA_DESCRIPTION, METADATA_PATH_CUE, MCP_EXAMPLE_PNG_BASE64, metadataArgWithCanonical, metadataProp, stateProp, optBool, optPosInt, optString, optStringArray, optStringRecord, usage, } from "./args.js";
25
25
  export { ToolBatchError, batchFailureMessage } from "./batch-error.js";
26
+ export { SEARCH_DOCS_DESCRIPTION, SEARCH_DOCS_INPUT_SCHEMA, runSearchDocsTool, } from "./docs-tool.js";
26
27
  export { mapBounded } from "../async.js";
27
28
  export { McpServer };
28
29
  export { commentResultSchema, deleteResultSchema, findFilesResultSchema, galleryFindResultSchema, galleryResultSchema, hostedOutputSchemas, listResultSchema, metadataFacetsResultSchema, metadataResultSchema, promoteToolResultSchema, purgeExpiredResultSchema, putResultSchema, reconcileResultSchema, repoLinkStatusResultSchema, stdioOutputSchemas, usageResultSchema, withOutputSchemas, } from "./output-schemas.js";
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * MCP tool set mirroring the CLI commands (put, attach, list, delete,
3
- * usage, reconcile, purge_expired, comment, whoami, doctor, changelog). Config is
3
+ * usage, reconcile, purge_expired, comment, whoami, doctor, changelog,
4
+ * search_docs). Config is
4
5
  * resolved fresh per tool call so a
5
6
  * per-call `workspace` argument behaves like the CLI's --workspace flag, and
6
7
  * a missing token surfaces as a tool error rather than a startup failure.
package/dist/mcp/tools.js CHANGED
@@ -4,7 +4,7 @@ import { resolveFrameId } from "../frame.js";
4
4
  import { resolveConfig, resolvePutDefaults, } from "../config.js";
5
5
  import { resolvePutPrefix } from "../destinations.js";
6
6
  import { fetchUploadSource, resolveUploadFilename } from "../fetch-upload-source.js";
7
- import { ghKeyPrefix, ghPrivateKeyPrefix } from "../github.js";
7
+ import { ghKeyPrefix, ghPrivateKeyPrefix, parseGithubIssueRef } from "../github.js";
8
8
  import { safeCaptureFacts } from "../capture-facts.js";
9
9
  import { deriveRepoSlugFromGit } from "../keys.js";
10
10
  import { validateMetaMap } from "../metadata.js";
@@ -15,6 +15,7 @@ import { batchFailureMessage, mcpDestroyPublic, mcpNoAuth, mcpOAuthAny, mcpOAuth
15
15
  import { attachmentFromText, buildReportPayload, parseReportType, REPORT_TYPES, submitReport, validateReportMessage, } from "../report.js";
16
16
  import { resolveApiUrl } from "../config.js";
17
17
  import { DEFAULT_CHANGELOG_LIMIT, MAX_CHANGELOG_LIMIT, fetchChangelog } from "../changelog.js";
18
+ import { SEARCH_DOCS_DESCRIPTION, SEARCH_DOCS_INPUT_SCHEMA, runSearchDocsTool, } from "./docs-tool.js";
18
19
  function mcpOptimizeOptions(args, defaults) {
19
20
  const quality = optPosInt(args, "optimizeQuality");
20
21
  if (quality !== undefined && quality > 100)
@@ -74,6 +75,12 @@ function galleryId(args) {
74
75
  usage("galleryId is required");
75
76
  return id;
76
77
  }
78
+ function feedId(args) {
79
+ const id = optString(args, "feedId");
80
+ if (!id)
81
+ usage("feedId is required");
82
+ return id;
83
+ }
77
84
  function galleryReference(args) {
78
85
  const provider = optString(args, "provider");
79
86
  const coordinate = optString(args, "coordinate");
@@ -85,6 +92,33 @@ function galleryReference(args) {
85
92
  usage("provider must be github");
86
93
  return { provider, coordinate };
87
94
  }
95
+ function feedCreateOptions(args) {
96
+ const github = optString(args, "github");
97
+ const repo = optString(args, "repo");
98
+ const path = optString(args, "path");
99
+ const pr = optPosInt(args, "pr");
100
+ const issue = optPosInt(args, "issue");
101
+ if (pr != null && issue != null)
102
+ usage("pr and issue are mutually exclusive");
103
+ if (github && (pr != null || issue != null))
104
+ usage("github cannot be combined with pr or issue");
105
+ if (github) {
106
+ const ref = parseGithubIssueRef(github);
107
+ if (!ref)
108
+ usage("github must be owner/repo#number or a GitHub issue/PR URL");
109
+ if (repo && repo.trim().toLowerCase() !== ref.repo)
110
+ usage("repo does not match github");
111
+ return { repo: ref.repo, path, number: ref.number, kind: ref.kind };
112
+ }
113
+ if (!repo)
114
+ usage("repo is required");
115
+ return {
116
+ repo,
117
+ path,
118
+ ...(pr != null ? { number: pr, kind: "pull" } : {}),
119
+ ...(issue != null ? { number: issue, kind: "issue" } : {}),
120
+ };
121
+ }
88
122
  const workspaceProp = {
89
123
  type: "string",
90
124
  description: "Override the workspace for this call (like the CLI's --workspace flag).",
@@ -288,6 +322,66 @@ export function createUploadsMcpTools(opts) {
288
322
  });
289
323
  },
290
324
  },
325
+ {
326
+ name: "feed_create",
327
+ title: "Create change feed",
328
+ annotations: mcpWritePublic,
329
+ securitySchemes: mcpOAuthWrite,
330
+ description: "Create a public newest-first screenshot feed (same product as CLI `uploads feed create`). Pass `repo` alone for the whole GitHub repo. Pass `pr` plus `repo` — or `github` as owner/repo#123 / a GitHub PR URL — to scope that same feed to one pull request. `issue` does the same for an issue. Optional `path` filters by page-path metadata. Creating the same scope again returns the existing URL. This is a live query, not a curated gallery. Anyone who knows the URL can view it.",
331
+ inputSchema: {
332
+ type: "object",
333
+ properties: {
334
+ repo: {
335
+ type: "string",
336
+ description: "GitHub owner/repo, for example acme/app. Required unless github is set.",
337
+ },
338
+ pr: {
339
+ type: "integer",
340
+ minimum: 1,
341
+ description: "Scope the feed to this pull request. Same product as a repo feed — one extra filter. Mutually exclusive with issue and github.",
342
+ },
343
+ issue: {
344
+ type: "integer",
345
+ minimum: 1,
346
+ description: "Scope the feed to this issue. Mutually exclusive with pr and github.",
347
+ },
348
+ github: {
349
+ type: "string",
350
+ description: "Scope via owner/repo#number or a GitHub issue/PR URL. Supplies repo when omitted. Mutually exclusive with pr and issue.",
351
+ },
352
+ path: {
353
+ type: "string",
354
+ description: "Optional exact page-path metadata filter, for example /settings.",
355
+ },
356
+ workspace: workspaceProp,
357
+ },
358
+ additionalProperties: false,
359
+ },
360
+ async handler(args) {
361
+ const { client } = await clientFor(args);
362
+ return client.createFeed(feedCreateOptions(args));
363
+ },
364
+ },
365
+ {
366
+ name: "feed_get",
367
+ title: "Get change feed",
368
+ annotations: mcpRead,
369
+ securitySchemes: mcpOAuthRead,
370
+ description: "Get a workspace-owned change feed by ID, including its current newest-first screenshots and the canonical public /c/<id> URL. Each item includes pageUrl (`/c/<id>/<item>`) for the pager. The feed may be repo-wide or scoped to one pull request / issue (see `number` and `kind` on the result). Anyone with the URL can view the media.",
371
+ inputSchema: {
372
+ type: "object",
373
+ properties: {
374
+ feedId: { type: "string", description: "Opaque feed ID." },
375
+ workspace: workspaceProp,
376
+ },
377
+ required: ["feedId"],
378
+ additionalProperties: false,
379
+ },
380
+ async handler(args) {
381
+ const { client } = await clientFor(args);
382
+ return client.getFeed(feedId(args));
383
+ },
384
+ },
291
385
  {
292
386
  name: "put",
293
387
  title: "Upload file",
@@ -1553,6 +1647,19 @@ export function createUploadsMcpTools(opts) {
1553
1647
  return { ...target, ...result };
1554
1648
  },
1555
1649
  },
1650
+ {
1651
+ name: "search_docs",
1652
+ title: "Search uploads.sh docs",
1653
+ annotations: {
1654
+ readOnlyHint: true,
1655
+ destructiveHint: false,
1656
+ openWorldHint: true,
1657
+ },
1658
+ securitySchemes: mcpNoAuth,
1659
+ description: SEARCH_DOCS_DESCRIPTION,
1660
+ inputSchema: SEARCH_DOCS_INPUT_SCHEMA,
1661
+ handler: runSearchDocsTool,
1662
+ },
1556
1663
  {
1557
1664
  name: "changelog",
1558
1665
  title: "Product changelog",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buildinternet/uploads",
3
- "version": "0.53.0",
3
+ "version": "0.55.0",
4
4
  "mcpName": "sh.uploads/mcp",
5
5
  "description": "CLI and client for uploads.sh — workspace-scoped image hosting for GitHub embeds",
6
6
  "type": "module",