thumbgate 1.7.0 → 1.9.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.
@@ -108,17 +108,28 @@ fi
108
108
  # ── ThumbGate package metadata ────────────────────────────────────────
109
109
  TG_VERSION="unknown"; TG_TIER="Free"
110
110
  _META_JSON=$(node "${SCRIPT_DIR}/statusline-meta.js" 2>/dev/null)
111
- if [ -n "$_META_JSON" ]; then
111
+ if [[ -n "$_META_JSON" ]]; then
112
112
  eval "$(echo "$_META_JSON" | jq -r '
113
113
  @sh "TG_VERSION=\(.version // "unknown")",
114
114
  @sh "TG_TIER=\(.tier // "Free")"
115
115
  ' 2>/dev/null)"
116
116
  fi
117
117
 
118
+ # ── Repo context (branch / work item / PR) ───────────────────────────
119
+ BRANCH_NAME=""; WORK_ITEM_LABEL=""; PR_LABEL=""
120
+ _CONTEXT_JSON=$(node "${SCRIPT_DIR}/statusline-context.js" 2>/dev/null)
121
+ if [[ -n "$_CONTEXT_JSON" ]]; then
122
+ eval "$(echo "$_CONTEXT_JSON" | jq -r '
123
+ @sh "BRANCH_NAME=\(.branchName // "")",
124
+ @sh "WORK_ITEM_LABEL=\(.workItemLabel // "")",
125
+ @sh "PR_LABEL=\(.prLabel // "")"
126
+ ' 2>/dev/null)"
127
+ fi
128
+
118
129
  # ── Control Tower stats ──────────────────────────────────────────
119
130
  SLO_V="0"; AT_RISK="0"; ANOMALIES="0"
120
131
  _TOWER_JSON=$(node "${SCRIPT_DIR}/statusline-tower.js" 2>/dev/null)
121
- if [ -n "$_TOWER_JSON" ]; then
132
+ if [[ -n "$_TOWER_JSON" ]]; then
122
133
  eval "$(echo "$_TOWER_JSON" | jq -r '
123
134
  @sh "SLO_V=\(.sloViolations // 0)",
124
135
  @sh "AT_RISK=\(.atRiskToolCount // 0)",
@@ -129,7 +140,7 @@ fi
129
140
  # ── Latest lesson (data available for extensions; not rendered in statusbar) ──
130
141
  LESSON_TEXT=""; LESSON_ID=""; LESSON_LABEL=""; LESSON_LINK=""
131
142
  _LESSON_JSON=$(node "${SCRIPT_DIR}/statusline-lesson.js" 2>/dev/null)
132
- if [ -n "$_LESSON_JSON" ]; then
143
+ if [[ -n "$_LESSON_JSON" ]]; then
133
144
  eval "$(echo "$_LESSON_JSON" | jq -r '
134
145
  @sh "LESSON_TEXT=\(.text // "")",
135
146
  @sh "LESSON_ID=\(.lessonId // "")",
