hanzo-tools-platform 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,63 @@
1
+ # IDE and editor
2
+ .vscode
3
+ .idea
4
+
5
+ # Python
6
+ *.egg-info
7
+ __pycache__
8
+ .mypy_cache
9
+ .pytest_cache
10
+ *.pyc
11
+ .venv
12
+
13
+ # Build
14
+ dist
15
+ _dev
16
+
17
+ # Environment
18
+ .env
19
+ .envrc
20
+
21
+ # Logs
22
+ .prism.log
23
+ codegen.log
24
+ *.log
25
+
26
+ # Package manager
27
+ Brewfile.lock.json
28
+
29
+ # Agent config files (symlinked from user home)
30
+ LLM.md
31
+ AGENTS.md
32
+ CLAUDE.md
33
+ GEMINI.md
34
+ GROK.md
35
+ QWEN.md
36
+
37
+ # Local databases and state
38
+ .hanzo/
39
+ .grok/
40
+
41
+ # Documentation build
42
+ docs/.next/
43
+ docs/out/
44
+ docs/node_modules/
45
+
46
+ # Training data and scripts (DO NOT COMMIT)
47
+ training_dataset.jsonl
48
+ scripts/full_extractor.py
49
+ scripts/mega_extractor.py
50
+ scripts/mega_full_extractor.py
51
+ scripts/streaming_extractor.py
52
+ scripts/supplement_extractor.py
53
+
54
+ # Test files at root (experimental)
55
+ test_post_quantum_*.py
56
+
57
+ # Analysis documents (internal)
58
+ HANZO_INNOVATION_OPPORTUNITIES.md
59
+ POST_QUANTUM_CRYPTOGRAPHY_IMPLEMENTATION.md
60
+
61
+ # Experimental cryptography (WIP)
62
+ pkg/hanzo/src/hanzo/cryptography/
63
+ site/
@@ -0,0 +1,22 @@
1
+ Metadata-Version: 2.4
2
+ Name: hanzo-tools-platform
3
+ Version: 0.1.0
4
+ Summary: Hanzo platform MCP tool — PaaS deployments, IAM users/orgs, and cloud services
5
+ Author-email: Hanzo AI <dev@hanzo.ai>
6
+ License: MIT
7
+ Keywords: hanzo,iam,mcp,paas,platform,tools
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Requires-Python: >=3.12
14
+ Requires-Dist: hanzo-iam>=0.1.0
15
+ Requires-Dist: hanzo-tools-auth>=0.1.0
16
+ Requires-Dist: hanzo-tools-core>=0.1.0
17
+ Requires-Dist: httpx>=0.27.0
18
+ Description-Content-Type: text/markdown
19
+
20
+ # hanzo-tools-platform
21
+
22
+ Hanzo platform MCP tool — PaaS deployments, IAM, and cloud services.
@@ -0,0 +1,3 @@
1
+ # hanzo-tools-platform
2
+
3
+ Hanzo platform MCP tool — PaaS deployments, IAM, and cloud services.
File without changes
@@ -0,0 +1,7 @@
1
+ """Hanzo Platform Tools — PaaS, IAM, and cloud services via MCP."""
2
+
3
+ from .platform_tool import PlatformTool
4
+
5
+ TOOLS = [PlatformTool]
6
+
7
+ __all__ = ["PlatformTool", "TOOLS"]
@@ -0,0 +1,314 @@
1
+ """MCP tool for Hanzo platform services — PaaS, IAM, and cloud.
2
+
3
+ Combines PaaS deployments, IAM user/org management, and cloud services
4
+ into a single MCP tool to avoid tool proliferation.
5
+
6
+ Auth: Uses HanzoSession from hanzo-tools-auth to exchange IAM tokens
7
+ for PaaS sessions and access IAM APIs.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import json
13
+ import logging
14
+ from typing import Annotated, Any, final
15
+
16
+ from mcp.server import FastMCP
17
+ from mcp.server.fastmcp import Context as MCPContext
18
+ from pydantic import Field
19
+
20
+ from hanzo_tools.core.base import BaseTool
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ DESCRIPTION = """Hanzo platform management — PaaS deployments, IAM, and cloud services.
25
+
26
+ Requires authentication via `hanzo login` (stored at ~/.hanzo/auth/token.json).
27
+
28
+ PaaS actions:
29
+ - deployments: List containers/deployments in a project environment
30
+ - deploy: Get details of a specific deployment
31
+ - logs: View container logs
32
+ - redeploy: Trigger a rolling restart of a container
33
+ - env: List environments for a project
34
+
35
+ IAM actions:
36
+ - whoami: Current user info
37
+ - users: List users in the organization
38
+ - orgs: List organizations
39
+ - roles: List roles
40
+
41
+ Cloud actions:
42
+ - services: List managed services (cluster info)
43
+ - projects: List projects in an organization
44
+ """
45
+
46
+
47
+ def _get_session():
48
+ """Get HanzoSession singleton."""
49
+ from hanzo_tools.auth.session import HanzoSession
50
+ return HanzoSession.get()
51
+
52
+
53
+ @final
54
+ class PlatformTool(BaseTool):
55
+ """MCP tool for Hanzo platform operations."""
56
+
57
+ @property
58
+ def name(self) -> str:
59
+ return "platform"
60
+
61
+ @property
62
+ def description(self) -> str:
63
+ return DESCRIPTION
64
+
65
+ async def call(
66
+ self,
67
+ ctx: MCPContext,
68
+ action: str = "whoami",
69
+ org: str | None = None,
70
+ project: str | None = None,
71
+ environment: str | None = None,
72
+ container: str | None = None,
73
+ **kwargs: Any,
74
+ ) -> str:
75
+ try:
76
+ # IAM actions
77
+ if action == "whoami":
78
+ return await self._whoami()
79
+ elif action == "users":
80
+ return await self._users()
81
+ elif action == "orgs":
82
+ return await self._orgs()
83
+ elif action == "roles":
84
+ return await self._roles()
85
+ # PaaS actions
86
+ elif action == "projects":
87
+ return await self._projects(org)
88
+ elif action == "env":
89
+ return await self._envs(org, project)
90
+ elif action == "deployments":
91
+ return await self._deployments(org, project, environment)
92
+ elif action == "deploy":
93
+ return await self._deploy_detail(org, project, environment, container)
94
+ elif action == "logs":
95
+ return await self._logs(org, project, environment, container)
96
+ elif action == "redeploy":
97
+ return await self._redeploy(org, project, environment, container)
98
+ # Cloud actions
99
+ elif action == "services":
100
+ return await self._services()
101
+ else:
102
+ return json.dumps({
103
+ "error": f"Unknown action: {action}",
104
+ "available": [
105
+ "whoami", "users", "orgs", "roles",
106
+ "projects", "env", "deployments", "deploy", "logs", "redeploy",
107
+ "services",
108
+ ],
109
+ })
110
+ except RuntimeError as e:
111
+ return json.dumps({"error": str(e)})
112
+ except Exception as e:
113
+ logger.exception(f"Platform tool error: {e}")
114
+ return json.dumps({"error": f"Platform error: {e}"})
115
+
116
+ # -- IAM actions ---------------------------------------------------------
117
+
118
+ async def _whoami(self) -> str:
119
+ session = _get_session()
120
+ if not session.is_authenticated():
121
+ return json.dumps({"error": "Not authenticated. Run 'hanzo login' first."})
122
+
123
+ token = session.get_iam_token()
124
+ if token:
125
+ try:
126
+ import jwt
127
+
128
+ claims = jwt.decode(token, options={"verify_signature": False})
129
+ return json.dumps({
130
+ "sub": claims.get("sub"),
131
+ "name": claims.get("name"),
132
+ "email": claims.get("email"),
133
+ "organization": claims.get("owner"),
134
+ "iss": claims.get("iss"),
135
+ }, indent=2)
136
+ except Exception:
137
+ pass
138
+
139
+ info = session.get_token_info()
140
+ return json.dumps(info, indent=2)
141
+
142
+ async def _users(self) -> str:
143
+ session = _get_session()
144
+ iam = session.get_iam_client()
145
+ users = iam.get_users()
146
+ result = []
147
+ for u in users:
148
+ result.append({
149
+ "id": getattr(u, "id", None),
150
+ "name": getattr(u, "name", None),
151
+ "email": getattr(u, "email", None),
152
+ "display_name": getattr(u, "display_name", None),
153
+ })
154
+ return json.dumps({"count": len(result), "users": result}, indent=2)
155
+
156
+ async def _orgs(self) -> str:
157
+ session = _get_session()
158
+ iam = session.get_iam_client()
159
+ orgs = iam.get_organizations()
160
+ return json.dumps({"count": len(orgs), "organizations": orgs}, indent=2)
161
+
162
+ async def _roles(self) -> str:
163
+ session = _get_session()
164
+ iam = session.get_iam_client()
165
+ roles = iam.get_roles()
166
+ return json.dumps({"count": len(roles), "roles": roles}, indent=2)
167
+
168
+ # -- PaaS actions --------------------------------------------------------
169
+
170
+ async def _projects(self, org: str | None) -> str:
171
+ if not org:
172
+ return json.dumps({"error": "Required: org (organization ID)"})
173
+
174
+ session = _get_session()
175
+ paas = session.get_paas_client()
176
+ projects = paas.get(f"/v1/org/{org}/project")
177
+ return json.dumps({"org": org, "count": len(projects), "projects": projects}, indent=2)
178
+
179
+ async def _envs(self, org: str | None, project: str | None) -> str:
180
+ if not org or not project:
181
+ return json.dumps({"error": "Required: org and project"})
182
+
183
+ session = _get_session()
184
+ paas = session.get_paas_client()
185
+ envs = paas.get(f"/v1/org/{org}/project/{project}/env")
186
+ return json.dumps({"org": org, "project": project, "environments": envs}, indent=2)
187
+
188
+ async def _deployments(self, org: str | None, project: str | None, env: str | None) -> str:
189
+ if not org or not project or not env:
190
+ return json.dumps({"error": "Required: org, project, and environment"})
191
+
192
+ session = _get_session()
193
+ paas = session.get_paas_client()
194
+ containers = paas.get(f"/v1/org/{org}/project/{project}/env/{env}/container")
195
+
196
+ result = []
197
+ for c in containers if isinstance(containers, list) else []:
198
+ result.append({
199
+ "id": c.get("id"),
200
+ "name": c.get("name"),
201
+ "image": c.get("image"),
202
+ "status": c.get("status"),
203
+ "replicas": c.get("replicas"),
204
+ })
205
+
206
+ return json.dumps({
207
+ "org": org,
208
+ "project": project,
209
+ "environment": env,
210
+ "count": len(result),
211
+ "containers": result,
212
+ }, indent=2)
213
+
214
+ async def _deploy_detail(
215
+ self, org: str | None, project: str | None, env: str | None, container: str | None
216
+ ) -> str:
217
+ if not org or not project or not env or not container:
218
+ return json.dumps({"error": "Required: org, project, environment, and container"})
219
+
220
+ session = _get_session()
221
+ paas = session.get_paas_client()
222
+ data = paas.get(f"/v1/org/{org}/project/{project}/env/{env}/container/{container}")
223
+ return json.dumps(data, indent=2)
224
+
225
+ async def _logs(
226
+ self, org: str | None, project: str | None, env: str | None, container: str | None
227
+ ) -> str:
228
+ if not org or not project or not env or not container:
229
+ return json.dumps({"error": "Required: org, project, environment, and container"})
230
+
231
+ session = _get_session()
232
+ paas = session.get_paas_client()
233
+ logs = paas.get(f"/v1/org/{org}/project/{project}/env/{env}/container/{container}/logs")
234
+ if isinstance(logs, dict):
235
+ return json.dumps(logs, indent=2)
236
+ return str(logs)
237
+
238
+ async def _redeploy(
239
+ self, org: str | None, project: str | None, env: str | None, container: str | None
240
+ ) -> str:
241
+ if not org or not project or not env or not container:
242
+ return json.dumps({"error": "Required: org, project, environment, and container"})
243
+
244
+ session = _get_session()
245
+ paas = session.get_paas_client()
246
+
247
+ # Fetch current config and PUT it back to trigger rolling restart
248
+ current = paas.get(f"/v1/org/{org}/project/{project}/env/{env}/container/{container}")
249
+ result = paas.put(
250
+ f"/v1/org/{org}/project/{project}/env/{env}/container/{container}",
251
+ json=current,
252
+ )
253
+ return json.dumps({"action": "redeployed", "container": container, "result": result}, indent=2)
254
+
255
+ # -- Cloud actions -------------------------------------------------------
256
+
257
+ async def _services(self) -> str:
258
+ session = _get_session()
259
+ paas = session.get_paas_client()
260
+ info = paas.get("/v1/cluster/info")
261
+ templates = paas.get("/v1/cluster/templates")
262
+ return json.dumps({
263
+ "cluster": info,
264
+ "templates": templates,
265
+ }, indent=2)
266
+
267
+ # -- Registration --------------------------------------------------------
268
+
269
+ def register(self, mcp_server: FastMCP) -> None:
270
+ """Register platform tool with explicit parameters."""
271
+ tool_instance = self
272
+
273
+ @mcp_server.tool(
274
+ name="platform",
275
+ description=DESCRIPTION,
276
+ )
277
+ async def platform(
278
+ action: Annotated[
279
+ str,
280
+ Field(
281
+ description=(
282
+ "Action to perform. "
283
+ "IAM: whoami, users, orgs, roles. "
284
+ "PaaS: projects, env, deployments, deploy, logs, redeploy. "
285
+ "Cloud: services."
286
+ ),
287
+ ),
288
+ ] = "whoami",
289
+ org: Annotated[
290
+ str | None,
291
+ Field(description="Organization ID (for PaaS actions)"),
292
+ ] = None,
293
+ project: Annotated[
294
+ str | None,
295
+ Field(description="Project ID (for PaaS actions)"),
296
+ ] = None,
297
+ environment: Annotated[
298
+ str | None,
299
+ Field(description="Environment ID (for PaaS actions)"),
300
+ ] = None,
301
+ container: Annotated[
302
+ str | None,
303
+ Field(description="Container/deployment ID (for deploy, logs, redeploy)"),
304
+ ] = None,
305
+ ctx: MCPContext = None,
306
+ ) -> str:
307
+ return await tool_instance.call(
308
+ ctx,
309
+ action=action,
310
+ org=org,
311
+ project=project,
312
+ environment=environment,
313
+ container=container,
314
+ )
@@ -0,0 +1,33 @@
1
+ [project]
2
+ name = "hanzo-tools-platform"
3
+ version = "0.1.0"
4
+ description = "Hanzo platform MCP tool — PaaS deployments, IAM users/orgs, and cloud services"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ license = { text = "MIT" }
8
+ authors = [{ name = "Hanzo AI", email = "dev@hanzo.ai" }]
9
+ keywords = ["hanzo", "mcp", "paas", "iam", "platform", "tools"]
10
+ classifiers = [
11
+ "Development Status :: 4 - Beta",
12
+ "Intended Audience :: Developers",
13
+ "License :: OSI Approved :: MIT License",
14
+ "Programming Language :: Python :: 3.12",
15
+ "Programming Language :: Python :: 3.13",
16
+ ]
17
+
18
+ dependencies = [
19
+ "hanzo-tools-core>=0.1.0",
20
+ "hanzo-tools-auth>=0.1.0",
21
+ "hanzo-iam>=0.1.0",
22
+ "httpx>=0.27.0",
23
+ ]
24
+
25
+ [project.entry-points."hanzo.tools"]
26
+ platform = "hanzo_tools.platform:TOOLS"
27
+
28
+ [build-system]
29
+ requires = ["hatchling"]
30
+ build-backend = "hatchling.build"
31
+
32
+ [tool.hatch.build.targets.wheel]
33
+ packages = ["hanzo_tools"]