alloc-context 0.2.1__tar.gz → 0.2.2__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 (167) hide show
  1. {alloc_context-0.2.1 → alloc_context-0.2.2}/PKG-INFO +2 -2
  2. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloc_context.egg-info/PKG-INFO +2 -2
  3. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloc_context.egg-info/SOURCES.txt +2 -0
  4. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/__init__.py +1 -1
  5. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/bazaar.py +88 -172
  6. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/server.py +23 -0
  7. alloc_context-0.2.2/alloccontext/mcp/tool_catalog.py +311 -0
  8. {alloc_context-0.2.1 → alloc_context-0.2.2}/pyproject.toml +2 -2
  9. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_mcp_bazaar.py +6 -0
  10. alloc_context-0.2.2/tests/test_tool_catalog.py +65 -0
  11. {alloc_context-0.2.1 → alloc_context-0.2.2}/LICENSE +0 -0
  12. {alloc_context-0.2.1 → alloc_context-0.2.2}/README.md +0 -0
  13. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloc_context.egg-info/dependency_links.txt +0 -0
  14. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloc_context.egg-info/entry_points.txt +0 -0
  15. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloc_context.egg-info/requires.txt +0 -0
  16. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloc_context.egg-info/top_level.txt +0 -0
  17. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/__main__.py +0 -0
  18. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/config.py +0 -0
  19. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/constants.py +0 -0
  20. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/horizon.py +0 -0
  21. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/__init__.py +0 -0
  22. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/alt_quote_registry.py +0 -0
  23. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/alt_quote_store.py +0 -0
  24. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/alt_quotes.py +0 -0
  25. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/asset_registry.py +0 -0
  26. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/cf_benchmarks.py +0 -0
  27. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/cf_history.py +0 -0
  28. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/coinbase_client.py +0 -0
  29. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/coinbase_portfolio.py +0 -0
  30. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/coingecko.py +0 -0
  31. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/coinmarketcap.py +0 -0
  32. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/env_keys.py +0 -0
  33. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/etf_flows.py +0 -0
  34. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/exchange/__init__.py +0 -0
  35. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/exchange/coinbase_adapter.py +0 -0
  36. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/exchange/kraken_adapter.py +0 -0
  37. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/exchange/live.py +0 -0
  38. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/exchange/portfolio.py +0 -0
  39. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/exchange/registry.py +0 -0
  40. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/exchange/types.py +0 -0
  41. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/exchange_http.py +0 -0
  42. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/fear_greed.py +0 -0
  43. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/fred.py +0 -0
  44. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/http_errors.py +0 -0
  45. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/kalshi.py +0 -0
  46. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/kalshi_api.py +0 -0
  47. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/kalshi_client.py +0 -0
  48. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/kalshi_files.py +0 -0
  49. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/kalshi_state.py +0 -0
  50. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/kraken_client.py +0 -0
  51. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/kraken_portfolio.py +0 -0
  52. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/macro_calendar.py +0 -0
  53. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/macro_normalize.py +0 -0
  54. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/market_snapshots.py +0 -0
  55. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/outcome.py +0 -0
  56. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/parse_helpers.py +0 -0
  57. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/portfolio_holdings.py +0 -0
  58. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/quote_resolver.py +0 -0
  59. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/ingest/runner.py +0 -0
  60. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/__init__.py +0 -0
  61. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/assets.py +0 -0
  62. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/bridge.py +0 -0
  63. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/bridge_portfolio.py +0 -0
  64. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/contracts.py +0 -0
  65. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/glama.py +0 -0
  66. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/handlers.py +0 -0
  67. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/http.py +0 -0
  68. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/instructions.py +0 -0
  69. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/payer.py +0 -0
  70. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/payment_middleware.py +0 -0
  71. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/setup.py +0 -0
  72. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/staleness.py +0 -0
  73. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/tool_fields.py +0 -0
  74. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/upstream.py +0 -0
  75. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/validation.py +0 -0
  76. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/x402_bazaar_dynamic.py +0 -0
  77. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/x402_config.py +0 -0
  78. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/x402_pricing.py +0 -0
  79. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/mcp/x402_stables.py +0 -0
  80. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/rollup/__init__.py +0 -0
  81. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/rollup/allocation_analysis.py +0 -0
  82. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/rollup/band.py +0 -0
  83. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/rollup/breadth.py +0 -0
  84. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/rollup/cf_math.py +0 -0
  85. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/rollup/cluster.py +0 -0
  86. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/rollup/cluster_config.py +0 -0
  87. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/rollup/comparison.py +0 -0
  88. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/rollup/context.py +0 -0
  89. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/rollup/delta.py +0 -0
  90. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/rollup/etf.py +0 -0
  91. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/rollup/fear_greed.py +0 -0
  92. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/rollup/macro.py +0 -0
  93. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/rollup/portfolio.py +0 -0
  94. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/rollup/portfolio_payload.py +0 -0
  95. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/rollup/rebalance.py +0 -0
  96. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/rollup/regime.py +0 -0
  97. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/rollup/sentiment.py +0 -0
  98. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/rollup/snapshots.py +0 -0
  99. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/rollup/tape.py +0 -0
  100. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/status_report.py +0 -0
  101. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/store/__init__.py +0 -0
  102. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/store/db.py +0 -0
  103. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/store/jsonutil.py +0 -0
  104. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/store/meta.py +0 -0
  105. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/store/retention.py +0 -0
  106. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/store/status.py +0 -0
  107. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/timeutil.py +0 -0
  108. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/user_config.py +0 -0
  109. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/x402_production_check.py +0 -0
  110. {alloc_context-0.2.1 → alloc_context-0.2.2}/alloccontext/x402_smoke_redact.py +0 -0
  111. {alloc_context-0.2.1 → alloc_context-0.2.2}/setup.cfg +0 -0
  112. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_backup_sqlite.py +0 -0
  113. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_bridge.py +0 -0
  114. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_bridge_portfolio.py +0 -0
  115. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_bump_version.py +0 -0
  116. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_coinbase_portfolio.py +0 -0
  117. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_config_cli.py +0 -0
  118. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_context_bundle_schema.py +0 -0
  119. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_context_snapshots.py +0 -0
  120. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_db_schema.py +0 -0
  121. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_deploy.py +0 -0
  122. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_dev_stack.py +0 -0
  123. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_etf.py +0 -0
  124. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_exchanges_config.py +0 -0
  125. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_fear_greed.py +0 -0
  126. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_fred.py +0 -0
  127. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_glama_well_known.py +0 -0
  128. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_holdings_scoped_delta_regime.py +0 -0
  129. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_holdings_scoped_market.py +0 -0
  130. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_horizon.py +0 -0
  131. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_ingest_outcome.py +0 -0
  132. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_ingest_runner.py +0 -0
  133. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_ingest_store_integration.py +0 -0
  134. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_kalshi_api.py +0 -0
  135. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_kraken_portfolio.py +0 -0
  136. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_live_ingest_handlers.py +0 -0
  137. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_macro.py +0 -0
  138. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_market_breadth.py +0 -0
  139. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_mcp_assets_regime.py +0 -0
  140. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_mcp_contracts.py +0 -0
  141. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_mcp_data_staleness.py +0 -0
  142. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_mcp_handlers.py +0 -0
  143. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_mcp_health.py +0 -0
  144. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_mcp_http_lifecycle.py +0 -0
  145. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_mcp_live_portfolio.py +0 -0
  146. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_mcp_server.py +0 -0
  147. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_mcp_validation.py +0 -0
  148. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_mcp_x402.py +0 -0
  149. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_mcp_x402_http.py +0 -0
  150. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_mcp_x402_pricing.py +0 -0
  151. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_mcp_x402_stables.py +0 -0
  152. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_payer.py +0 -0
  153. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_portfolio_holdings.py +0 -0
  154. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_quote_resolver.py +0 -0
  155. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_rebalance.py +0 -0
  156. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_rollup.py +0 -0
  157. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_script_runtime.py +0 -0
  158. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_security_hardening.py +0 -0
  159. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_server_json.py +0 -0
  160. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_setup.py +0 -0
  161. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_snapshots_and_delta.py +0 -0
  162. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_status_report.py +0 -0
  163. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_user_config.py +0 -0
  164. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_workflows.py +0 -0
  165. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_x402_bazaar_dynamic.py +0 -0
  166. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_x402_production_check.py +0 -0
  167. {alloc_context-0.2.1 → alloc_context-0.2.2}/tests/test_x402_smoke_redact.py +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alloc-context
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Portfolio-aware crypto context for agents — holdings, market, optional allocation analysis
5
5
  License: Elastic-2.0
