@agentdomain/mcp-server 0.6.0 → 0.8.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/index.js CHANGED
@@ -14,657 +14,643 @@
14
14
  * "command": "npx",
15
15
  * "args": ["-y", "@agentdomain/mcp-server"],
16
16
  * "env": {
17
- * "AGENTDOMAIN_API_URL": "https://agentdomain.app/api/v1",
18
- * "AGENT_PRIVATE_KEY": "0x..."
17
+ * "AGENTDOMAIN_API_URL": "https://api.agentdomain.app/api/v1"
19
18
  * }
20
19
  * }
21
20
  * }
22
21
  * }
22
+ *
23
+ * Inject optional signing credentials through a trusted external secret source,
24
+ * and only when a signing operation is explicitly enabled.
23
25
  */
24
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
25
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
26
- import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
27
- import { z } from "zod";
28
- import { AgentDomain } from "@agentdomain/sdk";
29
- import { DNS_RECORD_TYPES, dnsRecordSchema, dnsRecordObjectSchema, } from "@agentdomain/shared";
30
- import { AGENTDOMAIN_API_BASE_URL, SERVICE_PLAN_KEYS, SUPPORTED_FRAMEWORKS, SUPPORTED_TLDS, } from "@agentdomain/shared/constants";
31
- import { createPublicClient, createWalletClient, http, } from "viem";
32
- import { privateKeyToAccount } from "viem/accounts";
33
- import { base, baseSepolia } from "viem/chains";
26
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
27
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
28
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
29
+ import { z } from 'zod';
30
+ import { AgentDomain, validateBuilderCode } from '@agentdomain/sdk';
31
+ import { DNS_RECORD_TYPES, dnsRecordSchema, dnsRecordObjectSchema, } from '@agentdomain/shared';
32
+ import { AGENTDOMAIN_API_BASE_URL, SERVICE_PLAN_KEYS, SUPPORTED_FRAMEWORKS, SUPPORTED_TLDS, } from '@agentdomain/shared/constants';
33
+ import { createPublicClient, createWalletClient, http } from 'viem';
34
+ import { privateKeyToAccount } from 'viem/accounts';
35
+ import { base, baseSepolia } from 'viem/chains';
34
36
  const API_URL = process.env.AGENTDOMAIN_API_URL ?? AGENTDOMAIN_API_BASE_URL;
35
- const NETWORK = (process.env.AGENTDOMAIN_NETWORK ?? "base");
37
+ const NETWORK = (process.env.AGENTDOMAIN_NETWORK ?? 'base');
36
38
  const AGENT_PRIVATE_KEY = process.env.AGENT_PRIVATE_KEY;
37
39
  const AGENTDOMAIN_API_KEY = process.env.AGENTDOMAIN_API_KEY;
40
+ const AGENTDOMAIN_BUILDER_CODE = process.env.AGENTDOMAIN_BUILDER_CODE;
38
41
  const RENEWAL_VAULT_ADDRESS = process.env.RENEWAL_VAULT_ADDRESS;
42
+ const WRITE_TOOLS_ENV = 'AGENTDOMAIN_ENABLE_WRITE_TOOLS';
43
+ const WRITE_TOOLS_ENABLED = parseWriteToolsEnabled(process.env[WRITE_TOOLS_ENV]);
39
44
  function getClient() {
40
45
  const config = {
41
46
  apiUrl: API_URL,
42
47
  apiKey: AGENTDOMAIN_API_KEY,
43
48
  network: NETWORK,
44
49
  renewalVaultAddress: RENEWAL_VAULT_ADDRESS,
50
+ builderCode: AGENTDOMAIN_BUILDER_CODE,
45
51
  };
46
52
  if (AGENT_PRIVATE_KEY) {
47
53
  const account = privateKeyToAccount(AGENT_PRIVATE_KEY);
48
- const chain = NETWORK === "base" ? base : baseSepolia;
49
- const rpc = NETWORK === "base"
50
- ? "https://mainnet.base.org"
51
- : "https://sepolia.base.org";
54
+ const chain = NETWORK === 'base' ? base : baseSepolia;
55
+ const rpc = NETWORK === 'base' ? 'https://mainnet.base.org' : 'https://sepolia.base.org';
52
56
  // Cast: viem's deeply-generic types do not always match cleanly across
53
57
  // duplicated installs in monorepos. Behaviour is identical at runtime.
54
- config.walletClient = createWalletClient({
55
- account,
56
- chain,
57
- transport: http(rpc),
58
- });
59
- config.publicClient = createPublicClient({
60
- chain,
61
- transport: http(rpc),
62
- });
58
+ config.walletClient = createWalletClient({ account, chain, transport: http(rpc) });
59
+ config.publicClient = createPublicClient({ chain, transport: http(rpc) });
63
60
  }
64
61
  return new AgentDomain(config);
65
62
  }
66
- const server = new Server({ name: "agentdomain-mcp", version: "0.2.1" }, { capabilities: { tools: {} } });
63
+ const server = new Server({ name: 'agentdomain-mcp', version: '0.2.1' }, { capabilities: { tools: {} } });
67
64
  // ----------------------------------------------------------------------
68
65
  // TOOL DEFINITIONS
69
66
  // ----------------------------------------------------------------------
