/
gkat
/
gitverse-codex-plugin
Обзор
Документация
Войти
/
gkat
/
gitverse-codex-plugin
Код
Запросы
0
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
scripts/local-tools.mjs
130 строк
4 KB
gkat
Register repository listing as standalone MCP server
29 июл 2026, 15:31
29 июл 2026, 15:31
8aee95b
Код
Авторство
О чём код?
const apiBase = "https://api.gitverse.ru"; export const listMyRepositoriesTool = { name: "list_my_repositories", description: "List repositories available to the authenticated GitVerse user, including personal and organization repositories.", annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true, }, inputSchema: { type: "object", properties: { page: { type: "integer", minimum: 1, description: "Page number, starting from 1. Defaults to 1.", }, per_page: { type: "integer", minimum: 1, maximum: 50, description: "Repositories per page. Defaults to 30; maximum 50.", }, }, }, }; function integer(value, fallback, name, maximum) { const result = value ?? fallback; if (!Number.isInteger(result) || result < 1 || result > maximum) { throw new Error(`${name} must be an integer between 1 and ${maximum}`); } return result; } function repositorySummary(repository) { return { full_name: repository.full_name, description: repository.description ?? null, private: Boolean(repository.private), visibility: repository.visibility, archived: Boolean(repository.archived), default_branch: repository.default_branch, language: repository.language ?? null, updated_at: repository.updated_at, html_url: repository.html_url, permissions: repository.permissions, }; } export async function listMyRepositories({ token, args = {}, fetchImpl = fetch, } = {}) { const page = integer(args.page, 1, "page", Number.MAX_SAFE_INTEGER); const perPage = integer(args.per_page, 30, "per_page", 50); const url = new URL("/user/repos", apiBase); url.searchParams.set("page", String(page)); url.searchParams.set("per_page", String(perPage)); const response = await fetchImpl(url, { headers: { Authorization: `Bearer ${token}`, Accept: "application/vnd.gitverse.object+json;version=1", }, }); if (!response.ok) { if (response.status === 401) { throw new Error( "GitVerse REST API returned 401 Unauthorized: the token is missing, invalid, or revoked.", ); } if (response.status === 403) { throw new Error( "GitVerse REST API returned 403 Forbidden for GET /user/repos: the token is valid but lacks permission to list the authenticated user's repositories.", ); } throw new Error( `GitVerse REST API returned HTTP ${response.status} for GET /user/repos.`, ); } const payload = await response.json(); if (!Array.isArray(payload)) { throw new Error("GitVerse REST API returned an unexpected repository list."); } return { repositories: payload.map(repositorySummary), page, per_page: perPage, count: payload.length, has_next_page: /rel="next"/.test(response.headers.get("link") || ""), }; } export async function callLocalTool(message, { token, fetchImpl = fetch }) { if ( message?.method !== "tools/call" || message?.params?.name !== listMyRepositoriesTool.name ) { return undefined; } try { const result = await listMyRepositories({ token, args: message.params.arguments, fetchImpl, }); return { jsonrpc: "2.0", id: message.id, result: { content: [{ type: "text", text: JSON.stringify({ Result: result }) }], }, }; } catch (error) { return { jsonrpc: "2.0", id: message.id, error: { code: -32001, message: error.message }, }; } }