Skip to main content
Glama
Tlkh201313
by Tlkh201313

Basketed

Одна корзина. Много магазинов. Ничего не куплено без вас.

Универсальный MCP-сервер для покупок плюс самохостинговая панель управления. Любой MCP-агент получает реальный поиск товаров по многим ритейлерам, экономичное по токенам сравнение и шаг покупки, который может авторизовать только человек.

ANALYSE → EXTRACT → RESPOND → PURCHASE → RECEIVE

pnpm i && pnpm build
node packages/cli/bin.js install --client claude-code   # or --all

Это вся настройка. Ваш клиент сам запускает Basketed через stdio, а панель управления поднимается в том же процессе — её ссылка и ссылка на любую корзину, ожидающую вас, печатаются в консоли сервера. serve --http --open — другой способ входа: та же панель плюс конечная точка Streamable HTTP на /mcp.


Что здесь на самом деле

Межритейлерная корзина за обязательным шлюзом одобрения человеком

Никто этого не выпускал. Официальные серверы мерчантов — это один ритейлер и остановка на URL оформления заказа; комьюнити-серверы для покупок автоматизируют покупки без какого-либо одобрения. Середина была пуста.

Всё работает на вашей машине

Нет сервера Basketed, который можно было бы взломать. После утечки Composio в мае 2026 года — ~5 241 API-ключ и ~5 001 OAuth-токен, украденные из хранилища с ~1,7 млн живых учётных данных, — хостинговый агент для покупок является целью по построению. Хранилище учётных данных запечатано с помощью AES-256-GCM под ключом, который никогда не покидает эту машину, и модель не может его прочитать — нет ни инструмента, ни маршрута, возвращающего секрет. Ни один из поставляемых адаптеров пока никого не аутентифицирует, поэтому сегодня хранилище пусто, если вы сами что-то в него не положите со страницы Connect stores.

Опубликованный бенчмарк токенов для e-commerce MCP

на 91,9% меньше токенов, чем у наивного MCP-сервера, на 99,3% меньше, чем при просмотре витрин, на одной реальной задаче покупки — и эта цифра включает наши собственные 3 144 токена накладных расходов на определение инструментов. Метод — в docs/BENCHMARK.md.


Related MCP server: agent-commerce-mcp-server

Шлюз покупки

Агент может предложить покупку. Авторизовать её может только человек, и авторизация всегда поступает с поверхности, которую модель не может авторизовать.

cart_prepare ──► PENDING ──(human)──► APPROVED ──(purchase_confirm)──► order
                    │                                                    │
                    └──► EXPIRED (5 min) / REJECTED                       └─► HANDED_OFF
                                                                              outcome: unknown

Два канала одобрения, оба сходятся в одной функции, поэтому свойства безопасности идентичны независимо от того, где нажал человек:

Как

Работает на

A — панель

/approvals, открывается по ссылке-токену из консоли сервера; детализировано, введите точную сумму

всегда — панель работает и на stdio

C — код в консоли

6-значный код, печатаемый в собственном stderr сервера

100% клиентов

Канал B — элиситация, когда клиент сам отображает диалог, спроектирован и не реализован. ApprovalChannel — это "console" | "panel", и это весь список. Буква сохранена, чтобы план и код использовали одни и те же имена для одних и тех же вещей.

Канал C безопасен, потому что у модели нет доступа на чтение к этой поверхности. Единственный способ, которым агент получает код, — это когда человек зачитывает его вслух — что и есть то самое человеческое действие, которое мы хотим требовать.

Канал A опирается на тот же факт, а не на разделение маршрутов. У каждого клиента, в который устанавливается Basketed, есть оболочка, поэтому утверждение «агент говорит по MCP и не может дотянуться до /api» никогда не было истинным само по себе — локальный процесс может обратиться к любому порту на 127.0.0.1 и подделать любой заголовок. Поэтому панель находится за токеном, выпускаемым на процесс и печатаемым в той же консоли — на обоих транспортах, так что клиент, запустивший Basketed через stdio, всё равно имеет канал A.

На stdio панель также открывается в вашем браузере при запуске сервера. Это не удобство: клиент перехватывает stderr своего MCP-сервера, поэтому ссылка записывается туда, где её никогда не прочитает человек, а панель, до которой никто не может добраться, — это не канал. Вкладка и есть канал. Одна вкладка на процесс; --no-open (или BASKETED_NO_OPEN=1) отключает её, а вкладка опрашивает, так что корзина, которая понадобится вам позже, появится в уже открытой вкладке. /api также отклоняет любой запрос, чей Origin не совпадает точно с панелью, и отклоняет изменяющий запрос без него, что и не даёт веб-странице управлять панелью через ваш браузер.

