mcp-server-productive
mcp-server-productive
MCP-сервер для API v2 Productive.io — проекты, задачи, учёт времени, планирование ресурсов, финансы, CRM и отчёты для одной организации.
Productive предоставляет около 650 операций над 132 ресурсами. Превращение их в 650 MCP-инструментов переполнило бы список инструментов любого клиента, поэтому этот сервер — двенадцать инструментов на основе сгенерированного реестра: инструменты универсальны, а реестр знает, что именно принимает каждый ресурс.
Инструменты
Обнаружение — productive_search_capabilities, productive_describe_resource,
productive_check_connection, productive_describe_custom_fields
Чтение — productive_list (фильтры, сортировки, включения, постраничный вывод и 26 конечных
точек отчётов с группировкой), productive_get
Запись — productive_create, productive_update, productive_delete, productive_run_action
(150 именованных действий: archive, restore, approve, close, copy, finalize, send…),
productive_track_time, productive_commit_operation
Авторизация для каждого пользователя (опционально) — productive_connect, productive_status,
productive_disconnect — см. Аутентификация
Начните с productive_search_capabilities. У Productive собственные имена ресурсов — бюджет — это
deal, колонка доски — workflow_status, согласование табеля находится в time_entries, —
и угадывание стоит запросов.
Related MCP server: productive-mcp-rb2
Реестр
src/productive/registry.generated.ts создаётся из опубликованного OpenAPI-документа Productive
скриптом scripts/generate-registry.mjs и сохраняется в репозиторий, поэтому CI никогда не требует
сети, а изменение спецификации видно как проверяемый дифф. Для каждого ресурса в нём записаны поля
фильтров, ключи сортировки, ключи группировки отчётов, включаемые связи, атрибуты, доступные для
записи при создании и обновлении, с помеченными обязательными, а также каждое именованное действие.
Именно это позволяет двенадцати инструментам оставаться честными. productive_describe_resource
возвращает точный контракт для одного ресурса, и каждый аргумент проверяется по нему перед отправкой
запроса.
Перегенерируйте с помощью npm run registry:generate (добавьте аргумент пути, чтобы использовать
локальную копию спецификации). Генератор останавливает сборку, если рукописная классификация —
уровень риска, флаг внешнего воздействия, заблокированная операция — больше не соответствует ни
одному пути в спецификации, поэтому переименование на стороне API не может незаметно убрать защиту.
Что API делает, по результатам измерений
Всё здесь проверено на реальной организации, потому что спецификация и API расходятся в важных местах.
Время — в минутах. Деньги — в минимальных единицах. Запись времени 2 — это две минуты.
И в ответе они приходят так же.
Неизвестные фильтры, сортировки и включения падают громко. HTTP 400 с unsupported_filter,
sort_param_unsupported, unsupported_include. Поэтому проверка их здесь — это более качественная
ошибка, а не страховочная сеть.
Неизвестные атрибуты записи падают молча. PATCH с опечаткой в атрибуте возвращает HTTP 200
и ничего не меняет — неотличимо от успеха. Поэтому этот сервер отказывает в атрибуте, который ресурс
не объявляет, вместо того чтобы сообщить о записи, которой не было. Это самое полезное, что делает реестр.
Есть ровно шесть операторов фильтрации для каждого поля: contains, eq, gt, lt,
not_contain, not_eq. Спецификация перечисляет четыре на поле и опускает gt/lt, которые
работают; gte, lte, in, not_in, starts_with, ends_with, blank и present все
отклоняются с unsupported_filter_operation. Не существует включающего сравнения, поэтому для
включающего диапазона нужны собственные поля фильтра ресурса after/before или
<field>_after/<field>_before.
page[size] ограничен 200 и молча урезается. Запрос 500 возвращает 200 без ошибки. Результаты
содержат total и nextPage, чтобы страницу не приняли за весь ответ.
PATCH действительно частичный. Опущенные атрибуты сохраняют свои значения; нет необходимости пересылать всю запись.
data.type не проверяется. Патч задачи с type: "projects" успешно применяет изменение. Этот
сервер в любом случае отправляет правильный тип.
403 с сообщением, что id организации «has to be provided», может означать, что он был неверным,
а не отсутствующим. Один и тот же код no_organization_id покрывает отсутствующий заголовок
и организацию, до которой токен не может дотянуться.
Отсутствующая функция отвечает 404, а не 403. /boards даёт 404 в организации, где её нет,
что выглядит как сломанный путь.
Удаления могут быть восстанавливаемыми. Удалённая задача появляется в deleted_items с
item_type и item_id и может быть восстановлена через действие restore этого ресурса. Проверено
только для задач — не предполагайте, что это верно для всех типов.
GET /users — единственная конечная точка, ограниченная вызывающим. Она возвращает ровно одну
запись — вас, — и именно так этот сервер определяет владельца токена. /users/me не существует;
этот путь даёт 404. Остерегайтесь /organization_memberships: он не ограничен закреплённой
организацией, а перечисляет членства вызывающего во всех организациях, к которым он принадлежит,
так что количество его строк — это не численность сотрудников.
Нет заголовков ограничения частоты запросов. Только x-request-id, который цитируют ошибки
этого сервера. При 429 снижайте темп, а не выясняйте лимит.
Разрешения
Четыре переключателя, все по умолчанию выключены. Сервер только для чтения — полезный и безопасный вариант по умолчанию.
Переключатель | Что охватывает |
| Главный переключатель. Без него ничто не изменяется. |
| Деньги, цены, зарплата, документы, которые получает клиент: инвойсы, позиции, платежи, счета, расходы, заказы на закупку, предложения, договоры, цены, тарифные карты, оклады, накладные расходы, налоговые ставки, банковские счета, дочерние компании. |
| Доступ и общеорганизационная конфигурация: люди, членства, наборы разрешений, команды, приглашения, настраиваемые поля, вебхуки, интеграции, политики согласования и учёта времени. |
| Удаления, поверх уровневого ограничения. |
Одного главного переключателя для Productive недостаточно: одно и то же API перемещает задачу, выставляет инвойс и выдаёт набор разрешений, и это три разных решения. Сервер, которому доверено вести проектную работу, не должен из-за этого иметь возможность отправить инвойс.
PRODUCTIVE_ALLOWED_RESOURCES / PRODUCTIVE_DENIED_RESOURCES дополнительно сужают инстанс
и применяются также к чтению — инстанс, ограниченный учётом времени, не должен читать и оклады.
Никогда не открываются вообще, независимо от переключателей: passwords, sessions,
organization_subscriptions, неаутентифицированные ссылки общего доступа public/* и
PATCH /users/{id}/update_password. Их нет в реестре, а не просто закрыты, поэтому никакая ошибка
политики не сможет открыть их снова.
Запись в два шага
Обычная проектная работа — задача, запись времени, бронирование, комментарий — записывается одним вызовом. Требовать рукопожатие вокруг каждой записи времени сделало бы сервер непригодным для того, что люди делают чаще всего.
Всё с более широким радиусом поражения откладывается: инструмент возвращает точный запрос плюс
хэш и ничего не отправляет, а productive_commit_operation выполняет его, только если операция
вернулась без изменений. Это покрывает финансовый и административный уровни, каждое удаление, всё,
что покидает организацию, и каждое действие bulk_* — они действуют на все записи, подходящие под
фильтр, поэтому также отказываются выполняться без явного фильтра.
Шесть операций помечены как outward, потому что они обращаются к кому-то за пределами организации
в момент выполнения: invoices.send, invoices.send_einvoice, people.invite, people.resend,
organizations.resend_code и создание invitation.
Аутентификация
Два режима. PRODUCTIVE_ORGANIZATION_ID обязателен в обоих и никогда не является аргументом инструмента.
Персональные токены (рекомендуется)
Каждый человек привязывает собственный токен Productive, поэтому Productive применяет его разрешения и записывает его имя в том, что он делает.
Это важнее в Productive, чем в большинстве систем. Productive приписывает работу людям: запись времени
принадлежит person_id, и каждое изменение помечается владельцем токена в журнале активности — именно
этой записью защищают клиентский инвойс. При одном общем токене этот журнал говорит, что всё сделал
сервисный аккаунт.
PRODUCTIVE_PER_USER_AUTH=true
PRODUCTIVE_TRUST_FORWARDED_USER=true
PRODUCTIVE_ENCRYPTION_KEY=<min 16 chars>
PRODUCTIVE_STORE_PATH=/data/store.json
PRODUCTIVE_PUBLIC_BASE_URL=https://productive.example.com
# PRODUCTIVE_API_TOKEN deliberately unsetПорядок действий:
Вызывающий запускает
productive_connectи получает одноразовую ссылку, действительную 10 минут, привязанную к его личности.Он открывает её и вставляет токен, созданный в Productive в разделе Settings → API integrations. Токен идёт из его браузера напрямую на сервер, поэтому никогда не попадает в транскрипт разговора — токен Productive эквивалентен предъявителю всего его аккаунта и, как показано ниже, обычно достаёт до более чем одной организации.
Перед сохранением сервер вызывает
GET /usersс этим токеном и id этой организации. Один вызов доказывает три вещи: токен действителен, он достаёт до этой организации и кому он принадлежит. Затем страница подтверждает, какой аккаунт был привязан.Токены шифруются при хранении с помощью AES-256-GCM, одна строка на подтверждённую личность.
Острые углы:
Личность приходит только от шлюза.
X-MCP-Userчитается только приPRODUCTIVE_TRUST_FORWARDED_USER=true, и никогда из того, чем управляет MCP-клиент. Включайте его только за шлюзом, который устанавливает заголовок из проверенного токена и удаляет копию, предоставленную клиентом, — иначе вызывающий может назвать любую личность и действовать от её имени.Никакого запасного варианта. Не прошедший привязку вызывающий получает
NOT_CONNECTED, а не общий токен, даже еслиPRODUCTIVE_API_TOKENслучайно задан. Запасной вариант передал бы ему чужие права — именно этот сбой данный режим и призван устранить./productive/enrollдолжен быть доступен браузеру пользователя в обход MCP-шлюза — браузер не может нести bearer-токен шлюза. Направляйте/productive/*наPRODUCTIVE_PUBLIC_BASE_URLнапрямую в контейнер. Его безопасность — одноразовый state-токен, привязанный к личности.Храните
PRODUCTIVE_STORE_PATHна томе и держитеPRODUCTIVE_ENCRYPTION_KEYстабильным — измените его, и каждый сохранённый токен станет нерасшифровываемым.Если собственный email Productive у токена отличается от адреса вызывающего в каталоге, об этом громко сообщается на странице и в
productive_status, и подключение всё равно происходит. УстановитеPRODUCTIVE_REQUIRE_EMAIL_MATCH=true, чтобы вместо этого отказать. По умолчанию это выключено, потому что тот, кто вставляет чужой токен, уже держит его в руках, так что отказ даёт мало безопасности, а аккаунт Productive под другим адресом вполне правдоподобен.Авторизация для каждого пользователя разделяет разрешения и атрибуцию, а не организации. Привязка к организации по-прежнему действует для всех.
Общий токен
Установите PRODUCTIVE_API_TOKEN в один токен. Просто и правильно для stdio или одного оператора —
но тогда каждый вызывающий действует как владелец этого токена, с его разрешениями, и журнал
активности Productive приписывает каждое изменение ему.
PRODUCTIVE_TRUST_FORWARDED_USER по-прежнему помогает здесь: записи, относящиеся к человеку (учёт времени, бронирование),
по умолчанию приписываются определённому вызывающему, а не владельцу токена, и сервер отказывается угадывать, когда
адрес не соответствует никому или более чем одному человеку. productive_check_connection в любом случае называет владельца
токена, так что атрибуция никогда не становится неожиданностью.
Несколько организаций
Один экземпляр обслуживает ровно одну организацию. X-Organization-Id берётся из окружения и
никогда не является аргументом инструмента, поэтому ни один путь кода — включая универсальные инструменты — не может достичь другого тенанта.
Запустите второй экземпляр для второй организации; образ тот же.
Это не теория. Один токен регулярно получает доступ к нескольким организациям: на аккаунте, на котором это
разрабатывалось, GET /organizations вернул три, и изменение только заголовка переключало
между ними (остальные две отвечали 403 subscription_expired, а не «не найдено»). Заголовок — это
вся граница, поэтому он закреплён, а не передаётся — и поэтому зарегистрированный токен на пользователя
проверяется против этой организации перед сохранением.
Конфигурация
Смотрите .env.example. Две обязательные переменные: PRODUCTIVE_API_TOKEN (Настройки → API-интеграции
в Productive; он наследует права создавшего пользователя) и
PRODUCTIVE_ORGANIZATION_ID (числовой id в вашем URL Productive).
Установите PRODUCTIVE_AUDIT_LOG, чтобы добавлять одну JSON-строку на каждую попытку изменения, включая те, которые политика
отклонила. Тела запросов намеренно не записываются: они содержат зарплаты, ставки и персональные
данные, а журнал аудита, который нужно охранять так же строго, как исходную систему, как правило,
не читают.
Запуск
npm install
npm run dev # stdio
npm run dev:http # streamable HTTP on :3000/mcp (stateless), /healthz open
npm test
npm run smoke:live # reads a real organization; stages one write, commits nothingDocker-образы: ghcr.io/borgels/mcp-server-productive (публикуется при пуше в main).
Лицензия
Apache-2.0.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityAmaintenanceA Model Context Protocol (MCP) server for accessing Productive.io API endpoints (projects, tasks, comments, todos), tailored for read-only operations, providing streamlined access to essential data while minimizing token consumption18MIT
- AlicenseAqualityDmaintenanceEnables interaction with Productive.io for task management, time tracking, budget monitoring, and project overview through natural language.8358ISC
- FlicenseNot gradedqualityDmaintenanceEnables AI assistants to interact with a Productive.io workspace for managing projects, tasks, time entries, budgets, and invoices through natural language.
- AlicenseNot gradedqualityDmaintenanceEnables interaction with Productive.io task management platform, allowing users to retrieve tasks and filter by assignee, status, or project.32ISC
Related MCP Connectors
ClickUp MCP — wraps the ClickUp REST API v2 (BYO API key)
Product Hunt MCP — wraps the Product Hunt GraphQL API v2 (api.producthunt.com)
Direct access to your Sanity projects (content, datasets, releases, schemas) and agent rules
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/borgels/mcp-server-productive'
If you have feedback or need assistance with the MCP directory API, please join our Discord server