@@ -155,7 +166,7 @@ osc_link() {
155
166
  # ThumbGate as a non-last row in a multi-line statusline should set this, because
156
167
  # some agents (Claude Code) silently drop downstream rows when a preceding row
157
168
  # contains OSC 8 sequences.
158
- if [ "${THUMBGATE_STATUSLINE_PLAIN:-0}" = "1" ]; then
169
+ if [[ "${THUMBGATE_STATUSLINE_PLAIN:-0}" = "1" ]]; then
159
170
  printf '%s' "$label"
160
171
  return 0
161
172
  fi
@@ -171,9 +182,9 @@ DOWN_LINK="$(osc_link "$DOWN_URL" "👎")"
171
182
  DASHBOARD_LINK="$(osc_link "$DASHBOARD_URL" "$DASHBOARD_LABEL")"
172
183
  LESSONS_LINK="$(osc_link "$LESSONS_URL" "$LESSONS_LABEL")"
173
184
  LATEST_LESSON_LINK=""
174
- if [ -n "$LESSON_LABEL" ]; then
185
+ if [[ -n "$LESSON_LABEL" ]]; then
175
186
  _DISPLAY_LINK="$LESSON_LINK"
176
- if [ -n "$LESSON_TEXT" ]; then
187
+ if [[ -n "$LESSON_TEXT" ]]; then
177
188
  LATEST_LESSON_LINK="$(osc_link "$_DISPLAY_LINK" "${LESSON_LABEL}: ${LESSON_TEXT}")"
178
189
  else
179
190
  LATEST_LESSON_LINK="$(osc_link "$_DISPLAY_LINK" "$LESSON_LABEL")"
@@ -181,20 +192,26 @@ if [ -n "$LESSON_LABEL" ]; then
181
192
  fi
182
193
 
183
194
  # ── Output (single line) ─────────────────────────────────────────
184
- LINE="ThumbGate v${TG_VERSION} · ${TG_TIER}"
185
- if [ "$UP" = "0" ] && [ "$DOWN" = "0" ]; then
186
- LINE="${D}${LINE} · no feedback yet${RST} · ${C}${DASHBOARD_LINK}${RST} · ${M}${LESSONS_LINK}${RST}"
187
- [ -n "$LATEST_LESSON_LINK" ] && LINE="${LINE} · ${D}${LATEST_LESSON_LINK}${RST}"
195
+ LINE=""
196
+ [[ -n "$BRANCH_NAME" ]] && LINE="${BRANCH_NAME}"
197
+ [[ -n "$WORK_ITEM_LABEL" ]] && LINE="${LINE:+${LINE} · }${WORK_ITEM_LABEL}"
198
+ LINE="${LINE:+${LINE} · }ThumbGate v${TG_VERSION} · ${TG_TIER}"
199
+ if [[ "$UP" = "0" && "$DOWN" = "0" ]]; then
200
+ LINE="${D}${LINE}${RST} · no feedback yet"
201
+ [[ -n "$PR_LABEL" ]] && LINE="${LINE} · ${D}${PR_LABEL}${RST}"
202
+ LINE="${LINE} · ${C}${DASHBOARD_LINK}${RST} · ${M}${LESSONS_LINK}${RST}"
203
+ [[ -n "$LATEST_LESSON_LINK" ]] && LINE="${LINE} · ${D}${LATEST_LESSON_LINK}${RST}"
188
204
  printf '%b\n' "$LINE"
189
205
  else
190
206
  LINE="${LINE} · ${G}${BD}${UP}${RST}${UP_LINK} ${R}${BD}${DOWN}${RST}${DOWN_LINK} ${ARROW}"
191
207
 
192
208
  # Control Tower alerts (if any)
193
- [ "${SLO_V:-0}" -gt 0 ] && LINE="${LINE} ${R}${SLO_V} SLO${RST}"
194
- [ "${AT_RISK:-0}" -gt 0 ] && LINE="${LINE} ${R}${AT_RISK}⚠${RST}"
195
- [ "${ANOMALIES:-0}" -gt 0 ] && LINE="${LINE} ${R}${ANOMALIES}☠${RST}"
209
+ [[ "${SLO_V:-0}" -gt 0 ]] && LINE="${LINE} ${R}${SLO_V} SLO${RST}"
210
+ [[ "${AT_RISK:-0}" -gt 0 ]] && LINE="${LINE} ${R}${AT_RISK}⚠${RST}"
211
+ [[ "${ANOMALIES:-0}" -gt 0 ]] && LINE="${LINE} ${R}${ANOMALIES}☠${RST}"
212
+ [[ -n "$PR_LABEL" ]] && LINE="${LINE} · ${D}${PR_LABEL}${RST}"
196
213
  LINE="${LINE} · ${C}${DASHBOARD_LINK}${RST} · ${M}${LESSONS_LINK}${RST}"
197
- [ -n "$LATEST_LESSON_LINK" ] && LINE="${LINE} · ${D}${LATEST_LESSON_LINK}${RST}"
214
+ [[ -n "$LATEST_LESSON_LINK" ]] && LINE="${LINE} · ${D}${LATEST_LESSON_LINK}${RST}"
198
215
 
199
216
  printf '%b\n' "$LINE"
200
217
  fi
@@ -134,6 +134,25 @@ const TOOLS = [
134
134
  },
135
135
  },
