chrome-bridge-mcp 1.8.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.
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Misura deterministica del costo di schema dei tool MCP.
4
+ *
5
+ * Fonte di verità unica per i numeri pubblicati in README, RESULTS e listing:
6
+ * carica il modulo reale `server/tools.js`, cattura ogni
7
+ * server.tool(name, desc, schema, handler) e serializza lo schema con lo STESSO
8
+ * converter dell'SDK MCP (toJsonSchemaCompat) — byte reali, non stime.
9
+ *
10
+ * Il conteggio dei tool era divergente in cinque punti del repo (59 / 56 / 30):
11
+ * `test/unit/tool-counts.test.js` confronta questi numeri con i documenti e
12
+ * fallisce quando si separano.
13
+ *
14
+ * Uso:
15
+ * node tools/measure-schema.mjs # riepilogo leggibile
16
+ * node tools/measure-schema.mjs --json # JSON completo (per script/CI)
17
+ * node tools/measure-schema.mjs --caps core
18
+ */
19
+ import { z } from 'zod';
20
+ import { toJsonSchemaCompat } from '@modelcontextprotocol/sdk/server/zod-json-schema-compat.js';
21
+ import { registerTools, TOOL_CAPS } from '../server/tools.js';
22
+
23
+ const argv = process.argv.slice(2);
24
+ const asJson = argv.includes('--json');
25
+ const capsIdx = argv.indexOf('--caps');
26
+ const caps = capsIdx !== -1 ? argv[capsIdx + 1] : 'all';
27
+
28
+ const B = (s) => Buffer.byteLength(s ?? '', 'utf8');
29
+
30
+ export function measure(capsFilter = 'all') {
31
+ const captured = [];
32
+ const fakeServer = { tool(name, desc, schema) { captured.push({ name, desc, schema }); } };
33
+ const fakeWs = { isConnected: () => false, mode: 'primary', host: '127.0.0.1', port: 8765, sendCommand: async () => ({}) };
34
+ registerTools(fakeServer, fakeWs, capsFilter);
35
+
36
+ const toolToCap = new Map();
37
+ for (const [group, names] of Object.entries(TOOL_CAPS)) for (const n of names) toolToCap.set(n, group);
38
+
39
+ const tools = captured.map((t) => {
40
+ const js = toJsonSchemaCompat(z.object(t.schema ?? {}), { strictUnions: true });
41
+ const schemaJson = JSON.stringify(js);
42
+ const props = js.properties ? Object.keys(js.properties) : [];
43
+ const required = js.required ?? [];
44
+ // Payload MCP reale per tool: {"name":..,"description":..,"inputSchema":..}
45
+ const wire = JSON.stringify({ name: t.name, description: t.desc, inputSchema: js });
46
+ return {
47
+ name: t.name,
48
+ cap: toolToCap.get(t.name) ?? 'core',
49
+ name_bytes: B(t.name),
50
+ desc_bytes: B(t.desc),
51
+ schema_bytes: B(schemaJson),
52
+ total_bytes: B(t.name) + B(t.desc) + B(schemaJson),
53
+ wire_bytes: B(wire),
54
+ n_params: props.length,
55
+ n_required: required.length,
56
+ n_optional: props.length - required.length,
57
+ };
58
+ });
59
+
60
+ const sum = (key, list = tools) => list.reduce((a, t) => a + t[key], 0);
61
+ const byCap = {};
62
+ for (const t of tools) {
63
+ const c = (byCap[t.cap] ??= { n: 0, total_bytes: 0, wire_bytes: 0, names: [] });
64
+ c.n += 1; c.total_bytes += t.total_bytes; c.wire_bytes += t.wire_bytes; c.names.push(t.name);
65
+ }
66
+
67
+ return {
68
+ caps: capsFilter,
69
+ totals: {
70
+ n_tools: tools.length,
71
+ sum_name_bytes: sum('name_bytes'),
72
+ sum_desc_bytes: sum('desc_bytes'),
73
+ sum_schema_bytes: sum('schema_bytes'),
74
+ sum_total_bytes: sum('total_bytes'),
75
+ sum_wire_bytes: sum('wire_bytes'),
76
+ // 4 byte/token è l'euristica usata anche nei confronti pubblicati:
77
+ // approssimata per costruzione, ma applicata identica a noi e agli altri.
78
+ est_tokens: Math.round(sum('wire_bytes') / 4),
79
+ n_required: sum('n_required'),
80
+ n_optional: sum('n_optional'),
81
+ },
82
+ byCap,
83
+ tools: tools.sort((a, b) => b.total_bytes - a.total_bytes),
84
+ };
85
+ }
86
+
87
+ if (import.meta.url === `file://${process.argv[1]}`) {
88
+ const all = measure(caps);
89
+ if (asJson) {
90
+ process.stdout.write(JSON.stringify(all, null, 2) + '\n');
91
+ } else {
92
+ const t = all.totals;
93
+ console.log(`caps=${all.caps}: ${t.n_tools} tool`);
94
+ console.log(` nome+descrizione+schema : ${t.sum_total_bytes} B`);
95
+ console.log(` payload tools/list : ${t.sum_wire_bytes} B (~${(t.est_tokens / 1000).toFixed(1)}k token @4B)`);
96
+ console.log(` di cui schema : ${t.sum_schema_bytes} B (${Math.round(t.sum_schema_bytes / t.sum_total_bytes * 100)}%)`);
97
+ console.log(` di cui descrizioni : ${t.sum_desc_bytes} B (${Math.round(t.sum_desc_bytes / t.sum_total_bytes * 100)}%)`);
98
+ console.log(` parametri : ${t.n_required} obbligatori, ${t.n_optional} opzionali`);
99
+ console.log('\nper gruppo:');
100
+ for (const [cap, c] of Object.entries(all.byCap).sort((a, b) => b[1].total_bytes - a[1].total_bytes)) {
101
+ console.log(` ${cap.padEnd(8)} ${String(c.n).padStart(2)} tool ${String(c.total_bytes).padStart(6)} B`);
102
+ }
103
+ console.log('\ntop 10 per costo:');
104
+ for (const x of all.tools.slice(0, 10)) {
105
+ console.log(` ${x.name.padEnd(24)} ${String(x.total_bytes).padStart(5)} B (desc ${x.desc_bytes}, schema ${x.schema_bytes})`);
106
+ }
107
+ }
108
+ }