alloc-context 0.2.0__tar.gz → 0.2.1__tar.gz

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.
Files changed (166) hide show
  1. {alloc_context-0.2.0 → alloc_context-0.2.1}/PKG-INFO +11 -2
  2. {alloc_context-0.2.0 → alloc_context-0.2.1}/README.md +9 -0
  3. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloc_context.egg-info/PKG-INFO +11 -2
  4. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloc_context.egg-info/SOURCES.txt +3 -0
  5. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/__init__.py +1 -1
  6. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/mcp/bazaar.py +63 -10
  7. alloc_context-0.2.1/alloccontext/mcp/glama.py +51 -0
  8. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/mcp/http.py +25 -0
  9. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/mcp/server.py +101 -61
  10. alloc_context-0.2.1/alloccontext/mcp/tool_fields.py +130 -0
  11. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/x402_production_check.py +12 -2
  12. {alloc_context-0.2.0 → alloc_context-0.2.1}/pyproject.toml +7 -3
  13. alloc_context-0.2.1/tests/test_glama_well_known.py +51 -0
  14. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_mcp_bazaar.py +28 -0
  15. alloc_context-0.2.1/tests/test_mcp_server.py +47 -0
  16. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_workflows.py +6 -0
  17. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_x402_production_check.py +3 -3
  18. alloc_context-0.2.0/tests/test_mcp_server.py +0 -26
  19. {alloc_context-0.2.0 → alloc_context-0.2.1}/LICENSE +0 -0
  20. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloc_context.egg-info/dependency_links.txt +0 -0
  21. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloc_context.egg-info/entry_points.txt +0 -0
  22. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloc_context.egg-info/requires.txt +0 -0
  23. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloc_context.egg-info/top_level.txt +0 -0
  24. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/__main__.py +0 -0
  25. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/config.py +0 -0
  26. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/constants.py +0 -0
  27. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/horizon.py +0 -0
  28. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/__init__.py +0 -0
  29. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/alt_quote_registry.py +0 -0
  30. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/alt_quote_store.py +0 -0
  31. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/alt_quotes.py +0 -0
  32. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/asset_registry.py +0 -0
  33. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/cf_benchmarks.py +0 -0
  34. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/cf_history.py +0 -0
  35. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/coinbase_client.py +0 -0
  36. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/coinbase_portfolio.py +0 -0
  37. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/coingecko.py +0 -0
  38. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/coinmarketcap.py +0 -0
  39. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/env_keys.py +0 -0
  40. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/etf_flows.py +0 -0
  41. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/exchange/__init__.py +0 -0
  42. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/exchange/coinbase_adapter.py +0 -0
  43. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/exchange/kraken_adapter.py +0 -0
  44. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/exchange/live.py +0 -0
  45. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/exchange/portfolio.py +0 -0
  46. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/exchange/registry.py +0 -0
  47. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/exchange/types.py +0 -0
  48. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/exchange_http.py +0 -0
  49. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/fear_greed.py +0 -0
  50. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/fred.py +0 -0
  51. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/http_errors.py +0 -0
  52. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/kalshi.py +0 -0
  53. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/kalshi_api.py +0 -0
  54. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/kalshi_client.py +0 -0
  55. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/kalshi_files.py +0 -0
  56. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/kalshi_state.py +0 -0
  57. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/kraken_client.py +0 -0
  58. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/kraken_portfolio.py +0 -0
  59. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/macro_calendar.py +0 -0
  60. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/macro_normalize.py +0 -0
  61. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/market_snapshots.py +0 -0
  62. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/outcome.py +0 -0
  63. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/parse_helpers.py +0 -0
  64. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/portfolio_holdings.py +0 -0
  65. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/quote_resolver.py +0 -0
  66. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/ingest/runner.py +0 -0
  67. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/mcp/__init__.py +0 -0
  68. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/mcp/assets.py +0 -0
  69. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/mcp/bridge.py +0 -0
  70. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/mcp/bridge_portfolio.py +0 -0
  71. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/mcp/contracts.py +0 -0
  72. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/mcp/handlers.py +0 -0
  73. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/mcp/instructions.py +0 -0
  74. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/mcp/payer.py +0 -0
  75. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/mcp/payment_middleware.py +0 -0
  76. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/mcp/setup.py +0 -0
  77. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/mcp/staleness.py +0 -0
  78. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/mcp/upstream.py +0 -0
  79. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/mcp/validation.py +0 -0
  80. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/mcp/x402_bazaar_dynamic.py +0 -0
  81. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/mcp/x402_config.py +0 -0
  82. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/mcp/x402_pricing.py +0 -0
  83. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/mcp/x402_stables.py +0 -0
  84. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/rollup/__init__.py +0 -0
  85. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/rollup/allocation_analysis.py +0 -0
  86. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/rollup/band.py +0 -0
  87. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/rollup/breadth.py +0 -0
  88. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/rollup/cf_math.py +0 -0
  89. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/rollup/cluster.py +0 -0
  90. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/rollup/cluster_config.py +0 -0
  91. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/rollup/comparison.py +0 -0
  92. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/rollup/context.py +0 -0
  93. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/rollup/delta.py +0 -0
  94. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/rollup/etf.py +0 -0
  95. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/rollup/fear_greed.py +0 -0
  96. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/rollup/macro.py +0 -0
  97. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/rollup/portfolio.py +0 -0
  98. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/rollup/portfolio_payload.py +0 -0
  99. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/rollup/rebalance.py +0 -0
  100. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/rollup/regime.py +0 -0
  101. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/rollup/sentiment.py +0 -0
  102. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/rollup/snapshots.py +0 -0
  103. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/rollup/tape.py +0 -0
  104. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/status_report.py +0 -0
  105. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/store/__init__.py +0 -0
  106. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/store/db.py +0 -0
  107. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/store/jsonutil.py +0 -0
  108. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/store/meta.py +0 -0
  109. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/store/retention.py +0 -0
  110. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/store/status.py +0 -0
  111. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/timeutil.py +0 -0
  112. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/user_config.py +0 -0
  113. {alloc_context-0.2.0 → alloc_context-0.2.1}/alloccontext/x402_smoke_redact.py +0 -0
  114. {alloc_context-0.2.0 → alloc_context-0.2.1}/setup.cfg +0 -0
  115. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_backup_sqlite.py +0 -0
  116. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_bridge.py +0 -0
  117. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_bridge_portfolio.py +0 -0
  118. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_bump_version.py +0 -0
  119. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_coinbase_portfolio.py +0 -0
  120. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_config_cli.py +0 -0
  121. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_context_bundle_schema.py +0 -0
  122. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_context_snapshots.py +0 -0
  123. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_db_schema.py +0 -0
  124. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_deploy.py +0 -0
  125. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_dev_stack.py +0 -0
  126. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_etf.py +0 -0
  127. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_exchanges_config.py +0 -0
  128. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_fear_greed.py +0 -0
  129. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_fred.py +0 -0
  130. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_holdings_scoped_delta_regime.py +0 -0
  131. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_holdings_scoped_market.py +0 -0
  132. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_horizon.py +0 -0
  133. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_ingest_outcome.py +0 -0
  134. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_ingest_runner.py +0 -0
  135. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_ingest_store_integration.py +0 -0
  136. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_kalshi_api.py +0 -0
  137. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_kraken_portfolio.py +0 -0
  138. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_live_ingest_handlers.py +0 -0
  139. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_macro.py +0 -0
  140. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_market_breadth.py +0 -0
  141. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_mcp_assets_regime.py +0 -0
  142. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_mcp_contracts.py +0 -0
  143. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_mcp_data_staleness.py +0 -0
  144. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_mcp_handlers.py +0 -0
  145. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_mcp_health.py +0 -0
  146. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_mcp_http_lifecycle.py +0 -0
  147. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_mcp_live_portfolio.py +0 -0
  148. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_mcp_validation.py +0 -0
  149. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_mcp_x402.py +0 -0
  150. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_mcp_x402_http.py +0 -0
  151. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_mcp_x402_pricing.py +0 -0
  152. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_mcp_x402_stables.py +0 -0
  153. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_payer.py +0 -0
  154. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_portfolio_holdings.py +0 -0
  155. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_quote_resolver.py +0 -0
  156. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_rebalance.py +0 -0
  157. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_rollup.py +0 -0
  158. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_script_runtime.py +0 -0
  159. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_security_hardening.py +0 -0
  160. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_server_json.py +0 -0
  161. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_setup.py +0 -0
  162. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_snapshots_and_delta.py +0 -0
  163. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_status_report.py +0 -0
  164. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_user_config.py +0 -0
  165. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_x402_bazaar_dynamic.py +0 -0
  166. {alloc_context-0.2.0 → alloc_context-0.2.1}/tests/test_x402_smoke_redact.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alloc-context
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Portfolio-aware crypto context for agents — holdings, market, optional allocation analysis
5
5
  License: Elastic-2.0
