Addepar MCP Server
Addepar MCP Server
只读 MCP 服务器,向 Claude 暴露 Addepar 的投资组合和所有权数据。
为在注册投资顾问处的财务报告而构建。治理原则按优先级排列:可靠性、准确性、安全性,最后是便利性。
此服务器保证什么
它不保证任何数字在现实世界中是正确的。这不是任何工具都能诚实承诺的,因为 Addepar 本身就带有陈旧标记:按“截至今日”运行的查询通常会返回一个标记为几周前的值,因为私募基金按季度估值。
它保证的是对来源的完全诚实:
它从不凭空捏造数字。
它从不静默丢弃数据。
它始终说明自己所不知道的。
这里刻意没有置信度评分。标注“94% 置信”的数字是虚假精度,而在合规场景中,虚假精度比毫无用处更糟。相反,每个响应都带有一个结构化的 caveats 数组,当结果干净时该数组为空,因此“为空”本身是一种肯定性陈述,而非未做检查。
null 规则
null 值和 0.0 值是不同的事实,绝不会被合并。
在实时数据中得到确认,同一家庭中的两个持仓:
Position | Value | Meaning |
Leslie A Dahl, WRD Capital |
| Addepar 计算出了一个值,且该值为零 |
W Robert Dahl, Goldman Sachs -400P |
| 未计算出任何值,原因未说明 |
将该 null 强制转换为零并求和,会产生一个自信地错误且看起来完全合理的总额。因此,null 被排除在求和之外,被计数,并在 NULL_VALUES_EXCLUDED 中被点名。
警示代码
Code | Raised when |
| 某个持仓的估值日期早于请求日期超过 35 天 |
| 一个或多个持仓未返回任何计算值 |
| 查找发现了多个看似合理的候选 |
| 使用了名称匹配,而名称匹配本质上不具有穷尽性 |
| 遍历提前停止,提示可能存在循环嵌套 |
| 某个总额合并了在不同日期估值的数值 |
| 实际行数多于返回的行数;总额仍覆盖全部 |
| 该对象类型不存在已确认的 UI 链接模式 |
工具
Tool | Question it answers |
| 将名称转换为特定的 Addepar ID 和对象类型 |
| 总敞口、定向持仓或实益所有权 |
| 一组关联基金的整体敞口 |
| 某位客户的持仓是如何分类的 |
| 存在哪些已保存的报告 |
| 运行公司自己的某个已保存报告 |
| 已承诺、已缴付和未缴付的资本 |
全部七个工具都声明了 read_only_hint=True,因此受信任的客户端可以跳过确认提示。真正的保证是结构性的:见下文。
保持工具数量克制。工具定义会在每次请求时加载到模型的上下文中,因此无论是否使用,每个工具都会消耗 token,而过大的工具面会显著降低工具选择的质量。与其添加一个近乎重复的工具,不如给现有工具增加一个参数。
架构
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 没有任何写入用例,而且一个改变了客户所有权结构的 bug 是无法完全恢复的。
故障时关闭
在操作中途遇到超时或命中速率限制时,工具会返回错误且不返回任何数据。它们绝不会返回部分树或更小的总额,因为被截断的所有权总额乍一看与正确的总额无法区分。
设置
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 的日志无法在用户层面佐证它。
两种受支持的模式:
Mode | Per-user attribution |
共享组织凭据 ( | 否。 管理员输入一个凭据;每个用户的请求都携带它,所有用户无法区分。 |
按用户 OAuth | 是。 每个用户单独授权,因此请求能识别出他们。 |
如果合规性需要回答“谁请求了什么”,OAuth 就不是可选项。它是唯一能产生该答案的配置。
对于本次部署,最自然的授权服务器是公司的 Entra ID 租户,因为他们已经在运行 Microsoft 365。这会将访问与实际公司账户绑定,并使其可以通过公司自己的 SSO 进行治理,而不是依赖外部方持有的凭据。
一旦配置好 OAuth,就设置 REQUIRE_AUTH=true。在此之前,服务器会将调用记录为无归属,这虽然诚实,但无法满足按用户审计的要求。
值得提前了解的部署陷阱
托管 Claude 界面的重定向 URI 是
https://claude.ai/api/mcp/auth_callback。Anthropic 的出站流量来自
160.79.104.0/21。此服务器以及授权服务器的发现端点都必须能从该网段访问。即使 MCP 服务器本身可访问,身份提供者前面的防火墙也会中断该流程。使用 Entra ID 时,MCP 服务器 URL 还必须在应用注册中注册为应用程序 ID URI,否则令牌请求会以
AADSTS9010010失败。Claude 为发现端点和令牌端点预留约 10 秒,为刷新预留 30 秒。缓慢的端点表现为间歇性连接失败,而不是干净利落的错误。
签名验证尚未实现
auth.py 会解码 JWT 声明以供审计之用,但不验证签名。在受信任的网络上这是可以接受的,但一旦服务器可被不受信任的调用者访问,就不可接受了。在暴露服务器之前,请用真正的 JWKS 验证(获取租户密钥、验证签名、检查签发者、受众和过期时间)取代它。来自未验证令牌的身份是一种声明,而非事实。这里有意保持明显的未完成状态,而不是用桩代码伪装成已完成。
审计日志
结构化 JSON Lines,每次工具调用写一条对象记录,写入 AUDIT_LOG_PATH。每条记录包含时间戳、工具、调用者身份、参数、结果、耗时、Addepar 请求 ID、涉及的实体、行数、警示代码以及任何错误。
日志必须保存在服务器端。对话记录不是持久性记录:用户可将其删除,而且在某些界面上根本无法归档。如果一次数据访问的唯一痕迹只存在于聊天窗口中,那么从合规角度看,它就不存在。
请在合规性讨论中提出这一点: 这些记录包含实体名称、美元金额和访问模式。因此,日志与底层客户数据处于同一合规边界内,并附带相同的保留和访问问题。尽早决定它是写入自己的存储,还是输出到现有的归档管线,因为同一客户数据的两份存储会使合规面翻倍。
引用
规则:仅当对象类型和 ID 命名空间都已确认时才发出链接;否则,发出可复现的查询。一个自信地错误的引用比没有引用更糟,因为它看起来具有权威性,却会把用户引向错误的地方。
Object | Pattern | Status |
实体详情 |
| 已确认 |
实体上的视图 |
| 已确认 |
组上的视图 |
| 推断得出,未发出 |
持仓详情 | 未知,可能不存在 | 未发出 |
计算得出的聚合值(如总敞口)并非 Addepar 对象,也没有原生 URL。它们的引用方式是深层链接到以同一投资组合为根的已保存视图,使用户进入公司构建且已经信任的报告。
这些模式无法以编程方式验证。Addepar Web 应用是单页应用,会对每个路径(包括故意乱写的路由)都返回 HTTP 200,因此 curl 无法区分有效路由和无效路由。任何新模式都必须由人工从实时 UI 中复制真实 URL 来确认。
已验证的行为
已于 2026-08-26 针对实时租户验证。这些是 tests/test_regression_fixtures.py 中的回归测试固件。如果重构改变了其中任何一项,那么该重构就是错误的,除非有证据证明并非如此。
Assertion | Value |
Dahl 家庭总额 |
|
Loon Point Holdings II LLC,定向,4 处出现求和 |
|
Pacific Lake 家族,扫描 4,121 个中匹配 6 个 |
|
Charlotte 在共享 LLC 中的份额 |
|
真实最大所有权深度 |
|
公司内的家庭数 |
|
两项交叉校验使这些数据值得信赖,而不仅仅是记录在案:
无论通过按
ownership(嵌套的法律层级)分组还是按security(扁平持仓)分组,家庭总额都完全相同。两种完全不同的查询形态,结果精确到分也一样。一个共享 LLC 自身的总额等于四个兄弟信托对其份额之和,从相反的遍历方向得出,且无重复计数。
Addepar API 说明
来之不易的行为记录,以免日后痛苦地重新发现。
每个端点都需要
Addepar-Firm请求头,包括/v1/users/me。/v1/entities上的filter[name]会被静默忽略。 它不是真正的过滤器:它返回的是任意实体,而不是名称匹配;这比报错更糟,因为它看起来像是成功了。filter[entity_types]是真实且有效的。未加过滤器的
GET /v1/entities返回 400(“cache is not responsible for firm 2142”)。添加任意过滤器参数即可避免。这是 Addepar 侧的 bug,我们选择绕开而不是上报。名称搜索在
POST /v1/groups/query上有效,通过display_names。这是 API 中唯一有效的名称搜索。ownership分组只遍历法律实体。 它会在持仓账户风格的叶节点处停止,不会下探到其中的证券。对于实际投资条目,请使用security分组。离散过滤器仅支持精确匹配。 没有前缀或子串匹配,因此部分名称会返回零行,而不是模糊匹配结果。
错误信息干净且具体,例如
"Invalid grouping attribute: nonsense_grouping"。它们会被原样转发。延迟是变化的。 household 汇总通常约 3 秒完成。有一次运行耗时 47.8 秒,而 Addepar 的上限是 60 秒。客户端超时设置为 55 秒,因此会出现清晰的错误,而不是连接在响应中途被切断。
速率限制是全公司范围的,每 15 分钟 50 个请求,每 24 小时 1,000 个请求,与公司内所有其他集成共享。因此,即使活动与本服务器无关,也可能触发限制。
同名冲突
单次会话中就发现了三例,这说明这是数据的固有形态,而非运气不好。
名称 | 对象 |
Dahl 2012 Dynasty Trust | PERSON_NODE 实体 |
Pacific Lake Partners Long-Term Hold Fund One, L.P. | 出现两次 |
Dahl Family | GROUP |
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()上。自定义异常必须继承 SDK 的
ToolError。其他任何异常都会被当作崩溃处理,其消息会保留在服务端,因此模型只会收到一个通用失败信息。这会静默破坏任何本应携带信息返回的错误,例如歧义候选列表。
已知缺口
get_commitments和get_entity_attributes遵循与其他工具相同的已验证模式,但尚未在真实数据上验证过。在早期探测中,Commitment 列返回了0.0,可能需要该公司保存视图所使用的 period 参数。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