136
136
  }),
137
+ readOnlyTool({
138
+ name: 'plan_multimodal_retrieval',
139
+ description: 'Plan a high-ROI multimodal retrieval rollout for screenshots, PDF pages, dashboard captures, and proof artifacts without starting GPU training.',
140
+ inputSchema: {
141
+ type: 'object',
142
+ properties: {
143
+ goal: { type: 'string', description: 'Business or workflow objective for visual/document retrieval.' },
144
+ evidenceTypes: {
145
+ type: 'array',
146
+ items: { type: 'string' },
147
+ description: 'Evidence surfaces to include, such as screenshots, pdf_pages, proof_artifacts, dashboards, or videos.',
148
+ },
149
+ corpusItems: { type: 'number', description: 'Estimated number of visual artifacts or document pages to index.' },
150
+ maxEmbeddingDim: { type: 'number', description: 'Maximum embedding dimension to budget for Matryoshka-style truncation planning.' },
151
+ latencyBudgetMs: { type: 'number', description: 'Target retrieval latency budget for agent recall.' },
152
+ useReranker: { type: 'boolean', description: 'Whether to include a multimodal reranker stage after initial embedding retrieval.' },
153
+ },
154
+ },
155
+ }),
137
156
  destructiveTool({
138
157
  name: 'import_document',
139
158
  description: 'Import a local policy or runbook document into ThumbGate, normalize it for search, and propose provenance-backed gate candidates.',
@@ -872,6 +891,24 @@ const TOOLS = [
872
891
  },
873
892
  },
874
893
  }),
894
+ destructiveTool({
895
+ name: 'run_autoresearch',
896
+ description: 'Run a bounded metric-improvement loop: measure a baseline, test a hypothesis, require primary and holdout checks, then keep or discard the candidate mutation with proof.',
897
+ inputSchema: {
898
+ type: 'object',
899
+ properties: {
900
+ iterations: { type: 'number', description: 'Number of iterations to run. Capped at 5 per call; default 1.' },
901
+ targetName: { type: 'string', enum: ['half_life_days', 'decay_floor', 'prevention_min_occurrences', 'verification_max_retries', 'dpo_beta'], description: 'Optional evolution target to mutate.' },
902
+ nextValue: { type: 'number', description: 'Optional explicit candidate value for the target.' },
903
+ testCommand: { type: 'string', description: 'Primary metric command. Defaults to npm test.' },
904
+ holdoutCommands: { type: 'array', items: { type: 'string' }, description: 'Additional checks required before a candidate can be kept.' },
905
+ timeoutMs: { type: 'number', description: 'Per-command timeout in milliseconds. Capped at 600000; default 120000.' },
906
+ cwd: { type: 'string', description: 'Optional workspace directory for the evaluation commands.' },
907
+ researchQuery: { type: 'string', description: 'Optional research query used to build an autoresearch context brief.' },
908
+ paperLimit: { type: 'number', description: 'Maximum research papers to ingest when researchQuery is set. Capped at 10; default 5.' },
909
+ },
910
+ },
911
+ }),
875
912
  destructiveTool({
876
913
  name: 'schedule',
877
914
  description: 'Create, list, or delete scheduled tasks. Supports natural language scheduling like "daily 9:00", "weekly monday 8:30", "hourly". Installs as macOS LaunchAgent or Linux crontab.',
@@ -1041,6 +1078,45 @@ const TOOLS = [
1041
1078
  properties: {},
1042
1079
  },
1043
1080
  }),
