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.
@@ -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
+ [![CI](https://github.com/major/porkbun-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/major/porkbun-mcp/actions/workflows/ci.yml)
31
+ [![codecov](https://codecov.io/gh/major/porkbun-mcp/branch/main/graph/badge.svg)](https://codecov.io/gh/major/porkbun-mcp)
32
+ [![PyPI version](https://badge.fury.io/py/porkbun-mcp.svg)](https://pypi.org/project/porkbun-mcp/)
33
+ [![Python 3.13+](https://img.shields.io/badge/python-3.13+-blue.svg)](https://www.python.org/downloads/)
34
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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
+ [![CI](https://github.com/major/porkbun-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/major/porkbun-mcp/actions/workflows/ci.yml)
8
+ [![codecov](https://codecov.io/gh/major/porkbun-mcp/branch/main/graph/badge.svg)](https://codecov.io/gh/major/porkbun-mcp)
9
+ [![PyPI version](https://badge.fury.io/py/porkbun-mcp.svg)](https://pypi.org/project/porkbun-mcp/)
10
+ [![Python 3.13+](https://img.shields.io/badge/python-3.13+-blue.svg)](https://www.python.org/downloads/)
11
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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}")