70
- const TOOLS = [
67
+ const TOOL_DEFINITIONS = [
71
68
  {
72
- name: "check_domain_availability",
73
- description: "Check whether an agent domain (name + tld) is available for registration. Returns availability status and pricing.",
69
+ name: 'check_domain_availability',
70
+ description: 'Check whether an agent domain (name + tld) is available for registration. Returns availability status and pricing.',
74
71
  inputSchema: {
75
- type: "object",
72
+ type: 'object',
76
73
  properties: {
77
- name: {
78
- type: "string",
79
- description: 'The desired label, e.g. "myagent"',
80
- },
74
+ name: { type: 'string', description: 'The desired label, e.g. "myagent"' },
81
75
  tld: {
82
- type: "string",
76
+ type: 'string',
83
77
  enum: SUPPORTED_TLDS,
84
- description: "TLD",
78
+ description: 'TLD',
85
79
  },
86
80
  },
87
- required: ["name"],
81
+ required: ['name'],
88
82
  },
89
83
  },
90
84
  {
91
- name: "quote_registration",
92
- description: "Get a price quote for registering an agent identity bundle. Domain, DNS, email, SSL, AgentID NFT orchestration, and platform fee are included by default. Basename and ENS are optional.",
85
+ name: 'quote_registration',
86
+ description: 'Get a price quote for registering an agent identity bundle. Domain, DNS, email, SSL, AgentID NFT orchestration, and platform fee are included by default. Basename and ENS are optional.',
93
87
  inputSchema: {
94
- type: "object",
88
+ type: 'object',
95
89
  properties: {
96
- preferredName: { type: "string" },
97
- tld: { type: "string", enum: SUPPORTED_TLDS },
90
+ preferredName: { type: 'string' },
91
+ tld: { type: 'string', enum: SUPPORTED_TLDS },
98
92
  registerBasename: {
99
- type: "boolean",
100
- description: "Set false to skip Basename registration and cost.",
93
+ type: 'boolean',
94
+ description: 'Set false to skip Basename registration and cost.',
101
95
  default: true,
102
96
  },
103
97
  registerEns: {
104
- type: "boolean",
105
- description: "Set true to add ENS; false skips ENS cost.",
98
+ type: 'boolean',
99
+ description: 'Set true to add ENS; false skips ENS cost.',
106
100
  default: false,
107
101
  },
108
102
  emailEnabled: {
109
- type: "boolean",
110
- description: "Deprecated compatibility flag. Email is now always included.",
103
+ type: 'boolean',
104
+ description: 'Deprecated compatibility flag. Email is now always included.',
111
105
  default: true,
112
106
  },
113
107
  emailUsername: {
114
- type: "string",
115
- description: "Primary email username. Defaults to agent, producing agent@domain.",
116
- default: "agent",
108
+ type: 'string',
109
+ description: 'Primary email username. Defaults to agent, producing agent@domain.',
110
+ default: 'agent',
117
111
  },
118
112
  premiumPlan: {
119
- type: "string",
113
+ type: 'string',
120
114
  enum: SERVICE_PLAN_KEYS,
121
- default: "included",
122
- description: "Premium Plan to buy with registration.",
115
+ default: 'included',
116
+ description: 'Premium Plan to buy with registration.',
123
117
  },
124
- years: { type: "integer", default: 1, minimum: 1, maximum: 10 },
118
+ years: { type: 'integer', default: 1, minimum: 1, maximum: 10 },
125
119
  },
126
- required: ["preferredName"],
120
+ required: ['preferredName'],
127
121
  },
128
122
  },
129
123
  {
130
- name: "register_agent_identity",
131
- description: "Register a complete agent identity bundle. Domain, DNS, email, SSL, AgentID NFT orchestration, and platform fee are included by default. Basename and ENS are optional. Pays in USDC on Base. Requires AGENT_PRIVATE_KEY env var.",
124
+ name: 'register_agent_identity',
125
+ description: 'Register a complete agent identity bundle. Domain, DNS, email, SSL, AgentID NFT orchestration, and platform fee are included by default. Basename and ENS are optional. Pays in USDC on Base. Requires AGENT_PRIVATE_KEY env var.',
132
126
  inputSchema: {
133
- type: "object",
127
+ type: 'object',
134
128
  properties: {
135
- preferredName: { type: "string" },
136
- tld: { type: "string", enum: SUPPORTED_TLDS },
129
+ preferredName: { type: 'string' },
130
+ tld: { type: 'string', enum: SUPPORTED_TLDS },
137
131
  registerBasename: {
138
- type: "boolean",
139
- description: "Set false to skip Basename registration and cost.",
132
+ type: 'boolean',
133
+ description: 'Set false to skip Basename registration and cost.',
140
134
  default: true,
141
135
  },
142
- basenameLabel: {
143
- type: "string",
144
- description: "Optional alternate Basename label",
145
- },
136
+ basenameLabel: { type: 'string', description: 'Optional alternate Basename label' },
146
137
  registerEns: {
147
- type: "boolean",
148
- description: "Set true to add ENS; false skips ENS cost.",
138
+ type: 'boolean',
139
+ description: 'Set true to add ENS; false skips ENS cost.',
149
140
  default: false,
150
141
  },
151
- ensLabel: {
152
- type: "string",
153
- description: "Optional alternate ENS label",
154
- },
142
+ ensLabel: { type: 'string', description: 'Optional alternate ENS label' },
155
143
  ownerAddress: {
156
- type: "string",
157
- description: "Optional EVM address to receive NFT ownership",
144
+ type: 'string',
145
+ description: 'Optional EVM address to receive NFT ownership',
158
146
  },
159
147
  emailEnabled: {
160
- type: "boolean",
161
- description: "Deprecated compatibility flag. Email is now always included.",
148
+ type: 'boolean',
149
+ description: 'Deprecated compatibility flag. Email is now always included.',
162
150
  default: true,
163
151
  },
164
152
  emailUsername: {
165
- type: "string",
166
- description: "Primary email username. Defaults to agent, producing agent@domain.",
167
- default: "agent",
153
+ type: 'string',
154
+ description: 'Primary email username. Defaults to agent, producing agent@domain.',
155
+ default: 'agent',
168
156
  },
169
157
  premiumPlan: {
170
- type: "string",
158
+ type: 'string',
171
159
  enum: SERVICE_PLAN_KEYS,
172
- default: "included",
173
- description: "Premium Plan to buy with registration.",
174
- },
175
- years: { type: "integer", default: 1, minimum: 1, maximum: 10 },
176
- autoRenew: { type: "boolean", default: false },
177
- dnsTarget: {
178
- type: "string",
179
- description: "URL or IP to point the domain at",
160
+ default: 'included',
161
+ description: 'Premium Plan to buy with registration.',
180
162
  },
163
+ years: { type: 'integer', default: 1, minimum: 1, maximum: 10 },
164
+ autoRenew: { type: 'boolean', default: false },
165
+ dnsTarget: { type: 'string', description: 'URL or IP to point the domain at' },
181
166
  metadata: {
182
- type: "object",
167
+ type: 'object',
183
168
  properties: {
184
- name: { type: "string" },
185
- description: { type: "string" },
186
- capabilities: { type: "array", items: { type: "string" } },
187
- framework: { type: "string", enum: SUPPORTED_FRAMEWORKS },
188
- x402Endpoint: { type: "string" },
169
+ name: { type: 'string' },
170
+ description: { type: 'string' },
171
+ capabilities: { type: 'array', items: { type: 'string' } },
172
+ framework: { type: 'string', enum: SUPPORTED_FRAMEWORKS },
173
+ x402Endpoint: { type: 'string' },
189
174
  },
190
175
  },
191
176
  },
192
- required: ["preferredName"],
177
+ required: ['preferredName'],
193
178
  },
194
179
  },
195
180
  {
196
- name: "lookup_agent",
197
- description: "Look up agent identities by wallet address.",
181
+ name: 'lookup_agent',
182
+ description: 'Look up agent identities by wallet address.',
198
183
  inputSchema: {
199
- type: "object",
184
+ type: 'object',
200
185
  properties: {
201
- wallet: { type: "string", description: "0x wallet address" },
186
+ wallet: { type: 'string', description: '0x wallet address' },
202
187
  },
203
- required: ["wallet"],
188
+ required: ['wallet'],
204
189
  },
205
190
  },
206
191
  {
207
- name: "get_agent",
208
- description: "Get one agent identity by AgentDomain agent ID. Works with an agent-scoped API key.",
192
+ name: 'get_agent',
193
+ description: 'Get one agent identity by AgentDomain agent ID. Works with an agent-scoped API key.',
209
194
  inputSchema: {
210
- type: "object",
195
+ type: 'object',
211
196
  properties: {
212
- agentId: { type: "string", description: "AgentDomain agent ID (UUID)" },
197
+ agentId: { type: 'string', description: 'AgentDomain agent ID (UUID)' },
213
198
  },
214
- required: ["agentId"],
199
+ required: ['agentId'],
215
200
  },
216
201
  },
217
202
  {
218
- name: "search_agents",
219
- description: "Search the public agent registry by name, capability, or framework.",
203
+ name: 'search_agents',
204
+ description: 'Search the public agent registry by name, capability, or framework.',
220
205
  inputSchema: {
221
- type: "object",
206
+ type: 'object',
222
207
  properties: {
223
- q: { type: "string", description: "Free-text query" },
224
- capability: { type: "string" },
208
+ q: { type: 'string', description: 'Free-text query' },
209
+ capability: { type: 'string' },
225
210
  framework: {
226
- type: "string",
211
+ type: 'string',
227
212
  enum: SUPPORTED_FRAMEWORKS,
228
213
  },
229
- limit: { type: "number", default: 20 },
214
+ limit: { type: 'number', default: 20 },
230
215
  },
231
216
  },
232
217
  },
233
218
  {
234
- name: "send_agent_email",
219
+ name: 'send_agent_email',
235
220
  description: "Send an email from an agent's address (requires email-enabled identity).",
236
221
  inputSchema: {
237
- type: "object",
222
+ type: 'object',
238
223
  properties: {
239
- agentId: { type: "string", description: "AgentDomain agent ID (UUID)" },
240
- to: { type: "string" },
241
- subject: { type: "string" },
242
- text: { type: "string" },
224
+ agentId: { type: 'string', description: 'AgentDomain agent ID (UUID)' },
225
+ to: { type: 'string' },
226
+ subject: { type: 'string' },
227
+ text: { type: 'string' },
243
228
  fromAddress: {
244
- type: "string",
245
- description: "Optional primary or active alias address to send from.",
229
+ type: 'string',
230
+ description: 'Optional primary or active alias address to send from.',
246
231
  },
247
232
  },
248
- required: ["agentId", "to", "subject"],
233
+ required: ['agentId', 'to', 'subject'],
249
234
  },
250
235
  },
251
236
  {
252
- name: "update_primary_email",
253
- description: "Change the primary email username for an agent. The old primary address stops receiving new mail.",
237
+ name: 'update_primary_email',
238
+ description: 'Change the primary email username for an agent. The old primary address stops receiving new mail.',
254
239
  inputSchema: {
255
- type: "object",
240
+ type: 'object',
256
241
  properties: {
257
- agentId: { type: "string", description: "AgentDomain agent ID (UUID)" },
258
- username: {
259
- type: "string",
260
- description: "New primary local-part, e.g. agent or support",
261
- },
242
+ agentId: { type: 'string', description: 'AgentDomain agent ID (UUID)' },
243
+ username: { type: 'string', description: 'New primary local-part, e.g. agent or support' },
262
244
  },
263
- required: ["agentId", "username"],
245
+ required: ['agentId', 'username'],
264
246
  },
265
247
  },
266
248
  {
267
- name: "send_agent_email_batch",
268
- description: "Queue up to 100 emails in one request; each recipient counts toward monthly usage.",
249
+ name: 'send_agent_email_batch',
250
+ description: 'Queue up to 100 emails in one request; each recipient counts toward monthly usage.',
269
251
  inputSchema: {
270
- type: "object",
252
+ type: 'object',
271
253
  properties: {
272
- agentId: { type: "string" },
254
+ agentId: { type: 'string' },
273
255
  messages: {
274
- type: "array",
256
+ type: 'array',
275
257
  maxItems: 100,
276
258
  items: {
277
- type: "object",
259
+ type: 'object',
278
260
  properties: {
279
- to: { type: "string" },
280
- subject: { type: "string" },
281
- text: { type: "string" },
282
- fromAddress: { type: "string" },
261
+ to: { type: 'string' },
262
+ subject: { type: 'string' },
263
+ text: { type: 'string' },
264
+ fromAddress: { type: 'string' },
283
265
  },
284
- required: ["to", "subject", "text"],
266
+ required: ['to', 'subject', 'text'],
285
267
  },
286
268
  },
287
- idempotencyKey: { type: "string" },
269
+ idempotencyKey: { type: 'string' },
288
270
  },
289
- required: ["agentId", "messages"],
271
+ required: ['agentId', 'messages'],
290
272
  },
291
273
  },
292
274
  {
293
- name: "get_agent_email_usage",
294
- description: "Get combined sent and received monthly quota usage and reset date.",
275
+ name: 'get_agent_email_usage',
276
+ description: 'Get combined sent and received monthly quota usage and reset date.',
295
277
  inputSchema: {
296
- type: "object",
297
- properties: { agentId: { type: "string" } },
298
- required: ["agentId"],
278
+ type: 'object',
279
+ properties: { agentId: { type: 'string' } },
280
+ required: ['agentId'],
299
281
  },
300
282
  },
301
283
  {
302
- name: "configure_email_webhook",
303
- description: "Configure the signed email.received webhook for an agent.",
284
+ name: 'configure_email_webhook',
285
+ description: 'Configure the signed email.received webhook for an agent.',
304
286
  inputSchema: {
305
- type: "object",
287
+ type: 'object',
306
288
  properties: {
307
- agentId: { type: "string" },
308
- url: { type: "string" },
309
- payloadMode: { type: "string", enum: ["metadata", "inline_text"] },
310
- enabled: { type: "boolean" },
289
+ agentId: { type: 'string' },
290
+ url: { type: 'string' },
291
+ payloadMode: { type: 'string', enum: ['metadata', 'inline_text'] },
292
+ enabled: { type: 'boolean' },
311
293
  },
312
- required: ["agentId", "url"],
294
+ required: ['agentId', 'url'],
313
295
  },
314
296
  },
315
297
  {
316
- name: "create_email_alias",
317
- description: "Create an extra receive-and-send email alias. Requires paid-plan alias capacity.",
298
+ name: 'create_email_alias',
299
+ description: 'Create an extra receive-and-send email alias. Requires paid-plan alias capacity.',
318
300
  inputSchema: {
319
- type: "object",
301
+ type: 'object',
320
302
  properties: {
321
- agentId: { type: "string", description: "AgentDomain agent ID (UUID)" },
322
- username: {
323
- type: "string",
324
- description: "Alias local-part, e.g. billing",
325
- },
303
+ agentId: { type: 'string', description: 'AgentDomain agent ID (UUID)' },
304
+ username: { type: 'string', description: 'Alias local-part, e.g. billing' },
326
305
  },
327
- required: ["agentId", "username"],
306
+ required: ['agentId', 'username'],
328
307
  },
329
308
  },
330
309
  {
331
- name: "delete_email_alias",
332
- description: "Delete an active email alias from an agent.",
310
+ name: 'delete_email_alias',
311
+ description: 'Delete an active email alias from an agent.',
333
312
  inputSchema: {
334
- type: "object",
313
+ type: 'object',
335
314
  properties: {
336
- agentId: { type: "string", description: "AgentDomain agent ID (UUID)" },
337
- emailAddress: {
338
- type: "string",
339
- description: "Full alias address to delete",
340
- },
315
+ agentId: { type: 'string', description: 'AgentDomain agent ID (UUID)' },
316
+ emailAddress: { type: 'string', description: 'Full alias address to delete' },
341
317
  },
342
- required: ["agentId", "emailAddress"],
318
+ required: ['agentId', 'emailAddress'],
343
319
  },
344
320
  },
345
321
  {
346
- name: "list_agent_email",
347
- description: "List received/sent text-only email messages for an email-enabled agent, including extracted verification codes.",
322
+ name: 'list_agent_email',
323
+ description: 'List received/sent text-only email messages for an email-enabled agent, including extracted verification codes.',
348
324
  inputSchema: {
349
- type: "object",
325
+ type: 'object',
350
326
  properties: {
351
- agentId: { type: "string", description: "AgentDomain agent ID (UUID)" },
352
- limit: { type: "number", default: 20 },
327
+ agentId: { type: 'string', description: 'AgentDomain agent ID (UUID)' },
328
+ limit: { type: 'number', default: 20 },
353
329
  },
354
- required: ["agentId"],
330
+ required: ['agentId'],
355
331
  },
356
332
  },
357
333
  {
358
- name: "delete_agent_email",
359
- description: "Permanently delete one email message from an agent inbox.",
334
+ name: 'delete_agent_email',
335
+ description: 'Permanently delete one email message from an agent inbox.',
360
336
  inputSchema: {
361
- type: "object",
337
+ type: 'object',
362
338
  properties: {
363
- agentId: { type: "string", description: "AgentDomain agent ID (UUID)" },
364
- messageId: { type: "string", description: "Email message ID (UUID)" },
339
+ agentId: { type: 'string', description: 'AgentDomain agent ID (UUID)' },
340
+ messageId: { type: 'string', description: 'Email message ID (UUID)' },
365
341
  },
366
- required: ["agentId", "messageId"],
342
+ required: ['agentId', 'messageId'],
367
343
  },
368
344
  },
369
345
  {
370
- name: "get_dns_capabilities",
371
- description: "Get the machine-readable Spaceship DNS types, fields, limits, and safety warnings supported by AgentDomain.",
346
+ name: 'get_dns_capabilities',
347
+ description: 'Get the machine-readable DNS types, fields, limits, and safety warnings supported by AgentDomain.',
372
348
  inputSchema: {
373
- type: "object",
374
- properties: {
375
- agentId: { type: "string", description: "AgentDomain agent ID (UUID)" },
376
- },
377
- required: ["agentId"],
349
+ type: 'object',
350
+ properties: { agentId: { type: 'string', description: 'AgentDomain agent ID (UUID)' } },
351
+ required: ['agentId'],
378
352
  },
379
353
  },
380
354
  {
381
- name: "list_dns_records",
382
- description: "List Spaceship-backed DNS records for an agent domain.",
355
+ name: 'list_dns_records',
356
+ description: 'List DNS records for an agent domain.',
383
357
  inputSchema: {
384
- type: "object",
358
+ type: 'object',
385
359
  properties: {
386
- agentId: { type: "string", description: "AgentDomain agent ID (UUID)" },
360
+ agentId: { type: 'string', description: 'AgentDomain agent ID (UUID)' },
387
361
  },
388
- required: ["agentId"],
362
+ required: ['agentId'],
389
363
  },
390
364
  },
391
365
  {
392
- name: "create_dns_record",
393
- description: "Create a user-managed DNS record and sync the full DNS state to Spaceship.",
366
+ name: 'create_dns_record',
367
+ description: 'Create a user-managed DNS record and apply the resulting DNS state.',
394
368
  inputSchema: {
395
- type: "object",
369
+ type: 'object',
396
370
  properties: {
397
- agentId: { type: "string" },
398
- type: { type: "string", enum: DNS_RECORD_TYPES },
399
- name: { type: "string" },
400
- value: {
401
- type: "string",
402
- description: "Legacy format; structured data is preferred",
403
- },
404
- data: {
405
- type: "object",
406
- description: "Type-specific structured DNS record data",
407
- },
408
- ttl: { type: "number", default: 3600 },
409
- priority: { type: "number" },
371
+ agentId: { type: 'string' },
372
+ type: { type: 'string', enum: DNS_RECORD_TYPES },
373
+ name: { type: 'string' },
374
+ value: { type: 'string', description: 'Legacy format; structured data is preferred' },
375
+ data: { type: 'object', description: 'Type-specific structured DNS record data' },
376
+ ttl: { type: 'number', default: 3600 },
377
+ priority: { type: 'number' },
410
378
  },
411
- required: ["agentId", "type", "name"],
379
+ required: ['agentId', 'type', 'name'],
412
380
  },
413
381
  },
414
382
  {
415
- name: "update_dns_record",
416
- description: "Update a user-managed DNS record and sync the DNS state to Spaceship.",
383
+ name: 'update_dns_record',
384
+ description: 'Update a user-managed DNS record and apply the resulting DNS state.',
417
385
  inputSchema: {
418
- type: "object",
386
+ type: 'object',
419
387
  properties: {
420
- agentId: { type: "string" },
421
- recordId: { type: "string" },
422
- type: { type: "string", enum: DNS_RECORD_TYPES },
423
- name: { type: "string" },
424
- value: { type: "string" },
425
- data: {
426
- type: "object",
427
- description: "Type-specific structured DNS record data",
428
- },
429
- ttl: { type: "number" },
430
- priority: { type: "number" },
388
+ agentId: { type: 'string' },
389
+ recordId: { type: 'string' },
390
+ type: { type: 'string', enum: DNS_RECORD_TYPES },
391
+ name: { type: 'string' },
392
+ value: { type: 'string' },
393
+ data: { type: 'object', description: 'Type-specific structured DNS record data' },
394
+ ttl: { type: 'number' },
395
+ priority: { type: 'number' },
431
396
  },
432
- required: ["agentId", "recordId"],
397
+ required: ['agentId', 'recordId'],
433
398
  },
434
399
  },
435
400
  {
436
- name: "delete_dns_record",
437
- description: "Delete a user-managed DNS record and sync the DNS state to Spaceship.",
401
+ name: 'delete_dns_record',
402
+ description: 'Delete a user-managed DNS record and apply the resulting DNS state.',
438
403
  inputSchema: {
439
- type: "object",
404
+ type: 'object',
440
405
  properties: {
441
- agentId: { type: "string" },
442
- recordId: { type: "string" },
406
+ agentId: { type: 'string' },
407
+ recordId: { type: 'string' },
443
408
  },
444
- required: ["agentId", "recordId"],
409
+ required: ['agentId', 'recordId'],
445
410
  },
446
411
  },
447
412
  {
448
- name: "change_dns_records",
449
- description: "Preview or apply an atomic DNS merge/replace batch. Apply requires the baseRevision returned by preview.",
413
+ name: 'change_dns_records',
414
+ description: 'Preview or apply an atomic DNS merge/replace batch. Apply requires the baseRevision returned by preview.',
450
415
  inputSchema: {
451
- type: "object",
416
+ type: 'object',
452
417
  properties: {
453
- agentId: { type: "string" },
454
- records: { type: "array", maxItems: 200, items: { type: "object" } },
455
- mode: { type: "string", enum: ["merge", "replace"], default: "merge" },
456
- dryRun: { type: "boolean", default: true },
457
- baseRevision: {
458
- type: "string",
459
- description: "Required when dryRun is false",
460
- },
418
+ agentId: { type: 'string' },
419
+ records: { type: 'array', maxItems: 200, items: { type: 'object' } },
420
+ mode: { type: 'string', enum: ['merge', 'replace'], default: 'merge' },
421
+ dryRun: { type: 'boolean', default: true },
422
+ baseRevision: { type: 'string', description: 'Required when dryRun is false' },
461
423
  },
462
- required: ["agentId", "records"],
424
+ required: ['agentId', 'records'],
463
425
  },
464
426
  },
465
427
  {
466
- name: "import_dns_zone",
467
- description: "Preview or apply a BIND zone import. System-managed records remain protected.",
428
+ name: 'import_dns_zone',
429
+ description: 'Preview or apply a BIND zone import. System-managed records remain protected.',
468
430
  inputSchema: {
469
- type: "object",
431
+ type: 'object',
470
432
  properties: {
471
- agentId: { type: "string" },
472
- zoneFile: {
473
- type: "string",
474
- description: "BIND zone text, maximum 256 KiB",
475
- },
476
- mode: { type: "string", enum: ["merge", "replace"], default: "merge" },
477
- dryRun: { type: "boolean", default: true },
478
- baseRevision: {
479
- type: "string",
480
- description: "Required when dryRun is false",
481
- },
433
+ agentId: { type: 'string' },
434
+ zoneFile: { type: 'string', description: 'BIND zone text, maximum 256 KiB' },
435
+ mode: { type: 'string', enum: ['merge', 'replace'], default: 'merge' },
436
+ dryRun: { type: 'boolean', default: true },
437
+ baseRevision: { type: 'string', description: 'Required when dryRun is false' },
482
438
  },
483
- required: ["agentId", "zoneFile"],
439
+ required: ['agentId', 'zoneFile'],
484
440
  },
485
441
  },
486
442
  {
487
- name: "export_dns_zone",
488
- description: "Export user-managed or permitted complete DNS state as a standard BIND zone file.",
443
+ name: 'export_dns_zone',
444
+ description: 'Export user-managed or permitted complete DNS state as a standard BIND zone file.',
489
445
  inputSchema: {
490
- type: "object",
446
+ type: 'object',
491
447
  properties: {
492
- agentId: { type: "string" },
493
- scope: { type: "string", enum: ["user", "all"], default: "user" },
448
+ agentId: { type: 'string' },
449
+ scope: { type: 'string', enum: ['user', 'all'], default: 'user' },
494
450
  },
495
- required: ["agentId"],
451
+ required: ['agentId'],
496
452
  },
497
453
  },
498
454
  {
499
- name: "reconfigure_ssl",
500
- description: "Rebuild the Cloudflare SaaS SSL hostname and sync required DNS validation records.",
455
+ name: 'reconfigure_ssl',
456
+ description: 'Rebuild the managed SSL hostname and sync required DNS validation records.',
501
457
  inputSchema: {
502
- type: "object",
458
+ type: 'object',
503
459
  properties: {
504
- agentId: { type: "string" },
460
+ agentId: { type: 'string' },
505
461
  },
506
- required: ["agentId"],
462
+ required: ['agentId'],
507
463
  },
508
464
  },
509
465
  {
510
- name: "fund_renewal_vault",
466
+ name: 'fund_renewal_vault',
511
467
  description: "Deposit USDC into an agent's renewal vault to keep its domain alive.",
512
468
  inputSchema: {
513
- type: "object",
469
+ type: 'object',
514
470
  properties: {
515
- agentId: { type: "string" },
516
- amountUsdc: {
517
- type: "string",
518
- description: 'USDC amount, e.g. "10.00"',
519
- },
471
+ agentId: { type: 'string' },
472
+ amountUsdc: { type: 'string', description: 'USDC amount, e.g. "10.00"' },
520
473
  },
521
- required: ["agentId", "amountUsdc"],
474
+ required: ['agentId', 'amountUsdc'],
522
475
  },
523
476
  },
524
477
  {
525
- name: "withdraw_renewal_vault",
526
- description: "Build the owner-signed withdrawal transaction for unused funds in an AgentID renewal vault.",
478
+ name: 'withdraw_renewal_vault',
479
+ description: 'Build the owner-signed withdrawal transaction for unused funds in an AgentID renewal vault.',
527
480
  inputSchema: {
528
- type: "object",
481
+ type: 'object',
529
482
  properties: {
530
- agentId: { type: "string" },
531
- amountUsdc: { type: "string", description: 'USDC amount, e.g. "5.00"' },
483
+ agentId: { type: 'string' },
484
+ amountUsdc: { type: 'string', description: 'USDC amount, e.g. "5.00"' },
532
485
  },
533
- required: ["agentId", "amountUsdc"],
486
+ required: ['agentId', 'amountUsdc'],
534
487
  },
535
488
  },
536
489
  {
537
- name: "get_renewal_status",
538
- description: "Get renewal vault status for an agent, including exact renewal amount, vault balance, missing deposit, expiry, and auto-renew readiness.",
490
+ name: 'get_renewal_status',
491
+ description: 'Get renewal vault status for an agent, including exact renewal amount, vault balance, missing deposit, expiry, and auto-renew readiness.',
539
492
  inputSchema: {
540
- type: "object",
493
+ type: 'object',
541
494
  properties: {
542
- agentId: { type: "string", description: "AgentDomain agent ID (UUID)" },
495
+ agentId: { type: 'string', description: 'AgentDomain agent ID (UUID)' },
543
496
  },
544
- required: ["agentId"],
497
+ required: ['agentId'],
545
498
  },
546
499
  },
547
500
  {
548
- name: "get_service_plan",
549
- description: "Get the current AgentDomain Premium Plan, entitlement limits, billing interval, registry privacy, and recent purchases for one agent.",
501
+ name: 'get_service_plan',
502
+ description: 'Get the current AgentDomain Premium Plan, entitlement limits, billing interval, registry privacy, and recent purchases for one agent.',
550
503
  inputSchema: {
551
- type: "object",
504
+ type: 'object',
552
505
  properties: {
553
- agentId: { type: "string", description: "AgentDomain agent ID (UUID)" },
506
+ agentId: { type: 'string', description: 'AgentDomain agent ID (UUID)' },
554
507
  },
555
- required: ["agentId"],
508
+ required: ['agentId'],
556
509
  },
557
510
  },
558
511
  {
559
- name: "purchase_service_plan",
560
- description: "Upgrade one agent to Starter, Pro, or Enterprise with x402 USDC. Requires AGENT_PRIVATE_KEY for the owner wallet.",
512
+ name: 'purchase_service_plan',
513
+ description: 'Upgrade one agent to Starter, Pro, or Enterprise with x402 USDC. Requires AGENT_PRIVATE_KEY for the owner wallet.',
561
514
  inputSchema: {
562
- type: "object",
515
+ type: 'object',
563
516
  properties: {
564
- agentId: { type: "string" },
565
- plan: { type: "string", enum: ["starter", "pro", "enterprise"] },
517
+ agentId: { type: 'string' },
518
+ plan: { type: 'string', enum: ['starter', 'pro', 'enterprise'] },
566
519
  planSku: {
567
- type: "string",
568
- description: "For Enterprise, e.g. enterprise-100000 through enterprise-5000000.",
520
+ type: 'string',
521
+ description: 'For Enterprise, e.g. enterprise-100000 through enterprise-5000000.',
569
522
  },
570
523
  },
571
- required: ["agentId", "plan"],
524
+ required: ['agentId', 'plan'],
572
525
  },
573
526
  },
574
527
  {
575
- name: "set_registry_visibility",
576
- description: "Hide or show one agent in the public AgentDomain registry. Hiding requires an active paid Premium Plan.",
528
+ name: 'set_registry_visibility',
529
+ description: 'Hide or show one agent in the public AgentDomain registry. Hiding requires an active paid Premium Plan.',
577
530
  inputSchema: {
578
- type: "object",
531
+ type: 'object',
579
532
  properties: {
580
- agentId: { type: "string" },
533
+ agentId: { type: 'string' },
581
534
  registryHidden: {
582
- type: "boolean",
583
- description: "true hides the agent from public registry/search; false makes it public",
535
+ type: 'boolean',
536
+ description: 'true hides the agent from public registry/search; false makes it public',
584
537
  },
585
538
  },
586
- required: ["agentId", "registryHidden"],
539
+ required: ['agentId', 'registryHidden'],
587
540
  },
588
541
  },
589
542
  {
590
- name: "schedule_service_plan_renewal",
591
- description: "Choose the exact Premium Plan SKU to charge at the next identity renewal, including any Enterprise email tier.",
543
+ name: 'schedule_service_plan_renewal',
544
+ description: 'Choose the exact Premium Plan SKU to charge at the next identity renewal, including any Enterprise email tier.',
592
545
  inputSchema: {
593
- type: "object",
546
+ type: 'object',
594
547
  properties: {
595
- agentId: { type: "string" },
596
- plan: {
597
- type: "string",
598
- enum: ["included", "starter", "pro", "enterprise"],
599
- },
548
+ agentId: { type: 'string' },
549
+ plan: { type: 'string', enum: ['included', 'starter', 'pro', 'enterprise'] },
600
550
  planSku: {
601
- type: "string",
602
- description: "Exact SKU; Enterprise examples range from enterprise-100000 to enterprise-5000000.",
551
+ type: 'string',
552
+ description: 'Exact SKU; Enterprise examples range from enterprise-100000 to enterprise-5000000.',
603
553
  },
604
554
  },
605
- required: ["agentId", "plan", "planSku"],
555
+ required: ['agentId', 'plan', 'planSku'],
606
556
  },
607
557
  },
608
558
  {
609
- name: "enable_auto_renew",
610
- description: "Enable on-chain auto-renew for an agent. Requires the owner wallet private key and the RenewalVault contract address.",
559
+ name: 'enable_auto_renew',
560
+ description: 'Enable on-chain auto-renew for an agent. Requires the owner wallet private key and the RenewalVault contract address.',
611
561
  inputSchema: {
612
- type: "object",
562
+ type: 'object',
613
563
  properties: {
614
- agentId: { type: "string", description: "AgentDomain agent ID (UUID)" },
564
+ agentId: { type: 'string', description: 'AgentDomain agent ID (UUID)' },
615
565
  },
616
- required: ["agentId"],
566
+ required: ['agentId'],
617
567
  },
618
568
  },
619
569
  ];
570
+ const READ_ONLY_TOOL_NAMES = new Set([
571
+ 'check_domain_availability',
572
+ 'quote_registration',
573
+ 'lookup_agent',
574
+ 'get_agent',
575
+ 'search_agents',
576
+ 'get_agent_email_usage',
577
+ 'list_agent_email',
578
+ 'get_dns_capabilities',
579
+ 'list_dns_records',
580
+ 'export_dns_zone',
581
+ 'get_renewal_status',
582
+ 'get_service_plan',
583
+ ]);
584
+ const ADDITIVE_WRITE_TOOL_NAMES = new Set([
585
+ 'send_agent_email',
586
+ 'send_agent_email_batch',
587
+ 'create_email_alias',
588
+ 'create_dns_record',
589
+ ]);
590
+ function annotationsForTool(name) {
591
+ const readOnly = READ_ONLY_TOOL_NAMES.has(name);
592
+ return {
593
+ readOnlyHint: readOnly,
594
+ destructiveHint: readOnly ? false : !ADDITIVE_WRITE_TOOL_NAMES.has(name),
595
+ idempotentHint: readOnly,
596
+ openWorldHint: true,
597
+ };
598
+ }
599
+ const TOOLS = TOOL_DEFINITIONS.map((tool) => ({
600
+ ...tool,
601
+ annotations: annotationsForTool(tool.name),
602
+ }));
603
+ const ENABLED_TOOLS = WRITE_TOOLS_ENABLED
604
+ ? TOOLS
605
+ : TOOLS.filter((tool) => READ_ONLY_TOOL_NAMES.has(tool.name));
606
+ const ENABLED_TOOL_NAMES = new Set(ENABLED_TOOLS.map((tool) => tool.name));
620
607
  // ----------------------------------------------------------------------
621
608
  // HANDLERS
622
609
  // ----------------------------------------------------------------------
623
610
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
624
- tools: TOOLS,
611
+ tools: ENABLED_TOOLS,
625
612
  }));
