thumbgate 1.10.1 → 1.11.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thumbgate-marketplace",
3
- "version": "1.10.1",
3
+ "version": "1.11.0",
4
4
  "owner": {
5
5
  "name": "Igor Ganapolsky",
6
6
  "email": "ig5973700@gmail.com"
@@ -13,7 +13,7 @@
13
13
  "source": "npm",
14
14
  "package": "thumbgate"
15
15
  },
16
- "version": "1.10.1",
16
+ "version": "1.11.0",
17
17
  "author": {
18
18
  "name": "Igor Ganapolsky"
19
19
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "thumbgate",
3
3
  "description": "Type 👍 or 👎 on any agent action. ThumbGate captures it, distills a lesson, and blocks the pattern from repeating. One thumbs-down = the agent physically cannot make that mistake again. 33 pre-action gates, budget enforcement, self-protection, and NIST/SOC2 compliance tags.",
4
- "version": "1.10.1",
4
+ "version": "1.11.0",
5
5
  "author": {
6
6
  "name": "Igor Ganapolsky"
7
7
  },
@@ -48,6 +48,7 @@ npx thumbgate init --agent claude-code
48
48
 
49
49
  - Agent discovery: https://thumbgate-production.up.railway.app/.well-known/mcp.json
50
50
  - Progressive tool index: https://thumbgate-production.up.railway.app/.well-known/mcp/tools.json
51
+ - Context footprint report: https://thumbgate-production.up.railway.app/.well-known/mcp/footprint.json
51
52
  - Agent skills: https://thumbgate-production.up.railway.app/.well-known/mcp/skills.json
52
53
  - MCP applications: https://thumbgate-production.up.railway.app/.well-known/mcp/applications.json
53
54
  - Documentation: https://thumbgate-production.up.railway.app/guide
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thumbgate",
3
- "version": "1.10.1",
3
+ "version": "1.11.0",
4
4
  "description": "ThumbGate — 👍👎 feedback that teaches your AI agent. Thumbs down a mistake, it never happens again.",
5
5
  "homepage": "https://thumbgate-production.up.railway.app",
6
6
  "transport": "stdio",
@@ -8,6 +8,7 @@
8
8
  "manifestUrl": "https://thumbgate-production.up.railway.app/.well-known/mcp.json",
9
9
  "toolIndexUrl": "https://thumbgate-production.up.railway.app/.well-known/mcp/tools.json",
10
10
  "toolSchemaUrlTemplate": "https://thumbgate-production.up.railway.app/.well-known/mcp/tools/{name}.json",
11
+ "footprintUrl": "https://thumbgate-production.up.railway.app/.well-known/mcp/footprint.json",
11
12
  "skillsUrl": "https://thumbgate-production.up.railway.app/.well-known/mcp/skills.json",
12
13
  "applicationsUrl": "https://thumbgate-production.up.railway.app/.well-known/mcp/applications.json"
13
14
  }
@@ -3,7 +3,7 @@
3
3
  - `chatgpt/openapi.yaml`: import into GPT Actions.
4
4
  - `gemini/function-declarations.json`: Gemini function-calling definitions.
5
5
  - `mcp/server-stdio.js`: underlying local MCP stdio server implementation.
6
- - `claude/.mcp.json`: example Claude Code MCP config using `npx --yes --package thumbgate@1.10.1 thumbgate serve`.
6
+ - `claude/.mcp.json`: example Claude Code MCP config using `npx --yes --package thumbgate@1.11.0 thumbgate serve`.
7
7
  - `codex/config.toml`: example Codex MCP profile section using the same version-pinned portable launcher.
8
8
  - `amp/skills/thumbgate-feedback/SKILL.md`: Amp skill template.
9
9
  - `opencode/opencode.json`: portable OpenCode MCP profile using the same version-pinned portable launcher.
@@ -2,13 +2,13 @@
2
2
  "mcpServers": {
3
3
  "thumbgate": {
4
4
  "command": "npx",
5
- "args": ["--yes", "--package", "thumbgate@1.10.1", "thumbgate", "serve"]
5
+ "args": ["--yes", "--package", "thumbgate@1.11.0", "thumbgate", "serve"]
6
6
  }
7
7
  },
