@sendora/mcp 1.5.2 → 1.8.6
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/LICENSE +2 -0
- package/README.md +18 -8
- package/dist/docs.js +5 -1
- package/dist/errors.js +11 -0
- package/dist/generated/docs.json +1 -1
- package/dist/generated/screens/domain-records.html +1 -1
- package/dist/generated/screens/message-timeline.html +1 -1
- package/dist/icons.js +20 -0
- package/dist/instructions.js +2 -2
- package/dist/prompts.js +5 -5
- package/dist/search.js +10 -7
- package/dist/server.js +2 -0
- package/dist/tools/docs.js +1 -1
- package/dist/tools/messages.js +11 -4
- package/dist/tools/server.js +12 -0
- package/dist/tools/webhooks.js +3 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
package/dist/icons.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* A client shows a server's icon at 16 to 32 px, where brand/README.md asks
|
|
3
|
+
* for the small cut on its cobalt tile, which reads on light and dark alike.
|
|
4
|
+
* The files are written inline, so neither the npm package nor the hosted
|
|
5
|
+
* image reads brand/ at run time. test/brand.test.ts checks that they decode
|
|
6
|
+
* to brand/favicon.svg and brand/favicon-64.png.
|
|
7
|
+
*/
|
|
8
|
+
/** The icons that serverInfo lists. */
|
|
9
|
+
export const icons = [
|
|
10
|
+
{
|
|
11
|
+
src: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NCA2NCI+PHJlY3Qgd2lkdGg9IjY0IiBoZWlnaHQ9IjY0IiByeD0iOCIgZmlsbD0iIzIyNDVkMSIvPjxwYXRoIGZpbGw9IiNmZmZmZmYiIGQ9Ik00IDIwSDEwLjZDMTIuNyAyMCAxNC44IDIwLjggMTYuMyAyMi4zTDI5LjkgMzZINDBWNDRIMjkuNEMyNy4zIDQ0IDI1LjIgNDMuMiAyMy43IDQxLjdMMTAuMSAyOEg0Wk01NCA0NFE1MS41IDQ0IDQ5LjcgNDIuM1E0OCA0MC41IDQ4IDM4UTQ4IDM1LjUgNDkuNyAzMy43UTUxLjUgMzIgNTQgMzJRNTYuNSAzMiA1OC4zIDMzLjdRNjAgMzUuNSA2MCAzOFE2MCA0MC41IDU4LjMgNDIuM1E1Ni41IDQ0IDU0IDQ0WiIvPjwvc3ZnPgo=',
|
|
12
|
+
mimeType: 'image/svg+xml',
|
|
13
|
+
sizes: ['any'],
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAEP0lEQVR42u2bS0xcVRjHf+fOINAyDFAp1LS0ZXCjVAXRmlpsRdChMakLU+MjpQn4rCZCjLpQ042xCwVjqDYtNZamBqUL2yYQacWkunAhFiqlSqkQWkt5TQemM8M87hwXBFKRx6UOE4Z7/6ubOd+d5P+b75zvu+fOEUxRZmHbBgllwGMC1iFYTjRL4pbQg5CnBdT8deq+9puHxcRFlr0zNqR6q5DyZYRQWIqSqCD3xyWbKzrqs/2TALLsnbFq0NsoBI+iA0lJc3yyqbijPtuvAKiq91O9mAcQgoIxZ/ATAJFZ2LYBZOuSTftZpoMwhe5VJJTpzvz45DdJVZQqCArRqSSiSAGZgW4l1yoCkaBX+0IIi4LOZQAwABgADAAGAAOAAcAAYADQq8xTP7jUdM9Muyg4RoJ09fpo/mWUbxoduNxq9D8PZBa1SS0ApsrpUtlT/Tcnf3TqcwokWUxUvZvB68+n6XcNEALKS9J444U0fS+Cb+5Mi9pMCFsVKC9JY/dzK/VdBit2pUcdhLD3ARW70nnt2ZXRWwZn0g57Ch+Wr0YR2r744y+v8UXdwKIHYEq2vbpHS+D5Li8DwwEKHkpEaICwKScBf0Dya7t7aQCYhOAIULBRG4SHcxLw+UO0nPcsDQAA7RfnCSHXwphv8UIw38pNdQ0O4mIV3nvlDk0Q3i5dxZhfUvvd0IIZSVhmouSpFdjzraxfHYuU0HPFR8OZEWqPD+H2hsKTARNq/cODy6PySJ5FU8e4Jc/C9dEg5/70ht181to46ipt2DdbSU2JIcYsiDELUlNi2JSTwJNbk/j5NxeOETV8AABaL3hwe0Lka4XwQCLDI0F+7/SG9Zevq7SxJv22GWOsFhNb8hI59r2DQFCGDwDA2QsevGMhNt+vDcLWBxMZcoYPQtnTqdjzrXPGWS0mbnhD/6lKmvuAufTSjlTeKVsV9vS2PX5u1vGTn9/JXVnxmhfw7bsvLkwneODbQT460BfxVXz9mljNsZnTxIa1Fa45Nsjeg32R/r/P/4oN+7PAwfpBKr+6FjEAPVd8mmO7p4ldkE3RfV8PUHW4PyIAGs6MzCPWGbld4eqj/VQd7p9Xit6Kao8PcbnPP2dc71U/R04MR3ZbvPpoP+V7e3G6Fm732O0NUfZ+N71X/bOaf/GDbjzTdINhK4OzybLcxDPFKRRstGDLiGNFkllTC62lDE5oWbzCzu23U5xvnVztL/X6aPzJyZETw9OajxgA482QAcAAYAAwABgADAAGAAOAAcAAsPgASCldejUvJaMKQlzWcQL0KiBP6dW9gCZFQM34iUrdnZhSlZB6SBk/Syv3688/+7qaczsUgLhkc4VE/qAj+6dTrgfemiyDHfXZ/vgk8zaQ1Ut6OkhUKfks2RHc1tKSF/jX6fEJ2Z44e7dURalEFCHkumg/VieRN5CiR0CTElIPdTXndtw8/g9pUJA+sUHj5gAAAABJRU5ErkJggg==',
|
|
17
|
+
mimeType: 'image/png',
|
|
18
|
+
sizes: ['64x64'],
|
|
19
|
+
},
|
|
20
|
+
];
|
package/dist/instructions.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { KEYS_PAGE, } from './connection.js';
|
|
2
2
|
import { INDEX_URI } from './docs.js';
|
|
3
3
|
import { grantNote, LIVE_NOTE, SIMULATOR_NOTE, WRITES_OFF_NOTE } from './tools/server.js';
|
|
4
|
-
const ABOUT = "Sendora is a transactional email service: an HTTP API, SMTP submission, SDKs for TypeScript (@sendora/sdk) and
|
|
5
|
-
const DOCS = `Use the SDK for the project's language: @sendora/sdk for TypeScript or JavaScript, sendora for Python, the HTTP API otherwise. Before writing code, find the answer with search_docs and read the document with read_doc; ${INDEX_URI} lists them all, and sendora://sdk/skill and sendora://sdk/
|
|
4
|
+
const ABOUT = "Sendora is a transactional email service: an HTTP API, SMTP submission, SDKs for TypeScript (@sendora/sdk), Python (sendora) and PHP (sendora/sendora-php, and sendora/sendora-laravel for Laravel), signed webhooks and a message log with every delivery event. This server carries Sendora's documentation, and with a key, tools for that key's server or account.";
|
|
5
|
+
const DOCS = `Use the SDK for the project's language: @sendora/sdk for TypeScript or JavaScript, sendora for Python, sendora/sendora-php for PHP, or sendora/sendora-laravel in a Laravel app, which installs it; the HTTP API otherwise. Before writing code, find the answer with search_docs and read the document with read_doc; ${INDEX_URI} lists them all, and sendora://sdk/skill, sendora://sdk/python/skill and sendora://sdk/php/skill hold the rules an agent must keep with each SDK.`;
|
|
6
6
|
const KEYS = 'Never print or repeat a Sendora key, and never write one into a file under version control; keys belong in the environment.';
|
|
7
7
|
const LIVE_DASHBOARD = `A person makes and revokes this server's keys, and makes and deletes its webhooks, in the dashboard, ${KEYS_PAGE}, with changes on or off, since a new key or signing secret must not reach this conversation.`;
|
|
8
8
|
const ACCOUNT_DASHBOARD = `A server and a key are made in the dashboard, ${KEYS_PAGE}, since a new key is a secret this conversation must not see; a live server is deleted there too.`;
|
package/dist/prompts.js
CHANGED
|
@@ -18,13 +18,13 @@ export function registerPrompts(server) {
|
|
|
18
18
|
framework: z
|
|
19
19
|
.string()
|
|
20
20
|
.optional()
|
|
21
|
-
.describe('The web framework, such as Express, Next.js, Django or
|
|
21
|
+
.describe('The web framework, such as Express, Next.js, Django, FastAPI or Laravel.'),
|
|
22
22
|
}),
|
|
23
23
|
}, ({ framework }) => asking([
|
|
24
24
|
`Set up Sendora for transactional email in this project, which uses ${forFramework(framework)}.`,
|
|
25
25
|
'',
|
|
26
|
-
"1. Read sendora://docs/quickstart and the skill for the project's SDK, sendora://sdk/skill for TypeScript or sendora://sdk/
|
|
27
|
-
"2. Install the SDK for the project's language, @sendora/sdk for TypeScript or JavaScript
|
|
26
|
+
"1. Read sendora://docs/quickstart and the skill for the project's SDK, sendora://sdk/skill for TypeScript, sendora://sdk/python/skill for Python or sendora://sdk/php/skill for PHP, with read_doc before writing any code, and follow the skill's rules. In a Laravel app, read sendora://sdk/laravel/readme too.",
|
|
27
|
+
"2. Install the SDK for the project's language, @sendora/sdk for TypeScript or JavaScript, sendora for Python, sendora/sendora-php for PHP or sendora/sendora-laravel in a Laravel app, and construct the client once at startup from SENDORA_API_TOKEN in the environment; in any other language, call the HTTP API with that key. Never write a key into a file under version control, and never print one.",
|
|
28
28
|
'3. Add a function that sends one message, with the idempotency the SDK gives every send, or an Idempotency-Key header over HTTP.',
|
|
29
29
|
'4. If a test server key is connected here, send a test to delivered@simulator.sendora.se and to hardbounce@simulator.sendora.se, and show me how each ended; otherwise tell me how to connect one (sendora://docs/test-servers).',
|
|
30
30
|
'5. Add a webhook endpoint that verifies every delivery before it parses it, as sendora://docs/webhooks shows.',
|
|
@@ -48,11 +48,11 @@ export function registerPrompts(server) {
|
|
|
48
48
|
framework: z
|
|
49
49
|
.string()
|
|
50
50
|
.optional()
|
|
51
|
-
.describe('The web framework, such as Express, Next.js, Django or
|
|
51
|
+
.describe('The web framework, such as Express, Next.js, Django, FastAPI or Laravel.'),
|
|
52
52
|
}),
|
|
53
53
|
}, ({ framework }) => asking([
|
|
54
54
|
`Make this project's Sendora webhook endpoint in ${forFramework(framework)} verify every delivery.`,
|
|
55
55
|
'',
|
|
56
|
-
"Read sendora://docs/webhooks first. Before anything parses the body, verify it with the SDK for the project's language: in TypeScript verifyWebhook with the raw body and the Sendora-Signature header, or webhooks.receive with a Fetch Request; in Python verify_webhook with the raw body and the header; in any other language, the recipe in sendora://docs/webhooks. Answer 2xx quickly and do the work afterwards; key the handling on the event id, since deliveries arrive at least once. Keep the secret in the environment. Then test it: if a test server key is connected here, replay a delivery and check that the endpoint accepted it.",
|
|
56
|
+
"Read sendora://docs/webhooks first. Before anything parses the body, verify it with the SDK for the project's language: in TypeScript verifyWebhook with the raw body and the Sendora-Signature header, or webhooks.receive with a Fetch Request; in Python verify_webhook with the raw body and the header; in PHP WebhookVerifier::verify() with the raw body, the header and the secret; in a Laravel app the package's route, which checks each signature and fires a Laravel event; in any other language, the recipe in sendora://docs/webhooks. Answer 2xx quickly and do the work afterwards; key the handling on the event id, since deliveries arrive at least once. Keep the secret in the environment. Then test it: if a test server key is connected here, replay a delivery and check that the endpoint accepted it.",
|
|
57
57
|
].join('\n')));
|
|
58
58
|
}
|
package/dist/search.js
CHANGED
|
@@ -28,17 +28,20 @@ function singular(word) {
|
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* The terms of a text: its words lower-cased and singular, the words of a
|
|
31
|
-
* question that carry nothing left out, and a word joined by underscores
|
|
32
|
-
* as its parts, so `recipient_suppressed` is found
|
|
31
|
+
* question that carry nothing left out, and a word joined by underscores or
|
|
32
|
+
* written in camelCase also as its parts, so `recipient_suppressed` is found
|
|
33
|
+
* by either word, and so is `idempotencyKey`.
|
|
33
34
|
*/
|
|
34
35
|
export function termsOf(text) {
|
|
35
36
|
return text
|
|
36
|
-
.toLowerCase()
|
|
37
37
|
.split(/[^\p{L}\p{N}_]+/u)
|
|
38
|
-
.
|
|
39
|
-
.
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
.map((word) => ({ word: word.toLowerCase(), parts: word.split(/_|(?<=\p{Ll})(?=\p{Lu})/u) }))
|
|
39
|
+
.filter(({ word }) => word !== '' && !STOPWORDS.has(word))
|
|
40
|
+
.flatMap(({ word, parts }) => {
|
|
41
|
+
const kept = parts
|
|
42
|
+
.map((part) => part.toLowerCase())
|
|
43
|
+
.filter((part) => part !== '' && !STOPWORDS.has(part));
|
|
44
|
+
return kept.length > 1 ? [word, ...kept] : [word];
|
|
42
45
|
})
|
|
43
46
|
.map(singular);
|
|
44
47
|
}
|
package/dist/server.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/server';
|
|
2
2
|
import { registerDocuments } from './documents.js';
|
|
3
|
+
import { icons } from './icons.js';
|
|
3
4
|
import { instructionsFor } from './instructions.js';
|
|
4
5
|
import { listed, targetsFor } from './listing.js';
|
|
5
6
|
import { registerPrompts } from './prompts.js';
|
|
@@ -24,6 +25,7 @@ export function buildServer(docs, connection) {
|
|
|
24
25
|
title: 'Sendora',
|
|
25
26
|
version: MCP_VERSION,
|
|
26
27
|
websiteUrl: 'https://sendora.se/docs/mcp',
|
|
28
|
+
icons,
|
|
27
29
|
}, {
|
|
28
30
|
instructions: instructionsFor(connection),
|
|
29
31
|
cacheHints: {
|
package/dist/tools/docs.js
CHANGED
|
@@ -52,7 +52,7 @@ export const docsTools = [
|
|
|
52
52
|
operations: [],
|
|
53
53
|
register: (server, { docs }) => server.registerTool('read_doc', {
|
|
54
54
|
title: 'Read a Sendora document',
|
|
55
|
-
description: `Read one of Sendora's documents whole, as Markdown: a guide (sendora://docs/…), an API operation (sendora://reference/…), the error codes (sendora://errors) or an SDK's README and rules for agents (sendora://sdk/readme and sendora://sdk/skill for TypeScript, sendora://sdk/python/readme and sendora://sdk/python/skill for Python). ${INDEX_URI} lists every document; search_docs finds the right one.`,
|
|
55
|
+
description: `Read one of Sendora's documents whole, as Markdown: a guide (sendora://docs/…), an API operation (sendora://reference/…), the error codes (sendora://errors) or an SDK's README and rules for agents (sendora://sdk/readme and sendora://sdk/skill for TypeScript, sendora://sdk/python/readme and sendora://sdk/python/skill for Python, sendora://sdk/php/readme and sendora://sdk/php/skill for PHP, and sendora://sdk/laravel/readme for Laravel). ${INDEX_URI} lists every document; search_docs finds the right one.`,
|
|
56
56
|
inputSchema: z.object({
|
|
57
57
|
uri: z.string().min(1).describe(`A document's address, such as ${INDEX_URI}.`),
|
|
58
58
|
}),
|
package/dist/tools/messages.js
CHANGED
|
@@ -71,13 +71,20 @@ export const messageDetailOutput = messageOutput.extend({
|
|
|
71
71
|
.describe("The timeline, oldest event first, with the receiver's answer: Reception, Delivery, TransientFailure, Bounce, Feedback for a complaint and more."),
|
|
72
72
|
});
|
|
73
73
|
export const searchInput = z.object({
|
|
74
|
-
recipient: z
|
|
74
|
+
recipient: z
|
|
75
|
+
.string()
|
|
76
|
+
.trim()
|
|
77
|
+
.min(3)
|
|
78
|
+
.max(254)
|
|
79
|
+
.optional()
|
|
80
|
+
.describe('Messages to this address, in to, cc or bcc. A whole address matches exactly. Anything else of three characters or more matches anywhere in an address, such as kommun.se.'),
|
|
75
81
|
streamId: z.uuid().optional().describe('Messages on this stream of the server.'),
|
|
76
82
|
broadcastId: z.uuid().optional().describe('Messages of this broadcast.'),
|
|
77
83
|
tag: z.string().trim().min(1).max(100).optional().describe('Messages sent with this tag.'),
|
|
78
|
-
status:
|
|
84
|
+
status: z
|
|
85
|
+
.union([recipientStatus, z.array(recipientStatus).min(1).max(5)])
|
|
79
86
|
.optional()
|
|
80
|
-
.describe('Messages with at least one recipient in this state.'),
|
|
87
|
+
.describe('Messages with at least one recipient in this state, or in any of these states.'),
|
|
81
88
|
from: z.iso
|
|
82
89
|
.datetime({ offset: true })
|
|
83
90
|
.optional()
|
|
@@ -95,7 +102,7 @@ export const searchMessagesTool = {
|
|
|
95
102
|
operations: ['POST /v1/messages/search'],
|
|
96
103
|
register: (server, { connection, targets }) => registerServerTool(server, targets, 'search_messages', {
|
|
97
104
|
title: 'Search the message log',
|
|
98
|
-
description: "Searches this server's message log, newest first, by recipient, stream, broadcast, tag, a recipient's state and when the message was submitted. Answers each message with its recipients and their state, and next for the following page; get_message reads one message's timeline.",
|
|
105
|
+
description: "Searches this server's message log, newest first, by a recipient's address or part of one, stream, broadcast, tag, a recipient's state and when the message was submitted. Answers each message with its recipients and their state, and next for the following page; get_message reads one message's timeline.",
|
|
99
106
|
inputSchema: searchInput,
|
|
100
107
|
outputSchema: z.object({
|
|
101
108
|
messages: z.array(messageOutput).describe('The page of messages, newest first.'),
|
package/dist/tools/server.js
CHANGED
|
@@ -28,6 +28,14 @@ export const limitOutput = z.object({
|
|
|
28
28
|
used: z.number().int().describe('Emails the period has taken so far.'),
|
|
29
29
|
resetsAt: z.string().describe('When the period ends and the count starts again.'),
|
|
30
30
|
});
|
|
31
|
+
export const inboundCapOutput = z.object({
|
|
32
|
+
limit: z
|
|
33
|
+
.number()
|
|
34
|
+
.int()
|
|
35
|
+
.describe("Emails the account may receive in the calendar month in UTC, over every inbound stream; the account's monthly cap on sending until an administrator moves it."),
|
|
36
|
+
used: z.number().int().describe('Emails received this month so far, one per recipient.'),
|
|
37
|
+
resetsAt: z.string().describe('When the month ends and the count starts again.'),
|
|
38
|
+
});
|
|
31
39
|
export const streamOutput = z.object({
|
|
32
40
|
streamId: z.string().describe('What a send names as streamId.'),
|
|
33
41
|
kind: z
|
|
@@ -76,6 +84,9 @@ const outputSchema = z.object({
|
|
|
76
84
|
limits: z
|
|
77
85
|
.array(limitOutput)
|
|
78
86
|
.describe('Every limit a send on the server is held to, with its use this minute or month.'),
|
|
87
|
+
inboundCap: inboundCapOutput
|
|
88
|
+
.nullable()
|
|
89
|
+
.describe("The account's monthly cap on received mail, apart from the limits on sending, with its use; past it, mail to the account is refused for now. Null on a test server, which receives no mail."),
|
|
79
90
|
streams: z.array(streamOutput).describe("The server's streams; a send names one as streamId."),
|
|
80
91
|
sendingDomains: z
|
|
81
92
|
.array(sendableDomainOutput)
|
|
@@ -128,6 +139,7 @@ export const getServerTool = {
|
|
|
128
139
|
createdAt: own.createdAt,
|
|
129
140
|
},
|
|
130
141
|
limits: own.limits,
|
|
142
|
+
inboundCap: own.inboundCap,
|
|
131
143
|
streams,
|
|
132
144
|
sendingDomains: own.sendingDomains,
|
|
133
145
|
connection: { kind: own.mode, changes },
|
package/dist/tools/webhooks.js
CHANGED
|
@@ -11,6 +11,8 @@ const eventName = z.enum([
|
|
|
11
11
|
'cap_warning',
|
|
12
12
|
'cap_reached',
|
|
13
13
|
'inbound',
|
|
14
|
+
'inbound_cap_warning',
|
|
15
|
+
'inbound_cap_reached',
|
|
14
16
|
]);
|
|
15
17
|
export const webhookOutput = z.object({
|
|
16
18
|
webhookId: z.string().describe('What the deliveries and a delete refer to.'),
|
|
@@ -198,7 +200,7 @@ export const createWebhookTool = {
|
|
|
198
200
|
operations: ['POST /v1/webhooks'],
|
|
199
201
|
register: (server, { connection, targets }) => registerServerTool(server, targets, 'create_webhook', {
|
|
200
202
|
title: 'Add a webhook',
|
|
201
|
-
description: "Adds a webhook to this test server and answers its signing secret, shown this once: put it in the project's environment, such as SENDORA_WEBHOOK_SECRET in .env, never in a file under version control, and verify every delivery with the SDK: webhooks.receive or verifyWebhook in TypeScript, verify_webhook in Python. The API takes only a public https URL, so a local endpoint needs a tunnel. Events arrive as the simulator plays out each message.",
|
|
203
|
+
description: "Adds a webhook to this test server and answers its signing secret, shown this once: put it in the project's environment, such as SENDORA_WEBHOOK_SECRET in .env, never in a file under version control, and verify every delivery with the SDK: webhooks.receive or verifyWebhook in TypeScript, verify_webhook in Python, WebhookVerifier::verify() in PHP, or in Laravel the package's route. The API takes only a public https URL, so a local endpoint needs a tunnel. Events arrive as the simulator plays out each message.",
|
|
202
204
|
inputSchema: createWebhookInput,
|
|
203
205
|
outputSchema: createdWebhookOutput,
|
|
204
206
|
annotations: {
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** This package's version, held equal to its package.json by a test. */
|
|
2
|
-
export const MCP_VERSION = '1.
|
|
2
|
+
export const MCP_VERSION = '1.8.6';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sendora/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.6",
|
|
4
4
|
"description": "The official MCP server for Sendora: its documentation and API for coding agents.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@modelcontextprotocol/server": "^2.1.0",
|
|
27
27
|
"zod": "^4.6.2",
|
|
28
|
-
"@sendora/sdk": "^4.1
|
|
28
|
+
"@sendora/sdk": "^4.4.1"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@anthropic-ai/mcpb": "2.1.2",
|
|
@@ -40,10 +40,10 @@
|
|
|
40
40
|
"typescript": "~7.0.2",
|
|
41
41
|
"vitest": "^4.1.11",
|
|
42
42
|
"@sendora/api": "0.0.0",
|
|
43
|
-
"@sendora/db": "0.0.0",
|
|
44
43
|
"@sendora/domain": "0.0.0",
|
|
44
|
+
"@sendora/site": "0.0.0",
|
|
45
45
|
"@sendora/observability": "0.0.0",
|
|
46
|
-
"@sendora/
|
|
46
|
+
"@sendora/db": "0.0.0"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
49
|
"build": "pnpm --filter @sendora/sdk build && node scripts/build-docs.ts && node scripts/build-screens.ts && node -e \"fs.rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json && node -e \"fs.mkdirSync('dist/generated',{recursive:true});fs.copyFileSync('src/generated/docs.json','dist/generated/docs.json');fs.cpSync('src/generated/screens','dist/generated/screens',{recursive:true})\"",
|