626
613
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
627
614
  const { name, arguments: args } = request.params;
628
- const client = getClient();
629
615
  try {
616
+ if (!ENABLED_TOOL_NAMES.has(name)) {
617
+ return {
618
+ isError: true,
619
+ content: [{ type: 'text', text: `Unknown or disabled tool: ${name}` }],
620
+ };
621
+ }
622
+ const client = getClient();
630
623
  switch (name) {
631
- case "check_domain_availability": {
624
+ case 'check_domain_availability': {
632
625
  const a = z
633
- .object({
634
- name: z.string(),
635
- tld: z.enum(SUPPORTED_TLDS).default("xyz"),
636
- })
626
+ .object({ name: z.string(), tld: z.enum(SUPPORTED_TLDS).default('xyz') })
637
627
  .parse(args);
638
628
  const result = await client.checkAvailability(a.name, { tld: a.tld });
639
- return {
640
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
641
- };
629
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
642
630
  }
643
- case "quote_registration": {
631
+ case 'quote_registration': {
644
632
  const a = z
645
633
  .object({
646
634
  preferredName: z.string(),
647
- tld: z.enum(SUPPORTED_TLDS).default("xyz"),
635
+ tld: z.enum(SUPPORTED_TLDS).default('xyz'),
648
636
  registerBasename: z.boolean().default(true),
649
637
  registerEns: z.boolean().default(false),
650
638
  emailEnabled: z.boolean().default(true),
651
639
  emailUsername: z.string().optional(),
652
- premiumPlan: z.enum(SERVICE_PLAN_KEYS).default("included"),
640
+ premiumPlan: z.enum(SERVICE_PLAN_KEYS).default('included'),
653
641
  years: z.number().int().min(1).max(10).default(1),
654
642
  })
655
643
  .parse(args);
656
644
  const result = await client.quote(a);
657
- return {
658
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
659
- };
645
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
660
646
  }
661
- case "register_agent_identity": {
647
+ case 'register_agent_identity': {
662
648
  if (!AGENT_PRIVATE_KEY) {
663
649
  return {
664
650
  isError: true,
665
651
  content: [
666
652
  {
667
- type: "text",
653
+ type: 'text',
668
654
  text: "AGENT_PRIVATE_KEY env var is required for registration. Set it to your agent's wallet private key.",
669
655
  },
670
656
  ],
@@ -673,7 +659,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
673
659
  const a = z
674
660
  .object({
675
661
  preferredName: z.string(),
676
- tld: z.enum(SUPPORTED_TLDS).default("xyz"),
662
+ tld: z.enum(SUPPORTED_TLDS).default('xyz'),
677
663
  registerBasename: z.boolean().default(true),
678
664
  basenameLabel: z.string().optional(),
679
665
  registerEns: z.boolean().default(false),
@@ -684,7 +670,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
684
670
  .optional(),
685
671
  emailEnabled: z.boolean().default(true),
686
672
  emailUsername: z.string().optional(),
687
- premiumPlan: z.enum(SERVICE_PLAN_KEYS).default("included"),
673
+ premiumPlan: z.enum(SERVICE_PLAN_KEYS).default('included'),
688
674
  years: z.number().int().min(1).max(10).default(1),
689
675
  autoRenew: z.boolean().default(false),
690
676
  dnsTarget: z.string().optional(),
@@ -698,25 +684,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
698
684
  ownerAddress: a.ownerAddress,
699
685
  metadata: a.metadata,
700
686
  });
701
- return {
702
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
703
- };
687
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
704
688
  }
705
- case "lookup_agent": {
689
+ case 'lookup_agent': {
706
690
  const a = z.object({ wallet: z.string() }).parse(args);
707
691
  const result = await client.getAgentsByWallet(a.wallet);
708
- return {
709
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
710
- };
692
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
711
693
  }
712
- case "get_agent": {
694
+ case 'get_agent': {
713
695
  const a = z.object({ agentId: z.string() }).parse(args);
714
696
  const result = await client.getAgentById(a.agentId);
715
- return {
716
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
717
- };
697
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
718
698
  }
719
- case "search_agents": {
699
+ case 'search_agents': {
720
700
  const a = z
721
701
  .object({
722
702
  q: z.string().optional(),
@@ -726,11 +706,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
726
706
  })
727
707
  .parse(args);
728
708
  const result = await client.search(a);
729
- return {
730
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
731
- };
709
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
732
710
  }
733
- case "send_agent_email": {
711
+ case 'send_agent_email': {
734
712
  const a = z
735
713
  .object({
736
714
  agentId: z.string(),
@@ -746,29 +724,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
746
724
  subject: a.subject,
747
725
  text: a.text,
748
726
  });
749
- return {
750
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
751
- };
727
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
752
728
  }
753
- case "list_agent_email": {
754
- const a = z
755
- .object({ agentId: z.string(), limit: z.number().default(20) })
756
- .parse(args);
729
+ case 'list_agent_email': {
730
+ const a = z.object({ agentId: z.string(), limit: z.number().default(20) }).parse(args);
757
731
  const result = await client.listEmail(a.agentId, { limit: a.limit });
758
- return {
759
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
760
- };
732
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
761
733
  }
762
- case "delete_agent_email": {
734
+ case 'delete_agent_email': {
763
735
  const a = z
764
736
  .object({ agentId: z.string().uuid(), messageId: z.string().uuid() })
765
737
  .parse(args);
766
738
  const result = await client.deleteEmailMessage(a.agentId, a.messageId);
767
- return {
768
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
769
- };
739
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
770
740
  }
771
- case "send_agent_email_batch": {
741
+ case 'send_agent_email_batch': {
772
742
  const a = z
773
743
  .object({
774
744
  agentId: z.string(),
@@ -787,75 +757,51 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
787
757
  messages: a.messages,
788
758
  idempotencyKey: a.idempotencyKey,
789
759
  });
790
- return {
791
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
792
- };
760
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
793
761
  }
794
- case "get_agent_email_usage": {
762
+ case 'get_agent_email_usage': {
795
763
  const a = z.object({ agentId: z.string() }).parse(args);
796
764
  const result = await client.getEmailUsage(a.agentId);
797
- return {
798
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
799
- };
765
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
800
766
  }
801
- case "configure_email_webhook": {
767
+ case 'configure_email_webhook': {
802
768
  const a = z
803
769
  .object({
804
770
  agentId: z.string(),
805
771
  url: z.string().url(),
806
- payloadMode: z
807
- .enum(["metadata", "inline_text"])
808
- .default("metadata"),
772
+ payloadMode: z.enum(['metadata', 'inline_text']).default('metadata'),
809
773
  enabled: z.boolean().default(true),
810
774
  })
811
775
  .parse(args);
812
776
  const result = await client.setEmailWebhook(a.agentId, a);
813
- return {
814
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
815
- };
777
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
816
778
  }
817
- case "update_primary_email": {
818
- const a = z
819
- .object({ agentId: z.string(), username: z.string() })
820
- .parse(args);
779
+ case 'update_primary_email': {
780
+ const a = z.object({ agentId: z.string(), username: z.string() }).parse(args);
821
781
  const result = await client.updatePrimaryEmail(a.agentId, a.username);
822
- return {
823
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
824
- };
782
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
825
783
  }
826
- case "create_email_alias": {
827
- const a = z
828
- .object({ agentId: z.string(), username: z.string() })
829
- .parse(args);
784
+ case 'create_email_alias': {
785
+ const a = z.object({ agentId: z.string(), username: z.string() }).parse(args);
830
786
  const result = await client.createEmailAlias(a.agentId, a.username);
831
- return {
832
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
833
- };
787
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
834
788
  }
835
- case "delete_email_alias": {
836
- const a = z
837
- .object({ agentId: z.string(), emailAddress: z.string().email() })
838
- .parse(args);
789
+ case 'delete_email_alias': {
790
+ const a = z.object({ agentId: z.string(), emailAddress: z.string().email() }).parse(args);
839
791
  const result = await client.deleteEmailAlias(a.agentId, a.emailAddress);
840
- return {
841
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
842
- };
792
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
843
793
  }
844
- case "get_dns_capabilities": {
794
+ case 'get_dns_capabilities': {
845
795
  const a = z.object({ agentId: z.string() }).parse(args);
846
796
  const result = await client.getDnsCapabilities(a.agentId);
847
- return {
848
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
849
- };
797
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
850
798
  }
851
- case "list_dns_records": {
799
+ case 'list_dns_records': {
852
800
  const a = z.object({ agentId: z.string() }).parse(args);
853
801
  const result = await client.listDnsRecords(a.agentId);
854
- return {
855
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
856
- };
802
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
857
803
  }
858
- case "create_dns_record": {
804
+ case 'create_dns_record': {
859
805
  const a = z
860
806
  .object({
861
807
  agentId: z.string(),
@@ -869,11 +815,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
869
815
  .parse(args);
870
816
  const { agentId, ...record } = a;
871
817
  const result = await client.createDnsRecord(agentId, dnsRecordSchema.parse(record));
872
- return {
873
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
874
- };
818
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
875
819
  }
876
- case "update_dns_record": {
820
+ case 'update_dns_record': {
877
821
  const a = z
878
822
  .object({
879
823
  agentId: z.string(),
@@ -887,28 +831,20 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
887
831
  })
888
832
  .parse(args);
889
833
  const { agentId, recordId, ...record } = a;
890
- const result = await client.updateDnsRecord(agentId, recordId, dnsRecordObjectSchema
891
- .partial()
892
- .parse(record));
893
- return {
894
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
895
- };
834
+ const result = await client.updateDnsRecord(agentId, recordId, dnsRecordObjectSchema.partial().parse(record));
835
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
896
836
  }
897
- case "delete_dns_record": {
898
- const a = z
899
- .object({ agentId: z.string(), recordId: z.string() })
900
- .parse(args);
837
+ case 'delete_dns_record': {
838
+ const a = z.object({ agentId: z.string(), recordId: z.string() }).parse(args);
901
839
  const result = await client.deleteDnsRecord(a.agentId, a.recordId);
902
- return {
903
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
904
- };
840
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
905
841
  }
906
- case "change_dns_records": {
842
+ case 'change_dns_records': {
907
843
  const a = z
908
844
  .object({
909
845
  agentId: z.string(),
910
846
  records: z.array(dnsRecordSchema).min(1).max(200),
911
- mode: z.enum(["merge", "replace"]).default("merge"),
847
+ mode: z.enum(['merge', 'replace']).default('merge'),
912
848
  dryRun: z.boolean().default(true),
913
849
  baseRevision: z.string().length(64).optional(),
914
850
  })
@@ -916,16 +852,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
916
852
  const result = a.dryRun
917
853
  ? await client.previewDnsBatch(a.agentId, a.records, a.mode)
918
854
  : await client.applyDnsBatch(a.agentId, a.records, requireDnsRevision(a.baseRevision), a.mode);
919
- return {
920
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
921
- };
855
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
922
856
  }
923
- case "import_dns_zone": {
857
+ case 'import_dns_zone': {
924
858
  const a = z
925
859
  .object({
926
860
  agentId: z.string(),
927
861
  zoneFile: z.string().min(1).max(262_144),
928
- mode: z.enum(["merge", "replace"]).default("merge"),
862
+ mode: z.enum(['merge', 'replace']).default('merge'),
929
863
  dryRun: z.boolean().default(true),
930
864
  baseRevision: z.string().length(64).optional(),
931
865
  })
@@ -933,67 +867,49 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
933
867
  const result = a.dryRun
934
868
  ? await client.previewDnsImport(a.agentId, a.zoneFile, a.mode)
935
869
  : await client.applyDnsImport(a.agentId, a.zoneFile, requireDnsRevision(a.baseRevision), a.mode);
936
- return {
937
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
938
- };
870
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
939
871
  }
940
- case "export_dns_zone": {
872
+ case 'export_dns_zone': {
941
873
  const a = z
942
- .object({
943
- agentId: z.string(),
944
- scope: z.enum(["user", "all"]).default("user"),
945
- })
874
+ .object({ agentId: z.string(), scope: z.enum(['user', 'all']).default('user') })
946
875
  .parse(args);
947
876
  const result = await client.exportDnsZone(a.agentId, a.scope);
948
- return { content: [{ type: "text", text: result }] };
877
+ return { content: [{ type: 'text', text: result }] };
949
878
  }
950
- case "reconfigure_ssl": {
879
+ case 'reconfigure_ssl': {
951
880
  const a = z.object({ agentId: z.string() }).parse(args);
952
881
  const result = await client.reconfigureSsl(a.agentId);
953
- return {
954
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
955
- };
882
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
956
883
  }
957
- case "fund_renewal_vault": {
958
- const a = z
959
- .object({ agentId: z.string(), amountUsdc: z.string() })
960
- .parse(args);
884
+ case 'fund_renewal_vault': {
885
+ const a = z.object({ agentId: z.string(), amountUsdc: z.string() }).parse(args);
961
886
  const result = await client.fundRenewalVault(a.agentId, a.amountUsdc);
962
- return {
963
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
964
- };
887
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
965
888
  }
966
- case "withdraw_renewal_vault": {
967
- const a = z
968
- .object({ agentId: z.string(), amountUsdc: z.string() })
969
- .parse(args);
889
+ case 'withdraw_renewal_vault': {
890
+ requireDirectBaseWriteBuilderCode('withdraw_renewal_vault');
891
+ const a = z.object({ agentId: z.string(), amountUsdc: z.string() }).parse(args);
970
892
  const result = await client.withdrawFromVault(a.agentId, a.amountUsdc);
971
- return {
972
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
973
- };
893
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
974
894
  }
975
- case "get_renewal_status": {
895
+ case 'get_renewal_status': {
976
896
  const a = z.object({ agentId: z.string() }).parse(args);
977
897
  const result = await client.getRenewalStatus(a.agentId);
978
- return {
979
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
980
- };
898
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
981
899
  }
982
- case "get_service_plan": {
900
+ case 'get_service_plan': {
983
901
  const a = z.object({ agentId: z.string() }).parse(args);
984
902
  const result = await client.getServicePlan(a.agentId);
985
- return {
986
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
987
- };
903
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
988
904
  }
989
- case "purchase_service_plan": {
905
+ case 'purchase_service_plan': {
990
906
  if (!AGENT_PRIVATE_KEY) {
991
907
  return {
992
908
  isError: true,
993
909
  content: [
994
910
  {
995
- type: "text",
996
- text: "AGENT_PRIVATE_KEY env var is required to purchase a Premium Plan.",
911
+ type: 'text',
912
+ text: 'AGENT_PRIVATE_KEY env var is required to purchase a Premium Plan.',
997
913
  },
998
914
  ],
999
915
  };
@@ -1001,31 +917,23 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1001
917
  const a = z
1002
918
  .object({
1003
919
  agentId: z.string(),
1004
- plan: z.enum(["starter", "pro", "enterprise"]),
1005
- planSku: z
1006
- .custom()
1007
- .optional(),
920
+ plan: z.enum(['starter', 'pro', 'enterprise']),
921
+ planSku: z.custom().optional(),
1008
922
  })
1009
923
  .parse(args);
1010
924
  const result = await client.purchaseServicePlan(a);
1011
- return {
1012
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
1013
- };
925
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
1014
926
  }
1015
- case "set_registry_visibility": {
1016
- const a = z
1017
- .object({ agentId: z.string(), registryHidden: z.boolean() })
1018
- .parse(args);
927
+ case 'set_registry_visibility': {
928
+ const a = z.object({ agentId: z.string(), registryHidden: z.boolean() }).parse(args);
1019
929
  const result = await client.setRegistryVisibility(a.agentId, a.registryHidden);
1020
- return {
1021
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
1022
- };
930
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
1023
931
  }
1024
- case "schedule_service_plan_renewal": {
932
+ case 'schedule_service_plan_renewal': {
1025
933
  const a = z
1026
934
  .object({
1027
935
  agentId: z.string().uuid(),
1028
- plan: z.enum(["included", "starter", "pro", "enterprise"]),
936
+ plan: z.enum(['included', 'starter', 'pro', 'enterprise']),
1029
937
  planSku: z.custom(),
1030
938
  })
1031
939
  .parse(args);
@@ -1033,18 +941,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1033
941
  plan: a.plan,
1034
942
  planSku: a.planSku,
1035
943
  });
1036
- return {
1037
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
1038
- };
944
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
1039
945
  }
1040
- case "enable_auto_renew": {
946
+ case 'enable_auto_renew': {
1041
947
  if (!AGENT_PRIVATE_KEY) {
1042
948
  return {
1043
949
  isError: true,
1044
950
  content: [
1045
951
  {
1046
- type: "text",
1047
- text: "AGENT_PRIVATE_KEY env var is required to enable auto-renew. Use the owner wallet private key.",
952
+ type: 'text',
953
+ text: 'AGENT_PRIVATE_KEY env var is required to enable auto-renew. Use the owner wallet private key.',
1048
954
  },
1049
955
  ],
1050
956
  };
@@ -1054,34 +960,28 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1054
960
  isError: true,
1055
961
  content: [
1056
962
  {
1057
- type: "text",
1058
- text: "RENEWAL_VAULT_ADDRESS env var is required to enable auto-renew on-chain.",
963
+ type: 'text',
964
+ text: 'RENEWAL_VAULT_ADDRESS env var is required to enable auto-renew on-chain.',
1059
965
  },
1060
966
  ],
1061
967
  };
1062
968
  }
969
+ requireDirectBaseWriteBuilderCode('enable_auto_renew');
1063
970
  const a = z.object({ agentId: z.string() }).parse(args);
1064
- const result = await client.setAutoRenew(a.agentId, true);
1065
- return {
1066
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
1067
- };
971
+ const result = await client.setAutoRenew(a.agentId, true, { waitForReceipt: true });
972
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
1068
973
  }
1069
974
  default:
1070
975
  return {
1071
976
  isError: true,
1072
- content: [{ type: "text", text: `Unknown tool: ${name}` }],
977
+ content: [{ type: 'text', text: `Unknown tool: ${name}` }],
1073
978
  };
1074
979
  }
1075
980
  }
1076
981
  catch (e) {
1077
982
  return {
1078
983
  isError: true,
1079
- content: [
1080
- {
1081
- type: "text",
1082
- text: `Error: ${e instanceof Error ? e.message : String(e)}`,
1083
- },
1084
- ],
984
+ content: [{ type: 'text', text: `Error: ${e instanceof Error ? e.message : String(e)}` }],
1085
985
  };
1086
986
  }
1087
987
  });
@@ -1090,15 +990,33 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1090
990
  // ----------------------------------------------------------------------
1091
991
  function requireDnsRevision(value) {
1092
992
  if (!value)
1093
- throw new Error("Apply requires baseRevision from a fresh DNS dry-run preview.");
993
+ throw new Error('Apply requires baseRevision from a fresh DNS dry-run preview.');
1094
994
  return value;
1095
995
  }
996
+ function parseWriteToolsEnabled(value) {
997
+ if (value === undefined || value === 'false')
998
+ return false;
999
+ if (value === 'true')
1000
+ return true;
1001
+ throw new Error(`${WRITE_TOOLS_ENV} must be exactly "true" or "false" when set.`);
1002
+ }
1003
+ function requireDirectBaseWriteBuilderCode(toolName) {
1004
+ if (!AGENTDOMAIN_BUILDER_CODE) {
1005
+ throw new Error(`${toolName} requires AGENTDOMAIN_BUILDER_CODE so its direct Base transaction includes ERC-8021 attribution.`);
1006
+ }
1007
+ try {
1008
+ return validateBuilderCode(AGENTDOMAIN_BUILDER_CODE);
1009
+ }
1010
+ catch (error) {
1011
+ throw new Error(`AGENTDOMAIN_BUILDER_CODE is invalid: ${error instanceof Error ? error.message : String(error)}`);
1012
+ }
1013
+ }
1096
1014
  async function main() {
1097
1015
  const transport = new StdioServerTransport();
1098
1016
  await server.connect(transport);
1099
- console.error("AgentDomain MCP server running on stdio");
1017
+ console.error('AgentDomain MCP server running on stdio');
1100
1018
  }
1101
1019
  main().catch((e) => {
1102
- console.error("Fatal:", e);
1020
+ console.error('Fatal:', e);
1103
1021
  process.exit(1);
1104
1022
  });