@formstr/mcp 0.3.0 → 0.3.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/dist/index.js CHANGED
@@ -32161,14 +32161,22 @@ function buildSpecRows(formId, name, fields, settings) {
32161
32161
  for (const field of fields) rows.push(buildFieldTag(field));
32162
32162
  return rows;
32163
32163
  }
32164
- function buildEncryptedOuterTags(formId, name, relays, settings) {
32165
- const tags = [["d", formId], ["name", name], ...relays.map((r) => ["relay", r])];
32164
+ function buildParticipantTags(settings) {
32165
+ const tags = [];
32166
32166
  const allowed = settings?.allowedResponders ?? [];
32167
32167
  const pTags = /* @__PURE__ */ new Set([...allowed, ...settings?.collaborators ?? []]);
32168
32168
  for (const pk of allowed) tags.push(["allowed", pk]);
32169
32169
  for (const pk of pTags) tags.push(["p", pk]);
32170
32170
  return tags;
32171
32171
  }
32172
+ function buildEncryptedOuterTags(formId, name, relays, settings) {
32173
+ return [
32174
+ ["d", formId],
32175
+ ["name", name],
32176
+ ...relays.map((r) => ["relay", r]),
32177
+ ...buildParticipantTags(settings)
32178
+ ];
32179
+ }
32172
32180
  async function createForm(params) {
32173
32181
  await signerManager.getSigner();
32174
32182
  const formId = crypto.randomUUID().slice(0, 8);
@@ -32194,7 +32202,12 @@ async function createForm(params) {
32194
32202
  await appendToMyFormsList(signingPubkey, formId, relays[0] ?? "", signingKeyHex, viewKeyHex);
32195
32203
  return { formId, pubkey: signingPubkey, signingKey: signingKeyHex, viewKey: viewKeyHex };
32196
32204
  }
32197
- const tags = [...specRows, ["t", "public"], ...relays.map((r) => ["relay", r])];
32205
+ const tags = [
32206
+ ...specRows,
32207
+ ["t", "public"],
32208
+ ...relays.map((r) => ["relay", r]),
32209
+ ...buildParticipantTags(params.settings)
32210
+ ];
32198
32211
  const event = {
32199
32212
  kind: FORM_KINDS.template,
32200
32213
  created_at: Math.floor(Date.now() / 1e3),
@@ -32514,7 +32527,12 @@ async function updateForm(params) {
32514
32527
  await nostrRuntime.publish(relays, finalizeEvent(event2, signingKeyBytes));
32515
32528
  return;
32516
32529
  }
32517
- const tags = [...specRows, ["t", "public"], ...relays.map((r) => ["relay", r])];
32530
+ const tags = [
32531
+ ...specRows,
32532
+ ["t", "public"],
32533
+ ...relays.map((r) => ["relay", r]),
32534
+ ...buildParticipantTags(settings)
32535
+ ];
32518
32536
  const event = {
32519
32537
  kind: FORM_KINDS.template,
32520
32538
  created_at: Math.floor(Date.now() / 1e3),
@@ -32646,6 +32664,7 @@ function parseResponseEvent(event) {
32646
32664
  var service_exports2 = {};
32647
32665
  __export(service_exports2, {
32648
32666
  addEventToCalendarList: () => addEventToCalendarList,
32667
+ createCalendarEvent: () => createCalendarEvent,
32649
32668
  createCalendarList: () => createCalendarList,
32650
32669
  deleteCalendarEvent: () => deleteCalendarEvent,
32651
32670
  deleteCalendarList: () => deleteCalendarList,
@@ -33076,6 +33095,33 @@ async function publishPrivateCalendarEvent(draft, calendarId) {
33076
33095
  event: signed
33077
33096
  };
33078
33097
  }
33098
+ async function createCalendarEvent(draft, opts = {}) {
33099
+ let calendars = opts.calendars ?? null;
33100
+ const loadCalendars = async () => calendars ?? (calendars = await fetchCalendarLists());
33101
+ let targetCalendarId = draft.calendarId;
33102
+ let targetCalendar;
33103
+ if (targetCalendarId) {
33104
+ targetCalendar = (await loadCalendars()).find((c) => c.id === targetCalendarId);
33105
+ } else if (draft.isPrivate) {
33106
+ targetCalendar = (await loadCalendars())[0];
33107
+ if (!targetCalendar) targetCalendar = await createCalendarList("My Calendar", "#334155");
33108
+ targetCalendarId = targetCalendar.id;
33109
+ }
33110
+ const event = draft.isPrivate ? await publishPrivateCalendarEvent(
33111
+ { ...draft, calendarId: targetCalendarId },
33112
+ targetCalendarId ?? "default"
33113
+ ) : await publishPublicCalendarEvent(draft);
33114
+ let calendar;
33115
+ if (targetCalendar) {
33116
+ const ref = [
33117
+ `${event.kind}:${event.user}:${event.id}`,
33118
+ event.relayHint ?? "",
33119
+ event.viewKey ?? ""
33120
+ ];
33121
+ calendar = await addEventToCalendarList(targetCalendar, ref);
33122
+ }
33123
+ return { event, calendar };
33124
+ }
33079
33125
  async function fetchRelayListsForPubkeys(pubkeys) {
33080
33126
  const result = /* @__PURE__ */ new Map();
33081
33127
  if (pubkeys.length === 0) return result;
@@ -34202,6 +34248,7 @@ async function fetchDocMetadata(address) {
34202
34248
  async function saveDocMetadata(address, patch) {
34203
34249
  const existing = await fetchDocMetadata(address) ?? {};
34204
34250
  const merged = { ...existing, ...patch };
34251
+ if (!Array.isArray(merged.tags)) merged.tags = [];
34205
34252
  const signer = await signerManager.getSigner();
34206
34253
  const content = await nip44SelfEncrypt(signer, JSON.stringify(merged));
34207
34254
  const signed = await signer.signEvent({
@@ -34386,7 +34433,9 @@ async function uploadFile(params) {
34386
34433
  name: params.file.name,
34387
34434
  hash: result.sha256,
34388
34435
  size: params.file.size,
34389
- type: params.file.type,
34436
+ // Browsers report "" for unknown MIME types; coalesce exactly as upstream
34437
+ // formstr-drive's uploadPreparedFile does so the on-wire metadata matches.
34438
+ type: params.file.type || "application/octet-stream",
34390
34439
  folder: params.folder ?? "/",
34391
34440
  uploadedAt: Date.now(),
34392
34441
  server,
@@ -34602,8 +34651,9 @@ async function createPoll(draft) {
34602
34651
  for (const tag of draft.hashtags ?? []) {
34603
34652
  tags.push(["t", tag]);
34604
34653
  }
34605
- if (draft.endsAt) {
34606
- tags.push(["endsAt", String(Math.floor(draft.endsAt.getTime() / 1e3))]);
34654
+ const endsAtSec = draft.endsAt && Number.isFinite(draft.endsAt.getTime()) ? Math.floor(draft.endsAt.getTime() / 1e3) : void 0;
34655
+ if (endsAtSec !== void 0) {
34656
+ tags.push(["endsAt", String(endsAtSec)]);
34607
34657
  }
34608
34658
  const event = {
34609
34659
  kind: POLLS_KINDS.poll,
@@ -34620,7 +34670,7 @@ async function createPoll(draft) {
34620
34670
  pollType: draft.pollType,
34621
34671
  pubkey,
34622
34672
  createdAt: signed.created_at,
34623
- endsAt: draft.endsAt ? Math.floor(draft.endsAt.getTime() / 1e3) : void 0,
34673
+ endsAt: endsAtSec,
34624
34674
  relays,
34625
34675
  hashtags: draft.hashtags ?? [],
34626
34676
  event: signed
@@ -34854,7 +34904,7 @@ function buildCalendarTools() {
34854
34904
  server.registerTool(
34855
34905
  "create_calendar_event",
34856
34906
  {
34857
- description: "Schedule a calendar event. start/end are ISO 8601. Set isPrivate:true for an encrypted event; participants receive NIP-59 invitations.",
34907
+ description: "Schedule a calendar event. start/end are ISO 8601. Events default to PRIVATE (encrypted) and are linked into a calendar list \u2014 that is the only way they show on calendar.formstr.app, which renders only events referenced in a calendar list. Pass calendarId to choose which calendar; if omitted and the user already has calendars, this tool returns the list so you can ASK the user which one (then re-run with calendarId). Set isPrivate:false for a public, unencrypted event \u2014 note public events do NOT sync to calendar.formstr.app. participants receive NIP-59 invitations.",
34858
34908
  inputSchema: {
34859
34909
  title: external_exports.string(),
34860
34910
  description: external_exports.string().optional(),
@@ -34862,6 +34912,7 @@ function buildCalendarTools() {
34862
34912
  end: external_exports.string().optional(),
34863
34913
  location: external_exports.string().optional(),
34864
34914
  isPrivate: external_exports.boolean().optional(),
34915
+ calendarId: external_exports.string().optional(),
34865
34916
  participants: external_exports.array(external_exports.string()).optional(),
34866
34917
  rrule: external_exports.string().optional(),
34867
34918
  startTzid: external_exports.string().optional(),
@@ -34869,6 +34920,22 @@ function buildCalendarTools() {
34869
34920
  }
34870
34921
  },
34871
34922
  async (args) => {
34923
+ const isPrivate = args.isPrivate ?? true;
34924
+ const calendars = await service_exports2.fetchCalendarLists();
34925
+ if (isPrivate && !args.calendarId && calendars.length > 0) {
34926
+ const choices = calendars.map((c) => `${c.title} (${c.id})`).join("; ");
34927
+ return fail(
34928
+ `Which calendar should "${args.title}" go in? Re-run create_calendar_event with calendarId set to one of: ${choices}.`,
34929
+ "CALENDAR_REQUIRED"
34930
+ );
34931
+ }
34932
+ if (args.calendarId && !calendars.some((c) => c.id === args.calendarId)) {
34933
+ const available = calendars.map((c) => c.id).join(", ") || "(none)";
34934
+ return fail(
34935
+ `No calendar found for id ${args.calendarId}. Available: ${available}.`,
34936
+ "NOT_FOUND"
34937
+ );
34938
+ }
34872
34939
  const begin = new Date(args.start);
34873
34940
  const end = args.end ? new Date(args.end) : new Date(begin.getTime() + 36e5);
34874
34941
  const draft = {
@@ -34878,18 +34945,23 @@ function buildCalendarTools() {
34878
34945
  end,
34879
34946
  location: args.location,
34880
34947
  participants: args.participants,
34881
- isPrivate: Boolean(args.isPrivate),
34948
+ isPrivate,
34949
+ calendarId: args.calendarId,
34882
34950
  rrule: args.rrule,
34883
34951
  startTzid: args.startTzid,
34884
34952
  registrationFormRef: args.registrationFormRef
34885
34953
  };
34886
- const event = args.isPrivate ? await service_exports2.publishPrivateCalendarEvent(draft, "default") : await service_exports2.publishPublicCalendarEvent(draft);
34954
+ const { event, calendar: list } = await service_exports2.createCalendarEvent(draft, { calendars });
34887
34955
  const coordinate = `${event.kind}:${event.user}:${event.id}`;
34888
- return ok(`Created ${args.isPrivate ? "private" : "public"} event "${args.title}".`, {
34889
- id: event.id,
34890
- eventId: event.eventId,
34891
- coordinate
34892
- });
34956
+ return ok(
34957
+ `Created ${isPrivate ? "private" : "public"} event "${args.title}"${list ? ` in calendar "${list.title}"` : ""}.`,
34958
+ {
34959
+ id: event.id,
34960
+ eventId: event.eventId,
34961
+ coordinate,
34962
+ calendarId: list?.id
34963
+ }
34964
+ );
34893
34965
  }
34894
34966
  );
34895
34967
  server.registerTool(