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.
- apps/__init__.py +1 -0
- apps/audit/__init__.py +1 -0
- apps/audit/admin.py +6 -0
- apps/audit/apps.py +8 -0
- apps/audit/migrations/0001_initial.py +35 -0
- apps/audit/migrations/__init__.py +0 -0
- apps/audit/models.py +22 -0
- apps/harness/__init__.py +1 -0
- apps/harness/admin.py +6 -0
- apps/harness/apps.py +8 -0
- apps/harness/migrations/0001_initial.py +35 -0
- apps/harness/migrations/__init__.py +0 -0
- apps/harness/models.py +22 -0
- apps/harness/templatetags/__init__.py +1 -0
- apps/integrations/__init__.py +1 -0
- apps/integrations/admin.py +6 -0
- apps/integrations/apps.py +8 -0
- apps/integrations/migrations/0001_initial.py +29 -0
- apps/integrations/migrations/__init__.py +0 -0
- apps/integrations/models.py +16 -0
- apps/integrations/services.py +31 -0
- apps/mcp/__init__.py +1 -0
- apps/mcp/admin.py +20 -0
- apps/mcp/apps.py +8 -0
- apps/mcp/migrations/0001_initial.py +168 -0
- apps/mcp/migrations/__init__.py +0 -0
- apps/mcp/models.py +154 -0
- apps/mcp/services.py +327 -0
- apps/orders/__init__.py +1 -0
- apps/orders/admin.py +6 -0
- apps/orders/apps.py +8 -0
- apps/orders/migrations/0001_initial.py +79 -0
- apps/orders/migrations/__init__.py +0 -0
- apps/orders/models.py +66 -0
- apps/orders/services.py +107 -0
- apps/policy/__init__.py +1 -0
- apps/policy/admin.py +6 -0
- apps/policy/apps.py +8 -0
- apps/policy/migrations/0001_initial.py +75 -0
- apps/policy/migrations/__init__.py +0 -0
- apps/policy/models.py +61 -0
- apps/policy/services.py +110 -0
- apps/portfolio/__init__.py +1 -0
- apps/portfolio/admin.py +6 -0
- apps/portfolio/apps.py +8 -0
- apps/portfolio/migrations/0001_initial.py +67 -0
- apps/portfolio/migrations/__init__.py +0 -0
- apps/portfolio/models.py +53 -0
- apps/research/__init__.py +1 -0
- apps/research/admin.py +1 -0
- apps/research/apps.py +8 -0
- apps/research/migrations/__init__.py +0 -0
- apps/research/models.py +1 -0
- apps/workflows/__init__.py +1 -0
- apps/workflows/admin.py +6 -0
- apps/workflows/apps.py +8 -0
- apps/workflows/migrations/0001_initial.py +51 -0
- apps/workflows/migrations/__init__.py +0 -0
- apps/workflows/models.py +44 -0
- tradingcodex-0.1.0.dist-info/METADATA +337 -0
- tradingcodex-0.1.0.dist-info/RECORD +254 -0
- tradingcodex-0.1.0.dist-info/WHEEL +5 -0
- tradingcodex-0.1.0.dist-info/entry_points.txt +2 -0
- tradingcodex-0.1.0.dist-info/licenses/LICENSE +202 -0
- tradingcodex-0.1.0.dist-info/licenses/NOTICE +24 -0
- tradingcodex-0.1.0.dist-info/top_level.txt +4 -0
- tradingcodex_cli/__init__.py +1 -0
- tradingcodex_cli/__main__.py +124 -0
- tradingcodex_cli/commands/__init__.py +2 -0
- tradingcodex_cli/commands/bootstrap.py +157 -0
- tradingcodex_cli/commands/db.py +36 -0
- tradingcodex_cli/commands/doctor.py +230 -0
- tradingcodex_cli/commands/mcp.py +186 -0
- tradingcodex_cli/commands/orders.py +89 -0
- tradingcodex_cli/commands/policy.py +21 -0
- tradingcodex_cli/commands/profile.py +110 -0
- tradingcodex_cli/commands/research.py +76 -0
- tradingcodex_cli/commands/skills.py +93 -0
- tradingcodex_cli/commands/strategies.py +67 -0
- tradingcodex_cli/commands/subagents.py +106 -0
- tradingcodex_cli/commands/utils.py +134 -0
- tradingcodex_cli/commands/workspaces.py +53 -0
- tradingcodex_cli/generator.py +234 -0
- tradingcodex_cli/mcp_stdio.py +26 -0
- tradingcodex_cli/service_autostart.py +127 -0
- tradingcodex_service/__init__.py +5 -0
- tradingcodex_service/admin.py +1 -0
- tradingcodex_service/api.py +486 -0
- tradingcodex_service/application/__init__.py +2 -0
- tradingcodex_service/application/agents.py +1470 -0
- tradingcodex_service/application/audit.py +71 -0
- tradingcodex_service/application/common.py +88 -0
- tradingcodex_service/application/components.py +299 -0
- tradingcodex_service/application/harness.py +747 -0
- tradingcodex_service/application/markdown_preview.py +179 -0
- tradingcodex_service/application/orders.py +404 -0
- tradingcodex_service/application/policy.py +150 -0
- tradingcodex_service/application/portfolio.py +166 -0
- tradingcodex_service/application/research.py +356 -0
- tradingcodex_service/application/runtime.py +321 -0
- tradingcodex_service/asgi.py +6 -0
- tradingcodex_service/mcp_http.py +59 -0
- tradingcodex_service/mcp_runtime.py +565 -0
- tradingcodex_service/settings.py +91 -0
- tradingcodex_service/templates/web/activity.html +24 -0
- tradingcodex_service/templates/web/agent_skills.html +111 -0
- tradingcodex_service/templates/web/agents.html +121 -0
- tradingcodex_service/templates/web/base.html +150 -0
- tradingcodex_service/templates/web/dashboard.html +109 -0
- tradingcodex_service/templates/web/fragments/role_inspector.html +81 -0
- tradingcodex_service/templates/web/fragments/starter_prompt.html +24 -0
- tradingcodex_service/templates/web/fragments/topology_canvas.html +85 -0
- tradingcodex_service/templates/web/harness.html +87 -0
- tradingcodex_service/templates/web/mcp_router.html +250 -0
- tradingcodex_service/templates/web/orders.html +68 -0
- tradingcodex_service/templates/web/policy.html +81 -0
- tradingcodex_service/templates/web/portfolio.html +52 -0
- tradingcodex_service/templates/web/research.html +73 -0
- tradingcodex_service/templates/web/starter_prompt.html +40 -0
- tradingcodex_service/templates/web/strategies.html +74 -0
- tradingcodex_service/urls.py +42 -0
- tradingcodex_service/version.py +1 -0
- tradingcodex_service/web.py +885 -0
- tradingcodex_service/wsgi.py +6 -0
- workspace_templates/__init__.py +1 -0
- workspace_templates/modules/audit/files/.tradingcodex/audit/README.md +5 -0
- workspace_templates/modules/audit/files/trading/audit/.gitkeep +1 -0
- workspace_templates/modules/audit/module.json +16 -0
- workspace_templates/modules/codex-base/files/.codex/config.toml +272 -0
- workspace_templates/modules/codex-base/files/.codex/hooks/tradingcodex_hook.py +173 -0
- workspace_templates/modules/codex-base/files/.codex/hooks.json +105 -0
- workspace_templates/modules/codex-base/files/.codex/prompts/base_instructions/head-manager.md +129 -0
- workspace_templates/modules/codex-base/files/.codex/rules/tradingcodex.rules +50 -0
- workspace_templates/modules/codex-base/files/.tradingcodex/capabilities.yaml +56 -0
- workspace_templates/modules/codex-base/files/.tradingcodex/cli.py +16 -0
- workspace_templates/modules/codex-base/files/.tradingcodex/config.yaml +53 -0
- workspace_templates/modules/codex-base/files/.tradingcodex/policies/policy-bindings.yaml +16 -0
- workspace_templates/modules/codex-base/files/.tradingcodex/policies/principals.yaml +17 -0
- workspace_templates/modules/codex-base/files/.tradingcodex/policies/roles.yaml +27 -0
- workspace_templates/modules/codex-base/files/AGENTS.md +56 -0
- workspace_templates/modules/codex-base/files/pyproject.toml +13 -0
- workspace_templates/modules/codex-base/files/tcx +35 -0
- workspace_templates/modules/codex-base/module.json +17 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/policies/access-policies.yaml +39 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/policies/restricted-list.yaml +6 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/approval_receipt.schema.json +14 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/audit_event.schema.json +11 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/evidence_pack.schema.json +16 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/execution_result.schema.json +12 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/fundamental_report.schema.json +15 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/news_report.schema.json +15 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/order_intent.schema.json +30 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/portfolio_review.schema.json +15 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/postmortem_report.schema.json +14 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/risk_report.schema.json +15 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/technical_report.schema.json +15 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/thesis.schema.json +15 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/valuation.schema.json +15 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/scripts/validate-order-intent.py +15 -0
- workspace_templates/modules/enforcement-guardrails/module.json +19 -0
- workspace_templates/modules/fixed-subagents/files/.codex/agents/execution-operator.toml +71 -0
- workspace_templates/modules/fixed-subagents/files/.codex/agents/fundamental-analyst.toml +62 -0
- workspace_templates/modules/fixed-subagents/files/.codex/agents/instrument-analyst.toml +64 -0
- workspace_templates/modules/fixed-subagents/files/.codex/agents/macro-analyst.toml +64 -0
- workspace_templates/modules/fixed-subagents/files/.codex/agents/news-analyst.toml +63 -0
- workspace_templates/modules/fixed-subagents/files/.codex/agents/portfolio-manager.toml +62 -0
- workspace_templates/modules/fixed-subagents/files/.codex/agents/risk-manager.toml +65 -0
- workspace_templates/modules/fixed-subagents/files/.codex/agents/technical-analyst.toml +63 -0
- workspace_templates/modules/fixed-subagents/files/.codex/agents/valuation-analyst.toml +59 -0
- workspace_templates/modules/fixed-subagents/files/.tradingcodex/mainagent/head-manager.yaml +66 -0
- workspace_templates/modules/fixed-subagents/files/.tradingcodex/mainagent/skill-change-proposals/.gitkeep +1 -0
- workspace_templates/modules/fixed-subagents/files/.tradingcodex/mainagent/subagent-registry.yaml +56 -0
- workspace_templates/modules/fixed-subagents/module.json +23 -0
- workspace_templates/modules/guidance-guardrails/files/.tradingcodex/guidance/guardrails.md +17 -0
- workspace_templates/modules/guidance-guardrails/files/.tradingcodex/guidance/task-quality-checklist.md +37 -0
- workspace_templates/modules/guidance-guardrails/module.json +18 -0
- workspace_templates/modules/information-barriers/files/.tradingcodex/policies/information-barriers.yaml +211 -0
- workspace_templates/modules/information-barriers/files/.tradingcodex/secrets.md +9 -0
- workspace_templates/modules/information-barriers/files/trading/approvals/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/market-data/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/orders/approved/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/orders/draft/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/orders/executed/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/orders/rejected/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/portfolio/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/reports/fundamental/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/reports/instrument/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/reports/macro/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/reports/news/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/reports/policy/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/reports/portfolio/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/reports/postmortem/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/reports/risk/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/reports/technical/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/reports/valuation/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/research/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/module.json +19 -0
- workspace_templates/modules/paper-trading/files/.tradingcodex/mcp/adapters/paper-trading.py +4 -0
- workspace_templates/modules/paper-trading/module.json +16 -0
- workspace_templates/modules/postmortem/files/.tradingcodex/workflows/postmortem.yaml +12 -0
- workspace_templates/modules/postmortem/module.json +16 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/investment-workflow-map/SKILL.md +106 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/investment-workflow-map/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/manage-optional-skills/SKILL.md +101 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/manage-optional-skills/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/manage-subagents/SKILL.md +140 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/manage-subagents/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/orchestrate-workflow/SKILL.md +140 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/orchestrate-workflow/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/postmortem/SKILL.md +31 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/postmortem/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/scenario-quality-gates/SKILL.md +138 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/scenario-quality-gates/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/strategy-creator/SKILL.md +109 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/strategy-creator/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/synthesize-decision/SKILL.md +54 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/synthesize-decision/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/execution-operator/execute-paper-order/SKILL.md +35 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/execution-operator/execute-paper-order/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/fundamental-analyst/fundamental-analysis/SKILL.md +46 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/fundamental-analyst/fundamental-analysis/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/instrument-analyst/instrument-analysis/SKILL.md +40 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/instrument-analyst/instrument-analysis/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/macro-analyst/macro-analysis/SKILL.md +40 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/macro-analyst/macro-analysis/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/news-analyst/news-analysis/SKILL.md +43 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/news-analyst/news-analysis/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/portfolio-manager/create-order-intent/SKILL.md +46 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/portfolio-manager/create-order-intent/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/portfolio-manager/portfolio-review/SKILL.md +44 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/portfolio-manager/portfolio-review/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/approve-order/SKILL.md +38 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/approve-order/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/policy-review/SKILL.md +43 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/policy-review/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/review-risk/SKILL.md +45 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/review-risk/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/shared/collect-evidence/SKILL.md +46 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/shared/collect-evidence/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/shared/external-data-source-gate/SKILL.md +66 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/shared/external-data-source-gate/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/technical-analyst/technical-analysis/SKILL.md +43 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/technical-analyst/technical-analysis/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/valuation-analyst/valuation-review/SKILL.md +47 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/valuation-analyst/valuation-review/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/module.json +37 -0
- workspace_templates/modules/stub-execution/files/.tradingcodex/mcp/adapters/stub-execution.py +4 -0
- workspace_templates/modules/stub-execution/module.json +15 -0
- workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/adapters/live-adapter.contract.md +25 -0
- workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/enforcer/README.md +5 -0
- workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/gateway/README.md +8 -0
- workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/server.py +27 -0
- workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/smoke-call.py +18 -0
- 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
apps/audit/apps.py
ADDED
|
@@ -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}"
|
apps/harness/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
apps/harness/admin.py
ADDED
apps/harness/apps.py
ADDED
|
@@ -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,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,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}"
|