Addepar MCP Server
Addepar MCP Server
MCP-сервер только для чтения, предоставляющий Claude данные Addepar по портфелям и структуре владения.
Создан для использования в финансовой отчётности в зарегистрированном инвестиционном консультанте. Руководящие принципы в порядке приоритета: надёжность, точность, безопасность, затем удобство.
Что гарантирует этот сервер
Он не гарантирует, что какое-либо число в реальном мире корректно. Ни один инструмент не может честно обещать этого, потому что сам Addepar несёт устаревшие отметки: запрос «на сегодняшнюю дату» обычно возвращает значение, помеченное на несколько недель раньше, потому что частные фонды переоцениваются ежеквартально.
Что он гарантирует — это полная честность в отношении происхождения данных:
Он никогда не выдумывает числа.
Он никогда молча не отбрасывает данные.
Он всегда сообщает о том, чего не знает.
Здесь намеренно нет оценок уверенности. Цифра с пометкой «уверенность 94%» — это ложная точность, а ложная точность в контексте комплаенса хуже, чем бесполезна. Вместо этого каждый ответ содержит структурированный массив caveats, который пуст, когда результат чист, так что его пустота является утвердительным заявлением, а не отсутствием проверки.
Правило null
Значение null и значение 0.0 — это разные факты, и они никогда не объединяются.
Подтверждено на живых данных: две позиции в одном домохозяйстве:
Позиция | Значение | Смысл |
Leslie A Dahl, WRD Capital |
| Addepar вычислил значение, и оно равно нулю |
W Robert Dahl, Goldman Sachs -400P |
| Значение не вычислено, причина не указана |
Приведение этого null к нулю и суммирование даёт итог, который уверенно неверен и при этом выглядит вполне правдоподобно. Поэтому null исключаются из сумм, подсчитываются и перечисляются в NULL_VALUES_EXCLUDED.
Коды оговорок
Код | Возникает, когда |
| Позиция была отмечена более чем за 35 дней до запрошенной даты |
| Одна или несколько позиций не вернули вычисленного значения |
| Поиск нашёл более одного правдоподобного кандидата |
| Использовано сопоставление по имени, которое по своей природе не является исчерпывающим |
| Обход остановлен раньше времени, что указывает на циклическую вложенность |
| Итог объединяет значения, отмеченные разными датами |
| Существует больше строк, чем было возвращено; итоги по-прежнему покрывают все |
| Для этого типа объекта нет подтверждённого шаблона ссылки в интерфейсе |
Инструменты
Инструмент | На какой вопрос отвечает |
| Превратить имя в конкретный ID Addepar и тип объекта |
| Совокупная экспозиция, целевые активы или бенефициарное владение |
| Экспозиция по семейству связанных фондов |
| Как классифицируются активы одного клиента |
| Какие сохранённые отчёты существуют |
| Запустить один из собственных сохранённых отчётов фирмы |
| Обязательства по капиталу, востребованный и нефинансируемый капитал |
Все семь объявляют read_only_hint=True, поэтому доверенный клиент может пропускать запросы подтверждения. Настоящая гарантия — структурная: см. ниже.
Держите количество инструментов под контролем. Определения инструментов загружаются в контекст модели при каждом запросе, поэтому каждый из них стоит токенов, используется он или нет, а раздутая поверхность заметно ухудшает выбор инструментов. Предпочитайте добавить параметр к существующему инструменту, а не создавать почти дубликат.
Архитектура
src/addepar_mcp/
config.py Settings from environment. No secrets in code.
errors.py Three failure classes. Extends the SDK ToolError.
models.py The response contract. Caveats, provenance, disclosure.
client.py Read-only HTTP client. Cannot construct a mutating request.
tree.py Traversal and null-safe arithmetic. No network dependency.
citations.py UI links, only for confirmed URL patterns.
audit.py Structured JSON Lines compliance record.
auth.py Per-request identity extraction. Entra ready.
runtime.py Shared runtime container.
server.py Entrypoint, transports, identity middleware.
tools/ One module per tool, each exposing register(mcp).Добавление инструмента означает добавление модуля и одной строки в tools/__init__.py.
Только чтение, обеспечивается в коде
Клиент предоставляет только get и query, где query — это POST, ограниченный фиксированным списком разрешённых конечных точек запросов только для чтения. Не существует пути кода, который мог бы выполнить PATCH, PUT или DELETE, и нет способа отправить POST на произвольный путь. Попытка сделать это вызывает ReadOnlyViolationError.
Это сделано намеренно, а не для вида. Ни один сценарий использования v1 не выполняет запись, а сбой, изменивший структуру владения клиента, был бы не полностью обратимым.
Отказ с закрытием доступа
При тайм-ауте или достижении лимита запросов в середине операции инструменты возвращают ошибку и никаких данных. Они никогда не возвращают частичное дерево или уменьшенный итог, потому что усечённый итог по владению неотличим от корректного на первый взгляд.
Настройка
python -m venv .venv
.venv/bin/pip install -e ".[dev]"
cp .env.example .env # then fill in credentials
.venv/bin/python -m pytest tests/ -qЗапуск локально через stdio:
TRANSPORT=stdio .venv/bin/python -m addepar_mcp.serverЗапуск через HTTP:
TRANSPORT=http HOST=0.0.0.0 PORT=8080 .venv/bin/python -m addepar_mcp.serverПроверка работоспособности по адресу GET /healthz. Конечная точка MCP — /mcp.
Развёртывание и аутентификация
Разворачивайте удалённо через HTTPS, чтобы обслуживать один экземпляр, а не по одному на рабочую станцию.
Идентификация и почему это важно
Два отдельных уровня идентификации, и их смешение в дальнейшем приводит к путанице:
Идентификация вызывающего: кто вызвал инструмент. Извлекается для каждого запроса и записывается в каждую запись аудита.
Вышестоящая идентификация: что показывает собственный журнал Addepar, а именно единые учётные данные сервиса, независимо от того, кто запросил.
Это означает, что серверный журнал аудита является авторитетным ответом на вопрос «кто смотрел данные клиентов». Журнал Addepar не подтвердит его на уровне пользователей.
Два поддерживаемых режима:
Режим | Атрибуция по пользователям |
Общие учётные данные организации ( | Нет. Администратор вводит одни учётные данные; каждый пользовательский запрос несёт их, и все пользователи неразличимы. |
OAuth для каждого пользователя | Да. Каждый пользователь даёт согласие индивидуально, поэтому запрос идентифицирует его. |
Если комплаенсу нужно ответить на вопрос «кто и что запрашивал», OAuth не опционален. Это единственная конфигурация, которая даёт такой ответ.
Для этого развёртывания естественным сервером авторизации является тенант Entra ID фирмы, поскольку они уже используют Microsoft 365. Это привязывает доступ к реальным корпоративным учётным записям и позволяет управлять им через собственный SSO фирмы, а не через учётные данные, хранящиеся у внешней стороны.
Установите REQUIRE_AUTH=true, как только OAuth будет настроен. До этого сервер регистрирует вызовы как неприписанные; это честно, но не удовлетворяет требованию аудита на уровне пользователя.
Подводные камни развёртывания, о которых стоит знать заранее
URI перенаправления для размещённых поверхностей Claude:
https://claude.ai/api/mcp/auth_callback.Исходящий трафик Anthropic исходит из
160.79.104.0/21. И этот сервер, и конечные точки обнаружения сервера авторизации должны быть доступны из этого диапазона. Межсетевой экран перед поставщиком удостоверений нарушает поток, даже если сам MCP-сервер доступен.С Entra ID URL MCP-сервера также должен быть зарегистрирован как URI идентификатора приложения в регистрации приложения, иначе запрос токена завершится ошибкой
AADSTS9010010.Claude выделяет примерно 10 секунд на конечные точки обнаружения и токена и 30 секунд на обновление. Медленные конечные точки выглядят как периодические сбои соединения, а не как явные ошибки.
Проверка подписи не реализована
auth.py декодирует утверждения JWT для целей аудита, но не проверяет подписи. Это приемлемо в доверенной сети и не приемлемо, как только сервер становится доступен недоверенным вызывающим. Замените это на настоящую проверку JWKS (получение ключей тенанта, проверка подписи, проверка издателя, аудитории и срока действия), прежде чем открывать доступ. Идентичность из непроверенного токена — это утверждение, а не факт. Это было оставлено намеренно незавершённым, а не заглушкой, чтобы выглядеть готовым.
Журнал аудита
Структурированные JSON Lines, по одному объекту на вызов инструмента, записываемые в AUDIT_LOG_PATH. Каждая запись содержит метку времени, инструмент, идентификацию вызывающего, аргументы, результат, длительность, ID запросов Addepar, затронутые сущности, количество строк, коды оговорок и любую ошибку.
Журнал должен храниться на стороне сервера. Транскрипт разговора — это не долговечная запись: пользователь может его удалить, а на некоторых поверхностях его вообще нельзя архивировать. Если единственный след обращения к данным живёт в окне чата, для целей комплаенса его не существует.
Поднимите этот вопрос в разговоре о комплаенсе: эти записи содержат имена сущностей, денежные суммы и паттерны доступа. Поэтому журнал находится внутри того же комплаенс-периметра, что и лежащие в основе данные клиентов, с теми же вопросами хранения и доступа. Решите заранее, пишет ли он в собственное хранилище или передаёт данные в существующий конвейер архивирования, потому что два хранилища одних и тех же клиентских данных удваивают комплаенс-поверхность.
Цитирование
Правило: выдавайте ссылку только тогда, когда подтверждены и тип объекта, и пространство имён ID. В противном случае выдавайте воспроизводимый запрос. Уверенно неверная ссылка хуже, чем отсутствие ссылки, потому что она выглядит авторитетной и отправляет человека не туда.
Объект | Шаблон | Статус |
Детали сущности |
| Подтверждено |
Представление по сущности |
| Подтверждено |
Представление по группе |
| Выведено, не выдаётся |
Детали позиции | неизвестно, может не существовать | Не выдаётся |
Вычисляемые агрегаты, такие как совокупная экспозиция, не существуют как объекты Addepar и не имеют собственного URL. Они цитируются через глубокую ссылку на сохранённое представление, корнем которого является тот же портфель, что приводит пользователя к отчёту, который фирма построила и которому уже доверяет.
Эти шаблоны невозможно проверить программно. Веб-приложение Addepar — это одностраничное приложение, которое возвращает HTTP 200 для любого пути, включая намеренно бессмысленные маршруты, поэтому curl не может отличить действительный маршрут от недействительного. Любой новый шаблон должен быть подтверждён человеком, копирующим реальный URL из живого интерфейса.
Проверенное поведение
Проверено на живом тенанте 26 августа 2026 года. Это регрессионные фикстуры в tests/test_regression_fixtures.py. Если рефакторинг меняет любое из них, рефакторинг ошибочен, пока не доказано обратное.
Проверяемое утверждение | Значение |
Итог по домохозяйству Dahl |
|
Loon Point Holdings II LLC, по целевым позициям, 4 вхождения суммированы |
|
Семейство Pacific Lake, 6 совпадений из 4 121 проверенных |
|
Доля Шарлотты в общей LLC |
|
Реальная максимальная глубина владения |
|
Домохозяйств в фирме |
|
Две перекрёстные проверки делают эти значения заслуживающими доверия, а не просто зафиксированными:
Итог по домохозяйству идентичен независимо от того, получен ли он группировкой по
ownership(вложенная юридическая иерархия) или поsecurity(плоские позиции). Две совершенно разные формы запроса — одно и то же число с точностью до цента.Общий итог общей LLC равен сумме долей в ней четырёх родственных трастов, полученной при обходе с противоположных направлений, без двойного подсчёта.
Заметки об API Addepar
Поведение, доставшееся дорогой ценой, задокументировано, чтобы не переоткрывать его мучительно.
Каждая конечная точка требует заголовок
Addepar-Firm, включая/v1/users/me.filter[name]для/v1/entitiesмолча игнорируется. Это не настоящий фильтр: он возвращает произвольные сущности, а не совпадения по имени, что хуже ошибки, потому что выглядит как успех.filter[entity_types]настоящий и работает.Без фильтра
GET /v1/entitiesвозвращает 400 ("cache is not responsible for firm 2142"). Добавление любого параметра фильтра позволяет этого избежать. Баг на стороне Addepar; мы обошли его, а не сообщили о нём.Поиск по имени работает в
POST /v1/groups/queryпосредствомdisplay_names. Это единственный работающий поиск по имени в API.Группировка
ownershipобходит только юридические лица. Она останавливается на листьях типа холдинговых счетов и не спускается к ценным бумагам внутри них. Используйте группировкуsecurityдля фактических инвестиционных позиций.Дискретные фильтры работают только по точному совпадению. Ни префиксного, ни подстрочного сопоставления нет, поэтому частичное имя возвращает ноль строк вместо нечёткого совпадения.
Ошибки понятные и конкретные, например
"Invalid grouping attribute: nonsense_grouping". Они передаются без изменений.Задержка варьируется. Сводка по домохозяйству обычно выполняется примерно за 3 секунды. Один запуск занял 47,8 секунды при потолке Addepar в 60 секунд. Тайм-аут клиента установлен на 55 секунд, чтобы возникала явная ошибка, а не обрыв соединения на середине ответа.
Лимиты запросов действуют на всю фирму, 50 запросов за 15 минут и 1000 за 24 часа, общие для всех остальных интеграций в фирме. Поэтому лимит может сработать из-за активности, не связанной с этим сервером.
Коллизии одинаковых имён
Три случая были обнаружены за одну сессию — это говорит о том, что перед нами особенность данных, а не невезение:
Имя | Объекты |
Dahl 2012 Dynasty Trust | сущность PERSON_NODE |
Pacific Lake Partners Long-Term Hold Fund One, L.P. | Встречается дважды |
Dahl Family | GROUP |
Поэтому каждый ответ раскрывает имя, ID и тип объекта. ID из разных
пространств имён не взаимозаменяемы, и portfolio_type должен совпадать.
Примечание о версии SDK
Эта заметка ориентирована на MCP Python SDK 2.x. При переносе старого кода в этой организации:
FastMCPтеперь — этоMCPServerизmcp.server.mcpserver.Поля
ToolAnnotationsпереехали с camelCase на snake_case (readOnlyHintсталоread_only_hint).stateless_httpиjson_responseперенесены с конструктора наstreamable_http_app().Пользовательские исключения должны наследоваться от
ToolErrorиз SDK. Всё остальное считается сбоем, и его сообщение остаётся на стороне сервера, поэтому модель получает только общую ошибку. Это молча ломает любую ошибку, которая должна была передавать информацию обратно, например список кандидатов при неоднозначности.
Известные пробелы
get_commitmentsиget_entity_attributesследуют тем же проверенным шаблонам, что и другие инструменты, но не были опробованы на живых данных. Столбцы commitment во время более ранних проверок возвращали0.0и, возможно, требуют аргументов периода, которые используют сохранённые представления фирмы.Проверка подписи JWT не реализована. См. выше.
Шаблон URL представления с группой в качестве корня является предположительным и намеренно не выводится.
Учётные данные были раскрыты в открытом виде в ходе нескольких рабочих сессий. Код читает их из переменных окружения, поэтому ротация — это изменение конфигурации, но саму ротацию всё ещё нужно провести перед использованием в производстве.
This server cannot be installed
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 Connectors
Read-only public financial evidence from LiquiLens, Undertow, Seiche and Palimpsest.
Read-only access to your VortexIQ store data: audits, KPIs, alerts, Brand DNA, reports, Ask VIQ.
Read-only NuMetric.work accounting & ERP data: statements, KPIs, reports, invoices, documents.
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/graysonlevino/oakridge-addepar'
If you have feedback or need assistance with the MCP directory API, please join our Discord server