6
6
  Project-URL: Homepage, https://github.com/negillett/alloc-context
@@ -9,7 +9,7 @@ Project-URL: Repository, https://github.com/negillett/alloc-context
9
9
  Project-URL: Issues, https://github.com/negillett/alloc-context/issues
10
10
  Project-URL: Changelog, https://github.com/negillett/alloc-context/releases
11
11
  Project-URL: MCP Server, https://mcp.alloc-context.com/mcp
12
- Keywords: mcp,x402,bitcoin,ethereum,portfolio,allocation,agents,crypto,rebalance
12
+ Keywords: mcp,x402,bitcoin,ethereum,crypto,cryptocurrency,portfolio,allocation,holdings,rebalance,coinbase,kraken,agents
13
13
  Classifier: Development Status :: 4 - Beta
14
14
  Classifier: Intended Audience :: Developers
15
15
  Classifier: Programming Language :: Python :: 3
@@ -44,6 +44,9 @@ Dynamic: license-file
44
44
 
45
45
  # AllocContext
46
46
 
47
+ [![smithery badge](https://smithery.ai/badge/@negillett/alloc-context)](https://smithery.ai/server/@negillett/alloc-context)
48
+ [![Glama MCP server](https://glama.ai/mcp/servers/negillett/alloc-context/badges/score.svg)](https://glama.ai/mcp/servers/negillett/alloc-context)
49
+
47
50
  mcp-name: io.github.negillett/alloc-context
48
51
 
49
52
  **Portfolio-aware crypto context for agents** — discover holdings, market,
@@ -110,6 +113,7 @@ Not financial advice.
110
113
  | **Discovery** | [llms.txt](https://mcp.alloc-context.com/llms.txt), [x402 manifest](https://mcp.alloc-context.com/.well-known/x402.json) |
111
114
  | **Pricing** | **$0.02** cached context/math · **$0.05** live ingest or portfolio |
112
115
  | **Payment** | x402 on Base — USDC or EURC |
116
+ | **Market scope** | Holdings-scoped (BTC/ETH OHLC; alt quote snapshots); bridge auto-scopes from portfolio |
113
117
 
114
118
  Agents and wallets connect directly to the hosted endpoint — see
115
119
  [agent-integration.md](docs/agent-integration.md). The Cursor bridge above
@@ -128,6 +132,11 @@ combines local portfolio reads with this upstream for market context.
128
132
  | `check_allocation_bands` | Batch band checks for multiple target scenarios |
129
133
  | `get_portfolio_state` | Live NAV and holdings from Kraken or Coinbase |
130
134
 
135
+ Market context is **holdings-scoped**: band assets (BTC/ETH) use OHLC bars; alt
136
+ holdings (e.g. HYPE) use quote snapshots when cached. The bridge auto-scopes
137
+ `assets` from your portfolio (symbols only upstream). See
138
+ [context-bundle.md#market-coverage](docs/context-bundle.md#market-coverage).
139
+
131
140
  See [mcp.md](docs/mcp.md) for arguments, pricing, and resources.
132
141
 
133
142
  ## Self-host and development
@@ -1,5 +1,8 @@
1
1
  # AllocContext
2
2
 
3
+ [![smithery badge](https://smithery.ai/badge/@negillett/alloc-context)](https://smithery.ai/server/@negillett/alloc-context)
4
+ [![Glama MCP server](https://glama.ai/mcp/servers/negillett/alloc-context/badges/score.svg)](https://glama.ai/mcp/servers/negillett/alloc-context)
5
+
3
6
  mcp-name: io.github.negillett/alloc-context
4
7
 
5
8
  **Portfolio-aware crypto context for agents** — discover holdings, market,
@@ -66,6 +69,7 @@ Not financial advice.
66
69
  | **Discovery** | [llms.txt](https://mcp.alloc-context.com/llms.txt), [x402 manifest](https://mcp.alloc-context.com/.well-known/x402.json) |
67
70
  | **Pricing** | **$0.02** cached context/math · **$0.05** live ingest or portfolio |
68
71
  | **Payment** | x402 on Base — USDC or EURC |
72
+ | **Market scope** | Holdings-scoped (BTC/ETH OHLC; alt quote snapshots); bridge auto-scopes from portfolio |
69
73
 
70
74
  Agents and wallets connect directly to the hosted endpoint — see
71
75
  [agent-integration.md](docs/agent-integration.md). The Cursor bridge above
@@ -84,6 +88,11 @@ combines local portfolio reads with this upstream for market context.
84
88
  | `check_allocation_bands` | Batch band checks for multiple target scenarios |
85
89
  | `get_portfolio_state` | Live NAV and holdings from Kraken or Coinbase |
86
90
 
91
+ Market context is **holdings-scoped**: band assets (BTC/ETH) use OHLC bars; alt
92
+ holdings (e.g. HYPE) use quote snapshots when cached. The bridge auto-scopes
93
+ `assets` from your portfolio (symbols only upstream). See
94
+ [context-bundle.md#market-coverage](docs/context-bundle.md#market-coverage).
95
+
87
96
  See [mcp.md](docs/mcp.md) for arguments, pricing, and resources.
88
97
 
89
98
  ## Self-host and development
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alloc-context
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Portfolio-aware crypto context for agents — holdings, market, optional allocation analysis
5
5
  License: Elastic-2.0
6
6
  Project-URL: Homepage, https://github.com/negillett/alloc-context
@@ -9,7 +9,7 @@ Project-URL: Repository, https://github.com/negillett/alloc-context
9
9
  Project-URL: Issues, https://github.com/negillett/alloc-context/issues
10
10
  Project-URL: Changelog, https://github.com/negillett/alloc-context/releases
11
11
  Project-URL: MCP Server, https://mcp.alloc-context.com/mcp
12
- Keywords: mcp,x402,bitcoin,ethereum,portfolio,allocation,agents,crypto,rebalance
12
+ Keywords: mcp,x402,bitcoin,ethereum,crypto,cryptocurrency,portfolio,allocation,holdings,rebalance,coinbase,kraken,agents
13
13
  Classifier: Development Status :: 4 - Beta
14
14
  Classifier: Intended Audience :: Developers
15
15
  Classifier: Programming Language :: Python :: 3
@@ -44,6 +44,9 @@ Dynamic: license-file
44
44
 
45
45
  # AllocContext
46
46
 
47
+ [![smithery badge](https://smithery.ai/badge/@negillett/alloc-context)](https://smithery.ai/server/@negillett/alloc-context)
48
+ [![Glama MCP server](https://glama.ai/mcp/servers/negillett/alloc-context/badges/score.svg)](https://glama.ai/mcp/servers/negillett/alloc-context)
49
+
47
50
  mcp-name: io.github.negillett/alloc-context
48
51
 
49
52
  **Portfolio-aware crypto context for agents** — discover holdings, market,
@@ -110,6 +113,7 @@ Not financial advice.
110
113
  | **Discovery** | [llms.txt](https://mcp.alloc-context.com/llms.txt), [x402 manifest](https://mcp.alloc-context.com/.well-known/x402.json) |
111
114
  | **Pricing** | **$0.02** cached context/math · **$0.05** live ingest or portfolio |
112
115
  | **Payment** | x402 on Base — USDC or EURC |
116
+ | **Market scope** | Holdings-scoped (BTC/ETH OHLC; alt quote snapshots); bridge auto-scopes from portfolio |
113
117
 
114
118
  Agents and wallets connect directly to the hosted endpoint — see
115
119
  [agent-integration.md](docs/agent-integration.md). The Cursor bridge above
@@ -128,6 +132,11 @@ combines local portfolio reads with this upstream for market context.
128
132
  | `check_allocation_bands` | Batch band checks for multiple target scenarios |
129
133
  | `get_portfolio_state` | Live NAV and holdings from Kraken or Coinbase |
130
134
 
135
+ Market context is **holdings-scoped**: band assets (BTC/ETH) use OHLC bars; alt
136
+ holdings (e.g. HYPE) use quote snapshots when cached. The bridge auto-scopes
137
+ `assets` from your portfolio (symbols only upstream). See
138
+ [context-bundle.md#market-coverage](docs/context-bundle.md#market-coverage).
139
+
131
140
  See [mcp.md](docs/mcp.md) for arguments, pricing, and resources.
132
141
 
133
142
  ## Self-host and development
@@ -62,6 +62,7 @@ alloccontext/mcp/bazaar.py
62
62
  alloccontext/mcp/bridge.py
63
63
  alloccontext/mcp/bridge_portfolio.py
64
64
  alloccontext/mcp/contracts.py
65
+ alloccontext/mcp/glama.py
65
66
  alloccontext/mcp/handlers.py
66
67
  alloccontext/mcp/http.py
67
68
  alloccontext/mcp/instructions.py
@@ -70,6 +71,7 @@ alloccontext/mcp/payment_middleware.py
70
71
  alloccontext/mcp/server.py
71
72
  alloccontext/mcp/setup.py
72
73
  alloccontext/mcp/staleness.py
74
+ alloccontext/mcp/tool_fields.py
73
75
  alloccontext/mcp/upstream.py
74
76
  alloccontext/mcp/validation.py
75
77
  alloccontext/mcp/x402_bazaar_dynamic.py
@@ -117,6 +119,7 @@ tests/test_etf.py
117
119
  tests/test_exchanges_config.py
118
120
  tests/test_fear_greed.py
119
121
  tests/test_fred.py
122
+ tests/test_glama_well_known.py
120
123
  tests/test_holdings_scoped_delta_regime.py
121
124
  tests/test_holdings_scoped_market.py
122
125
  tests/test_horizon.py
@@ -1,3 +1,3 @@
1
1
  """AllocContext — portfolio-aware crypto context for agents (MCP)."""
2
2
 
3
- __version__ = "0.2.0"
3
+ __version__ = "0.2.1"
@@ -43,28 +43,49 @@ SERVICE_TITLE = (
43
43
  # CDP Bazaar indexes service_name (≤32 chars) and up to five tags from payments.
44
44
  BAZAAR_SERVICE_NAME = "AllocContext portfolio MCP"
45
45
  SERVICE_TAGS = (
46
+ "crypto",
47
+ "cryptocurrency",
48
+ "bitcoin",
46
49
  "btc",
50
+ "ethereum",
47
51
  "eth",
48
52
  "holdings",
49
53
  "portfolio",
50
- "crypto",
51
- "bitcoin",
52
- "agent-tools",
53
- "mcp",
54
+ "allocation",
55
+ "rebalance",
54
56
  "sentiment",
55
57
  "macro",
56
- "rebalance",
57
- "allocation",
58
+ "coinbase",
59
+ "kraken",
60
+ "agent-tools",
61
+ "mcp",
62
+ "x402",
63
+ )
64
+ BAZAAR_INDEX_TAGS = (
65
+ "crypto",
66
+ "cryptocurrency",
67
+ "portfolio",
68
+ "holdings",
69
+ "btc",
58
70
  )
59
- BAZAAR_INDEX_TAGS = ("btc", "eth", "portfolio", "holdings", "mcp")
60
71
 
61
72
  DISCOVERY_KEYWORD_MARKERS = (
73
+ "crypto",
74
+ "cryptocurrency",
75
+ "digital assets",
76
+ "crypto portfolio",
62
77
  "portfolio allocation",
78
+ "portfolio context",
63
79
  "allocation drift",
64
80
  "rebalance plan",
65
81
  "fear and greed",
66
82
  "etf flows",
67
83
  "holdings",
84
+ "holdings-scoped",
85
+ "coinbase",
86
+ "kraken",
87
+ "market context",
88
+ "sentiment",
68
89
  )
69
90
 
70
91
  LISTING_DESCRIPTION = (
@@ -616,9 +637,12 @@ portfolio never persist on our servers.
616
637
 
617
638
  ## Search keywords
618
639
 
619
- bitcoin, ethereum, btc, eth, portfolio allocation, portfolio context, holdings,
620
- market context, sentiment, macro calendar, etf flows, allocation drift,
621
- rebalance plan, fear and greed, agent tools, mcp, x402
640
+ bitcoin, ethereum, btc, eth, crypto, cryptocurrency, digital assets, altcoin,
641
+ stablecoin, crypto portfolio, portfolio allocation, portfolio context, holdings,
642
+ holdings-scoped, coinbase, kraken, market context, market data, sentiment,
643
+ macro calendar, etf flows, allocation drift, allocation bands, rebalance plan,
644
+ fear and greed, fear greed index, nav, agent tools, ai agents, mcp, x402,
645
+ model context protocol, context bundle
622
646
 
623
647
  ## Examples
624
648
 
@@ -673,3 +697,32 @@ def build_well_known_x402(
673
697
  },
674
698
  },
675
699
  }
700
+
701
+
702
+ def build_mcp_server_card(*, version: str) -> dict[str, Any]:
703
+ """Smithery static server card (SEP-1649) — free metadata when POST /mcp is x402."""
704
+ return {
705
+ "serverInfo": {
706
+ "name": SERVICE_TITLE,
707
+ "version": version,
708
+ "description": LISTING_DESCRIPTION,
709
+ },
710
+ "authentication": {
711
+ "required": True,
712
+ "schemes": ["x402"],
713
+ "description": (
714
+ "x402 exact payment on Base mainnet (USDC or EURC) per tool call; "
715
+ "see /.well-known/x402.json for pricing."
716
+ ),
717
+ },
718
+ "tools": [
719
+ {
720
+ "name": spec["tool_name"],
721
+ "description": spec["description"],
722
+ "inputSchema": spec["input_schema"],
723
+ }
724
+ for spec in _MCP_TOOLS
725
+ ],
726
+ "resources": [],
727
+ "prompts": [],
728
+ }
@@ -0,0 +1,51 @@
1
+ """Glama connector well-known metadata (/.well-known/glama.json)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ GLAMA_CONNECTOR_SCHEMA = "https://glama.ai/mcp/schemas/connector.json"
11
+
12
+
13
+ def _repo_glama_json_path() -> Path:
14
+ # alloccontext/mcp/glama.py -> repo root (glama.json lives beside pyproject.toml)
15
+ return Path(__file__).resolve().parents[2] / "glama.json"
16
+
17
+
18
+ def build_glama_well_known() -> dict[str, Any]:
19
+ """Load glama.json for Glama ownership verification on the hosted domain."""
20
+ path = _repo_glama_json_path()
21
+ if not path.is_file():
22
+ msg = f"glama.json not found at {path}"
23
+ raise FileNotFoundError(msg)
24
+
25
+ data = json.loads(path.read_text(encoding="utf-8"))
26
+ maintainers: list[dict[str, str]] = []
27
+
28
+ email_override = os.environ.get("GLAMA_MAINTAINER_EMAIL", "").strip()
29
+ if email_override:
30
+ maintainers = [{"email": email_override}]
31
+ else:
32
+ connector_emails = data.get("connector_emails") or []
33
+ for entry in connector_emails:
34
+ if isinstance(entry, str) and "@" in entry:
35
+ maintainers.append({"email": entry})
36
+ if not maintainers:
37
+ raw = data.get("maintainers") or []
38
+ for entry in raw:
39
+ if isinstance(entry, dict) and entry.get("email"):
40
+ maintainers.append({"email": str(entry["email"])})
41
+ elif isinstance(entry, str) and "@" in entry:
42
+ maintainers.append({"email": entry})
43
+
44
+ if not maintainers:
45
+ msg = "glama.json maintainers must include at least one email"
46
+ raise ValueError(msg)
47
+
48
+ return {
49
+ "$schema": GLAMA_CONNECTOR_SCHEMA,
50
+ "maintainers": maintainers,
51
+ }
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import contextlib
4
+ import json
4
5
  import os
5
6
  from collections.abc import AsyncIterator
6
7
  from typing import Any
@@ -12,9 +13,11 @@ from starlette.routing import Mount, Route
12
13
 
13
14
  from alloccontext.mcp.bazaar import (
14
15
  build_llms_txt,
16
+ build_mcp_server_card,
15
17
  build_well_known_x402,
16
18
  resolve_public_base_url,
17
19
  )
20
+ from alloccontext.mcp.glama import build_glama_well_known
18
21
  from alloccontext.mcp.server import create_server
19
22
  from alloccontext.mcp.x402_config import (
20
23
  CDP_FACILITATOR_URL,
@@ -119,6 +122,26 @@ def _well_known_x402(settings: X402Settings) -> JSONResponse:
119
122
  return JSONResponse(payload)
120
123
 
121
124
 
125
+ def _well_known_mcp_server_card() -> JSONResponse:
126
+ public_base = resolve_public_base_url()
127
+ if not public_base:
128
+ return JSONResponse({"error": "discovery metadata unavailable"}, status_code=404)
129
+ from alloccontext import __version__
130
+
131
+ return JSONResponse(build_mcp_server_card(version=__version__))
132
+
133
+
134
+ def _well_known_glama() -> JSONResponse:
135
+ public_base = resolve_public_base_url()
136
+ if not public_base:
137
+ return JSONResponse({"error": "discovery metadata unavailable"}, status_code=404)
138
+ try:
139
+ payload = build_glama_well_known()
140
+ except (FileNotFoundError, ValueError, json.JSONDecodeError) as exc:
141
+ return JSONResponse({"error": str(exc)}, status_code=404)
142
+ return JSONResponse(payload)
143
+
144
+
122
145
  def _is_loopback_host(host: str) -> bool:
123
146
  normalized = host.strip().lower()
124
147
  return normalized in {"127.0.0.1", "localhost", "::1"}
@@ -154,6 +177,8 @@ def build_http_app(
154
177
  Route("/health", _make_health_handler(config_path)),
155
178
  Route("/llms.txt", lambda req: _llms_txt(settings)),
156
179
  Route("/.well-known/x402.json", lambda req: _well_known_x402(settings)),
180
+ Route("/.well-known/mcp/server-card.json", lambda req: _well_known_mcp_server_card()),
181
+ Route("/.well-known/glama.json", lambda req: _well_known_glama()),
157
182
  ]
158
183
 
159
184
  if not settings.enabled:
@@ -6,6 +6,26 @@ from typing import Any
6
6
  from alloccontext.config import load_config
7
7
  from alloccontext.mcp import handlers
8
8
  from alloccontext.mcp.instructions import PRODUCT_INSTRUCTIONS, REBALANCE_HINT_GUIDE
9
+ from alloccontext.mcp.tool_fields import (
10
+ AllocationPct,
11
+ ApiKey,
12
+ ApiSecret,
13
+ AsOf,
14
+ Assets,
15
+ BandDefault,
16
+ BandOptional,
17
+ CurrentAsOf,
18
+ Exchange,
19
+ ExchangeKrakenDefault,
20
+ Freshness,
21
+ MatchMode,
22
+ NavUsd,
23
+ OptionalTargetPct,
24
+ PriorAsOf,
25
+ Scenarios,
26
+ Scope,
27
+ TargetPct,
28
+ )
9
29
  from alloccontext.store.db import connect
10
30
 
11
31
 
@@ -78,19 +98,21 @@ def create_server(
78
98
  @mcp.tool(
79
99
  name="get_context_bundle",
80
100
  description=(
81
- "Full ContextBundle JSON: portfolio holdings, market, sentiment, "
82
- "macro, regime hints, and delta vs the prior saved snapshot. Optional "
83
- "assets filter (default BTC, ETH). Optional target_pct and band attach "
84
- "allocation_analysis (opt-in drift math). freshness=cached uses the "
85
- "local ingest DB; freshness=live runs ingest first."
101
+ "Return the full read-only ContextBundle JSON: portfolio holdings, "
102
+ "market, sentiment, macro, regime hints, and delta vs the prior saved "
103
+ "snapshot. Use get_market_context for market-only; use get_context_at "
104
+ "for a historical snapshot; use get_context_delta to compare two times. "
105
+ "Optional target_pct and band attach allocation_analysis (opt-in drift "
106
+ "math). freshness=cached uses the local ingest DB; freshness=live runs "
107
+ "ingest first (may add latency; needs ingest API keys on the host)."
86
108
  ),
87
109
  )
88
110
  def get_context_bundle(
89
- scope: str = "daily",
90
- freshness: str = "cached",
91
- assets: list[str] | None = None,
92
- target_pct: dict[str, float] | None = None,
93
- band: float | None = None,
111
+ scope: Scope = "daily",
112
+ freshness: Freshness = "cached",
113
+ assets: Assets = None,
114
+ target_pct: OptionalTargetPct = None,
115
+ band: BandOptional = None,
94
116
  ) -> dict[str, Any]:
95
117
  """Return the full deterministic context bundle for daily or weekly scope."""
96
118
  validated_scope = handlers.validate_scope(scope)
@@ -112,16 +134,17 @@ def create_server(
112
134
  @mcp.tool(
113
135
  name="get_market_context",
114
136
  description=(
115
- "Fused market backdrop: sentiment (Fear & Greed, Kalshi), macro events, "
116
- "FRED indicators, ETF flows, and market breadth. Optional assets filter "
117
- "(default BTC, ETH). freshness=cached uses the local ingest DB; "
137
+ "Return read-only fused market backdrop: sentiment (Fear & Greed, "
138
+ "Kalshi), macro events, FRED indicators, ETF flows, and market breadth "
139
+ "(no portfolio holdings). Use get_context_bundle when you also need "
140
+ "holdings, delta, or regime. freshness=cached uses the local ingest DB; "
118
141
  "freshness=live runs ingest first (requires ingest API keys on the host)."
119
142
  ),
120
143
  )
121
144
  def get_market_context(
122
- scope: str = "daily",
123
- freshness: str = "cached",
124
- assets: list[str] | None = None,
145
+ scope: Scope = "daily",
146
+ freshness: Freshness = "cached",
147
+ assets: Assets = None,
125
148
  ) -> dict[str, Any]:
126
149
  """Return ContextBundle subset for daily or weekly scope."""
127
150
  validated_scope = handlers.validate_scope(scope)
@@ -141,18 +164,22 @@ def create_server(
141
164
  @mcp.tool(
142
165
  name="get_rebalance_plan",
143
166
  description=(
144
- "USD deltas and exchange-style move lines to reach a target BTC/ETH/CASH "
145
- "split. Requires allocation_pct, target_pct, and nav_usd. Optional band "
146
- "returns a band_check block alongside the plan. exchange=kraken|coinbase "
147
- "adjusts move wording."
167
+ "Compute read-only USD deltas and suggested exchange move lines to "
168
+ "reach a BTC/ETH/CASH target split. Pure math no exchange API calls. "
169
+ "Requires allocation_pct, target_pct, and nav_usd. Use "
170
+ "get_portfolio_state or get_context_bundle when you need live or cached "
171
+ "weights first. Use check_allocation_band for pass/fail drift only; use "
172
+ "check_allocation_bands for multiple scenarios. Optional band adds a "
173
+ "band_check block alongside the plan. exchange=kraken|coinbase adjusts "
174
+ "move wording only."
148
175
  ),
149
176
  )
150
177
  def get_rebalance_plan(
151
- allocation_pct: dict[str, float],
152
- target_pct: dict[str, float],
153
- nav_usd: float,
154
- exchange: str = "kraken",
155
- band: float | None = None,
178
+ allocation_pct: AllocationPct,
179
+ target_pct: TargetPct,
180
+ nav_usd: NavUsd,
181
+ exchange: ExchangeKrakenDefault = "kraken",
182
+ band: BandOptional = None,
156
183
  ) -> dict[str, Any]:
157
184
  """Compute rebalance plan from current allocation and NAV."""
158
185
  return handlers.get_rebalance_plan(
@@ -166,18 +193,21 @@ def create_server(
166
193
  @mcp.tool(
167
194
  name="get_portfolio_state",
168
195
  description=(
169
- "Live portfolio NAV, holdings[], and band weights from read-only "
170
- "exchange credentials passed in the request. Optional target_pct "
171
- "attaches allocation_analysis. Credentials are never stored. "
172
- "Supports kraken and coinbase."
196
+ "Fetch live read-only portfolio NAV, holdings[], and band weights from "
197
+ "Kraken or Coinbase credentials passed in this call (never stored). "
198
+ "Requires exchange, api_key, and api_secret. Use get_context_bundle "
199
+ "for cached market and history without exchange keys. Optional "
200
+ "target_pct attaches allocation_analysis; optional band sets drift "
201
+ "width when target_pct is supplied. Returns an error payload on invalid "
202
+ "credentials or unsupported exchange — no side effects."
173
203
  ),
174
204
  )
175
205
  def get_portfolio_state(
176
- exchange: str,
177
- api_key: str,
178
- api_secret: str,
179
- target_pct: dict[str, float] | None = None,
180
- band: float | None = None,
206
+ exchange: Exchange,
207
+ api_key: ApiKey,
208
+ api_secret: ApiSecret,
209
+ target_pct: OptionalTargetPct = None,
210
+ band: BandOptional = None,
181
211
  ) -> dict[str, Any]:
182
212
  """Fetch live portfolio state using caller-supplied read-only API keys."""
183
213
  return handlers.get_portfolio_state(
@@ -192,16 +222,19 @@ def create_server(
192
222
  @mcp.tool(
193
223
  name="check_allocation_band",
194
224
  description=(
195
- "Check whether BTC/ETH/CASH band weights are outside a drift band vs "
196
- "target_pct and return hint (within_band, consider_rebalance, etc.). "
197
- "All three inputs are required. For bundle drift, pass target_pct on "
198
- "get_context_bundle to attach allocation_analysis."
225
+ "Read-only drift check: are BTC/ETH/CASH band weights outside the "
226
+ "drift band vs target_pct? Returns rebalance_hint (within_band, "
227
+ "consider_rebalance, etc.). Requires allocation_pct and target_pct; "
228
+ "band defaults to 0.15. Single-scenario only — use check_allocation_bands "
229
+ "for multiple targets in one call. Use get_rebalance_plan when you need "
230
+ "USD move lines, not just a hint. For bundle drift, pass target_pct on "
231
+ "get_context_bundle to attach allocation_analysis instead."
199
232
  ),
200
233
  )
201
234
  def check_allocation_band(
202
- allocation_pct: dict[str, float],
203
- target_pct: dict[str, float],
204
- band: float = 0.15,
235
+ allocation_pct: AllocationPct,
236
+ target_pct: TargetPct,
237
+ band: BandDefault = 0.15,
205
238
  ) -> dict[str, Any]:
206
239
  """Evaluate allocation drift against band width (default 0.15 = 15%)."""
207
240
  return handlers.check_band(allocation_pct, target_pct, band)
@@ -209,18 +242,20 @@ def create_server(
209
242
  @mcp.tool(
210
243
  name="get_context_at",
211
244
  description=(
212
- "Load a saved ContextBundle snapshot from ingest history. "
213
- "as_of is an ISO timestamp; match=at_or_before returns the latest "
214
- "snapshot on or before that time."
245
+ "Load a read-only ContextBundle snapshot from ingest history at a "
246
+ "point in time. Use get_context_bundle for the latest snapshot; use "
247
+ "get_context_delta to compare two timestamps. Read-only; returns an "
248
+ "unavailable payload when no snapshot matches as_of and match. Optional "
249
+ "target_pct and band attach allocation_analysis to the historical bundle."
215
250
  ),
216
251
  )
217
252
  def get_context_at(
218
- as_of: str,
219
- scope: str = "daily",
220
- match: str = "at_or_before",
221
- assets: list[str] | None = None,
222
- target_pct: dict[str, float] | None = None,
223
- band: float | None = None,
253
+ as_of: AsOf,
254
+ scope: Scope = "daily",
255
+ match: MatchMode = "at_or_before",
256
+ assets: Assets = None,
257
+ target_pct: OptionalTargetPct = None,
258
+ band: BandOptional = None,
224
259
  ) -> dict[str, Any]:
225
260
  validated_scope = handlers.validate_scope(scope)
226
261
  if match not in ("exact", "at_or_before"):
@@ -243,15 +278,18 @@ def create_server(
243
278
  @mcp.tool(
244
279
  name="get_context_delta",
245
280
  description=(
246
- "Compare two ContextBundle snapshots and return notable_shifts. "
247
- "prior_as_of is required; omit current_as_of for latest live bundle."
281
+ "Compare two read-only ContextBundle snapshots and return "
282
+ "notable_shifts between them. Requires prior_as_of; omit current_as_of "
283
+ "to diff against the latest live bundle. Use get_context_at to load one "
284
+ "snapshot without diffing. Read-only; no ingest unless you combine with "
285
+ "a live current_as_of path."
248
286
  ),
249
287
  )
250
288
  def get_context_delta(
251
- prior_as_of: str,
252
- scope: str = "daily",
253
- current_as_of: str | None = None,
254
- assets: list[str] | None = None,
289
+ prior_as_of: PriorAsOf,
290
+ scope: Scope = "daily",
291
+ current_as_of: CurrentAsOf = None,
292
+ assets: Assets = None,
255
293
  ) -> dict[str, Any]:
256
294
  validated_scope = handlers.validate_scope(scope)
257
295
  conn = connect(config.paths.db)
@@ -270,14 +308,16 @@ def create_server(
270
308
  @mcp.tool(
271
309
  name="check_allocation_bands",
272
310
  description=(
273
- "Evaluate allocation drift against multiple target_pct/band "
274
- "scenarios in one call. Each scenario needs target_pct; optional "
275
- "name and band (default 0.15)."
311
+ "Read-only batch drift check: evaluate allocation_pct against multiple "
312
+ "target_pct/band scenarios in one call. Each scenario requires "
313
+ "target_pct; optional name and band (default 0.15). Use "
314
+ "check_allocation_band for a single target. Use get_rebalance_plan when "
315
+ "you need USD move lines after identifying drift."
276
316
  ),
277
317
  )
278
318
  def check_allocation_bands(
279
- allocation_pct: dict[str, float],
280
- scenarios: list[dict[str, Any]],
319
+ allocation_pct: AllocationPct,
320
+ scenarios: Scenarios,
281
321
  ) -> dict[str, Any]:
282
322
  return handlers.check_allocation_bands(allocation_pct, scenarios)
283
323