arkaos 4.4.0 → 4.6.0
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.
- package/VERSION +1 -1
- package/arka/SKILL.md +2 -1
- package/config/mcp-policy.yaml +8 -8
- package/core/agents/__pycache__/__init__.cpython-312.pyc +0 -0
- package/core/agents/__pycache__/loader.cpython-312.pyc +0 -0
- package/core/agents/__pycache__/registry_gen.cpython-313.pyc +0 -0
- package/core/agents/__pycache__/schema.cpython-312.pyc +0 -0
- package/core/agents/registry_gen.py +3 -2
- package/core/cognition/scheduler/__pycache__/daemon.cpython-313.pyc +0 -0
- package/core/forge/__pycache__/orchestrator.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/_shared.cpython-314.pyc +0 -0
- package/core/hooks/__pycache__/post_tool_use.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/stop.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/stop.cpython-314.pyc +0 -0
- package/core/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
- package/core/hooks/post_tool_use.py +10 -0
- package/core/knowledge/__pycache__/indexer.cpython-313.pyc +0 -0
- package/core/knowledge/__pycache__/vector_store.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/mcp_telemetry.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/mcp_telemetry_cli.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/native_usage.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/native_usage.cpython-314.pyc +0 -0
- package/core/runtime/mcp_telemetry.py +184 -0
- package/core/runtime/mcp_telemetry_cli.py +62 -0
- package/core/shared/__pycache__/temp_paths.cpython-314.pyc +0 -0
- package/core/synapse/__pycache__/kb_cache.cpython-314.pyc +0 -0
- package/core/sync/__pycache__/agent_provisioner.cpython-313.pyc +0 -0
- package/core/sync/__pycache__/ai_mcp_decider.cpython-313.pyc +0 -0
- package/core/sync/__pycache__/content_syncer.cpython-313.pyc +0 -0
- package/core/sync/__pycache__/descriptor_syncer.cpython-313.pyc +0 -0
- package/core/sync/__pycache__/discovery.cpython-313.pyc +0 -0
- package/core/sync/__pycache__/engine.cpython-313.pyc +0 -0
- package/core/sync/__pycache__/manifest.cpython-313.pyc +0 -0
- package/core/sync/__pycache__/mcp_optimizer.cpython-313.pyc +0 -0
- package/core/sync/__pycache__/mcp_syncer.cpython-313.pyc +0 -0
- package/core/sync/__pycache__/policy_loader.cpython-313.pyc +0 -0
- package/core/sync/__pycache__/reporter.cpython-313.pyc +0 -0
- package/core/sync/__pycache__/settings_syncer.cpython-313.pyc +0 -0
- package/core/sync/mcp_syncer.py +4 -0
- package/core/terminal/__pycache__/session.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/flow_authorization.cpython-314.pyc +0 -0
- package/core/workflow/__pycache__/flow_enforcer.cpython-314.pyc +0 -0
- package/core/workflow/__pycache__/marker_cache.cpython-314.pyc +0 -0
- package/core/workflow/__pycache__/research_gate.cpython-314.pyc +0 -0
- package/dashboard/package.json +1 -1
- package/departments/dev/skills/mcp/SKILL.md +3 -1
- package/departments/ops/skills/update/references/sync-engine.md +2 -1
- package/departments/quality/agents/copy-director.yaml +2 -0
- package/departments/quality/agents/tech-director.yaml +2 -0
- package/knowledge/agents-registry-v2.json +487 -120
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/__pycache__/dashboard-api.cpython-313.pyc +0 -0
- package/scripts/__pycache__/synapse-bridge.cpython-313.pyc +0 -0
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.
|
|
1
|
+
4.6.0
|
package/arka/SKILL.md
CHANGED
|
@@ -179,9 +179,10 @@ violation (squad-routing, arka-supremacy, spec-driven, mandatory-qa).
|
|
|
179
179
|
|
|
180
180
|
| Command | Description |
|
|
181
181
|
|---------|-------------|
|
|
182
|
-
| `/arka status` | System status (version, departments, agents, active projects). Includes **LLM costs (24h)** section: top-line cost + cache hit rate + call count from `core.runtime.llm_cost_telemetry.summarise(period="today")`. Also includes **Enforcement (24h)** section: total calls, block rate, top blocked tools/reasons from `core.governance.enforcement_telemetry.summarise(period="today")` (PR19 v2.41.0). Plus **Reorganization (today)** section: today's proposal path + artifact count from `core.cognition.reorganizer_scheduler.status_summary()` (PR24 v2.46.0). Plus **Model routing** section: gateway live/off + resolved per-slot routes + served counts from `core.runtime.model_routing_check.status_summary()`. |
|
|
182
|
+
| `/arka status` | System status (version, departments, agents, active projects). Includes **LLM costs (24h)** section: top-line cost + cache hit rate + call count from `core.runtime.llm_cost_telemetry.summarise(period="today")`. Also includes **Enforcement (24h)** section: total calls, block rate, top blocked tools/reasons from `core.governance.enforcement_telemetry.summarise(period="today")` (PR19 v2.41.0). Plus **Reorganization (today)** section: today's proposal path + artifact count from `core.cognition.reorganizer_scheduler.status_summary()` (PR24 v2.46.0). Plus **Model routing** section: gateway live/off + resolved per-slot routes + served counts from `core.runtime.model_routing_check.status_summary()`. Plus **MCP usage (24h)** section: total calls + servers in use + top servers from `core.runtime.mcp_telemetry.summarise(period="today")`. |
|
|
183
183
|
| `/arka costs [period]` | LLM cost visibility — aggregates telemetry by day/week/month/all, with top expensive sessions. See `arka/skills/costs/SKILL.md`. Shells out to `~/.arkaos/bin/arka-py -m core.runtime.llm_cost_telemetry_cli <period>`. |
|
|
184
184
|
| `/arka enforcement [period]` | Enforcement compliance — aggregates flow-marker enforcement telemetry by day/week/month/all. Shows block rate, top blocked tools, top reasons. Shells out to `~/.arkaos/bin/arka-py -m core.governance.enforcement_telemetry_cli <period>`. |
|
|
185
|
+
| `/arka mcps [period]` | MCP usage — aggregates the PostToolUse MCP telemetry (`~/.arkaos/telemetry/mcp-usage.jsonl`) by day/week/month/all. Shows total calls, servers in use, top servers and top tools. Shells out to `~/.arkaos/bin/arka-py -m core.runtime.mcp_telemetry_cli <period>`. |
|
|
185
186
|
| `/arka compliance [period]` | Behavior compliance summary (PR29 v2.48.0) — aggregates stop-hook telemetry by day/week/month/all. Shows rates for the four contracts: closing marker, `[arka:meta]` tag, KB citation pass, sycophancy clean. Shells out to `~/.arkaos/bin/arka-py -m core.governance.compliance_telemetry_cli <period>`. |
|
|
186
187
|
| `/arka reorganize [--since-days N]` | Dreaming → Agent reorganizer. Aggregates recent KB pattern/anti-pattern/lesson artifacts (default last 7 days) into a markdown proposal at `~/.arkaos/reorganize-proposals/<date>.md`. **Propose-only** — never modifies agent YAMLs. Sanitizes client identifiers from titles and body excerpts; drops `tags:` field entirely to prevent project-name leaks. **Auto-fires on session start when today's proposal is missing** (PR24 v2.46.0 stale-aware trigger, 30s timeout, background). Shells out to `~/.arkaos/bin/arka-py -m core.cognition.reorganizer_cli [--since-days N] [--dry-run]`. |
|
|
187
188
|
| `/arka standup` | Daily standup (projects, priorities, blockers, updates) |
|
package/config/mcp-policy.yaml
CHANGED
|
@@ -16,13 +16,13 @@ policies:
|
|
|
16
16
|
- obsidian
|
|
17
17
|
- context7
|
|
18
18
|
- gh-grep
|
|
19
|
-
- memory-bank
|
|
20
19
|
- postgres
|
|
21
20
|
- supabase
|
|
22
21
|
- laravel-boost
|
|
23
22
|
- serena
|
|
24
23
|
- sentry
|
|
25
24
|
deferred:
|
|
25
|
+
- memory-bank
|
|
26
26
|
- playwright
|
|
27
27
|
- nuxt
|
|
28
28
|
- nuxt-ui
|
|
@@ -46,12 +46,12 @@ policies:
|
|
|
46
46
|
- obsidian
|
|
47
47
|
- context7
|
|
48
48
|
- gh-grep
|
|
49
|
-
- memory-bank
|
|
50
49
|
- playwright
|
|
51
50
|
- nuxt
|
|
52
51
|
- nuxt-ui
|
|
53
52
|
- sentry
|
|
54
53
|
deferred:
|
|
54
|
+
- memory-bank
|
|
55
55
|
- postgres
|
|
56
56
|
- supabase
|
|
57
57
|
- laravel-boost
|
|
@@ -76,11 +76,11 @@ policies:
|
|
|
76
76
|
- obsidian
|
|
77
77
|
- context7
|
|
78
78
|
- gh-grep
|
|
79
|
-
- memory-bank
|
|
80
79
|
- playwright
|
|
81
80
|
- next-devtools
|
|
82
81
|
- sentry
|
|
83
82
|
deferred:
|
|
83
|
+
- memory-bank
|
|
84
84
|
- postgres
|
|
85
85
|
- supabase
|
|
86
86
|
- laravel-boost
|
|
@@ -106,12 +106,12 @@ policies:
|
|
|
106
106
|
- obsidian
|
|
107
107
|
- context7
|
|
108
108
|
- gh-grep
|
|
109
|
-
- memory-bank
|
|
110
109
|
- playwright
|
|
111
110
|
- shopify-dev
|
|
112
111
|
- mirakl
|
|
113
112
|
- sentry
|
|
114
113
|
deferred:
|
|
114
|
+
- memory-bank
|
|
115
115
|
- postgres
|
|
116
116
|
- supabase
|
|
117
117
|
- laravel-boost
|
|
@@ -136,9 +136,9 @@ policies:
|
|
|
136
136
|
- obsidian
|
|
137
137
|
- context7
|
|
138
138
|
- gh-grep
|
|
139
|
-
- memory-bank
|
|
140
139
|
- sentry
|
|
141
140
|
deferred:
|
|
141
|
+
- memory-bank
|
|
142
142
|
- playwright
|
|
143
143
|
- postgres
|
|
144
144
|
- supabase
|
|
@@ -165,11 +165,11 @@ policies:
|
|
|
165
165
|
- arka-prompts
|
|
166
166
|
- obsidian
|
|
167
167
|
- context7
|
|
168
|
-
- memory-bank
|
|
169
168
|
- canva
|
|
170
169
|
- firecrawl
|
|
171
170
|
- clickup
|
|
172
171
|
deferred:
|
|
172
|
+
- memory-bank
|
|
173
173
|
- gh-grep
|
|
174
174
|
- postgres
|
|
175
175
|
- supabase
|
|
@@ -195,10 +195,10 @@ policies:
|
|
|
195
195
|
- arka-prompts
|
|
196
196
|
- obsidian
|
|
197
197
|
- context7
|
|
198
|
-
- memory-bank
|
|
199
198
|
- canva
|
|
200
199
|
- firecrawl
|
|
201
200
|
deferred:
|
|
201
|
+
- memory-bank
|
|
202
202
|
- gh-grep
|
|
203
203
|
- postgres
|
|
204
204
|
- supabase
|
|
@@ -226,8 +226,8 @@ policies:
|
|
|
226
226
|
- obsidian
|
|
227
227
|
- context7
|
|
228
228
|
- gh-grep
|
|
229
|
-
- memory-bank
|
|
230
229
|
deferred:
|
|
230
|
+
- memory-bank
|
|
231
231
|
- postgres
|
|
232
232
|
- supabase
|
|
233
233
|
- playwright
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -38,6 +38,7 @@ def generate_registry(departments_dir: str | Path, output_path: str | Path) -> d
|
|
|
38
38
|
"role": agent.role,
|
|
39
39
|
"department": agent.department,
|
|
40
40
|
"tier": agent.tier,
|
|
41
|
+
"model": agent.get_model(),
|
|
41
42
|
"parent_squad": agent.parent_squad,
|
|
42
43
|
"sub_squad_role": agent.sub_squad_role,
|
|
43
44
|
"disc": {
|
|
@@ -62,8 +63,8 @@ def generate_registry(departments_dir: str | Path, output_path: str | Path) -> d
|
|
|
62
63
|
k: v for k, v in agent.authority.model_dump().items()
|
|
63
64
|
if v and v != [] and k not in ("delegates_to", "escalates_to")
|
|
64
65
|
},
|
|
65
|
-
"expertise_domains": agent.expertise.domains
|
|
66
|
-
"frameworks": agent.expertise.frameworks
|
|
66
|
+
"expertise_domains": agent.expertise.domains,
|
|
67
|
+
"frameworks": agent.expertise.frameworks,
|
|
67
68
|
"knowledge_sources": agent.expertise.knowledge_sources,
|
|
68
69
|
"file": str(yaml_file.relative_to(departments_dir.parent)),
|
|
69
70
|
"memory_path": agent.memory_path,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -632,6 +632,16 @@ def main(stdin_json: dict | None = None) -> int:
|
|
|
632
632
|
# and confirm persistent authorization when a marker is present.
|
|
633
633
|
_confirm_flow_authorization(session_id, transcript_path)
|
|
634
634
|
|
|
635
|
+
# MCP usage telemetry — must run BEFORE the error-signal early exit
|
|
636
|
+
# below (MCP calls normally succeed). record() is a no-op for
|
|
637
|
+
# non-MCP tools and never raises on I/O; the guard covers import
|
|
638
|
+
# failures on stripped installs.
|
|
639
|
+
try:
|
|
640
|
+
from core.runtime.mcp_telemetry import record as _record_mcp_usage
|
|
641
|
+
_record_mcp_usage(tool_name, session_id=session_id)
|
|
642
|
+
except Exception: # noqa: BLE001 — telemetry must never break the hook
|
|
643
|
+
pass
|
|
644
|
+
|
|
635
645
|
if tool_name in ("Task", "Agent"):
|
|
636
646
|
subagent_type = get_str(stdin_json, "tool_input", "subagent_type")
|
|
637
647
|
if subagent_type == "cqo":
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""MCP usage telemetry (audit E2E v4.3.6, P1).
|
|
2
|
+
|
|
3
|
+
Writer + summarizer for ``~/.arkaos/telemetry/mcp-usage.jsonl``. The
|
|
4
|
+
PostToolUse hook calls :func:`record` on every tool event; MCP tool
|
|
5
|
+
names (``mcp__<server>__<tool>``) are parsed and appended as one JSONL
|
|
6
|
+
line, everything else is ignored. Before this module the only usage
|
|
7
|
+
signal was grepping session transcripts.
|
|
8
|
+
|
|
9
|
+
Mirrors ``core.governance.enforcement_telemetry`` so periods,
|
|
10
|
+
malformed-line tolerance, and zero-division safety behave the same way
|
|
11
|
+
across telemetry surfaces. :func:`summarise` is read-only.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
from collections import Counter
|
|
18
|
+
from dataclasses import dataclass, field
|
|
19
|
+
from datetime import datetime, timedelta, timezone
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Any, Iterable
|
|
22
|
+
|
|
23
|
+
DEFAULT_PATH: Path = Path.home() / ".arkaos" / "telemetry" / "mcp-usage.jsonl"
|
|
24
|
+
_VALID_PERIODS: frozenset[str] = frozenset({"today", "week", "month", "all"})
|
|
25
|
+
_TOP_SERVERS: int = 10
|
|
26
|
+
_TOP_TOOLS: int = 5
|
|
27
|
+
_MCP_PREFIX = "mcp__"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class McpUsageSummary:
|
|
32
|
+
"""Aggregated MCP usage over a time slice."""
|
|
33
|
+
period: str
|
|
34
|
+
total_calls: int
|
|
35
|
+
unique_servers: int
|
|
36
|
+
top_servers: list[tuple[str, int]] = field(default_factory=list)
|
|
37
|
+
top_tools: list[tuple[str, int]] = field(default_factory=list)
|
|
38
|
+
corrupt_line_count: int = 0
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def parse_mcp_tool(tool_name: str) -> tuple[str, str] | None:
|
|
42
|
+
"""Split ``mcp__<server>__<tool>`` into (server, tool).
|
|
43
|
+
|
|
44
|
+
Returns None for non-MCP tool names. Handles servers whose slug
|
|
45
|
+
contains single underscores (``claude_ai_Canva``, plugin servers
|
|
46
|
+
like ``plugin_claude-mem_mcp-search``) by splitting on the FIRST
|
|
47
|
+
double underscore after the prefix.
|
|
48
|
+
"""
|
|
49
|
+
if not tool_name.startswith(_MCP_PREFIX):
|
|
50
|
+
return None
|
|
51
|
+
rest = tool_name[len(_MCP_PREFIX):]
|
|
52
|
+
server, sep, tool = rest.partition("__")
|
|
53
|
+
if not sep or not server or not tool:
|
|
54
|
+
return None
|
|
55
|
+
return server, tool
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def record(
|
|
59
|
+
tool_name: str,
|
|
60
|
+
*,
|
|
61
|
+
session_id: str = "",
|
|
62
|
+
path: Path | None = None,
|
|
63
|
+
) -> bool:
|
|
64
|
+
"""Append one usage line when tool_name is an MCP tool.
|
|
65
|
+
|
|
66
|
+
Never raises — the caller is a hook that must not block on
|
|
67
|
+
telemetry. Returns True when a line was written.
|
|
68
|
+
"""
|
|
69
|
+
parsed = parse_mcp_tool(tool_name)
|
|
70
|
+
if parsed is None:
|
|
71
|
+
return False
|
|
72
|
+
server, tool = parsed
|
|
73
|
+
dest = path or DEFAULT_PATH
|
|
74
|
+
entry = {
|
|
75
|
+
"ts": datetime.now(timezone.utc).isoformat(),
|
|
76
|
+
"server": server,
|
|
77
|
+
"tool": tool,
|
|
78
|
+
"session": session_id,
|
|
79
|
+
}
|
|
80
|
+
try:
|
|
81
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
82
|
+
with dest.open("a", encoding="utf-8") as fh:
|
|
83
|
+
fh.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
|
84
|
+
except OSError:
|
|
85
|
+
return False
|
|
86
|
+
return True
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def summarise(period: str, *, path: Path | None = None) -> McpUsageSummary:
|
|
90
|
+
"""Return a McpUsageSummary for the requested period.
|
|
91
|
+
|
|
92
|
+
period: one of "today", "week", "month", "all".
|
|
93
|
+
path: override telemetry source (used by tests; defaults to DEFAULT_PATH).
|
|
94
|
+
"""
|
|
95
|
+
if period not in _VALID_PERIODS:
|
|
96
|
+
raise ValueError(f"invalid period: {period!r}")
|
|
97
|
+
src = path or DEFAULT_PATH
|
|
98
|
+
cutoff = _period_cutoff(period)
|
|
99
|
+
entries, corrupt = _read_jsonl(src, cutoff)
|
|
100
|
+
return _build_summary(period, entries, corrupt)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _period_cutoff(period: str, now: datetime | None = None) -> datetime | None:
|
|
104
|
+
ref = now or datetime.now(timezone.utc)
|
|
105
|
+
if period == "today":
|
|
106
|
+
return ref.replace(hour=0, minute=0, second=0, microsecond=0)
|
|
107
|
+
if period == "week":
|
|
108
|
+
return ref - timedelta(days=7)
|
|
109
|
+
if period == "month":
|
|
110
|
+
return ref - timedelta(days=30)
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _read_jsonl(src: Path, cutoff: datetime | None) -> tuple[list[dict[str, Any]], int]:
|
|
115
|
+
if not src.exists():
|
|
116
|
+
return [], 0
|
|
117
|
+
entries: list[dict[str, Any]] = []
|
|
118
|
+
corrupt = 0
|
|
119
|
+
# Line-stream to keep memory O(1) — this file grows on every MCP call.
|
|
120
|
+
try:
|
|
121
|
+
with src.open("r", encoding="utf-8", errors="replace") as fh:
|
|
122
|
+
for line in fh:
|
|
123
|
+
if not line.strip():
|
|
124
|
+
continue
|
|
125
|
+
try:
|
|
126
|
+
entry = json.loads(line)
|
|
127
|
+
except json.JSONDecodeError:
|
|
128
|
+
corrupt += 1
|
|
129
|
+
continue
|
|
130
|
+
if not isinstance(entry, dict):
|
|
131
|
+
corrupt += 1
|
|
132
|
+
continue
|
|
133
|
+
if cutoff is not None and not _within_cutoff(entry, cutoff):
|
|
134
|
+
continue
|
|
135
|
+
entries.append(entry)
|
|
136
|
+
except OSError:
|
|
137
|
+
return entries, corrupt
|
|
138
|
+
return entries, corrupt
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _within_cutoff(entry: dict[str, Any], cutoff: datetime) -> bool:
|
|
142
|
+
ts = _parse_ts(entry.get("ts"))
|
|
143
|
+
if ts is None:
|
|
144
|
+
return False
|
|
145
|
+
return ts >= cutoff
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _parse_ts(raw: Any) -> datetime | None:
|
|
149
|
+
if not isinstance(raw, str) or not raw:
|
|
150
|
+
return None
|
|
151
|
+
try:
|
|
152
|
+
parsed = datetime.fromisoformat(raw.replace("Z", "+00:00"))
|
|
153
|
+
except ValueError:
|
|
154
|
+
return None
|
|
155
|
+
if parsed.tzinfo is None:
|
|
156
|
+
parsed = parsed.replace(tzinfo=timezone.utc)
|
|
157
|
+
return parsed
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _build_summary(
|
|
161
|
+
period: str,
|
|
162
|
+
entries: Iterable[dict[str, Any]],
|
|
163
|
+
corrupt: int,
|
|
164
|
+
) -> McpUsageSummary:
|
|
165
|
+
total = 0
|
|
166
|
+
servers: Counter[str] = Counter()
|
|
167
|
+
tools: Counter[str] = Counter()
|
|
168
|
+
for entry in entries:
|
|
169
|
+
server = str(entry.get("server", ""))
|
|
170
|
+
tool = str(entry.get("tool", ""))
|
|
171
|
+
if not server:
|
|
172
|
+
continue
|
|
173
|
+
total += 1
|
|
174
|
+
servers[server] += 1
|
|
175
|
+
if tool:
|
|
176
|
+
tools[f"{server}/{tool}"] += 1
|
|
177
|
+
return McpUsageSummary(
|
|
178
|
+
period=period,
|
|
179
|
+
total_calls=total,
|
|
180
|
+
unique_servers=len(servers),
|
|
181
|
+
top_servers=servers.most_common(_TOP_SERVERS),
|
|
182
|
+
top_tools=tools.most_common(_TOP_TOOLS),
|
|
183
|
+
corrupt_line_count=corrupt,
|
|
184
|
+
)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""CLI entry for the MCP usage telemetry summarizer.
|
|
2
|
+
|
|
3
|
+
Invoked as ``python -m core.runtime.mcp_telemetry_cli <period>`` where
|
|
4
|
+
``<period>`` is one of: today, week, month, all (default: today).
|
|
5
|
+
|
|
6
|
+
Output is plain markdown so it renders cleanly in both the terminal and
|
|
7
|
+
the ``/arka status`` skill which concatenates it into its report.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
from core.runtime.mcp_telemetry import McpUsageSummary, summarise
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _sanitize_md(value: str) -> str:
|
|
18
|
+
"""Strip markdown-breaking characters before rendering JSONL strings.
|
|
19
|
+
|
|
20
|
+
Telemetry is local-writer-only, but a malformed server/tool value
|
|
21
|
+
(newline, backtick) can still distort the markdown if /arka status
|
|
22
|
+
pipes the output to a UI.
|
|
23
|
+
"""
|
|
24
|
+
return value.replace("\n", " ").replace("\r", " ").replace("`", "").strip()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _render(summary: McpUsageSummary) -> str:
|
|
28
|
+
lines = [
|
|
29
|
+
f"# MCP usage — {summary.period}",
|
|
30
|
+
"",
|
|
31
|
+
f"- Calls: **{summary.total_calls}**",
|
|
32
|
+
f"- Servers in use: **{summary.unique_servers}**",
|
|
33
|
+
]
|
|
34
|
+
if summary.top_servers:
|
|
35
|
+
lines += ["", "## Top servers"]
|
|
36
|
+
for server, count in summary.top_servers:
|
|
37
|
+
lines.append(f"- `{_sanitize_md(server)}` — {count}")
|
|
38
|
+
if summary.top_tools:
|
|
39
|
+
lines += ["", "## Top tools"]
|
|
40
|
+
for tool, count in summary.top_tools:
|
|
41
|
+
lines.append(f"- `{_sanitize_md(tool)}` — {count}")
|
|
42
|
+
if summary.corrupt_line_count:
|
|
43
|
+
lines += ["", f"_Skipped {summary.corrupt_line_count} corrupt line(s)._"]
|
|
44
|
+
if summary.total_calls == 0:
|
|
45
|
+
lines += ["", "_No MCP calls recorded for this period._"]
|
|
46
|
+
return "\n".join(lines)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def main(argv: list[str] | None = None) -> int:
|
|
50
|
+
args = argv if argv is not None else sys.argv[1:]
|
|
51
|
+
period = args[0] if args else "today"
|
|
52
|
+
try:
|
|
53
|
+
summary = summarise(period)
|
|
54
|
+
except ValueError as exc:
|
|
55
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
56
|
+
return 2
|
|
57
|
+
print(_render(summary))
|
|
58
|
+
return 0
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if __name__ == "__main__":
|
|
62
|
+
raise SystemExit(main())
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/core/sync/mcp_syncer.py
CHANGED
|
@@ -60,6 +60,10 @@ def resolve_mcps_for_stack(
|
|
|
60
60
|
|
|
61
61
|
result: list[tuple[str, dict]] = []
|
|
62
62
|
for name, config in registry.items():
|
|
63
|
+
if "managed" in config:
|
|
64
|
+
# Runtime-managed servers (extension/plugin) are registered for
|
|
65
|
+
# governance and telemetry — they have no launchable command to write.
|
|
66
|
+
continue
|
|
63
67
|
if config.get("category") in allowed:
|
|
64
68
|
result.append((name, config))
|
|
65
69
|
return result
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dashboard/package.json
CHANGED
|
@@ -40,7 +40,7 @@ Manage Model Context Protocol (MCP) servers for projects. MCPs extend Claude Cod
|
|
|
40
40
|
|
|
41
41
|
| Profile | MCPs Included | Use For |
|
|
42
42
|
|---------|---------------|---------|
|
|
43
|
-
| `base` | obsidian, context7, playwright,
|
|
43
|
+
| `base` | obsidian, context7, playwright, sentry, gh-grep, clickup, firecrawl, supabase | All projects |
|
|
44
44
|
| `laravel` | base + laravel-boost, serena | Laravel backends |
|
|
45
45
|
| `nuxt` | base + nuxt, nuxt-ui | Nuxt 3/4 apps |
|
|
46
46
|
| `vue` | base + nuxt-ui | Vue 3 SPAs |
|
|
@@ -50,6 +50,8 @@ Manage Model Context Protocol (MCP) servers for projects. MCPs extend Claude Cod
|
|
|
50
50
|
| `full-stack` | base + laravel-boost, serena, nuxt, nuxt-ui | Laravel + Nuxt apps |
|
|
51
51
|
| `comms` | base + slack, discord, whatsapp, teams | Messaging platforms |
|
|
52
52
|
|
|
53
|
+
> Runtime-managed MCPs (not a profile): `claude-in-chrome` (extension) and `claude-mem` (plugin) are registered in the registry for governance and telemetry only — they have no launchable command and are never written to `.mcp.json`.
|
|
54
|
+
|
|
53
55
|
## How It Works
|
|
54
56
|
|
|
55
57
|
### /dev mcp apply <profile>
|
|
@@ -31,7 +31,8 @@ Deduplicate by absolute path. Skip projects whose path does not exist.
|
|
|
31
31
|
### Phase 3a — MCP Sync
|
|
32
32
|
For each project: regenerate `.mcp.json` based on the registry + detected stack.
|
|
33
33
|
|
|
34
|
-
Base MCPs (all projects): `arka-prompts`, `context7`, `obsidian`, `clickup`, `
|
|
34
|
+
Base MCPs (all projects): `arka-prompts`, `context7`, `obsidian`, `clickup`, `playwright`, `gh-grep`.
|
|
35
|
+
Runtime-managed entries (`claude-in-chrome`, `claude-mem`) are registered for governance and telemetry only and are never written to `.mcp.json`; `memory-bank` is `optional` (demoted from base 2026-07-08).
|
|
35
36
|
|
|
36
37
|
Stack additions:
|
|
37
38
|
- Laravel: `laravel-boost`, `serena`, `sentry`
|
|
@@ -5,6 +5,8 @@ department: quality
|
|
|
5
5
|
tier: 0
|
|
6
6
|
# PR-4 evidence QG (v4.1): reviewers run sonnet by default; dispatch on
|
|
7
7
|
# opus ONLY for Tier 0/security-scope diffs (see constitution model-routing).
|
|
8
|
+
# Excellence Reform (v4.2): the Model Fabric quality_gate role upgrades this
|
|
9
|
+
# at dispatch time (~/.arkaos/models.yaml) — sonnet here is the floor, not the route.
|
|
8
10
|
model: sonnet
|
|
9
11
|
|
|
10
12
|
behavioral_dna:
|
|
@@ -5,6 +5,8 @@ department: quality
|
|
|
5
5
|
tier: 0
|
|
6
6
|
# PR-4 evidence QG (v4.1): reviewers run sonnet by default; dispatch on
|
|
7
7
|
# opus ONLY for Tier 0/security-scope diffs (see constitution model-routing).
|
|
8
|
+
# Excellence Reform (v4.2): the Model Fabric quality_gate role upgrades this
|
|
9
|
+
# at dispatch time (~/.arkaos/models.yaml) — sonnet here is the floor, not the route.
|
|
8
10
|
model: sonnet
|
|
9
11
|
|
|
10
12
|
behavioral_dna:
|