@formstr/mcp 0.6.0 → 0.7.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.
package/README.md CHANGED
@@ -236,17 +236,26 @@ Email through mailstr, using the same identity you logged in with (the account t
236
236
 
237
237
  - `list_mail` — your inbox: sender, subject, date per message (kind-1059 gift-wrapped mail).
238
238
  - `read_mail` — the full body of one message by its id.
239
- - `who_is_my_mail_address` — the account's mail address (NIP-05) and pubkey.
239
+ - `list_mail_aliases` — every address (NIP-05 alias) this account can send as, and the default.
240
+ - `who_is_my_mail_address` — the signed-in identity, its default `From:`, and its aliases.
240
241
 
241
242
  **Gated (require `--allow-writes` + `confirm: true`)**
242
243
 
243
244
  - `send_mail` — send email to an npub/hex key, or an external address via the domain's SMTP bridge.
245
+ Pass `from` to choose which alias to send as (one of your own addresses); omit it to send from
246
+ an owned alias, or the npub mailbox when you have none. A `from` you do not own is rejected with
247
+ the list of valid senders.
244
248
  - `claim_mailbox` — start claiming `you@mailstr.app`. Returns a **Lightning invoice to pay in
245
249
  your own wallet** — the server holds no wallet, so it cannot pay it. Mail works once NIP-05
246
250
  propagates.
247
251
  - `publish_mail_setup` — publish your profile (with the NIP-05 address) and the kind-10050
248
252
  delivery-relay list senders read.
249
253
 
254
+ **Aliases.** A mail identity is one Nostr key; an *alias* is a NIP-05 name bound to that key.
255
+ All of an account's aliases share one key and one inbox — mail is encrypted to the key, so which
256
+ alias a message was addressed to is only a header. `send_mail`'s `from` picks which alias appears
257
+ in the `From:`; `list_mail_aliases` shows the choices.
258
+
250
259
  ## Safety model
251
260
 
252
261
  Destructive / outward tools are **not registered** unless `--allow-writes` (or
package/dist/index.js CHANGED
@@ -33354,6 +33354,64 @@ var defaultFetchJson = async (url, init) => {
33354
33354
  clearTimeout(timer);
33355
33355
  }
33356
33356
  };
33357
+ var OWNED_ADDRESSES_PATH = "/api/nip-05/get-nip05";
33358
+ function normalizeOwnedAddresses(body, domain = DEFAULT_MAIL_DOMAIN) {
33359
+ const qualify = (addr, entryDomain) => {
33360
+ if (addr.includes("@")) return addr;
33361
+ if (typeof entryDomain === "string" && entryDomain.trim()) return `${addr}@${entryDomain}`;
33362
+ return `${addr}@${domain}`;
33363
+ };
33364
+ if (typeof body === "string") return [qualify(body)];
33365
+ let raw = [];
33366
+ if (Array.isArray(body)) {
33367
+ raw = body.flatMap((entry) => {
33368
+ if (typeof entry === "string") return [{ value: entry }];
33369
+ if (entry && typeof entry === "object") {
33370
+ const obj = entry;
33371
+ if (typeof obj.nip05 === "string") return [{ value: obj.nip05, domain: obj.domain }];
33372
+ if (typeof obj.name === "string") return [{ value: obj.name, domain: obj.domain }];
33373
+ }
33374
+ return [];
33375
+ });
33376
+ } else if (body && typeof body === "object") {
33377
+ const obj = body;
33378
+ if (typeof obj.nip05 === "string") {
33379
+ raw = [{ value: obj.nip05, domain: obj.domain }];
33380
+ } else if (Array.isArray(obj.nip05Addresses)) {
33381
+ raw = obj.nip05Addresses.filter((v) => typeof v === "string").map((value) => ({ value }));
33382
+ }
33383
+ }
33384
+ return raw.map(({ value, domain: d }) => qualify(value, d));
33385
+ }
33386
+ async function defaultFetch(url, init) {
33387
+ const res = await fetch(url, init);
33388
+ let body = null;
33389
+ try {
33390
+ body = await res.json();
33391
+ } catch {
33392
+ }
33393
+ return { ok: res.ok, status: res.status, body };
33394
+ }
33395
+ async function fetchAddresses(url, header, opts) {
33396
+ const fetchJson = opts.fetchJson ?? defaultFetch;
33397
+ const res = await fetchJson(url, { method: "GET", headers: { Authorization: header } });
33398
+ if (res.status === 404 || res.status === 401) return [];
33399
+ if (!res.ok) throw new Error(`Address lookup failed (${res.status})`);
33400
+ return normalizeOwnedAddresses(res.body, opts.domain ?? DEFAULT_MAIL_DOMAIN);
33401
+ }
33402
+ async function fetchOwnedAddressesWith(signer, opts = {}) {
33403
+ const api = (opts.api ?? DEFAULT_CLAIM_API).replace(/\/+$/, "");
33404
+ const url = `${api}${OWNED_ADDRESSES_PATH}`;
33405
+ return fetchAddresses(url, await signNip98With(signer, url, "GET"), opts);
33406
+ }
33407
+ function defaultFromAddress(pubkey, ownedAddresses) {
33408
+ const isNpubAlias = (a) => {
33409
+ const at = a.indexOf("@");
33410
+ return at > 0 && a.slice(0, at).toLowerCase().startsWith("npub1");
33411
+ };
33412
+ const alias = ownedAddresses.find((a) => !isNpubAlias(a));
33413
+ return alias ?? ownedAddresses[0] ?? `${nip19_exports.npubEncode(pubkey)}@${DEFAULT_MAIL_DOMAIN}`;
33414
+ }
33357
33415
  function verifySealAndRumor(wrap, seal, rumor, opts = {}) {
33358
33416
  const maxAge = opts.maxAgeSeconds ?? MAX_RUMOR_AGE_SECONDS;
33359
33417
  const now3 = opts.now ?? Math.floor(Date.now() / 1e3);
@@ -33719,9 +33777,11 @@ async function publishEverywhere(pool, relays, event) {
33719
33777
  };
33720
33778
  }
33721
33779
 
33722
- // ../agent/dist/chunk-ZPXEAOMQ.js
33780
+ // ../agent/dist/chunk-5L26ARX5.js
33723
33781
  var service_exports2 = {};
33724
33782
  __export3(service_exports2, {
33783
+ defaultSenderAddress: () => defaultSenderAddress,
33784
+ listAliases: () => listAliases,
33725
33785
  mailIdentity: () => mailIdentity,
33726
33786
  publishMailSetup: () => publishMailSetup,
33727
33787
  readMail: () => readMail,
@@ -33740,6 +33800,15 @@ async function mailIdentity() {
33740
33800
  const profile = await fetchProfile(pubkey).catch(() => null);
33741
33801
  return { pubkey, npub: nip19_exports.npubEncode(pubkey), nip05: profile?.nip05 ?? null };
33742
33802
  }
33803
+ async function listAliases() {
33804
+ const signer = await mailSigner();
33805
+ return fetchOwnedAddressesWith(signer).catch(() => []);
33806
+ }
33807
+ async function defaultSenderAddress() {
33808
+ const signer = await signerManager.getSigner();
33809
+ const pubkey = await signer.getPublicKey();
33810
+ return defaultFromAddress(pubkey, await listAliases());
33811
+ }
33743
33812
  async function readMail(opts = {}) {
33744
33813
  const signer = await mailSigner();
33745
33814
  const { mail, failures } = await readInboxWith(signer, {
@@ -33750,12 +33819,13 @@ async function readMail(opts = {}) {
33750
33819
  }
33751
33820
  async function sendMail(params) {
33752
33821
  const signer = await mailSigner();
33822
+ const from = params.from ?? await defaultSenderAddress();
33753
33823
  return sendMailWith(signer, {
33754
33824
  to: params.to,
33755
33825
  ...params.subject !== void 0 ? { subject: params.subject } : {},
33756
33826
  ...params.text !== void 0 ? { text: params.text } : {},
33757
33827
  ...params.raw !== void 0 ? { raw: params.raw } : {},
33758
- ...params.from !== void 0 ? { from: params.from } : {},
33828
+ from,
33759
33829
  relays: relayManager.getRelaysForModule("mail")
33760
33830
  });
33761
33831
  }
@@ -44700,7 +44770,7 @@ var coerce = {
44700
44770
  };
44701
44771
  var NEVER = INVALID;
44702
44772
 
44703
- // ../agent/dist/chunk-QX4DSGPL.js
44773
+ // ../agent/dist/chunk-JORUNTXB.js
44704
44774
  function ok(text, data) {
44705
44775
  return data !== void 0 ? { ok: true, text, data } : { ok: true, text };
44706
44776
  }
@@ -45956,14 +46026,31 @@ function buildMailTools() {
45956
46026
  server.registerTool(
45957
46027
  "who_is_my_mail_address",
45958
46028
  {
45959
- description: "Show the mail address (NIP-05) and pubkey of the account currently signed in to this server.",
46029
+ description: "Show the account signed in to this server, its default mail address, and every alias it can send as.",
45960
46030
  inputSchema: {}
45961
46031
  },
45962
46032
  async () => {
45963
46033
  const who = await service_exports2.mailIdentity();
46034
+ const aliases = await service_exports2.listAliases();
46035
+ const defaultFrom = await service_exports2.defaultSenderAddress();
45964
46036
  return ok(
45965
- who.nip05 ? `Your mail address is ${who.nip05}.` : `Signed in as ${who.npub}. No mailstr address is bound to this key yet \u2014 claim one with claim_mailbox.`,
45966
- who
46037
+ `Signed in as ${who.npub}. Sending as ${defaultFrom}.` + (aliases.length ? ` Aliases: ${aliases.join(", ")}.` : " No registered alias yet \u2014 claim one with claim_mailbox."),
46038
+ { ...who, aliases, defaultFrom }
46039
+ );
46040
+ }
46041
+ );
46042
+ server.registerTool(
46043
+ "list_mail_aliases",
46044
+ {
46045
+ description: "List every mail address (NIP-05 alias) this account can send as, and which is the default. All aliases share one inbox.",
46046
+ inputSchema: {}
46047
+ },
46048
+ async () => {
46049
+ const aliases = await service_exports2.listAliases();
46050
+ const defaultFrom = await service_exports2.defaultSenderAddress();
46051
+ return ok(
46052
+ aliases.length ? `You can send as ${aliases.length} address(es); default is ${defaultFrom}.` : `No aliases yet \u2014 sending uses ${defaultFrom}. Claim an address with claim_mailbox.`,
46053
+ { aliases, defaultFrom }
45967
46054
  );
45968
46055
  }
45969
46056
  );
@@ -45987,6 +46074,18 @@ function buildMailTools() {
45987
46074
  if (args.text === void 0 && args.raw === void 0) {
45988
46075
  return fail("send_mail needs either `text` or `raw`.", "BAD_INPUT");
45989
46076
  }
46077
+ if (args.from !== void 0) {
46078
+ const identity = await service_exports2.mailIdentity();
46079
+ const aliases = await service_exports2.listAliases();
46080
+ const npubAddress = `${identity.npub}@${DEFAULT_MAIL_DOMAIN}`;
46081
+ const validSenders = [npubAddress, ...aliases];
46082
+ if (!validSenders.map((a) => a.toLowerCase()).includes(args.from.toLowerCase())) {
46083
+ return fail(
46084
+ `You cannot send as "${args.from}". Use one of: ${validSenders.join(", ")}.`,
46085
+ "BAD_SENDER"
46086
+ );
46087
+ }
46088
+ }
45990
46089
  try {
45991
46090
  const result = await service_exports2.sendMail({
45992
46091
  to: args.to,