Чего не существует намеренно: инструмента approve(), параметра approved: true, флага переопределения или инструмента set_delivery_address. Запустите tools/list и проверьте. Отсутствие — это и есть фича.

Адверсариальный проход

pnpm smoke        # five smoke suites, all offline
pnpm test         # 160 unit tests
pnpm drill        # the whole demo path with the network genuinely severed
pnpm smoke:live   # ...and against live merchants, spending real requests
  1. purchase_confirm перед одобрением → отказано

  2. одобрить, подтвердить, затем воспроизвести тот же approval_id → отказано, израсходован

  3. изменить цену в БД после одобрения → отказано, расхождение хэша

  4. перезапустить с --fast-mode и повторить 1–3 → всё ещё отказано

  5. попросить агента одобрить собственную покупку → нет инструмента, который может это сделать

--fast-mode не может коснуться покупки, и это доказано дважды

Поведенчески и путём обхода реального графа импортов: ничто, достижимое из commerce/purchase.ts, не импортирует mcp/policy.ts, где живёт флаг. Флаг не игнорируется на пути покупки — он недостижим из него, а рефакторинг, связывающий их, проваливает CI.

Офлайн-учение действительно режет сеть

pnpm drill предзагружает защиту, которая отклоняет все не-loopback соединения в процессе сервера, а затем проходит весь демонстрационный путь. Установка флага снапшота на машине, где всё ещё есть Wi-Fi, доказывает лишь то, что флаг парсится. При реальном отключении семь из десяти закреплённых магазинов Shopify гаснут — и возвращаются по имени в stores_failed, потому что поиск, который молча возвращает меньше магазинов, выглядит точно как успех.


Откуда берутся данные

Каждый адаптер объявляет две независимые вещи, и ни одна не может быть преувеличена. Каждый ответ несёт свой режим, и панель показывает его как бейдж.

Режим

Значение

native

собственный официальный endpoint ритейлера — Shopify UCP и (S16) настоящий Tesco: search.api.tesco.com / xapi.tesco.com, те же запросы, которые делает собственный фронтенд tesco.com, портированные и проверенные вживую, а не соскобленные

provider

реальные данные ритейлера через лицензированного коммерческого провайдера (спроектировано, не реализовано)

connected

собственная учётная запись пользователя через реальный OAuth ритейлера (спроектировано, не реализовано)

simulated

на основе фикстур и с пометкой SIMULATED

Уровень

У кого есть

discovery detail

каждый адаптер, плюс настоящий Tesco

cart

Shopify UCP, симуляция и настоящий Tesco (через bearer-токен, вставленный из собственной сессии tesco.com покупателя — см. Connect stores)

handoff

Shopify UCP, настоящий Tesco (tesco.com/groceries/.../trolley, реальная корзина)

checkout

никто. Shopify закрывает завершение оплаты за вручную выданным мерчант-токеном без публичного приложения; корзинный API Tesco неофициальный, и этот проект в любом случае не касается данных карт. Интерфейс определён, не реализован.

Настоящий Tesco — это не лицензированная интеграция. search.api.tesco.com и xapi.tesco.com — это публичные endpoints, которые вызывает собственный сайт Tesco, с API-ключом, который публичен и встроен в их фронтенд-JS, — но Tesco не документирует и не поддерживает стороннее использование ни одного из них, и такое использование выходит за рамки Условий предоставления услуг Tesco, как и любой неофициальный API-клиент. sim:tesco это не касается, и он остаётся ровно тем, чем был всегда: фикстурные данные, на которых по-прежнему работает офлайн-учение, по-прежнему без реальной сети.

Никакого скрейпинга и никакого обхода анти-бот защиты. Cloudflare-челленджи, WAF-фingerprinting и CAPTCHA — это средства контроля доступа, которые оператор сознательно включил. Их обход разрушает защиту «пользователь — действующее лицо», которая делает агентные покупки вообще защитимыми, и это постоянно ломается. Ритейлеры за таким — это provider или simulated. Это не возможность, которую мы поставляем отключённой, — это то, что мы не строим.

HANDED_OFF никогда не заявляет об успехе. Когда маршрут заканчивается URL-адресом, который человек заполняет сам, мы действительно не знаем результата, и заказ говорит именно об этом, пока человек не отметит его в панели. Тихо показывать зеленую галочку для заказа, за который никто не заплатил, было бы самой разрушительной ошибкой, которую этот продукт мог бы выпустить.


Инструменты

Только чтение:

basket_list_stores

каждая строка содержит mode и status

basket_search_products

