@sendora/mcp 1.0.0 → 1.2.12

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
@@ -2,12 +2,12 @@
2
2
 
3
3
  The official [Model Context Protocol](https://modelcontextprotocol.io) server for
4
4
  [Sendora](https://sendora.se), transactional email delivery. It helps a coding
5
- agent such as Claude Code, Cursor or VS Code build against the API or
6
- `@sendora/sdk`: it reads Sendora's documentation, sends a test mail and reads what
5
+ agent such as Claude Code, Cursor or VS Code build against the API,
6
+ `@sendora/sdk` for TypeScript or `sendora` for Python: it reads Sendora's documentation, sends a test mail and reads what
7
7
  happened to it, and sets up streams, webhooks and sending domains.
8
8
 
9
9
  The documentation is built into it: the guides, every operation of the API
10
- reference, the error codes and the SDK's README and rules for agents, from the
10
+ reference, the error codes and each SDK's README and rules for agents, from the
11
11
  same sources as [sendora.se/docs](https://sendora.se/docs), bundled with the
12
12
  release. It works offline and cannot describe a different version of the API
13
13
  than the one it shipped with.
@@ -63,12 +63,14 @@ on a test server, and what cannot be undone happens only to a test server.
63
63
  `read_inbound_message`, `list_inbound_domains`, `check_inbound_domain` and
64
64
  `add_inbound_domain`. The text of a received mail was written by a stranger:
65
65
  it reaches the model only from `read_inbound_message`, fenced and marked as
66
- data, which lessens prompt injection without preventing it.
66
+ data, in an answer that is text alone so that every client shows the model
67
+ the fence, which lessens prompt injection without preventing it.
67
68
  - The account key: `list_servers`, `list_domains`, `check_domain`,
68
69
  `add_domain` and `delete_test_server`, which refuses a live server.
69
70
 
70
71
  The documents are resources under `sendora://docs/…`, `sendora://reference/…`,
71
- `sendora://errors`, `sendora://sdk/readme` and `sendora://sdk/skill`, listed by
72
+ `sendora://errors`, `sendora://sdk/readme`, `sendora://sdk/skill`,
73
+ `sendora://sdk/python/readme` and `sendora://sdk/python/skill`, listed by
72
74
  `sendora://index`. Three prompts: `set_up_sendora`, `why_not_delivered` and
73
75
  `verify_webhooks`.
74
76
 
package/dist/docs.js CHANGED
@@ -23,6 +23,7 @@ function indexOf(documents) {
23
23
  ].join('\n'),
24
24
  };
25
25
  }
26
+ const SKILLS = new Set(['sendora://sdk/skill', 'sendora://sdk/python/skill']);
26
27
  /** The bundle as `scripts/build-docs.ts` writes it, checked, with the index in front. */
27
28
  export function docsFrom(bundle) {
28
29
  const built = bundleSchema.parse(bundle).documents;
@@ -31,7 +32,9 @@ export function docsFrom(bundle) {
31
32
  if (byUri.size !== documents.length) {
32
33
  throw new Error('two documents of the bundle share an address');
33
34
  }
34
- const index = searchIndex(documents);
35
+ // The skills are rules an agent reads whole, as the instructions say; searched,
36
+ // their condensed code ranked above the guides it condenses.
37
+ const index = searchIndex(documents.filter((doc) => !SKILLS.has(doc.uri)));
35
38
  return {
36
39
  documents,
37
40
  find: (uri) => byUri.get(uri.split('#')[0] ?? uri),
@@ -46,7 +49,7 @@ export function loadDocs(from = bundleUrl) {
46
49
  raw = readFileSync(from, 'utf8');
47
50
  }
48
51
  catch (error) {
49
- throw new Error('The documentation bundle is missing; in the repository, run node apps/mcp/scripts/build-docs.ts', { cause: error });
52
+ throw new Error('The documentation bundle is missing; in the repository, run pnpm --filter @sendora/sdk build && node apps/mcp/scripts/build-docs.ts', { cause: error });
50
53
  }
51
54
  return docsFrom(JSON.parse(raw));
52
55
  }
package/dist/errors.js CHANGED
@@ -41,7 +41,7 @@ const hints = {
41
41
  server_reserved: null,
42
42
  server_in_flight: null,
43
43
  request_too_large: null,
44
- from_domain_not_verified: () => `The from address's domain must be a verified sending domain of the account. Sending domains are the account's, not a server's: with the account key, add_domain adds one and answers the DNS records to publish, and check_domain looks them up; or an administrator adds it under Domains (Domäner) in the dashboard, ${DASHBOARD}/domains.`,
44
+ from_domain_not_verified: () => `The from address's domain must be one of the account's sending domains that get_server lists as sendingDomains. To add one, which is the account's and not a server's: with the account key, add_domain adds it and answers the DNS records to publish, and check_domain looks them up; or an administrator adds it under Domains (Domäner) in the dashboard, ${DASHBOARD}/domains.`,
45
45
  stream_not_found: null,
46
46
  stream_archived: null,
47
47
  stream_paused: null,
@@ -5,7 +5,7 @@
5
5
  * operation arrives with its tool or its line here.
6
6
  */
7
7
  export const exclusions = {
8
- 'POST /v1/email/batch': 'An agent sends one message and reads its outcome; a batch is code’s work, which the SDK serves.',
8
+ 'POST /v1/email/batch': 'An agent sends one message and reads its outcome; a batch is code’s work, which the SDKs serve.',
9
9
  'GET /v1/inbound/{id}/raw': 'The raw MIME is bytes for a parser; read_inbound_message gives the parsed message inside the fence.',
10
10
  'GET /v1/inbound/{id}/attachments/{attachmentId}': 'A stranger’s attachment bytes are not for a model’s context; the read describes each attachment.',
11
11
  'GET /v1/inbound/domains/{id}': 'list_inbound_domains returns every field.',
@@ -25,4 +25,6 @@ export const exclusions = {
25
25
  'DELETE /v1/servers/{id}/tokens/{tokenId}': 'Revoking a key can break a live integration and cannot be undone.',
26
26
  'GET /v1/domains/{id}': 'list_domains returns every field.',
27
27
  'DELETE /v1/domains/{id}': 'A sending domain serves every server, live ones included; deleting it stops their mail and cannot be undone.',
28
+ 'POST /v1/erasures': 'It deletes a person’s data on every server and cannot be undone; the customer’s staff or code asks for it.',
29
+ 'GET /v1/erasures/{id}': 'It reads an erasure, which no tool requests.',
28
30
  };