mcgl-mcp 0.1.0 → 0.1.1
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.
- package/README.md +40 -154
- package/dist/forum/http.js +3 -1
- package/dist/forum/parsers/content.js +4 -1
- package/dist/mcp/tools/public/read.js +11 -5
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -1,196 +1,82 @@
|
|
|
1
1
|
# mcgl-mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/mcgl-mcp)
|
|
4
|
+
[](https://github.com/exi66/mcgl-mcp/actions/workflows/ci.yml)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
MCP-сервер для [форума Minecraft Galaxy](https://forum.minecraft-galaxy.ru/main/). Подключается к Claude, Cursor и другим AI-ассистентам и даёт им читать форум: разделы, темы, посты, опросы.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Все инструменты публичные: авторизация не нужна, работают в обоих режимах. В локальном режиме с кредами страницы читаются залогиненным.
|
|
10
|
-
|
|
11
|
-
| Инструмент | Страница | Что возвращает |
|
|
12
|
-
|---|---|---|
|
|
13
|
-
| `forum_list_sections` | `/main/` | разделы 1..11 по категориям: описание, число тем и ответов, последнее сообщение |
|
|
14
|
-
| `forum_latest_topics` | `/forum/0/{page}` | последние темы всех разделов, у каждой указан раздел |
|
|
15
|
-
| `forum_list_topics` | `/forum/{id}/{page}` | темы раздела: закреплена, закрыта (зачёркнута), кто может отвечать (`all`/`vip`/`premium_platinum`/`moderators`), автор, ответы, число страниц, последнее сообщение |
|
|
16
|
-
| `forum_get_topic` | `/topic/{id}/{page}` | без `page` — последние 15 постов темы; с `page` — страницы по 15 от начала (`pageSize` 1..50). Всего постов и страниц; в режиме «последние» и на 1-й странице ещё опрос, системные сообщения («перемещено из…») и кто читает сейчас |
|
|
17
|
-
| `forum_get_post` | `/post/{id}` | один пост с номером в теме, его тема и страница `forum_get_topic`, на которой он находится |
|
|
18
|
-
|
|
19
|
-
В инструментах страницы нумеруются с 1, в URL форума — с 0.
|
|
20
|
-
|
|
21
|
-
**Пагинация тем.** Форум показывает на странице столько постов, сколько задано в настройках игрока (у гостя и игроков бывает 15…100). Агенту сто постов за раз дорого, поэтому `forum_get_topic` отдаёт свои страницы фиксированного размера поверх пагинации форума. Номера постов `#N` в теме сквозные, удалённые тоже нумеруются, так что страница `v` — это посты `#(v-1)*15+1 … #v*15`. Без `page` отдаются последние 15 постов: в больших темах обычно нужен конец. Если тема недавно встречалась в списке тем, там уже было число её страниц, и `ForumReader` открывает последнюю страницу форума одним запросом. Если тема с тех пор выросла на новую страницу, это видно по пагинации, и reader идёт обычным путём. Размер страницы форума `ForumReader` узнаёт по первой (или последней) странице темы и перепроверяет по номерам: если игрок поменял настройку, пересчитывает. Разобранные страницы форума кешируются на 60 секунд, чтобы листание темы не скачивало их заново.
|
|
22
|
-
|
|
23
|
-
**Недоступность форума.** GET повторяется до 3 раз при сетевой ошибке, таймауте или 5xx, с паузами 1 и 2 секунды. POST не повторяется: повтор может создать дубль поста. После 2 подряд неудачных запросов срабатывает circuit breaker (`src/forum/health.ts`): 30 секунд запросы на форум не идут, инструменты сразу отвечают «форум не отвечает, повтори через N секунд». Затем проходит один пробный запрос. Состояние видно в `/health` HTTP-режима.
|
|
24
|
-
|
|
25
|
-
Пост содержит номер, id, автора (ник, id, группа, статус, клан со званием, число ответов), дату, текст и рейтинг. Удалённый пост возвращается как `{deleted: true, number, id}`. Если аккаунт автора удалён или заблокирован (на форуме это `player$0`), у автора стоит `accountDeleted: true`.
|
|
26
|
-
|
|
27
|
-
Текст поста получается из отрендеренного bbCode ([справка](https://forum.minecraft-galaxy.ru/wiki/201)):
|
|
28
|
-
|
|
29
|
-
- `[quote: ник] … [/quote]`, `[spoiler: заголовок] … [/spoiler]`;
|
|
30
|
-
- `[image: url]`, `[video: url]`, `[audio: url]`;
|
|
31
|
-
- предметы — `[Золотой блок #41]`, с метадатой — `[Красная шерсть #35:14]`;
|
|
32
|
-
- окна крафта, печи и наковальни — `[craft]`: сетка id предметов (`-` — пустой слот, `->` — результат, `x4` — количество) и легенда `124 = Ажурный камень`;
|
|
33
|
-
- таблицы — ячейками через `|`, списки — с `- `;
|
|
34
|
-
- ссылки — как `текст (url)`, редиректы форума раскрываются.
|
|
35
|
-
|
|
36
|
-
Названия предметов берутся из эндпоинта подсказок форума `/object/{id}/2?d={meta}` (`src/forum/objects.ts`). Кеш живёт весь процесс. За один вызов инструмента делается не больше 25 новых запросов, остальные предметы остаются как `[item #id]`. Тот же эндпоинт отдаёт карточку игрока (тип 1) и сообщества (тип 3).
|
|
37
|
-
|
|
38
|
-
### Экономия токенов
|
|
8
|
+
Что можно попросить у ассистента:
|
|
39
9
|
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
10
|
+
- «Что нового на форуме MCGL?»
|
|
11
|
+
- «Перескажи последние сообщения в теме про Гран-при амфибий»
|
|
12
|
+
- «О чём спорят в разделе Предложения?»
|
|
13
|
+
- «Найди в теме 133931 рецепт крафта и объясни его»
|
|
43
14
|
|
|
44
|
-
|
|
15
|
+
## Установка
|
|
45
16
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
| Режим | Транспорт | Инструменты | Авторизация |
|
|
49
|
-
|---|---|---|---|
|
|
50
|
-
| Локальный | stdio | публичные + от лица игрока | логин и пароль игрока в env клиента |
|
|
51
|
-
| Хост | Streamable HTTP, `POST /public/mcp` | только публичные | нет (пока) |
|
|
52
|
-
|
|
53
|
-
## Авторизация на форуме
|
|
54
|
-
|
|
55
|
-
1. `GET /guilogin/`: из формы `form[action="/login.php"]` берутся скрытые поля `form`, `ref`, `fcode`, `recap`.
|
|
56
|
-
2. `POST /login.php` с этими полями плюс `login` и `pass`.
|
|
57
|
-
- Успех: 302 на `/main/`, в шапке появляется ник (`a.button[href="/profilemain/"] .text`).
|
|
58
|
-
- Неудача: 302 на `/guilogin/4?f=N` с плашкой `table.alert td.caption` («Введенный логин или пароль неверны! Попыток входа осталось: N…»).
|
|
59
|
-
3. Гостевая страница определяется по кнопке «Войти» в шапке (`a.button[href="/guilogin/"]`). Форма ответа в теме видна и гостю, поэтому по ней судить о логине нельзя.
|
|
60
|
-
|
|
61
|
-
Особенности форума:
|
|
62
|
-
|
|
63
|
-
- **`fcode` обязателен.** Без него вход отклоняется и списывается попытка.
|
|
64
|
-
- **reCAPTCHA v3 сервер не проверяет**, `recap` уходит пустым.
|
|
65
|
-
- **У входов есть лимит попыток.** Поэтому после неудачного логина `PlayerSession` больше не пробует войти, пока процесс не перезапустят.
|
|
66
|
-
- **Протухшая сессия переустанавливается сама.** Если страница пришла гостевой, бросается `SessionExpiredError`, сессия перелогинивается и повторяет операцию один раз.
|
|
67
|
-
|
|
68
|
-
Проверка без MCP (креды из `.env`): `pnpm check:login`. Скрипт логинится, повторно использует сессию и имитирует её протухание. Каждый запуск тратит 2 входа.
|
|
69
|
-
|
|
70
|
-
## Локальный режим
|
|
71
|
-
|
|
72
|
-
Нужен Node.js 22+. Ставить отдельно ничего не надо: клиент сам запустит пакет через `npx`.
|
|
17
|
+
Нужен [Node.js](https://nodejs.org) 22 или новее. Отдельно ставить ничего не надо: клиент сам скачает и запустит сервер через `npx`.
|
|
73
18
|
|
|
74
19
|
**Claude Code:**
|
|
75
20
|
|
|
76
21
|
```bash
|
|
77
|
-
claude mcp add mcgl
|
|
22
|
+
claude mcp add mcgl -- npx -y mcgl-mcp
|
|
78
23
|
```
|
|
79
24
|
|
|
80
|
-
**Claude Desktop, Cursor и
|
|
25
|
+
**Claude Desktop, Cursor и другие:** добавьте в конфиг MCP-серверов (`claude_desktop_config.json`, `.cursor/mcp.json` и т.п.):
|
|
81
26
|
|
|
82
27
|
```json
|
|
83
28
|
{
|
|
84
29
|
"mcpServers": {
|
|
85
30
|
"mcgl": {
|
|
86
31
|
"command": "npx",
|
|
87
|
-
"args": ["-y", "mcgl-mcp"]
|
|
88
|
-
"env": {
|
|
89
|
-
"FORUM_LOGIN": "ник",
|
|
90
|
-
"FORUM_PASSWORD": "пароль"
|
|
91
|
-
}
|
|
32
|
+
"args": ["-y", "mcgl-mcp"]
|
|
92
33
|
}
|
|
93
34
|
}
|
|
94
35
|
}
|
|
95
36
|
```
|
|
96
37
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
Если на Windows клиент не находит `npx`, укажи `"command": "cmd"` и `"args": ["/c", "npx", "-y", "mcgl-mcp"]`.
|
|
38
|
+
На Windows, если клиент не находит `npx`: `"command": "cmd"`, `"args": ["/c", "npx", "-y", "mcgl-mcp"]`.
|
|
100
39
|
|
|
101
|
-
|
|
40
|
+
### Вход под своим аккаунтом (необязательно)
|
|
102
41
|
|
|
103
|
-
|
|
42
|
+
Сейчас все инструменты работают и без входа. Вход понадобится для действий от вашего имени, например чтобы писать посты: это в планах. Настроить его можно уже сейчас.
|
|
104
43
|
|
|
105
44
|
```bash
|
|
106
|
-
|
|
107
|
-
pnpm build
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
В конфиге клиента: `"command": "node"`, `"args": ["<путь>/mcgl-mcp/dist/entry/stdio.js"]`.
|
|
111
|
-
|
|
112
|
-
## Публикация в npm
|
|
113
|
-
|
|
114
|
-
Публикует GitHub Actions ([.github/workflows/publish.yml](.github/workflows/publish.yml)) по тегу:
|
|
115
|
-
|
|
116
|
-
```bash
|
|
117
|
-
git tag v0.1.0
|
|
118
|
-
git push origin v0.1.0
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
- **Версия берётся из тега.** В `package.json` в репозитории лежит заглушка `0.0.0-dev`, менять её руками не нужно.
|
|
122
|
-
- **Пререлизы** (`v1.0.0-beta.1`) публикуются с dist-tag `next`, поэтому `npx mcgl-mcp` продолжает ставить последнюю стабильную версию.
|
|
123
|
-
- **Тег, который не является semver,** останавливает workflow с ошибкой.
|
|
124
|
-
- **Пакет подписывается `--provenance`:** npm показывает, что он собран этим workflow из этого репозитория.
|
|
125
|
-
|
|
126
|
-
Один раз нужно добавить секрет `NPM_TOKEN` в Settings → Secrets and variables → Actions. Токен создаётся на npmjs.com в разделе Access Tokens → Generate New Token → Granular, с правом публикации.
|
|
127
|
-
|
|
128
|
-
Что попадёт в пакет: `npm pack --dry-run`. Идут только `dist/**/*.js` (без `dist/dev`), README и LICENSE.
|
|
129
|
-
|
|
130
|
-
## Хост
|
|
131
|
-
|
|
132
|
-
```bash
|
|
133
|
-
cp .env.example .env
|
|
134
|
-
docker compose up -d --build
|
|
45
|
+
claude mcp add mcgl -e FORUM_LOGIN=ник -e FORUM_PASSWORD=пароль -- npx -y mcgl-mcp
|
|
135
46
|
```
|
|
136
47
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
## Устройство
|
|
48
|
+
В JSON-конфиге то же самое задаётся через `"env": { "FORUM_LOGIN": "ник", "FORUM_PASSWORD": "пароль" }`.
|
|
140
49
|
|
|
141
|
-
|
|
142
|
-
src/
|
|
143
|
-
entry/stdio.ts локальный режим
|
|
144
|
-
entry/http.ts публичный режим
|
|
145
|
-
dev/check-login.ts ручная проверка авторизации
|
|
146
|
-
env.ts конфиг из переменных окружения
|
|
147
|
-
forum/
|
|
148
|
-
site.ts URL форума
|
|
149
|
-
http.ts fetch + куки + ручные редиректы + повторы GET
|
|
150
|
-
health.ts circuit breaker: форум лежит → не ходим 30 с
|
|
151
|
-
cookie-jar.ts
|
|
152
|
-
rate-limiter.ts минимальный интервал между запросами (общий на процесс)
|
|
153
|
-
player-session.ts логин, проверка сессии, перелогин
|
|
154
|
-
reader.ts чтение форума (гостем или через сессию игрока)
|
|
155
|
-
types.ts формы данных, которые отдают инструменты
|
|
156
|
-
parsers/
|
|
157
|
-
common.ts общие хелперы, признаки гостя, пользователи, пагинация
|
|
158
|
-
topic-list.ts главная, /forum/0, /forum/N (и разделы сообществ)
|
|
159
|
-
topic.ts тема и отдельный пост: посты, опрос, шапка
|
|
160
|
-
content.ts HTML поста (bbCode) → текст
|
|
161
|
-
player.ts форма логина
|
|
162
|
-
mcp/
|
|
163
|
-
define-tool.ts definePublicTool / definePlayerTool
|
|
164
|
-
tools/index.ts реестр инструментов
|
|
165
|
-
tools/public/read.ts инструменты чтения
|
|
166
|
-
server.ts сборка McpServer
|
|
167
|
-
```
|
|
50
|
+
Пароль хранится только в конфиге клиента на вашем компьютере и отправляется только на форум. Если войти не получилось, текст ошибки форума виден в логах MCP-сервера в клиенте. У форума есть лимит попыток входа, поэтому после неудачи сервер больше не пытается войти: исправьте пароль и перезапустите клиент.
|
|
168
51
|
|
|
169
|
-
|
|
52
|
+
## Инструменты
|
|
170
53
|
|
|
171
|
-
|
|
54
|
+
| Инструмент | Что делает |
|
|
55
|
+
| --------------------- | ------------------------------------------------------------------------------------ |
|
|
56
|
+
| `forum_list_sections` | разделы форума по категориям |
|
|
57
|
+
| `forum_latest_topics` | темы со свежими сообщениями во всех разделах |
|
|
58
|
+
| `forum_list_topics` | темы раздела: закреплённые, закрытые, кто может отвечать |
|
|
59
|
+
| `forum_get_topic` | посты темы по 15 штук; по умолчанию — последние, плюс опрос и кто читает тему сейчас |
|
|
60
|
+
| `forum_get_post` | один пост по ссылке `/post/N` и где он находится в теме |
|
|
172
61
|
|
|
173
|
-
|
|
174
|
-
pnpm test # все тесты, без сети (их же гоняет CI)
|
|
175
|
-
pnpm test:watch
|
|
176
|
-
pnpm test:live # smoke-тесты на настоящем форуме, креды игрока из .env необязательны
|
|
177
|
-
pnpm fixtures:update # перекачать страницы форума в test/fixtures
|
|
178
|
-
```
|
|
62
|
+
## Настройки
|
|
179
63
|
|
|
180
|
-
|
|
181
|
-
|---|---|
|
|
182
|
-
| `test/parsers/` | конвертер bbCode → текст на коротких HTML-фрагментах; парсеры страниц на фикстурах |
|
|
183
|
-
| `test/forum/` | куки, редиректы, cp1251, лимитер; логин, перелогин, блокировка после неверного пароля; названия предметов (кеш, лимит запросов) |
|
|
184
|
-
| `test/mcp/` | MCP целиком: клиент SDK ↔ сервер (`InMemoryTransport`) ↔ фейковый форум |
|
|
185
|
-
| `test/live/` | только `pnpm test:live`: живая разметка всё ещё парсится |
|
|
64
|
+
Переменные окружения задаются в конфиге клиента, все необязательные.
|
|
186
65
|
|
|
187
|
-
|
|
66
|
+
| Переменная | По умолчанию | Что это |
|
|
67
|
+
| ------------------------------- | ----------------------------------- | ------------------------------------------ |
|
|
68
|
+
| `FORUM_LOGIN`, `FORUM_PASSWORD` | — | вход под своим аккаунтом |
|
|
69
|
+
| `LOG_LEVEL` | `info` | `debug` — подробные логи для отладки |
|
|
70
|
+
| `FORUM_MIN_INTERVAL_MS` | `300` | минимальная пауза между запросами к форуму |
|
|
71
|
+
| `FORUM_TIMEOUT_MS` | `15000` | таймаут одного запроса к форуму |
|
|
72
|
+
| `FORUM_BASE_URL` | `https://forum.minecraft-galaxy.ru` | адрес форума |
|
|
188
73
|
|
|
189
|
-
|
|
74
|
+
## Документация
|
|
190
75
|
|
|
191
|
-
|
|
76
|
+
- [Как сервер работает с форумом](docs/forum.md): вход, пагинация тем, bbCode, предметы, недоступность форума.
|
|
77
|
+
- [Разработка](docs/development.md): запуск из исходников, тесты, релизы, свой HTTP-сервер.
|
|
192
78
|
|
|
193
|
-
|
|
79
|
+
Нашли ошибку или форум что-то поменял — [заведите issue](https://github.com/exi66/mcgl-mcp/issues/new/choose).
|
|
194
80
|
|
|
195
81
|
## Лицензия
|
|
196
82
|
|
package/dist/forum/http.js
CHANGED
|
@@ -119,7 +119,9 @@ export class ForumHttp {
|
|
|
119
119
|
}
|
|
120
120
|
catch (error) {
|
|
121
121
|
const timeout = error instanceof DOMException && error.name === "TimeoutError";
|
|
122
|
-
throw new RetryableError(timeout ? `Forum timed out: ${url}` : `Request to forum failed: ${url}`, {
|
|
122
|
+
throw new RetryableError(timeout ? `Forum timed out: ${url}` : `Request to forum failed: ${url}`, {
|
|
123
|
+
cause: error,
|
|
124
|
+
});
|
|
123
125
|
}
|
|
124
126
|
}
|
|
125
127
|
}
|
|
@@ -24,7 +24,10 @@ export function postHtmlToText($, el) {
|
|
|
24
24
|
node.find(".bc_quote").each((_, block) => {
|
|
25
25
|
const b = $(block);
|
|
26
26
|
const title = b.children(".bc_quote_title").first();
|
|
27
|
-
const who = title
|
|
27
|
+
const who = title
|
|
28
|
+
.text()
|
|
29
|
+
.replace(/\s*писал\(а\):\s*$/, "")
|
|
30
|
+
.trim();
|
|
28
31
|
title.remove();
|
|
29
32
|
b.prepend(`\n[quote${who ? `: ${who}` : ""}]\n`).append("\n[/quote]\n");
|
|
30
33
|
b.replaceWith(b.contents());
|
|
@@ -9,7 +9,8 @@ const FLAGS_NOTE = " Boolean flags appear only when true.";
|
|
|
9
9
|
const page = z.number().int().positive().default(1).describe("1-based page number");
|
|
10
10
|
const TOPIC_ROW_FIELDS = "Topic fields: pinned; closed (title struck through on the forum, no replies allowed); " +
|
|
11
11
|
"whoCanReply: all | vip | premium_platinum | moderators (only visible in topic lists, not on the topic page); " +
|
|
12
|
-
"pages (topic page count); created/last: author and time." +
|
|
12
|
+
"pages (topic page count); created/last: author and time." +
|
|
13
|
+
FLAGS_NOTE;
|
|
13
14
|
export const listSections = definePublicTool({
|
|
14
15
|
name: "forum_list_sections",
|
|
15
16
|
title: "Forum sections",
|
|
@@ -22,8 +23,7 @@ export const listSections = definePublicTool({
|
|
|
22
23
|
export const latestTopics = definePublicTool({
|
|
23
24
|
name: "forum_latest_topics",
|
|
24
25
|
title: "Latest topics",
|
|
25
|
-
description: "Recently active topics across all sections, newest first; each row includes its section name. " +
|
|
26
|
-
TOPIC_ROW_FIELDS,
|
|
26
|
+
description: "Recently active topics across all sections, newest first; each row includes its section name. " + TOPIC_ROW_FIELDS,
|
|
27
27
|
inputSchema: { page },
|
|
28
28
|
annotations: readOnly,
|
|
29
29
|
handler: async ({ page }, { reader }) => jsonResult(await reader.latestTopics(page)),
|
|
@@ -50,10 +50,16 @@ export const getTopic = definePublicTool({
|
|
|
50
50
|
"page 1 also include poll (if any), notices (e.g. 'moved from section') and current readers. " +
|
|
51
51
|
"author.accountDeleted: account removed or banned. " +
|
|
52
52
|
"Post text markup: [quote: nick]..[/quote], [spoiler: title]..[/spoiler], [image: url], [video: url], " +
|
|
53
|
-
"[audio: url], [Item name #id] or [item #id] (#id:meta for variants), [craft] grid of item ids with a legend." +
|
|
53
|
+
"[audio: url], [Item name #id] or [item #id] (#id:meta for variants), [craft] grid of item ids with a legend." +
|
|
54
|
+
FLAGS_NOTE,
|
|
54
55
|
inputSchema: {
|
|
55
56
|
topicId: z.number().int().positive(),
|
|
56
|
-
page: z
|
|
57
|
+
page: z
|
|
58
|
+
.number()
|
|
59
|
+
.int()
|
|
60
|
+
.positive()
|
|
61
|
+
.optional()
|
|
62
|
+
.describe("1-based page from the topic start; omit for the newest posts"),
|
|
57
63
|
pageSize: z.number().int().min(1).max(50).default(DEFAULT_PAGE_SIZE).describe("Posts per page"),
|
|
58
64
|
},
|
|
59
65
|
annotations: readOnly,
|
package/package.json
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcgl-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "MCP server for the Minecraft Galaxy (MCGL) game forum: read sections, topics and posts.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
7
7
|
"model-context-protocol",
|
|
8
|
-
"minecraft-galaxy",
|
|
9
|
-
"mcgl",
|
|
10
8
|
"forum",
|
|
11
9
|
"claude"
|
|
12
10
|
],
|
|
@@ -48,7 +46,9 @@
|
|
|
48
46
|
"test": "vitest run",
|
|
49
47
|
"test:watch": "vitest",
|
|
50
48
|
"test:live": "vitest run --config vitest.live.config.ts",
|
|
51
|
-
"fixtures:update": "tsx --env-file-if-exists=.env scripts/update-fixtures.ts"
|
|
49
|
+
"fixtures:update": "tsx --env-file-if-exists=.env scripts/update-fixtures.ts",
|
|
50
|
+
"format": "prettier --write .",
|
|
51
|
+
"format:check": "prettier --check ."
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"@modelcontextprotocol/sdk": "^1.32.2",
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
"@modelcontextprotocol/inspector": "^2.15.0",
|
|
61
61
|
"@types/express": "^5.0.6",
|
|
62
62
|
"@types/node": "^26.6.6",
|
|
63
|
+
"prettier": "^3.10.1",
|
|
63
64
|
"tsx": "^4.23.15",
|
|
64
65
|
"typescript": "^7.0.2",
|
|
65
66
|
"vitest": "^5.1.0"
|