response_format · fields · budget_tokens · max_results

basket_get_product_detail

тяжелые поля только через include

basket_get_token_report

выданные токены по сравнению с базовым уровнем, накопительно

Денежные — destructiveHint: true, никогда не повышаются до ALLOW:

basket_cart_prepare

создает реальную корзину, выпускает мандат корзины, возвращает approval_id. charged: false.

basket_purchase_confirm

успешно выполняется только при наличии одобренного человеком, не истекшего, не использованного мандата с совпадающим хэшем

basket_list_orders · basket_get_order_status

чтение; cart_json и approval_id удаляются

Рычаги токенов

response_format: concise (по умолчанию) · detailed · compact (короткие ключи плюс однострочная легенда). fields для явного разрешенного списка. budget_tokens для жесткого предела — обрезка url → image → attrs → усечение имени → удаление строк, при этом _meta.truncated указывает, что было удалено. id, price и mode никогда не удаляются: результат не должен терять свое происхождение ради экономии токенов.


Установка

basketed install управляется одной таблицей вариантов, той же, из которой панель отображает — поэтому установщик, текстовые блоки и значки не могут расходиться в том, где находится файл конфигурации. Это важно, потому что почти каждое исключение ниже терпит неудачу молча: неправильное имя ключа не вызывает ошибку, ваш сервер просто никогда не появляется.

basketed clients                       # every client, its file, its key
basketed install --client claude-code
basketed install --all --dry-run       # show the diff, write nothing
basketed doctor                        # check the install end to end

Клиент

Ключ

Что молча сломает его

Claude Code

mcpServers

url без type — жесткая ошибка

Cursor

mcpServers

поддерживает элиситацию, где мог бы жить канал B — он не построен

Codex CLI

[mcp_servers.x]

единственная цель TOML, с подчеркиванием

Claude Desktop

mcpServers

удаленно только через Настройки → Подключения

VS Code

servers

не mcpServers

opencode

mcp

command — это массив; ключ env — environment

Kiro

mcpServers

имеет autoApprove — мы перечисляем там только инструменты для чтения

Zed

context_servers

—

Windsurf

mcpServers

serverUrl; жесткий лимит в 100 инструментов на всех серверах

Gemini CLI

mcpServers

httpUrl для Streamable HTTP, url означает SSE

Goose

extensions

uri, а не url; streamable_http с подчеркиванием

Warp

mcpServers

также читает ~/.claude.json и ~/.codex/config.toml — бесплатно

Записи выполняются как слияние-затем-замена, никогда не перезаписываются. Существующий файл сохраняется в <file>.basketed-backup-<timestamp>, несвязанные ключи и другие серверы сохраняются, запись атомарна, и выводится diff. Конфиг, который мы не можем разобрать, отклоняется и остается байт-в-байт идентичным, а не заменяется.

autoApprove в Kiro — ловушка. Наш сгенерированный конфиг перечисляет там только четыре инструмента для чтения, и basketed doctor предупреждает, если денежный инструмент был добавлен вручную.


Протокол

Двухэпохальный, оба транспорта. MCP 2026-07-28 удалил initialize, сессии и запросы, инициируемые сервером; современный клиент не может общаться с устаревшим сервером и наоборот. «Устанавливается в любого агента» полностью основано на обслуживании обоих из одного бинарника, поэтому scripts/smoke-mcp.mjs открывает один и тот же бинарник дважды — один раз с initialize, один раз без состояния — потому что ни одна из ошибок не видна из собственных логов сервера.

Также: server/discover, outputSchema для каждого инструмента, структурированный вывод, дублируемый текстом для старых клиентов, детерминированный порядок инструментов, все четыре аннотации, пространства имен.


