agentdomain-mcp 0.1.0__tar.gz

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.
@@ -0,0 +1 @@
1
+ __pycache__/
@@ -0,0 +1,84 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentdomain-mcp
3
+ Version: 0.1.0
4
+ Summary: MCP server for AgentDomain — domain registration API for AI agents
5
+ License-Expression: MIT
6
+ Requires-Python: >=3.10
7
+ Requires-Dist: httpx>=0.27.0
8
+ Requires-Dist: mcp>=1.0.0
9
+ Description-Content-Type: text/markdown
10
+
11
+ # AgentDomain MCP Server
12
+
13
+ MCP server for [AgentDomain](https://agentdomain.cloud) — register and manage domain names for AI agents.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ # Via uvx (recommended)
19
+ uvx agentdomain-mcp
20
+
21
+ # Via pip
22
+ pip install agentdomain-mcp
23
+ ```
24
+
25
+ ## Configure
26
+
27
+ Set your API key as an environment variable:
28
+
29
+ ```bash
30
+ export AGENTDOMAIN_API_KEY="da_your_api_key_here"
31
+ ```
32
+
33
+ Get a free API key at [agentdomain.cloud/dashboard](https://agentdomain.cloud/dashboard/).
34
+
35
+ ## Use with Claude Desktop
36
+
37
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
38
+
39
+ ```json
40
+ {
41
+ "mcpServers": {
42
+ "agentdomain": {
43
+ "command": "uvx",
44
+ "args": ["agentdomain-mcp"],
45
+ "env": {
46
+ "AGENTDOMAIN_API_KEY": "da_your_api_key_here"
47
+ }
48
+ }
49
+ }
50
+ }
51
+ ```
52
+
53
+ ## Use with Hermes Agent
54
+
55
+ Add to `~/.hermes/config.yaml`:
56
+
57
+ ```yaml
58
+ mcp_servers:
59
+ agentdomain:
60
+ command: "uvx"
61
+ args: ["agentdomain-mcp"]
62
+ env:
63
+ AGENTDOMAIN_API_KEY: "da_your_api_key_here"
64
+ ```
65
+
66
+ ## Tools
67
+
68
+ | Tool | Description |
69
+ |------|-------------|
70
+ | `domain_search` | Search for available domains |
71
+ | `domain_check` | Check availability + price |
72
+ | `domain_buy` | Register a domain |
73
+ | `domain_list` | List your domains |
74
+ | `domain_dns_get` | Get DNS records |
75
+ | `domain_dns_update` | Update DNS records |
76
+ | `domain_transfer` | Get EPP auth code |
77
+ | `wallet_balance` | Check balance |
78
+ | `wallet_topup` | Create crypto top-up invoice |
79
+ | `wallet_transactions` | List recent transactions |
80
+ | `account_info` | Get account info |
81
+
82
+ ## License
83
+
84
+ MIT
@@ -0,0 +1,74 @@
1
+ # AgentDomain MCP Server
2
+
3
+ MCP server for [AgentDomain](https://agentdomain.cloud) — register and manage domain names for AI agents.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ # Via uvx (recommended)
9
+ uvx agentdomain-mcp
10
+
11
+ # Via pip
12
+ pip install agentdomain-mcp
13
+ ```
14
+
15
+ ## Configure
16
+
17
+ Set your API key as an environment variable:
18
+
19
+ ```bash
20
+ export AGENTDOMAIN_API_KEY="da_your_api_key_here"
21
+ ```
22
+
23
+ Get a free API key at [agentdomain.cloud/dashboard](https://agentdomain.cloud/dashboard/).
24
+
25
+ ## Use with Claude Desktop
26
+
27
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
28
+
29
+ ```json
30
+ {
31
+ "mcpServers": {
32
+ "agentdomain": {
33
+ "command": "uvx",
34
+ "args": ["agentdomain-mcp"],
35
+ "env": {
36
+ "AGENTDOMAIN_API_KEY": "da_your_api_key_here"
37
+ }
38
+ }
39
+ }
40
+ }
41
+ ```
42
+
43
+ ## Use with Hermes Agent
44
+
45
+ Add to `~/.hermes/config.yaml`:
46
+
47
+ ```yaml
48
+ mcp_servers:
49
+ agentdomain:
50
+ command: "uvx"
51
+ args: ["agentdomain-mcp"]
52
+ env:
53
+ AGENTDOMAIN_API_KEY: "da_your_api_key_here"
54
+ ```
55
+
56
+ ## Tools
57
+
58
+ | Tool | Description |
59
+ |------|-------------|
60
+ | `domain_search` | Search for available domains |
61
+ | `domain_check` | Check availability + price |
62
+ | `domain_buy` | Register a domain |
63
+ | `domain_list` | List your domains |
64
+ | `domain_dns_get` | Get DNS records |
65
+ | `domain_dns_update` | Update DNS records |
66
+ | `domain_transfer` | Get EPP auth code |
67
+ | `wallet_balance` | Check balance |
68
+ | `wallet_topup` | Create crypto top-up invoice |
69
+ | `wallet_transactions` | List recent transactions |
70
+ | `account_info` | Get account info |
71
+
72
+ ## License
73
+
74
+ MIT
@@ -0,0 +1,21 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "agentdomain-mcp"
7
+ version = "0.1.0"
8
+ description = "MCP server for AgentDomain — domain registration API for AI agents"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ dependencies = [
13
+ "mcp>=1.0.0",
14
+ "httpx>=0.27.0",
15
+ ]
16
+
17
+ [project.scripts]
18
+ agentdomain-mcp = "server:mcp.run"
19
+
20
+ [tool.hatch.build.targets.wheel]
21
+ packages = ["."]
@@ -0,0 +1,292 @@
1
+ """AgentDomain MCP Server — Domain registration tools for AI agents."""
2
+
3
+ import os
4
+ import httpx
5
+ from mcp.server.fastmcp import FastMCP
6
+
7
+ API_BASE = os.environ.get("AGENTDOMAIN_API_URL", "https://api.agentdomain.cloud")
8
+ API_KEY = os.environ.get("AGENTDOMAIN_API_KEY", "")
9
+
10
+ mcp = FastMCP(
11
+ "AgentDomain",
12
+ instructions="Domain registration API for AI agents. Search, register, and manage domains autonomously.",
13
+ )
14
+
15
+
16
+ def _headers():
17
+ return {
18
+ "Authorization": f"Bearer {API_KEY}",
19
+ "Content-Type": "application/json",
20
+ }
21
+
22
+
23
+ async def _get(path: str) -> dict:
24
+ async with httpx.AsyncClient(timeout=30) as c:
25
+ r = await c.get(f"{API_BASE}{path}", headers=_headers())
26
+ if r.status_code >= 400:
27
+ try:
28
+ detail = r.json().get("detail", r.text)
29
+ except Exception:
30
+ detail = r.text
31
+ return {"error": f"API error {r.status_code}: {detail}"}
32
+ return r.json()
33
+
34
+
35
+ async def _post(path: str, data: dict = None) -> dict:
36
+ async with httpx.AsyncClient(timeout=30) as c:
37
+ r = await c.post(f"{API_BASE}{path}", headers=_headers(), json=data or {})
38
+ if r.status_code >= 400:
39
+ try:
40
+ detail = r.json().get("detail", r.text)
41
+ except Exception:
42
+ detail = r.text
43
+ return {"error": f"API error {r.status_code}: {detail}"}
44
+ return r.json()
45
+
46
+
47
+ # ── Domains ──
48
+
49
+
50
+ @mcp.tool()
51
+ async def domain_search(query: str) -> str:
52
+ """Search for available domain names matching a query.
53
+
54
+ Args:
55
+ query: Search term (e.g. 'awesome', 'mybrand')
56
+
57
+ Returns:
58
+ List of domains with availability and pricing.
59
+ """
60
+ data = await _get(f"/v1/domains/search?q={query}")
61
+ if "error" in data:
62
+ return f"Error: {data['error']}"
63
+ domains = data.get("domains", [])
64
+ if not domains:
65
+ return "No domains found."
66
+ lines = []
67
+ for d in domains:
68
+ status = "✅ available" if d.get("available") else "❌ taken"
69
+ price = f"${d['price']:.2f}" if d.get("price") else ""
70
+ lines.append(f" {d['name']} {status} {price}")
71
+ return "Search results:\n" + "\n".join(lines)
72
+
73
+
74
+ @mcp.tool()
75
+ async def domain_check(domain: str) -> str:
76
+ """Check if a specific domain is available for registration and get its price.
77
+
78
+ Args:
79
+ domain: Full domain name (e.g. 'awesome.com')
80
+
81
+ Returns:
82
+ Availability status and price.
83
+ """
84
+ data = await _post("/v1/domains/check", {"domain": domain})
85
+ if "error" in data:
86
+ return f"Error: {data['error']}"
87
+ if data.get("registrable"):
88
+ price = data.get("price_cents", 0) / 100
89
+ return f"✅ {domain} is available — ${price:.2f}/yr"
90
+ else:
91
+ reason = data.get("reason", "already taken")
92
+ return f"❌ {domain} is not available ({reason})"
93
+
94
+
95
+ @mcp.tool()
96
+ async def domain_buy(domain: str, years: int = 1) -> str:
97
+ """Register/buy a domain name.
98
+
99
+ Args:
100
+ domain: Full domain name to register (e.g. 'awesome.com')
101
+ years: Registration period in years (default: 1)
102
+
103
+ Returns:
104
+ Registration result with status and next steps.
105
+ """
106
+ data = await _post("/v1/domains/buy", {"domain": domain, "years": years})
107
+ if "error" in data:
108
+ return f"Error: {data['error']}"
109
+ status = data.get("status", "unknown")
110
+
111
+ if status == "active":
112
+ cost = data.get("cost_cents", 0) / 100
113
+ return f"✅ {domain} registered successfully! Cost: ${cost:.2f}"
114
+ elif status == "payment_required":
115
+ url = data.get("payment_url", "")
116
+ cost = data.get("cost_cents", 0) / 100
117
+ return f"💳 Payment needed (${cost:.2f}): {url}"
118
+ elif status == "approval_needed":
119
+ url = data.get("approval_url", "")
120
+ cost = data.get("cost_cents", 0) / 100
121
+ return f"⏳ Approval needed (${cost:.2f}): {url}"
122
+ elif status == "email_not_verified":
123
+ return "⚠️ Email not verified. Please verify your email first."
124
+ else:
125
+ msg = data.get("message", str(data))
126
+ return f"Result: {msg}"
127
+
128
+
129
+ @mcp.tool()
130
+ async def domain_list() -> str:
131
+ """List all domains registered to your account.
132
+
133
+ Returns:
134
+ List of domains with status, expiry, and cost.
135
+ """
136
+ data = await _get("/v1/domains")
137
+ if "error" in data:
138
+ return f"Error: {data['error']}"
139
+ domains = data.get("domains", [])
140
+ if not domains:
141
+ return "No domains registered yet."
142
+ lines = []
143
+ for d in domains:
144
+ expires = d.get("expires_at", "—")[:10] if d.get("expires_at") else "—"
145
+ cost = f"${d['cost_cents'] / 100:.2f}" if d.get("cost_cents") else "—"
146
+ lines.append(f" {d['domain']} [{d['status']}] expires: {expires} cost: {cost}")
147
+ return "Your domains:\n" + "\n".join(lines)
148
+
149
+
150
+ @mcp.tool()
151
+ async def domain_dns_get(domain: str) -> str:
152
+ """Get DNS records for a domain.
153
+
154
+ Args:
155
+ domain: The domain to query (e.g. 'awesome.com')
156
+
157
+ Returns:
158
+ List of DNS records.
159
+ """
160
+ data = await _get(f"/v1/domains/{domain}/dns")
161
+ if "error" in data:
162
+ return f"Error: {data['error']}"
163
+ records = data.get("records", [])
164
+ if not records:
165
+ return f"No DNS records for {domain}."
166
+ lines = [f"DNS records for {domain}:"]
167
+ for r in records:
168
+ lines.append(f" {r.get('type', '?')} {r.get('name', '@')} {r.get('content', '')} TTL:{r.get('ttl', 300)}")
169
+ return "\n".join(lines)
170
+
171
+
172
+ @mcp.tool()
173
+ async def domain_dns_update(domain: str, records_json: str) -> str:
174
+ """Update DNS records for a domain. Replaces ALL records with the provided set.
175
+
176
+ Args:
177
+ domain: The domain to update (e.g. 'awesome.com')
178
+ records_json: JSON array of DNS records. Each record has: type (A/CNAME/MX/TXT), name, content, ttl.
179
+ Example: [{"type": "A", "name": "@", "content": "1.2.3.4", "ttl": 300}]
180
+
181
+ Returns:
182
+ Confirmation of the update.
183
+ """
184
+ import json
185
+ records = json.loads(records_json)
186
+ data = await _post(f"/v1/domains/{domain}/dns", {"records": records})
187
+ if "error" in data:
188
+ return f"Error: {data['error']}"
189
+ return f"✅ DNS records updated for {domain}. {len(records)} record(s) applied."
190
+
191
+
192
+ @mcp.tool()
193
+ async def domain_transfer(domain: str) -> str:
194
+ """Get the EPP auth code to transfer a domain out.
195
+
196
+ Args:
197
+ domain: The domain to transfer (e.g. 'awesome.com')
198
+
199
+ Returns:
200
+ EPP authorization code and transfer instructions.
201
+ """
202
+ data = await _post(f"/v1/domains/{domain}/transfer")
203
+ if "error" in data:
204
+ return f"Error: {data['error']}"
205
+ code = data.get("epp_code", "")
206
+ msg = data.get("message", "")
207
+ return f"EPP auth code for {domain}: {code}\n\n{msg}"
208
+
209
+
210
+ # ── Wallet ──
211
+
212
+
213
+ @mcp.tool()
214
+ async def wallet_balance() -> str:
215
+ """Check your wallet balance and total spending.
216
+
217
+ Returns:
218
+ Current balance and total spent.
219
+ """
220
+ data = await _get("/v1/wallet")
221
+ if "error" in data:
222
+ return f"Error: {data['error']}"
223
+ balance = data.get("balance_cents", 0) / 100
224
+ spent = data.get("total_spent_cents", 0) / 100
225
+ return f"Balance: ${balance:.2f}\nTotal spent: ${spent:.2f}"
226
+
227
+
228
+ @mcp.tool()
229
+ async def wallet_topup(amount_usd: float, currency: str = "USDC") -> str:
230
+ """Create a crypto top-up invoice for your wallet.
231
+
232
+ Args:
233
+ amount_usd: Amount in USD to add
234
+ currency: Crypto currency (USDC, BTC, ETH). Default: USDC.
235
+
236
+ Returns:
237
+ Payment invoice URL.
238
+ """
239
+ amount_cents = int(amount_usd * 100)
240
+ data = await _post("/v1/wallet/topup/crypto", {
241
+ "amount_cents": amount_cents,
242
+ "crypto_currency": currency,
243
+ })
244
+ if "error" in data:
245
+ return f"Error: {data['error']}"
246
+ url = data.get("invoice_url", "")
247
+ return f"💳 Top-up invoice (${amount_usd:.2f} via {currency}): {url}"
248
+
249
+
250
+ @mcp.tool()
251
+ async def wallet_transactions(limit: int = 10) -> str:
252
+ """List recent wallet transactions.
253
+
254
+ Args:
255
+ limit: Max number of transactions to return (default: 10)
256
+
257
+ Returns:
258
+ Recent transactions with type, amount, and status.
259
+ """
260
+ data = await _get("/v1/wallet/transactions")
261
+ if "error" in data:
262
+ return f"Error: {data['error']}"
263
+ txs = data.get("transactions", [])[:limit]
264
+ if not txs:
265
+ return "No transactions yet."
266
+ lines = ["Recent transactions:"]
267
+ for t in txs:
268
+ amount = f"${t['amount_cents'] / 100:.2f}" if t.get("amount_cents") is not None else "—"
269
+ domain = t.get("domain", "")
270
+ date = t.get("created_at", "")[:10]
271
+ lines.append(f" {t.get('type', '?')} {amount} {domain} [{t.get('status', '?')}] {date}")
272
+ return "\n".join(lines)
273
+
274
+
275
+ # ── Account ──
276
+
277
+
278
+ @mcp.tool()
279
+ async def account_info() -> str:
280
+ """Get your account information.
281
+
282
+ Returns:
283
+ Agent name, email, and account ID.
284
+ """
285
+ data = await _get("/v1/auth/me")
286
+ if "error" in data:
287
+ return f"Error: {data['error']}"
288
+ return f"Name: {data.get('name', '—')}\nEmail: {data.get('email', '—')}\nAgent ID: {data.get('id', '—')}"
289
+
290
+
291
+ if __name__ == "__main__":
292
+ mcp.run(transport="stdio")