@claudetools/tools 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,274 @@
1
+ // =============================================================================
2
+ // Task Management System
3
+ // =============================================================================
4
+ import { apiRequest } from './api-client.js';
5
+ import { matchTaskToWorker } from './workers.js';
6
+ export function parseJsonArray(value) {
7
+ if (!value)
8
+ return [];
9
+ if (Array.isArray(value))
10
+ return value;
11
+ if (typeof value === 'string') {
12
+ try {
13
+ const parsed = JSON.parse(value);
14
+ return Array.isArray(parsed) ? parsed : [];
15
+ }
16
+ catch {
17
+ return [];
18
+ }
19
+ }
20
+ return [];
21
+ }
22
+ export async function createTask(userId, projectId, taskData) {
23
+ // Filter out undefined values to avoid sending them to API
24
+ const cleanTaskData = {};
25
+ for (const [key, value] of Object.entries(taskData)) {
26
+ if (value !== undefined) {
27
+ cleanTaskData[key] = value;
28
+ }
29
+ }
30
+ return apiRequest(`/api/v1/tasks/${userId}/${projectId}`, 'POST', cleanTaskData);
31
+ }
32
+ export async function listTasks(userId, projectId, filters) {
33
+ const params = new URLSearchParams();
34
+ if (filters?.type)
35
+ params.set('type', filters.type);
36
+ if (filters?.status)
37
+ params.set('status', filters.status);
38
+ if (filters?.parent_id)
39
+ params.set('parent_id', filters.parent_id);
40
+ if (filters?.assigned_to)
41
+ params.set('assigned_to', filters.assigned_to);
42
+ if (filters?.limit)
43
+ params.set('limit', String(filters.limit));
44
+ if (filters?.offset)
45
+ params.set('offset', String(filters.offset));
46
+ const queryString = params.toString();
47
+ const url = `/api/v1/tasks/${userId}/${projectId}${queryString ? `?${queryString}` : ''}`;
48
+ return apiRequest(url);
49
+ }
50
+ export async function getTask(userId, projectId, taskId, full = false) {
51
+ const endpoint = full ? 'full' : '';
52
+ return apiRequest(`/api/v1/tasks/${userId}/${projectId}/${taskId}${endpoint ? `/${endpoint}` : ''}`);
53
+ }
54
+ export async function claimTask(userId, projectId, taskId, agentId, lockDurationMinutes = 30) {
55
+ return apiRequest(`/api/v1/tasks/${userId}/${projectId}/${taskId}/claim`, 'POST', {
56
+ agent_id: agentId,
57
+ lock_duration_minutes: lockDurationMinutes,
58
+ });
59
+ }
60
+ export async function releaseTask(userId, projectId, taskId, agentId, newStatus, workLog) {
61
+ return apiRequest(`/api/v1/tasks/${userId}/${projectId}/${taskId}/release`, 'POST', {
62
+ agent_id: agentId,
63
+ new_status: newStatus,
64
+ work_log: workLog,
65
+ });
66
+ }
67
+ export async function updateTaskStatus(userId, projectId, taskId, status, agentId) {
68
+ return apiRequest(`/api/v1/tasks/${userId}/${projectId}/${taskId}/status`, 'POST', {
69
+ status,
70
+ agent_id: agentId,
71
+ });
72
+ }
73
+ export async function addTaskContext(userId, projectId, taskId, contextType, content, addedBy, source) {
74
+ return apiRequest(`/api/v1/tasks/${userId}/${projectId}/${taskId}/context`, 'POST', {
75
+ context_type: contextType,
76
+ content,
77
+ added_by: addedBy,
78
+ source,
79
+ });
80
+ }
81
+ export async function getTaskSummary(userId, projectId) {
82
+ return apiRequest(`/api/v1/tasks/${userId}/${projectId}/summary`);
83
+ }
84
+ export async function heartbeatTask(userId, projectId, taskId, agentId, extendMinutes = 30) {
85
+ return apiRequest(`/api/v1/tasks/${userId}/${projectId}/${taskId}/heartbeat`, 'POST', {
86
+ agent_id: agentId,
87
+ extend_minutes: extendMinutes,
88
+ });
89
+ }
90
+ // -----------------------------------------------------------------------------
91
+ // Orchestration Functions
92
+ // -----------------------------------------------------------------------------
93
+ /**
94
+ * Get all tasks ready for parallel dispatch
95
+ * - Filters for 'ready' status
96
+ * - Excludes already claimed tasks
97
+ * - Resolves dependencies (only returns unblocked tasks)
98
+ * - Matches each to appropriate expert worker
99
+ */
100
+ export async function getDispatchableTasks(userId, projectId, epicId, maxParallel = 5) {
101
+ // Get all tasks with 'ready' status
102
+ const result = await listTasks(userId, projectId, {
103
+ status: 'ready',
104
+ parent_id: epicId,
105
+ limit: 50,
106
+ });
107
+ if (!result.success || !result.data.length) {
108
+ return [];
109
+ }
110
+ const dispatchable = [];
111
+ for (const task of result.data) {
112
+ // Skip if already claimed (has lock)
113
+ if (task.assigned_to && task.lock_expires_at) {
114
+ const lockExpires = new Date(task.lock_expires_at);
115
+ if (lockExpires > new Date()) {
116
+ continue; // Still locked
117
+ }
118
+ }
119
+ // Check dependencies (blocked_by)
120
+ const blockedBy = parseJsonArray(task.blocked_by);
121
+ if (blockedBy.length > 0) {
122
+ // Check if blocking tasks are done
123
+ let allBlockersComplete = true;
124
+ for (const blockerId of blockedBy) {
125
+ const blockerResult = await getTask(userId, projectId, blockerId);
126
+ if (blockerResult.success) {
127
+ const blocker = blockerResult.data;
128
+ if (blocker.status !== 'done') {
129
+ allBlockersComplete = false;
130
+ break;
131
+ }
132
+ }
133
+ }
134
+ if (!allBlockersComplete) {
135
+ continue; // Still blocked
136
+ }
137
+ }
138
+ // Match to expert worker
139
+ const worker = matchTaskToWorker({
140
+ title: task.title,
141
+ description: task.description || undefined,
142
+ domain: task.domain || undefined,
143
+ tags: parseJsonArray(task.tags),
144
+ });
145
+ // Get task context
146
+ const fullTask = await getTask(userId, projectId, task.id, true);
147
+ const taskData = fullTask.data;
148
+ dispatchable.push({
149
+ task,
150
+ worker,
151
+ context: taskData.context || [],
152
+ parentContext: taskData.parent,
153
+ dependencies: [],
154
+ });
155
+ if (dispatchable.length >= maxParallel) {
156
+ break;
157
+ }
158
+ }
159
+ return dispatchable;
160
+ }
161
+ /**
162
+ * Get full execution context for a worker agent
163
+ */
164
+ export async function getExecutionContext(userId, projectId, taskId) {
165
+ // Get full task data
166
+ const result = await getTask(userId, projectId, taskId, true);
167
+ if (!result.success) {
168
+ throw new Error(`Task not found: ${taskId}`);
169
+ }
170
+ const taskData = result.data;
171
+ const task = taskData.task;
172
+ // Match to worker
173
+ const worker = matchTaskToWorker({
174
+ title: task.title,
175
+ description: task.description || undefined,
176
+ domain: task.domain || undefined,
177
+ tags: parseJsonArray(task.tags),
178
+ });
179
+ // Get sibling tasks for context
180
+ let siblingTasks;
181
+ if (task.parent_id) {
182
+ const siblingResult = await listTasks(userId, projectId, { parent_id: task.parent_id, limit: 20 });
183
+ if (siblingResult.success) {
184
+ siblingTasks = siblingResult.data.filter(t => t.id !== taskId);
185
+ }
186
+ }
187
+ // Build system prompt
188
+ let systemPrompt = worker.promptTemplate + '\n\n';
189
+ systemPrompt += `# Task: ${task.title}\n\n`;
190
+ if (task.description) {
191
+ systemPrompt += `## Description\n${task.description}\n\n`;
192
+ }
193
+ // Add acceptance criteria
194
+ const criteria = parseJsonArray(task.acceptance_criteria);
195
+ if (criteria.length > 0) {
196
+ systemPrompt += `## Acceptance Criteria\n`;
197
+ criteria.forEach((c, i) => {
198
+ systemPrompt += `${i + 1}. ${c}\n`;
199
+ });
200
+ systemPrompt += '\n';
201
+ }
202
+ // Add parent context
203
+ if (taskData.parent) {
204
+ systemPrompt += `## Epic Context\n`;
205
+ systemPrompt += `**Epic:** ${taskData.parent.title}\n`;
206
+ if (taskData.parent.description) {
207
+ systemPrompt += `**Goal:** ${taskData.parent.description}\n`;
208
+ }
209
+ systemPrompt += '\n';
210
+ }
211
+ // Add attached context
212
+ if (taskData.context.length > 0) {
213
+ systemPrompt += `## Attached Context\n`;
214
+ for (const ctx of taskData.context) {
215
+ systemPrompt += `### ${ctx.context_type.toUpperCase()}${ctx.source ? ` (${ctx.source})` : ''}\n`;
216
+ systemPrompt += `${ctx.content}\n\n`;
217
+ }
218
+ }
219
+ // Add sibling task awareness
220
+ if (siblingTasks && siblingTasks.length > 0) {
221
+ systemPrompt += `## Related Tasks (for awareness)\n`;
222
+ for (const sibling of siblingTasks.slice(0, 5)) {
223
+ systemPrompt += `- ${sibling.title} [${sibling.status}]\n`;
224
+ }
225
+ systemPrompt += '\n';
226
+ }
227
+ return {
228
+ task,
229
+ worker,
230
+ systemPrompt,
231
+ context: taskData.context,
232
+ parentTask: taskData.parent,
233
+ siblingTasks,
234
+ };
235
+ }
236
+ /**
237
+ * Find newly unblocked tasks after a completion
238
+ */
239
+ export async function resolveTaskDependencies(userId, projectId, completedTaskId, epicId) {
240
+ // Get all tasks that might be blocked by the completed task
241
+ const result = await listTasks(userId, projectId, {
242
+ status: 'ready', // or 'blocked'
243
+ parent_id: epicId,
244
+ limit: 50,
245
+ });
246
+ if (!result.success) {
247
+ return [];
248
+ }
249
+ const newlyUnblocked = [];
250
+ for (const task of result.data) {
251
+ const blockedBy = parseJsonArray(task.blocked_by);
252
+ // Check if this task was blocked by the completed task
253
+ if (blockedBy.includes(completedTaskId)) {
254
+ // Check if all other blockers are also done
255
+ let allBlockersComplete = true;
256
+ for (const blockerId of blockedBy) {
257
+ if (blockerId === completedTaskId)
258
+ continue; // Already done
259
+ const blockerResult = await getTask(userId, projectId, blockerId);
260
+ if (blockerResult.success) {
261
+ const blocker = blockerResult.data;
262
+ if (blocker.status !== 'done') {
263
+ allBlockersComplete = false;
264
+ break;
265
+ }
266
+ }
267
+ }
268
+ if (allBlockersComplete) {
269
+ newlyUnblocked.push(task);
270
+ }
271
+ }
272
+ }
273
+ return newlyUnblocked;
274
+ }
@@ -0,0 +1,18 @@
1
+ export interface ExpertWorker {
2
+ id: string;
3
+ name: string;
4
+ description: string;
5
+ domains: string[];
6
+ capabilities: string[];
7
+ promptTemplate: string;
8
+ }
9
+ export declare const EXPERT_WORKERS: Record<string, ExpertWorker>;
10
+ /**
11
+ * Match a task to the best expert worker based on domain patterns
12
+ */
13
+ export declare function matchTaskToWorker(task: {
14
+ title: string;
15
+ description?: string;
16
+ domain?: string;
17
+ tags?: string[];
18
+ }): ExpertWorker;
@@ -0,0 +1,146 @@
1
+ // =============================================================================
2
+ // Expert Worker Definitions (Orchestration System)
3
+ // =============================================================================
4
+ export const EXPERT_WORKERS = {
5
+ 'schema-expert': {
6
+ id: 'schema-expert',
7
+ name: 'Database Schema Expert',
8
+ description: 'Specialises in database schema design, migrations, SQL, and data modelling',
9
+ domains: ['**/schema/**', '**/*.sql', '**/migrations/**', '**/models/**'],
10
+ capabilities: ['schema-design', 'sql-queries', 'migrations', 'indexing', 'data-modelling'],
11
+ promptTemplate: `You are a database schema expert. Your domain is limited to:
12
+ - SQL schema files and migrations
13
+ - Database table design and relationships
14
+ - Index optimisation
15
+ - Data modelling decisions
16
+
17
+ Focus only on schema-related changes. Do not modify application code outside your domain.
18
+ When complete, provide a concise summary of changes made.`,
19
+ },
20
+ 'api-expert': {
21
+ id: 'api-expert',
22
+ name: 'API & Routes Expert',
23
+ description: 'Specialises in HTTP endpoints, route handlers, request/response handling',
24
+ domains: ['**/routes/**', '**/api/**', '**/handlers/**', '**/controllers/**'],
25
+ capabilities: ['rest-api', 'route-handlers', 'validation', 'error-handling', 'http-methods'],
26
+ promptTemplate: `You are an API and routes expert. Your domain is limited to:
27
+ - HTTP route definitions and handlers
28
+ - Request validation and parsing
29
+ - Response formatting
30
+ - Error handling for endpoints
31
+ - API documentation
32
+
33
+ Focus only on API-related changes. Do not modify schema or extraction code.
34
+ When complete, provide a concise summary of endpoints created or modified.`,
35
+ },
36
+ 'mcp-expert': {
37
+ id: 'mcp-expert',
38
+ name: 'MCP Server Expert',
39
+ description: 'Specialises in MCP tool definitions, handlers, and server configuration',
40
+ domains: ['**/mcp-server/**', '**/mcp/**', '**/*mcp*.ts'],
41
+ capabilities: ['mcp-tools', 'mcp-handlers', 'tool-definitions', 'mcp-resources'],
42
+ promptTemplate: `You are an MCP (Model Context Protocol) expert. Your domain is limited to:
43
+ - MCP tool definitions and schemas
44
+ - Tool handler implementations
45
+ - MCP resources and prompts
46
+ - Server configuration
47
+
48
+ Focus only on MCP-related changes. Follow MCP SDK patterns exactly.
49
+ When complete, provide a concise summary of tools added or modified.`,
50
+ },
51
+ 'extraction-expert': {
52
+ id: 'extraction-expert',
53
+ name: 'Code Extraction Expert',
54
+ description: 'Specialises in AST parsing, code analysis, and fact extraction',
55
+ domains: ['**/extraction/**', '**/parsers/**', '**/analyzers/**', '**/*extractor*.ts'],
56
+ capabilities: ['ast-parsing', 'code-analysis', 'fact-extraction', 'language-support'],
57
+ promptTemplate: `You are a code extraction expert. Your domain is limited to:
58
+ - AST parsing and traversal
59
+ - Code pattern recognition
60
+ - Fact and relationship extraction
61
+ - Multi-language support
62
+
63
+ Focus only on extraction logic. Follow the BaseExtractor pattern.
64
+ When complete, provide a concise summary of extraction capabilities added.`,
65
+ },
66
+ 'integration-expert': {
67
+ id: 'integration-expert',
68
+ name: 'Integration Expert',
69
+ description: 'Specialises in connecting systems, wiring components, and configuration',
70
+ domains: ['**/config/**', '**/index.ts', '**/main.ts', '**/wrangler.toml', '**/*.json'],
71
+ capabilities: ['system-integration', 'configuration', 'wiring', 'dependency-injection'],
72
+ promptTemplate: `You are an integration expert. Your domain is limited to:
73
+ - Connecting components and systems
74
+ - Configuration files and environment setup
75
+ - Import/export wiring
76
+ - Dependency management
77
+
78
+ Focus on integration without modifying core implementation logic.
79
+ When complete, provide a concise summary of integrations configured.`,
80
+ },
81
+ 'general-expert': {
82
+ id: 'general-expert',
83
+ name: 'General Purpose Expert',
84
+ description: 'Handles tasks that do not fit other expert domains',
85
+ domains: ['**/*'],
86
+ capabilities: ['general-development', 'refactoring', 'documentation', 'testing'],
87
+ promptTemplate: `You are a general purpose development expert. Handle this task with care:
88
+ - Follow existing code patterns
89
+ - Make minimal necessary changes
90
+ - Document your reasoning
91
+
92
+ When complete, provide a concise summary of work done.`,
93
+ },
94
+ };
95
+ /**
96
+ * Match a task to the best expert worker based on domain patterns
97
+ */
98
+ export function matchTaskToWorker(task) {
99
+ // If task has explicit domain pattern, match against worker domains
100
+ if (task.domain) {
101
+ for (const [_, worker] of Object.entries(EXPERT_WORKERS)) {
102
+ if (worker.id === 'general-expert')
103
+ continue; // Skip fallback
104
+ for (const pattern of worker.domains) {
105
+ // Simple glob matching - convert ** to regex
106
+ const regex = new RegExp(pattern
107
+ .replace(/\*\*/g, '.*')
108
+ .replace(/\*/g, '[^/]*')
109
+ .replace(/\//g, '\\/'));
110
+ if (regex.test(task.domain)) {
111
+ return worker;
112
+ }
113
+ }
114
+ }
115
+ }
116
+ // Keyword-based matching from title and description
117
+ const searchText = `${task.title} ${task.description || ''} ${(task.tags || []).join(' ')}`.toLowerCase();
118
+ const keywordMap = {
119
+ 'schema': 'schema-expert',
120
+ 'database': 'schema-expert',
121
+ 'sql': 'schema-expert',
122
+ 'migration': 'schema-expert',
123
+ 'table': 'schema-expert',
124
+ 'api': 'api-expert',
125
+ 'route': 'api-expert',
126
+ 'endpoint': 'api-expert',
127
+ 'handler': 'api-expert',
128
+ 'http': 'api-expert',
129
+ 'mcp': 'mcp-expert',
130
+ 'tool': 'mcp-expert',
131
+ 'extraction': 'extraction-expert',
132
+ 'extractor': 'extraction-expert',
133
+ 'ast': 'extraction-expert',
134
+ 'parser': 'extraction-expert',
135
+ 'integration': 'integration-expert',
136
+ 'config': 'integration-expert',
137
+ 'wiring': 'integration-expert',
138
+ };
139
+ for (const [keyword, workerId] of Object.entries(keywordMap)) {
140
+ if (searchText.includes(keyword)) {
141
+ return EXPERT_WORKERS[workerId];
142
+ }
143
+ }
144
+ // Fallback to general expert
145
+ return EXPERT_WORKERS['general-expert'];
146
+ }
@@ -0,0 +1,6 @@
1
+ export type { ExpertWorker } from './helpers/workers.js';
2
+ export type { Task, TaskContext, DispatchableTask } from './helpers/tasks.js';
3
+ export { EXPERT_WORKERS, matchTaskToWorker } from './helpers/workers.js';
4
+ export { parseJsonArray, getDispatchableTasks, getExecutionContext, resolveTaskDependencies, createTask, listTasks, getTask, claimTask, releaseTask, updateTaskStatus, addTaskContext, getTaskSummary, heartbeatTask } from './helpers/tasks.js';
5
+ export { injectContext } from './helpers/api-client.js';
6
+ export declare function startServer(): Promise<void>;
package/dist/index.js ADDED
@@ -0,0 +1,75 @@
1
+ // =============================================================================
2
+ // ClaudeTools Memory MCP Server - Main Entry Point
3
+ // =============================================================================
4
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
5
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
6
+ import { mcpLogger } from './logger.js';
7
+ import { API_BASE_URL, getDefaultProjectIdAsync, AUTO_INJECT_CONTEXT, } from './helpers/config.js';
8
+ // Tool definitions and handlers
9
+ import { registerToolDefinitions } from './tools.js';
10
+ import { registerToolHandlers } from './handlers/tool-handlers.js';
11
+ // Resource and prompt handlers
12
+ import { registerResourceHandlers } from './resources.js';
13
+ import { registerPromptHandlers } from './prompts.js';
14
+ // Re-export functions that are used by handlers
15
+ export { EXPERT_WORKERS, matchTaskToWorker } from './helpers/workers.js';
16
+ export { parseJsonArray, getDispatchableTasks, getExecutionContext, resolveTaskDependencies, createTask, listTasks, getTask, claimTask, releaseTask, updateTaskStatus, addTaskContext, getTaskSummary, heartbeatTask } from './helpers/tasks.js';
17
+ export { injectContext } from './helpers/api-client.js';
18
+ // =============================================================================
19
+ // Server Initialization
20
+ // =============================================================================
21
+ const server = new Server({
22
+ name: 'claudetools-memory',
23
+ version: '1.0.0',
24
+ }, {
25
+ capabilities: {
26
+ tools: {},
27
+ resources: {},
28
+ prompts: {},
29
+ },
30
+ });
31
+ // =============================================================================
32
+ // Request Handler Registration
33
+ // =============================================================================
34
+ // Tools
35
+ registerToolDefinitions(server);
36
+ registerToolHandlers(server);
37
+ // Resources
38
+ registerResourceHandlers(server);
39
+ // Prompts
40
+ registerPromptHandlers(server);
41
+ // =============================================================================
42
+ // Server Start
43
+ // =============================================================================
44
+ export async function startServer() {
45
+ mcpLogger.separator('MCP Server Starting');
46
+ // Resolve project ID asynchronously (may auto-register)
47
+ let projectId;
48
+ try {
49
+ projectId = await getDefaultProjectIdAsync();
50
+ mcpLogger.info('TOOL', `Project ID resolved: ${projectId}`);
51
+ }
52
+ catch (error) {
53
+ mcpLogger.error('TOOL', `Failed to resolve project ID: ${error}`);
54
+ mcpLogger.warn('TOOL', 'Continuing startup - tools will fail until project is registered');
55
+ projectId = 'not-configured';
56
+ }
57
+ mcpLogger.info('TOOL', 'ClaudeTools Memory MCP Server v1.0.0', {
58
+ autoInject: AUTO_INJECT_CONTEXT,
59
+ apiUrl: API_BASE_URL,
60
+ projectId,
61
+ });
62
+ const transport = new StdioServerTransport();
63
+ await server.connect(transport);
64
+ mcpLogger.info('TOOL', 'Server connected via stdio');
65
+ console.error(`ClaudeTools Memory MCP server running on stdio (auto-inject: ${AUTO_INJECT_CONTEXT ? 'enabled' : 'disabled'})`);
66
+ }
67
+ // Only run if this file is executed directly (not imported)
68
+ // This check doesn't work reliably in ESM, so we'll rely on cli.ts
69
+ // But we keep this for backward compatibility when running index.ts directly
70
+ if (import.meta.url === `file://${process.argv[1]}`) {
71
+ startServer().catch((error) => {
72
+ console.error('Fatal error:', error);
73
+ process.exit(1);
74
+ });
75
+ }
@@ -0,0 +1,32 @@
1
+ export type LogCategory = 'TOOL' | 'API' | 'MEMORY' | 'SEARCH' | 'INJECT' | 'EXTRACT' | 'STORE' | 'QUERY' | 'IMPACT' | 'PATTERN' | 'CONFIG' | 'REGISTRATION' | 'ERROR';
2
+ declare class MCPLogger {
3
+ private logFile;
4
+ private logDir;
5
+ private enabled;
6
+ constructor();
7
+ private formatTimestamp;
8
+ private formatForFile;
9
+ private formatDataAsNaturalLanguage;
10
+ private write;
11
+ debug(category: LogCategory, message: string, data?: Record<string, unknown>): void;
12
+ info(category: LogCategory, message: string, data?: Record<string, unknown>): void;
13
+ warn(category: LogCategory, message: string, data?: Record<string, unknown>): void;
14
+ error(category: LogCategory, message: string, error?: unknown): void;
15
+ toolCall(toolName: string, args: Record<string, unknown>): void;
16
+ toolResult(toolName: string, success: boolean, duration: number, resultSummary?: string): void;
17
+ apiRequest(method: string, endpoint: string): void;
18
+ apiResponse(method: string, endpoint: string, status: number, duration: number): void;
19
+ searchQuery(query: string, method: string): void;
20
+ searchResults(factsFound: number, entitiesFound: number, duration: number): void;
21
+ memoryStore(entity1: string, relation: string, entity2: string): void;
22
+ queryDependencies(functionName: string, direction: string, resultsCount: number, duration: number): void;
23
+ impactAnalysis(functionName: string, analysisType: string, riskLevel: string, totalAffected: number, duration: number): void;
24
+ patternCheck(codeLength: number, warningsFound: number, securityScore: number, perfScore: number, duration: number): void;
25
+ contextInjection(query: string, factsIncluded: number, tokenBudget: number, duration: number): void;
26
+ private sanitizeArgs;
27
+ startTimer(): () => number;
28
+ separator(label?: string): void;
29
+ clear(): void;
30
+ }
31
+ export declare const mcpLogger: MCPLogger;
32
+ export {};