6
- Project-URL: Homepage, https://github.com/negillett/alloc-context
6
+ Project-URL: Homepage, https://mcp.alloc-context.com/llms.txt
7
7
  Project-URL: Documentation, https://github.com/negillett/alloc-context/blob/main/docs/agent-integration.md
8
8
  Project-URL: Repository, https://github.com/negillett/alloc-context
9
9
  Project-URL: Issues, https://github.com/negillett/alloc-context/issues
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alloc-context
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Portfolio-aware crypto context for agents — holdings, market, optional allocation analysis
5
5
  License: Elastic-2.0
6
- Project-URL: Homepage, https://github.com/negillett/alloc-context
6
+ Project-URL: Homepage, https://mcp.alloc-context.com/llms.txt
7
7
  Project-URL: Documentation, https://github.com/negillett/alloc-context/blob/main/docs/agent-integration.md
8
8
  Project-URL: Repository, https://github.com/negillett/alloc-context
9
9
  Project-URL: Issues, https://github.com/negillett/alloc-context/issues
@@ -71,6 +71,7 @@ alloccontext/mcp/payment_middleware.py
71
71
  alloccontext/mcp/server.py
72
72
  alloccontext/mcp/setup.py
73
73
  alloccontext/mcp/staleness.py
74
+ alloccontext/mcp/tool_catalog.py
74
75
  alloccontext/mcp/tool_fields.py
