@inso_web/els-mcp
Version:
MCP-сервер поверх INSO Error Logs Service. Read-only tools (search, analytics, fingerprinting, correlations) для подключения Claude Desktop/Code и ChatGPT к логам ошибок. Streamable HTTP transport + stdio для npx-запуска.
91 lines • 3.37 kB
JavaScript
import { z } from 'zod';
import { ToolError } from '../lib/errors.js';
import { encodeCursor, decodeCursor } from '../lib/cursor.js';
import { applyResponseFormat } from '../lib/responseFormat.js';
/**
* Tool: errors_in_session (composite)
*
* Wrapper над search_logs с фиксированным sessionId, sortBy=receivedAt asc.
* Используется когда саппорту дали session-ID — нужно посмотреть всю
* хронологию ошибок этого пользователя.
*
* Cursor — passthrough из search_logs (offset-based transitional wrapper).
*/
export const errorsInSessionInputShape = {
sessionId: z.string().min(1).max(128),
from: z.string().optional(),
to: z.string().optional(),
limit: z.number().int().min(1).max(200).default(20),
cursor: z.string().nullable().optional(),
// eslint-disable-next-line camelcase
response_format: z.enum(['compact', 'full', 'summary']).default('compact'),
};
export const errorsInSessionToolDef = {
name: 'errors_in_session',
title: 'All errors in a user session',
description: 'Return chronologically-ordered errors for a single session/user. Use when support gives you a session-ID and asks "what happened in this session?".',
inputShape: errorsInSessionInputShape,
};
function filtersFromArgs(args) {
return {
sessionId: args.sessionId,
from: args.from,
to: args.to,
};
}
export async function handleErrorsInSession(args, client) {
try {
const filters = filtersFromArgs(args);
let page = 1;
if (args.cursor && args.cursor.length > 0) {
const decoded = decodeCursor(args.cursor, filters);
page = decoded.page ?? 1;
}
const params = {
page,
limit: args.limit,
sortBy: 'receivedAt',
sortOrder: 'asc',
sessionId: args.sessionId,
from: args.from,
to: args.to,
};
const { data, elsRequestId } = await client.searchLogs(params);
const raw = data;
const { items: formattedItems, truncated } = applyResponseFormat(raw.items ?? [], args.response_format);
const hasMore = raw.page * raw.limit < raw.total;
let nextCursor = null;
if (hasMore && raw.items.length > 0) {
const lastItem = raw.items[raw.items.length - 1];
nextCursor = encodeCursor({ receivedAt: lastItem.receivedAt, id: lastItem.id }, filters, raw.page + 1, raw.limit);
}
const meta = {
elsRequestId,
cached: false,
ttlSec: 30,
redactionApplied: false,
truncated,
};
return {
structuredContent: {
sessionId: args.sessionId,
total: raw.total,
items: formattedItems,
nextCursor,
_meta: meta,
},
content: [
{
type: 'text',
text: `Session ${args.sessionId}: ${raw.total} error(s) total, returning ${formattedItems.length}.`,
},
],
};
}
catch (err) {
if (err instanceof ToolError)
return err.toToolResult();
throw err;
}
}
//# sourceMappingURL=errorsInSession.js.map