1081
+ readOnlyTool({
1082
+ name: 'require_evidence_for_claim',
1083
+ description: 'Leader-Agent completion gate. Before any agent declares done/fixed/shipped/resolved, require tracked evidence. Blocking response when evidence missing; callers honor the blocking flag to stop completion claims.',
1084
+ inputSchema: {
1085
+ type: 'object',
1086
+ required: ['claim'],
1087
+ properties: {
1088
+ claim: { type: 'string', description: 'The completion claim text to verify (e.g. "Fix shipped", "Tests passing")' },
1089
+ mode: { type: 'string', enum: ['blocking', 'advisory'], description: 'blocking (default) returns blocking=true when evidence missing; advisory returns blocking=false' },
1090
+ sessionId: { type: 'string', description: 'Optional session id to associate with the gate decision' },
1091
+ },
1092
+ },
1093
+ }),
1094
+ destructiveTool({
1095
+ name: 'distribute_context_to_agents',
1096
+ description: 'Leader-Agent swarm coordinator. Constructs one context pack and distributes it to N worker agents (perplexity-bug-resolver, codex-reviewer, grok-x-intelligence, etc.), recording provenance per agent. Replaces N independent context derivations with a single shared pack.',
1097
+ inputSchema: {
1098
+ type: 'object',
1099
+ required: ['agents'],
1100
+ properties: {
1101
+ query: { type: 'string', description: 'Context query used to construct the pack' },
1102
+ agents: { type: 'array', items: { type: 'string' }, description: 'Agent names that should receive the pack' },
1103
+ maxItems: { type: 'number', description: 'Max items in the constructed pack (default 8)' },
1104
+ maxChars: { type: 'number', description: 'Max characters in the constructed pack (default 6000)' },
1105
+ namespaces: { type: 'array', items: { type: 'string' }, description: 'Optional contextfs namespaces to source from' },
1106
+ ttlMs: { type: 'number', description: 'Optional pack TTL in milliseconds (default 15 minutes)' },
1107
+ },
1108
+ },
1109
+ }),
1110
+ readOnlyTool({
1111
+ name: 'session_report',
1112
+ description: 'Unified observability rollup. Aggregates feedback stats, gate stats, and recent context/provenance events over a time window in one call. Replaces separate dashboard/gate_stats/feedback_stats calls with a single LangSmith-style report.',
1113
+ inputSchema: {
1114
+ type: 'object',
1115
+ properties: {
1116
+ windowHours: { type: 'number', description: 'Lookback window in hours (default 24, max 720)' },
1117
+ },
1118
+ },
1119
+ }),
1044
1120
  readOnlyTool({
1045
1121
  name: 'context_stuff_lessons',
1046
1122
  description: 'Dump ALL prevention lessons into a single text block for context-window injection. Bypasses RAG/search — returns every lesson sorted by confidence. For most projects (20-200 lessons), fits in 1K-10K tokens.',
package/src/api/server.js CHANGED
@@ -552,6 +552,169 @@ function getServerCardTools() {
552
552
  }));
553
553
  }
554
554
 
