import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { EmployeeApiClient } from "../api/employee-api.js";
import eventBus, {
type EmployeeConflictEvent,
type EmployeeCreatedEvent,
type EmployeeErrorEvent,
type EmployeeProcessingEvent,
type EmployeeValidationEvent,
} from "../common/event-bus.js";
import {
type CreateEmployeeDTO,
EmployeeAddListSchema,
EmployeeAddOneSchema,
type EmployeeConflictResolution,
EmployeeConflictResolutionSchema,
type EmployeeDraft,
type EmployeeForceCreate,
EmployeeForceCreateSchema,
EmployeeImportXlsxSchema,
type EmployeeProcessingResult,
EmployeeTelegramHandlerSchema,
type UpdateEmployeeDTO,
} from "../schemas/employee-schemas.js";
import { DataProcessor } from "../utils/data-processor.js";
import { measure } from "../utils/performance-monitor.js";
import { XlsxParser } from "../utils/xlsx-parser.js";
import { SessionManager } from "./auth-tools.js";
/**
* Основной класс для обработки команд добавления сотрудников
* Реализует 4 сценария: add one, add list, import XLSX, Telegram
*/
export class EmployeeHandler {
private apiClient: EmployeeApiClient;
private sessionManager: SessionManager;
constructor() {
this.sessionManager = SessionManager.getInstance();
this.apiClient = new EmployeeApiClient();
}
/**
* Получение API ключа из сессии или параметров
*/
private getApiKey(sessionId?: string): string {
return (
this.sessionManager.getApiKey(sessionId) ||
this.sessionManager.getCurrentApiKey() ||
process.env.EMPLOYEE_API_KEY ||
""
);
}
/**
* Публикация события assistant.employee.created
*/
private publishEmployeeCreated(employee: CreateEmployeeDTO, result: unknown): void {
const event: EmployeeCreatedEvent = {
event: "assistant.employee.created",
payload: {
employee,
result,
timestamp: new Date().toISOString(),
},
};
console.log("[EVENT] Publishing assistant.employee.created", event);
eventBus.emitEmployeeCreated(event);
}
/**
* Публикация ошибки в mcp.logs.error
*/
private publishError(error: string, context?: unknown): void {
const errorEvent: EmployeeErrorEvent = {
event: "mcp.logs.error",
payload: {
error,
context,
timestamp: new Date().toISOString(),
},
};
console.error("[ERROR] Publishing error event", errorEvent);
eventBus.emitEmployeeError(errorEvent);
}
/**
* Публикация события начала обработки
*/
private publishProcessingStart(
action: EmployeeProcessingEvent["payload"]["action"],
companyId: string,
sessionId?: string,
userId?: string
): void {
const event: EmployeeProcessingEvent = {
event: "assistant.employee.processing",
payload: {
action,
companyId,
sessionId,
userId,
timestamp: new Date().toISOString(),
},
};
console.log("[EVENT] Publishing processing start", event);
eventBus.emitEmployeeProcessing(event);
}
/**
* Публикация события валидации
*/
private publishValidation(
action: EmployeeValidationEvent["payload"]["action"],
employeeData: unknown,
qualityScore?: number,
errors?: string[]
): void {
const event: EmployeeValidationEvent = {
event: "assistant.employee.validation",
payload: {
action,
employeeData,
qualityScore,
errors,
timestamp: new Date().toISOString(),
},
};
console.log("[EVENT] Publishing validation", event);
eventBus.emitEmployeeValidation(event);
}
/**
* Публикация события конфликта
*/
private publishConflict(
conflictType: EmployeeConflictEvent["payload"]["conflictType"],
action: EmployeeConflictEvent["payload"]["action"],
employeeData: unknown,
existingEmployees?: unknown[]
): void {
const event: EmployeeConflictEvent = {
event: "assistant.employee.conflict",
payload: {
conflictType,
action,
employeeData,
existingEmployees,
timestamp: new Date().toISOString(),
},
};
console.log("[EVENT] Publishing conflict", event);
eventBus.emitEmployeeConflict(event);
}
/**
* Сценарий 1: Добавление одного сотрудника
*/
async processAddOne(rawText: string, companyId: string, sessionId?: string): Promise<EmployeeProcessingResult> {
return measure(
"employee_add_one",
async () => {
try {
console.log("[EMPLOYEE] Processing add one", { rawText, companyId, sessionId });
// Публикуем событие начала обработки
this.publishProcessingStart("add_one", companyId, sessionId);
// Валидация входных данных
if (!rawText || rawText.trim().length === 0) {
return {
success: false,
processed: 0,
failed: 1,
duplicates: 0,
errors: ["Пустой текст для обработки"],
report: "Ошибка: Не предоставлен текст для добавления сотрудника",
};
}
if (!companyId || companyId.trim().length === 0) {
return {
success: false,
processed: 0,
failed: 1,
duplicates: 0,
errors: ["Не указан ID компании"],
report: "Ошибка: Не указан companyId",
};
}
// Создаём draft из текста с продвинутым парсингом
const draft = DataProcessor.parseAdvancedText(rawText);
const validatedDraft = DataProcessor.validateEmployeeDraft(draft);
// Дополнительная проверка качества данных
const qualityCheck = DataProcessor.validateDataQuality(validatedDraft);
// Публикуем событие валидации
this.publishValidation("validation_start", validatedDraft, qualityCheck.score);
if (!validatedDraft.isValid) {
// Публикуем событие неудачной валидации
this.publishValidation("validation_failed", validatedDraft, qualityCheck.score, validatedDraft.errors);
return {
success: false,
processed: 0,
failed: 1,
duplicates: 0,
errors: validatedDraft.errors,
report: `Ошибка валидации (качество: ${qualityCheck.score}%): ${validatedDraft.errors?.join(", ")}`,
};
}
// Публикуем событие успешной валидации
this.publishValidation("validation_success", validatedDraft, qualityCheck.score);
// Проверяем уникальность
const apiKey = this.getApiKey(sessionId);
this.apiClient = new EmployeeApiClient(undefined, apiKey);
const uniquenessCheck = await this.apiClient.checkEmployeeUniqueness(
validatedDraft.lastName!,
validatedDraft.firstName!,
validatedDraft.email
);
// Обрабатываем конфликты
const conflictResolution = await this.apiClient.handleEmployeeConflict(
{
companyId,
lastName: validatedDraft.lastName!,
firstName: validatedDraft.firstName!,
middleName: validatedDraft.middleName,
email: validatedDraft.email!,
phones: validatedDraft.phones,
},
uniquenessCheck
);
if (conflictResolution.action === "manual") {
// Публикуем событие конфликта
this.publishConflict(
uniquenessCheck.conflictType || "exact",
"manual",
validatedDraft,
uniquenessCheck.employees
);
return {
success: false,
processed: 0,
failed: 1,
duplicates: uniquenessCheck.found ? 1 : 0,
errors: [conflictResolution.message],
report: `${conflictResolution.message}\n\nРекомендуемые действия:\n${conflictResolution.suggestedActions?.join("\n") || ""}`,
};
}
if (conflictResolution.action === "skip") {
// Публикуем событие конфликта
this.publishConflict(
uniquenessCheck.conflictType || "exact",
"skip",
validatedDraft,
uniquenessCheck.employees
);
return {
success: false,
processed: 0,
failed: 1,
duplicates: 1,
errors: ["Добавление пропущено из-за конфликта"],
report: conflictResolution.message,
};
}
// Создаём или обновляем сотрудника
const createDto: CreateEmployeeDTO = {
companyId,
lastName: validatedDraft.lastName!,
firstName: validatedDraft.firstName!,
middleName: validatedDraft.middleName,
email: validatedDraft.email!,
phones: validatedDraft.phones,
};
let result: unknown;
let actionType: string;
if (
conflictResolution.action === "update" &&
uniquenessCheck.employees &&
uniquenessCheck.employees.length > 0
) {
// Обновляем существующего сотрудника
const existingEmployee = uniquenessCheck.employees[0] as { id: string };
const updateDto: UpdateEmployeeDTO = {
lastName: createDto.lastName,
firstName: createDto.firstName,
middleName: createDto.middleName,
email: createDto.email,
phones: createDto.phones,
};
result = await this.apiClient.updateEmployee(existingEmployee.id, updateDto);
actionType = "обновлён";
} else {
// Создаём нового сотрудника
result = await this.apiClient.createEmployee(createDto);
actionType = "добавлен";
}
// Публикуем событие
this.publishEmployeeCreated(createDto, result);
return {
success: true,
processed: 1,
failed: 0,
duplicates: 0,
employees: [createDto],
report: `✅ Сотрудник ${createDto.lastName} ${createDto.firstName} успешно ${actionType} (качество данных: ${qualityCheck.score}%)`,
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.publishError(errorMessage, { rawText, companyId, sessionId });
return {
success: false,
processed: 0,
failed: 1,
duplicates: 0,
errors: [errorMessage],
report: `❌ Ошибка при добавлении сотрудника: ${errorMessage}`,
};
}
},
{ companyId, sessionId, operation: "add_one" }
);
}
/**
* Сценарий 2: Добавление списка сотрудников из текста
*/
async processAddList(rawText: string, companyId: string, sessionId?: string): Promise<EmployeeProcessingResult> {
return measure(
"employee_add_list",
async () => {
try {
console.log("[EMPLOYEE] Processing add list", { rawText, companyId, sessionId });
// Публикуем событие начала обработки
this.publishProcessingStart("add_list", companyId, sessionId);
// Валидация входных данных
if (!rawText || rawText.trim().length === 0) {
return {
success: false,
processed: 0,
failed: 1,
duplicates: 0,
errors: ["Пустой текст для обработки"],
report: "Ошибка: Не предоставлен текст для добавления списка сотрудников",
};
}
if (!companyId || companyId.trim().length === 0) {
return {
success: false,
processed: 0,
failed: 1,
duplicates: 0,
errors: ["Не указан ID компании"],
report: "Ошибка: Не указан companyId",
};
}
// Парсим список сотрудников из текста
const lines = rawText.split("\n").filter((line) => line.trim().length > 0);
if (lines.length === 0) {
return {
success: false,
processed: 0,
failed: 1,
duplicates: 0,
errors: ["Не найдено ни одной строки для обработки"],
report: "Ошибка: Список сотрудников пуст",
};
}
if (lines.length > 100) {
return {
success: false,
processed: 0,
failed: lines.length,
duplicates: 0,
errors: [`Список содержит ${lines.length} строк, но поддерживается максимум 100`],
report: "Ошибка: Превышен лимит строк (максимум 100)",
};
}
const drafts: EmployeeDraft[] = [];
const results: CreateEmployeeDTO[] = [];
let processed = 0;
let failed = 0;
let duplicates = 0;
const errors: string[] = [];
const apiKey = this.getApiKey(sessionId);
this.apiClient = new EmployeeApiClient(undefined, apiKey);
for (const line of lines) {
const draft = DataProcessor.parseAdvancedText(line);
const validatedDraft = DataProcessor.validateEmployeeDraft(draft);
drafts.push(validatedDraft);
if (!validatedDraft.isValid) {
failed++;
errors.push(`Строка "${line}": ${validatedDraft.errors?.join(", ")}`);
continue;
}
// Проверяем уникальность
const uniquenessCheck = await this.apiClient.checkEmployeeUniqueness(
validatedDraft.lastName!,
validatedDraft.firstName!,
validatedDraft.email
);
if (uniquenessCheck.found) {
duplicates++;
errors.push(`Строка "${line}": Дубликат - ${uniquenessCheck.message}`);
continue;
}
// Создаём сотрудника
const createDto: CreateEmployeeDTO = {
companyId,
lastName: validatedDraft.lastName!,
firstName: validatedDraft.firstName!,
middleName: validatedDraft.middleName,
email: validatedDraft.email!,
phones: validatedDraft.phones,
};
try {
const result = await this.apiClient.createEmployee(createDto);
this.publishEmployeeCreated(createDto, result);
results.push(createDto);
processed++;
} catch (error) {
failed++;
const errorMessage = error instanceof Error ? error.message : String(error);
errors.push(`Строка "${line}": ${errorMessage}`);
}
}
const report = DataProcessor.createErrorReport(drafts);
const success = processed > 0;
return {
success,
processed,
failed,
duplicates,
employees: results,
errors: errors.length > 0 ? errors : undefined,
report,
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.publishError(errorMessage, { rawText, companyId, sessionId });
return {
success: false,
processed: 0,
failed: 1,
duplicates: 0,
errors: [errorMessage],
report: `❌ Ошибка при обработке списка: ${errorMessage}`,
};
}
},
{ companyId, sessionId, operation: "add_list" }
);
}
/**
* Сценарий 3: Импорт из XLSX файла
*/
async processImportXlsx(fileUrl: string, companyId: string, sessionId?: string): Promise<EmployeeProcessingResult> {
try {
console.log("[EMPLOYEE] Processing XLSX import", { fileUrl, companyId, sessionId });
// Парсим XLSX файл
const parseResult = await XlsxParser.parseFromUrl(fileUrl);
if (!parseResult.success) {
return {
success: false,
processed: 0,
failed: parseResult.totalRows,
duplicates: 0,
errors: parseResult.errors,
report: parseResult.report,
};
}
// Обрабатываем валидные записи
const results: CreateEmployeeDTO[] = [];
let processed = 0;
let failed = 0;
let duplicates = 0;
const errors: string[] = [...parseResult.errors];
const apiKey = this.getApiKey(sessionId);
this.apiClient = new EmployeeApiClient(undefined, apiKey);
for (const draft of parseResult.employees) {
if (!draft.isValid) {
failed++;
continue;
}
// Проверяем уникальность
const uniquenessCheck = await this.apiClient.checkEmployeeUniqueness(
draft.lastName!,
draft.firstName!,
draft.email
);
if (uniquenessCheck.found) {
duplicates++;
errors.push(`Сотрудник ${draft.lastName} ${draft.firstName} уже существует`);
continue;
}
// Создаём сотрудника
const createDto: CreateEmployeeDTO = {
companyId,
lastName: draft.lastName!,
firstName: draft.firstName!,
middleName: draft.middleName,
email: draft.email!,
phones: draft.phones,
};
try {
const result = await this.apiClient.createEmployee(createDto);
this.publishEmployeeCreated(createDto, result);
results.push(createDto);
processed++;
} catch (error) {
failed++;
const errorMessage = error instanceof Error ? error.message : String(error);
errors.push(`Ошибка создания ${draft.lastName} ${draft.firstName}: ${errorMessage}`);
}
}
const success = processed > 0;
const report =
parseResult.report +
`\n\nРезультат обработки:\nОбработано: ${processed}\nОшибок: ${failed}\nДубликатов: ${duplicates}`;
return {
success,
processed,
failed,
duplicates,
employees: results,
errors: errors.length > 0 ? errors : undefined,
report,
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.publishError(errorMessage, { fileUrl, companyId, sessionId });
return {
success: false,
processed: 0,
failed: 1,
duplicates: 0,
errors: [errorMessage],
report: `Ошибка при импорте XLSX: ${errorMessage}`,
};
}
}
/**
* Сценарий 4: Обработка Telegram сообщений
*/
async processTelegramMessage(
message: string,
companyId: string,
userId: string,
sessionId?: string
): Promise<EmployeeProcessingResult> {
try {
console.log("[EMPLOYEE] Processing Telegram message", { message, companyId, userId, sessionId });
// Публикуем событие начала обработки
this.publishProcessingStart("telegram_handler", companyId, sessionId, userId);
// Валидация входных данных
if (!message || message.trim().length === 0) {
return {
success: false,
processed: 0,
failed: 1,
duplicates: 0,
errors: ["Пустое сообщение"],
report: "❌ Ошибка: Получено пустое сообщение",
};
}
if (!companyId || companyId.trim().length === 0) {
return {
success: false,
processed: 0,
failed: 1,
duplicates: 0,
errors: ["Не указан ID компании"],
report: "❌ Ошибка: Не указан companyId",
};
}
if (!userId || userId.trim().length === 0) {
return {
success: false,
processed: 0,
failed: 1,
duplicates: 0,
errors: ["Не указан ID пользователя"],
report: "❌ Ошибка: Не указан userId",
};
}
// Определяем тип команды из сообщения
const lowerMessage = message.toLowerCase();
// Ключевые слова для определения типа команды
const singleEmployeeKeywords = [
"добавь сотрудника",
"добавить сотрудника",
"новый сотрудник",
"создай сотрудника",
"создать сотрудника",
"добавь работника",
];
const listKeywords = [
"список",
"добавь список",
"добавить список",
"импорт",
"массовое добавление",
"несколько сотрудников",
];
const isSingleEmployee = singleEmployeeKeywords.some((keyword) => lowerMessage.includes(keyword));
const isList = listKeywords.some((keyword) => lowerMessage.includes(keyword));
if (isSingleEmployee) {
// Одиночное добавление
console.log("[EMPLOYEE] Detected single employee command");
return this.processAddOne(message, companyId, sessionId);
} else if (isList) {
// Добавление списка
console.log("[EMPLOYEE] Detected list command");
return this.processAddList(message, companyId, sessionId);
} else {
// Пытаемся определить по контексту
const lines = message.split("\n").filter((line) => line.trim().length > 0);
if (lines.length === 1) {
console.log("[EMPLOYEE] Auto-detected single employee (1 line)");
return this.processAddOne(message, companyId, sessionId);
} else {
console.log("[EMPLOYEE] Auto-detected list (multiple lines)");
return this.processAddList(message, companyId, sessionId);
}
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.publishError(errorMessage, { message, companyId, userId, sessionId });
return {
success: false,
processed: 0,
failed: 1,
duplicates: 0,
errors: [errorMessage],
report: `❌ Ошибка при обработке Telegram сообщения: ${errorMessage}`,
};
}
}
/**
* Обработка разрешения конфликта
*/
async processConflictResolution(
employeeData: EmployeeConflictResolution["employeeData"],
conflictType: EmployeeConflictResolution["conflictType"],
action: EmployeeConflictResolution["action"],
existingEmployeeId: string | undefined,
companyId: string,
sessionId?: string
): Promise<EmployeeProcessingResult> {
try {
console.log("[EMPLOYEE] Processing conflict resolution", {
employeeData,
conflictType,
action,
existingEmployeeId,
companyId,
sessionId,
});
// Публикуем событие начала обработки
this.publishProcessingStart("resolve_conflict", companyId, sessionId);
const apiKey = this.getApiKey(sessionId);
this.apiClient = new EmployeeApiClient(undefined, apiKey);
let result: unknown;
let actionType: string;
switch (action) {
case "create":
// Создаём нового сотрудника
const createDto: CreateEmployeeDTO = {
companyId,
lastName: employeeData.lastName,
firstName: employeeData.firstName,
middleName: employeeData.middleName,
email: employeeData.email,
phones: employeeData.phones,
};
result = await this.apiClient.createEmployeeForce(createDto);
actionType = "создан";
break;
case "update":
// Обновляем существующего сотрудника
if (!existingEmployeeId) {
return {
success: false,
processed: 0,
failed: 1,
duplicates: 0,
errors: ["Не указан ID существующего сотрудника для обновления"],
report: "❌ Ошибка: Не указан ID сотрудника для обновления",
};
}
const updateDto: UpdateEmployeeDTO = {
lastName: employeeData.lastName,
firstName: employeeData.firstName,
middleName: employeeData.middleName,
email: employeeData.email,
phones: employeeData.phones,
};
result = await this.apiClient.updateEmployee(existingEmployeeId, updateDto);
actionType = "обновлён";
break;
case "skip":
return {
success: false,
processed: 0,
failed: 1,
duplicates: 1,
errors: ["Добавление пропущено по решению пользователя"],
report: "⏭️ Добавление сотрудника пропущено",
};
default:
return {
success: false,
processed: 0,
failed: 1,
duplicates: 0,
errors: ["Неизвестное действие для разрешения конфликта"],
report: "❌ Ошибка: Неизвестное действие",
};
}
// Публикуем событие
this.publishEmployeeCreated(
{
companyId,
lastName: employeeData.lastName,
firstName: employeeData.firstName,
middleName: employeeData.middleName,
email: employeeData.email,
phones: employeeData.phones,
},
result
);
return {
success: true,
processed: 1,
failed: 0,
duplicates: 0,
employees: [
{
companyId,
lastName: employeeData.lastName,
firstName: employeeData.firstName,
middleName: employeeData.middleName,
email: employeeData.email,
phones: employeeData.phones,
},
],
report: `✅ Сотрудник ${employeeData.lastName} ${employeeData.firstName} успешно ${actionType}`,
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.publishError(errorMessage, { employeeData, conflictType, action, companyId, sessionId });
return {
success: false,
processed: 0,
failed: 1,
duplicates: 0,
errors: [errorMessage],
report: `❌ Ошибка при разрешении конфликта: ${errorMessage}`,
};
}
}
/**
* Принудительное создание сотрудника
*/
async processForceCreate(
employeeData: EmployeeForceCreate["employeeData"],
companyId: string,
sessionId?: string,
reason?: string
): Promise<EmployeeProcessingResult> {
try {
console.log("[EMPLOYEE] Processing force create", { employeeData, companyId, sessionId, reason });
// Публикуем событие начала обработки
this.publishProcessingStart("force_create", companyId, sessionId);
const apiKey = this.getApiKey(sessionId);
this.apiClient = new EmployeeApiClient(undefined, apiKey);
const createDto: CreateEmployeeDTO = {
companyId,
lastName: employeeData.lastName,
firstName: employeeData.firstName,
middleName: employeeData.middleName,
email: employeeData.email,
phones: employeeData.phones,
};
const result = await this.apiClient.createEmployeeForce(createDto);
// Публикуем событие
this.publishEmployeeCreated(createDto, result);
const reasonText = reason ? ` (причина: ${reason})` : "";
return {
success: true,
processed: 1,
failed: 0,
duplicates: 0,
employees: [createDto],
report: `✅ Сотрудник ${createDto.lastName} ${createDto.firstName} принудительно создан${reasonText}`,
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.publishError(errorMessage, { employeeData, companyId, sessionId, reason });
return {
success: false,
processed: 0,
failed: 1,
duplicates: 0,
errors: [errorMessage],
report: `❌ Ошибка при принудительном создании: ${errorMessage}`,
};
}
}
}
/**
* Регистрация MCP tools для работы с сотрудниками
*/
export const registerEmployeeTools = (server: McpServer) => {
const handler = new EmployeeHandler();
// Tool 1: Добавление одного сотрудника
server.tool(
"employee_add_one",
EmployeeAddOneSchema.shape,
{
title: "Добавить одного сотрудника",
description: "Добавляет одного сотрудника из текстового описания",
},
async (args: unknown) => {
const validatedArgs = EmployeeAddOneSchema.parse(args);
const { rawText, companyId, sessionId } = validatedArgs;
const result = await handler.processAddOne(rawText, companyId, sessionId);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
);
// Tool 2: Добавление списка сотрудников
server.tool(
"employee_add_list",
EmployeeAddListSchema.shape,
{
title: "Добавить список сотрудников",
description: "Добавляет список сотрудников из текста",
},
async (args: unknown) => {
const validatedArgs = EmployeeAddListSchema.parse(args);
const { rawText, companyId, sessionId } = validatedArgs;
const result = await handler.processAddList(rawText, companyId, sessionId);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
);
// Tool 3: Импорт из XLSX
server.tool(
"employee_import_xlsx",
EmployeeImportXlsxSchema.shape,
{
title: "Импорт сотрудников из XLSX",
description: "Импортирует сотрудников из Excel файла",
},
async (args: unknown) => {
const validatedArgs = EmployeeImportXlsxSchema.parse(args);
const { fileUrl, companyId, sessionId } = validatedArgs;
const result = await handler.processImportXlsx(fileUrl, companyId, sessionId);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
);
// Tool 4: Обработка Telegram сообщений
server.tool(
"employee_telegram_handler",
EmployeeTelegramHandlerSchema.shape,
{
title: "Обработка Telegram сообщений",
description: "Обрабатывает команды добавления сотрудников из Telegram",
},
async (args: unknown) => {
const validatedArgs = EmployeeTelegramHandlerSchema.parse(args);
const { message, companyId, userId, sessionId } = validatedArgs;
const result = await handler.processTelegramMessage(message, companyId, userId, sessionId);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
);
// Tool 5: Разрешение конфликтов
server.tool(
"employee_resolve_conflict",
EmployeeConflictResolutionSchema.shape,
{
title: "Разрешение конфликта сотрудника",
description: "Разрешает конфликт при добавлении сотрудника (создать/обновить/пропустить)",
},
async (args: unknown) => {
const validatedArgs = EmployeeConflictResolutionSchema.parse(args);
const { employeeData, conflictType, action, existingEmployeeId, companyId, sessionId } = validatedArgs;
const result = await handler.processConflictResolution(
employeeData,
conflictType,
action,
existingEmployeeId,
companyId,
sessionId
);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
);
// Tool 6: Принудительное создание
server.tool(
"employee_force_create",
EmployeeForceCreateSchema.shape,
{
title: "Принудительное создание сотрудника",
description: "Создаёт сотрудника, игнорируя конфликты",
},
async (args: unknown) => {
const validatedArgs = EmployeeForceCreateSchema.parse(args);
const { employeeData, companyId, sessionId, reason } = validatedArgs;
const result = await handler.processForceCreate(employeeData, companyId, sessionId, reason);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
);
};