tradingcodex 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.
Files changed (254) hide show
  1. apps/__init__.py +1 -0
  2. apps/audit/__init__.py +1 -0
  3. apps/audit/admin.py +6 -0
  4. apps/audit/apps.py +8 -0
  5. apps/audit/migrations/0001_initial.py +35 -0
  6. apps/audit/migrations/__init__.py +0 -0
  7. apps/audit/models.py +22 -0
  8. apps/harness/__init__.py +1 -0
  9. apps/harness/admin.py +6 -0
  10. apps/harness/apps.py +8 -0
  11. apps/harness/migrations/0001_initial.py +35 -0
  12. apps/harness/migrations/__init__.py +0 -0
  13. apps/harness/models.py +22 -0
  14. apps/harness/templatetags/__init__.py +1 -0
  15. apps/integrations/__init__.py +1 -0
  16. apps/integrations/admin.py +6 -0
  17. apps/integrations/apps.py +8 -0
  18. apps/integrations/migrations/0001_initial.py +29 -0
  19. apps/integrations/migrations/__init__.py +0 -0
  20. apps/integrations/models.py +16 -0
  21. apps/integrations/services.py +31 -0
  22. apps/mcp/__init__.py +1 -0
  23. apps/mcp/admin.py +20 -0
  24. apps/mcp/apps.py +8 -0
  25. apps/mcp/migrations/0001_initial.py +168 -0
  26. apps/mcp/migrations/__init__.py +0 -0
  27. apps/mcp/models.py +154 -0
  28. apps/mcp/services.py +327 -0
  29. apps/orders/__init__.py +1 -0
  30. apps/orders/admin.py +6 -0
  31. apps/orders/apps.py +8 -0
  32. apps/orders/migrations/0001_initial.py +79 -0
  33. apps/orders/migrations/__init__.py +0 -0
  34. apps/orders/models.py +66 -0
  35. apps/orders/services.py +107 -0
  36. apps/policy/__init__.py +1 -0
  37. apps/policy/admin.py +6 -0
  38. apps/policy/apps.py +8 -0
  39. apps/policy/migrations/0001_initial.py +75 -0
  40. apps/policy/migrations/__init__.py +0 -0
  41. apps/policy/models.py +61 -0
  42. apps/policy/services.py +110 -0
  43. apps/portfolio/__init__.py +1 -0
  44. apps/portfolio/admin.py +6 -0
  45. apps/portfolio/apps.py +8 -0
  46. apps/portfolio/migrations/0001_initial.py +67 -0
  47. apps/portfolio/migrations/__init__.py +0 -0
  48. apps/portfolio/models.py +53 -0
  49. apps/research/__init__.py +1 -0
  50. apps/research/admin.py +1 -0
  51. apps/research/apps.py +8 -0
  52. apps/research/migrations/__init__.py +0 -0
  53. apps/research/models.py +1 -0
  54. apps/workflows/__init__.py +1 -0
  55. apps/workflows/admin.py +6 -0
  56. apps/workflows/apps.py +8 -0
  57. apps/workflows/migrations/0001_initial.py +51 -0
  58. apps/workflows/migrations/__init__.py +0 -0
  59. apps/workflows/models.py +44 -0
  60. tradingcodex-0.1.0.dist-info/METADATA +337 -0
  61. tradingcodex-0.1.0.dist-info/RECORD +254 -0
  62. tradingcodex-0.1.0.dist-info/WHEEL +5 -0
  63. tradingcodex-0.1.0.dist-info/entry_points.txt +2 -0
  64. tradingcodex-0.1.0.dist-info/licenses/LICENSE +202 -0
  65. tradingcodex-0.1.0.dist-info/licenses/NOTICE +24 -0
  66. tradingcodex-0.1.0.dist-info/top_level.txt +4 -0
  67. tradingcodex_cli/__init__.py +1 -0
  68. tradingcodex_cli/__main__.py +124 -0
  69. tradingcodex_cli/commands/__init__.py +2 -0
  70. tradingcodex_cli/commands/bootstrap.py +157 -0
  71. tradingcodex_cli/commands/db.py +36 -0
  72. tradingcodex_cli/commands/doctor.py +230 -0
  73. tradingcodex_cli/commands/mcp.py +186 -0
  74. tradingcodex_cli/commands/orders.py +89 -0
  75. tradingcodex_cli/commands/policy.py +21 -0
  76. tradingcodex_cli/commands/profile.py +110 -0
  77. tradingcodex_cli/commands/research.py +76 -0
  78. tradingcodex_cli/commands/skills.py +93 -0
  79. tradingcodex_cli/commands/strategies.py +67 -0
  80. tradingcodex_cli/commands/subagents.py +106 -0
  81. tradingcodex_cli/commands/utils.py +134 -0
  82. tradingcodex_cli/commands/workspaces.py +53 -0
  83. tradingcodex_cli/generator.py +234 -0
  84. tradingcodex_cli/mcp_stdio.py +26 -0
  85. tradingcodex_cli/service_autostart.py +127 -0
  86. tradingcodex_service/__init__.py +5 -0
  87. tradingcodex_service/admin.py +1 -0
  88. tradingcodex_service/api.py +486 -0
  89. tradingcodex_service/application/__init__.py +2 -0
  90. tradingcodex_service/application/agents.py +1470 -0
  91. tradingcodex_service/application/audit.py +71 -0
  92. tradingcodex_service/application/common.py +88 -0
  93. tradingcodex_service/application/components.py +299 -0
  94. tradingcodex_service/application/harness.py +747 -0
  95. tradingcodex_service/application/markdown_preview.py +179 -0
  96. tradingcodex_service/application/orders.py +404 -0
  97. tradingcodex_service/application/policy.py +150 -0
  98. tradingcodex_service/application/portfolio.py +166 -0
  99. tradingcodex_service/application/research.py +356 -0
  100. tradingcodex_service/application/runtime.py +321 -0
  101. tradingcodex_service/asgi.py +6 -0
  102. tradingcodex_service/mcp_http.py +59 -0
  103. tradingcodex_service/mcp_runtime.py +565 -0
  104. tradingcodex_service/settings.py +91 -0
  105. tradingcodex_service/templates/web/activity.html +24 -0
  106. tradingcodex_service/templates/web/agent_skills.html +111 -0
  107. tradingcodex_service/templates/web/agents.html +121 -0
  108. tradingcodex_service/templates/web/base.html +150 -0
  109. tradingcodex_service/templates/web/dashboard.html +109 -0
  110. tradingcodex_service/templates/web/fragments/role_inspector.html +81 -0
  111. tradingcodex_service/templates/web/fragments/starter_prompt.html +24 -0
  112. tradingcodex_service/templates/web/fragments/topology_canvas.html +85 -0
  113. tradingcodex_service/templates/web/harness.html +87 -0
  114. tradingcodex_service/templates/web/mcp_router.html +250 -0
  115. tradingcodex_service/templates/web/orders.html +68 -0
  116. tradingcodex_service/templates/web/policy.html +81 -0
  117. tradingcodex_service/templates/web/portfolio.html +52 -0
  118. tradingcodex_service/templates/web/research.html +73 -0
  119. tradingcodex_service/templates/web/starter_prompt.html +40 -0
  120. tradingcodex_service/templates/web/strategies.html +74 -0
  121. tradingcodex_service/urls.py +42 -0
  122. tradingcodex_service/version.py +1 -0
  123. tradingcodex_service/web.py +885 -0
  124. tradingcodex_service/wsgi.py +6 -0
  125. workspace_templates/__init__.py +1 -0
  126. workspace_templates/modules/audit/files/.tradingcodex/audit/README.md +5 -0
  127. workspace_templates/modules/audit/files/trading/audit/.gitkeep +1 -0
  128. workspace_templates/modules/audit/module.json +16 -0
  129. workspace_templates/modules/codex-base/files/.codex/config.toml +272 -0
  130. workspace_templates/modules/codex-base/files/.codex/hooks/tradingcodex_hook.py +173 -0
  131. workspace_templates/modules/codex-base/files/.codex/hooks.json +105 -0
  132. workspace_templates/modules/codex-base/files/.codex/prompts/base_instructions/head-manager.md +129 -0
  133. workspace_templates/modules/codex-base/files/.codex/rules/tradingcodex.rules +50 -0
  134. workspace_templates/modules/codex-base/files/.tradingcodex/capabilities.yaml +56 -0
  135. workspace_templates/modules/codex-base/files/.tradingcodex/cli.py +16 -0
  136. workspace_templates/modules/codex-base/files/.tradingcodex/config.yaml +53 -0
  137. workspace_templates/modules/codex-base/files/.tradingcodex/policies/policy-bindings.yaml +16 -0
  138. workspace_templates/modules/codex-base/files/.tradingcodex/policies/principals.yaml +17 -0
  139. workspace_templates/modules/codex-base/files/.tradingcodex/policies/roles.yaml +27 -0
  140. workspace_templates/modules/codex-base/files/AGENTS.md +56 -0
  141. workspace_templates/modules/codex-base/files/pyproject.toml +13 -0
  142. workspace_templates/modules/codex-base/files/tcx +35 -0
  143. workspace_templates/modules/codex-base/module.json +17 -0
  144. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/policies/access-policies.yaml +39 -0
  145. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/policies/restricted-list.yaml +6 -0
  146. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/approval_receipt.schema.json +14 -0
  147. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/audit_event.schema.json +11 -0
  148. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/evidence_pack.schema.json +16 -0
  149. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/execution_result.schema.json +12 -0
  150. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/fundamental_report.schema.json +15 -0
  151. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/news_report.schema.json +15 -0
  152. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/order_intent.schema.json +30 -0
  153. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/portfolio_review.schema.json +15 -0
  154. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/postmortem_report.schema.json +14 -0
  155. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/risk_report.schema.json +15 -0
  156. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/technical_report.schema.json +15 -0
  157. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/thesis.schema.json +15 -0
  158. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/valuation.schema.json +15 -0
  159. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/scripts/validate-order-intent.py +15 -0
  160. workspace_templates/modules/enforcement-guardrails/module.json +19 -0
  161. workspace_templates/modules/fixed-subagents/files/.codex/agents/execution-operator.toml +71 -0
  162. workspace_templates/modules/fixed-subagents/files/.codex/agents/fundamental-analyst.toml +62 -0
  163. workspace_templates/modules/fixed-subagents/files/.codex/agents/instrument-analyst.toml +64 -0
  164. workspace_templates/modules/fixed-subagents/files/.codex/agents/macro-analyst.toml +64 -0
  165. workspace_templates/modules/fixed-subagents/files/.codex/agents/news-analyst.toml +63 -0
  166. workspace_templates/modules/fixed-subagents/files/.codex/agents/portfolio-manager.toml +62 -0
  167. workspace_templates/modules/fixed-subagents/files/.codex/agents/risk-manager.toml +65 -0
  168. workspace_templates/modules/fixed-subagents/files/.codex/agents/technical-analyst.toml +63 -0
  169. workspace_templates/modules/fixed-subagents/files/.codex/agents/valuation-analyst.toml +59 -0
  170. workspace_templates/modules/fixed-subagents/files/.tradingcodex/mainagent/head-manager.yaml +66 -0
  171. workspace_templates/modules/fixed-subagents/files/.tradingcodex/mainagent/skill-change-proposals/.gitkeep +1 -0
  172. workspace_templates/modules/fixed-subagents/files/.tradingcodex/mainagent/subagent-registry.yaml +56 -0
  173. workspace_templates/modules/fixed-subagents/module.json +23 -0
  174. workspace_templates/modules/guidance-guardrails/files/.tradingcodex/guidance/guardrails.md +17 -0
  175. workspace_templates/modules/guidance-guardrails/files/.tradingcodex/guidance/task-quality-checklist.md +37 -0
  176. workspace_templates/modules/guidance-guardrails/module.json +18 -0
  177. workspace_templates/modules/information-barriers/files/.tradingcodex/policies/information-barriers.yaml +211 -0
  178. workspace_templates/modules/information-barriers/files/.tradingcodex/secrets.md +9 -0
  179. workspace_templates/modules/information-barriers/files/trading/approvals/.gitkeep +1 -0
  180. workspace_templates/modules/information-barriers/files/trading/market-data/.gitkeep +1 -0
  181. workspace_templates/modules/information-barriers/files/trading/orders/approved/.gitkeep +1 -0
  182. workspace_templates/modules/information-barriers/files/trading/orders/draft/.gitkeep +1 -0
  183. workspace_templates/modules/information-barriers/files/trading/orders/executed/.gitkeep +1 -0
  184. workspace_templates/modules/information-barriers/files/trading/orders/rejected/.gitkeep +1 -0
  185. workspace_templates/modules/information-barriers/files/trading/portfolio/.gitkeep +1 -0
  186. workspace_templates/modules/information-barriers/files/trading/reports/fundamental/.gitkeep +1 -0
  187. workspace_templates/modules/information-barriers/files/trading/reports/instrument/.gitkeep +1 -0
  188. workspace_templates/modules/information-barriers/files/trading/reports/macro/.gitkeep +1 -0
  189. workspace_templates/modules/information-barriers/files/trading/reports/news/.gitkeep +1 -0
  190. workspace_templates/modules/information-barriers/files/trading/reports/policy/.gitkeep +1 -0
  191. workspace_templates/modules/information-barriers/files/trading/reports/portfolio/.gitkeep +1 -0
  192. workspace_templates/modules/information-barriers/files/trading/reports/postmortem/.gitkeep +1 -0
  193. workspace_templates/modules/information-barriers/files/trading/reports/risk/.gitkeep +1 -0
  194. workspace_templates/modules/information-barriers/files/trading/reports/technical/.gitkeep +1 -0
  195. workspace_templates/modules/information-barriers/files/trading/reports/valuation/.gitkeep +1 -0
  196. workspace_templates/modules/information-barriers/files/trading/research/.gitkeep +1 -0
  197. workspace_templates/modules/information-barriers/module.json +19 -0
  198. workspace_templates/modules/paper-trading/files/.tradingcodex/mcp/adapters/paper-trading.py +4 -0
  199. workspace_templates/modules/paper-trading/module.json +16 -0
  200. workspace_templates/modules/postmortem/files/.tradingcodex/workflows/postmortem.yaml +12 -0
  201. workspace_templates/modules/postmortem/module.json +16 -0
  202. workspace_templates/modules/repo-skills/files/.agents/skills/investment-workflow-map/SKILL.md +106 -0
  203. workspace_templates/modules/repo-skills/files/.agents/skills/investment-workflow-map/agents/openai.yaml +6 -0
  204. workspace_templates/modules/repo-skills/files/.agents/skills/manage-optional-skills/SKILL.md +101 -0
  205. workspace_templates/modules/repo-skills/files/.agents/skills/manage-optional-skills/agents/openai.yaml +6 -0
  206. workspace_templates/modules/repo-skills/files/.agents/skills/manage-subagents/SKILL.md +140 -0
  207. workspace_templates/modules/repo-skills/files/.agents/skills/manage-subagents/agents/openai.yaml +6 -0
  208. workspace_templates/modules/repo-skills/files/.agents/skills/orchestrate-workflow/SKILL.md +140 -0
  209. workspace_templates/modules/repo-skills/files/.agents/skills/orchestrate-workflow/agents/openai.yaml +6 -0
  210. workspace_templates/modules/repo-skills/files/.agents/skills/postmortem/SKILL.md +31 -0
  211. workspace_templates/modules/repo-skills/files/.agents/skills/postmortem/agents/openai.yaml +6 -0
  212. workspace_templates/modules/repo-skills/files/.agents/skills/scenario-quality-gates/SKILL.md +138 -0
  213. workspace_templates/modules/repo-skills/files/.agents/skills/scenario-quality-gates/agents/openai.yaml +6 -0
  214. workspace_templates/modules/repo-skills/files/.agents/skills/strategy-creator/SKILL.md +109 -0
  215. workspace_templates/modules/repo-skills/files/.agents/skills/strategy-creator/agents/openai.yaml +6 -0
  216. workspace_templates/modules/repo-skills/files/.agents/skills/synthesize-decision/SKILL.md +54 -0
  217. workspace_templates/modules/repo-skills/files/.agents/skills/synthesize-decision/agents/openai.yaml +6 -0
  218. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/execution-operator/execute-paper-order/SKILL.md +35 -0
  219. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/execution-operator/execute-paper-order/agents/openai.yaml +6 -0
  220. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/fundamental-analyst/fundamental-analysis/SKILL.md +46 -0
  221. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/fundamental-analyst/fundamental-analysis/agents/openai.yaml +6 -0
  222. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/instrument-analyst/instrument-analysis/SKILL.md +40 -0
  223. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/instrument-analyst/instrument-analysis/agents/openai.yaml +6 -0
  224. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/macro-analyst/macro-analysis/SKILL.md +40 -0
  225. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/macro-analyst/macro-analysis/agents/openai.yaml +6 -0
  226. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/news-analyst/news-analysis/SKILL.md +43 -0
  227. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/news-analyst/news-analysis/agents/openai.yaml +6 -0
  228. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/portfolio-manager/create-order-intent/SKILL.md +46 -0
  229. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/portfolio-manager/create-order-intent/agents/openai.yaml +6 -0
  230. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/portfolio-manager/portfolio-review/SKILL.md +44 -0
  231. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/portfolio-manager/portfolio-review/agents/openai.yaml +6 -0
  232. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/approve-order/SKILL.md +38 -0
  233. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/approve-order/agents/openai.yaml +6 -0
  234. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/policy-review/SKILL.md +43 -0
  235. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/policy-review/agents/openai.yaml +6 -0
  236. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/review-risk/SKILL.md +45 -0
  237. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/review-risk/agents/openai.yaml +6 -0
  238. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/shared/collect-evidence/SKILL.md +46 -0
  239. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/shared/collect-evidence/agents/openai.yaml +6 -0
  240. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/shared/external-data-source-gate/SKILL.md +66 -0
  241. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/shared/external-data-source-gate/agents/openai.yaml +6 -0
  242. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/technical-analyst/technical-analysis/SKILL.md +43 -0
  243. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/technical-analyst/technical-analysis/agents/openai.yaml +6 -0
  244. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/valuation-analyst/valuation-review/SKILL.md +47 -0
  245. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/valuation-analyst/valuation-review/agents/openai.yaml +6 -0
  246. workspace_templates/modules/repo-skills/module.json +37 -0
  247. workspace_templates/modules/stub-execution/files/.tradingcodex/mcp/adapters/stub-execution.py +4 -0
  248. workspace_templates/modules/stub-execution/module.json +15 -0
  249. workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/adapters/live-adapter.contract.md +25 -0
  250. workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/enforcer/README.md +5 -0
  251. workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/gateway/README.md +8 -0
  252. workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/server.py +27 -0
  253. workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/smoke-call.py +18 -0
  254. workspace_templates/modules/tradingcodex-mcp/module.json +24 -0
