Добавь HTTP-клиент, каталог моделей и шесть read-only инструментов MCP
Клиент — замороженный объект с семью методами GET, без универсального запроса и тела; денежные поля проверяются на строковый тип (fail-closed). Каталог джойнит /api/models и /v1/models: модальность и рубли из первого, USD и доступность ключу из второго. Инструменты: list_models, pick_model, estimate_cost, get_balance, get_usage, list_api_keys. Точка входа stdio уводит console.log/info в stderr первым импортом. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ade6eb82df
commit
d7d02267e2
18 files changed
+1935
No files matched your search
@@ -0,0 +1,61 @@
|
||||
// Инструмент list_api_keys — только чтение списка ключей аккаунта.
|
||||
|
||||
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import { z } from "zod";
|
||||
import type { ApiKeyEntry, Client } from "../client.js";
|
||||
import { cleanText } from "../sanitize.js";
|
||||
import { READ_ONLY_ANNOTATIONS, guarded, okResult } from "./common.js";
|
||||
|
||||
export const KEYS_TOOL_DESCRIPTION =
|
||||
"Список API-ключей аккаунта openrusrouter.ru: префикс, лимит расхода и его окно, израсходовано, разрешённые модели. " +
|
||||
"Полный ключ не выводится никогда, только префикс; is_current_key отмечает ключ, которым работает этот MCP. " +
|
||||
"allowed_models: null вместе с all_models_allowed: true означает, что ключу доступны все модели. " +
|
||||
"Создать, отозвать или изменить ключ через MCP нельзя — такие действия требуют браузерной сессии на openrusrouter.ru. " +
|
||||
"Это сознательное ограничение безопасности: ключ, которым пользуется MCP, не должен уметь управлять другими ключами или собой";
|
||||
|
||||
export function presentKeys(
|
||||
keys: readonly ApiKeyEntry[],
|
||||
currentApiKey: string,
|
||||
includeRevoked: boolean,
|
||||
): Record<string, unknown>[] {
|
||||
return keys
|
||||
.filter((k) => includeRevoked || k.revoked_at === null)
|
||||
.map((k) => {
|
||||
const allowed = Array.isArray(k.allowed_models) && k.allowed_models.length > 0 ? k.allowed_models : null;
|
||||
return {
|
||||
id: k.id,
|
||||
name: cleanText(typeof k.name === "string" ? k.name : "", 200),
|
||||
key_prefix: k.prefix,
|
||||
created_at: k.created_at,
|
||||
last_used_at: k.last_used_at ?? null,
|
||||
revoked_at: k.revoked_at ?? null,
|
||||
spend_window: k.spend_window === "" ? null : k.spend_window,
|
||||
spend_limit_rub: k.spend_limit_rub,
|
||||
spend_used_rub: k.spend_used,
|
||||
spend_resets_at: k.spend_resets_at ?? null,
|
||||
allowed_models: allowed === null ? null : allowed.map((m) => cleanText(String(m), 200)),
|
||||
all_models_allowed: allowed === null,
|
||||
// Сравнение локальное: сам ключ в результат не попадает.
|
||||
is_current_key: k.prefix !== "" && currentApiKey.startsWith(k.prefix),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function registerKeyTools(server: McpServer, client: Client, currentApiKey: string): void {
|
||||
server.registerTool(
|
||||
"list_api_keys",
|
||||
{
|
||||
title: "API-ключи аккаунта",
|
||||
description: KEYS_TOOL_DESCRIPTION,
|
||||
inputSchema: {
|
||||
include_revoked: z.boolean().default(false),
|
||||
},
|
||||
annotations: READ_ONLY_ANNOTATIONS,
|
||||
},
|
||||
(args) =>
|
||||
guarded(async () => {
|
||||
const { keys } = await client.getKeys();
|
||||
return okResult({ keys: presentKeys(keys, currentApiKey, args.include_revoked) });
|
||||
}),
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user