75
76
  alloccontext/mcp/upstream.py
76
77
  alloccontext/mcp/validation.py
@@ -156,6 +157,7 @@ tests/test_server_json.py
156
157
  tests/test_setup.py
157
158
  tests/test_snapshots_and_delta.py
158
159
  tests/test_status_report.py
160
+ tests/test_tool_catalog.py
159
161
  tests/test_user_config.py
160
162
  tests/test_workflows.py
161
163
  tests/test_x402_bazaar_dynamic.py
@@ -1,3 +1,3 @@
1
1
  """AllocContext — portfolio-aware crypto context for agents (MCP)."""
2
2
 
3
- __version__ = "0.2.1"
3
+ __version__ = "0.2.2"
@@ -10,6 +10,24 @@ from x402.extensions.bazaar import (
10
10
  declare_mcp_discovery_extension,
11
11
  )
12
12
 
13
+ from alloccontext.mcp.tool_catalog import (
14
+ ASSET_FILTER_SCHEMA,
15
+ AS_OF_SCHEMA,
16
+ BAND_SCHEMA,
17
+ CURRENT_AS_OF_SCHEMA,
18
+ FRESHNESS_SCHEMA,
19
+ MATCH_SCHEMA,
20
+ MCP_SERVER_PROMPTS,
21
+ MCP_SERVER_RESOURCES,
22
+ OPTIONAL_TARGET_PCT_SCHEMA,
23
+ PRIOR_AS_OF_SCHEMA,
24
+ SCENARIOS_SCHEMA,
25
+ SCOPE_SCHEMA,
26
+ TARGET_PCT_SCHEMA,
27
+ allocation_pct_schema,
28
+ server_card_tool_entry,
29
+ )
30
+
13
31
  SERVICE_NAME = "AllocContext"
14
32
  OFFICIAL_HOSTED_MCP_URL = "https://mcp.alloc-context.com/mcp"
15
33
  USE_DOCS_PATH = "docs/USE.md"
