lovieme-mcp 0.1.0 → 0.1.1

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/client.d.ts CHANGED
@@ -1,67 +1,67 @@
1
- import type { LovieMeProfile, LovieMeSubdomain, LovieMeDnsRecord, CloudPreset, DoHResolutionResult } from "./types.js";
2
- export declare class LovieMeClient {
3
- private baseUrl;
4
- private apiKey;
5
- constructor(apiKey?: string, baseUrl?: string);
6
- setApiKey(key: string): void;
7
- hasApiKey(): boolean;
8
- private request;
9
- checkSubdomain(name: string): Promise<{
10
- available: boolean;
11
- name: string;
12
- fqdn: string;
13
- reason?: string;
14
- }>;
15
- getProfile(): Promise<{
16
- profile: LovieMeProfile;
17
- }>;
18
- listSubdomains(): Promise<{
19
- subdomains: LovieMeSubdomain[];
20
- }>;
21
- getSubdomain(name: string): Promise<{
22
- subdomain: LovieMeSubdomain;
23
- dns_records: LovieMeDnsRecord[];
24
- }>;
25
- claimSubdomain(name: string): Promise<{
26
- success: boolean;
27
- message: string;
28
- subdomain: LovieMeSubdomain;
29
- }>;
30
- deleteSubdomain(name: string): Promise<{
31
- success: boolean;
32
- message: string;
33
- }>;
34
- listDnsRecords(subdomain: string): Promise<{
35
- dns_records: LovieMeDnsRecord[];
36
- }>;
37
- createDnsRecord(data: {
38
- subdomain: string;
39
- type: "A" | "AAAA" | "CNAME" | "TXT" | "MX";
40
- name: string;
41
- content: string;
42
- ttl?: number;
43
- priority?: number;
44
- }): Promise<{
45
- success: boolean;
46
- message: string;
47
- record: LovieMeDnsRecord;
48
- }>;
49
- deleteDnsRecord(id: string): Promise<{
50
- success: boolean;
51
- message: string;
52
- }>;
53
- listPresets(): Promise<{
54
- presets: CloudPreset[];
55
- }>;
56
- applyPreset(data: {
57
- subdomain: string;
58
- preset: string;
59
- customTarget?: string;
60
- }): Promise<{
61
- success: boolean;
62
- message: string;
63
- created_records: LovieMeDnsRecord[];
64
- }>;
65
- verifyDoH(domain: string, type?: string): Promise<DoHResolutionResult[]>;
66
- private ensureAuth;
67
- }
1
+ import type { LovieMeProfile, LovieMeSubdomain, LovieMeDnsRecord, CloudPreset, DoHResolutionResult } from "./types.js";
2
+ export declare class LovieMeClient {
3
+ private baseUrl;
4
+ private apiKey;
5
+ constructor(apiKey?: string, baseUrl?: string);
6
+ setApiKey(key: string): void;
7
+ hasApiKey(): boolean;
8
+ private request;
9
+ checkSubdomain(name: string): Promise<{
10
+ available: boolean;
11
+ name: string;
12
+ fqdn: string;
13
+ reason?: string;
14
+ }>;
15
+ getProfile(): Promise<{
16
+ profile: LovieMeProfile;
17
+ }>;
18
+ listSubdomains(): Promise<{
19
+ subdomains: LovieMeSubdomain[];
20
+ }>;
21
+ getSubdomain(name: string): Promise<{
22
+ subdomain: LovieMeSubdomain;
23
+ dns_records: LovieMeDnsRecord[];
24
+ }>;
25
+ claimSubdomain(name: string): Promise<{
26
+ success: boolean;
27
+ message: string;
28
+ subdomain: LovieMeSubdomain;
29
+ }>;
30
+ deleteSubdomain(name: string): Promise<{
31
+ success: boolean;
32
+ message: string;
33
+ }>;
34
+ listDnsRecords(subdomain: string): Promise<{
35
+ dns_records: LovieMeDnsRecord[];
36
+ }>;
37
+ createDnsRecord(data: {
38
+ subdomain: string;
39
+ type: "A" | "AAAA" | "CNAME" | "TXT" | "MX";
40
+ name: string;
41
+ content: string;
42
+ ttl?: number;
43
+ priority?: number;
44
+ }): Promise<{
45
+ success: boolean;
46
+ message: string;
47
+ record: LovieMeDnsRecord;
48
+ }>;
49
+ deleteDnsRecord(id: string): Promise<{
50
+ success: boolean;
51
+ message: string;
52
+ }>;
53
+ listPresets(): Promise<{
54
+ presets: CloudPreset[];
55
+ }>;
56
+ applyPreset(data: {
57
+ subdomain: string;
58
+ preset: string;
59
+ customTarget?: string;
60
+ }): Promise<{
61
+ success: boolean;
62
+ message: string;
63
+ created_records: LovieMeDnsRecord[];
64
+ }>;
65
+ verifyDoH(domain: string, type?: string): Promise<DoHResolutionResult[]>;
66
+ private ensureAuth;
67
+ }
package/dist/client.js CHANGED
@@ -1,3 +1,10 @@
1
+ import dns from "node:dns";
2
+ try {
3
+ dns.setDefaultResultOrder?.("ipv4first");
4
+ }
5
+ catch {
6
+ // Ignore in environments where setDefaultResultOrder is unsupported
7
+ }
1
8
  export class LovieMeClient {
2
9
  baseUrl;
3
10
  apiKey;
@@ -15,7 +22,7 @@ export class LovieMeClient {
15
22
  const url = `${this.baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
16
23
  const headers = {
17
24
  "Content-Type": "application/json",
18
- "User-Agent": "LovieMe-MCP-Server/0.1.0",
25
+ "User-Agent": "LovieMe-MCP-Server/0.1.1",
19
26
  ...(options.headers || {}),
20
27
  };
21
28
  if (this.apiKey) {
@@ -102,11 +109,12 @@ export class LovieMeClient {
102
109
  }
103
110
  // 6. Global DoH Multi-Resolver Verification
104
111
  async verifyDoH(domain, type = "A") {
105
- const fqdn = domain.endsWith(".lovie.me") ? domain : `${domain}.lovie.me`;
112
+ const clean = domain.trim().toLowerCase();
113
+ const fqdn = clean === "lovie.me" || clean.endsWith(".lovie.me") ? clean : `${clean}.lovie.me`;
106
114
  const resolvers = [
107
115
  { name: "Google Public DNS", url: `https://dns.google/resolve?name=${encodeURIComponent(fqdn)}&type=${type}` },
108
- { name: "Cloudflare 1.1.1.1", url: `https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(fqdn)}&type=${type}` },
109
- { name: "Quad9 9.9.9.9", url: `https://dns.quad9.net/dns-query?name=${encodeURIComponent(fqdn)}&type=${type}` },
116
+ { name: "Cloudflare (1.1.1.1)", url: `https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(fqdn)}&type=${type}` },
117
+ { name: "AliDNS Global", url: `https://dns.alidns.com/resolve?name=${encodeURIComponent(fqdn)}&type=${type}` },
110
118
  ];
111
119
  const results = await Promise.allSettled(resolvers.map(async ({ name, url }) => {
112
120
  const start = Date.now();
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
- export declare function createServer(apiKey?: string, baseUrl?: string): Server;
3
- export declare function main(): Promise<void>;
1
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
+ export declare function createServer(apiKey?: string, baseUrl?: string): Server;
3
+ export declare function main(): Promise<void>;
package/dist/index.js CHANGED
@@ -1,34 +1,34 @@
1
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
- import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
4
- import { LovieMeClient } from "./client.js";
5
- import { LOVIE_TOOLS, handleToolCall } from "./tools.js";
6
- export function createServer(apiKey, baseUrl) {
7
- const client = new LovieMeClient(apiKey, baseUrl);
8
- const server = new Server({
9
- name: "lovieme-mcp",
10
- version: "0.1.0",
11
- }, {
12
- capabilities: {
13
- tools: {},
14
- },
15
- });
16
- // 1. List available tools
17
- server.setRequestHandler(ListToolsRequestSchema, async () => {
18
- return {
19
- tools: LOVIE_TOOLS,
20
- };
21
- });
22
- // 2. Call tool
23
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
24
- const { name, arguments: args } = request.params;
25
- return handleToolCall(name, args, client);
26
- });
27
- return server;
28
- }
29
- export async function main() {
30
- const server = createServer();
31
- const transport = new StdioServerTransport();
32
- await server.connect(transport);
33
- console.error("LovieMe MCP Server running on stdio");
34
- }
1
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
4
+ import { LovieMeClient } from "./client.js";
5
+ import { LOVIE_TOOLS, handleToolCall } from "./tools.js";
6
+ export function createServer(apiKey, baseUrl) {
7
+ const client = new LovieMeClient(apiKey, baseUrl);
8
+ const server = new Server({
9
+ name: "lovieme-mcp",
10
+ version: "0.1.0",
11
+ }, {
12
+ capabilities: {
13
+ tools: {},
14
+ },
15
+ });
16
+ // 1. List available tools
17
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
18
+ return {
19
+ tools: LOVIE_TOOLS,
20
+ };
21
+ });
22
+ // 2. Call tool
23
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
24
+ const { name, arguments: args } = request.params;
25
+ return handleToolCall(name, args, client);
26
+ });
27
+ return server;
28
+ }
29
+ export async function main() {
30
+ const server = createServer();
31
+ const transport = new StdioServerTransport();
32
+ await server.connect(transport);
33
+ console.error("LovieMe MCP Server running on stdio");
34
+ }
package/dist/tools.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import type { Tool } from "@modelcontextprotocol/sdk/types.js";
2
- import { LovieMeClient } from "./client.js";
3
- export declare const LOVIE_TOOLS: Tool[];
4
- export declare function handleToolCall(name: string, args: Record<string, unknown> | undefined, client: LovieMeClient): Promise<{
5
- content: Array<{
6
- type: "text";
7
- text: string;
8
- }>;
9
- isError?: boolean;
10
- }>;
1
+ import type { Tool } from "@modelcontextprotocol/sdk/types.js";
2
+ import { LovieMeClient } from "./client.js";
3
+ export declare const LOVIE_TOOLS: Tool[];
4
+ export declare function handleToolCall(name: string, args: Record<string, unknown> | undefined, client: LovieMeClient): Promise<{
5
+ content: Array<{
6
+ type: "text";
7
+ text: string;
8
+ }>;
9
+ isError?: boolean;
10
+ }>;
package/dist/tools.js CHANGED
@@ -1,359 +1,359 @@
1
- export const LOVIE_TOOLS = [
2
- {
3
- name: "lovie_check_subdomain",
4
- description: "Check whether a *.lovie.me subdomain prefix is available for registration.",
5
- inputSchema: {
6
- type: "object",
7
- properties: {
8
- name: {
9
- type: "string",
10
- description: "Subdomain prefix to check (e.g. 'my-app' for my-app.lovie.me).",
11
- },
12
- },
13
- required: ["name"],
14
- },
15
- },
16
- {
17
- name: "lovie_claim_subdomain",
18
- description: "Claim and register a new *.lovie.me subdomain. Requires LOVIE_API_KEY with 'subdomains:write' scope.",
19
- inputSchema: {
20
- type: "object",
21
- properties: {
22
- name: {
23
- type: "string",
24
- description: "Subdomain prefix to claim (alphanumeric and hyphens, 3-63 chars).",
25
- },
26
- },
27
- required: ["name"],
28
- },
29
- },
30
- {
31
- name: "lovie_list_subdomains",
32
- description: "List all subdomains owned by the authenticated account with their DNS records count. Requires LOVIE_API_KEY with 'subdomains:read' scope.",
33
- inputSchema: {
34
- type: "object",
35
- properties: {},
36
- },
37
- },
38
- {
39
- name: "lovie_get_subdomain",
40
- description: "Inspect details of a specific subdomain including its active DNS records. Requires LOVIE_API_KEY with 'subdomains:read' scope.",
41
- inputSchema: {
42
- type: "object",
43
- properties: {
44
- name: {
45
- type: "string",
46
- description: "Subdomain name or prefix to inspect.",
47
- },
48
- },
49
- required: ["name"],
50
- },
51
- },
52
- {
53
- name: "lovie_delete_subdomain",
54
- description: "Release and permanently delete a subdomain, purging all its DNS records from Gcore Anycast nameservers. Requires LOVIE_API_KEY with 'subdomains:write' scope.",
55
- inputSchema: {
56
- type: "object",
57
- properties: {
58
- name: {
59
- type: "string",
60
- description: "Subdomain prefix to release (e.g. 'test-app').",
61
- },
62
- },
63
- required: ["name"],
64
- },
65
- },
66
- {
67
- name: "lovie_list_dns_records",
68
- description: "List all active DNS records for a given subdomain. Requires LOVIE_API_KEY with 'dns:read' scope.",
69
- inputSchema: {
70
- type: "object",
71
- properties: {
72
- subdomain: {
73
- type: "string",
74
- description: "Subdomain prefix (e.g. 'my-app').",
75
- },
76
- },
77
- required: ["subdomain"],
78
- },
79
- },
80
- {
81
- name: "lovie_create_dns_record",
82
- description: "Create an RFC-compliant DNS record (A, AAAA, CNAME, TXT, MX) with sub-2s Gcore Anycast propagation. Requires LOVIE_API_KEY with 'dns:write' scope.",
83
- inputSchema: {
84
- type: "object",
85
- properties: {
86
- subdomain: {
87
- type: "string",
88
- description: "Subdomain prefix where the record belongs (e.g. 'my-app').",
89
- },
90
- type: {
91
- type: "string",
92
- enum: ["A", "AAAA", "CNAME", "TXT", "MX"],
93
- description: "DNS record type.",
94
- },
95
- name: {
96
- type: "string",
97
- description: "Host label ('@' for apex of subdomain, or 'www', 'api', etc.).",
98
- },
99
- content: {
100
- type: "string",
101
- description: "Record destination (IPv4 for A, IPv6 for AAAA, target host for CNAME, string for TXT).",
102
- },
103
- ttl: {
104
- type: "number",
105
- description: "TTL in seconds (default: 300).",
106
- },
107
- priority: {
108
- type: "number",
109
- description: "MX record priority (required for MX, e.g. 10).",
110
- },
111
- },
112
- required: ["subdomain", "type", "name", "content"],
113
- },
114
- },
115
- {
116
- name: "lovie_delete_dns_record",
117
- description: "Delete an existing DNS record by ID, removing it from both database and Anycast edge. Requires LOVIE_API_KEY with 'dns:write' scope.",
118
- inputSchema: {
119
- type: "object",
120
- properties: {
121
- id: {
122
- type: "string",
123
- description: "The unique UUID of the DNS record to delete.",
124
- },
125
- },
126
- required: ["id"],
127
- },
128
- },
129
- {
130
- name: "lovie_list_presets",
131
- description: "List all verified 1-click cloud hosting presets (Vercel, Railway, Render, GitHub Pages, Cloudflare Pages, Netlify, Fly.io).",
132
- inputSchema: {
133
- type: "object",
134
- properties: {},
135
- },
136
- },
137
- {
138
- name: "lovie_apply_preset",
139
- description: "Apply a 1-click cloud hosting DNS preset to your subdomain. Automatically configures tested CNAME and A records. Requires LOVIE_API_KEY with 'dns:write' scope.",
140
- inputSchema: {
141
- type: "object",
142
- properties: {
143
- subdomain: {
144
- type: "string",
145
- description: "Subdomain prefix to apply preset to (e.g. 'my-app').",
146
- },
147
- preset: {
148
- type: "string",
149
- enum: ["vercel", "railway", "render", "github-pages", "cloudflare-pages", "netlify", "flyio"],
150
- description: "Target platform preset ID.",
151
- },
152
- customTarget: {
153
- type: "string",
154
- description: "Optional custom target required for some presets (e.g. GitHub username for github-pages, or Railway target domain).",
155
- },
156
- },
157
- required: ["subdomain", "preset"],
158
- },
159
- },
160
- {
161
- name: "lovie_verify_doh",
162
- description: "Verify live Anycast DNS propagation worldwide by parallel-querying Google (8.8.8.8), Cloudflare (1.1.1.1), and Quad9 (9.9.9.9) DoH resolvers.",
163
- inputSchema: {
164
- type: "object",
165
- properties: {
166
- domain: {
167
- type: "string",
168
- description: "Subdomain to test (e.g. 'my-app' or 'my-app.lovie.me').",
169
- },
170
- type: {
171
- type: "string",
172
- enum: ["A", "AAAA", "CNAME", "TXT", "MX"],
173
- description: "Record type to verify (default: 'A').",
174
- },
175
- },
176
- required: ["domain"],
177
- },
178
- },
179
- {
180
- name: "lovie_get_profile",
181
- description: "Inspect the current user profile, plan tier (Free / Pro Lifetime / Builder Grant), and remaining subdomain quotas. Requires LOVIE_API_KEY with 'profile:read' scope.",
182
- inputSchema: {
183
- type: "object",
184
- properties: {},
185
- },
186
- },
187
- ];
188
- export async function handleToolCall(name, args, client) {
189
- try {
190
- const p = args || {};
191
- switch (name) {
192
- case "lovie_check_subdomain": {
193
- const result = await client.checkSubdomain(String(p.name));
194
- return {
195
- content: [
196
- {
197
- type: "text",
198
- text: JSON.stringify(result, null, 2),
199
- },
200
- ],
201
- };
202
- }
203
- case "lovie_claim_subdomain": {
204
- const result = await client.claimSubdomain(String(p.name));
205
- return {
206
- content: [
207
- {
208
- type: "text",
209
- text: JSON.stringify(result, null, 2),
210
- },
211
- ],
212
- };
213
- }
214
- case "lovie_list_subdomains": {
215
- const result = await client.listSubdomains();
216
- return {
217
- content: [
218
- {
219
- type: "text",
220
- text: JSON.stringify(result, null, 2),
221
- },
222
- ],
223
- };
224
- }
225
- case "lovie_get_subdomain": {
226
- const result = await client.getSubdomain(String(p.name));
227
- return {
228
- content: [
229
- {
230
- type: "text",
231
- text: JSON.stringify(result, null, 2),
232
- },
233
- ],
234
- };
235
- }
236
- case "lovie_delete_subdomain": {
237
- const result = await client.deleteSubdomain(String(p.name));
238
- return {
239
- content: [
240
- {
241
- type: "text",
242
- text: JSON.stringify(result, null, 2),
243
- },
244
- ],
245
- };
246
- }
247
- case "lovie_list_dns_records": {
248
- const result = await client.listDnsRecords(String(p.subdomain));
249
- return {
250
- content: [
251
- {
252
- type: "text",
253
- text: JSON.stringify(result, null, 2),
254
- },
255
- ],
256
- };
257
- }
258
- case "lovie_create_dns_record": {
259
- const result = await client.createDnsRecord({
260
- subdomain: String(p.subdomain),
261
- type: p.type,
262
- name: String(p.name),
263
- content: String(p.content),
264
- ttl: p.ttl ? Number(p.ttl) : undefined,
265
- priority: p.priority ? Number(p.priority) : undefined,
266
- });
267
- return {
268
- content: [
269
- {
270
- type: "text",
271
- text: JSON.stringify(result, null, 2),
272
- },
273
- ],
274
- };
275
- }
276
- case "lovie_delete_dns_record": {
277
- const result = await client.deleteDnsRecord(String(p.id));
278
- return {
279
- content: [
280
- {
281
- type: "text",
282
- text: JSON.stringify(result, null, 2),
283
- },
284
- ],
285
- };
286
- }
287
- case "lovie_list_presets": {
288
- const result = await client.listPresets();
289
- return {
290
- content: [
291
- {
292
- type: "text",
293
- text: JSON.stringify(result, null, 2),
294
- },
295
- ],
296
- };
297
- }
298
- case "lovie_apply_preset": {
299
- const result = await client.applyPreset({
300
- subdomain: String(p.subdomain),
301
- preset: String(p.preset),
302
- customTarget: p.customTarget ? String(p.customTarget) : undefined,
303
- });
304
- return {
305
- content: [
306
- {
307
- type: "text",
308
- text: JSON.stringify(result, null, 2),
309
- },
310
- ],
311
- };
312
- }
313
- case "lovie_verify_doh": {
314
- const result = await client.verifyDoH(String(p.domain), p.type ? String(p.type) : "A");
315
- return {
316
- content: [
317
- {
318
- type: "text",
319
- text: JSON.stringify(result, null, 2),
320
- },
321
- ],
322
- };
323
- }
324
- case "lovie_get_profile": {
325
- const result = await client.getProfile();
326
- return {
327
- content: [
328
- {
329
- type: "text",
330
- text: JSON.stringify(result, null, 2),
331
- },
332
- ],
333
- };
334
- }
335
- default:
336
- return {
337
- isError: true,
338
- content: [
339
- {
340
- type: "text",
341
- text: `Unknown tool: ${name}`,
342
- },
343
- ],
344
- };
345
- }
346
- }
347
- catch (err) {
348
- const msg = err instanceof Error ? err.message : String(err);
349
- return {
350
- isError: true,
351
- content: [
352
- {
353
- type: "text",
354
- text: `Error executing ${name}: ${msg}`,
355
- },
356
- ],
357
- };
358
- }
359
- }
1
+ export const LOVIE_TOOLS = [
2
+ {
3
+ name: "lovie_check_subdomain",
4
+ description: "Check whether a *.lovie.me subdomain prefix is available for registration.",
5
+ inputSchema: {
6
+ type: "object",
7
+ properties: {
8
+ name: {
9
+ type: "string",
10
+ description: "Subdomain prefix to check (e.g. 'my-app' for my-app.lovie.me).",
11
+ },
12
+ },
13
+ required: ["name"],
14
+ },
15
+ },
16
+ {
17
+ name: "lovie_claim_subdomain",
18
+ description: "Claim and register a new *.lovie.me subdomain. Requires LOVIE_API_KEY with 'subdomains:write' scope.",
19
+ inputSchema: {
20
+ type: "object",
21
+ properties: {
22
+ name: {
23
+ type: "string",
24
+ description: "Subdomain prefix to claim (alphanumeric and hyphens, 3-63 chars).",
25
+ },
26
+ },
27
+ required: ["name"],
28
+ },
29
+ },
30
+ {
31
+ name: "lovie_list_subdomains",
32
+ description: "List all subdomains owned by the authenticated account with their DNS records count. Requires LOVIE_API_KEY with 'subdomains:read' scope.",
33
+ inputSchema: {
34
+ type: "object",
35
+ properties: {},
36
+ },
37
+ },
38
+ {
39
+ name: "lovie_get_subdomain",
40
+ description: "Inspect details of a specific subdomain including its active DNS records. Requires LOVIE_API_KEY with 'subdomains:read' scope.",
41
+ inputSchema: {
42
+ type: "object",
43
+ properties: {
44
+ name: {
45
+ type: "string",
46
+ description: "Subdomain name or prefix to inspect.",
47
+ },
48
+ },
49
+ required: ["name"],
50
+ },
51
+ },
52
+ {
53
+ name: "lovie_delete_subdomain",
54
+ description: "Release and permanently delete a subdomain, purging all its DNS records from Gcore Anycast nameservers. Requires LOVIE_API_KEY with 'subdomains:write' scope.",
55
+ inputSchema: {
56
+ type: "object",
57
+ properties: {
58
+ name: {
59
+ type: "string",
60
+ description: "Subdomain prefix to release (e.g. 'test-app').",
61
+ },
62
+ },
63
+ required: ["name"],
64
+ },
65
+ },
66
+ {
67
+ name: "lovie_list_dns_records",
68
+ description: "List all active DNS records for a given subdomain. Requires LOVIE_API_KEY with 'dns:read' scope.",
69
+ inputSchema: {
70
+ type: "object",
71
+ properties: {
72
+ subdomain: {
73
+ type: "string",
74
+ description: "Subdomain prefix (e.g. 'my-app').",
75
+ },
76
+ },
77
+ required: ["subdomain"],
78
+ },
79
+ },
80
+ {
81
+ name: "lovie_create_dns_record",
82
+ description: "Create an RFC-compliant DNS record (A, AAAA, CNAME, TXT, MX) with sub-2s Gcore Anycast propagation. Requires LOVIE_API_KEY with 'dns:write' scope.",
83
+ inputSchema: {
84
+ type: "object",
85
+ properties: {
86
+ subdomain: {
87
+ type: "string",
88
+ description: "Subdomain prefix where the record belongs (e.g. 'my-app').",
89
+ },
90
+ type: {
91
+ type: "string",
92
+ enum: ["A", "AAAA", "CNAME", "TXT", "MX"],
93
+ description: "DNS record type.",
94
+ },
95
+ name: {
96
+ type: "string",
97
+ description: "Host label ('@' for apex of subdomain, or 'www', 'api', etc.).",
98
+ },
99
+ content: {
100
+ type: "string",
101
+ description: "Record destination (IPv4 for A, IPv6 for AAAA, target host for CNAME, string for TXT).",
102
+ },
103
+ ttl: {
104
+ type: "number",
105
+ description: "TTL in seconds (default: 300).",
106
+ },
107
+ priority: {
108
+ type: "number",
109
+ description: "MX record priority (required for MX, e.g. 10).",
110
+ },
111
+ },
112
+ required: ["subdomain", "type", "name", "content"],
113
+ },
114
+ },
115
+ {
116
+ name: "lovie_delete_dns_record",
117
+ description: "Delete an existing DNS record by ID, removing it from both database and Anycast edge. Requires LOVIE_API_KEY with 'dns:write' scope.",
118
+ inputSchema: {
119
+ type: "object",
120
+ properties: {
121
+ id: {
122
+ type: "string",
123
+ description: "The unique UUID of the DNS record to delete.",
124
+ },
125
+ },
126
+ required: ["id"],
127
+ },
128
+ },
129
+ {
130
+ name: "lovie_list_presets",
131
+ description: "List all verified 1-click cloud hosting presets (Vercel, Railway, Render, GitHub Pages, Cloudflare Pages, Netlify, Fly.io).",
132
+ inputSchema: {
133
+ type: "object",
134
+ properties: {},
135
+ },
136
+ },
137
+ {
138
+ name: "lovie_apply_preset",
139
+ description: "Apply a 1-click cloud hosting DNS preset to your subdomain. Automatically configures tested CNAME and A records. Requires LOVIE_API_KEY with 'dns:write' scope.",
140
+ inputSchema: {
141
+ type: "object",
142
+ properties: {
143
+ subdomain: {
144
+ type: "string",
145
+ description: "Subdomain prefix to apply preset to (e.g. 'my-app').",
146
+ },
147
+ preset: {
148
+ type: "string",
149
+ enum: ["vercel", "railway", "render", "github-pages", "cloudflare-pages", "netlify", "flyio"],
150
+ description: "Target platform preset ID.",
151
+ },
152
+ customTarget: {
153
+ type: "string",
154
+ description: "Optional custom target required for some presets (e.g. GitHub username for github-pages, or Railway target domain).",
155
+ },
156
+ },
157
+ required: ["subdomain", "preset"],
158
+ },
159
+ },
160
+ {
161
+ name: "lovie_verify_doh",
162
+ description: "Verify live Anycast DNS propagation worldwide by parallel-querying Google (8.8.8.8), Cloudflare (1.1.1.1), and Quad9 (9.9.9.9) DoH resolvers.",
163
+ inputSchema: {
164
+ type: "object",
165
+ properties: {
166
+ domain: {
167
+ type: "string",
168
+ description: "Subdomain to test (e.g. 'my-app' or 'my-app.lovie.me').",
169
+ },
170
+ type: {
171
+ type: "string",
172
+ enum: ["A", "AAAA", "CNAME", "TXT", "MX"],
173
+ description: "Record type to verify (default: 'A').",
174
+ },
175
+ },
176
+ required: ["domain"],
177
+ },
178
+ },
179
+ {
180
+ name: "lovie_get_profile",
181
+ description: "Inspect the current user profile, plan tier (Free / Pro Lifetime / Builder Grant), and remaining subdomain quotas. Requires LOVIE_API_KEY with 'profile:read' scope.",
182
+ inputSchema: {
183
+ type: "object",
184
+ properties: {},
185
+ },
186
+ },
187
+ ];
188
+ export async function handleToolCall(name, args, client) {
189
+ try {
190
+ const p = args || {};
191
+ switch (name) {
192
+ case "lovie_check_subdomain": {
193
+ const result = await client.checkSubdomain(String(p.name));
194
+ return {
195
+ content: [
196
+ {
197
+ type: "text",
198
+ text: JSON.stringify(result, null, 2),
199
+ },
200
+ ],
201
+ };
202
+ }
203
+ case "lovie_claim_subdomain": {
204
+ const result = await client.claimSubdomain(String(p.name));
205
+ return {
206
+ content: [
207
+ {
208
+ type: "text",
209
+ text: JSON.stringify(result, null, 2),
210
+ },
211
+ ],
212
+ };
213
+ }
214
+ case "lovie_list_subdomains": {
215
+ const result = await client.listSubdomains();
216
+ return {
217
+ content: [
218
+ {
219
+ type: "text",
220
+ text: JSON.stringify(result, null, 2),
221
+ },
222
+ ],
223
+ };
224
+ }
225
+ case "lovie_get_subdomain": {
226
+ const result = await client.getSubdomain(String(p.name));
227
+ return {
228
+ content: [
229
+ {
230
+ type: "text",
231
+ text: JSON.stringify(result, null, 2),
232
+ },
233
+ ],
234
+ };
235
+ }
236
+ case "lovie_delete_subdomain": {
237
+ const result = await client.deleteSubdomain(String(p.name));
238
+ return {
239
+ content: [
240
+ {
241
+ type: "text",
242
+ text: JSON.stringify(result, null, 2),
243
+ },
244
+ ],
245
+ };
246
+ }
247
+ case "lovie_list_dns_records": {
248
+ const result = await client.listDnsRecords(String(p.subdomain));
249
+ return {
250
+ content: [
251
+ {
252
+ type: "text",
253
+ text: JSON.stringify(result, null, 2),
254
+ },
255
+ ],
256
+ };
257
+ }
258
+ case "lovie_create_dns_record": {
259
+ const result = await client.createDnsRecord({
260
+ subdomain: String(p.subdomain),
261
+ type: p.type,
262
+ name: String(p.name),
263
+ content: String(p.content),
264
+ ttl: p.ttl ? Number(p.ttl) : undefined,
265
+ priority: p.priority ? Number(p.priority) : undefined,
266
+ });
267
+ return {
268
+ content: [
269
+ {
270
+ type: "text",
271
+ text: JSON.stringify(result, null, 2),
272
+ },
273
+ ],
274
+ };
275
+ }
276
+ case "lovie_delete_dns_record": {
277
+ const result = await client.deleteDnsRecord(String(p.id));
278
+ return {
279
+ content: [
280
+ {
281
+ type: "text",
282
+ text: JSON.stringify(result, null, 2),
283
+ },
284
+ ],
285
+ };
286
+ }
287
+ case "lovie_list_presets": {
288
+ const result = await client.listPresets();
289
+ return {
290
+ content: [
291
+ {
292
+ type: "text",
293
+ text: JSON.stringify(result, null, 2),
294
+ },
295
+ ],
296
+ };
297
+ }
298
+ case "lovie_apply_preset": {
299
+ const result = await client.applyPreset({
300
+ subdomain: String(p.subdomain),
301
+ preset: String(p.preset),
302
+ customTarget: p.customTarget ? String(p.customTarget) : undefined,
303
+ });
304
+ return {
305
+ content: [
306
+ {
307
+ type: "text",
308
+ text: JSON.stringify(result, null, 2),
309
+ },
310
+ ],
311
+ };
312
+ }
313
+ case "lovie_verify_doh": {
314
+ const result = await client.verifyDoH(String(p.domain), p.type ? String(p.type) : "A");
315
+ return {
316
+ content: [
317
+ {
318
+ type: "text",
319
+ text: JSON.stringify(result, null, 2),
320
+ },
321
+ ],
322
+ };
323
+ }
324
+ case "lovie_get_profile": {
325
+ const result = await client.getProfile();
326
+ return {
327
+ content: [
328
+ {
329
+ type: "text",
330
+ text: JSON.stringify(result, null, 2),
331
+ },
332
+ ],
333
+ };
334
+ }
335
+ default:
336
+ return {
337
+ isError: true,
338
+ content: [
339
+ {
340
+ type: "text",
341
+ text: `Unknown tool: ${name}`,
342
+ },
343
+ ],
344
+ };
345
+ }
346
+ }
347
+ catch (err) {
348
+ const msg = err instanceof Error ? err.message : String(err);
349
+ return {
350
+ isError: true,
351
+ content: [
352
+ {
353
+ type: "text",
354
+ text: `Error executing ${name}: ${msg}`,
355
+ },
356
+ ],
357
+ };
358
+ }
359
+ }
package/dist/types.d.ts CHANGED
@@ -1,54 +1,54 @@
1
- export interface LovieMeProfile {
2
- id: string;
3
- email?: string;
4
- name?: string;
5
- plan: "starter" | "pro" | "grant";
6
- quota: {
7
- subdomains_max: number;
8
- subdomains_used: number;
9
- subdomains_remaining: number;
10
- };
11
- }
12
- export interface LovieMeSubdomain {
13
- id: string;
14
- name: string;
15
- fqdn: string;
16
- created_at: string;
17
- dns_records_count: number;
18
- email_forwarder_active: boolean;
19
- }
20
- export interface LovieMeDnsRecord {
21
- id: string;
22
- subdomain_id: string;
23
- type: "A" | "AAAA" | "CNAME" | "TXT" | "MX" | "CAA";
24
- name: string;
25
- content: string;
26
- ttl: number;
27
- priority?: number | null;
28
- created_at: string;
29
- }
30
- export interface PresetTarget {
31
- name: string;
32
- type: "CNAME" | "A" | "TXT";
33
- content: string;
34
- ttl: number;
35
- priority?: number;
36
- }
37
- export interface CloudPreset {
38
- id: string;
39
- name: string;
40
- description: string;
41
- records: PresetTarget[];
42
- docs_url: string;
43
- }
44
- export interface DoHResolutionResult {
45
- resolver: string;
46
- status: "resolved" | "not_found" | "error";
47
- latency_ms: number;
48
- answers: Array<{
49
- name: string;
50
- type: number;
51
- data: string;
52
- TTL: number;
53
- }>;
54
- }
1
+ export interface LovieMeProfile {
2
+ id: string;
3
+ email?: string;
4
+ name?: string;
5
+ plan: "starter" | "pro" | "grant";
6
+ quota: {
7
+ subdomains_max: number;
8
+ subdomains_used: number;
9
+ subdomains_remaining: number;
10
+ };
11
+ }
12
+ export interface LovieMeSubdomain {
13
+ id: string;
14
+ name: string;
15
+ fqdn: string;
16
+ created_at: string;
17
+ dns_records_count: number;
18
+ email_forwarder_active: boolean;
19
+ }
20
+ export interface LovieMeDnsRecord {
21
+ id: string;
22
+ subdomain_id: string;
23
+ type: "A" | "AAAA" | "CNAME" | "TXT" | "MX" | "CAA";
24
+ name: string;
25
+ content: string;
26
+ ttl: number;
27
+ priority?: number | null;
28
+ created_at: string;
29
+ }
30
+ export interface PresetTarget {
31
+ name: string;
32
+ type: "CNAME" | "A" | "TXT";
33
+ content: string;
34
+ ttl: number;
35
+ priority?: number;
36
+ }
37
+ export interface CloudPreset {
38
+ id: string;
39
+ name: string;
40
+ description: string;
41
+ records: PresetTarget[];
42
+ docs_url: string;
43
+ }
44
+ export interface DoHResolutionResult {
45
+ resolver: string;
46
+ status: "resolved" | "not_found" | "error";
47
+ latency_ms: number;
48
+ answers: Array<{
49
+ name: string;
50
+ type: number;
51
+ data: string;
52
+ TTL: number;
53
+ }>;
54
+ }
package/dist/types.js CHANGED
@@ -1 +1 @@
1
- export {};
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lovieme-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Official Model Context Protocol (MCP) server for LovieMe — claim free *.lovie.me subdomains, configure Anycast DNS, and wire up cloud presets directly from AI coding agents.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",