apps/__init__.py ADDED
@@ -0,0 +1 @@
1
+
apps/audit/__init__.py ADDED
@@ -0,0 +1 @@
1
+
apps/audit/admin.py ADDED
@@ -0,0 +1,6 @@
1
+ from django.contrib import admin
2
+
3
+ from apps.audit.models import AuditEvent
4
+
5
+
6
+ admin.site.register(AuditEvent)
apps/audit/apps.py ADDED
@@ -0,0 +1,8 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class AuditConfig(AppConfig):
5
+ default_auto_field = "django.db.models.BigAutoField"
6
+ name = "apps.audit"
7
+ label = "audit"
8
+ verbose_name = "Audit Ledger"
@@ -0,0 +1,35 @@
1
+ # Generated by Django 5.2.5 on 2026-06-12 16:32
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ initial = True
9
+
10
+ dependencies = [
11
+ ]
12
+
13
+ operations = [
14
+ migrations.CreateModel(
15
+ name='AuditEvent',
16
+ fields=[
17
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18
+ ('created_at', models.DateTimeField(auto_now_add=True)),
19
+ ('actor_principal', models.CharField(default='system', max_length=128)),
20
+ ('source', models.CharField(default='service', max_length=32)),
21
+ ('action', models.CharField(max_length=160)),
22
+ ('resource', models.CharField(blank=True, max_length=255)),
23
+ ('decision', models.CharField(default='recorded', max_length=32)),
24
+ ('request_hash', models.CharField(blank=True, max_length=64)),
25
+ ('result_hash', models.CharField(blank=True, max_length=64)),
26
+ ('workspace_context', models.JSONField(blank=True, default=dict)),
27
+ ('payload', models.JSONField(blank=True, default=dict)),
28
+ ],
29
+ options={
30
+ 'verbose_name': 'Audit event',
31
+ 'verbose_name_plural': 'Audit events',
32
+ 'ordering': ['-created_at', '-id'],
33
+ },
34
+ ),
35
+ ]
File without changes
apps/audit/models.py ADDED
@@ -0,0 +1,22 @@
1
+ from django.db import models
2
+
3
+
4
+ class AuditEvent(models.Model):
5
+ created_at = models.DateTimeField(auto_now_add=True)
6
+ actor_principal = models.CharField(max_length=128, default="system")
7
+ source = models.CharField(max_length=32, default="service")
8
+ action = models.CharField(max_length=160)
9
+ resource = models.CharField(max_length=255, blank=True)
10
+ decision = models.CharField(max_length=32, default="recorded")
11
+ request_hash = models.CharField(max_length=64, blank=True)
12
+ result_hash = models.CharField(max_length=64, blank=True)
13
+ workspace_context = models.JSONField(default=dict, blank=True)
14
+ payload = models.JSONField(default=dict, blank=True)
15
+
16
+ class Meta:
17
+ ordering = ["-created_at", "-id"]
18
+ verbose_name = "Audit event"
19
+ verbose_name_plural = "Audit events"
20
+
21
+ def __str__(self) -> str:
22
+ return f"{self.created_at:%Y-%m-%d %H:%M:%S} {self.action}"
@@ -0,0 +1 @@
1
+
apps/harness/admin.py ADDED
@@ -0,0 +1,6 @@
1
+ from django.contrib import admin
2
+
3
+ from apps.harness.models import WorkspaceContext
4
+
5
+
6
+ admin.site.register(WorkspaceContext)
apps/harness/apps.py ADDED
@@ -0,0 +1,8 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class HarnessConfig(AppConfig):
5
+ default_auto_field = "django.db.models.BigAutoField"
6
+ name = "apps.harness"
7
+ label = "harness"
8
+ verbose_name = "Harness Operations"
@@ -0,0 +1,35 @@
1
+ # Generated by Django 5.2.5 on 2026-06-12 16:32
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ initial = True
9
+
10
+ dependencies = [
11
+ ]
12
+
13
+ operations = [
14
+ migrations.CreateModel(
15
+ name='WorkspaceContext',
16
+ fields=[
17
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18
+ ('workspace_id', models.CharField(max_length=80, unique=True)),
19
+ ('path_hash', models.CharField(max_length=64, unique=True)),
20
+ ('project_name', models.CharField(max_length=180)),
21
+ ('path', models.CharField(max_length=1024)),
22
+ ('git_remote', models.CharField(blank=True, max_length=512)),
23
+ ('git_branch', models.CharField(blank=True, max_length=180)),
24
+ ('active_profile', models.JSONField(blank=True, default=dict)),
25
+ ('metadata', models.JSONField(blank=True, default=dict)),
26
+ ('created_at', models.DateTimeField(auto_now_add=True)),
27
+ ('last_seen_at', models.DateTimeField(auto_now=True)),
28
+ ],
29
+ options={
30
+ 'verbose_name': 'Workspace context',
31
+ 'verbose_name_plural': 'Workspace contexts',
32
+ 'ordering': ['project_name', 'id'],
33
+ },
34
+ ),
35
+ ]
File without changes
apps/harness/models.py ADDED
@@ -0,0 +1,22 @@
1
+ from django.db import models
2
+
3
+
4
+ class WorkspaceContext(models.Model):
5
+ workspace_id = models.CharField(max_length=80, unique=True)
6
+ path_hash = models.CharField(max_length=64, unique=True)
7
+ project_name = models.CharField(max_length=180)
8
+ path = models.CharField(max_length=1024)
9
+ git_remote = models.CharField(max_length=512, blank=True)
10
+ git_branch = models.CharField(max_length=180, blank=True)
11
+ active_profile = models.JSONField(default=dict, blank=True)
12
+ metadata = models.JSONField(default=dict, blank=True)
13
+ created_at = models.DateTimeField(auto_now_add=True)
14
+ last_seen_at = models.DateTimeField(auto_now=True)
15
+
16
+ class Meta:
17
+ ordering = ["project_name", "id"]
18
+ verbose_name = "Workspace context"
19
+ verbose_name_plural = "Workspace contexts"
20
+
21
+ def __str__(self) -> str:
22
+ return f"{self.project_name} {self.workspace_id}"
@@ -0,0 +1 @@
1
+
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,6 @@
1
+ from django.contrib import admin
2
+
3
+ from apps.integrations.models import AdapterDefinition
4
+
5
+
6
+ admin.site.register(AdapterDefinition)
@@ -0,0 +1,8 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class IntegrationsConfig(AppConfig):
5
+ default_auto_field = "django.db.models.BigAutoField"
6
+ name = "apps.integrations"
7
+ label = "integrations"
8
+ verbose_name = "Integrations"
@@ -0,0 +1,29 @@
1
+ # Generated by Django 5.2.5 on 2026-06-12 16:32
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ initial = True
9
+
10
+ dependencies = [
11
+ ]
12
+
13
+ operations = [
14
+ migrations.CreateModel(
15
+ name='AdapterDefinition',
16
+ fields=[
17
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18
+ ('adapter_id', models.CharField(max_length=120, unique=True)),
19
+ ('kind', models.CharField(default='execution', max_length=64)),
20
+ ('enabled', models.BooleanField(default=False)),
21
+ ('live', models.BooleanField(default=False)),
22
+ ('config', models.JSONField(blank=True, default=dict)),
23
+ ],
24
+ options={
25
+ 'verbose_name': 'Adapter definition',
26
+ 'verbose_name_plural': 'Adapter definitions',
27
+ },
28
+ ),
29
+ ]
File without changes
@@ -0,0 +1,16 @@
1
+ from django.db import models
2
+
3
+
4
+ class AdapterDefinition(models.Model):
5
+ adapter_id = models.CharField(max_length=120, unique=True)
6
+ kind = models.CharField(max_length=64, default="execution")
7
+ enabled = models.BooleanField(default=False)
8
+ live = models.BooleanField(default=False)
9
+ config = models.JSONField(default=dict, blank=True)
10
+
11
+ class Meta:
12
+ verbose_name = "Adapter definition"
13
+ verbose_name_plural = "Adapter definitions"
14
+
15
+ def __str__(self) -> str:
16
+ return self.adapter_id
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from django.db.models import QuerySet
6
+
7
+ from apps.integrations.models import AdapterDefinition
8
+
9
+
10
+ def enable_non_live_adapters(queryset: QuerySet[AdapterDefinition], actor: str = "admin") -> int:
11
+ count = queryset.filter(live=False).update(enabled=True)
12
+ _audit("adapter.enabled_non_live", {"count": count}, actor)
13
+ return count
14
+
15
+
16
+ def disable_adapters(queryset: QuerySet[AdapterDefinition], actor: str = "admin") -> int:
17
+ count = queryset.update(enabled=False)
18
+ _audit("adapter.disabled", {"count": count}, actor)
19
+ return count
20
+
21
+
22
+ def disable_live_adapters(queryset: QuerySet[AdapterDefinition], actor: str = "admin") -> int:
23
+ count = queryset.filter(live=True).update(enabled=False)
24
+ _audit("adapter.live_disabled", {"count": count}, actor)
25
+ return count
26
+
27
+
28
+ def _audit(action: str, payload: dict[str, Any], actor: str) -> None:
29
+ from tradingcodex_service.application.audit import write_audit_event_if_available
30
+
31
+ write_audit_event_if_available(None, actor, "admin", {"type": action, "payload": payload})
apps/mcp/__init__.py ADDED
@@ -0,0 +1 @@
1
+
apps/mcp/admin.py ADDED
@@ -0,0 +1,20 @@
1
+ from django.contrib import admin
2
+
3
+ from apps.mcp.models import (
4
+ McpExternalTool,
5
+ McpExternalToolCall,
6
+ McpExternalToolPermission,
7
+ McpRouter,
8
+ McpToolCall,
9
+ McpToolDefinition,
10
+ )
11
+
12
+
13
+ admin.site.register([
14
+ McpToolDefinition,
15
+ McpToolCall,
16
+ McpRouter,
17
+ McpExternalTool,
18
+ McpExternalToolPermission,
19
+ McpExternalToolCall,
20
+ ])
apps/mcp/apps.py ADDED
@@ -0,0 +1,8 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class McpConfig(AppConfig):
5
+ default_auto_field = "django.db.models.BigAutoField"
6
+ name = "apps.mcp"
7
+ label = "mcp"
8
+ verbose_name = "MCP Gateway"
@@ -0,0 +1,168 @@
1
+ # Generated by Django 5.2.5 on 2026-06-12 16:32
2
+
3
+ import django.db.models.deletion
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ initial = True
10
+
11
+ dependencies = [
12
+ ]
13
+
14
+ operations = [
15
+ migrations.CreateModel(
16
+ name='McpExternalTool',
17
+ fields=[
18
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19
+ ('primitive', models.CharField(default='tool', max_length=32)),
20
+ ('external_name', models.CharField(max_length=200)),
21
+ ('description', models.TextField(blank=True)),
22
+ ('input_schema', models.JSONField(blank=True, default=dict)),
23
+ ('output_schema', models.JSONField(blank=True, default=dict)),
24
+ ('schema_hash', models.CharField(blank=True, max_length=64)),
25
+ ('category', models.CharField(default='unknown', max_length=64)),
26
+ ('risk_level', models.CharField(default='unknown', max_length=32)),
27
+ ('sensitivity', models.CharField(default='unknown', max_length=32)),
28
+ ('canonical_capability', models.CharField(blank=True, max_length=160)),
29
+ ('proxy_mode', models.CharField(default='blocked', max_length=32)),
30
+ ('allowed_roles', models.JSONField(blank=True, default=list)),
31
+ ('conditions', models.JSONField(blank=True, default=dict)),
32
+ ('enabled', models.BooleanField(default=False)),
33
+ ('review_status', models.CharField(default='review_required', max_length=32)),
34
+ ('drift_detected', models.BooleanField(default=False)),
35
+ ('last_seen_at', models.DateTimeField(blank=True, null=True)),
36
+ ('created_at', models.DateTimeField(auto_now_add=True)),
37
+ ('updated_at', models.DateTimeField(auto_now=True)),
38
+ ],
39
+ options={
40
+ 'verbose_name': 'external MCP tool',
41
+ 'verbose_name_plural': 'external MCP tools',
42
+ 'ordering': ['router__name', 'primitive', 'external_name'],
43
+ },
44
+ ),
45
+ migrations.CreateModel(
46
+ name='McpRouter',
47
+ fields=[
48
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
49
+ ('name', models.CharField(max_length=160, unique=True)),
50
+ ('label', models.CharField(blank=True, max_length=160)),
51
+ ('transport', models.CharField(default='stdio', max_length=32)),
52
+ ('command', models.TextField(blank=True)),
53
+ ('url', models.URLField(blank=True)),
54
+ ('args', models.JSONField(blank=True, default=list)),
55
+ ('env', models.JSONField(blank=True, default=dict)),
56
+ ('credential_ref', models.CharField(blank=True, max_length=255)),
57
+ ('trust_level', models.CharField(default='unreviewed', max_length=32)),
58
+ ('enabled', models.BooleanField(default=False)),
59
+ ('last_status', models.CharField(default='not_checked', max_length=32)),
60
+ ('last_error', models.TextField(blank=True)),
61
+ ('last_checked_at', models.DateTimeField(blank=True, null=True)),
62
+ ('created_at', models.DateTimeField(auto_now_add=True)),
63
+ ('updated_at', models.DateTimeField(auto_now=True)),
64
+ ],
65
+ options={
66
+ 'verbose_name': 'MCP router',
67
+ 'verbose_name_plural': 'MCP routers',
68
+ 'ordering': ['name'],
69
+ },
70
+ ),
71
+ migrations.CreateModel(
72
+ name='McpToolCall',
73
+ fields=[
74
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
75
+ ('created_at', models.DateTimeField(auto_now_add=True)),
76
+ ('tool_name', models.CharField(max_length=160)),
77
+ ('principal_id', models.CharField(default='unknown', max_length=128)),
78
+ ('status', models.CharField(default='recorded', max_length=32)),
79
+ ('request', models.JSONField(blank=True, default=dict)),
80
+ ('response', models.JSONField(blank=True, default=dict)),
81
+ ('workspace_context', models.JSONField(blank=True, default=dict)),
82
+ ('request_hash', models.CharField(blank=True, max_length=64)),
83
+ ('result_hash', models.CharField(blank=True, max_length=64)),
84
+ ('error', models.TextField(blank=True)),
85
+ ('duration_ms', models.PositiveIntegerField(default=0)),
86
+ ],
87
+ options={
88
+ 'verbose_name': 'MCP tool call',
89
+ 'verbose_name_plural': 'MCP tool calls',
90
+ 'ordering': ['-created_at', '-id'],
91
+ },
92
+ ),
93
+ migrations.CreateModel(
94
+ name='McpToolDefinition',
95
+ fields=[
96
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
97
+ ('name', models.CharField(max_length=160, unique=True)),
98
+ ('description', models.TextField(blank=True)),
99
+ ('category', models.CharField(default='general', max_length=64)),
100
+ ('capability_required', models.CharField(blank=True, max_length=160)),
101
+ ('input_schema', models.JSONField(blank=True, default=dict)),
102
+ ('risk_level', models.CharField(default='read', max_length=32)),
103
+ ('allowed_roles', models.JSONField(blank=True, default=list)),
104
+ ('requires_approval', models.BooleanField(default=False)),
105
+ ('audit_required', models.BooleanField(default=True)),
106
+ ('experimental', models.BooleanField(default=False)),
107
+ ('enabled', models.BooleanField(default=True)),
108
+ ('updated_at', models.DateTimeField(auto_now=True)),
109
+ ],
110
+ options={
111
+ 'verbose_name': 'MCP tool definition',
112
+ 'verbose_name_plural': 'MCP tool definitions',
113
+ },
114
+ ),
115
+ migrations.CreateModel(
116
+ name='McpExternalToolCall',
117
+ fields=[
118
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
119
+ ('created_at', models.DateTimeField(auto_now_add=True)),
120
+ ('router_name', models.CharField(max_length=160)),
121
+ ('external_name', models.CharField(max_length=200)),
122
+ ('principal_id', models.CharField(default='unknown', max_length=128)),
123
+ ('proxy_mode', models.CharField(default='blocked', max_length=32)),
124
+ ('decision', models.CharField(default='denied', max_length=32)),
125
+ ('reasons', models.JSONField(blank=True, default=list)),
126
+ ('request', models.JSONField(blank=True, default=dict)),
127
+ ('response', models.JSONField(blank=True, default=dict)),
128
+ ('request_hash', models.CharField(blank=True, max_length=64)),
129
+ ('result_hash', models.CharField(blank=True, max_length=64)),
130
+ ('workspace_context', models.JSONField(blank=True, default=dict)),
131
+ ('external_tool', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='calls', to='mcp.mcpexternaltool')),
132
+ ],
133
+ options={
134
+ 'verbose_name': 'external MCP tool call',
135
+ 'verbose_name_plural': 'external MCP tool calls',
136
+ 'ordering': ['-created_at', '-id'],
137
+ },
138
+ ),
139
+ migrations.AddField(
140
+ model_name='mcpexternaltool',
141
+ name='router',
142
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='external_tools', to='mcp.mcprouter'),
143
+ ),
144
+ migrations.CreateModel(
145
+ name='McpExternalToolPermission',
146
+ fields=[
147
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
148
+ ('principal_or_role', models.CharField(max_length=128)),
149
+ ('capability', models.CharField(blank=True, max_length=160)),
150
+ ('decision', models.CharField(default='allow', max_length=16)),
151
+ ('conditions', models.JSONField(blank=True, default=dict)),
152
+ ('enabled', models.BooleanField(default=True)),
153
+ ('created_at', models.DateTimeField(auto_now_add=True)),
154
+ ('updated_at', models.DateTimeField(auto_now=True)),
155
+ ('external_tool', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='permissions', to='mcp.mcpexternaltool')),
156
+ ],
157
+ options={
158
+ 'verbose_name': 'external MCP permission',
159
+ 'verbose_name_plural': 'external MCP permissions',
160
+ 'ordering': ['external_tool__external_name', 'principal_or_role'],
161
+ 'constraints': [models.UniqueConstraint(fields=('external_tool', 'principal_or_role', 'capability'), name='unique_external_mcp_permission')],
162
+ },
163
+ ),
164
+ migrations.AddConstraint(
165
+ model_name='mcpexternaltool',
166
+ constraint=models.UniqueConstraint(fields=('router', 'primitive', 'external_name'), name='unique_external_mcp_primitive'),
167
+ ),
168
+ ]
File without changes
apps/mcp/models.py ADDED
@@ -0,0 +1,154 @@
1
+ from django.db import models
2
+
3
+
4
+ class McpToolDefinition(models.Model):
5
+ name = models.CharField(max_length=160, unique=True)
6
+ description = models.TextField(blank=True)
7
+ category = models.CharField(max_length=64, default="general")
8
+ capability_required = models.CharField(max_length=160, blank=True)
9
+ input_schema = models.JSONField(default=dict, blank=True)
10
+ risk_level = models.CharField(max_length=32, default="read")
11
+ allowed_roles = models.JSONField(default=list, blank=True)
12
+ requires_approval = models.BooleanField(default=False)
13
+ audit_required = models.BooleanField(default=True)
14
+ experimental = models.BooleanField(default=False)
15
+ enabled = models.BooleanField(default=True)
16
+ updated_at = models.DateTimeField(auto_now=True)
17
+
18
+ class Meta:
19
+ verbose_name = "MCP tool definition"
20
+ verbose_name_plural = "MCP tool definitions"
21
+
22
+ def __str__(self) -> str:
23
+ return self.name
24
+
25
+
26
+ class McpToolCall(models.Model):
27
+ created_at = models.DateTimeField(auto_now_add=True)
28
+ tool_name = models.CharField(max_length=160)
29
+ principal_id = models.CharField(max_length=128, default="unknown")
30
+ status = models.CharField(max_length=32, default="recorded")
31
+ request = models.JSONField(default=dict, blank=True)
32
+ response = models.JSONField(default=dict, blank=True)
33
+ workspace_context = models.JSONField(default=dict, blank=True)
34
+ request_hash = models.CharField(max_length=64, blank=True)
35
+ result_hash = models.CharField(max_length=64, blank=True)
36
+ error = models.TextField(blank=True)
37
+ duration_ms = models.PositiveIntegerField(default=0)
38
+
39
+ class Meta:
40
+ ordering = ["-created_at", "-id"]
41
+ verbose_name = "MCP tool call"
42
+ verbose_name_plural = "MCP tool calls"
43
+
44
+ def __str__(self) -> str:
45
+ return f"{self.tool_name} {self.status}"
46
+
47
+
48
+ class McpRouter(models.Model):
49
+ name = models.CharField(max_length=160, unique=True)
50
+ label = models.CharField(max_length=160, blank=True)
51
+ transport = models.CharField(max_length=32, default="stdio")
52
+ command = models.TextField(blank=True)
53
+ url = models.URLField(blank=True)
54
+ args = models.JSONField(default=list, blank=True)
55
+ env = models.JSONField(default=dict, blank=True)
56
+ credential_ref = models.CharField(max_length=255, blank=True)
57
+ trust_level = models.CharField(max_length=32, default="unreviewed")
58
+ enabled = models.BooleanField(default=False)
59
+ last_status = models.CharField(max_length=32, default="not_checked")
60
+ last_error = models.TextField(blank=True)
61
+ last_checked_at = models.DateTimeField(null=True, blank=True)
62
+ created_at = models.DateTimeField(auto_now_add=True)
63
+ updated_at = models.DateTimeField(auto_now=True)
64
+
65
+ class Meta:
66
+ ordering = ["name"]
67
+ verbose_name = "MCP router"
68
+ verbose_name_plural = "MCP routers"
69
+
70
+ def __str__(self) -> str:
71
+ return self.label or self.name
72
+
73
+
74
+ class McpExternalTool(models.Model):
75
+ router = models.ForeignKey(McpRouter, on_delete=models.CASCADE, related_name="external_tools")
76
+ primitive = models.CharField(max_length=32, default="tool")
77
+ external_name = models.CharField(max_length=200)
78
+ description = models.TextField(blank=True)
79
+ input_schema = models.JSONField(default=dict, blank=True)
80
+ output_schema = models.JSONField(default=dict, blank=True)
81
+ schema_hash = models.CharField(max_length=64, blank=True)
82
+ category = models.CharField(max_length=64, default="unknown")
83
+ risk_level = models.CharField(max_length=32, default="unknown")
84
+ sensitivity = models.CharField(max_length=32, default="unknown")
85
+ canonical_capability = models.CharField(max_length=160, blank=True)
86
+ proxy_mode = models.CharField(max_length=32, default="blocked")
87
+ allowed_roles = models.JSONField(default=list, blank=True)
88
+ conditions = models.JSONField(default=dict, blank=True)
89
+ enabled = models.BooleanField(default=False)
90
+ review_status = models.CharField(max_length=32, default="review_required")
91
+ drift_detected = models.BooleanField(default=False)
92
+ last_seen_at = models.DateTimeField(null=True, blank=True)
93
+ created_at = models.DateTimeField(auto_now_add=True)
94
+ updated_at = models.DateTimeField(auto_now=True)
95
+
96
+ class Meta:
97
+ ordering = ["router__name", "primitive", "external_name"]
98
+ constraints = [
99
+ models.UniqueConstraint(fields=["router", "primitive", "external_name"], name="unique_external_mcp_primitive")
100
+ ]
101
+ verbose_name = "external MCP tool"
102
+ verbose_name_plural = "external MCP tools"
103
+
104
+ def __str__(self) -> str:
105
+ return f"{self.router.name}:{self.external_name}"
106
+
107
+
108
+ class McpExternalToolPermission(models.Model):
109
+ external_tool = models.ForeignKey(McpExternalTool, on_delete=models.CASCADE, related_name="permissions")
110
+ principal_or_role = models.CharField(max_length=128)
111
+ capability = models.CharField(max_length=160, blank=True)
112
+ decision = models.CharField(max_length=16, default="allow")
113
+ conditions = models.JSONField(default=dict, blank=True)
114
+ enabled = models.BooleanField(default=True)
115
+ created_at = models.DateTimeField(auto_now_add=True)
116
+ updated_at = models.DateTimeField(auto_now=True)
117
+
118
+ class Meta:
119
+ ordering = ["external_tool__external_name", "principal_or_role"]
120
+ constraints = [
121
+ models.UniqueConstraint(
122
+ fields=["external_tool", "principal_or_role", "capability"],
123
+ name="unique_external_mcp_permission",
124
+ )
125
+ ]
126
+ verbose_name = "external MCP permission"
127
+ verbose_name_plural = "external MCP permissions"
128
+
129
+ def __str__(self) -> str:
130
+ return f"{self.external_tool.external_name} {self.principal_or_role} {self.decision}"
131
+
132
+
133
+ class McpExternalToolCall(models.Model):
134
+ created_at = models.DateTimeField(auto_now_add=True)
135
+ external_tool = models.ForeignKey(McpExternalTool, on_delete=models.SET_NULL, null=True, blank=True, related_name="calls")
136
+ router_name = models.CharField(max_length=160)
137
+ external_name = models.CharField(max_length=200)
138
+ principal_id = models.CharField(max_length=128, default="unknown")
139
+ proxy_mode = models.CharField(max_length=32, default="blocked")
140
+ decision = models.CharField(max_length=32, default="denied")
141
+ reasons = models.JSONField(default=list, blank=True)
142
+ request = models.JSONField(default=dict, blank=True)
143
+ response = models.JSONField(default=dict, blank=True)
144
+ request_hash = models.CharField(max_length=64, blank=True)
145
+ result_hash = models.CharField(max_length=64, blank=True)
146
+ workspace_context = models.JSONField(default=dict, blank=True)
147
+
148
+ class Meta:
149
+ ordering = ["-created_at", "-id"]
150
+ verbose_name = "external MCP tool call"
151
+ verbose_name_plural = "external MCP tool calls"
152
+
153
+ def __str__(self) -> str:
154
+ return f"{self.router_name}:{self.external_name} {self.decision}"