@markmnl/fmsg-mcp 1.4.0 → 1.4.2

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/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - `react` is non-destructive again (`destructiveHint: false`), reversing 1.4.1: a reaction is one emoji
6
+ its sender changes or clears with another reaction, and it alters no one's messages, so hosts needn't
7
+ ask before each one.
8
+ - Mailbox tools set `openWorldHint: false` (inbox, threads, drafts, mark read, attachment URLs).
9
+ `react` is `destructiveHint: true` because a reaction message cannot be recalled, so a host may ask
10
+ before one. `indicate` stays non-destructive. Sends to other addresses stay open-world and destructive.
5
11
  - FMSG-006 indicators. The new `indicate` tool shows the other participants of a message a short-lived
6
12
  status such as ⏳ or ✍️, refreshed at half its ttl (every 1–30 s) and cleared when the agent replies in
7
13
  that thread, reacts, marks it read, after 5 minutes, or when the session ends; an empty emoji clears it
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.4.0",
3
- "commit": "36e8dafb2afbee867f5518c63b662cd500f24ddb",
4
- "describe": "v1.4.0"
2
+ "version": "1.4.2",
3
+ "commit": "33469ebc77bea6add5bfd454d8d60914d1b90e31",
4
+ "describe": "v1.4.2"
5
5
  }
@@ -4,7 +4,10 @@ import { toolError } from "../errors.js";
4
4
  import { OAuthRequestError } from "../oauth/errors.js";
5
5
  import { FmsgHttpError } from "../client/client.js";
6
6
  import { UNTRUSTED_CONTENT_NOTICE, attachmentType, headerValue, isoTime, preview, sortAttachments } from "../render.js";
7
- export const READ_ONLY = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true };
7
+ // Mailbox reads stay inside the signed-in address, so openWorldHint is false.
8
+ // Hosts that treat open-world tools as reaching arbitrary other parties otherwise
9
+ // flag an inbox read. SENDS stays open-world: a send can reach any fmsg address.
10
+ export const READ_ONLY = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
8
11
  export const SENDS = { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true };
9
12
  export const idSchema = z.string().regex(/^[0-9]+$/u, "fmsg message ids are decimal integers").describe("fmsg message id");
10
13
  /**
@@ -105,7 +105,7 @@ export const registerDownloadTool = (server, deps) => {
105
105
  content_type: z.string().regex(/^[\x20-\x7e]+$/u).optional().describe("inferred from the filename when omitted"),
106
106
  }),
107
107
  outputSchema: outputObject({ draft_id: z.string(), filename: z.string(), ...presignedOutput.shape }),
108
- annotations: { ...SENDS, destructiveHint: false },
108
+ annotations: { ...SENDS, destructiveHint: false, openWorldHint: false },
109
109
  }, async ({ id, filename, size, sha256, content_type }, ctx) => withCaller(deps, ctx, async (caller, signal) => {
110
110
  const draft = await caller.client.getMessage(id, signal);
111
111
  if (draft.time !== null && draft.time !== undefined)
@@ -51,7 +51,7 @@ export const registerDraftTools = (server, deps) => {
51
51
  next: z.string(),
52
52
  hint: hintOutput,
53
53
  }),
54
- annotations: { ...SENDS, destructiveHint: false },
54
+ annotations: { ...SENDS, destructiveHint: false, openWorldHint: false },
55
55
  }, async ({ to, topic, reply_to, body, type, important, no_reply, allow_no_reply, include_owner, attachments }, ctx) => {
56
56
  if (reply_to && topic)
57
57
  return toolError("a reply has no topic: pass topic only for a new message");
@@ -151,7 +151,7 @@ export const registerDraftTools = (server, deps) => {
151
151
  description: "Delete an unsent draft and its attachments. Sent messages cannot be deleted.",
152
152
  inputSchema: z.strictObject({ id: idSchema.describe("the draft to delete") }),
153
153
  outputSchema: outputObject({ id: z.string(), deleted: z.boolean() }),
154
- annotations: { ...SENDS, idempotentHint: true },
154
+ annotations: { ...SENDS, idempotentHint: true, openWorldHint: false },
155
155
  }, async ({ id }, ctx) => withCaller(deps, ctx, async (caller, signal) => {
156
156
  const draft = await caller.client.getMessage(id, signal);
157
157
  if (draft.time !== null && draft.time !== undefined)
@@ -166,7 +166,7 @@ export const registerReadTools = (server, deps) => {
166
166
  marked: z.array(outputObject({ id: z.string(), time_read: z.string().nullable() })),
167
167
  failed: z.array(outputObject({ id: z.string(), error: z.string() })),
168
168
  }),
169
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
169
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
170
170
  }, async ({ ids }, ctx) => withCaller(deps, ctx, async (caller, signal) => {
171
171
  deps.indicators.handled(caller, ids);
172
172
  const marked = [];
@@ -245,6 +245,9 @@ export const registerSendTools = (server, deps) => {
245
245
  emoji: z.string().max(32).nullable().describe("a single emoji; null or empty clears your reaction"),
246
246
  }),
247
247
  outputSchema: outputObject({ id: z.string(), reaction_id: z.string().nullable(), time: z.string().nullable(), cleared: z.boolean() }),
248
+ // Not destructive: a reaction is one emoji that its sender changes or
249
+ // clears with another reaction, and it alters no one's messages, so hosts
250
+ // needn't ask before each one (maintainer decision).
248
251
  annotations: { ...SENDS, destructiveHint: false, idempotentHint: true },
249
252
  }, async ({ id, emoji }, ctx) => withCaller(deps, ctx, async (caller, signal) => {
250
253
  const value = emoji && emoji.trim() ? emoji.trim() : null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@markmnl/fmsg-mcp",
3
3
  "mcpName": "io.github.markmnl/fmsg-mcp",
4
- "version": "1.4.0",
4
+ "version": "1.4.2",
5
5
  "description": "MCP server for fmsg: send and receive federated messages from any AI agent via a deployed fmsg Web API",
6
6
  "type": "module",
7
7
  "license": "MIT",
package/server.json CHANGED
@@ -8,13 +8,13 @@
8
8
  "url": "https://github.com/markmnl/fmsg-mcp",
9
9
  "source": "github"
10
10
  },
11
- "version": "1.4.0",
11
+ "version": "1.4.2",
12
12
  "packages": [
13
13
  {
14
14
  "registryType": "npm",
15
15
  "registryBaseUrl": "https://registry.npmjs.org",
16
16
  "identifier": "@markmnl/fmsg-mcp",
17
- "version": "1.4.0",
17
+ "version": "1.4.2",
18
18
  "transport": {
19
19
  "type": "stdio"
20
20
  },