Безопасность

  • Хранилище учетных данных построено. packages/vault запечатывает каждый сохраненный секрет с помощью AES-256-GCM под 32-байтным ключом в ~/.basketed/master.key (режим 0600). Есть ровно одна функция, возвращающая открытый текст — reveal() — и она вызывается только из перехватчика запросов, который добавляет заголовок к исходящему fetch; тест проходит по рабочей области в поисках других мест вызова и завершается ошибкой, если такое появляется. Панель — за тем же токеном процесса, что и все остальное в этом списке — пишет в него и читает только метаданные. Ни один MCP-инструмент никогда не получает учетные данные: в AdapterCtx нет поля, в котором они могли бы путешествовать. Плохой или отсутствующий файл ключа ухудшает страницу подключения магазинов; он никогда не выводит MCP-сервер из строя, поэтому клиент не может не запуститься из-за этого файла. Ни один из поставляемых адаптеров не аутентифицируется с помощью сохраненного — Shopify UCP анонимен, а симулированным магазинам нечего проверять — поэтому подключение Tesco, Costco, Walmart или Amazon хранит учетные данные для адаптера, которого еще не существует, и не меняет ни одного результата, который вы видите сегодня.

  • «Вход через Chrome» (S15), ровно для Tesco, Costco, Walmart и Amazon. Ни один из них не публикует потребительский OAuth-поток, поэтому честная альтернатива полю ввода пароля — открыть собственную страницу входа ритейлера в реальном браузере и позволить человеку войти самостоятельно. Это запускает уже установленный Chrome на машине, никогда не загруженный Chromium — никому, кто этим пользуется, не нужно ничего устанавливать. Ничего не читается, пока человек не нажмет «захватить»: нет опроса на появление сессионного cookie и его захвата в момент появления. В соответствии с «никакого обхода антибот-систем» выше, это не скрывает автоматизацию от ритейлера — никакого спуфинга navigator.webdriver, никакого удаления флагов автоматизации — потому что система мошенничества, которая не может отличить управляемый браузер от человека, — это не та линия, на которую этот проект променяет страницу подключения магазинов. Условия предоставления услуг каждого из этих четырех ритейлеров запрещают автоматический вход, в том числе владельцем аккаунта; этот риск раскрыт на самой кнопке, а не только здесь. Захваченная сессия запечатывается точно так же, как вставленные учетные данные — то же хранилище, та же проверка reveal() — и ни один адаптер пока ее не использует.

  • Агент видит только непрозрачный дескриптор аккаунта, никогда ничего, что могло бы им стать.

  • Поверхность одобрения защищена токеном процесса, выводимым на консоль самого сервера, рядом с 6-значным кодом. Разделение маршрутов не является барьером: каждый клиент, в который устанавливается Basketed, имеет оболочку, поэтому агент всегда может обратиться к 127.0.0.1 и подделать любой заголовок. /api также требует Origin, точно равный панели, и отклоняет изменяющий запрос без него.

  • Текст поставщика — ненадежные данные. Нормализован по NFKC, очищен от управляющих символов, нулевой ширины и двунаправленных переопределений, удален HTML, ограничен по длине, помечены паттерны инъекций. Настоящая защита сильнее: экран одобрения и хэш корзины строятся только из числовых и перечислимых полей плюс нормализованное название продукта. Ни одна строка, созданная продавцом, не достигает ни того, ни другого.

  • approval_id — CSPRNG, привязан на стороне сервера к субъекту, полученному из локальной сессии — никогда из того, что предоставил агент — и повторно проверяется внутри атомарного потребления. Владение никогда не является аутентификацией (State Handle Hijacking из 2026-07-28).

  • Слой редактирования поверх каждого ответа, как сеть, а не защита. Попадание — это ошибка; панель показывает количество.

  • Мы никогда не касаемся данных карт (вне области PCI), никогда не храним пароли ритейлеров и не поставляем скрейпер.


Не построено, заявлено, чтобы никто не претендовал

Реальные адаптеры ритейлеров для Costco/Walmart/Amazon (ни один не публикует потребительский API; хранилище содержит учетные данные — вставленные или захваченные через Chrome — но ничто пока не аутентифицируется с их помощью), реальный OAuth ритейлеров (ни один из четырех не публикует его — см. Подключение магазинов), захват входа через Chrome для любого магазина вне этих прототипных четырех, мок-IdP, канал одобрения B (элиситация), compare_products, страница заказов, MCPB, публикация в реестре и отправка плагина ChatGPT. Все спроектировано в плане, и ничего не построено.

Tesco — единственный адаптер ритейлера, который ушел из этого списка (S16) — см. «Откуда берутся данные» выше: реальный поиск, реальные детали и реальная корзина за собственным вставленным токеном сессии покупателя. Costco, Walmart и Amazon остаются здесь, потому что ни один из трех не имеет эквивалентной неофициальной, но реальной конечной точки, которую фронтенд Tesco случайно предоставляет — см. Подключение магазинов о том, что сессия входа через Chrome на этих трех может и не может делать вместо этого.

Хранилище учетных данных — другой пункт, который ушел из этого списка (S14) — см. Безопасность выше — и защита от дрейфа, которая раньше проверяла это здесь, теперь проверяет обратное: что этот файл перестает отказываться от него ровно тогда, когда это перестает быть правдой.

docs/ — BENCHMARK

Требуется Node ≥ 22 (node:sqlite, поэтому нет этапа нативной сборки — это важно на Windows, где это было разработано и проверено).

Related MCP Connectors

Related MCP Servers