@remit/mcp-server 0.9.0 → 0.10.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/dist/deps.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { RemitClient } from "@remit/backend/client";
2
- import type { IAccountRepository, IAddressRepository, IFilterRepository, IMailboxRepository, IMessageRepository, IOrganizeJobRequestRepository, IThreadMessageRepository, OrganizeJobEvent, QueueProducer } from "@remit/data-ports";
2
+ import type { IAccountRepository, IAddressRepository, IFilterRepository, ILabelMailboxRepository, ILabelRepository, IMailboxRepository, IMessageRepository, IOrganizeJobRequestRepository, IThreadMessageRepository, OrganizeJobEvent, QueueProducer } from "@remit/data-ports";
3
3
  import type { BodyPartMaterializer, BodySyncCue, MailboxQueueService, MaterializeLogger, OrganizeJobLogger } from "@remit/mailbox-service";
4
4
  import type { StorageService } from "@remit/storage-service";
5
5
  export declare const FOLDER_SETTLE_TIMEOUT_MS = 20000;
@@ -15,12 +15,14 @@ export interface FilterToolDeps {
15
15
  logger: OrganizeJobLogger;
16
16
  }
17
17
  export interface McpDeps {
18
- account: Pick<IAccountRepository, "get" | "listAllByAccountConfig">;
18
+ account: Pick<IAccountRepository, "get" | "listAllByAccountConfig" | "listMailSourcesByAccountConfig">;
19
19
  mailbox: Pick<IMailboxRepository, "get" | "listAllByAccount" | "findByPath" | "resolveAccountId">;
20
20
  address: Pick<IAddressRepository, "countCategoryOverridesSince">;
21
+ label: Pick<ILabelRepository, "create" | "findByNormalizedName" | "listByAccountConfig">;
22
+ labelMailbox: Pick<ILabelMailboxRepository, "get" | "put" | "listByAccount" | "listByLabel">;
21
23
  message: Pick<IMessageRepository, "get" | "describe" | "countClassifiedSince">;
22
24
  threadMessage: Pick<IThreadMessageRepository, "listByMailbox" | "listByThread" | "searchByDate">;
23
- filter: Pick<IFilterRepository, "listPageByAccountConfig" | "refreshExpiry" | "create" | "get" | "update">;
25
+ filter: Pick<IFilterRepository, "listByAccountConfig" | "refreshExpiry" | "create" | "get" | "update">;
24
26
  organizeJobRequest: Pick<IOrganizeJobRequestRepository, "create" | "get">;
25
27
  storage: Pick<StorageService, "retrieveBodyPart">;
26
28
  bodySync: BodyPartMaterializer;
package/dist/deps.js CHANGED
@@ -4,6 +4,8 @@ export const mcpDepsOf = (client, { logger, settle }) => ({
4
4
  account: client.account,
5
5
  mailbox: client.mailbox,
6
6
  address: client.address,
7
+ label: client.label,
8
+ labelMailbox: client.labelMailbox,
7
9
  message: client.message,
8
10
  threadMessage: client.threadMessage,
9
11
  filter: client.filter,
@@ -2,7 +2,7 @@ import { FILTER_NO_ACTION, FilterRuleV1Schema, filterClausesFromRule, MAX_FILTER
2
2
  import { isNotFoundError } from "@remit/data-ports/errors";
3
3
  import { deriveAccountConfigId } from "@remit/data-ports/id";
4
4
  import { FilterClauseField, FilterMatchOperator, FilterScope, FilterState, } from "@remit/domain-enums";
5
- import { accept, createFilter, listFilters, ownedAccount, queueOrganizeJob, refuse, updateFilter, } from "@remit/mailbox-service";
5
+ import { accept, createFilter, listAccountFilters, ownedAccount, queueOrganizeJob, refuse, updateFilter, } from "@remit/mailbox-service";
6
6
  import { z } from "zod";
7
7
  import { asJson, refused, toolError } from "./tool-result.js";
8
8
  const FILTER_WRITE = {
@@ -63,7 +63,7 @@ const literalClauses = z
63
63
  }))
64
64
  .min(1)
65
65
  .describe("Literal clauses combined by matchOperator. Send these or rule.");
66
- const rule = FilterRuleV1Schema.describe(`A Sieve-like test tree. Send this or literalClauses, never both. At most ${MAX_FILTER_RULE_LEAVES} header, address or hasWords tests and ${MAX_FILTER_RULE_DEPTH} levels, counting the top test. Example: {"version":1,"test":{"test":"allof","tests":[{"test":"address","match":"Is","part":"RegistrableDomain","key":"github.com"},{"test":"not","operand":{"test":"header","match":"Contains","header":"Subject","key":"security"}}]}}`);
66
+ const rule = FilterRuleV1Schema.describe(`A Sieve-like test tree. Send this or literalClauses, never both. At most ${MAX_FILTER_RULE_LEAVES} header, address, hasWords or classification tests and ${MAX_FILTER_RULE_DEPTH} levels, counting the top test. Example: {"version":1,"test":{"test":"allof","tests":[{"test":"address","match":"Is","part":"RegistrableDomain","key":"github.com"},{"test":"not","operand":{"test":"header","match":"Contains","header":"Subject","key":"security"}}]}}`);
67
67
  const priority = z
68
68
  .int()
69
69
  .describe("Evaluation order: lower runs first; ties go to the most recently changed filter. The first matching filter with a folder decides the move, and labels add up");
@@ -95,26 +95,14 @@ export const registerFilterTools = (server, deps, userId) => {
95
95
  logger: deps.filters.logger,
96
96
  };
97
97
  server.registerTool("list_filters", {
98
- description: "List the user's mail filters: what each matches (rule, plus literalClauses when the rule is a flat list), its priority and stop, where it moves or labels mail, and whether it is on. A page at a time.",
99
- inputSchema: {
100
- accountId,
101
- continuationToken: z
102
- .string()
103
- .optional()
104
- .describe("Opaque cursor from a previous response"),
105
- },
98
+ description: "List the mail filters that act on one account: what each matches (rule, plus literalClauses when the rule is a flat list), its priority and stop, where it moves or labels mail, and whether it is on. A filter that moves mail into a folder is listed under the account that holds the folder; a filter that only labels mail, or moves it into a label's folder, is listed under every account.",
99
+ inputSchema: { accountId },
106
100
  annotations: { readOnlyHint: true, openWorldHint: false },
107
101
  }, async (args) => {
108
- const page = await listFilters(deps, accountConfigId, {
109
- accountId: args.accountId,
110
- continuationToken: args.continuationToken,
111
- }).catch(notFound);
112
- if (!page.ok)
113
- return refused(page.refusal);
114
- return asJson({
115
- items: page.value.items.map(toFilter),
116
- continuationToken: page.value.continuationToken,
117
- });
102
+ const listed = await listAccountFilters(deps, accountConfigId, args.accountId).catch(notFound);
103
+ if (!listed.ok)
104
+ return refused(listed.refusal);
105
+ return asJson({ items: listed.value.map(toFilter) });
118
106
  });
119
107
  server.registerTool("create_filter", {
120
108
  description: "Create a filter that sorts new mail, and queue a job that applies it to existing mail. Match with literalClauses for a flat list, or with rule for not, nesting and exact matches. Returns the filter and the organizeJobId of that job. A target folder must be confirmed by the mail server first.",
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { McpDeps } from "./deps.js";
3
+ export declare const registerLabelReadTools: (server: McpServer, deps: McpDeps, accountConfigId: string) => void;
4
+ export declare const registerLabelWriteTools: (server: McpServer, deps: McpDeps, accountConfigId: string) => void;
@@ -0,0 +1,52 @@
1
+ import { LabelColor } from "@remit/domain-enums";
2
+ import { createLabelWithFolders, listLabelsWithFolders, } from "@remit/mailbox-service";
3
+ import { z } from "zod";
4
+ import { asJson, refused } from "./tool-result.js";
5
+ const READ_ONLY = { readOnlyHint: true, openWorldHint: false };
6
+ const LABEL_WRITE = {
7
+ readOnlyHint: false,
8
+ destructiveHint: false,
9
+ idempotentHint: true,
10
+ openWorldHint: false,
11
+ };
12
+ const toLabel = ({ label, folders }) => ({
13
+ labelId: label.labelId,
14
+ name: label.name,
15
+ color: label.color,
16
+ folders,
17
+ });
18
+ export const registerLabelReadTools = (server, deps, accountConfigId) => {
19
+ server.registerTool("list_labels", {
20
+ description: "List the user's labels. Each label lists its folder in every mail account, with state Present, Pending (the mail server has not confirmed it yet) or Missing, and the mailboxId of a folder that is there.",
21
+ inputSchema: {},
22
+ annotations: READ_ONLY,
23
+ }, async () => {
24
+ const labels = await listLabelsWithFolders(deps, accountConfigId);
25
+ return asJson({ labels: labels.map(toLabel) });
26
+ });
27
+ };
28
+ export const registerLabelWriteTools = (server, deps, accountConfigId) => {
29
+ server.registerTool("create_label", {
30
+ description: "Create a label and its folder in every mail account, under the account's remit folder. A label with the same name is reused, and calling again creates only the folders that are missing. Returns each folder's state; a Pending folder settles once the mail server confirms it, which list_labels shows.",
31
+ inputSchema: {
32
+ name: z.string().trim().min(1).max(140).describe("Label name"),
33
+ color: z
34
+ .enum(LabelColor)
35
+ .optional()
36
+ .describe("Display color, Default when left out"),
37
+ },
38
+ annotations: LABEL_WRITE,
39
+ }, async ({ name, color }) => {
40
+ const created = await createLabelWithFolders({
41
+ account: deps.account,
42
+ mailbox: deps.mailbox,
43
+ label: deps.label,
44
+ labelMailbox: deps.labelMailbox,
45
+ mailboxQueue: deps.folders.mailboxQueue,
46
+ log: deps.logger,
47
+ }, accountConfigId, { name, color });
48
+ if (!created.ok)
49
+ return refused(created.refusal);
50
+ return asJson(toLabel(created.value));
51
+ });
52
+ };
package/dist/server.js CHANGED
@@ -4,6 +4,7 @@ import { deriveAccountConfigId } from "@remit/data-ports/id";
4
4
  import { MCP_READ_SCOPE, MCP_RULES_SCOPE } from "./auth/scopes.js";
5
5
  import { registerFilterTools } from "./filter-tools.js";
6
6
  import { registerFolderTools } from "./folder-tools.js";
7
+ import { registerLabelReadTools, registerLabelWriteTools, } from "./label-tools.js";
7
8
  import { registerReadTools } from "./read-tools.js";
8
9
  const unusedJsonSchemaValidator = {
9
10
  getValidator: () => {
@@ -14,9 +15,11 @@ const buildServer = (principal, deps) => {
14
15
  const server = new McpServer({ name: "remit-reader", version: "1.0.0" }, { jsonSchemaValidator: unusedJsonSchemaValidator });
15
16
  if (principal.scopes.includes(MCP_READ_SCOPE)) {
16
17
  registerReadTools(server, deps, deriveAccountConfigId(principal.userId));
18
+ registerLabelReadTools(server, deps, deriveAccountConfigId(principal.userId));
17
19
  }
18
20
  if (principal.scopes.includes(MCP_RULES_SCOPE)) {
19
21
  registerFolderTools(server, deps, deriveAccountConfigId(principal.userId));
22
+ registerLabelWriteTools(server, deps, deriveAccountConfigId(principal.userId));
20
23
  registerFilterTools(server, deps, principal.userId);
21
24
  }
22
25
  return server;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/mcp-server",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "The remit MCP server: mail tools over streamable HTTP and the OAuth authorization server that guards them.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -21,16 +21,16 @@
21
21
  "mcp-login": "node --import tsx src/cli/mcp-login.ts"
22
22
  },
23
23
  "dependencies": {
24
- "@remit/logger": "*",
24
+ "@remit/logger": "0.2.0",
25
25
  "@better-auth/oauth-provider": "^1.7.12",
26
26
  "@hono/node-server": "^2.1.3",
27
27
  "@modelcontextprotocol/sdk": "1.32.1",
28
- "@remit/auth-service": "*",
29
- "@remit/data-ports": "*",
30
- "@remit/domain-enums": "*",
31
- "@remit/drizzle-service": "*",
32
- "@remit/mailbox-service": "*",
33
- "@remit/storage-service": "*",
28
+ "@remit/auth-service": "0.3.7",
29
+ "@remit/data-ports": "2.6.0",
30
+ "@remit/domain-enums": "0.0.50",
31
+ "@remit/drizzle-service": "3.4.0",
32
+ "@remit/mailbox-service": "2.18.0",
33
+ "@remit/storage-service": "0.0.36",
34
34
  "@remit/ui": "0.0.234",
35
35
  "better-auth": "^1.7.12",
36
36
  "drizzle-orm": "1.0.0-rc.5",
@@ -38,12 +38,12 @@
38
38
  "react": "^19",
39
39
  "react-dom": "^19",
40
40
  "zod": "^4.6.7",
41
- "@remit/backend": "*",
41
+ "@remit/backend": "5.4.0",
42
42
  "@types/react": "^19",
43
43
  "@types/react-dom": "^19"
44
44
  },
45
45
  "devDependencies": {
46
- "@remit/imap-worker": "*",
46
+ "@remit/imap-worker": "3.2.0",
47
47
  "@tailwindcss/vite": "^4",
48
48
  "tailwindcss": "^4",
49
49
  "tsx": "*",