porkbun-mcp 0.1.1__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.
- porkbun_mcp-0.1.1/PKG-INFO +220 -0
- porkbun_mcp-0.1.1/README.md +197 -0
- porkbun_mcp-0.1.1/pyproject.toml +107 -0
- porkbun_mcp-0.1.1/src/porkbun_mcp/__init__.py +37 -0
- porkbun_mcp-0.1.1/src/porkbun_mcp/config.py +24 -0
- porkbun_mcp-0.1.1/src/porkbun_mcp/context.py +20 -0
- porkbun_mcp-0.1.1/src/porkbun_mcp/errors.py +45 -0
- porkbun_mcp-0.1.1/src/porkbun_mcp/models.py +130 -0
- porkbun_mcp-0.1.1/src/porkbun_mcp/prompts/__init__.py +19 -0
- porkbun_mcp-0.1.1/src/porkbun_mcp/prompts/dns.py +117 -0
- porkbun_mcp-0.1.1/src/porkbun_mcp/resources/__init__.py +25 -0
- porkbun_mcp-0.1.1/src/porkbun_mcp/resources/dns.py +28 -0
- porkbun_mcp-0.1.1/src/porkbun_mcp/resources/domains.py +28 -0
- porkbun_mcp-0.1.1/src/porkbun_mcp/resources/pricing.py +26 -0
- porkbun_mcp-0.1.1/src/porkbun_mcp/resources/ssl.py +28 -0
- porkbun_mcp-0.1.1/src/porkbun_mcp/server.py +55 -0
- porkbun_mcp-0.1.1/src/porkbun_mcp/tools/__init__.py +25 -0
- porkbun_mcp-0.1.1/src/porkbun_mcp/tools/dns.py +235 -0
- porkbun_mcp-0.1.1/src/porkbun_mcp/tools/dnssec.py +88 -0
- porkbun_mcp-0.1.1/src/porkbun_mcp/tools/domains.py +249 -0
- porkbun_mcp-0.1.1/src/porkbun_mcp/tools/ping.py +27 -0
- porkbun_mcp-0.1.1/src/porkbun_mcp/tools/pricing.py +35 -0
- porkbun_mcp-0.1.1/src/porkbun_mcp/tools/ssl.py +43 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: porkbun-mcp
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: MCP server for Porkbun DNS API
|
|
5
|
+
Keywords: mcp,porkbun,dns,model-context-protocol
|
|
6
|
+
Author: Major Hayden
|
|
7
|
+
Author-email: Major Hayden <major@mhtx.net>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Classifier: Topic :: Internet :: Name Service (DNS)
|
|
15
|
+
Classifier: Typing :: Typed
|
|
16
|
+
Requires-Dist: fastmcp>=2.14.3
|
|
17
|
+
Requires-Dist: oinker>=0.1.1
|
|
18
|
+
Requires-Dist: pydantic-settings>=2.0.0
|
|
19
|
+
Requires-Python: >=3.13
|
|
20
|
+
Project-URL: Homepage, https://github.com/major/porkbun-mcp
|
|
21
|
+
Project-URL: Repository, https://github.com/major/porkbun-mcp
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# porkbun-mcp
|
|
25
|
+
|
|
26
|
+
MCP server for the [Porkbun](https://porkbun.com/) DNS API.
|
|
27
|
+
|
|
28
|
+
Manage DNS records, domains, DNSSEC, SSL certificates, and more via the Model Context Protocol.
|
|
29
|
+
|
|
30
|
+
[](https://github.com/major/porkbun-mcp/actions/workflows/ci.yml)
|
|
31
|
+
[](https://codecov.io/gh/major/porkbun-mcp)
|
|
32
|
+
[](https://pypi.org/project/porkbun-mcp/)
|
|
33
|
+
[](https://www.python.org/downloads/)
|
|
34
|
+
[](https://opensource.org/licenses/MIT)
|
|
35
|
+
|
|
36
|
+
## Configuration
|
|
37
|
+
|
|
38
|
+
Set your Porkbun API credentials as environment variables:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
export PORKBUN_API_KEY="pk1_..."
|
|
42
|
+
export PORKBUN_SECRET_KEY="sk1_..."
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Get your API keys from the [Porkbun API Access page](https://porkbun.com/account/api).
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
Run directly with [uvx](https://docs.astral.sh/uv/) (no installation required):
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
uvx porkbun-mcp
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### SSE transport
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
uvx porkbun-mcp --transport sse
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## MCP Client Configuration
|
|
62
|
+
|
|
63
|
+
### Claude Desktop
|
|
64
|
+
|
|
65
|
+
Add to `~/.config/claude/claude_desktop_config.json`:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"mcpServers": {
|
|
70
|
+
"porkbun": {
|
|
71
|
+
"command": "uvx",
|
|
72
|
+
"args": ["porkbun-mcp"],
|
|
73
|
+
"env": {
|
|
74
|
+
"PORKBUN_API_KEY": "pk1_...",
|
|
75
|
+
"PORKBUN_SECRET_KEY": "sk1_..."
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Claude Code / Codex
|
|
83
|
+
|
|
84
|
+
Add to `~/.claude/settings.json`:
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"mcpServers": {
|
|
89
|
+
"porkbun": {
|
|
90
|
+
"command": "uvx",
|
|
91
|
+
"args": ["porkbun-mcp"],
|
|
92
|
+
"env": {
|
|
93
|
+
"PORKBUN_API_KEY": "pk1_...",
|
|
94
|
+
"PORKBUN_SECRET_KEY": "sk1_..."
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### VS Code
|
|
102
|
+
|
|
103
|
+
Add to `.vscode/mcp.json` in your workspace (or use `MCP: Add Server` command):
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"servers": {
|
|
108
|
+
"porkbun": {
|
|
109
|
+
"command": "uvx",
|
|
110
|
+
"args": ["porkbun-mcp"],
|
|
111
|
+
"env": {
|
|
112
|
+
"PORKBUN_API_KEY": "pk1_...",
|
|
113
|
+
"PORKBUN_SECRET_KEY": "sk1_..."
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### OpenCode
|
|
121
|
+
|
|
122
|
+
Add to your `opencode.json` configuration:
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"mcp": {
|
|
127
|
+
"porkbun": {
|
|
128
|
+
"type": "local",
|
|
129
|
+
"command": ["uvx", "porkbun-mcp"],
|
|
130
|
+
"environment": {
|
|
131
|
+
"PORKBUN_API_KEY": "pk1_...",
|
|
132
|
+
"PORKBUN_SECRET_KEY": "sk1_..."
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Available Tools
|
|
140
|
+
|
|
141
|
+
### DNS
|
|
142
|
+
|
|
143
|
+
- `dns_list` - List all DNS records for a domain
|
|
144
|
+
- `dns_get` - Get a specific DNS record by ID
|
|
145
|
+
- `dns_get_by_name_type` - Get DNS records by subdomain and type
|
|
146
|
+
- `dns_create` - Create a new DNS record
|
|
147
|
+
- `dns_edit` - Edit a DNS record by ID
|
|
148
|
+
- `dns_edit_by_name_type` - Edit DNS records by subdomain and type
|
|
149
|
+
- `dns_delete` - Delete a DNS record by ID
|
|
150
|
+
- `dns_delete_by_name_type` - Delete DNS records by subdomain and type
|
|
151
|
+
|
|
152
|
+
### Domains
|
|
153
|
+
|
|
154
|
+
- `domains_list` - List all domains in your account
|
|
155
|
+
- `domains_get_nameservers` - Get nameservers for a domain
|
|
156
|
+
- `domains_update_nameservers` - Update nameservers for a domain
|
|
157
|
+
- `domains_get_url_forwards` - Get URL forwarding rules
|
|
158
|
+
- `domains_add_url_forward` - Add a URL forwarding rule
|
|
159
|
+
- `domains_delete_url_forward` - Delete a URL forwarding rule
|
|
160
|
+
- `domains_check_availability` - Check domain availability and pricing
|
|
161
|
+
- `domains_get_glue_records` - Get glue records for a domain
|
|
162
|
+
|
|
163
|
+
### DNSSEC
|
|
164
|
+
|
|
165
|
+
- `dnssec_list` - List DNSSEC records for a domain
|
|
166
|
+
- `dnssec_create` - Create a DNSSEC record
|
|
167
|
+
- `dnssec_delete` - Delete a DNSSEC record
|
|
168
|
+
|
|
169
|
+
### SSL
|
|
170
|
+
|
|
171
|
+
- `ssl_retrieve` - Retrieve the SSL certificate bundle for a domain
|
|
172
|
+
|
|
173
|
+
### Pricing
|
|
174
|
+
|
|
175
|
+
- `pricing_get` - Get pricing for all available TLDs
|
|
176
|
+
|
|
177
|
+
### Utility
|
|
178
|
+
|
|
179
|
+
- `ping` - Test API connectivity and get your public IP
|
|
180
|
+
|
|
181
|
+
## Resources
|
|
182
|
+
|
|
183
|
+
Browse data via MCP resources:
|
|
184
|
+
|
|
185
|
+
- `porkbun://domains` - List all domains
|
|
186
|
+
- `porkbun://dns/{domain}` - DNS records for a domain
|
|
187
|
+
- `porkbun://ssl/{domain}` - SSL certificate bundle for a domain
|
|
188
|
+
- `porkbun://pricing` - TLD pricing information
|
|
189
|
+
|
|
190
|
+
## Prompts
|
|
191
|
+
|
|
192
|
+
Pre-defined workflows to guide common DNS operations:
|
|
193
|
+
|
|
194
|
+
- `dns_setup` - Set up basic DNS for a new server (root A + www records)
|
|
195
|
+
- `dns_audit` - Audit DNS configuration for issues (duplicates, missing email records, low TTLs)
|
|
196
|
+
- `email_dns_setup` - Configure email DNS (MX, SPF, DKIM, DMARC)
|
|
197
|
+
- `update_server_ip` - Update DNS records when migrating to a new server IP
|
|
198
|
+
- `subdomain_setup` - Create A/CNAME records for a new subdomain
|
|
199
|
+
|
|
200
|
+
## Development
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
# Install dependencies
|
|
204
|
+
uv sync --dev
|
|
205
|
+
|
|
206
|
+
# Run all checks
|
|
207
|
+
make check
|
|
208
|
+
|
|
209
|
+
# Individual commands
|
|
210
|
+
make lint # ruff check
|
|
211
|
+
make format # ruff format
|
|
212
|
+
make typecheck # ty check
|
|
213
|
+
make test # pytest with coverage
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
MIT
|
|
219
|
+
|
|
220
|
+
<!-- mcp-name: io.github.major/porkbun -->
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# porkbun-mcp
|
|
2
|
+
|
|
3
|
+
MCP server for the [Porkbun](https://porkbun.com/) DNS API.
|
|
4
|
+
|
|
5
|
+
Manage DNS records, domains, DNSSEC, SSL certificates, and more via the Model Context Protocol.
|
|
6
|
+
|
|
7
|
+
[](https://github.com/major/porkbun-mcp/actions/workflows/ci.yml)
|
|
8
|
+
[](https://codecov.io/gh/major/porkbun-mcp)
|
|
9
|
+
[](https://pypi.org/project/porkbun-mcp/)
|
|
10
|
+
[](https://www.python.org/downloads/)
|
|
11
|
+
[](https://opensource.org/licenses/MIT)
|
|
12
|
+
|
|
13
|
+
## Configuration
|
|
14
|
+
|
|
15
|
+
Set your Porkbun API credentials as environment variables:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
export PORKBUN_API_KEY="pk1_..."
|
|
19
|
+
export PORKBUN_SECRET_KEY="sk1_..."
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Get your API keys from the [Porkbun API Access page](https://porkbun.com/account/api).
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
Run directly with [uvx](https://docs.astral.sh/uv/) (no installation required):
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
uvx porkbun-mcp
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### SSE transport
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
uvx porkbun-mcp --transport sse
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## MCP Client Configuration
|
|
39
|
+
|
|
40
|
+
### Claude Desktop
|
|
41
|
+
|
|
42
|
+
Add to `~/.config/claude/claude_desktop_config.json`:
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"mcpServers": {
|
|
47
|
+
"porkbun": {
|
|
48
|
+
"command": "uvx",
|
|
49
|
+
"args": ["porkbun-mcp"],
|
|
50
|
+
"env": {
|
|
51
|
+
"PORKBUN_API_KEY": "pk1_...",
|
|
52
|
+
"PORKBUN_SECRET_KEY": "sk1_..."
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Claude Code / Codex
|
|
60
|
+
|
|
61
|
+
Add to `~/.claude/settings.json`:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"mcpServers": {
|
|
66
|
+
"porkbun": {
|
|
67
|
+
"command": "uvx",
|
|
68
|
+
"args": ["porkbun-mcp"],
|
|
69
|
+
"env": {
|
|
70
|
+
"PORKBUN_API_KEY": "pk1_...",
|
|
71
|
+
"PORKBUN_SECRET_KEY": "sk1_..."
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### VS Code
|
|
79
|
+
|
|
80
|
+
Add to `.vscode/mcp.json` in your workspace (or use `MCP: Add Server` command):
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"servers": {
|
|
85
|
+
"porkbun": {
|
|
86
|
+
"command": "uvx",
|
|
87
|
+
"args": ["porkbun-mcp"],
|
|
88
|
+
"env": {
|
|
89
|
+
"PORKBUN_API_KEY": "pk1_...",
|
|
90
|
+
"PORKBUN_SECRET_KEY": "sk1_..."
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### OpenCode
|
|
98
|
+
|
|
99
|
+
Add to your `opencode.json` configuration:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"mcp": {
|
|
104
|
+
"porkbun": {
|
|
105
|
+
"type": "local",
|
|
106
|
+
"command": ["uvx", "porkbun-mcp"],
|
|
107
|
+
"environment": {
|
|
108
|
+
"PORKBUN_API_KEY": "pk1_...",
|
|
109
|
+
"PORKBUN_SECRET_KEY": "sk1_..."
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Available Tools
|
|
117
|
+
|
|
118
|
+
### DNS
|
|
119
|
+
|
|
120
|
+
- `dns_list` - List all DNS records for a domain
|
|
121
|
+
- `dns_get` - Get a specific DNS record by ID
|
|
122
|
+
- `dns_get_by_name_type` - Get DNS records by subdomain and type
|
|
123
|
+
- `dns_create` - Create a new DNS record
|
|
124
|
+
- `dns_edit` - Edit a DNS record by ID
|
|
125
|
+
- `dns_edit_by_name_type` - Edit DNS records by subdomain and type
|
|
126
|
+
- `dns_delete` - Delete a DNS record by ID
|
|
127
|
+
- `dns_delete_by_name_type` - Delete DNS records by subdomain and type
|
|
128
|
+
|
|
129
|
+
### Domains
|
|
130
|
+
|
|
131
|
+
- `domains_list` - List all domains in your account
|
|
132
|
+
- `domains_get_nameservers` - Get nameservers for a domain
|
|
133
|
+
- `domains_update_nameservers` - Update nameservers for a domain
|
|
134
|
+
- `domains_get_url_forwards` - Get URL forwarding rules
|
|
135
|
+
- `domains_add_url_forward` - Add a URL forwarding rule
|
|
136
|
+
- `domains_delete_url_forward` - Delete a URL forwarding rule
|
|
137
|
+
- `domains_check_availability` - Check domain availability and pricing
|
|
138
|
+
- `domains_get_glue_records` - Get glue records for a domain
|
|
139
|
+
|
|
140
|
+
### DNSSEC
|
|
141
|
+
|
|
142
|
+
- `dnssec_list` - List DNSSEC records for a domain
|
|
143
|
+
- `dnssec_create` - Create a DNSSEC record
|
|
144
|
+
- `dnssec_delete` - Delete a DNSSEC record
|
|
145
|
+
|
|
146
|
+
### SSL
|
|
147
|
+
|
|
148
|
+
- `ssl_retrieve` - Retrieve the SSL certificate bundle for a domain
|
|
149
|
+
|
|
150
|
+
### Pricing
|
|
151
|
+
|
|
152
|
+
- `pricing_get` - Get pricing for all available TLDs
|
|
153
|
+
|
|
154
|
+
### Utility
|
|
155
|
+
|
|
156
|
+
- `ping` - Test API connectivity and get your public IP
|
|
157
|
+
|
|
158
|
+
## Resources
|
|
159
|
+
|
|
160
|
+
Browse data via MCP resources:
|
|
161
|
+
|
|
162
|
+
- `porkbun://domains` - List all domains
|
|
163
|
+
- `porkbun://dns/{domain}` - DNS records for a domain
|
|
164
|
+
- `porkbun://ssl/{domain}` - SSL certificate bundle for a domain
|
|
165
|
+
- `porkbun://pricing` - TLD pricing information
|
|
166
|
+
|
|
167
|
+
## Prompts
|
|
168
|
+
|
|
169
|
+
Pre-defined workflows to guide common DNS operations:
|
|
170
|
+
|
|
171
|
+
- `dns_setup` - Set up basic DNS for a new server (root A + www records)
|
|
172
|
+
- `dns_audit` - Audit DNS configuration for issues (duplicates, missing email records, low TTLs)
|
|
173
|
+
- `email_dns_setup` - Configure email DNS (MX, SPF, DKIM, DMARC)
|
|
174
|
+
- `update_server_ip` - Update DNS records when migrating to a new server IP
|
|
175
|
+
- `subdomain_setup` - Create A/CNAME records for a new subdomain
|
|
176
|
+
|
|
177
|
+
## Development
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
# Install dependencies
|
|
181
|
+
uv sync --dev
|
|
182
|
+
|
|
183
|
+
# Run all checks
|
|
184
|
+
make check
|
|
185
|
+
|
|
186
|
+
# Individual commands
|
|
187
|
+
make lint # ruff check
|
|
188
|
+
make format # ruff format
|
|
189
|
+
make typecheck # ty check
|
|
190
|
+
make test # pytest with coverage
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## License
|
|
194
|
+
|
|
195
|
+
MIT
|
|
196
|
+
|
|
197
|
+
<!-- mcp-name: io.github.major/porkbun -->
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "porkbun-mcp"
|
|
3
|
+
version = "0.1.1"
|
|
4
|
+
description = "MCP server for Porkbun DNS API"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
authors = [{ name = "Major Hayden", email = "major@mhtx.net" }]
|
|
8
|
+
requires-python = ">=3.13"
|
|
9
|
+
keywords = ["mcp", "porkbun", "dns", "model-context-protocol"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 3 - Alpha",
|
|
12
|
+
"Intended Audience :: Developers",
|
|
13
|
+
"License :: OSI Approved :: MIT License",
|
|
14
|
+
"Programming Language :: Python :: 3.13",
|
|
15
|
+
"Programming Language :: Python :: 3.14",
|
|
16
|
+
"Topic :: Internet :: Name Service (DNS)",
|
|
17
|
+
"Typing :: Typed",
|
|
18
|
+
]
|
|
19
|
+
dependencies = ["fastmcp>=2.14.3", "oinker>=0.1.1", "pydantic-settings>=2.0.0"]
|
|
20
|
+
|
|
21
|
+
[project.urls]
|
|
22
|
+
Homepage = "https://github.com/major/porkbun-mcp"
|
|
23
|
+
Repository = "https://github.com/major/porkbun-mcp"
|
|
24
|
+
|
|
25
|
+
[project.scripts]
|
|
26
|
+
porkbun-mcp = "porkbun_mcp:main"
|
|
27
|
+
|
|
28
|
+
[build-system]
|
|
29
|
+
requires = ["uv_build>=0.9.18"]
|
|
30
|
+
build-backend = "uv_build"
|
|
31
|
+
|
|
32
|
+
[dependency-groups]
|
|
33
|
+
dev = [
|
|
34
|
+
"pytest>=8",
|
|
35
|
+
"pytest-cov>=6",
|
|
36
|
+
"pytest-asyncio>=0.25",
|
|
37
|
+
"respx>=0.22",
|
|
38
|
+
"ty>=0.0.12",
|
|
39
|
+
"ruff>=0.9",
|
|
40
|
+
"radon>=6",
|
|
41
|
+
"mkdocs>=1.6",
|
|
42
|
+
"mkdocs-material>=9",
|
|
43
|
+
"mkdocstrings[python]>=0.29",
|
|
44
|
+
"pytest-randomly>=4.0.1",
|
|
45
|
+
"commitizen>=4.0",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[tool.uv.build-backend]
|
|
49
|
+
module-name = "porkbun_mcp"
|
|
50
|
+
|
|
51
|
+
[tool.ruff]
|
|
52
|
+
target-version = "py313"
|
|
53
|
+
line-length = 100
|
|
54
|
+
|
|
55
|
+
[tool.ruff.lint]
|
|
56
|
+
select = ["E", "F", "I", "UP", "B", "SIM", "ASYNC", "D"]
|
|
57
|
+
ignore = [
|
|
58
|
+
"D100", # Missing docstring in public module
|
|
59
|
+
"D104", # Missing docstring in public package
|
|
60
|
+
"D105", # Missing docstring in magic method
|
|
61
|
+
"D107", # Missing docstring in __init__
|
|
62
|
+
"UP037", # Quoted type annotations - needed for FastMCP/Pydantic compatibility
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
[tool.ruff.lint.per-file-ignores]
|
|
66
|
+
"tests/**/*.py" = ["D"]
|
|
67
|
+
|
|
68
|
+
[tool.ruff.lint.pydocstyle]
|
|
69
|
+
convention = "google"
|
|
70
|
+
|
|
71
|
+
[tool.pytest.ini_options]
|
|
72
|
+
asyncio_mode = "auto"
|
|
73
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
74
|
+
testpaths = ["tests"]
|
|
75
|
+
addopts = "--cov=porkbun_mcp --cov-report=term-missing"
|
|
76
|
+
|
|
77
|
+
[tool.coverage.run]
|
|
78
|
+
source = ["src/porkbun_mcp"]
|
|
79
|
+
branch = true
|
|
80
|
+
|
|
81
|
+
[tool.coverage.report]
|
|
82
|
+
exclude_lines = [
|
|
83
|
+
"pragma: no cover",
|
|
84
|
+
"if TYPE_CHECKING:",
|
|
85
|
+
"raise NotImplementedError",
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
[tool.ty.environment]
|
|
89
|
+
python-version = "3.14"
|
|
90
|
+
python-platform = "linux"
|
|
91
|
+
extra-paths = ["src"]
|
|
92
|
+
|
|
93
|
+
[tool.pyright]
|
|
94
|
+
pythonVersion = "3.14"
|
|
95
|
+
pythonPlatform = "Linux"
|
|
96
|
+
venvPath = "."
|
|
97
|
+
venv = ".venv"
|
|
98
|
+
typeCheckingMode = "standard"
|
|
99
|
+
|
|
100
|
+
[tool.commitizen]
|
|
101
|
+
name = "cz_conventional_commits"
|
|
102
|
+
version_provider = "pep621"
|
|
103
|
+
tag_format = "v$version"
|
|
104
|
+
update_changelog_on_bump = true
|
|
105
|
+
changelog_file = "CHANGELOG.md"
|
|
106
|
+
changelog_incremental = true
|
|
107
|
+
bump_message = "bump: version $current_version -> $new_version"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Porkbun MCP Server - DNS management via Model Context Protocol."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main() -> None:
|
|
9
|
+
"""Run the Porkbun MCP server."""
|
|
10
|
+
parser = argparse.ArgumentParser(
|
|
11
|
+
description="Porkbun DNS MCP Server",
|
|
12
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
13
|
+
epilog="""
|
|
14
|
+
Environment Variables:
|
|
15
|
+
PORKBUN_API_KEY Porkbun API key (required)
|
|
16
|
+
PORKBUN_SECRET_KEY Porkbun secret key (required)
|
|
17
|
+
|
|
18
|
+
Examples:
|
|
19
|
+
porkbun-mcp
|
|
20
|
+
porkbun-mcp --transport sse
|
|
21
|
+
""",
|
|
22
|
+
)
|
|
23
|
+
parser.add_argument(
|
|
24
|
+
"--transport",
|
|
25
|
+
choices=["stdio", "sse", "streamable-http"],
|
|
26
|
+
default="stdio",
|
|
27
|
+
help="Transport protocol (default: stdio)",
|
|
28
|
+
)
|
|
29
|
+
args = parser.parse_args()
|
|
30
|
+
|
|
31
|
+
from porkbun_mcp.server import create_server
|
|
32
|
+
|
|
33
|
+
server = create_server()
|
|
34
|
+
server.run(transport=args.transport)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
__all__ = ["main"]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Configuration for the Porkbun MCP server."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pydantic import Field
|
|
6
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PorkbunMCPSettings(BaseSettings):
|
|
10
|
+
"""Configuration for the Porkbun MCP server.
|
|
11
|
+
|
|
12
|
+
Attributes:
|
|
13
|
+
api_key: Porkbun API key (pk1_...).
|
|
14
|
+
secret_key: Porkbun secret API key (sk1_...).
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
model_config = SettingsConfigDict(
|
|
18
|
+
env_file=".env",
|
|
19
|
+
env_prefix="PORKBUN_",
|
|
20
|
+
extra="ignore",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
api_key: str = Field(default="", description="Porkbun API key")
|
|
24
|
+
secret_key: str = Field(default="", description="Porkbun secret key")
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Context helpers for safe lifespan context access."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from fastmcp import Context
|
|
8
|
+
from fastmcp.exceptions import ToolError
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from oinker import AsyncPiglet
|
|
12
|
+
|
|
13
|
+
from porkbun_mcp.server import AppContext
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_piglet(ctx: Context[object, AppContext]) -> AsyncPiglet:
|
|
17
|
+
"""Get AsyncPiglet from context."""
|
|
18
|
+
if ctx.request_context is None or ctx.request_context.lifespan_context is None:
|
|
19
|
+
raise ToolError("Server context not available")
|
|
20
|
+
return ctx.request_context.lifespan_context.piglet
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Error mapping from oinker to MCP ToolErrors."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from fastmcp.exceptions import ToolError
|
|
6
|
+
from oinker import (
|
|
7
|
+
APIError,
|
|
8
|
+
AuthenticationError,
|
|
9
|
+
AuthorizationError,
|
|
10
|
+
NotFoundError,
|
|
11
|
+
RateLimitError,
|
|
12
|
+
ValidationError,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def handle_oinker_error(e: Exception, operation: str) -> ToolError:
|
|
17
|
+
"""Convert oinker exceptions to MCP ToolErrors.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
e: The exception to convert.
|
|
21
|
+
operation: Description of the operation that failed.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
A ToolError with an appropriate message.
|
|
25
|
+
"""
|
|
26
|
+
match e:
|
|
27
|
+
case AuthenticationError():
|
|
28
|
+
return ToolError(
|
|
29
|
+
"Authentication failed. Check PORKBUN_API_KEY and PORKBUN_SECRET_KEY.",
|
|
30
|
+
)
|
|
31
|
+
case AuthorizationError():
|
|
32
|
+
return ToolError(
|
|
33
|
+
f"Not authorized to {operation}. Ensure API access is enabled for this domain.",
|
|
34
|
+
)
|
|
35
|
+
case NotFoundError():
|
|
36
|
+
return ToolError(f"Not found: {e}")
|
|
37
|
+
case RateLimitError() as rle:
|
|
38
|
+
retry_msg = f" Retry in {rle.retry_after}s." if rle.retry_after else ""
|
|
39
|
+
return ToolError(f"Rate limited.{retry_msg}")
|
|
40
|
+
case ValidationError():
|
|
41
|
+
return ToolError(f"Validation error: {e}")
|
|
42
|
+
case APIError() as ae:
|
|
43
|
+
return ToolError(f"Porkbun API error: {ae.message}")
|
|
44
|
+
case _:
|
|
45
|
+
return ToolError(f"Error during {operation}: {e}")
|