@@ -98,54 +116,22 @@ LISTING_DESCRIPTION = (
98
116
  f"MCP at {OFFICIAL_HOSTED_MCP_URL} — see {USE_DOCS_PATH}."
99
117
  )
100
118
 
101
- _ASSET_FILTER_SCHEMA = {
102
- "type": "array",
103
- "items": {"type": "string", "enum": ["BTC", "ETH", "CASH"]},
104
- "description": "Subset market and ETF fields (default BTC and ETH).",
105
- }
106
-
107
- _TARGET_PCT_SCHEMA = {
108
- "type": "object",
109
- "description": "Target weights keyed by BTC, ETH, CASH.",
110
- "properties": {
111
- "BTC": {"type": "number"},
112
- "ETH": {"type": "number"},
113
- "CASH": {"type": "number"},
114
- },
115
- "required": ["BTC", "ETH", "CASH"],
116
- }
117
-
118
- _BAND_SCHEMA = {
119
- "type": "number",
120
- "description": "Drift band width (for example 0.15 = 15%).",
121
- }
122
-
123
119
  _MCP_TOOLS: tuple[dict[str, Any], ...] = (
124
120
  {
125
121
  "tool_name": "get_market_context",
126
122
  "description": (
127
- "Fused market backdrop for portfolio context: Fear & Greed, Kalshi "
128
- "sentiment, macro calendar, FRED indicators, ETF flows, and breadth. "
129
- "Use freshness=cached for hosted cache; freshness=live runs ingest "
130
- "first (requires ingest API keys on the host)."
123
+ "Return read-only fused market backdrop for crypto portfolio context: "
124
+ "sentiment (Fear & Greed, Kalshi), macro events, FRED indicators, ETF "
125
+ "flows, and market breadth no portfolio holdings. Use "
126
+ "get_context_bundle when you also need holdings, delta, or regime. "
127
+ "freshness=cached reads the ingest DB; freshness=live runs ingest first."
131
128
  ),
132
129
  "input_schema": {
133
130
  "type": "object",
134
131
  "properties": {
135
- "scope": {
136
- "type": "string",
137
- "enum": ["daily", "weekly"],
138
- "description": "Rollup horizon for macro and context bundle.",
139
- },
140
- "freshness": {
141
- "type": "string",
142
- "enum": ["cached", "live"],
143
- "description": (
144
- "cached reads the ingest DB; live runs ingest first "
145
- "(requires ingest API keys on the host)."
146
- ),
147
- },
148
- "assets": _ASSET_FILTER_SCHEMA,
132
+ "scope": SCOPE_SCHEMA,
133
+ "freshness": FRESHNESS_SCHEMA,
134
+ "assets": ASSET_FILTER_SCHEMA,
149
135
  },
150
136
  },
151
137
  "example": {"scope": "daily", "freshness": "cached", "assets": ["BTC", "ETH"]},
@@ -163,25 +149,20 @@ _MCP_TOOLS: tuple[dict[str, Any], ...] = (
163
149
  {
164
150
  "tool_name": "get_context_bundle",
165
151
  "description": (
166
- "Full ContextBundle JSON: portfolio holdings and band weights, "
167
- "market, sentiment, macro, regime hints, and delta vs the prior "
168
- "saved snapshot. Optional target_pct and band enable "
169
- "allocation_analysis (opt-in drift math)."
152
+ "Return the full read-only ContextBundle JSON: portfolio holdings, "
153
+ "market, sentiment, macro, regime hints, and delta vs the prior saved "
154
+ "snapshot. Use get_market_context for market-only; use get_context_at "
155
+ "for a historical snapshot. Optional target_pct and band attach "
156
+ "allocation_analysis drift math."
170
157
  ),
171
158
  "input_schema": {
172
159
  "type": "object",
173
160
  "properties": {
174
- "scope": {
175
- "type": "string",
176
- "enum": ["daily", "weekly"],
177
- },
178
- "freshness": {
179
- "type": "string",
180
- "enum": ["cached", "live"],
181
- },
182
- "assets": _ASSET_FILTER_SCHEMA,
183
- "target_pct": _TARGET_PCT_SCHEMA,
184
- "band": _BAND_SCHEMA,
161
+ "scope": SCOPE_SCHEMA,
162
+ "freshness": FRESHNESS_SCHEMA,
163
+ "assets": ASSET_FILTER_SCHEMA,
164
+ "target_pct": TARGET_PCT_SCHEMA,
165
+ "band": BAND_SCHEMA,
185
166
  },
186
167
  },
187
168
  "example": {
@@ -211,45 +192,28 @@ _MCP_TOOLS: tuple[dict[str, Any], ...] = (
211
192
  {
212
193
  "tool_name": "get_rebalance_plan",
213
194
  "description": (
214
- "Compute USD deltas and exchange-style move lines toward a target "
215
- "BTC/ETH/CASH allocation from current band weights and NAV. Pure "
216
- "math explicit inputs required."
195
+ "Compute read-only USD deltas and suggested exchange move lines to "
196
+ "reach a BTC/ETH/CASH target split. Pure math no exchange API calls. "
197
+ "Requires allocation_pct, target_pct, and nav_usd. Use get_portfolio_state "
198
+ "or get_context_bundle when you need live weights first."
217
199
  ),
218
200
  "input_schema": {
219
201
  "type": "object",
220
202
  "properties": {
221
- "allocation_pct": {
222
- "type": "object",
223
- "description": "Current weights keyed by BTC, ETH, CASH.",
224
- "properties": {
225
- "BTC": {"type": "number"},
226
- "ETH": {"type": "number"},
227
- "CASH": {"type": "number"},
228
- },
229
- "required": ["BTC", "ETH", "CASH"],
230
- },
231
- "target_pct": {
232
- "type": "object",
233
- "description": "Target weights keyed by BTC, ETH, CASH.",
234
- "properties": {
235
- "BTC": {"type": "number"},
236
- "ETH": {"type": "number"},
237
- "CASH": {"type": "number"},
238
- },
239
- "required": ["BTC", "ETH", "CASH"],
240
- },
203
+ "allocation_pct": allocation_pct_schema(role="Current"),
204
+ "target_pct": allocation_pct_schema(role="Target"),
241
205
  "nav_usd": {
242
206
  "type": "number",
243
- "description": "Portfolio NAV in USD.",
207
+ "description": "Portfolio net asset value in USD.",
244
208
  },
245
209
  "exchange": {
246
210
  "type": "string",
247
211
  "enum": ["kraken", "coinbase"],
248
212
  "description": (
249
- "Exchange-specific move wording (default kraken)."
213
+ "Spot exchange for move wording: kraken (default) or coinbase."
250
214
  ),
251
215
  },
252
- "band": _BAND_SCHEMA,
216
+ "band": BAND_SCHEMA,
253
217
  },
254
218
  "required": ["allocation_pct", "target_pct", "nav_usd"],
255
219
  },
@@ -272,10 +236,10 @@ _MCP_TOOLS: tuple[dict[str, Any], ...] = (
272
236
  {
273
237
  "tool_name": "get_portfolio_state",
274
238
  "description": (
275
- "Live portfolio read: NAV, holdings[], band weights, and optional "
276
- "allocation_analysis when target_pct is supplied. Pass read-only "
277
- "Kraken or Coinbase credentials in the request; never stored "
278
- "server-side."
239
+ "Fetch live read-only portfolio NAV, holdings[], and band weights from "
240
+ "Kraken or Coinbase credentials passed in this call (never stored). "
241
+ "Requires exchange, api_key, and api_secret. Returns available=false "
242
+ "with reason on invalid credentials — no side effects."
279
243
  ),
280
244
  "input_schema": {
281
245
  "type": "object",
@@ -283,11 +247,13 @@ _MCP_TOOLS: tuple[dict[str, Any], ...] = (
283
247
  "exchange": {
284
248
  "type": "string",
285
249
  "enum": ["kraken", "coinbase"],
286
- "description": "Spot exchange to query.",
250
+ "description": "Spot exchange to query: kraken or coinbase.",
287
251
  },
288
252
  "api_key": {
289
253
  "type": "string",
290
- "description": "Read-only API key (CDP key name for Coinbase).",
254
+ "description": (
255
+ "Read-only exchange API key (Coinbase CDP key name)."
256
+ ),
291
257
  },
292
258
  "api_secret": {
293
259
  "type": "string",
@@ -295,19 +261,8 @@ _MCP_TOOLS: tuple[dict[str, Any], ...] = (
295
261
  "Read-only API secret (Kraken base64 secret or Coinbase EC PEM)."
296
262
  ),
297
263
  },
298
- "target_pct": {
299
- "type": "object",
300
- "description": "Optional target weights for allocation_analysis.",
301
- "properties": {
302
- "BTC": {"type": "number"},
303
- "ETH": {"type": "number"},
304
- "CASH": {"type": "number"},
305
- },
306
- },
307
- "band": {
308
- "type": "number",
309
- "description": "Drift band width when target_pct is supplied (e.g. 0.15).",
310
- },
264
+ "target_pct": OPTIONAL_TARGET_PCT_SCHEMA,
265
+ "band": BAND_SCHEMA,
311
266
  },
312
267
  "required": ["exchange", "api_key", "api_secret"],
313
268
  },
@@ -330,34 +285,19 @@ _MCP_TOOLS: tuple[dict[str, Any], ...] = (
330
285
  {
331
286
  "tool_name": "check_allocation_band",
332
287
  "description": (
333
- "Check whether band weights (BTC/ETH/CASH) are outside a drift band "
334
- "vs target and return hint (within_band, consider_rebalance, etc.). "
335
- "Explicit inputs required."
288
+ "Check read-only drift: are BTC/ETH/CASH band weights outside the drift "
289
+ "band vs target_pct? Returns rebalance_hint (within_band, "
290
+ "consider_rebalance, etc.). Single scenario — use check_allocation_bands "
291
+ "for multiple targets. Use get_rebalance_plan when you need USD move lines."
336
292
  ),
337
293
  "input_schema": {
338
294
  "type": "object",
339
295
  "properties": {
340
- "allocation_pct": {
341
- "type": "object",
342
- "properties": {
343
- "BTC": {"type": "number"},
344
- "ETH": {"type": "number"},
345
- "CASH": {"type": "number"},
346
- },
347
- "required": ["BTC", "ETH", "CASH"],
348
- },
349
- "target_pct": {
350
- "type": "object",
351
- "properties": {
352
- "BTC": {"type": "number"},
353
- "ETH": {"type": "number"},
354
- "CASH": {"type": "number"},
355
- },
356
- "required": ["BTC", "ETH", "CASH"],
357
- },
296
+ "allocation_pct": allocation_pct_schema(role="Current"),
297
+ "target_pct": allocation_pct_schema(role="Target"),
358
298
  "band": {
359
299
  "type": "number",
360
- "description": "Drift band width (default 0.15 = 15%).",
300
+ "description": "Drift band width as a fraction (default 0.15 = 15%).",
361
301
  },
362
302
  },
363
303
  "required": ["allocation_pct", "target_pct"],
@@ -378,16 +318,20 @@ _MCP_TOOLS: tuple[dict[str, Any], ...] = (
378
318
  {
379
319
  "tool_name": "get_context_at",
380
320
  "description": (
381
- "Load a saved ContextBundle snapshot from ingest history by ISO "
382
- "timestamp (match exact or at_or_before)."
321
+ "Load a read-only ContextBundle snapshot from ingest history at a point "
322
+ "in time. Use get_context_bundle for the latest snapshot; use "
323
+ "get_context_delta to compare two timestamps. Returns unavailable when "
324
+ "no snapshot matches as_of and match."
383
325
  ),
384
326
  "input_schema": {
385
327
  "type": "object",
386
328
  "properties": {
387
- "as_of": {"type": "string", "description": "ISO timestamp."},
388
- "scope": {"type": "string", "enum": ["daily", "weekly"]},
389
- "match": {"type": "string", "enum": ["exact", "at_or_before"]},
390
- "assets": _ASSET_FILTER_SCHEMA,
329
+ "as_of": AS_OF_SCHEMA,
330
+ "scope": SCOPE_SCHEMA,
331
+ "match": MATCH_SCHEMA,
332
+ "assets": ASSET_FILTER_SCHEMA,
333
+ "target_pct": TARGET_PCT_SCHEMA,
334
+ "band": BAND_SCHEMA,
391
335
  },
392
336
  "required": ["as_of"],
393
337
  },
@@ -406,15 +350,18 @@ _MCP_TOOLS: tuple[dict[str, Any], ...] = (
406
350
  {
407
351
  "tool_name": "get_context_delta",
408
352
  "description": (
409
- "Compare two ContextBundle snapshots and return notable_shifts."
353
+ "Compare two read-only ContextBundle snapshots and return notable_shifts "
354
+ "between them. Requires prior_as_of; omit current_as_of to diff against "
355
+ "the latest live bundle. Use get_context_at to load one snapshot without "
356
+ "diffing."
410
357
  ),
411
358
  "input_schema": {
412
359
  "type": "object",
413
360
  "properties": {
414
- "prior_as_of": {"type": "string"},
415
- "scope": {"type": "string", "enum": ["daily", "weekly"]},
416
- "current_as_of": {"type": "string"},
417
- "assets": _ASSET_FILTER_SCHEMA,
361
+ "prior_as_of": PRIOR_AS_OF_SCHEMA,
362
+ "scope": SCOPE_SCHEMA,
363
+ "current_as_of": CURRENT_AS_OF_SCHEMA,
364
+ "assets": ASSET_FILTER_SCHEMA,
418
365
  },
419
366
  "required": ["prior_as_of"],
420
367
  },
@@ -431,39 +378,15 @@ _MCP_TOOLS: tuple[dict[str, Any], ...] = (
431
378
  {
432
379
  "tool_name": "check_allocation_bands",
433
380
  "description": (
434
- "Evaluate allocation drift against multiple target/band scenarios."
381
+ "Evaluate read-only allocation drift against multiple target_pct/band "
382
+ "scenarios in one call. Each scenario requires target_pct; optional name "
383
+ "and band (default 0.15). Use check_allocation_band for a single target."
435
384
  ),
436
385
  "input_schema": {
437
386
  "type": "object",
438
387
  "properties": {
439
- "allocation_pct": {
440
- "type": "object",
441
- "properties": {
442
- "BTC": {"type": "number"},
443
- "ETH": {"type": "number"},
444
- "CASH": {"type": "number"},
445
- },
446
- "required": ["BTC", "ETH", "CASH"],
447
- },
448
- "scenarios": {
449
- "type": "array",
450
- "items": {
451
- "type": "object",
452
- "properties": {
453
- "name": {"type": "string"},
454
- "target_pct": {
455
- "type": "object",
456
- "properties": {
457
- "BTC": {"type": "number"},
458
- "ETH": {"type": "number"},
459
- "CASH": {"type": "number"},
460
- },
461
- },
462
- "band": {"type": "number"},
463
- },
464
- "required": ["target_pct"],
465
- },
466
- },
388
+ "allocation_pct": allocation_pct_schema(role="Current"),
389
+ "scenarios": SCENARIOS_SCHEMA,
467
390
  },
468
391
  "required": ["allocation_pct", "scenarios"],
469
392
  },
@@ -715,14 +638,7 @@ def build_mcp_server_card(*, version: str) -> dict[str, Any]:
715
638
  "see /.well-known/x402.json for pricing."
716
639
  ),
717
640
  },
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": [],
641
+ "tools": [server_card_tool_entry(spec) for spec in _MCP_TOOLS],
642
+ "resources": list(MCP_SERVER_RESOURCES),
643
+ "prompts": list(MCP_SERVER_PROMPTS),
728
644
  }
@@ -6,6 +6,7 @@ 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_catalog import tool_annotations, tool_title
9
10
  from alloccontext.mcp.tool_fields import (
10
11
  AllocationPct,
11
12
  ApiKey,
@@ -72,6 +73,12 @@ def _require_mcp():
72
73
  return FastMCP
73
74
 
74
75
 
76
+ def _tool_hints(tool_name: str):
77
+ from mcp.types import ToolAnnotations
78
+
79
+ return ToolAnnotations(**tool_annotations(tool_name))
80
+
81
+
75
82
  def create_server(
76
83
  *,
77
84
  config_path: str | None = None,
@@ -97,6 +104,8 @@ def create_server(
97
104
 
98
105
  @mcp.tool(
99
106
  name="get_context_bundle",
107
+ title=tool_title("get_context_bundle"),
108
+ annotations=_tool_hints("get_context_bundle"),
100
109
  description=(
101
110
  "Return the full read-only ContextBundle JSON: portfolio holdings, "
102
111
  "market, sentiment, macro, regime hints, and delta vs the prior saved "
@@ -133,6 +142,8 @@ def create_server(
133
142
 
134
143
  @mcp.tool(
135
144
  name="get_market_context",
145
+ title=tool_title("get_market_context"),
146
+ annotations=_tool_hints("get_market_context"),
136
147
  description=(
137
148
  "Return read-only fused market backdrop: sentiment (Fear & Greed, "
138
149
  "Kalshi), macro events, FRED indicators, ETF flows, and market breadth "
@@ -163,6 +174,8 @@ def create_server(
163
174
 
164
175
  @mcp.tool(
165
176
  name="get_rebalance_plan",
177
+ title=tool_title("get_rebalance_plan"),
178
+ annotations=_tool_hints("get_rebalance_plan"),
166
179
  description=(
167
180
  "Compute read-only USD deltas and suggested exchange move lines to "
168
181
  "reach a BTC/ETH/CASH target split. Pure math — no exchange API calls. "
@@ -192,6 +205,8 @@ def create_server(
192
205
 
193
206
  @mcp.tool(
194
207
  name="get_portfolio_state",
208
+ title=tool_title("get_portfolio_state"),
209
+ annotations=_tool_hints("get_portfolio_state"),
195
210
  description=(
196
211
  "Fetch live read-only portfolio NAV, holdings[], and band weights from "
197
212
  "Kraken or Coinbase credentials passed in this call (never stored). "
@@ -221,6 +236,8 @@ def create_server(
221
236
 
222
237
  @mcp.tool(
223
238
  name="check_allocation_band",
239
+ title=tool_title("check_allocation_band"),
240
+ annotations=_tool_hints("check_allocation_band"),
224
241
  description=(
225
242
  "Read-only drift check: are BTC/ETH/CASH band weights outside the "
226
243
  "drift band vs target_pct? Returns rebalance_hint (within_band, "
@@ -241,6 +258,8 @@ def create_server(
241
258
 
242
259
  @mcp.tool(
243
260
  name="get_context_at",
261
+ title=tool_title("get_context_at"),
262
+ annotations=_tool_hints("get_context_at"),
244
263
  description=(
245
264
  "Load a read-only ContextBundle snapshot from ingest history at a "
246
265
  "point in time. Use get_context_bundle for the latest snapshot; use "
@@ -277,6 +296,8 @@ def create_server(
277
296
 
278
297
  @mcp.tool(
279
298
  name="get_context_delta",
299
+ title=tool_title("get_context_delta"),
300
+ annotations=_tool_hints("get_context_delta"),
280
301
  description=(
281
302
  "Compare two read-only ContextBundle snapshots and return "
282
303
  "notable_shifts between them. Requires prior_as_of; omit current_as_of "
@@ -307,6 +328,8 @@ def create_server(
307
328
 
308
329
  @mcp.tool(
309
330
  name="check_allocation_bands",
331
+ title=tool_title("check_allocation_bands"),
332
+ annotations=_tool_hints("check_allocation_bands"),
310
333
  description=(
311
334
  "Read-only batch drift check: evaluate allocation_pct against multiple "
312
335
  "target_pct/band scenarios in one call. Each scenario requires "