8
8
  "hooks": {
9
9
  "preToolUse": {
10
10
  "command": "npx",
11
- "args": ["--yes", "--package", "thumbgate@1.10.1", "thumbgate", "gate-check"]
11
+ "args": ["--yes", "--package", "thumbgate@1.11.0", "thumbgate", "gate-check"]
12
12
  }
13
13
  }
14
14
  }
@@ -122,6 +122,7 @@ const {
122
122
  } = require('../../scripts/natural-language-harness');
123
123
  const { runLoop: runAutoresearchLoop } = require('../../scripts/autoresearch-runner');
124
124
  const { TOOLS } = require('../../scripts/tool-registry');
125
+ const { buildContextFootprintReport } = require('../../scripts/context-footprint');
125
126
  const { reflect: reflectOnFeedback } = require('../../scripts/reflector-agent');
126
127
  const { submitProductIssue } = require('../../scripts/product-feedback');
127
128
  const {
@@ -152,7 +153,7 @@ const {
152
153
  finalizeSession: finalizeFeedbackSession,
153
154
  } = require('../../scripts/feedback-session');
154
155
 
155
- const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.10.1' };
156
+ const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.11.0' };
156
157
  const COMMERCE_CATEGORIES = [
157
158
  'product_recommendation',
158
159
  'brand_compliance',
@@ -493,6 +494,7 @@ async function callTool(name, args = {}) {
493
494
  }
494
495
 
495
496
  async function callToolInner(name, args) {
497
+ args = args || {};
496
498
  // Semantic Aliases for high-level branding alignment
497
499
  if (name === 'capture_memory_feedback') name = 'capture_feedback';
498
500
  if (name === 'get_reliability_rules') name = 'prevention_rules';
@@ -868,6 +870,17 @@ async function callToolInner(name, args) {
868
870
  return toTextResult(runHarness(args.harness, args.inputs || {}, { jobId: args.jobId }));
869
871
  case 'plan_multimodal_retrieval':
870
872
  return toTextResult(buildMultimodalRetrievalPlan(args));
873
+ case 'plan_context_footprint':
874
+ return toTextResult(buildContextFootprintReport({
875
+ tools: TOOLS,
876
+ entries: Array.isArray(args.entries) ? args.entries : undefined,
877
+ anchors: Array.isArray(args.anchors) ? args.anchors : undefined,
878
+ schemaUrlTemplate: args.schemaUrlTemplate || '/.well-known/mcp/tools/{name}.json',
879
+ targetReduction: args.targetReduction,
880
+ windowSize: args.windowSize,
881
+ perEntryMaxChars: args.perEntryMaxChars,
882
+ totalMaxChars: args.totalMaxChars,
883
+ }));
871
884
  case 'run_autoresearch': {
872
885
  const iterations = Math.max(1, Math.min(5, Number(args.iterations || 1)));
873
886
  const timeoutMs = Math.max(1000, Math.min(600000, Number(args.timeoutMs || 120000)));
@@ -7,7 +7,7 @@
7
7
  "npx",
8
8
  "--yes",
9
9
  "--package",
10
- "thumbgate@1.10.1",
10
+ "thumbgate@1.11.0",
11
11
  "thumbgate",
12
12
  "serve"
13
13
  ],
@@ -13,6 +13,7 @@
13
13
  "retrieve_lessons",
14
14
  "search_thumbgate",
15
15
  "plan_multimodal_retrieval",
16
+ "plan_context_footprint",
16
17
  "reflect_on_feedback",
17
18
  "feedback_stats",
18
19
  "diagnose_failure",
@@ -73,6 +74,7 @@
73
74
  "retrieve_lessons",
74
75
  "search_thumbgate",
75
76
  "plan_multimodal_retrieval",
77
+ "plan_context_footprint",
76
78
  "reflect_on_feedback",
77
79
  "prevention_rules",
78
80
  "set_task_scope",
@@ -118,6 +120,7 @@
118
120
  "retrieve_lessons",
119
121
  "search_thumbgate",
120
122
  "plan_multimodal_retrieval",
123
+ "plan_context_footprint",
121
124
  "feedback_stats",
122
125
  "diagnose_failure",
123
126
  "list_harnesses",
@@ -151,6 +154,7 @@
151
154
  "retrieve_lessons",
152
155
  "search_thumbgate",
153
156
  "plan_multimodal_retrieval",
157
+ "plan_context_footprint",
154
158
  "feedback_stats",
155
159
  "diagnose_failure",
156
160
  "list_harnesses",
@@ -180,6 +184,7 @@
180
184
  "search_lessons",
181
185
  "retrieve_lessons",
182
186
  "search_thumbgate",
187
+ "plan_context_footprint",
183
188
  "diagnose_failure",
184
189
  "list_intents",
185
190
  "plan_intent",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thumbgate",
3
- "version": "1.10.1",
3
+ "version": "1.11.0",
4
4
  "description": "Self-improving agent governance: type thumbs-up or thumbs-down on any AI agent action. ThumbGate turns every mistake into a prevention rule and blocks the pattern from repeating. One thumbs-down, never again. 33 pre-action gates, budget enforcement, and self-protection for Claude Code, Cursor, Codex, Gemini CLI, and Amp.",
5
5
  "homepage": "https://thumbgate-production.up.railway.app",
6
6
  "repository": {
@@ -76,6 +76,7 @@
76
76
  "scripts/conversation-context.js",
77
77
  "scripts/creator-campaigns.js",
78
78
  "scripts/cross-encoder-reranker.js",
79
+ "scripts/context-footprint.js",
79
80
  "scripts/daemon-manager.js",
80
81
  "scripts/dashboard-render-spec.js",
81
82
  "scripts/dashboard.js",
@@ -293,7 +294,7 @@
293
294
  "test:pulse": "node --test tests/pulse.test.js",
294
295
  "test:semantic-layer": "node --test tests/semantic-layer.test.js",
295
296
  "test:data-pipeline": "node --test tests/data-pipeline.test.js",
296
- "test:optimize-context": "node --test tests/optimize-context.test.js",
297
+ "test:optimize-context": "node --test tests/optimize-context.test.js tests/context-footprint.test.js",
297
298
  "test:principle-extractor": "node --test tests/principle-extractor.test.js",
298
299
  "test:analytics-window": "node --test tests/analytics-window.test.js",
299
300
  "test:funnel-analytics": "node --test tests/funnel-analytics.test.js",
package/public/index.html CHANGED
@@ -974,7 +974,7 @@ __GA_BOOTSTRAP__
974
974
  <!-- HOW IT WORKS -->
975
975
  <section class="how-it-works" id="how-it-works">
976
976
  <div class="container">
977
- <div class="section-label">New in v1.10.1</div>
977
+ <div class="section-label">New in v1.11.0</div>
978
978
  <h2 class="section-title">Three steps to stop repeated AI failures</h2>
979
979
  <div class="steps">
980
980
  <div class="step">
@@ -1330,7 +1330,7 @@ __GA_BOOTSTRAP__
1330
1330
  <a href="https://www.linkedin.com/in/igorganapolsky" target="_blank" rel="noopener">LinkedIn</a>
1331
1331
  <a href="/blog">Blog</a>
1332
1332
  </div>
1333
- <span class="footer-copy">© 2026 Max Smith KDP LLC · MIT License · v1.10.1</span>
1333
+ <span class="footer-copy">© 2026 Max Smith KDP LLC · MIT License · v1.11.0</span>
1334
1334
  </div>
1335
1335
  </footer>
1336
1336
 
@@ -0,0 +1,191 @@
1
+ 'use strict';
2
+
3
+ const DEFAULT_CHARS_PER_TOKEN = 4;
4
+ const DEFAULT_TARGET_REDUCTION = 0.22;
5
+
6
+ function normalizeRatio(value, fallback = DEFAULT_TARGET_REDUCTION) {
7
+ const n = Number(value);
8
+ if (!Number.isFinite(n) || n < 0) return fallback;
9
+ if (n > 1) return n / 100;
10
+ return n;
11
+ }
12
+
13
+ function stablePayload(value) {
14
+ if (typeof value === 'string') return value;
15
+ return JSON.stringify(value == null ? '' : value, null, 2);
16
+ }
17
+
18
+ function estimateTokens(value, charsPerToken = DEFAULT_CHARS_PER_TOKEN) {
19
+ const payload = stablePayload(value);
20
+ const divisor = Math.max(1, Number(charsPerToken) || DEFAULT_CHARS_PER_TOKEN);
21
+ return Math.ceil(payload.length / divisor);
22
+ }
23
+
24
+ function measureFootprint(baseline, optimized, options = {}) {
25
+ const baselinePayload = stablePayload(baseline);
26
+ const optimizedPayload = stablePayload(optimized);
27
+ const baselineBytes = Buffer.byteLength(baselinePayload, 'utf8');
28
+ const optimizedBytes = Buffer.byteLength(optimizedPayload, 'utf8');
29
+ const baselineTokens = estimateTokens(baselinePayload, options.charsPerToken);
30
+ const optimizedTokens = estimateTokens(optimizedPayload, options.charsPerToken);
31
+ const bytesSaved = Math.max(0, baselineBytes - optimizedBytes);
32
+ const tokensSaved = Math.max(0, baselineTokens - optimizedTokens);
33
+ const reductionRatio = baselineBytes > 0 ? bytesSaved / baselineBytes : 0;
34
+
35
+ return {
36
+ baseline: {
37
+ bytes: baselineBytes,
38
+ estimatedTokens: baselineTokens,
39
+ },
40
+ optimized: {
41
+ bytes: optimizedBytes,
42
+ estimatedTokens: optimizedTokens,
43
+ },
44
+ savings: {
45
+ bytes: bytesSaved,
46
+ estimatedTokens: tokensSaved,
47
+ reductionRatio,
48
+ reductionPercent: Number((reductionRatio * 100).toFixed(1)),
49
+ targetMet: reductionRatio >= normalizeRatio(options.targetReduction),
50
+ },
51
+ };
52
+ }
53
+
54
+ function toolSchemaUrl(schemaUrlTemplate, toolName) {
55
+ const encodedName = encodeURIComponent(String(toolName || ''));
56
+ const template = String(schemaUrlTemplate || '/.well-known/mcp/tools/{name}.json');
57
+ return template.includes('{name}')
58
+ ? template.replace('{name}', encodedName)
59
+ : `${template.replace(/\/$/, '')}/${encodedName}.json`;
60
+ }
61
+
62
+ function normalizeToolForFullManifest(tool) {
63
+ return {
64
+ name: tool.name,
65
+ description: tool.description,
66
+ annotations: tool.annotations || {},
67
+ inputSchema: tool.inputSchema || {},
68
+ };
69
+ }
70
+
71
+ function normalizeToolForProgressiveManifest(tool, schemaUrlTemplate) {
72
+ return {
73
+ name: tool.name,
74
+ description: tool.description,
75
+ annotations: tool.annotations || {},
76
+ schemaUrl: toolSchemaUrl(schemaUrlTemplate, tool.name),
77
+ };
78
+ }
79
+
80
+ function buildMcpToolFootprintReport(tools = [], options = {}) {
81
+ const toolList = Array.isArray(tools) ? tools : [];
82
+ const schemaUrlTemplate = options.schemaUrlTemplate || '/.well-known/mcp/tools/{name}.json';
83
+ const baseline = {
84
+ pattern: 'preload-all-tool-schemas',
85
+ tools: toolList.map(normalizeToolForFullManifest),
86
+ };
87
+ const optimized = {
88
+ pattern: 'progressive-tool-discovery',
89
+ tools: toolList.map((tool) => normalizeToolForProgressiveManifest(tool, schemaUrlTemplate)),
90
+ };
91
+
92
+ return {
93
+ kind: 'mcp-tool-discovery',
94
+ strategy: 'lossless-progressive-disclosure',
95
+ toolCount: toolList.length,
96
+ qualityContract: {
97
+ behaviorPreserved: true,
98
+ reason: 'Each omitted inputSchema is still available through the tool schema URL.',
99
+ schemaUrlTemplate,
100
+ },
101
+ footprint: measureFootprint(baseline, optimized, {
102
+ targetReduction: options.targetReduction,
103
+ charsPerToken: options.charsPerToken,
104
+ }),
105
+ };
106
+ }
107
+
108
+ function buildFeedbackContextFootprintReport(entries = [], anchors = [], options = {}) {
109
+ const { compactContext } = require('./context-engine');
110
+ const safeEntries = Array.isArray(entries) ? entries : [];
111
+ const safeAnchors = Array.isArray(anchors) ? anchors : [];
112
+ const compaction = compactContext(safeEntries, safeAnchors, {
113
+ windowSize: options.windowSize,
114
+ perEntryMaxChars: options.perEntryMaxChars,
115
+ totalMaxChars: options.totalMaxChars,
116
+ });
117
+
118
+ const anchorIds = new Set(safeAnchors.map((entry) => entry && entry.id).filter(Boolean));
119
+ const optimizedAnchorIds = new Set(compaction.entries.map((entry) => entry && entry.id).filter(Boolean));
120
+ const anchorsPreserved = Array.from(anchorIds).every((id) => optimizedAnchorIds.has(id));
121
+
122
+ return {
123
+ kind: 'feedback-context-compaction',
124
+ strategy: 'bounded-context-compaction',
125
+ qualityContract: {
126
+ behaviorPreserved: false,
127
+ anchorsPreserved,
128
+ reason: 'Feedback context is intentionally bounded; anchor entries are preserved while stale or duplicate entries are removed.',
129
+ },
130
+ compaction: {
131
+ stage: compaction.stage,
132
+ removedCount: compaction.removedCount,
133
+ compacted: compaction.compacted,
134
+ baselineItems: safeEntries.length,
135
+ optimizedItems: compaction.entries.length,
136
+ },
137
+ footprint: measureFootprint(safeEntries, compaction.entries, {
138
+ targetReduction: options.targetReduction,
139
+ charsPerToken: options.charsPerToken,
140
+ }),
141
+ };
142
+ }
143
+
144
+ function buildContextFootprintReport(options = {}) {
145
+ const targetReduction = normalizeRatio(options.targetReduction);
146
+ const report = {
147
+ name: 'thumbgate-context-footprint',
148
+ targetReduction,
149
+ sourcePattern: 'Compress the bottleneck without changing the behavior agents rely on.',
150
+ recommendations: [
151
+ 'Load the MCP tool index first; fetch one tool schema only when the agent selects that tool.',
152
+ 'Use construct_context_pack with maxChars before injecting lessons into a model prompt.',
153
+ 'Keep gate ids, proof URLs, and anchor lessons stable so compaction does not hide evidence.',
154
+ 'Track estimated token savings beside every optimized context path.',
155
+ ],
156
+ };
157
+
158
+ if (Array.isArray(options.tools)) {
159
+ report.mcpToolDiscovery = buildMcpToolFootprintReport(options.tools, {
160
+ schemaUrlTemplate: options.schemaUrlTemplate,
161
+ targetReduction,
162
+ charsPerToken: options.charsPerToken,
163
+ });
164
+ }
165
+
166
+ if (Array.isArray(options.entries)) {
167
+ report.feedbackContext = buildFeedbackContextFootprintReport(
168
+ options.entries,
169
+ options.anchors,
170
+ {
171
+ windowSize: options.windowSize,
172
+ perEntryMaxChars: options.perEntryMaxChars,
173
+ totalMaxChars: options.totalMaxChars,
174
+ targetReduction,
175
+ charsPerToken: options.charsPerToken,
176
+ },
177
+ );
178
+ }
179
+
180
+ return report;
181
+ }
182
+
183
+ module.exports = {
184
+ DEFAULT_CHARS_PER_TOKEN,
185
+ DEFAULT_TARGET_REDUCTION,
186
+ estimateTokens,
187
+ measureFootprint,
188
+ buildMcpToolFootprintReport,
189
+ buildFeedbackContextFootprintReport,
190
+ buildContextFootprintReport,
191
+ };
@@ -153,6 +153,45 @@ const TOOLS = [
153
153
  },
154
154
  },
155
155
  }),
156
+ readOnlyTool({
157
+ name: 'plan_context_footprint',
158
+ description: 'Estimate MCP schema and feedback-context footprint before loading large manifests into an agent prompt. Reports progressive-discovery savings, context compaction savings, and proof-preserving recommendations.',
159
+ inputSchema: {
160
+ type: 'object',
161
+ properties: {
162
+ entries: {
163
+ type: 'array',
164
+ description: 'Optional feedback/context entries to compact and measure.',
165
+ items: { type: 'object' },
166
+ },
167
+ anchors: {
168
+ type: 'array',
169
+ description: 'Optional entries that must survive compaction.',
170
+ items: { type: 'object' },
171
+ },
172
+ schemaUrlTemplate: {
173
+ type: 'string',
174
+ description: 'Template for progressive MCP tool schema URLs, using {name}.',
175
+ },
176
+ targetReduction: {
177
+ type: 'number',
178
+ description: 'Target footprint reduction as a ratio or percentage. Default: 0.22.',
179
+ },
180
+ windowSize: {
181
+ type: 'number',
182
+ description: 'Feedback compaction recency window.',
183
+ },
184
+ perEntryMaxChars: {
185
+ type: 'number',
186
+ description: 'Maximum characters retained per large feedback field.',
187
+ },
188
+ totalMaxChars: {
189
+ type: 'number',
190
+ description: 'Optional total character budget for compacted feedback entries.',
191
+ },
192
+ },
193
+ },
194
+ }),
156
195
  destructiveTool({
157
196
  name: 'import_document',
158
197
  description: 'Import a local policy or runbook document into ThumbGate, normalize it for search, and propose provenance-backed gate candidates.',
package/src/api/server.js CHANGED
@@ -208,6 +208,9 @@ const {
208
208
  } = require('../../scripts/rate-limiter');
209
209
  const { sendProblem, PROBLEM_TYPES } = require('../../scripts/problem-detail');
210
210
  const { TOOLS: MCP_TOOLS } = require('../../scripts/tool-registry');
211
+ const {
212
+ buildContextFootprintReport,
213
+ } = require('../../scripts/context-footprint');
211
214
  const {
212
215
  findSeoPageByPath,
213
216
  renderSeoPageHtml,
@@ -567,6 +570,13 @@ function getToolDiscoveryIndex(hostedConfig) {
567
570
  }));
568
571
  }
569
572
 
573
+ function getContextFootprintReport(hostedConfig) {
574
+ return buildContextFootprintReport({
575
+ tools: MCP_TOOLS,
576
+ schemaUrlTemplate: buildPublicUrl(hostedConfig, '/.well-known/mcp/tools/{name}.json'),
577
+ });
578
+ }
579
+
570
580
  function getMcpSkillManifests(hostedConfig) {
571
581
  return [
572
582
  {
@@ -611,6 +621,21 @@ function getMcpSkillManifests(hostedConfig) {
611
621
  contextUrl: buildPublicUrl(hostedConfig, '/public/llm-context.md'),
612
622
  proofUrl: VERIFICATION_EVIDENCE_URL,
613
623
  },
624
+ {
625
+ name: 'context-footprint-optimizer',
626
+ title: 'Context Footprint Optimizer',
627
+ description: 'Measure MCP schema payloads and feedback-context packs before spending model context on them.',
628
+ triggers: ['context compression', 'token savings', 'progressive discovery', 'MCP schema loading', 'context budget'],
629
+ recommendedFlow: [
630
+ 'Measure full schema or memory payload footprint.',
631
+ 'Load the slim index first, then fetch only selected tool schemas.',
632
+ 'Compact feedback context with anchors for proof-critical lessons.',
633
+ 'Record estimated token savings next to the workflow evidence.',
634
+ ],
635
+ contextUrl: buildPublicUrl(hostedConfig, '/public/llm-context.md'),
636
+ footprintUrl: buildPublicUrl(hostedConfig, '/.well-known/mcp/footprint.json'),
637
+ proofUrl: VERIFICATION_EVIDENCE_URL,
638
+ },
614
639
  ];
615
640
  }
616
641
 
@@ -671,6 +696,7 @@ function getMcpDiscoveryManifest(hostedConfig) {
671
696
  serverCardUrl: buildPublicUrl(hostedConfig, '/.well-known/mcp/server-card.json'),
672
697
  toolIndexUrl: buildPublicUrl(hostedConfig, '/.well-known/mcp/tools.json'),
673
698
  toolSchemaUrlTemplate: buildPublicUrl(hostedConfig, '/.well-known/mcp/tools/{name}.json'),
699
+ footprintUrl: buildPublicUrl(hostedConfig, '/.well-known/mcp/footprint.json'),
674
700
  skillsUrl: buildPublicUrl(hostedConfig, '/.well-known/mcp/skills.json'),
675
701
  applicationsUrl: buildPublicUrl(hostedConfig, '/.well-known/mcp/applications.json'),
676
702
  llmsTxtUrl: buildPublicUrl(hostedConfig, '/.well-known/llms.txt'),
@@ -705,9 +731,15 @@ function getMcpDiscoveryManifest(hostedConfig) {
705
731
  description: 'Plan screenshot/PDF/proof-artifact retrieval before investing in multimodal finetuning.',
706
732
  tools: ['plan_multimodal_retrieval', 'search_thumbgate', 'construct_context_pack', 'require_evidence_for_claim'],
707
733
  },
734
+ {
735
+ name: 'context-footprint-optimizer',
736
+ description: 'Measure MCP schema and feedback-context footprint before loading large manifests into model context.',
737
+ tools: ['plan_context_footprint', 'construct_context_pack', 'context_provenance'],
738
+ },
708
739
  ],
709
740
  skills: getMcpSkillManifests(hostedConfig),
710
741
  applications: getMcpApplications(hostedConfig),
742
+ footprint: getContextFootprintReport(hostedConfig),
711
743
  proof: {
712
744
  verificationEvidenceUrl: VERIFICATION_EVIDENCE_URL,
713
745
  llmContextUrl: buildPublicUrl(hostedConfig, '/public/llm-context.md'),
@@ -4086,6 +4118,17 @@ async function addContext(){
4086
4118
  return;
4087
4119
  }
4088
4120
 
4121
+ if (isGetLikeRequest && pathname === '/.well-known/mcp/footprint.json') {
4122
+ sendJson(res, 200, {
4123
+ name: 'thumbgate',
4124
+ version: pkg.version,
4125
+ ...getContextFootprintReport(hostedConfig),
4126
+ }, {}, {
4127
+ headOnly: isHeadRequest,
4128
+ });
4129
+ return;
4130
+ }
4131
+
4089
4132
  if (isGetLikeRequest && pathname.startsWith('/.well-known/mcp/tools/') && pathname.endsWith('.json')) {
4090
4133
  const encodedToolName = pathname.slice('/.well-known/mcp/tools/'.length, -'.json'.length);
4091
4134
  let toolName = encodedToolName;
@@ -4156,6 +4199,7 @@ async function addContext(){
4156
4199
  version: pkg.version,
4157
4200
  transport: discoveryManifest.transport,
4158
4201
  discovery: discoveryManifest.discovery,
4202
+ footprint: discoveryManifest.footprint,
4159
4203
  tools: getServerCardTools(),
4160
4204
  skills: getMcpSkillManifests(hostedConfig),
4161
4205
  applications: getMcpApplications(hostedConfig),