mirror of
https://github.com/MaksTinyWorkshop/_Assistant_Lead_Tech
synced 2026-06-28 01:53:40 +02:00
feat(mcp): add token report script to measure real MCP consumption
scripts/mcp_token_report.py scanne un transcript Claude Code (.jsonl) et mesure la consommation réelle des appels MCP leadtech en condition d'usage : - nombre d'appels par tool (get_guidance, validate_plan, validate_patch, ...) - tokens des résultats MCP injectés dans le contexte - part dans l'input non caché + totaux session (input/output/cache) Usage: python3 mcp_token_report.py --project RL799_V2 (dernier transcript du projet) Validé sur transcript simulé (3 appels = pattern d'une story dev): détection + comptage OK. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+87
@@ -0,0 +1,87 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Mesure la consommation tokens des appels MCP leadtech dans un transcript Claude Code.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python3 mcp_token_report.py <transcript.jsonl>
|
||||||
|
python3 mcp_token_report.py --project RL799_V2 # dernier transcript du projet
|
||||||
|
|
||||||
|
Deux niveaux mesurés:
|
||||||
|
- taille des RESULTATS d'outils leadtech (ce que le MCP injecte dans le contexte)
|
||||||
|
- total tokens de session (input/output/cache) pour situer la part MCP
|
||||||
|
"""
|
||||||
|
import json, sys, glob, os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
LEADTECH_TOOLS = {"get_guidance","validate_plan","validate_patch","emit_checklist",
|
||||||
|
"propose_capitalization","triage_capitalization","route_to_project_memory"}
|
||||||
|
|
||||||
|
def is_leadtech(name: str) -> bool:
|
||||||
|
n = (name or "").lower()
|
||||||
|
return "leadtech" in n or any(t in n for t in LEADTECH_TOOLS)
|
||||||
|
|
||||||
|
def approx_tokens(s: str) -> int:
|
||||||
|
return round(len(s) / 4)
|
||||||
|
|
||||||
|
def resolve_file(arg: str) -> str:
|
||||||
|
if arg.startswith("--project"):
|
||||||
|
proj = sys.argv[sys.argv.index("--project")+1]
|
||||||
|
base = Path.home()/".claude"/"projects"
|
||||||
|
cands = []
|
||||||
|
for d in base.glob("*"):
|
||||||
|
if proj.replace("_","-").replace("/","-").lower() in d.name.lower():
|
||||||
|
cands += glob.glob(str(d/"*.jsonl"))
|
||||||
|
if not cands: sys.exit(f"Aucun transcript pour projet {proj}")
|
||||||
|
return max(cands, key=os.path.getmtime)
|
||||||
|
return arg
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) < 2: sys.exit(__doc__)
|
||||||
|
f = resolve_file(sys.argv[1])
|
||||||
|
calls = [] # (name, args_len)
|
||||||
|
results = {} # tool_use_id -> result_chars
|
||||||
|
id_to_name = {}
|
||||||
|
tot_in = tot_out = tot_cache_r = tot_cache_w = 0
|
||||||
|
|
||||||
|
for line in open(f):
|
||||||
|
try: d = json.loads(line)
|
||||||
|
except: continue
|
||||||
|
msg = d.get("message", {})
|
||||||
|
if not isinstance(msg, dict): continue
|
||||||
|
u = msg.get("usage")
|
||||||
|
if u:
|
||||||
|
tot_in += u.get("input_tokens",0)
|
||||||
|
tot_out += u.get("output_tokens",0)
|
||||||
|
tot_cache_r += u.get("cache_read_input_tokens",0)
|
||||||
|
tot_cache_w += u.get("cache_creation_input_tokens",0)
|
||||||
|
for c in (msg.get("content") or []):
|
||||||
|
if not isinstance(c, dict): continue
|
||||||
|
if c.get("type") == "tool_use" and is_leadtech(c.get("name","")):
|
||||||
|
calls.append((c.get("name"), approx_tokens(json.dumps(c.get("input",{}),ensure_ascii=False))))
|
||||||
|
id_to_name[c.get("id")] = c.get("name")
|
||||||
|
if c.get("type") == "tool_result":
|
||||||
|
rid = c.get("tool_use_id")
|
||||||
|
if rid in id_to_name:
|
||||||
|
content = c.get("content")
|
||||||
|
txt = content if isinstance(content,str) else json.dumps(content,ensure_ascii=False)
|
||||||
|
results[rid] = approx_tokens(txt)
|
||||||
|
|
||||||
|
print(f"# Transcript: {Path(f).name}\n")
|
||||||
|
print(f"Total session : in={tot_in} out={tot_out} cache_read={tot_cache_r} cache_write={tot_cache_w}")
|
||||||
|
print(f"Appels MCP leadtech : {len(calls)}")
|
||||||
|
if not calls:
|
||||||
|
print(" (aucun appel MCP leadtech dans cette session)")
|
||||||
|
return
|
||||||
|
by_tool = {}
|
||||||
|
for name, _ in calls:
|
||||||
|
by_tool[name] = by_tool.get(name,0)+1
|
||||||
|
res_tokens = sum(results.values())
|
||||||
|
print(f" par tool: " + ", ".join(f"{k}×{v}" for k,v in by_tool.items()))
|
||||||
|
print(f" tokens des résultats MCP injectés : ~{res_tokens}")
|
||||||
|
if tot_in:
|
||||||
|
print(f" part estimée des résultats MCP / input non caché : ~{round(100*res_tokens/max(tot_in,1))}%")
|
||||||
|
print(f"\n détail résultats par appel:")
|
||||||
|
for rid, name in id_to_name.items():
|
||||||
|
print(f" {name:<24} ~{results.get(rid,0)} tok")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user