555
+ function buildPublicUrl(hostedConfig, pathname) {
556
+ return `${hostedConfig.appOrigin}${pathname}`;
557
+ }
558
+
559
+ const VERIFICATION_EVIDENCE_URL = 'https://github.com/IgorGanapolsky/ThumbGate/blob/main/docs/VERIFICATION_EVIDENCE.md';
560
+
561
+ function getToolDiscoveryIndex(hostedConfig) {
562
+ return MCP_TOOLS.map((tool) => ({
563
+ name: tool.name,
564
+ description: tool.description,
565
+ annotations: tool.annotations || {},
566
+ schemaUrl: buildPublicUrl(hostedConfig, `/.well-known/mcp/tools/${encodeURIComponent(tool.name)}.json`),
567
+ }));
568
+ }
569
+
570
+ function getMcpSkillManifests(hostedConfig) {
571
+ return [
572
+ {
573
+ name: 'thumbgate',
574
+ title: 'ThumbGate Pre-Action Gates',
575
+ description: 'Capture feedback, recall lessons, generate rules, and block repeated agent mistakes before tool execution.',
576
+ triggers: ['thumbgate', 'pre-action gates', 'prevent repeated AI mistakes', 'agent feedback', 'PreToolUse hooks'],
577
+ recommendedFlow: [
578
+ 'Recall lessons before risky work.',
579
+ 'Plan high-risk actions with checkpoints.',
580
+ 'Capture concrete thumbs-down/up feedback.',
581
+ 'Inspect prevention_rules after repeats.',
582
+ ],
583
+ installCommand: 'npx thumbgate init',
584
+ contextUrl: buildPublicUrl(hostedConfig, '/public/llm-context.md'),
585
+ proofUrl: VERIFICATION_EVIDENCE_URL,
586
+ },
587
+ {
588
+ name: 'workflow-hardening-sprint',
589
+ title: 'Workflow Hardening Sprint',
590
+ description: 'Turn one repeated agent failure into an enforced gate with proof and rollout evidence.',
591
+ triggers: ['workflow hardening', 'team rollout', 'agent governance', 'approval boundary', 'audit trail'],
592
+ recommendedFlow: [
593
+ 'Pick one costly repeated failure.',
594
+ 'Import the policy or runbook.',
595
+ 'Ship the gate with dashboard proof.',
596
+ ],
597
+ intakeUrl: buildPublicUrl(hostedConfig, '/#workflow-sprint-intake'),
598
+ proofUrl: VERIFICATION_EVIDENCE_URL,
599
+ },
600
+ {
601
+ name: 'visual-proof-retrieval',
602
+ title: 'Visual Proof Retrieval',
603
+ description: 'Use screenshots, PDF pages, dashboard captures, and proof artifacts as searchable evidence for agent-governance claims.',
604
+ triggers: ['visual document retrieval', 'multimodal embeddings', 'screenshots', 'PDF evidence', 'proof artifacts'],
605
+ recommendedFlow: [
606
+ 'Plan the corpus and Matryoshka dimension budget.',
607
+ 'Baseline text-only retrieval before finetuning.',
608
+ 'Evaluate NDCG@10 on visual hard negatives.',
609
+ 'Require artifact links before using retrieved evidence in claims.',
610
+ ],
611
+ contextUrl: buildPublicUrl(hostedConfig, '/public/llm-context.md'),
612
+ proofUrl: VERIFICATION_EVIDENCE_URL,
613
+ },
614
+ ];
615
+ }
616
+
617
+ function getMcpApplications(hostedConfig) {
618
+ return [
619
+ {
620
+ name: 'dashboard',
621
+ title: 'ThumbGate Dashboard',
622
+ description: 'Review feedback, gates, blocked actions, funnel metrics, and proof.',
623
+ url: buildPublicUrl(hostedConfig, '/dashboard'),
624
+ useWhen: 'Need proof before approving more autonomy.',
625
+ },
626
+ {
627
+ name: 'lessons',
628
+ title: 'Lessons',
629
+ description: 'Browse promoted lessons and corrective actions.',
630
+ url: buildPublicUrl(hostedConfig, '/lessons'),
631
+ useWhen: 'Need human-approved context before risk.',
632
+ },
633
+ {
634
+ name: 'guide',
635
+ title: 'Setup Guide',
636
+ description: 'Install ThumbGate for Claude Code, Cursor, Codex, Gemini CLI, Amp, OpenCode, and MCP agents.',
637
+ url: buildPublicUrl(hostedConfig, '/guide'),
638
+ useWhen: 'Need setup without searching the repo.',
639
+ },
640
+ {
641
+ name: 'workflow-sprint-intake',
642
+ title: 'Workflow Hardening Sprint Intake',
643
+ description: 'Submit a repeated agent failure for a proof-backed sprint.',
644
+ url: buildPublicUrl(hostedConfig, '/#workflow-sprint-intake'),
645
+ useWhen: 'Ready to convert mistakes into gates.',
646
+ },
647
+ ];
648
+ }
649
+
650
+ function getMcpDiscoveryManifest(hostedConfig) {
651
+ return {
652
+ schemaVersion: '2026-04-20',
653
+ name: 'thumbgate',
654
+ title: 'ThumbGate',
655
+ version: pkg.version,
656
+ description: 'Pre-Action Gates for AI coding agents: feedback, recall, prevention rules, and tool-call blocking.',
657
+ homepage: hostedConfig.appOrigin,
658
+ repository: 'https://github.com/IgorGanapolsky/ThumbGate',
659
+ package: {
660
+ registry: 'npm',
661
+ name: 'thumbgate',
662
+ installCommand: 'npx thumbgate init',
663
+ },
664
+ transport: {
665
+ type: 'streamable-http',
666
+ endpoint: buildPublicUrl(hostedConfig, '/mcp'),
667
+ unauthenticatedDiscovery: ['initialize', 'tools/list'],
668
+ authenticatedMethods: ['tools/call'],
669
+ },
670
+ discovery: {
671
+ serverCardUrl: buildPublicUrl(hostedConfig, '/.well-known/mcp/server-card.json'),
672
+ toolIndexUrl: buildPublicUrl(hostedConfig, '/.well-known/mcp/tools.json'),
673
+ toolSchemaUrlTemplate: buildPublicUrl(hostedConfig, '/.well-known/mcp/tools/{name}.json'),
674
+ skillsUrl: buildPublicUrl(hostedConfig, '/.well-known/mcp/skills.json'),
675
+ applicationsUrl: buildPublicUrl(hostedConfig, '/.well-known/mcp/applications.json'),
676
+ llmsTxtUrl: buildPublicUrl(hostedConfig, '/.well-known/llms.txt'),
677
+ progressive: {
678
+ pattern: 'Load manifest, inspect tools.json, fetch one tool schema only when needed.',
679
+ tokenStrategy: 'Do not preload every inputSchema. Use per-tool schema URLs.',
680
+ },
681
+ },
682
+ primaryFlows: [
683
+ {
684
+ name: 'capture-to-gate',
685
+ description: 'Capture feedback, retrieve lessons, generate rules, enforce a gate.',
686
+ tools: ['capture_feedback', 'search_lessons', 'prevention_rules', 'gate_stats'],
687
+ },
688
+ {
689
+ name: 'safe-autonomous-work',
690
+ description: 'Plan high-risk work, recall lessons, diagnose failures.',
691
+ tools: ['plan_intent', 'recall', 'diagnose_failure', 'feedback_summary'],
692
+ },
693
+ {
694
+ name: 'team-rollout-proof',
695
+ description: 'Show dashboard evidence, metrics, and sprint proof.',
696
+ tools: ['dashboard', 'get_business_metrics', 'construct_context_pack'],
697
+ },
698
+ {
699
+ name: 'metric-autoresearch',
700
+ description: 'Run bounded baseline -> hypothesis -> holdout loops with keep/discard proof.',
701
+ tools: ['get_business_metrics', 'construct_context_pack', 'run_autoresearch', 'require_evidence_for_claim'],
702
+ },
703
+ {
704
+ name: 'visual-proof-retrieval',
705
+ description: 'Plan screenshot/PDF/proof-artifact retrieval before investing in multimodal finetuning.',
706
+ tools: ['plan_multimodal_retrieval', 'search_thumbgate', 'construct_context_pack', 'require_evidence_for_claim'],
707
+ },
708
+ ],
709
+ skills: getMcpSkillManifests(hostedConfig),
710
+ applications: getMcpApplications(hostedConfig),
711
+ proof: {
712
+ verificationEvidenceUrl: VERIFICATION_EVIDENCE_URL,
713
+ llmContextUrl: buildPublicUrl(hostedConfig, '/public/llm-context.md'),
714
+ },
715
+ };
716
+ }
717
+
555
718
  function createHttpError(statusCode, message) {
556
719
  const err = new Error(message);
557
720
  err.statusCode = statusCode;
@@ -3904,7 +4067,85 @@ async function addContext(){
3904
4067
  return;
3905
4068
  }
3906
4069
 
4070
+ if (isGetLikeRequest && pathname === '/.well-known/mcp.json') {
4071
+ sendJson(res, 200, getMcpDiscoveryManifest(hostedConfig), {}, {
4072
+ headOnly: isHeadRequest,
4073
+ });
4074
+ return;
4075
+ }
4076
+
4077
+ if (isGetLikeRequest && pathname === '/.well-known/mcp/tools.json') {
4078
+ sendJson(res, 200, {
4079
+ name: 'thumbgate',
4080
+ version: pkg.version,
4081
+ count: MCP_TOOLS.length,
4082
+ tools: getToolDiscoveryIndex(hostedConfig),
4083
+ }, {}, {
4084
+ headOnly: isHeadRequest,
4085
+ });
4086
+ return;
4087
+ }
4088
+
4089
+ if (isGetLikeRequest && pathname.startsWith('/.well-known/mcp/tools/') && pathname.endsWith('.json')) {
4090
+ const encodedToolName = pathname.slice('/.well-known/mcp/tools/'.length, -'.json'.length);
4091
+ let toolName = encodedToolName;
4092
+ try {
4093
+ toolName = decodeURIComponent(encodedToolName);
4094
+ } catch (_err) {
4095
+ sendJson(res, 400, {
4096
+ error: 'invalid_tool_name',
4097
+ toolIndexUrl: buildPublicUrl(hostedConfig, '/.well-known/mcp/tools.json'),
4098
+ }, {}, {
4099
+ headOnly: isHeadRequest,
4100
+ });
4101
+ return;
4102
+ }
4103
+ const tool = MCP_TOOLS.find((candidate) => candidate.name === toolName);
4104
+ if (!tool) {
4105
+ sendJson(res, 404, {
4106
+ error: 'tool_not_found',
4107
+ toolName,
4108
+ toolIndexUrl: buildPublicUrl(hostedConfig, '/.well-known/mcp/tools.json'),
4109
+ }, {}, {
4110
+ headOnly: isHeadRequest,
4111
+ });
4112
+ return;
4113
+ }
4114
+ sendJson(res, 200, {
4115
+ name: tool.name,
4116
+ description: tool.description,
4117
+ annotations: tool.annotations || {},
4118
+ inputSchema: tool.inputSchema,
4119
+ }, {}, {
4120
+ headOnly: isHeadRequest,
4121
+ });
4122
+ return;
4123
+ }
4124
+
4125
+ if (isGetLikeRequest && pathname === '/.well-known/mcp/skills.json') {
4126
+ sendJson(res, 200, {
4127
+ name: 'thumbgate',
4128
+ version: pkg.version,
4129
+ skills: getMcpSkillManifests(hostedConfig),
4130
+ }, {}, {
4131
+ headOnly: isHeadRequest,
4132
+ });
4133
+ return;
4134
+ }
4135
+
4136
+ if (isGetLikeRequest && pathname === '/.well-known/mcp/applications.json') {
4137
+ sendJson(res, 200, {
4138
+ name: 'thumbgate',
4139
+ version: pkg.version,
4140
+ applications: getMcpApplications(hostedConfig),
4141
+ }, {}, {
4142
+ headOnly: isHeadRequest,
4143
+ });
4144
+ return;
4145
+ }
4146
+
3907
4147
  if (isGetLikeRequest && pathname === '/.well-known/mcp/server-card.json') {
4148
+ const discoveryManifest = getMcpDiscoveryManifest(hostedConfig);
3908
4149
  sendJson(res, 200, {
3909
4150
  serverInfo: {
3910
4151
  name: 'thumbgate',
@@ -3913,7 +4154,12 @@ async function addContext(){
3913
4154
  name: 'thumbgate',
3914
4155
  description: 'Pre-action gates that physically block AI coding agents from repeating known mistakes. Captures feedback, auto-promotes failures into prevention rules, and enforces them via PreToolUse hooks. Works with Claude Code, Codex, Gemini, Amp, Cursor, OpenCode, and any MCP-compatible agent.',
3915
4156
  version: pkg.version,
4157
+ transport: discoveryManifest.transport,
4158
+ discovery: discoveryManifest.discovery,
3916
4159
  tools: getServerCardTools(),
4160
+ skills: getMcpSkillManifests(hostedConfig),
4161
+ applications: getMcpApplications(hostedConfig),
4162
+ proof: discoveryManifest.proof,
3917
4163
  repository: 'https://github.com/IgorGanapolsky/ThumbGate',
3918
4164
  homepage: hostedConfig.appOrigin,
3919
4165
  }, {}, {