tachibot-mcp 2.22.0 → 2.23.1
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/CHANGELOG.md +12 -0
- package/CONTRIBUTING.md +4 -0
- package/README.md +2 -2
- package/dist/src/config/model-constants.js +9 -6
- package/dist/src/config/model-defaults.js +1 -1
- package/dist/src/orchestrators/collaborative/registries/ModelProviderRegistry.js +1 -1
- package/dist/src/server.js +16 -105
- package/dist/src/tools/factory/base-schemas.js +61 -0
- package/dist/src/tools/factory/define-model-tool.js +37 -0
- package/dist/src/tools/gemini-tools.js +24 -22
- package/dist/src/tools/grok-tools.js +17 -15
- package/dist/src/tools/jury-tool.js +25 -20
- package/dist/src/tools/local-tools.js +15 -2
- package/dist/src/tools/openai-tools.js +23 -21
- package/dist/src/tools/openrouter-tools.js +52 -48
- package/dist/src/tools/perplexity-tools.js +15 -13
- package/dist/src/tools/planner-tools.js +10 -8
- package/dist/src/tools/prompt-technique-tools.js +7 -6
- package/dist/src/tools/registry.js +147 -0
- package/dist/src/tools/tachi-tool.js +3 -2
- package/dist/src/tools/workflow-validator-tool.js +5 -4
- package/package.json +8 -3
- package/skills/algo/SKILL.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,18 @@ All notable changes to TachiBot MCP will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.23.1] - 2026-06-11
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- **MiniMax M2.7 → M3** (`minimax/minimax-m3`, released May 31 on OpenRouter, API-verified via grok_search): 1M-token context (up from 200K), MSA sparse attention (~1/20 compute at 1M ctx vs prior gen), native multimodal, tuned for long-horizon agentic work. Same promo pricing as M2.7 ($0.30/$1.20 per M). Powers `minimax_code`, `minimax_agent`, and the `minimax` juror. M2.7 kept as quota fallback via `MODEL_FALLBACKS`.
|
|
12
|
+
|
|
13
|
+
## [2.23.0] - 2026-06-11
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- **Tool registration via central scan registry** — merged the tool-standardization refactor (PR #5): all provider tools register through `src/tools/registry.ts` + `defineModelTool` factory, with golden wire-contract tests (57 tool schemas snapshot-locked) and a plop `add-tool` generator. No behavior change; the registered tool surface is byte-identical.
|
|
17
|
+
- **Jury: removed the `hermes` juror** — it was a persona variant of `local` calling the same `LOCAL_LLM_MODEL` weights while claiming "You are Hermes" (a false-role prompt). Council-reviewed rationale: jury independence comes from different model weights, not different system prompts on the same backend. `hermes` is kept as a **legacy alias** of `local`; panels are deduped after alias mapping, so `jurors: "hermes,local"` now yields one local vote instead of two correlated ones. 12 jurors total.
|
|
18
|
+
- Docs now describe the Hermes connection honestly (verified via grok_search against Nous docs/GitHub): the local juror runs whatever `LOCAL_LLM_MODEL` points at, e.g. a Nous Hermes build via Ollama. The Hermes *agent* is model-agnostic — it consumes 300+ backends (GPT, Claude, Gemini, self-hosted Ollama/vLLM); it is not an OpenAI-compatible endpoint to point `LOCAL_LLM_BASE_URL` at.
|
|
19
|
+
|
|
8
20
|
## [2.22.0] - 2026-06-10
|
|
9
21
|
|
|
10
22
|
### Added
|
package/CONTRIBUTING.md
CHANGED
|
@@ -47,6 +47,10 @@ Use the issue templates and include:
|
|
|
47
47
|
- Open a [Discussion](https://github.com/pavveu/tachibot-mcp/discussions)
|
|
48
48
|
- Create an issue
|
|
49
49
|
|
|
50
|
+
## Adding a tool
|
|
51
|
+
|
|
52
|
+
Run `npm run add-tool` and answer the prompts (provider, wire name, description). It scaffolds a `defineModelTool(...)` in the provider file at the `// plop:tools`/`// plop:register` anchors and a test stub. Fill in the `parameters` and `execute` TODOs. The emitted-schema golden test (`npm run test:golden`) guards the tool-name/schema wire contract.
|
|
53
|
+
|
|
50
54
|
## License
|
|
51
55
|
|
|
52
56
|
By submitting code to this project, you agree that your contributions will be licensed under the AGPL-3.0 license and you grant Pawel Pawlowski the right to relicense your contributions under alternative licenses (including commercial licenses).
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
**57 AI tools. 12 providers. One protocol.**
|
|
14
14
|
|
|
15
|
-
Orchestrate Perplexity, Grok, GPT-5, Gemini, Qwen, Kimi K2.
|
|
15
|
+
Orchestrate Perplexity, Grok, GPT-5.5, Gemini, Qwen, Kimi K2.6, and MiniMax M3
|
|
16
16
|
from Claude Code, Claude Desktop, Cursor, or any MCP client.
|
|
17
17
|
|
|
18
18
|
[Get Started](#-quick-start) · [View Tools](#-tool-ecosystem-57-tools) · [Documentation](https://tachibot.com/docs)
|
|
@@ -202,7 +202,7 @@ See [Installation Guide](docs/INSTALLATION_BOTH.md) for detailed instructions.
|
|
|
202
202
|
`list_prompt_techniques` · `preview_prompt_technique` · `execute_prompt_technique`
|
|
203
203
|
|
|
204
204
|
### Local Models (1)
|
|
205
|
-
`local_query` — any OpenAI-compatible local server (Ollama / LM Studio / llama.cpp / vLLM). Zero-cost, offline, private; also available as `
|
|
205
|
+
`local_query` — any OpenAI-compatible local server (Ollama / LM Studio / llama.cpp / vLLM). Zero-cost, offline, private; also available as the `local` jury juror (`hermes` is accepted as a legacy alias). Runs whatever `LOCAL_LLM_MODEL` points at — e.g. a Nous Hermes build (`ollama pull hermes3`). Note the [Hermes agent](https://hermes-agent.nousresearch.com) itself is model-agnostic — it runs on 300+ backends (GPT, Claude, Gemini, DeepSeek, or self-hosted Ollama/vLLM) — so "Hermes" was never a guarantee of distinct weights.
|
|
206
206
|
|
|
207
207
|
### Advanced Modes (bonus)
|
|
208
208
|
- **Challenger** — Critical analysis with multi-model fact-checking
|
|
@@ -95,9 +95,10 @@ export const KIMI_MODELS = {
|
|
|
95
95
|
K2_5: "moonshotai/kimi-k2.5", // Previous: multimodal + agent swarm (fallback)
|
|
96
96
|
};
|
|
97
97
|
// MiniMax Models (MiniMax via OpenRouter)
|
|
98
|
-
//
|
|
98
|
+
// M3 released May 31, 2026 - 1M ctx, MSA sparse attention (~1/20 compute at 1M), native multimodal
|
|
99
99
|
export const MINIMAX_MODELS = {
|
|
100
|
-
|
|
100
|
+
M3: "minimax/minimax-m3", // 1M ctx, MSA sparse attention, multimodal, agentic/coding, $0.30/$1.20
|
|
101
|
+
M2_7: "minimax/minimax-m2.7", // 2300B/100B MoE, 200K ctx, SWE-Pro 56.22%, Multi-SWE #1 (fallback)
|
|
101
102
|
M2_5: "minimax/minimax-m2.5", // SWE-Bench 80.2%, 37% faster than M2.1 (legacy)
|
|
102
103
|
M2_1: "minimax/minimax-m2.1", // 230B/10B MoE - SWE-Bench 72.5% (legacy)
|
|
103
104
|
};
|
|
@@ -188,7 +189,7 @@ export const CURRENT_MODELS = {
|
|
|
188
189
|
kimi: KIMI_MODELS.K2_6, // K2.6 (Apr 2026): 1T MoE, SWE-bench Pro leader
|
|
189
190
|
qwen: QWEN_MODELS.CODER_NEXT, // Qwen3-Coder-Next: 80B/3B MoE, 262K ctx, SWE >70% (no 3.6-coder yet)
|
|
190
191
|
qwen_reason: QWEN_MODELS.MAX_THINKING, // 235B MoE thinking mode (HMMT 98%) — still best for reasoning
|
|
191
|
-
minimax: MINIMAX_MODELS.
|
|
192
|
+
minimax: MINIMAX_MODELS.M3, // M3: 1M ctx, MSA sparse attention, agentic/coding
|
|
192
193
|
}
|
|
193
194
|
};
|
|
194
195
|
// Tool-specific defaults - References CURRENT_MODELS for easy bumping
|
|
@@ -309,12 +310,12 @@ export const TOOL_DEFAULTS = {
|
|
|
309
310
|
},
|
|
310
311
|
// MiniMax tools - VERY CHEAP, open source
|
|
311
312
|
minimax_code: {
|
|
312
|
-
model: MINIMAX_MODELS.
|
|
313
|
+
model: MINIMAX_MODELS.M3, // 1M ctx, MSA sparse attention, agentic/coding
|
|
313
314
|
maxTokens: 4000,
|
|
314
315
|
temperature: 0.3, // Lower for precise code
|
|
315
316
|
},
|
|
316
317
|
minimax_agent: {
|
|
317
|
-
model: MINIMAX_MODELS.
|
|
318
|
+
model: MINIMAX_MODELS.M3, // 1M ctx, multimodal, long-horizon agentic work
|
|
318
319
|
maxTokens: 4000,
|
|
319
320
|
temperature: 0.5, // Balanced for agentic tasks
|
|
320
321
|
},
|
|
@@ -381,6 +382,7 @@ export const MODEL_DISPLAY_NAMES = {
|
|
|
381
382
|
"qwen/qwen3-max-thinking": "qwen-max",
|
|
382
383
|
"qwen/qwen3-235b-a22b-thinking-2507": "qwen-235b-thinking",
|
|
383
384
|
// MiniMax
|
|
385
|
+
"minimax/minimax-m3": "minimax-m3",
|
|
384
386
|
"minimax/minimax-m2.7": "minimax-m2.7",
|
|
385
387
|
"minimax/minimax-m2.5": "minimax-m2.5",
|
|
386
388
|
"minimax/minimax-m2.1": "minimax-m2.1",
|
|
@@ -434,7 +436,8 @@ export const MODEL_PRICING = {
|
|
|
434
436
|
"qwen/qwen3-max-thinking": 0.005,
|
|
435
437
|
"qwen/qwen3-235b-a22b-thinking-2507": 0.000822, // ($0.15 + $1.50) / 2 / 1000
|
|
436
438
|
// OpenRouter models - MiniMax (VERY CHEAP!)
|
|
437
|
-
"minimax/minimax-
|
|
439
|
+
"minimax/minimax-m3": 0.00075, // ($0.30 + $1.20) / 2 / 1000 - flagship (promo pricing)
|
|
440
|
+
"minimax/minimax-m2.7": 0.00075, // ($0.30 + $1.20) / 2 / 1000 - fallback
|
|
438
441
|
"minimax/minimax-m2.5": 0.000685, // legacy
|
|
439
442
|
"minimax/minimax-m2.1": 0.000685, // legacy
|
|
440
443
|
};
|
|
@@ -29,7 +29,7 @@ const MODELS = {
|
|
|
29
29
|
QWEN: QWEN_MODELS.CODER_NEXT, // qwen/qwen3-coder-next (80B/3B MoE, 262K ctx)
|
|
30
30
|
QWEN_REASON: QWEN_MODELS.MAX_THINKING, // qwen/qwen3-235b-a22b-thinking (235B MoE)
|
|
31
31
|
KIMI: KIMI_MODELS.K2_6, // moonshotai/kimi-k2.6 (Apr 2026 - SWE-bench Pro leader)
|
|
32
|
-
MINIMAX: MINIMAX_MODELS.
|
|
32
|
+
MINIMAX: MINIMAX_MODELS.M3, // minimax/minimax-m3 (May 2026 - 1M ctx, MSA sparse attention)
|
|
33
33
|
};
|
|
34
34
|
/**
|
|
35
35
|
* Get Scout model configuration
|
|
@@ -101,7 +101,7 @@ modelProviderRegistry.registerMany([
|
|
|
101
101
|
// Qwen reason (heavy math/reasoning)
|
|
102
102
|
{ modelName: "qwen-reason", toolName: "qwen_reason", provider: "openrouter", aliases: ["qwen_reason", "qwen-max", "qwen-thinking"] },
|
|
103
103
|
// MiniMax models (cheap agentic)
|
|
104
|
-
{ modelName: "minimax", toolName: "minimax_agent", provider: "openrouter", aliases: ["minimax-agent", "minimax-m2.7", "minimax-m2.5"] },
|
|
104
|
+
{ modelName: "minimax", toolName: "minimax_agent", provider: "openrouter", aliases: ["minimax-agent", "minimax-m3", "minimax-m2.7", "minimax-m2.5"] },
|
|
105
105
|
{ modelName: "minimax-code", toolName: "minimax_code", provider: "openrouter", aliases: ["minimax_code"] },
|
|
106
106
|
// Think tool
|
|
107
107
|
{ modelName: "think", toolName: "think", provider: "anthropic" },
|
package/dist/src/server.js
CHANGED
|
@@ -65,16 +65,14 @@ import { getProviderInfo } from "./tools/unified-ai-provider.js";
|
|
|
65
65
|
import { getAllPerplexityTools, isPerplexityAvailable } from "./tools/perplexity-tools.js";
|
|
66
66
|
import { getAllGrokTools, isGrokAvailable } from "./tools/grok-tools.js";
|
|
67
67
|
import { registerWorkflowTools } from "./tools/workflow-runner.js";
|
|
68
|
-
import {
|
|
68
|
+
import { getAllTools } from "./tools/registry.js";
|
|
69
69
|
import { canRunFocusDeep } from "./focus-deep.js";
|
|
70
70
|
import { loadConfig } from "./config.js";
|
|
71
71
|
// import { registerSessionTools } from "./session/session-tools.js"; // Removed - not needed for minimal tool set
|
|
72
72
|
import { getAllAdvancedTools, areAdvancedModesAvailable } from "./tools/advanced-modes.js";
|
|
73
73
|
import { isOpenAIAvailable, getAllOpenAITools } from "./tools/openai-tools.js";
|
|
74
|
-
import { isGeminiAvailable
|
|
74
|
+
import { isGeminiAvailable } from "./tools/gemini-tools.js";
|
|
75
75
|
import { isOpenRouterAvailable } from "./tools/openrouter-tools.js";
|
|
76
|
-
import { getTachiTools } from "./tools/tachi-tool.js";
|
|
77
|
-
import { getPromptTechniqueTools } from "./tools/prompt-technique-tools.js";
|
|
78
76
|
import { withParamAliases } from "./utils/param-aliases.js";
|
|
79
77
|
// import { registerGPT5Tools, isGPT5Available } from "./tools/openai-gpt5-fixed.js"; // DISABLED - using regular openai-tools.ts
|
|
80
78
|
import { initializeOptimizations } from "./optimization/index.js";
|
|
@@ -553,114 +551,27 @@ const availableProviders = Object.entries(providerInfo)
|
|
|
553
551
|
.filter(([_, info]) => info.available)
|
|
554
552
|
.map(([name]) => name);
|
|
555
553
|
console.error(`✅ Available AI providers: ${availableProviders.join(', ')}`);
|
|
556
|
-
// Register Perplexity tools separately (custom API, not OpenAI-compatible)
|
|
557
|
-
if (isPerplexityAvailable()) {
|
|
558
|
-
const perplexityTools = getAllPerplexityTools();
|
|
559
|
-
perplexityTools.forEach(tool => {
|
|
560
|
-
safeAddTool(tool);
|
|
561
|
-
});
|
|
562
|
-
console.error(`✅ Registered ${perplexityTools.length} Perplexity tools (custom API)`);
|
|
563
|
-
}
|
|
564
|
-
// Register Grok tools separately (custom API, not OpenAI-compatible)
|
|
565
|
-
if (isGrokAvailable()) {
|
|
566
|
-
const grokTools = getAllGrokTools();
|
|
567
|
-
grokTools.forEach(tool => {
|
|
568
|
-
safeAddTool(tool);
|
|
569
|
-
});
|
|
570
|
-
console.error(`✅ Registered ${grokTools.length} Grok tools (custom API)`);
|
|
571
|
-
}
|
|
572
|
-
// Register all OpenAI tools (includes openai_reason, openai_brainstorm, etc.)
|
|
573
|
-
if (isOpenAIAvailable()) {
|
|
574
|
-
const openaiTools = getAllOpenAITools();
|
|
575
|
-
openaiTools.forEach(tool => {
|
|
576
|
-
safeAddTool(tool);
|
|
577
|
-
});
|
|
578
|
-
console.error(`✅ Registered ${openaiTools.length} OpenAI tools (GPT-5 suite)`);
|
|
579
|
-
}
|
|
580
554
|
// Async initialization function to handle dynamic imports and startup
|
|
581
555
|
async function initializeServer() {
|
|
582
556
|
try {
|
|
583
|
-
// Register
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
safeAddTool(tool);
|
|
595
|
-
});
|
|
596
|
-
console.error(`✅ Registered ${geminiTools.length} Gemini tools (brainstorm, code, text, judge, search)`);
|
|
597
|
-
// Register Jury tool (multi-model panel with Gemini judge)
|
|
598
|
-
const { juryTool } = await import("./tools/jury-tool.js");
|
|
599
|
-
safeAddTool(juryTool);
|
|
600
|
-
console.error(`✅ Registered jury tool (multi-model panel)`);
|
|
601
|
-
}
|
|
602
|
-
// Register OpenRouter tools (Qwen, Kimi, MiniMax - filtered by profile via safeAddTool)
|
|
603
|
-
if (isOpenRouterAvailable()) {
|
|
604
|
-
const { qwenCoderTool, qwenAlgoTool, qwqReasoningTool, qwenCompetitiveTool, kimiThinkingTool, kimiCodeTool, kimiDecomposeTool, kimiLongContextTool, qwenReasonTool, minimaxCodeTool, minimaxAgentTool, deepseekReasonTool, deepseekAlgoTool, glmReasonTool, stepfunReasonTool, ernieReasonTool } = await import("./tools/openrouter-tools.js");
|
|
605
|
-
// safeAddTool checks isToolEnabled internally
|
|
606
|
-
safeAddTool(qwenCoderTool);
|
|
607
|
-
safeAddTool(qwenAlgoTool);
|
|
608
|
-
safeAddTool(qwqReasoningTool); // QwQ-32B - multi-perspective deliberation (free tier)
|
|
609
|
-
safeAddTool(qwenCompetitiveTool);
|
|
610
|
-
safeAddTool(kimiThinkingTool);
|
|
611
|
-
safeAddTool(kimiCodeTool); // SWE-focused code (Kimi K2.5 - 76.8% SWE-Bench)
|
|
612
|
-
safeAddTool(kimiDecomposeTool); // Task decomposition (Kimi K2.5 Agent Swarm)
|
|
613
|
-
safeAddTool(kimiLongContextTool); // Long-context analysis (Kimi K2.5 - 256K)
|
|
614
|
-
safeAddTool(qwenReasonTool); // Heavy reasoning (Qwen3-Max-Thinking >1T params)
|
|
615
|
-
safeAddTool(minimaxCodeTool); // MiniMax M2.7 - SWE-Pro 56.22%, #1 AI Intelligence Index
|
|
616
|
-
safeAddTool(minimaxAgentTool); // MiniMax M2.7 - agentic workflows, self-evolving
|
|
617
|
-
safeAddTool(deepseekReasonTool); // DeepSeek V4 Pro - frontier reasoning/math (open-weight)
|
|
618
|
-
safeAddTool(deepseekAlgoTool); // DeepSeek V4 Pro - algorithmic code review (top AIME/CodeElo)
|
|
619
|
-
safeAddTool(glmReasonTool); // Zhipu GLM-5.1 - agentic reasoning (SWE-Bench Pro leader)
|
|
620
|
-
safeAddTool(stepfunReasonTool); // StepFun Step 3.7 Flash - efficient reasoning
|
|
621
|
-
safeAddTool(ernieReasonTool); // Baidu ERNIE 4.5 VL - broad-knowledge reasoning
|
|
622
|
-
console.error(`✅ Registered OpenRouter tools (Qwen, QwQ, Kimi x4, MiniMax, DeepSeek x2, GLM, StepFun, ERNIE)`);
|
|
623
|
-
// Register planner tools (multi-model council for plan creation/execution)
|
|
624
|
-
const { plannerMakerTool, plannerRunnerTool, listPlansTool } = await import("./tools/planner-tools.js");
|
|
625
|
-
safeAddTool(plannerMakerTool); // Council-based plan creation
|
|
626
|
-
safeAddTool(plannerRunnerTool); // Plan execution with checkpoints
|
|
627
|
-
safeAddTool(listPlansTool); // List recent plans
|
|
628
|
-
console.error(`✅ Registered planner tools (planner_maker, planner_runner, list_plans)`);
|
|
557
|
+
// Register ALL provider tools via the central scan registry. getAllTools()
|
|
558
|
+
// evaluates the SAME is*Available() guards, in the SAME order, with the SAME
|
|
559
|
+
// async dynamic-import timing the per-provider blocks used to use inline here
|
|
560
|
+
// — so the registered set is byte-identical across every API-key scenario.
|
|
561
|
+
// This single loop replaces the former Perplexity/Grok/OpenAI sync blocks and
|
|
562
|
+
// the Gemini+jury / OpenRouter+planner / local / workflow-validator /
|
|
563
|
+
// advanced / tachi / prompt-technique async blocks. The 5 inline tools
|
|
564
|
+
// (think/focus/nextThought/usage_stats/continue_focus) are registered above
|
|
565
|
+
// and are intentionally NOT returned by the registry, so there is no overlap.
|
|
566
|
+
for (const t of await getAllTools()) {
|
|
567
|
+
safeAddTool(t);
|
|
629
568
|
}
|
|
630
|
-
|
|
631
|
-
//
|
|
632
|
-
//
|
|
633
|
-
const { localQueryTool } = await import("./tools/local-tools.js");
|
|
634
|
-
safeAddTool(localQueryTool);
|
|
635
|
-
console.error(`✅ Registered local-model tools (local_query)`);
|
|
636
|
-
// Register workflow tools
|
|
569
|
+
console.error(`✅ Registered provider tools via central registry`);
|
|
570
|
+
// Register workflow tools (registers directly onto FastMCP, not via
|
|
571
|
+
// safeAddTool — intentionally OUT of the registry, kept in its original spot).
|
|
637
572
|
registerWorkflowTools(server);
|
|
638
573
|
console.error(`✅ Registered workflow tools (execute, list, create, visualize)`);
|
|
639
|
-
// Register workflow validator tools
|
|
640
|
-
safeAddTool(validateWorkflowTool);
|
|
641
|
-
safeAddTool(validateWorkflowFileTool);
|
|
642
|
-
console.error(`✅ Registered workflow validator tools`);
|
|
643
574
|
// Session management tools removed - not needed for minimal TachiBot
|
|
644
|
-
// Register advanced mode tools (Verifier, Challenger, Scout, etc.)
|
|
645
|
-
if (areAdvancedModesAvailable()) {
|
|
646
|
-
const advancedTools = getAllAdvancedTools();
|
|
647
|
-
advancedTools.forEach(tool => {
|
|
648
|
-
safeAddTool(tool);
|
|
649
|
-
});
|
|
650
|
-
console.error(`✅ Registered ${advancedTools.length} advanced mode tools`);
|
|
651
|
-
}
|
|
652
|
-
// Register tachi tools (smart auto-routing AI assistant)
|
|
653
|
-
const tachiTools = getTachiTools();
|
|
654
|
-
tachiTools.forEach(tool => {
|
|
655
|
-
safeAddTool(tool);
|
|
656
|
-
});
|
|
657
|
-
console.error(`✅ Registered tachi tools (tachi, focus alias)`);
|
|
658
|
-
// Register prompt technique tools (transparent prompt engineering)
|
|
659
|
-
const promptTechniqueTools = getPromptTechniqueTools();
|
|
660
|
-
promptTechniqueTools.forEach(tool => {
|
|
661
|
-
safeAddTool(tool);
|
|
662
|
-
});
|
|
663
|
-
console.error(`✅ Registered ${promptTechniqueTools.length} prompt technique tools`);
|
|
664
575
|
// Log startup information
|
|
665
576
|
const perplexityCount = isPerplexityAvailable() ? getAllPerplexityTools().length : 0;
|
|
666
577
|
const grokCount = isGrokAvailable() ? getAllGrokTools().length : 0;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Zod base schema partials — Task 0.3.
|
|
3
|
+
*
|
|
4
|
+
* Each export is a plain object meant to be SPREAD into a `z.object({...})`.
|
|
5
|
+
* Spreading creates a fresh `ZodObject` whose shape owns a copy of each
|
|
6
|
+
* field reference; two tools that spread the same partial do NOT alias a
|
|
7
|
+
* single mutable ZodObject — they each get an independent schema instance.
|
|
8
|
+
*
|
|
9
|
+
* SELECTION CRITERIA (grep-verified across all src/tools/*-tools.ts):
|
|
10
|
+
* A field is factored HERE only if its FULL definition
|
|
11
|
+
* (type + .describe() text + optionality) is **byte-identical** across
|
|
12
|
+
* every use site (outliers with different describe text stay inline).
|
|
13
|
+
*
|
|
14
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
15
|
+
* filesField
|
|
16
|
+
* 30 of 31 occurrences across gemini/grok/openai/openrouter/perplexity/
|
|
17
|
+
* planner tools share this exact definition. The one outlier
|
|
18
|
+
* (openrouter-tools.ts line ~758) has a different describe text and stays
|
|
19
|
+
* inline in its tool.
|
|
20
|
+
*
|
|
21
|
+
* reasoningContextField
|
|
22
|
+
* 7 occurrences across openai/openrouter/perplexity tools share this exact
|
|
23
|
+
* definition. The remaining ~7 context fields have different describe text
|
|
24
|
+
* (e.g. "Additional context for the problem", "Additional context about the
|
|
25
|
+
* environment or conditions", "Additional context") and stay inline.
|
|
26
|
+
*
|
|
27
|
+
* REJECTED CANDIDATES (non-identical definitions — do NOT factor here):
|
|
28
|
+
* temperature — 3 defs, all differ (defaults 0.4 vs 0.7, z.coerce.number
|
|
29
|
+
* vs z.number, different describe text).
|
|
30
|
+
* query — many defs with varying describe text across tools.
|
|
31
|
+
* problem — varying describe text.
|
|
32
|
+
* prompt — varying describe text.
|
|
33
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
34
|
+
*/
|
|
35
|
+
import { z } from "zod";
|
|
36
|
+
/**
|
|
37
|
+
* `files` — file-paths-as-code-context field.
|
|
38
|
+
*
|
|
39
|
+
* Identical across 30 tool definitions (verified by grep, Task 0.3 recon).
|
|
40
|
+
* Spread into z.object({...filesField, ...}) to include this field.
|
|
41
|
+
*/
|
|
42
|
+
export const filesField = {
|
|
43
|
+
files: z
|
|
44
|
+
.array(z.string())
|
|
45
|
+
.optional()
|
|
46
|
+
.describe("File paths to read as code context. Supports line ranges: 'src/foo.ts:100-200'. Model sees ACTUAL CODE."),
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* `context` — additional reasoning context field.
|
|
50
|
+
*
|
|
51
|
+
* Identical across 7 tool definitions in openai/openrouter/perplexity tools
|
|
52
|
+
* (verified by grep, Task 0.3 recon). Context fields with different describe
|
|
53
|
+
* text stay inline in their respective tools.
|
|
54
|
+
* Spread into z.object({...reasoningContextField, ...}) to include this field.
|
|
55
|
+
*/
|
|
56
|
+
export const reasoningContextField = {
|
|
57
|
+
context: z
|
|
58
|
+
.string()
|
|
59
|
+
.optional()
|
|
60
|
+
.describe("Additional context for the reasoning task"),
|
|
61
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PURE pass-through. Adds types + a single definition site — NO schema
|
|
3
|
+
* transformation, NO added .describe()/defaults. The emitted JSON schema MUST
|
|
4
|
+
* be unchanged. The body is exactly `return tool;` and must stay that way.
|
|
5
|
+
*
|
|
6
|
+
* IDENTITY-PRESERVING: the RETURN type is the concrete tool type `T`, not
|
|
7
|
+
* `ModelTool<S>`. Wrapping a tool is therefore type-transparent — the wrapped
|
|
8
|
+
* const keeps the *exact* concrete type it would have unwrapped (e.g.
|
|
9
|
+
* `execute`'s real `Promise<string>` return and its concrete input where
|
|
10
|
+
* `.default()` fields are still optional at the call site). Returning
|
|
11
|
+
* `ModelTool<S>` instead would widen `execute` to
|
|
12
|
+
* `(input: z.infer<S>, ctx) => Promise<unknown>` and break direct callers such
|
|
13
|
+
* as `src/tools/prompt-technique-tools.ts` that invoke `.execute(...)` outside
|
|
14
|
+
* FastMCP's parse step.
|
|
15
|
+
*
|
|
16
|
+
* Two type params are required for BOTH goals at once:
|
|
17
|
+
* - `S` is inferred from `tool.parameters` so the shape constraint's
|
|
18
|
+
* `execute` input is `z.infer<S>` — i.e. the tool's OWN output type, which
|
|
19
|
+
* its concrete `execute` is assignable to (contravariantly). Constraining
|
|
20
|
+
* to the generic `ModelTool<z.ZodObject<z.ZodRawShape>>` instead resolves
|
|
21
|
+
* the input to `{ [x: string]: any }` (an index signature with no required
|
|
22
|
+
* keys), to which a concrete `execute(input: { prompt: string })` is NOT
|
|
23
|
+
* assignable — so a single `T extends ModelTool<…>` param wrongly rejects
|
|
24
|
+
* every real tool.
|
|
25
|
+
* - `T extends ModelTool<S>` captures and returns the concrete tool type.
|
|
26
|
+
* The `ModelTool<S>` bound still validates the shape (rejects a missing
|
|
27
|
+
* `name`/`description`/`execute` or a non-`ZodObject` `parameters`).
|
|
28
|
+
*
|
|
29
|
+
* C3: a future tool whose top-level `parameters` uses `.refine()` /
|
|
30
|
+
* `.superRefine()` / `.transform()` would produce a `ZodEffects` (not a
|
|
31
|
+
* `ZodObject`) and would require widening the `S` constraint here. No such
|
|
32
|
+
* tool exists today (grep-confirmed), so the `z.ZodObject` constraint is
|
|
33
|
+
* correct for now.
|
|
34
|
+
*/
|
|
35
|
+
export function defineModelTool(tool) {
|
|
36
|
+
return tool;
|
|
37
|
+
}
|
|
@@ -12,6 +12,8 @@ import { FORMAT_INSTRUCTION } from "../utils/format-constants.js";
|
|
|
12
12
|
import { withHeartbeat } from "../utils/streaming-helper.js";
|
|
13
13
|
import { getTimeoutConfig } from "../config/timeout-config.js";
|
|
14
14
|
import { readFilesIntoContext } from "../utils/file-reader.js";
|
|
15
|
+
import { defineModelTool } from "./factory/define-model-tool.js";
|
|
16
|
+
import { filesField } from "./factory/base-schemas.js";
|
|
15
17
|
// Note: renderOutput is applied centrally in server.ts safeAddTool() - no need to import here
|
|
16
18
|
// NOTE: dotenv is loaded in server.ts before any imports
|
|
17
19
|
// No need to reload here - just read from process.env
|
|
@@ -164,7 +166,7 @@ export async function callGemini(prompt, model = GEMINI_MODELS.GEMINI_3_PRO, sys
|
|
|
164
166
|
* Gemini Query Tool
|
|
165
167
|
* Direct querying of Gemini models for general information
|
|
166
168
|
*/
|
|
167
|
-
export const geminiQueryTool = {
|
|
169
|
+
export const geminiQueryTool = defineModelTool({
|
|
168
170
|
name: "gemini_query",
|
|
169
171
|
description: "Query Gemini. Put your PROMPT in the 'prompt' parameter.",
|
|
170
172
|
parameters: z.object({
|
|
@@ -173,7 +175,7 @@ export const geminiQueryTool = {
|
|
|
173
175
|
.optional()
|
|
174
176
|
.default("gemini-3")
|
|
175
177
|
.describe("Model variant - must be one of: gemini-3 (default), pro, flash"),
|
|
176
|
-
|
|
178
|
+
...filesField
|
|
177
179
|
}),
|
|
178
180
|
execute: async (args, { log, reportProgress }) => {
|
|
179
181
|
let model = GEMINI_MODELS.GEMINI_3_PRO; // Default to Gemini 3
|
|
@@ -189,7 +191,7 @@ export const geminiQueryTool = {
|
|
|
189
191
|
const result = await withHeartbeat(() => callGemini(args.prompt + fileContext, model, undefined, 0.7, 'llm-orchestration'), reportFn);
|
|
190
192
|
return stripFormatting(result);
|
|
191
193
|
}
|
|
192
|
-
};
|
|
194
|
+
});
|
|
193
195
|
/**
|
|
194
196
|
* Gemini Brainstorm Tool
|
|
195
197
|
* Collaborative problem-solving and ideation
|
|
@@ -197,14 +199,14 @@ export const geminiQueryTool = {
|
|
|
197
199
|
* Note: skipValidation is used for internal LLM-to-LLM calls where input
|
|
198
200
|
* has already been validated at the MCP entry point (server.ts).
|
|
199
201
|
*/
|
|
200
|
-
export const geminiBrainstormTool = {
|
|
202
|
+
export const geminiBrainstormTool = defineModelTool({
|
|
201
203
|
name: "gemini_brainstorm",
|
|
202
204
|
description: "Convergent synthesis: cluster, refine, and prioritize raw ideas into structured hierarchies. Use AFTER divergent ideation to organize and rank ideas by impact/feasibility. Put your PROMPT in the 'prompt' parameter.",
|
|
203
205
|
parameters: z.object({
|
|
204
206
|
prompt: z.string().describe("The ideas or topic to organize and refine (REQUIRED - put raw ideas or topic here)"),
|
|
205
207
|
claudeThoughts: z.string().optional().describe("Claude's initial thoughts or raw ideas to cluster and refine"),
|
|
206
208
|
maxClusters: z.number().optional().default(5).describe("Number of idea clusters to create (default: 5)"),
|
|
207
|
-
|
|
209
|
+
...filesField
|
|
208
210
|
}),
|
|
209
211
|
execute: async (args, { log, reportProgress }) => {
|
|
210
212
|
const systemPrompt = `Convergent synthesis engine. Output consumed by automated toolchain.
|
|
@@ -237,18 +239,18 @@ ${FORMAT_INSTRUCTION}`;
|
|
|
237
239
|
const response = await withHeartbeat(() => callGemini(args.prompt + fileContext, GEMINI_MODELS.GEMINI_3_PRO, systemPrompt, 0.7, 'llm-orchestration'), reportFn);
|
|
238
240
|
return stripFormatting(response);
|
|
239
241
|
}
|
|
240
|
-
};
|
|
242
|
+
});
|
|
241
243
|
/**
|
|
242
244
|
* Gemini Analyze Code Tool
|
|
243
245
|
* Code quality and performance analysis
|
|
244
246
|
*/
|
|
245
|
-
export const geminiAnalyzeCodeTool = {
|
|
247
|
+
export const geminiAnalyzeCodeTool = defineModelTool({
|
|
246
248
|
name: "gemini_analyze_code",
|
|
247
249
|
description: "Analyze code for bugs, quality, security, or performance issues. Put the CODE in the 'code' parameter, NOT in 'focus'.",
|
|
248
250
|
parameters: z.object({
|
|
249
251
|
code: z.string().describe("The actual source code to analyze (REQUIRED - put your code here)"),
|
|
250
252
|
language: z.string().optional().describe("Programming language (e.g., 'typescript', 'python')"),
|
|
251
|
-
|
|
253
|
+
...filesField,
|
|
252
254
|
focus: z.string().optional().default("general").describe("Analysis focus (e.g., quality, security, performance, bugs, general)")
|
|
253
255
|
}),
|
|
254
256
|
execute: async (args, { log, reportProgress }) => {
|
|
@@ -271,17 +273,17 @@ ${FORMAT_INSTRUCTION}`;
|
|
|
271
273
|
const result = await withHeartbeat(() => callGemini(`Analyze this code:\n\n\`\`\`${args.language || ''}\n${args.code}\n\`\`\`${fileContext}`, GEMINI_MODELS.GEMINI_3_PRO, systemPrompt, 0.3, 'llm-orchestration'), reportFn);
|
|
272
274
|
return stripFormatting(result);
|
|
273
275
|
}
|
|
274
|
-
};
|
|
276
|
+
});
|
|
275
277
|
/**
|
|
276
278
|
* Gemini Analyze Text Tool
|
|
277
279
|
* Text analysis and sentiment detection
|
|
278
280
|
*/
|
|
279
|
-
export const geminiAnalyzeTextTool = {
|
|
281
|
+
export const geminiAnalyzeTextTool = defineModelTool({
|
|
280
282
|
name: "gemini_analyze_text",
|
|
281
283
|
description: "Rhetorical analysis: dissect arguments for bias, logical fallacies, and persuasion tactics. Use for evaluating claims, detecting manipulation, or understanding argument structure. Put the TEXT in the 'text' parameter.",
|
|
282
284
|
parameters: z.object({
|
|
283
285
|
text: z.string().describe("The text to analyze (REQUIRED - put your text here)"),
|
|
284
|
-
|
|
286
|
+
...filesField,
|
|
285
287
|
type: z.string()
|
|
286
288
|
.optional()
|
|
287
289
|
.default("rhetoric")
|
|
@@ -325,12 +327,12 @@ ${FORMAT_INSTRUCTION}`;
|
|
|
325
327
|
const result = await withHeartbeat(() => callGemini(`Analyze this text:\n\n${args.text}${fileContext}`, GEMINI_MODELS.GEMINI_3_PRO, systemPrompt, 0.3, 'llm-orchestration'), reportFn);
|
|
326
328
|
return stripFormatting(result);
|
|
327
329
|
}
|
|
328
|
-
};
|
|
330
|
+
});
|
|
329
331
|
/**
|
|
330
332
|
* Gemini Summarize Tool
|
|
331
333
|
* Content summarization at different levels
|
|
332
334
|
*/
|
|
333
|
-
export const geminiSummarizeTool = {
|
|
335
|
+
export const geminiSummarizeTool = defineModelTool({
|
|
334
336
|
name: "gemini_summarize",
|
|
335
337
|
description: "Summarization. Put the CONTENT in the 'content' parameter.",
|
|
336
338
|
parameters: z.object({
|
|
@@ -343,7 +345,7 @@ export const geminiSummarizeTool = {
|
|
|
343
345
|
.optional()
|
|
344
346
|
.default("paragraph")
|
|
345
347
|
.describe("Output format - must be one of: paragraph, bullet-points, outline"),
|
|
346
|
-
|
|
348
|
+
...filesField
|
|
347
349
|
}),
|
|
348
350
|
execute: async (args, { log, reportProgress }) => {
|
|
349
351
|
const lengthGuides = {
|
|
@@ -370,12 +372,12 @@ ${FORMAT_INSTRUCTION}`;
|
|
|
370
372
|
const result = await withHeartbeat(() => callGemini(`Summarize this content:\n\n${args.content}` + fileContext, GEMINI_MODELS.GEMINI_3_PRO, systemPrompt, 0.3, 'llm-orchestration'), reportFn);
|
|
371
373
|
return stripFormatting(result);
|
|
372
374
|
}
|
|
373
|
-
};
|
|
375
|
+
});
|
|
374
376
|
/**
|
|
375
377
|
* Gemini Image Prompt Tool
|
|
376
378
|
* Create detailed prompts for image generation
|
|
377
379
|
*/
|
|
378
|
-
export const geminiImagePromptTool = {
|
|
380
|
+
export const geminiImagePromptTool = defineModelTool({
|
|
379
381
|
name: "gemini_image_prompt",
|
|
380
382
|
description: "Image prompt generation. Put the DESCRIPTION in the 'description' parameter.",
|
|
381
383
|
parameters: z.object({
|
|
@@ -407,7 +409,7 @@ ${args.details ? `Additional details: ${args.details}` : ''}`;
|
|
|
407
409
|
const result = await withHeartbeat(() => callGemini(userPrompt, GEMINI_MODELS.GEMINI_3_PRO, systemPrompt, 0.7, 'llm-orchestration'), reportFn);
|
|
408
410
|
return stripFormatting(result);
|
|
409
411
|
}
|
|
410
|
-
};
|
|
412
|
+
});
|
|
411
413
|
/**
|
|
412
414
|
* Gemini Judge Tool
|
|
413
415
|
* Multi-perspective evaluation and synthesis (LLM-as-a-Judge)
|
|
@@ -417,7 +419,7 @@ ${args.details ? `Additional details: ${args.details}` : ''}`;
|
|
|
417
419
|
* - Position bias mitigation (don't favor first/last)
|
|
418
420
|
* - Extract-then-synthesize (not pick-a-winner)
|
|
419
421
|
*/
|
|
420
|
-
export const geminiJudgeTool = {
|
|
422
|
+
export const geminiJudgeTool = defineModelTool({
|
|
421
423
|
name: "gemini_judge",
|
|
422
424
|
description: "Evaluate and synthesize multiple AI perspectives into a unified verdict. Put CONTENT in the 'perspectives' parameter.",
|
|
423
425
|
parameters: z.object({
|
|
@@ -429,7 +431,7 @@ export const geminiJudgeTool = {
|
|
|
429
431
|
.optional()
|
|
430
432
|
.default("synthesize")
|
|
431
433
|
.describe("Judge mode: synthesize (merge best), evaluate (score each), rank (order by quality), resolve (settle conflicts)"),
|
|
432
|
-
|
|
434
|
+
...filesField
|
|
433
435
|
}),
|
|
434
436
|
execute: async (args, { log, reportProgress }) => {
|
|
435
437
|
// Resolve perspectives from fallback params (AI clients sometimes use wrong param name)
|
|
@@ -500,13 +502,13 @@ ${FORMAT_INSTRUCTION}`;
|
|
|
500
502
|
const result = await withHeartbeat(() => callGemini(userPrompt + fileContext, GEMINI_MODELS.GEMINI_3_PRO, systemPrompt, 0.3, 'llm-orchestration'), reportFn);
|
|
501
503
|
return stripFormatting(result);
|
|
502
504
|
}
|
|
503
|
-
};
|
|
505
|
+
});
|
|
504
506
|
/**
|
|
505
507
|
* Gemini Search Tool
|
|
506
508
|
* Web search using Google Search grounding
|
|
507
509
|
* Uses google_search_retrieval with dynamic retrieval config
|
|
508
510
|
*/
|
|
509
|
-
export const geminiSearchTool = {
|
|
511
|
+
export const geminiSearchTool = defineModelTool({
|
|
510
512
|
name: "gemini_search",
|
|
511
513
|
description: "Web search via Gemini with Google Search grounding",
|
|
512
514
|
parameters: z.object({
|
|
@@ -634,7 +636,7 @@ IMPORTANT:
|
|
|
634
636
|
return `[Gemini Search error: ${error instanceof Error ? error.message : String(error)}]`;
|
|
635
637
|
}
|
|
636
638
|
}
|
|
637
|
-
};
|
|
639
|
+
});
|
|
638
640
|
/**
|
|
639
641
|
* Check if Gemini is available
|
|
640
642
|
*/
|