dockerhub-api 0.1.0__py3-none-any.whl
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.
- dockerhub_api/__init__.py +80 -0
- dockerhub_api/__main__.py +4 -0
- dockerhub_api/agent_server.py +92 -0
- dockerhub_api/api/__init__.py +1 -0
- dockerhub_api/api/api_client_access_tokens.py +77 -0
- dockerhub_api/api/api_client_audit_logs.py +56 -0
- dockerhub_api/api/api_client_auth.py +80 -0
- dockerhub_api/api/api_client_base.py +338 -0
- dockerhub_api/api/api_client_groups.py +158 -0
- dockerhub_api/api/api_client_org_access_tokens.py +106 -0
- dockerhub_api/api/api_client_orgs.py +149 -0
- dockerhub_api/api/api_client_repositories.py +217 -0
- dockerhub_api/api/api_client_scim.py +153 -0
- dockerhub_api/api_client.py +35 -0
- dockerhub_api/auth.py +252 -0
- dockerhub_api/dockerhub_input_models.py +756 -0
- dockerhub_api/dockerhub_response_models.py +344 -0
- dockerhub_api/main_agent.json +14 -0
- dockerhub_api/mcp/__init__.py +120 -0
- dockerhub_api/mcp/mcp_admin.py +45 -0
- dockerhub_api/mcp/mcp_audit.py +44 -0
- dockerhub_api/mcp/mcp_auth.py +66 -0
- dockerhub_api/mcp/mcp_org.py +58 -0
- dockerhub_api/mcp/mcp_repos.py +58 -0
- dockerhub_api/mcp/mcp_scim.py +56 -0
- dockerhub_api/mcp/mcp_teams.py +55 -0
- dockerhub_api/mcp_config.json +3 -0
- dockerhub_api/mcp_server.py +109 -0
- dockerhub_api-0.1.0.dist-info/METADATA +230 -0
- dockerhub_api-0.1.0.dist-info/RECORD +34 -0
- dockerhub_api-0.1.0.dist-info/WHEEL +5 -0
- dockerhub_api-0.1.0.dist-info/entry_points.txt +3 -0
- dockerhub_api-0.1.0.dist-info/licenses/LICENSE +21 -0
- dockerhub_api-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""MCP tool for Docker Hub repositories and tags.
|
|
2
|
+
|
|
3
|
+
CONCEPT:HUB-1.4 — action-routed MCP surface.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from fastmcp import Context, FastMCP
|
|
9
|
+
from fastmcp.dependencies import Depends
|
|
10
|
+
from pydantic import Field
|
|
11
|
+
|
|
12
|
+
from dockerhub_api.mcp import get_hub_client, parse_params, redact_secrets, run_action
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def register_repos_tools(mcp: FastMCP):
|
|
16
|
+
@mcp.tool(tags={"repositories"})
|
|
17
|
+
async def hub_repos(
|
|
18
|
+
action: str = Field(
|
|
19
|
+
description=(
|
|
20
|
+
"Action to perform. Must be one of: 'list', 'create', 'get', "
|
|
21
|
+
"'check', 'list_tags', 'check_tags', 'get_tag', 'check_tag', "
|
|
22
|
+
"'set_immutable_tags', 'verify_immutable_tags', 'assign_group'"
|
|
23
|
+
)
|
|
24
|
+
),
|
|
25
|
+
params_json: str = Field(
|
|
26
|
+
default="{}", description="JSON string of parameters to pass to the action."
|
|
27
|
+
),
|
|
28
|
+
client=Depends(get_hub_client),
|
|
29
|
+
ctx: Context | None = Field(
|
|
30
|
+
default=None, description="MCP context for progress reporting"
|
|
31
|
+
),
|
|
32
|
+
) -> Any:
|
|
33
|
+
"""Manage Docker Hub repositories: list/create/inspect repositories,
|
|
34
|
+
browse tags, configure and verify immutable tags, and grant teams
|
|
35
|
+
repository permissions. Repository creation is the primary release
|
|
36
|
+
provisioning path and is allowed by default.
|
|
37
|
+
"""
|
|
38
|
+
if ctx:
|
|
39
|
+
await ctx.info("Executing tool...")
|
|
40
|
+
try:
|
|
41
|
+
kwargs = parse_params(params_json)
|
|
42
|
+
except Exception as e:
|
|
43
|
+
return {"error": f"Invalid params_json: {e}"}
|
|
44
|
+
|
|
45
|
+
handlers = {
|
|
46
|
+
"list": client.get_repositories,
|
|
47
|
+
"create": client.create_repository,
|
|
48
|
+
"get": client.get_repository,
|
|
49
|
+
"check": client.check_repository,
|
|
50
|
+
"list_tags": client.get_repository_tags,
|
|
51
|
+
"check_tags": client.check_repository_tags,
|
|
52
|
+
"get_tag": client.get_repository_tag,
|
|
53
|
+
"check_tag": client.check_repository_tag,
|
|
54
|
+
"set_immutable_tags": client.update_immutable_tags,
|
|
55
|
+
"verify_immutable_tags": client.verify_immutable_tags,
|
|
56
|
+
"assign_group": client.assign_repository_group,
|
|
57
|
+
}
|
|
58
|
+
return redact_secrets(run_action(handlers, action, kwargs))
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""MCP tool for Docker Hub SCIM 2.0 provisioning.
|
|
2
|
+
|
|
3
|
+
CONCEPT:HUB-1.4 — action-routed MCP surface.
|
|
4
|
+
CONCEPT:HUB-1.5 — SCIM provisioning.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from fastmcp import Context, FastMCP
|
|
10
|
+
from fastmcp.dependencies import Depends
|
|
11
|
+
from pydantic import Field
|
|
12
|
+
|
|
13
|
+
from dockerhub_api.mcp import get_hub_client, parse_params, redact_secrets, run_action
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def register_scim_tools(mcp: FastMCP):
|
|
17
|
+
@mcp.tool(tags={"scim"})
|
|
18
|
+
async def hub_scim(
|
|
19
|
+
action: str = Field(
|
|
20
|
+
description=(
|
|
21
|
+
"Action to perform. Must be one of: 'service_provider_config', "
|
|
22
|
+
"'resource_types', 'resource_type', 'schemas', 'schema', "
|
|
23
|
+
"'list_users', 'get_user', 'create_user', 'update_user'"
|
|
24
|
+
)
|
|
25
|
+
),
|
|
26
|
+
params_json: str = Field(
|
|
27
|
+
default="{}", description="JSON string of parameters to pass to the action."
|
|
28
|
+
),
|
|
29
|
+
client=Depends(get_hub_client),
|
|
30
|
+
ctx: Context | None = Field(
|
|
31
|
+
default=None, description="MCP context for progress reporting"
|
|
32
|
+
),
|
|
33
|
+
) -> Any:
|
|
34
|
+
"""Docker Hub SCIM 2.0: service discovery (ServiceProviderConfig,
|
|
35
|
+
ResourceTypes, Schemas) and user provisioning (list with
|
|
36
|
+
startIndex/count/filter/sortBy/sortOrder, get, create, replace).
|
|
37
|
+
"""
|
|
38
|
+
if ctx:
|
|
39
|
+
await ctx.info("Executing tool...")
|
|
40
|
+
try:
|
|
41
|
+
kwargs = parse_params(params_json)
|
|
42
|
+
except Exception as e:
|
|
43
|
+
return {"error": f"Invalid params_json: {e}"}
|
|
44
|
+
|
|
45
|
+
handlers = {
|
|
46
|
+
"service_provider_config": client.get_scim_service_provider_config,
|
|
47
|
+
"resource_types": client.get_scim_resource_types,
|
|
48
|
+
"resource_type": client.get_scim_resource_type,
|
|
49
|
+
"schemas": client.get_scim_schemas,
|
|
50
|
+
"schema": client.get_scim_schema,
|
|
51
|
+
"list_users": client.get_scim_users,
|
|
52
|
+
"get_user": client.get_scim_user,
|
|
53
|
+
"create_user": client.create_scim_user,
|
|
54
|
+
"update_user": client.replace_scim_user,
|
|
55
|
+
}
|
|
56
|
+
return redact_secrets(run_action(handlers, action, kwargs))
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""MCP tool for Docker Hub groups (teams) and their membership.
|
|
2
|
+
|
|
3
|
+
CONCEPT:HUB-1.4 — action-routed MCP surface.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from fastmcp import Context, FastMCP
|
|
9
|
+
from fastmcp.dependencies import Depends
|
|
10
|
+
from pydantic import Field
|
|
11
|
+
|
|
12
|
+
from dockerhub_api.mcp import get_hub_client, parse_params, redact_secrets, run_action
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def register_teams_tools(mcp: FastMCP):
|
|
16
|
+
@mcp.tool(tags={"teams"})
|
|
17
|
+
async def hub_teams(
|
|
18
|
+
action: str = Field(
|
|
19
|
+
description=(
|
|
20
|
+
"Action to perform. Must be one of: 'list', 'create', 'get', "
|
|
21
|
+
"'update', 'patch', 'delete', 'list_members', 'add_member', "
|
|
22
|
+
"'remove_member'"
|
|
23
|
+
)
|
|
24
|
+
),
|
|
25
|
+
params_json: str = Field(
|
|
26
|
+
default="{}", description="JSON string of parameters to pass to the action."
|
|
27
|
+
),
|
|
28
|
+
client=Depends(get_hub_client),
|
|
29
|
+
ctx: Context | None = Field(
|
|
30
|
+
default=None, description="MCP context for progress reporting"
|
|
31
|
+
),
|
|
32
|
+
) -> Any:
|
|
33
|
+
"""Manage Docker Hub organization groups (teams) and their members.
|
|
34
|
+
Group deletion and member removal require
|
|
35
|
+
DOCKERHUB_ALLOW_DESTRUCTIVE=True.
|
|
36
|
+
"""
|
|
37
|
+
if ctx:
|
|
38
|
+
await ctx.info("Executing tool...")
|
|
39
|
+
try:
|
|
40
|
+
kwargs = parse_params(params_json)
|
|
41
|
+
except Exception as e:
|
|
42
|
+
return {"error": f"Invalid params_json: {e}"}
|
|
43
|
+
|
|
44
|
+
handlers = {
|
|
45
|
+
"list": client.get_groups,
|
|
46
|
+
"create": client.create_group,
|
|
47
|
+
"get": client.get_group,
|
|
48
|
+
"update": client.update_group,
|
|
49
|
+
"patch": client.patch_group,
|
|
50
|
+
"delete": client.delete_group,
|
|
51
|
+
"list_members": client.get_group_members,
|
|
52
|
+
"add_member": client.add_group_member,
|
|
53
|
+
"remove_member": client.remove_group_member,
|
|
54
|
+
}
|
|
55
|
+
return redact_secrets(run_action(handlers, action, kwargs))
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
"""Docker Hub MCP server entry point.
|
|
3
|
+
|
|
4
|
+
CONCEPT:HUB-1.4 — action-routed MCP surface. Registers the consolidated,
|
|
5
|
+
togglable tool modules (hub_auth, hub_repos, hub_org, hub_teams, hub_audit,
|
|
6
|
+
hub_scim, hub_admin) on an agent-utilities FastMCP server.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import warnings
|
|
10
|
+
|
|
11
|
+
with warnings.catch_warnings():
|
|
12
|
+
warnings.simplefilter("ignore")
|
|
13
|
+
|
|
14
|
+
warnings.filterwarnings("ignore", message=".*urllib3.*or chardet.*")
|
|
15
|
+
warnings.filterwarnings("ignore", message=".*urllib3.*or charset_normalizer.*")
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
from agent_utilities.base_utilities import to_boolean
|
|
23
|
+
from agent_utilities.mcp_utilities import create_mcp_server
|
|
24
|
+
from dotenv import find_dotenv, load_dotenv
|
|
25
|
+
from fastmcp.utilities.logging import get_logger
|
|
26
|
+
|
|
27
|
+
from dockerhub_api.mcp import (
|
|
28
|
+
register_admin_tools,
|
|
29
|
+
register_audit_tools,
|
|
30
|
+
register_auth_tools,
|
|
31
|
+
register_org_tools,
|
|
32
|
+
register_repos_tools,
|
|
33
|
+
register_scim_tools,
|
|
34
|
+
register_teams_tools,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
__version__ = "0.1.0"
|
|
38
|
+
print(f"Docker Hub MCP v{__version__}", file=sys.stderr)
|
|
39
|
+
|
|
40
|
+
logger = get_logger(name="mcp_server")
|
|
41
|
+
logger.setLevel(logging.DEBUG)
|
|
42
|
+
|
|
43
|
+
DEFAULT_DOCKERHUB_SSL_VERIFY = to_boolean(
|
|
44
|
+
string=os.getenv("DOCKERHUB_SSL_VERIFY", "True")
|
|
45
|
+
)
|
|
46
|
+
DEFAULT_DOCKERHUB_URL = os.getenv("DOCKERHUB_URL", "https://hub.docker.com")
|
|
47
|
+
DEFAULT_DOCKERHUB_TOKEN = os.getenv("DOCKERHUB_TOKEN", None)
|
|
48
|
+
|
|
49
|
+
#: (env toggle, register function) — every consolidated tool module.
|
|
50
|
+
TOOL_REGISTRY = (
|
|
51
|
+
("AUTHTOOL", register_auth_tools),
|
|
52
|
+
("REPOSTOOL", register_repos_tools),
|
|
53
|
+
("ORGTOOL", register_org_tools),
|
|
54
|
+
("TEAMSTOOL", register_teams_tools),
|
|
55
|
+
("AUDITTOOL", register_audit_tools),
|
|
56
|
+
("SCIMTOOL", register_scim_tools),
|
|
57
|
+
("ADMINTOOL", register_admin_tools),
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_mcp_instance() -> tuple[Any, Any, Any, Any]:
|
|
62
|
+
"""Initialize and return the Docker Hub MCP instance, args, and middlewares."""
|
|
63
|
+
load_dotenv(find_dotenv())
|
|
64
|
+
os.environ["FASTMCP_LOG_LEVEL"] = "ERROR"
|
|
65
|
+
os.environ["TERM"] = "dumb"
|
|
66
|
+
os.environ["NO_COLOR"] = "1"
|
|
67
|
+
|
|
68
|
+
args, mcp, middlewares = create_mcp_server(
|
|
69
|
+
name="DockerHub",
|
|
70
|
+
version=__version__,
|
|
71
|
+
instructions=(
|
|
72
|
+
"Docker Hub API MCP Server - Manage repositories, tags, access "
|
|
73
|
+
"tokens, organizations, teams, audit logs, and SCIM provisioning."
|
|
74
|
+
),
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
registered_tags: list[str] = []
|
|
78
|
+
for toggle, register in TOOL_REGISTRY:
|
|
79
|
+
if to_boolean(string=os.getenv(toggle, "True")):
|
|
80
|
+
register(mcp)
|
|
81
|
+
registered_tags.append(toggle)
|
|
82
|
+
|
|
83
|
+
for mw in middlewares:
|
|
84
|
+
mcp.add_middleware(mw)
|
|
85
|
+
|
|
86
|
+
return mcp, args, middlewares, registered_tags
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def mcp_server() -> None:
|
|
90
|
+
mcp, args, middlewares, registered_tags = get_mcp_instance()
|
|
91
|
+
print(f"{'dockerhub-api'} MCP v{__version__}", file=sys.stderr)
|
|
92
|
+
print("\nStarting MCP Server", file=sys.stderr)
|
|
93
|
+
print(f" Transport: {args.transport.upper()}", file=sys.stderr)
|
|
94
|
+
print(f" Auth: {args.auth_type}", file=sys.stderr)
|
|
95
|
+
print(f" Dynamic Tags Loaded: {len(set(registered_tags))}", file=sys.stderr)
|
|
96
|
+
|
|
97
|
+
if args.transport == "stdio":
|
|
98
|
+
mcp.run(transport="stdio")
|
|
99
|
+
elif args.transport == "streamable-http":
|
|
100
|
+
mcp.run(transport="streamable-http", host=args.host, port=args.port)
|
|
101
|
+
elif args.transport == "sse":
|
|
102
|
+
mcp.run(transport="sse", host=args.host, port=args.port)
|
|
103
|
+
else:
|
|
104
|
+
logger.error("Invalid transport", extra={"transport": args.transport})
|
|
105
|
+
sys.exit(1)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
if __name__ == "__main__":
|
|
109
|
+
mcp_server()
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dockerhub-api
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Docker Hub API + MCP Server + A2A Server
|
|
5
|
+
Author-email: Audel Rouhi <knucklessg1@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Requires-Python: <3.15,>=3.11
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: agent-utilities>=0.47.0
|
|
16
|
+
Requires-Dist: httpx>=0.27.0
|
|
17
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
18
|
+
Provides-Extra: mcp
|
|
19
|
+
Requires-Dist: agent-utilities[mcp]>=0.47.0; extra == "mcp"
|
|
20
|
+
Provides-Extra: agent
|
|
21
|
+
Requires-Dist: agent-utilities[agent,logfire]>=0.47.0; extra == "agent"
|
|
22
|
+
Provides-Extra: all
|
|
23
|
+
Requires-Dist: dockerhub-api[agent,logfire,mcp]>=0.1.0; extra == "all"
|
|
24
|
+
Provides-Extra: test
|
|
25
|
+
Requires-Dist: pytest-xdist>=3.6.0; extra == "test"
|
|
26
|
+
Requires-Dist: pytest; extra == "test"
|
|
27
|
+
Requires-Dist: pytest-asyncio; extra == "test"
|
|
28
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# Dockerhub Api
|
|
32
|
+
## CLI or API | MCP | Agent
|
|
33
|
+
|
|
34
|
+

|
|
35
|
+

|
|
36
|
+

|
|
37
|
+

|
|
38
|
+

|
|
39
|
+

|
|
40
|
+

|
|
41
|
+

|
|
42
|
+

|
|
43
|
+

|
|
44
|
+

|
|
45
|
+
|
|
46
|
+
*Version: 0.1.0*
|
|
47
|
+
|
|
48
|
+
> **Documentation** — Installation, deployment, usage across the API, CLI, and MCP
|
|
49
|
+
> interfaces, the integrated A2A agent server, and guidance on the backing
|
|
50
|
+
> Docker Hub platform are maintained in [docs/](docs/index.md).
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Table of Contents
|
|
55
|
+
|
|
56
|
+
- [Overview](#overview)
|
|
57
|
+
- [Key Features](#key-features)
|
|
58
|
+
- [Installation](#installation)
|
|
59
|
+
- [Usage](#usage)
|
|
60
|
+
- [Python API / CLI](#python-api--cli)
|
|
61
|
+
- [MCP](#mcp)
|
|
62
|
+
- [Agent (A2A)](#agent-a2a)
|
|
63
|
+
- [Environment Variables](#environment-variables)
|
|
64
|
+
- [Deployment](#deployment)
|
|
65
|
+
- [Safety Model](#safety-model)
|
|
66
|
+
- [Concepts](#concepts)
|
|
67
|
+
- [License](#license)
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Overview
|
|
72
|
+
|
|
73
|
+
**Dockerhub Api** is a production-grade Agent and Model Context Protocol (MCP) server
|
|
74
|
+
that wraps the official **Docker Hub API v2** (`https://hub.docker.com`): repositories
|
|
75
|
+
and tags, immutable tags, personal and organization access tokens, organization
|
|
76
|
+
members/settings/invites, teams, audit logs, and SCIM 2.0 provisioning.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Key Features
|
|
81
|
+
|
|
82
|
+
- **Consolidated Action-Routed MCP Tools:** Seven togglable tool modules
|
|
83
|
+
(`hub_auth`, `hub_repos`, `hub_org`, `hub_teams`, `hub_audit`, `hub_scim`,
|
|
84
|
+
`hub_admin`) minimize token overhead in LLM contexts.
|
|
85
|
+
- **JWT Auth Lifecycle:** Short-lived bearer minted from `POST /v2/auth/token`
|
|
86
|
+
(password, PAT `dckr_pat_*`, or org access token), cached and refreshed before
|
|
87
|
+
expiry, with one transparent re-mint on 401.
|
|
88
|
+
- **Rate-Limit Telemetry:** `X-RateLimit-*` headers surfaced in every result;
|
|
89
|
+
HTTP 429 retried with bounded `Retry-After` backoff.
|
|
90
|
+
- **Safety by Default:** Deletes and org-settings writes are gated behind
|
|
91
|
+
`DOCKERHUB_ALLOW_DESTRUCTIVE` (default `False`); secrets are redacted from tool
|
|
92
|
+
results (plaintext tokens appear exactly once — on creation). Repository creation
|
|
93
|
+
stays enabled: it is the primary release-provisioning use case.
|
|
94
|
+
- **Integrated Graph Agent:** Built-in Pydantic AI agent (`dockerhub-agent`) with
|
|
95
|
+
A2A and AG-UI web interfaces.
|
|
96
|
+
- **Native Telemetry & Tracing:** Out-of-the-box OpenTelemetry exports and Langfuse
|
|
97
|
+
tracing via agent-utilities.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Installation
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pip install dockerhub-api # API client only
|
|
105
|
+
pip install "dockerhub-api[mcp]" # + MCP server
|
|
106
|
+
pip install "dockerhub-api[agent]" # + A2A agent server
|
|
107
|
+
pip install "dockerhub-api[all]" # everything
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
| Extra | Adds |
|
|
111
|
+
|---|---|
|
|
112
|
+
| `mcp` | FastMCP server (`dockerhub-mcp`) via `agent-utilities[mcp]` |
|
|
113
|
+
| `agent` | Pydantic-AI A2A agent (`dockerhub-agent`) + Logfire via `agent-utilities[agent,logfire]` |
|
|
114
|
+
| `all` | `mcp` + `agent` |
|
|
115
|
+
| `test` | pytest toolchain for development |
|
|
116
|
+
|
|
117
|
+
Or pull the published image:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
docker pull knucklessg1/dockerhub-api:latest
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Usage
|
|
126
|
+
|
|
127
|
+
### Python API / CLI
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from dockerhub_api.auth import get_client
|
|
131
|
+
|
|
132
|
+
api = get_client() # reads DOCKERHUB_URL / DOCKER_HUB_USER / DOCKER_HUB_TOKEN
|
|
133
|
+
|
|
134
|
+
repos = api.get_repositories(namespace="acme", ordering="-last_updated")
|
|
135
|
+
api.create_repository(namespace="acme", name="release-images", is_private=True)
|
|
136
|
+
tags = api.get_repository_tags(namespace="acme", repository="release-images")
|
|
137
|
+
print(api.rate_limit) # latest X-RateLimit-* snapshot
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Every client method returns a uniform envelope:
|
|
141
|
+
`{"status_code": int, "data": ..., "rate_limit": {"limit", "remaining", "reset"}}`.
|
|
142
|
+
|
|
143
|
+
### MCP
|
|
144
|
+
|
|
145
|
+
#### Available MCP Tools
|
|
146
|
+
|
|
147
|
+
| Tool Module | Toggle Env Var | Enabled by Default | Description & Nested Actions |
|
|
148
|
+
|---|---|---|---|
|
|
149
|
+
| `hub_auth` | `AUTHTOOL` | True | Token lifecycle: `create_token`, `login` (deprecated), `two_factor_login`, `list_pats`, `create_pat`, `get_pat`, `update_pat`, `delete_pat`, `list_oats`, `create_oat`, `get_oat`, `update_oat`, `delete_oat` |
|
|
150
|
+
| `hub_repos` | `REPOSTOOL` | True | Repositories & tags: `list`, `create`, `get`, `check`, `list_tags`, `check_tags`, `get_tag`, `check_tag`, `set_immutable_tags`, `verify_immutable_tags`, `assign_group` |
|
|
151
|
+
| `hub_org` | `ORGTOOL` | True | Org admin: `get_settings`, `update_settings`, `list_members`, `export_members`, `update_member`, `remove_member`, `list_invites`, `delete_invite`, `resend_invite`, `bulk_invite` |
|
|
152
|
+
| `hub_teams` | `TEAMSTOOL` | True | Teams: `list`, `create`, `get`, `update`, `patch`, `delete`, `list_members`, `add_member`, `remove_member` |
|
|
153
|
+
| `hub_audit` | `AUDITTOOL` | True | Audit trail: `logs`, `actions` |
|
|
154
|
+
| `hub_scim` | `SCIMTOOL` | True | SCIM 2.0: `service_provider_config`, `resource_types`, `resource_type`, `schemas`, `schema`, `list_users`, `get_user`, `create_user`, `update_user` |
|
|
155
|
+
| `hub_admin` | `ADMINTOOL` | True | Diagnostics: `rate_limit`, `whoami` (local JWT introspection) |
|
|
156
|
+
|
|
157
|
+
Run the server:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
export DOCKER_HUB_USER=youruser
|
|
161
|
+
export DOCKER_HUB_TOKEN=dckr_pat_xxx
|
|
162
|
+
dockerhub-mcp --transport streamable-http --host 0.0.0.0 --port 8000
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Agent (A2A)
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
dockerhub-agent --mcp-url http://localhost:8000/mcp --web
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Environment Variables
|
|
174
|
+
|
|
175
|
+
| Variable | Default | Purpose |
|
|
176
|
+
|---|---|---|
|
|
177
|
+
| `DOCKERHUB_URL` | `https://hub.docker.com` | Docker Hub API base URL |
|
|
178
|
+
| `DOCKER_HUB_USER` | — | Account identifier (official hub-tool name, primary) |
|
|
179
|
+
| `DOCKER_HUB_TOKEN` | — | Password, PAT `dckr_pat_*`, or org access token (primary) |
|
|
180
|
+
| `DOCKERHUB_USERNAME` / `DOCKERHUB_TOKEN` | — | Legacy fallback aliases for the two above |
|
|
181
|
+
| `DOCKERHUB_JWT` | — | Optional pre-minted bearer (overrides credential exchange) |
|
|
182
|
+
| `DOCKERHUB_SSL_VERIFY` | `True` | TLS certificate verification |
|
|
183
|
+
| `DOCKERHUB_ALLOW_DESTRUCTIVE` | `False` | Enable deletes and org-settings writes |
|
|
184
|
+
| `AUTHTOOL` … `ADMINTOOL` | `True` | Per-module MCP tool toggles (see table above) |
|
|
185
|
+
| `HOST` / `PORT` / `TRANSPORT` | `0.0.0.0` / `8000` / `stdio` | MCP server bind & transport (`stdio`, `streamable-http`, `sse`) |
|
|
186
|
+
| `AUTH_TYPE` | `none` | MCP server auth mode (Docker image) |
|
|
187
|
+
| `MCP_URL` | — | MCP endpoint the A2A agent connects to |
|
|
188
|
+
| `ENABLE_OTEL` | `True` | OpenTelemetry / Langfuse export via agent-utilities |
|
|
189
|
+
| `EUNOMIA_TYPE` / `EUNOMIA_POLICY_FILE` / `EUNOMIA_REMOTE_URL` | `none` / `mcp_policies.json` / — | Eunomia access-governance middleware |
|
|
190
|
+
| `FASTMCP_LOG_LEVEL` / `NO_COLOR` | — | FastMCP logging controls |
|
|
191
|
+
|
|
192
|
+
A complete annotated template lives in [.env.example](.env.example).
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Deployment
|
|
197
|
+
|
|
198
|
+
Docker Compose definitions ship in [docker/](docker/):
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
cp .env.example .env # fill in DOCKER_HUB_USER / DOCKER_HUB_TOKEN
|
|
202
|
+
docker compose -f docker/mcp.compose.yml up -d # MCP server only
|
|
203
|
+
docker compose -f docker/agent.compose.yml up -d # MCP server + A2A agent (port 9018)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Both services expose `/health` endpoints; see
|
|
207
|
+
[docs/deployment.md](docs/deployment.md) for transports, Caddy ingress, and
|
|
208
|
+
Technitium DNS guidance.
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Safety Model
|
|
213
|
+
|
|
214
|
+
| Operation class | Default | Override |
|
|
215
|
+
|---|---|---|
|
|
216
|
+
| Reads (repos, tags, members, logs, SCIM) | allowed | — |
|
|
217
|
+
| Repository create / immutable-tag config / invites / role updates | allowed | — |
|
|
218
|
+
| Deletes (PATs, OATs, groups, members, invites) | **blocked** | `DOCKERHUB_ALLOW_DESTRUCTIVE=True` |
|
|
219
|
+
| Org-settings writes (`PUT /v2/orgs/{org}/settings`) | **blocked** | `DOCKERHUB_ALLOW_DESTRUCTIVE=True` |
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Concepts
|
|
224
|
+
|
|
225
|
+
The concept registry (`CONCEPT:HUB-1.x`) is documented in
|
|
226
|
+
[docs/concepts.md](docs/concepts.md).
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
dockerhub_api/__init__.py,sha256=k1wiT9akFBY7B-yLh83mkS1NLgtnGjRiFEyisivWJgc,2507
|
|
2
|
+
dockerhub_api/__main__.py,sha256=U3vMyjze1PLsQP_7Ykt4ut64-J3zSRepGHR_hVDgv4M,99
|
|
3
|
+
dockerhub_api/agent_server.py,sha256=pc4lde4kFMFYNoYVUEiT7XQYjv1V1mG-HShDeJlFox0,2665
|
|
4
|
+
dockerhub_api/api_client.py,sha256=r2dnhw2iMA4FTrMibQOuus__gkQemsZ9P6gbZoOYHh0,1247
|
|
5
|
+
dockerhub_api/auth.py,sha256=OJeYPbtD6HReobaZE3wHnkE7oE8XZAKyKsxzUjaeCMI,8710
|
|
6
|
+
dockerhub_api/dockerhub_input_models.py,sha256=661VY4sWohGDc_QVnndbgRqI5mLVhOjED7fG9YS5ZPA,26142
|
|
7
|
+
dockerhub_api/dockerhub_response_models.py,sha256=3jA-3aPcaHSgZXF3gaLD-VFDqzCQ59nKzuFZrOeeevI,9280
|
|
8
|
+
dockerhub_api/main_agent.json,sha256=k9FA3f4eAiaeCbgCHj-ug5Vj38By7NhCbd9MMPdfhHw,983
|
|
9
|
+
dockerhub_api/mcp_config.json,sha256=2OOXrwO1sDLyHQqpZwhvDHizPIe3by6YmK4KFE333gI,23
|
|
10
|
+
dockerhub_api/mcp_server.py,sha256=XRxcRfE6Ph4-fh4T9TlA4bp_TZ8QAhiHv12zMXs1kzY,3512
|
|
11
|
+
dockerhub_api/api/__init__.py,sha256=Ofb5z_yDBJ2JgbZxwQys6YBX327RFc0kKeOCOp1p3QY,47
|
|
12
|
+
dockerhub_api/api/api_client_access_tokens.py,sha256=MsAgkA8QQOE3p5bLYL6mvjkqpg_ENasPILNfuR42zSE,2859
|
|
13
|
+
dockerhub_api/api/api_client_audit_logs.py,sha256=uGqiKE8gOzkT4o6bDmz1TrhEAUDSudHNSsSMZRETEYg,1810
|
|
14
|
+
dockerhub_api/api/api_client_auth.py,sha256=pqo2Rasdr-1lUXZRXK-q23SP2hGh-L2mafvMzuopzuc,3251
|
|
15
|
+
dockerhub_api/api/api_client_base.py,sha256=WTMyCIDRXgSQPpSR32S2cKBAZa1BZtsYOCzpYVW1rK0,12236
|
|
16
|
+
dockerhub_api/api/api_client_groups.py,sha256=FqGaAu9SznW2uCpZJyJEj1s07Wf7bVQhE1IBbxMUmX8,5336
|
|
17
|
+
dockerhub_api/api/api_client_org_access_tokens.py,sha256=j3yqMoeZ3UTuXpDbcnmbfzMqfmEq-Gf5jFPfpWF-khk,3709
|
|
18
|
+
dockerhub_api/api/api_client_orgs.py,sha256=l1siP_O_0GVzBrNrJTEHRchuL8lw-X0A7OJkjMQRSms,5644
|
|
19
|
+
dockerhub_api/api/api_client_repositories.py,sha256=wwT3oe9lCcGtSGN1uXGqahCbYcm4pBSbrY7JmnHBk5k,7512
|
|
20
|
+
dockerhub_api/api/api_client_scim.py,sha256=HLkUSzb1RHP0vOFpzANgMYKjTG4tPbgXfFKuD93FvoI,5287
|
|
21
|
+
dockerhub_api/mcp/__init__.py,sha256=IEb_LmnQCWTk_Y__wS-2qWs_5EFbDUTmNkfQGFdldBE,3694
|
|
22
|
+
dockerhub_api/mcp/mcp_admin.py,sha256=v-L8uCTWe7he-BAGoMl9dBapdHganA7RNgtRRGyPy48,1543
|
|
23
|
+
dockerhub_api/mcp/mcp_audit.py,sha256=HNGnBxrHnuND31zontePiD56Zp2ObRoKc7MlnhXk-mQ,1458
|
|
24
|
+
dockerhub_api/mcp/mcp_auth.py,sha256=bTi6teDamh5huppPGspwLKZqQ3-0d6eYtkDF-hJ-ntY,2652
|
|
25
|
+
dockerhub_api/mcp/mcp_org.py,sha256=GBwQ2_WW-Zb0MmdAtg4uRNTYa4tve4dxMDjEoPgOacM,2238
|
|
26
|
+
dockerhub_api/mcp/mcp_repos.py,sha256=CE3hcCYD1nzKidRDF-dRuyp2ANNN2uM2kiqpOodEE7s,2242
|
|
27
|
+
dockerhub_api/mcp/mcp_scim.py,sha256=ZoBP9UvYlcEKs8-NN-wX3WGujt-8vaLbOvo2Hrnsimo,2106
|
|
28
|
+
dockerhub_api/mcp/mcp_teams.py,sha256=upA2ojftlmNYlfZmoRNqkQAM0-oiyUqYQiBOOhtp7NE,1914
|
|
29
|
+
dockerhub_api-0.1.0.dist-info/licenses/LICENSE,sha256=lAmFjsQO9B015kHJ11AulWIYtfW4DjC-_eP_yamF5Yg,1068
|
|
30
|
+
dockerhub_api-0.1.0.dist-info/METADATA,sha256=6IDML66_rsD7497wU67qTlV4wUwhfe-gp8qolkV_Qfw,9268
|
|
31
|
+
dockerhub_api-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
32
|
+
dockerhub_api-0.1.0.dist-info/entry_points.txt,sha256=fDrOoLNTQvXQkguffAuFgiMXYi2KwnmE8N1a88esoMo,128
|
|
33
|
+
dockerhub_api-0.1.0.dist-info/top_level.txt,sha256=2WKbefEedQJEA-Vfhw7K1aRwo7riYKLL1BomvcXNMio,14
|
|
34
|
+
dockerhub_api-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Audel Rouhi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dockerhub_api
|