@mailkite/mcp 0.1.0 → 0.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mailkite/mcp",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Model Context Protocol server for MailKite — exposes the MailKite API to LLM agents as tools. A thin layer over the MailKite Node SDK and the shared sdks/spec contract.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -34,13 +34,13 @@
34
34
  "homepage": "https://mailkite.dev/docs/libraries",
35
35
  "repository": {
36
36
  "type": "git",
37
- "url": "https://github.com/fijiwebdesign/mailkite.git",
37
+ "url": "git+https://github.com/fijiwebdesign/mailkite.git",
38
38
  "directory": "sdks/mcp"
39
39
  },
40
40
  "license": "MIT",
41
41
  "dependencies": {
42
42
  "@modelcontextprotocol/sdk": "^1.0.0",
43
43
  "ajv": "^8.17.1",
44
- "mailkite": "^0.1.0"
44
+ "mailkite": "^0.2.0"
45
45
  }
46
46
  }
package/server.mjs CHANGED
@@ -58,6 +58,9 @@ function buildInputSchema(method) {
58
58
  if (arg.in === "path") {
59
59
  properties[arg.name] = { type: "string", description: `Path parameter \`${arg.name}\`.` };
60
60
  required.push(arg.name);
61
+ } else if (arg.in === "query") {
62
+ properties[arg.name] = { type: "string", description: `Query parameter \`${arg.name}\`.` };
63
+ required.push(arg.name);
61
64
  } else if (arg.in === "body" && arg.schema && schemas[arg.schema]) {
62
65
  const body = schemas[arg.schema];
63
66
  Object.assign(properties, body.properties || {});
@@ -77,7 +80,11 @@ const credentialNote = (method) => {
77
80
 
78
81
  const tools = api.methods.map((method) => ({
79
82
  name: toolName(method),
80
- description: `${method.summary} ${credentialNote(method)}`,
83
+ description: `${method.summary} ${credentialNote(method)}${
84
+ method.agentConfirm
85
+ ? " Does NOT register automatically — returns a dashboard link for the user to review the price and confirm. An assistant can never purchase a domain on its own."
86
+ : ""
87
+ }`,
81
88
  inputSchema: buildInputSchema(method),
82
89
  _method: method, // kept server-side for dispatch; not sent to the client
83
90
  }));
@@ -94,16 +101,22 @@ function resolveCall(method, input) {
94
101
  input = input || {};
95
102
  let urlPath = method.http.path;
96
103
  let bodySchemaId = null;
104
+ const query = [];
97
105
 
98
106
  for (const arg of method.args || []) {
99
107
  if (arg.in === "path") {
100
108
  const value = input[arg.name];
101
109
  if (value == null || value === "") throw new Error(`Missing required path parameter: ${arg.name}`);
102
110
  urlPath = urlPath.replace(`{${arg.name}}`, encodeURIComponent(String(value)));
111
+ } else if (arg.in === "query") {
112
+ const value = input[arg.name];
113
+ if (value == null || value === "") throw new Error(`Missing required query parameter: ${arg.name}`);
114
+ query.push(`${encodeURIComponent(arg.name)}=${encodeURIComponent(String(value))}`);
103
115
  } else if (arg.in === "body" && arg.schema) {
104
116
  bodySchemaId = arg.schema;
105
117
  }
106
118
  }
119
+ if (query.length) urlPath += (urlPath.includes("?") ? "&" : "?") + query.join("&");
107
120
 
108
121
  let body;
109
122
  if (bodySchemaId) {
@@ -163,6 +176,25 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
163
176
  }
164
177
  }
165
178
 
179
+ // Gated methods (e.g. domain registration) must be approved by a human and are never run
180
+ // automatically by an AI agent. Return the dashboard URL to confirm instead of calling the API.
181
+ if (tool._method.agentConfirm) {
182
+ const dash = (process.env.MAILKITE_DASHBOARD_URL || "https://app.mailkite.dev").replace(/\/+$/, "");
183
+ const domain = (req.params.arguments && req.params.arguments.domain) || "the domain";
184
+ return {
185
+ content: [
186
+ {
187
+ type: "text",
188
+ text:
189
+ `Domain registration can't be completed automatically — it has to be approved by you.\n\n` +
190
+ `Open the dashboard to review the price and confirm the purchase of ${domain}:\n` +
191
+ `${dash}/domains\n\n` +
192
+ `Once it's registered there, tell me to continue.`,
193
+ },
194
+ ],
195
+ };
196
+ }
197
+
166
198
  if (!apiKey) {
167
199
  return {
168
200
  isError: true,
package/spec/api.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mailkite",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Canonical interface contract for every MailKite SDK. One low-level request() plus one function per endpoint. All languages expose the same shape; only naming adapts to each language's convention (e.g. Go exports PascalCase).",
5
5
  "baseUrl": "https://api.mailkite.dev",
6
6
  "auth": {
@@ -75,6 +75,21 @@
75
75
  "args": [{ "name": "id", "in": "path" }],
76
76
  "returns": "any"
77
77
  },
78
+ {
79
+ "name": "checkDomainAvailability",
80
+ "summary": "Check whether a domain is available to register, and at what price. Read-only — no charge.",
81
+ "http": { "method": "GET", "path": "/api/domains/register/check" },
82
+ "args": [{ "name": "domain", "in": "query" }],
83
+ "returns": "any"
84
+ },
85
+ {
86
+ "name": "registerDomain",
87
+ "summary": "Register (buy) a domain on the customer's behalf; provisions mail DNS and adds it to the account in one call. Charges the registrar.",
88
+ "http": { "method": "POST", "path": "/api/domains/register" },
89
+ "args": [{ "name": "body", "in": "body", "schema": "register-domain-request" }],
90
+ "returns": "any",
91
+ "agentConfirm": true
92
+ },
78
93
  {
79
94
  "name": "listRoutes",
80
95
  "summary": "List inbound routing rules.",
package/spec/cases.json CHANGED
@@ -122,6 +122,81 @@
122
122
  "response": { "status": 200, "body": { "ok": true } },
123
123
  "result": { "ok": true }
124
124
  },
125
+ {
126
+ "name": "check_domain_availability",
127
+ "method": "checkDomainAvailability",
128
+ "args": { "domain": "acme.com" },
129
+ "request": { "method": "GET", "path": "/api/domains/register/check" },
130
+ "response": {
131
+ "status": 200,
132
+ "body": {
133
+ "configured": true,
134
+ "domain": "acme.com",
135
+ "available": true,
136
+ "premium": false,
137
+ "price": { "amount": 12.99, "currency": "USD", "period": 1, "periodUnit": "y" }
138
+ }
139
+ },
140
+ "result": {
141
+ "configured": true,
142
+ "domain": "acme.com",
143
+ "available": true,
144
+ "premium": false,
145
+ "price": { "amount": 12.99, "currency": "USD", "period": 1, "periodUnit": "y" }
146
+ }
147
+ },
148
+ {
149
+ "name": "register_domain",
150
+ "method": "registerDomain",
151
+ "args": {
152
+ "domain": "acme.com",
153
+ "contact": {
154
+ "firstName": "Jane",
155
+ "lastName": "Doe",
156
+ "email": "jane@example.com",
157
+ "phone": "+1.4155551234",
158
+ "address": "123 Main St",
159
+ "city": "SF",
160
+ "zip": "94016",
161
+ "country": "US"
162
+ },
163
+ "years": 1
164
+ },
165
+ "request": {
166
+ "method": "POST",
167
+ "path": "/api/domains/register",
168
+ "bodySchema": "register-domain-request",
169
+ "body": {
170
+ "domain": "acme.com",
171
+ "contact": {
172
+ "firstName": "Jane",
173
+ "lastName": "Doe",
174
+ "email": "jane@example.com",
175
+ "phone": "+1.4155551234",
176
+ "address": "123 Main St",
177
+ "city": "SF",
178
+ "zip": "94016",
179
+ "country": "US"
180
+ },
181
+ "years": 1
182
+ }
183
+ },
184
+ "response": {
185
+ "status": 201,
186
+ "body": {
187
+ "domain": { "id": "dom_1", "domain": "acme.com", "status": "verified" },
188
+ "dns": [],
189
+ "registration": { "status": "registered", "reference": "ref_1" },
190
+ "dnsProvisioned": true
191
+ }
192
+ },
193
+ "result": {
194
+ "domain": { "id": "dom_1", "domain": "acme.com", "status": "verified" },
195
+ "dns": [],
196
+ "registration": { "status": "registered", "reference": "ref_1" },
197
+ "dnsProvisioned": true
198
+ }
199
+ },
125
200
  {
126
201
  "name": "list_routes",
127
202
  "method": "listRoutes",
@@ -0,0 +1,40 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "register-domain-request",
4
+ "title": "Register domain request body",
5
+ "type": "object",
6
+ "required": ["domain", "contact"],
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "domain": { "type": "string" },
10
+ "contact": {
11
+ "type": "object",
12
+ "required": [
13
+ "firstName",
14
+ "lastName",
15
+ "email",
16
+ "phone",
17
+ "address",
18
+ "city",
19
+ "zip",
20
+ "country"
21
+ ],
22
+ "additionalProperties": false,
23
+ "properties": {
24
+ "firstName": { "type": "string" },
25
+ "lastName": { "type": "string" },
26
+ "email": { "type": "string" },
27
+ "phone": { "type": "string", "description": "E.164-ish, +<cc>.<number>, e.g. +1.4155551234" },
28
+ "address": { "type": "string" },
29
+ "city": { "type": "string" },
30
+ "zip": { "type": "string" },
31
+ "country": { "type": "string", "description": "ISO 3166-1 alpha-2, e.g. US" },
32
+ "state": { "type": "string", "description": "ISO 3166-2 subdivision, e.g. US-CA" },
33
+ "organization": { "type": "string" },
34
+ "type": { "type": "string", "enum": ["individual", "company", "association", "publicbody"] }
35
+ }
36
+ },
37
+ "years": { "type": "integer", "minimum": 1 },
38
+ "dryRun": { "type": "boolean" }
39
+ }
40
+ }