23f7624596
ADR-166 MCP Bridge Security Lock / Static-source security lock (push) Failing after 0s
ADR-166 MCP Bridge Security Lock / Compose default binds loopback + Mongo has auth (push) Failing after 2s
CodeQL Advanced / Analyze (rust) (push) Failing after 0s
ADR-166 MCP Bridge Security Lock / plugin-agent-federation bindHost default (push) Failing after 1s
ADR-166 MCP Bridge Security Lock / Runtime behavior — 401 + terminal gate + fail-closed (push) Failing after 4s
business-pods-smoke / smoke (push) Failing after 1s
all-plugins-smoke / smoke-all (push) Failing after 2s
CI/CD Pipeline / Security & Code Quality (push) Failing after 1s
CI/CD Pipeline / Test Suite (ubuntu-latest) (push) Failing after 1s
CI/CD Pipeline / Build & Package (macos-latest) (push) Has been skipped
CI/CD Pipeline / Build & Package (ubuntu-latest) (push) Has been skipped
CI/CD Pipeline / Build & Package (windows-latest) (push) Has been skipped
CI/CD Pipeline / Documentation & Examples (push) Failing after 1s
Clone Tracker (14-day rolling) / Snapshot clones for ruflo ecosystem (push) Failing after 1s
CodeQL Advanced / Analyze (actions) (push) Failing after 1s
CodeQL Advanced / Analyze (javascript-typescript) (push) Failing after 1s
federation-peer-rust / stable-noop (push) Failing after 1s
metaharness-ci / score (push) Failing after 1s
metaharness-ci / router-compat (push) Failing after 0s
metaharness-ci / similarity-tests (push) Failing after 0s
no-agentbbs-smoke / smoke-without-agentbbs (push) Failing after 1s
V3 CI/CD Pipeline / Build V3 (windows-latest) (push) Has been skipped
codex-integration-audit / Codex integration audit (push) Failing after 1s
helpers-manifest-guard / guard (push) Failing after 1s
🔗 Cross-Agent Integration Tests / 🤝 Agent Coordination Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / 🧠 Memory Sharing Integration (push) Has been skipped
🔗 Cross-Agent Integration Tests / 🛡️ Fault Tolerance Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / ⚡ Performance Integration Tests (push) Has been skipped
metaharness-ci / mcp-scan (push) Failing after 1s
metaharness-ci / eject-dryrun (push) Failing after 1s
metaharness-ci / metaharness-real-data (push) Failing after 0s
no-cli-optdep-bloat-2561 / guard (push) Failing after 1s
no-metaharness-smoke / smoke-without-metaharness (push) Failing after 1s
no-phantom-agentic-flow-subpath / guard (push) Failing after 1s
🔄 Automated Rollback Manager / 🚨 Failure Detection (push) Failing after 1s
V3 CI/CD Pipeline / Plugin hooks smoke / ubuntu-latest / Node 22 (push) Failing after 1s
V3 CI/CD Pipeline / ruflo-graph-intelligence build + test smoke (#2044, ADR-123) (push) Failing after 1s
CVE Audit Gate / Audit root (critical-blocking) (push) Failing after 2s
cost-tracker-smoke / smoke (push) Failing after 3s
oia-audit-weekly / audit (push) Failing after 2s
ruflo-agent-smoke / ruflo-agent structural smoke (push) Failing after 1s
📊 Status Badges Update / 📊 Update Status Badges (push) Failing after 1s
V3 CI/CD Pipeline / Static regression guards (#2267 YAML + (push) Failing after 1s
V3 CI/CD Pipeline / Test V3 Packages (push) Failing after 0s
V3 CI/CD Pipeline / agent_execute provider routing smoke (#2042) (push) Failing after 0s
CVE Audit Gate / Audit v3 (critical-blocking) (push) Failing after 1s
federation-peer-rust / stable-native (push) Failing after 2s
🔗 Cross-Agent Integration Tests / 🚀 Integration Test Setup (push) Failing after 2s
neural-trader-smoke / runtime-smoke (push) Failing after 1s
V3 CI/CD Pipeline / Build V3 (macos-latest) (push) Has been skipped
V3 CI/CD Pipeline / Build V3 (ubuntu-latest) (push) Has been skipped
V3 CI/CD Pipeline / Type Check V3 (push) Failing after 1s
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / ubuntu-latest / Node 24 (push) Failing after 1s
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / ubuntu-latest / Node 22 (push) Failing after 2s
V3 CI/CD Pipeline / browser rvf create flag smoke (#2015) (push) Failing after 0s
V3 CI/CD Pipeline / Dependency review (#2046) (push) Has been skipped
V3 CI/CD Pipeline / Supply-chain audit (#2046) (push) Failing after 0s
V3 CI/CD Pipeline / witness marker drift smoke (#2021) (push) Failing after 1s
V3 CI/CD Pipeline / neural-trader portfolio CG smoke (#2068, ADR-126 Phase 3) (push) Failing after 1s
V3 CI/CD Pipeline / neural-trader backtest signing smoke (#2068, ADR-126 Phase 4) (push) Failing after 1s
V3 CI/CD Pipeline / kg-extract type-import classification smoke (#2049) (push) Failing after 0s
V3 CI/CD Pipeline / witness verify precondition smoke (#1880) (push) Failing after 2s
V3 CI/CD Pipeline / neural-trader pipeline risk-gate smoke (#2068, ADR-126 Phase 5) (push) Failing after 0s
V3 CI/CD Pipeline / neural-trader feature attribution smoke (#2068, ADR-126 Phase 6) (push) Failing after 0s
V3 CI/CD Pipeline / plugin-registry signature verification smoke (#1922, CWE-347) (push) Failing after 4s
V3 CI/CD Pipeline / memory stats legacy-DB smoke (#2120) (push) Failing after 4s
V3 CI/CD Pipeline / github deprecated actions smoke (#2089, ADR-127 Phase 3) (push) Failing after 1s
V3 CI/CD Pipeline / graph query + pathfinder smoke (ADR-130 P2+P5) (push) Has been skipped
V3 CI/CD Pipeline / graph trajectory hooks smoke (ADR-130 P3) (push) Has been skipped
V3 CI/CD Pipeline / graph plugin adapter smoke (ADR-130 P4) (push) Has been skipped
V3 CI/CD Pipeline / graph benchmark (ADR-130 P6) (push) Has been skipped
V3 CI/CD Pipeline / statusline generator delegation smoke (#2195) (push) Failing after 1s
V3 CI/CD Pipeline / wizard init regression guard (#2206 (push) Failing after 1s
V3 CI/CD Pipeline / memory no-stray-db smoke (ADR-125 P7) (push) Failing after 1s
V3 CI/CD Pipeline / github-safe injection smoke (#2089, ADR-127 Phase 1) (push) Failing after 1s
V3 CI/CD Pipeline / github actions pin smoke (#2089, ADR-127 Phase 1) (push) Failing after 1s
V3 CI/CD Pipeline / github attribution opt-in smoke (#2089, ADR-127 Phase 4) (push) Failing after 1s
V3 CI/CD Pipeline / pre-bash hook safety smoke (#2017) (push) Failing after 1s
V3 CI/CD Pipeline / Memory import smoke / ubuntu-latest (push) Failing after 0s
V3 CI/CD Pipeline / MCP protocol smoke / ubuntu-latest (push) Failing after 2s
V3 CI/CD Pipeline / ruvllm WASM auto-init smoke (#2086) (push) Failing after 4s
V3 CI/CD Pipeline / MCP paired-tool round-trip smoke (#1889) (push) Failing after 1s
V3 CI/CD Pipeline / Plugin package install-safety (#1902/#1903/#1904) (push) Failing after 1s
V3 CI/CD Pipeline / Tool description discoverability (ADR-112) (push) Failing after 3s
V3 CI/CD Pipeline / CLI npx-install smoke (#1147 / (22) (push) Failing after 1s
V3 CI/CD Pipeline / CLI npx-install smoke (#1147 / (24) (push) Failing after 1s
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / ubuntu-latest (push) Failing after 2s
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / ubuntu-latest (push) Failing after 1s
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / ubuntu-latest (push) Failing after 1s
V3 CI/CD Pipeline / Vector-index dimension audit (#1947) (push) Failing after 0s
V3 CI/CD Pipeline / Hook-command install safety (#1921) (push) Failing after 1s
V3 CI/CD Pipeline / ToolOutputGuardrail smoke (ADR-131, (push) Failing after 1s
V3 CI/CD Pipeline / init-bundle invariants smoke (#2095, ADR-128 Phase 5) (push) Failing after 1s
V3 CI/CD Pipeline / wasm provider bridge smoke (ADR-129 P1) (push) Failing after 2s
V3 CI/CD Pipeline / wasm gallery CRUD smoke (ADR-129 P3) (push) Failing after 1s
V3 CI/CD Pipeline / wasm plugin bridge smoke (ADR-129 P4) (push) Failing after 0s
V3 CI/CD Pipeline / wasm compose smoke (ADR-129 P2) (push) Failing after 4s
V3 CI/CD Pipeline / graph schema smoke (ADR-130 P1) (push) Failing after 0s
Validate Marketplace / validate (push) Failing after 1s
🔍 Verification Pipeline / 🚀 Setup Verification (push) Failing after 1s
🔍 Verification Pipeline / 🛡️ Security Verification (push) Has been skipped
🔍 Verification Pipeline / 📝 Code Quality (push) Has been skipped
🔍 Verification Pipeline / 🧪 Test Verification (${{ matrix.os }}, Node ${{ matrix.node }}) (push) Has been skipped
🔍 Verification Pipeline / 🏗️ Build Verification (push) Has been skipped
🔍 Verification Pipeline / 📚 Documentation Verification (push) Has been skipped
CVE Audit Gate / High-severity report (warn only) (push) Has been cancelled
🔄 Automated Rollback Manager / 🔄 Execute Rollback (push) Has been cancelled
🔄 Automated Rollback Manager / ✅ Post-Rollback Verification (push) Has been cancelled
🔄 Automated Rollback Manager / 📊 Rollback Monitoring (push) Has been cancelled
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / windows-latest (push) Has been cancelled
🔄 Automated Rollback Manager / ⏳ Manual Rollback Approval (push) Has been cancelled
V3 CI/CD Pipeline / MCP protocol smoke / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Memory import smoke / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / ubuntu-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Publish to npm (alpha) (push) Has been cancelled
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / macos-latest / Node 22 (push) Has been cancelled
V3 CI/CD Pipeline / Plugin hooks smoke / macos-latest / Node 22 (push) Has been cancelled
CI/CD Pipeline / Deploy & Release (push) Has been cancelled
CI/CD Pipeline / CI Status (push) Has been cancelled
🔗 Cross-Agent Integration Tests / 📊 Integration Test Report (push) Has been cancelled
🔄 Automated Rollback Manager / 🔍 Pre-Rollback Validation (push) Has been cancelled
🔍 Verification Pipeline / ⚡ Performance Verification (push) Has been cancelled
🔍 Verification Pipeline / 📊 Verification Report (push) Has been cancelled
425 lines
18 KiB
TypeScript
425 lines
18 KiB
TypeScript
/**
|
|
* MCP Tool Optimizer Plugin
|
|
*
|
|
* Learn tool usage patterns and suggest optimal tool sequences.
|
|
* Uses @ruvector/wasm for pattern storage and @ruvector/learning-wasm for optimization.
|
|
*
|
|
* Features:
|
|
* - Track tool usage patterns
|
|
* - Learn successful tool sequences
|
|
* - Suggest optimal tool combinations
|
|
* - Identify redundant tool calls
|
|
* - Performance optimization recommendations
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* import { mcpToolOptimizerPlugin } from '@claude-flow/plugins/examples/ruvector-plugins';
|
|
* await getDefaultRegistry().register(mcpToolOptimizerPlugin);
|
|
* ```
|
|
*/
|
|
|
|
import {
|
|
PluginBuilder,
|
|
MCPToolBuilder,
|
|
HookBuilder,
|
|
HookEvent,
|
|
HookPriority,
|
|
Security,
|
|
} from '../../src/index.js';
|
|
|
|
// Import shared vector utilities (consolidated from all plugins)
|
|
import {
|
|
IVectorDB,
|
|
ILoRAEngine,
|
|
createVectorDB,
|
|
createLoRAEngine,
|
|
generateHashEmbedding,
|
|
} from './shared/vector-utils.js';
|
|
|
|
// ============================================================================
|
|
// Types
|
|
// ============================================================================
|
|
|
|
export interface ToolUsagePattern {
|
|
id: string;
|
|
toolName: string;
|
|
context: string;
|
|
inputPatterns: string[];
|
|
outcome: 'success' | 'failure' | 'partial';
|
|
duration: number;
|
|
followedBy?: string[];
|
|
precededBy?: string[];
|
|
metadata: {
|
|
usageCount: number;
|
|
avgDuration: number;
|
|
successRate: number;
|
|
lastUsed: Date;
|
|
};
|
|
}
|
|
|
|
export interface ToolSequence {
|
|
id: string;
|
|
tools: string[];
|
|
context: string;
|
|
outcome: 'success' | 'failure';
|
|
totalDuration: number;
|
|
efficiency: number;
|
|
embedding?: Float32Array;
|
|
}
|
|
|
|
export interface OptimizationSuggestion {
|
|
type: 'sequence' | 'replacement' | 'parallel' | 'removal';
|
|
description: string;
|
|
currentTools: string[];
|
|
suggestedTools: string[];
|
|
expectedImprovement: number;
|
|
confidence: number;
|
|
}
|
|
|
|
// ============================================================================
|
|
// MCP Tool Optimizer Core
|
|
// ============================================================================
|
|
|
|
export class MCPToolOptimizer {
|
|
private vectorDb: IVectorDB | null = null;
|
|
private loraEngine: ILoRAEngine | null = null;
|
|
private patterns = new Map<string, ToolUsagePattern>();
|
|
private sequences = new Map<string, ToolSequence>();
|
|
private currentSession: { tools: string[]; startTime: number; context: string } | null = null;
|
|
private dimensions = 512;
|
|
private nextId = 1;
|
|
private initPromise: Promise<void> | null = null;
|
|
|
|
private toolRelations = new Map<string, { parallelWith: string[]; alternatives: string[]; bestAfter: string[] }>([
|
|
['Glob', { parallelWith: ['Grep'], alternatives: [], bestAfter: [] }],
|
|
['Grep', { parallelWith: ['Glob'], alternatives: [], bestAfter: ['Glob'] }],
|
|
['Read', { parallelWith: ['Read'], alternatives: [], bestAfter: ['Glob', 'Grep'] }],
|
|
['Edit', { parallelWith: [], alternatives: ['Write'], bestAfter: ['Read'] }],
|
|
['Write', { parallelWith: [], alternatives: ['Edit'], bestAfter: ['Read'] }],
|
|
['Bash', { parallelWith: ['Bash'], alternatives: [], bestAfter: [] }],
|
|
]);
|
|
|
|
async initialize(): Promise<void> {
|
|
if (this.vectorDb && this.loraEngine) return;
|
|
if (this.initPromise) return this.initPromise;
|
|
|
|
this.initPromise = (async () => {
|
|
this.vectorDb = await createVectorDB(this.dimensions);
|
|
this.loraEngine = await createLoRAEngine();
|
|
})();
|
|
|
|
return this.initPromise;
|
|
}
|
|
|
|
private async ensureInitialized(): Promise<{ db: IVectorDB; lora: ILoRAEngine }> {
|
|
await this.initialize();
|
|
return { db: this.vectorDb!, lora: this.loraEngine! };
|
|
}
|
|
|
|
/**
|
|
* Record a tool usage.
|
|
*/
|
|
async recordUsage(
|
|
toolName: string,
|
|
context: string,
|
|
inputSummary: string,
|
|
outcome: 'success' | 'failure' | 'partial',
|
|
duration: number
|
|
): Promise<ToolUsagePattern> {
|
|
const safeToolName = Security.validateString(toolName, { maxLength: 100 });
|
|
const safeContext = Security.validateString(context, { maxLength: 500 });
|
|
const safeInput = Security.validateString(inputSummary, { maxLength: 500 });
|
|
|
|
const patternKey = `${safeToolName}:${this.hashContext(safeContext)}`;
|
|
let pattern = this.patterns.get(patternKey);
|
|
|
|
if (pattern) {
|
|
pattern.metadata.usageCount++;
|
|
pattern.metadata.avgDuration = (pattern.metadata.avgDuration * (pattern.metadata.usageCount - 1) + duration) / pattern.metadata.usageCount;
|
|
pattern.metadata.successRate = (pattern.metadata.successRate * (pattern.metadata.usageCount - 1) + (outcome === 'success' ? 1 : 0)) / pattern.metadata.usageCount;
|
|
pattern.metadata.lastUsed = new Date();
|
|
if (!pattern.inputPatterns.includes(safeInput)) {
|
|
pattern.inputPatterns.push(safeInput);
|
|
if (pattern.inputPatterns.length > 10) pattern.inputPatterns.shift();
|
|
}
|
|
} else {
|
|
const id = `pattern-${this.nextId++}`;
|
|
pattern = {
|
|
id, toolName: safeToolName, context: safeContext, inputPatterns: [safeInput], outcome, duration,
|
|
followedBy: [], precededBy: [],
|
|
metadata: { usageCount: 1, avgDuration: duration, successRate: outcome === 'success' ? 1 : 0, lastUsed: new Date() },
|
|
};
|
|
this.patterns.set(patternKey, pattern);
|
|
}
|
|
|
|
if (this.currentSession) {
|
|
const lastTool = this.currentSession.tools[this.currentSession.tools.length - 1];
|
|
if (lastTool) {
|
|
const lastPatternKey = `${lastTool}:${this.hashContext(this.currentSession.context)}`;
|
|
const lastPattern = this.patterns.get(lastPatternKey);
|
|
if (lastPattern) {
|
|
if (!lastPattern.followedBy) lastPattern.followedBy = [];
|
|
if (!lastPattern.followedBy.includes(safeToolName)) lastPattern.followedBy.push(safeToolName);
|
|
}
|
|
if (!pattern.precededBy) pattern.precededBy = [];
|
|
if (!pattern.precededBy.includes(lastTool)) pattern.precededBy.push(lastTool);
|
|
}
|
|
this.currentSession.tools.push(safeToolName);
|
|
}
|
|
|
|
return pattern;
|
|
}
|
|
|
|
startSession(context: string): void {
|
|
this.currentSession = { tools: [], startTime: Date.now(), context: Security.validateString(context, { maxLength: 500 }) };
|
|
}
|
|
|
|
async endSession(outcome: 'success' | 'failure'): Promise<ToolSequence | null> {
|
|
const { db } = await this.ensureInitialized();
|
|
|
|
if (!this.currentSession || this.currentSession.tools.length === 0) {
|
|
this.currentSession = null;
|
|
return null;
|
|
}
|
|
|
|
const id = `seq-${this.nextId++}`;
|
|
const totalDuration = Date.now() - this.currentSession.startTime;
|
|
const uniqueTools = new Set(this.currentSession.tools);
|
|
const redundancy = 1 - (uniqueTools.size / this.currentSession.tools.length);
|
|
const efficiency = outcome === 'success' ? (1 - redundancy * 0.5) : 0.3;
|
|
|
|
const embedding = this.generateSequenceEmbedding(this.currentSession.tools, this.currentSession.context);
|
|
const sequence: ToolSequence = { id, tools: [...this.currentSession.tools], context: this.currentSession.context, outcome, totalDuration, efficiency, embedding };
|
|
|
|
db.insert(embedding, id, { tools: sequence.tools.join(','), outcome, efficiency });
|
|
this.sequences.set(id, sequence);
|
|
this.currentSession = null;
|
|
|
|
return sequence;
|
|
}
|
|
|
|
async optimize(tools: string[], context: string): Promise<OptimizationSuggestion[]> {
|
|
const { db } = await this.ensureInitialized();
|
|
const suggestions: OptimizationSuggestion[] = [];
|
|
const safeTools = tools.map(t => Security.validateString(t, { maxLength: 100 }));
|
|
const safeContext = Security.validateString(context, { maxLength: 500 });
|
|
|
|
const embedding = this.generateSequenceEmbedding(safeTools, safeContext);
|
|
const similarSequences = db.search(embedding, 5)
|
|
.map(r => this.sequences.get(r.id))
|
|
.filter((s): s is ToolSequence => s !== undefined && s.outcome === 'success' && s.efficiency > 0.7);
|
|
|
|
// Parallel execution suggestions
|
|
for (let i = 0; i < safeTools.length - 1; i++) {
|
|
const tool = safeTools[i];
|
|
const nextTool = safeTools[i + 1];
|
|
const relations = this.toolRelations.get(tool);
|
|
if (relations?.parallelWith.includes(nextTool)) {
|
|
suggestions.push({ type: 'parallel', description: `Run ${tool} and ${nextTool} in parallel`, currentTools: [tool, nextTool], suggestedTools: [`${tool} || ${nextTool}`], expectedImprovement: 0.4, confidence: 0.8 });
|
|
}
|
|
}
|
|
|
|
// Redundant tool suggestions
|
|
const toolCounts = new Map<string, number>();
|
|
for (const tool of safeTools) toolCounts.set(tool, (toolCounts.get(tool) ?? 0) + 1);
|
|
for (const [tool, count] of toolCounts) {
|
|
if (count > 2 && !['Read', 'Bash'].includes(tool)) {
|
|
suggestions.push({ type: 'removal', description: `Combine ${count} ${tool} calls`, currentTools: Array(count).fill(tool), suggestedTools: [tool], expectedImprovement: 0.3 * (count - 1), confidence: 0.7 });
|
|
}
|
|
}
|
|
|
|
// Better sequences from history
|
|
for (const seq of similarSequences) {
|
|
if (seq.tools.length < safeTools.length && seq.efficiency > 0.8) {
|
|
suggestions.push({ type: 'sequence', description: `Similar task completed with fewer tools`, currentTools: safeTools, suggestedTools: seq.tools, expectedImprovement: (safeTools.length - seq.tools.length) * 0.1, confidence: 0.6 });
|
|
break;
|
|
}
|
|
}
|
|
|
|
return suggestions.sort((a, b) => b.expectedImprovement * b.confidence - a.expectedImprovement * a.confidence);
|
|
}
|
|
|
|
async suggestNext(currentTool: string, context: string): Promise<Array<{ tool: string; probability: number; reason: string }>> {
|
|
const suggestions: Array<{ tool: string; probability: number; reason: string }> = [];
|
|
const safeTool = Security.validateString(currentTool, { maxLength: 100 });
|
|
|
|
const patternKey = `${safeTool}:${this.hashContext(context)}`;
|
|
const pattern = this.patterns.get(patternKey);
|
|
|
|
if (pattern?.followedBy) {
|
|
const counts = new Map<string, number>();
|
|
for (const tool of pattern.followedBy) counts.set(tool, (counts.get(tool) ?? 0) + 1);
|
|
const total = pattern.followedBy.length;
|
|
for (const [tool, count] of counts) {
|
|
suggestions.push({ tool, probability: count / total, reason: `Followed ${safeTool} ${count}x` });
|
|
}
|
|
}
|
|
|
|
const relations = this.toolRelations.get(safeTool);
|
|
if (relations?.bestAfter.length === 0) {
|
|
for (const [tool, rel] of this.toolRelations) {
|
|
if (rel.bestAfter.includes(safeTool)) {
|
|
suggestions.push({ tool, probability: 0.6, reason: `${tool} often follows ${safeTool}` });
|
|
}
|
|
}
|
|
}
|
|
|
|
return suggestions.sort((a, b) => b.probability - a.probability).slice(0, 5);
|
|
}
|
|
|
|
getStats(): {
|
|
totalPatterns: number;
|
|
totalSequences: number;
|
|
topTools: Array<{ name: string; usageCount: number; successRate: number; avgDuration: number }>;
|
|
avgEfficiency: number;
|
|
commonSequences: Array<{ tools: string[]; count: number }>;
|
|
} {
|
|
const toolStats = new Map<string, { usageCount: number; successTotal: number; durationTotal: number }>();
|
|
|
|
for (const pattern of this.patterns.values()) {
|
|
const existing = toolStats.get(pattern.toolName) ?? { usageCount: 0, successTotal: 0, durationTotal: 0 };
|
|
existing.usageCount += pattern.metadata.usageCount;
|
|
existing.successTotal += pattern.metadata.successRate * pattern.metadata.usageCount;
|
|
existing.durationTotal += pattern.metadata.avgDuration * pattern.metadata.usageCount;
|
|
toolStats.set(pattern.toolName, existing);
|
|
}
|
|
|
|
const topTools = Array.from(toolStats.entries())
|
|
.map(([name, stats]) => ({ name, usageCount: stats.usageCount, successRate: stats.usageCount > 0 ? stats.successTotal / stats.usageCount : 0, avgDuration: stats.usageCount > 0 ? stats.durationTotal / stats.usageCount : 0 }))
|
|
.sort((a, b) => b.usageCount - a.usageCount).slice(0, 10);
|
|
|
|
let totalEfficiency = 0;
|
|
const sequenceCounts = new Map<string, number>();
|
|
for (const seq of this.sequences.values()) {
|
|
totalEfficiency += seq.efficiency;
|
|
const key = seq.tools.join(' → ');
|
|
sequenceCounts.set(key, (sequenceCounts.get(key) ?? 0) + 1);
|
|
}
|
|
|
|
const commonSequences = Array.from(sequenceCounts.entries())
|
|
.map(([tools, count]) => ({ tools: tools.split(' → '), count }))
|
|
.sort((a, b) => b.count - a.count).slice(0, 5);
|
|
|
|
return { totalPatterns: this.patterns.size, totalSequences: this.sequences.size, topTools, avgEfficiency: this.sequences.size > 0 ? totalEfficiency / this.sequences.size : 0, commonSequences };
|
|
}
|
|
|
|
private hashContext(context: string): string {
|
|
let hash = 0;
|
|
for (let i = 0; i < context.length; i++) { hash = ((hash << 5) - hash) + context.charCodeAt(i); hash = hash & hash; }
|
|
return hash.toString(16);
|
|
}
|
|
|
|
private generateSequenceEmbedding(tools: string[], context: string): Float32Array {
|
|
const text = `${tools.join(' ')} ${context}`.toLowerCase();
|
|
const embedding = new Float32Array(this.dimensions);
|
|
let hash = 0;
|
|
for (let i = 0; i < text.length; i++) { hash = ((hash << 5) - hash) + text.charCodeAt(i); hash = hash & hash; }
|
|
for (let i = 0; i < this.dimensions; i++) { embedding[i] = Math.sin(hash * (i + 1) * 0.001) * 0.5 + 0.5; }
|
|
let norm = 0;
|
|
for (let i = 0; i < this.dimensions; i++) norm += embedding[i] * embedding[i];
|
|
norm = Math.sqrt(norm);
|
|
for (let i = 0; i < this.dimensions; i++) embedding[i] /= norm;
|
|
return embedding;
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Plugin Definition
|
|
// ============================================================================
|
|
|
|
let optimizerInstance: MCPToolOptimizer | null = null;
|
|
|
|
async function getOptimizer(): Promise<MCPToolOptimizer> {
|
|
if (!optimizerInstance) {
|
|
optimizerInstance = new MCPToolOptimizer();
|
|
await optimizerInstance.initialize();
|
|
}
|
|
return optimizerInstance;
|
|
}
|
|
|
|
export const mcpToolOptimizerPlugin = new PluginBuilder('mcp-tool-optimizer', '1.0.0')
|
|
.withDescription('Learn tool patterns and suggest optimal sequences using @ruvector/wasm + @ruvector/learning-wasm')
|
|
.withAuthor('Claude Flow Team')
|
|
.withTags(['optimization', 'tools', 'patterns', 'ruvector', 'learning', 'hnsw'])
|
|
.withMCPTools([
|
|
new MCPToolBuilder('tool-optimize')
|
|
.withDescription('Get optimization suggestions')
|
|
.addStringParam('tools', 'Comma-separated tool names', { required: true })
|
|
.addStringParam('context', 'Task context', { required: true })
|
|
.withHandler(async (params) => {
|
|
try {
|
|
const optimizer = await getOptimizer();
|
|
const tools = (params.tools as string).split(',').map(t => t.trim());
|
|
const suggestions = await optimizer.optimize(tools, params.context as string);
|
|
if (suggestions.length === 0) return { content: [{ type: 'text', text: '✅ Tool sequence looks optimal!' }] };
|
|
const output = suggestions.map((s, i) => `**${i + 1}. ${s.type.toUpperCase()}** (${(s.confidence * 100).toFixed(0)}%)\n ${s.description}`).join('\n\n');
|
|
return { content: [{ type: 'text', text: `🔧 **Optimizations:**\n\n${output}` }] };
|
|
} catch (error) {
|
|
return { content: [{ type: 'text', text: `❌ Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true };
|
|
}
|
|
})
|
|
.build(),
|
|
|
|
new MCPToolBuilder('tool-suggest-next')
|
|
.withDescription('Suggest next tool')
|
|
.addStringParam('currentTool', 'Current tool', { required: true })
|
|
.addStringParam('context', 'Context')
|
|
.withHandler(async (params) => {
|
|
try {
|
|
const optimizer = await getOptimizer();
|
|
const suggestions = await optimizer.suggestNext(params.currentTool as string, (params.context as string) ?? '');
|
|
if (suggestions.length === 0) return { content: [{ type: 'text', text: '🤔 No suggestions.' }] };
|
|
const output = suggestions.map((s, i) => `${i + 1}. **${s.tool}** (${(s.probability * 100).toFixed(0)}%) - ${s.reason}`).join('\n');
|
|
return { content: [{ type: 'text', text: `💡 **Next tools:**\n\n${output}` }] };
|
|
} catch (error) {
|
|
return { content: [{ type: 'text', text: `❌ Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true };
|
|
}
|
|
})
|
|
.build(),
|
|
|
|
new MCPToolBuilder('tool-stats')
|
|
.withDescription('Get tool statistics')
|
|
.withHandler(async () => {
|
|
const optimizer = await getOptimizer();
|
|
const stats = optimizer.getStats();
|
|
const topToolsOutput = stats.topTools.slice(0, 5).map((t, i) => ` ${i + 1}. ${t.name}: ${t.usageCount} uses (${(t.successRate * 100).toFixed(0)}%)`).join('\n');
|
|
return { content: [{ type: 'text', text: `📊 **Tool Optimizer:**\n\n**Patterns:** ${stats.totalPatterns}\n**Sequences:** ${stats.totalSequences}\n**Efficiency:** ${(stats.avgEfficiency * 100).toFixed(1)}%\n\n**Top Tools:**\n${topToolsOutput || ' None'}\n\n**Backend:** @ruvector/wasm + @ruvector/learning-wasm` }] };
|
|
})
|
|
.build(),
|
|
])
|
|
.withHooks([
|
|
new HookBuilder(HookEvent.PostToolCall)
|
|
.withName('tool-usage-record')
|
|
.withDescription('Record tool usage')
|
|
.withPriority(HookPriority.Low)
|
|
.handle(async (ctx) => {
|
|
const data = ctx.data as { toolName?: string; context?: string; input?: string; success?: boolean; duration?: number } | undefined;
|
|
if (!data?.toolName) return { success: true };
|
|
try {
|
|
const optimizer = await getOptimizer();
|
|
await optimizer.recordUsage(data.toolName, data.context ?? 'unknown', data.input ?? '', data.success ? 'success' : 'failure', data.duration ?? 0);
|
|
} catch { /* silent */ }
|
|
return { success: true };
|
|
})
|
|
.build(),
|
|
|
|
new HookBuilder(HookEvent.PostTaskComplete)
|
|
.withName('tool-session-end')
|
|
.withDescription('End tool session')
|
|
.withPriority(HookPriority.Low)
|
|
.handle(async (ctx) => {
|
|
const data = ctx.data as { success?: boolean } | undefined;
|
|
try { await (await getOptimizer()).endSession(data?.success ? 'success' : 'failure'); } catch { /* silent */ }
|
|
return { success: true };
|
|
})
|
|
.build(),
|
|
])
|
|
.onInitialize(async (ctx) => {
|
|
ctx.logger.info('MCP Tool Optimizer initializing with @ruvector/wasm + @ruvector/learning-wasm...');
|
|
await getOptimizer();
|
|
ctx.logger.info('MCP Tool Optimizer ready - HNSW + LoRA enabled');
|
|
})
|
|
.build();
|
|
|
|
export default mcpToolOptimizerPlugin;
|