alloc-context 0.2.6__tar.gz → 0.2.7__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.
- {alloc_context-0.2.6 → alloc_context-0.2.7}/PKG-INFO +1 -1
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloc_context.egg-info/PKG-INFO +1 -1
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloc_context.egg-info/SOURCES.txt +2 -2
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/__init__.py +1 -1
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/mcp/bazaar.py +2 -1
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/mcp/bridge.py +39 -1
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/mcp/bridge_portfolio.py +54 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/mcp/contracts.py +35 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/mcp/handlers.py +139 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/mcp/http.py +0 -14
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/mcp/instructions.py +3 -1
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/mcp/server.py +3 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/mcp/tool_catalog.py +61 -1
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/mcp/tool_fields.py +10 -0
- alloc_context-0.2.7/alloccontext/mcp/validation.py +109 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/rollup/delta.py +10 -0
- alloc_context-0.2.7/alloccontext/rollup/expectation_review.py +594 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/user_config.py +25 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/x402_production_check.py +0 -1
- {alloc_context-0.2.6 → alloc_context-0.2.7}/pyproject.toml +1 -1
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_bridge.py +25 -0
- alloc_context-0.2.7/tests/test_expectation_review.py +421 -0
- alloc_context-0.2.7/tests/test_mcp_validation.py +38 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_x402_production_check.py +3 -3
- alloc_context-0.2.6/alloccontext/mcp/glama.py +0 -51
- alloc_context-0.2.6/alloccontext/mcp/validation.py +0 -58
- alloc_context-0.2.6/tests/test_glama_well_known.py +0 -51
- alloc_context-0.2.6/tests/test_mcp_validation.py +0 -47
- {alloc_context-0.2.6 → alloc_context-0.2.7}/LICENSE +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/README.md +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloc_context.egg-info/dependency_links.txt +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloc_context.egg-info/entry_points.txt +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloc_context.egg-info/requires.txt +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloc_context.egg-info/top_level.txt +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/__main__.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/config.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/constants.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/horizon.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/__init__.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/alt_quote_registry.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/alt_quote_store.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/alt_quotes.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/asset_registry.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/cf_benchmarks.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/cf_history.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/coinbase_client.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/coinbase_portfolio.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/coingecko.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/coinmarketcap.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/env_keys.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/etf_flows.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/exchange/__init__.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/exchange/coinbase_adapter.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/exchange/kraken_adapter.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/exchange/live.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/exchange/portfolio.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/exchange/registry.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/exchange/types.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/exchange_http.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/fear_greed.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/fred.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/http_errors.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/kalshi.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/kalshi_api.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/kalshi_client.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/kalshi_files.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/kalshi_state.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/kraken_client.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/kraken_portfolio.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/macro_calendar.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/macro_normalize.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/market_snapshots.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/outcome.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/parse_helpers.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/portfolio_holdings.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/quote_resolver.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/runner.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/wallet/__init__.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/wallet/alchemy.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/wallet/chains.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/wallet/curated_tokens.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/wallet/etherscan.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/ingest/wallet/portfolio.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/integrations/__init__.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/integrations/langchain.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/mcp/__init__.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/mcp/assets.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/mcp/payer.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/mcp/payment_middleware.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/mcp/setup.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/mcp/staleness.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/mcp/upstream.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/mcp/x402_bazaar_dynamic.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/mcp/x402_config.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/mcp/x402_pricing.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/mcp/x402_stables.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/rollup/__init__.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/rollup/allocation_analysis.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/rollup/band.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/rollup/breadth.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/rollup/cf_math.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/rollup/cluster.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/rollup/cluster_config.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/rollup/comparison.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/rollup/context.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/rollup/etf.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/rollup/fear_greed.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/rollup/macro.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/rollup/portfolio.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/rollup/portfolio_payload.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/rollup/rebalance.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/rollup/regime.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/rollup/sentiment.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/rollup/snapshots.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/rollup/tape.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/status_report.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/store/__init__.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/store/db.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/store/jsonutil.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/store/meta.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/store/retention.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/store/status.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/timeutil.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/alloccontext/x402_smoke_redact.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/setup.cfg +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_backup_sqlite.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_bridge_portfolio.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_bump_version.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_check_pypi_release_json.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_coinbase_portfolio.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_config_cli.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_context_bundle_schema.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_context_snapshots.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_db_schema.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_deploy.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_dev_stack.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_etf.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_exchanges_config.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_fear_greed.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_fred.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_holdings_scoped_delta_regime.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_holdings_scoped_market.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_horizon.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_ingest_outcome.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_ingest_runner.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_ingest_store_integration.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_kalshi_api.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_kraken_portfolio.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_langchain_integration.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_live_ingest_handlers.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_macro.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_market_breadth.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_mcp_assets_regime.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_mcp_bazaar.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_mcp_contracts.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_mcp_data_staleness.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_mcp_handlers.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_mcp_health.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_mcp_http_lifecycle.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_mcp_live_portfolio.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_mcp_server.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_mcp_x402.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_mcp_x402_http.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_mcp_x402_pricing.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_mcp_x402_stables.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_payer.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_portfolio_holdings.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_quote_resolver.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_rebalance.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_rollup.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_script_runtime.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_security_hardening.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_server_json.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_setup.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_snapshots_and_delta.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_status_report.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_tool_catalog.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_user_config.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_wallet_alchemy.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_wallet_etherscan.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_wallet_portfolio.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_workflows.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/tests/test_x402_bazaar_dynamic.py +0 -0
- {alloc_context-0.2.6 → alloc_context-0.2.7}/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.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: Portfolio-aware crypto context — CEX or wallet holdings, holdings-scoped market, optional allocation analysis
|
|
5
5
|
License: Elastic-2.0
|
|
6
6
|
Project-URL: Homepage, https://mcp.alloc-context.com/llms.txt
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: alloc-context
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: Portfolio-aware crypto context — CEX or wallet holdings, holdings-scoped market, optional allocation analysis
|
|
5
5
|
License: Elastic-2.0
|
|
6
6
|
Project-URL: Homepage, https://mcp.alloc-context.com/llms.txt
|
|
@@ -70,7 +70,6 @@ alloccontext/mcp/bazaar.py
|
|
|
70
70
|
alloccontext/mcp/bridge.py
|
|
71
71
|
alloccontext/mcp/bridge_portfolio.py
|
|
72
72
|
alloccontext/mcp/contracts.py
|
|
73
|
-
alloccontext/mcp/glama.py
|
|
74
73
|
alloccontext/mcp/handlers.py
|
|
75
74
|
alloccontext/mcp/http.py
|
|
76
75
|
alloccontext/mcp/instructions.py
|
|
@@ -98,6 +97,7 @@ alloccontext/rollup/comparison.py
|
|
|
98
97
|
alloccontext/rollup/context.py
|
|
99
98
|
alloccontext/rollup/delta.py
|
|
100
99
|
alloccontext/rollup/etf.py
|
|
100
|
+
alloccontext/rollup/expectation_review.py
|
|
101
101
|
alloccontext/rollup/fear_greed.py
|
|
102
102
|
alloccontext/rollup/macro.py
|
|
103
103
|
alloccontext/rollup/portfolio.py
|
|
@@ -127,9 +127,9 @@ tests/test_deploy.py
|
|
|
127
127
|
tests/test_dev_stack.py
|
|
128
128
|
tests/test_etf.py
|
|
129
129
|
tests/test_exchanges_config.py
|
|
130
|
+
tests/test_expectation_review.py
|
|
130
131
|
tests/test_fear_greed.py
|
|
131
132
|
tests/test_fred.py
|
|
132
|
-
tests/test_glama_well_known.py
|
|
133
133
|
tests/test_holdings_scoped_delta_regime.py
|
|
134
134
|
tests/test_holdings_scoped_market.py
|
|
135
135
|
tests/test_horizon.py
|
|
@@ -25,7 +25,8 @@ USE_DOCS_PATH = "docs/USE.md"
|
|
|
25
25
|
|
|
26
26
|
PRIVACY_COMPACT_COPY = (
|
|
27
27
|
"Privacy: nothing stored. One-time read-only fetch. Pass-through only — "
|
|
28
|
-
"your keys, wallet address, and portfolio never persist on our
|
|
28
|
+
"your keys, wallet address, theses, and portfolio never persist on our "
|
|
29
|
+
"servers."
|
|
29
30
|
)
|
|
30
31
|
|
|
31
32
|
PRIVACY_PILLAR_MARKERS = (
|
|
@@ -5,6 +5,7 @@ from typing import Any
|
|
|
5
5
|
from alloccontext.mcp import handlers
|
|
6
6
|
from alloccontext.mcp.bridge_portfolio import (
|
|
7
7
|
attach_assets_scope,
|
|
8
|
+
attach_bridge_expectation_review,
|
|
8
9
|
bridge_upstream_ready,
|
|
9
10
|
build_upstream_context_args,
|
|
10
11
|
default_bridge_app_config,
|
|
@@ -14,7 +15,7 @@ from alloccontext.mcp.bridge_portfolio import (
|
|
|
14
15
|
resolve_bridge_assets,
|
|
15
16
|
strip_upstream_allocation_regime,
|
|
16
17
|
)
|
|
17
|
-
from alloccontext.mcp.setup import allocation_not_configured
|
|
18
|
+
from alloccontext.mcp.setup import allocation_not_configured, upstream_payment_required
|
|
18
19
|
from alloccontext.mcp.upstream import call_upstream_tool
|
|
19
20
|
from alloccontext.user_config import (
|
|
20
21
|
UserConfig,
|
|
@@ -46,6 +47,27 @@ def _effective_band(user: UserConfig, band: float | None) -> float | None:
|
|
|
46
47
|
return user.band
|
|
47
48
|
|
|
48
49
|
|
|
50
|
+
def _effective_theses(
|
|
51
|
+
user: UserConfig,
|
|
52
|
+
theses: list[dict[str, Any]] | None,
|
|
53
|
+
) -> list[dict[str, Any]] | None:
|
|
54
|
+
if theses is not None:
|
|
55
|
+
return theses
|
|
56
|
+
return user.theses
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _fetch_upstream_baseline(user: UserConfig, *, scope: str, recorded_at: str) -> dict[str, Any]:
|
|
60
|
+
return call_upstream_tool(
|
|
61
|
+
user,
|
|
62
|
+
"get_context_at",
|
|
63
|
+
{
|
|
64
|
+
"scope": scope,
|
|
65
|
+
"as_of": recorded_at,
|
|
66
|
+
"match": "at_or_before",
|
|
67
|
+
},
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
49
71
|
def _bridge_tool_meta(tool_name: str) -> dict[str, Any]:
|
|
50
72
|
return {
|
|
51
73
|
"name": tool_name,
|
|
@@ -111,10 +133,13 @@ def create_bridge_server(user: UserConfig):
|
|
|
111
133
|
assets: list[str] | None = None,
|
|
112
134
|
target_pct: dict[str, float] | None = None,
|
|
113
135
|
band: float | None = None,
|
|
136
|
+
theses: list[dict[str, Any]] | None = None,
|
|
114
137
|
) -> dict[str, Any]:
|
|
115
138
|
validated_scope = handlers.validate_scope(scope)
|
|
116
139
|
validated_freshness = handlers.validate_freshness(freshness)
|
|
117
140
|
if not bridge_upstream_ready(user):
|
|
141
|
+
if _effective_theses(user, theses):
|
|
142
|
+
return upstream_payment_required()
|
|
118
143
|
effective_assets, _assets_scope = resolve_bridge_assets(
|
|
119
144
|
user,
|
|
120
145
|
bridge_config,
|
|
@@ -156,6 +181,19 @@ def create_bridge_server(user: UserConfig):
|
|
|
156
181
|
merged = merge_portfolio_into_bundle(bundle, portfolio)
|
|
157
182
|
merged = strip_upstream_allocation_regime(merged)
|
|
158
183
|
merged = merge_assets_omitted(merged, portfolio)
|
|
184
|
+
merged = attach_bridge_expectation_review(
|
|
185
|
+
user=user,
|
|
186
|
+
bundle=merged,
|
|
187
|
+
scope=validated_scope,
|
|
188
|
+
theses=_effective_theses(user, theses),
|
|
189
|
+
target_pct=_effective_target_pct(user, target_pct),
|
|
190
|
+
band=_effective_band(user, band),
|
|
191
|
+
fetch_baseline=lambda **kwargs: _fetch_upstream_baseline(
|
|
192
|
+
user,
|
|
193
|
+
scope=kwargs["scope"],
|
|
194
|
+
recorded_at=kwargs["recorded_at"],
|
|
195
|
+
),
|
|
196
|
+
)
|
|
159
197
|
return attach_assets_scope(merged, assets_scope)
|
|
160
198
|
|
|
161
199
|
@mcp.tool(**_bridge_tool_meta("get_portfolio_state"))
|
|
@@ -15,6 +15,8 @@ from alloccontext.rollup.portfolio_payload import (
|
|
|
15
15
|
attach_allocation_analysis_to_payload,
|
|
16
16
|
portfolio_dict_from_snapshot,
|
|
17
17
|
)
|
|
18
|
+
from alloccontext.mcp.validation import validate_band, validate_target_pct, validate_theses
|
|
19
|
+
from alloccontext.rollup.expectation_review import build_expectation_review
|
|
18
20
|
from alloccontext.user_config import UserConfig
|
|
19
21
|
|
|
20
22
|
AssetsScope = Literal["explicit", "portfolio", "default", "portfolio_unavailable"]
|
|
@@ -218,3 +220,55 @@ def strip_upstream_allocation_regime(bundle: dict[str, Any]) -> dict[str, Any]:
|
|
|
218
220
|
result = dict(bundle)
|
|
219
221
|
result["regime"] = updated
|
|
220
222
|
return result
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def attach_bridge_expectation_review(
|
|
226
|
+
*,
|
|
227
|
+
user: UserConfig,
|
|
228
|
+
bundle: dict[str, Any],
|
|
229
|
+
scope: str,
|
|
230
|
+
theses: list[dict[str, Any]] | None,
|
|
231
|
+
target_pct: dict[str, float] | None,
|
|
232
|
+
band: float | None,
|
|
233
|
+
fetch_baseline,
|
|
234
|
+
) -> dict[str, Any]:
|
|
235
|
+
"""Score local theses on a merged bridge bundle (baselines via upstream)."""
|
|
236
|
+
effective_theses = theses if theses is not None else user.theses
|
|
237
|
+
if not effective_theses:
|
|
238
|
+
return bundle
|
|
239
|
+
|
|
240
|
+
validated = validate_theses(effective_theses)
|
|
241
|
+
if not validated:
|
|
242
|
+
return bundle
|
|
243
|
+
|
|
244
|
+
by_recorded_at: dict[str, dict[str, Any] | None] = {}
|
|
245
|
+
baseline_bundles: dict[str, dict[str, Any] | None] = {}
|
|
246
|
+
for thesis in validated:
|
|
247
|
+
thesis_id = thesis["id"]
|
|
248
|
+
recorded_at = thesis["recorded_at"]
|
|
249
|
+
if recorded_at not in by_recorded_at:
|
|
250
|
+
baseline = fetch_baseline(scope=scope, recorded_at=recorded_at)
|
|
251
|
+
by_recorded_at[recorded_at] = (
|
|
252
|
+
baseline
|
|
253
|
+
if isinstance(baseline, dict) and baseline.get("as_of")
|
|
254
|
+
else None
|
|
255
|
+
)
|
|
256
|
+
baseline_bundles[thesis_id] = by_recorded_at[recorded_at]
|
|
257
|
+
|
|
258
|
+
effective_target = target_pct if target_pct is not None else user.target_allocation
|
|
259
|
+
if effective_target is not None:
|
|
260
|
+
effective_target = validate_target_pct(effective_target)
|
|
261
|
+
effective_band = band if band is not None else user.band
|
|
262
|
+
if effective_band is not None:
|
|
263
|
+
effective_band = validate_band(effective_band)
|
|
264
|
+
|
|
265
|
+
review = build_expectation_review(
|
|
266
|
+
baseline_bundles=baseline_bundles,
|
|
267
|
+
current_bundle=bundle,
|
|
268
|
+
theses=validated,
|
|
269
|
+
target_pct=effective_target,
|
|
270
|
+
band=effective_band,
|
|
271
|
+
)
|
|
272
|
+
result = dict(bundle)
|
|
273
|
+
result["expectation_review"] = review
|
|
274
|
+
return result
|
|
@@ -34,6 +34,22 @@ REGIME_AVAILABLE_KEYS = (
|
|
|
34
34
|
"allocation",
|
|
35
35
|
)
|
|
36
36
|
|
|
37
|
+
EXPECTATION_REVIEW_KEYS = (
|
|
38
|
+
"available",
|
|
39
|
+
"current_as_of",
|
|
40
|
+
"supported",
|
|
41
|
+
"weakened",
|
|
42
|
+
"unknown",
|
|
43
|
+
"claims",
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
EXPECTATION_CLAIM_KEYS = (
|
|
47
|
+
"thesis_id",
|
|
48
|
+
"type",
|
|
49
|
+
"status",
|
|
50
|
+
"evidence",
|
|
51
|
+
)
|
|
52
|
+
|
|
37
53
|
MCP_TOOL_NAMES = frozenset(spec["tool_name"] for spec in mcp_tool_specs())
|
|
38
54
|
|
|
39
55
|
|
|
@@ -121,6 +137,25 @@ def validate_context_bundle(payload: dict[str, Any]) -> None:
|
|
|
121
137
|
label="regime",
|
|
122
138
|
when_available=REGIME_AVAILABLE_KEYS,
|
|
123
139
|
)
|
|
140
|
+
review = payload.get("expectation_review")
|
|
141
|
+
if isinstance(review, dict) and review.get("available"):
|
|
142
|
+
assert_available_block(
|
|
143
|
+
review,
|
|
144
|
+
label="expectation_review",
|
|
145
|
+
when_available=EXPECTATION_REVIEW_KEYS,
|
|
146
|
+
)
|
|
147
|
+
for index, claim in enumerate(review.get("claims") or []):
|
|
148
|
+
if not isinstance(claim, dict):
|
|
149
|
+
raise AssertionError(f"expectation_review.claims[{index}] must be object")
|
|
150
|
+
assert_has_keys(
|
|
151
|
+
claim,
|
|
152
|
+
EXPECTATION_CLAIM_KEYS,
|
|
153
|
+
label=f"expectation_review.claims[{index}]",
|
|
154
|
+
)
|
|
155
|
+
if claim.get("status") == "unknown" and not claim.get("reason"):
|
|
156
|
+
raise AssertionError(
|
|
157
|
+
f"expectation_review.claims[{index}] unknown requires reason"
|
|
158
|
+
)
|
|
124
159
|
|
|
125
160
|
|
|
126
161
|
def validate_market_context(payload: dict[str, Any]) -> None:
|
|
@@ -32,6 +32,11 @@ from alloccontext.mcp.validation import (
|
|
|
32
32
|
validate_band,
|
|
33
33
|
validate_nav_usd,
|
|
34
34
|
validate_target_pct,
|
|
35
|
+
validate_theses,
|
|
36
|
+
)
|
|
37
|
+
from alloccontext.rollup.expectation_review import (
|
|
38
|
+
build_expectation_review,
|
|
39
|
+
theses_need_allocation_fit,
|
|
35
40
|
)
|
|
36
41
|
from alloccontext.rollup.comparison import compare_context_bundles
|
|
37
42
|
from alloccontext.rollup.regime import build_regime_context
|
|
@@ -116,6 +121,116 @@ def _band_allocation_pct(portfolio: dict[str, Any]) -> dict[str, float]:
|
|
|
116
121
|
return band_allocation_pct(portfolio.get("holdings") or [])
|
|
117
122
|
|
|
118
123
|
|
|
124
|
+
def _load_baseline_bundle(
|
|
125
|
+
conn: sqlite3.Connection,
|
|
126
|
+
*,
|
|
127
|
+
scope: Scope,
|
|
128
|
+
recorded_at: str,
|
|
129
|
+
) -> dict[str, Any] | None:
|
|
130
|
+
try:
|
|
131
|
+
resolved = resolve_context_snapshot_as_of(
|
|
132
|
+
conn,
|
|
133
|
+
scope=scope,
|
|
134
|
+
as_of=recorded_at,
|
|
135
|
+
mode="at_or_before",
|
|
136
|
+
)
|
|
137
|
+
return load_context_bundle_snapshot(conn, scope=scope, as_of=resolved)
|
|
138
|
+
except SnapshotNotFoundError:
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _prepare_baseline_bundle(
|
|
143
|
+
conn: sqlite3.Connection,
|
|
144
|
+
config,
|
|
145
|
+
*,
|
|
146
|
+
scope: Scope,
|
|
147
|
+
recorded_at: str,
|
|
148
|
+
) -> dict[str, Any] | None:
|
|
149
|
+
"""Load baseline snapshot and rebuild regime for market-wide claim scoring."""
|
|
150
|
+
raw = _load_baseline_bundle(conn, scope=scope, recorded_at=recorded_at)
|
|
151
|
+
if raw is None:
|
|
152
|
+
return None
|
|
153
|
+
return _attach_regime(dict(raw), config)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _baseline_bundles_for_theses(
|
|
157
|
+
conn: sqlite3.Connection,
|
|
158
|
+
config,
|
|
159
|
+
*,
|
|
160
|
+
scope: Scope,
|
|
161
|
+
theses: list[dict[str, Any]],
|
|
162
|
+
) -> dict[str, dict[str, Any] | None]:
|
|
163
|
+
by_recorded_at: dict[str, dict[str, Any] | None] = {}
|
|
164
|
+
result: dict[str, dict[str, Any] | None] = {}
|
|
165
|
+
for thesis in theses:
|
|
166
|
+
thesis_id = thesis["id"]
|
|
167
|
+
recorded_at = thesis.get("recorded_at") or ""
|
|
168
|
+
if recorded_at not in by_recorded_at:
|
|
169
|
+
by_recorded_at[recorded_at] = _prepare_baseline_bundle(
|
|
170
|
+
conn,
|
|
171
|
+
config,
|
|
172
|
+
scope=scope,
|
|
173
|
+
recorded_at=recorded_at,
|
|
174
|
+
)
|
|
175
|
+
result[thesis_id] = by_recorded_at[recorded_at]
|
|
176
|
+
return result
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _effective_allocation_inputs(
|
|
180
|
+
config,
|
|
181
|
+
*,
|
|
182
|
+
target_pct: dict[str, float] | None,
|
|
183
|
+
band: float | None,
|
|
184
|
+
) -> tuple[dict[str, float] | None, float | None]:
|
|
185
|
+
effective_target = target_pct
|
|
186
|
+
if effective_target is None and config.portfolio.target_allocations:
|
|
187
|
+
effective_target = validate_target_pct(dict(config.portfolio.target_allocations))
|
|
188
|
+
effective_band = band
|
|
189
|
+
if effective_band is None and config.portfolio.rebalance_band is not None:
|
|
190
|
+
effective_band = validate_band(config.portfolio.rebalance_band)
|
|
191
|
+
return effective_target, effective_band
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _attach_expectation_review(
|
|
195
|
+
conn: sqlite3.Connection,
|
|
196
|
+
config,
|
|
197
|
+
bundle: dict[str, Any],
|
|
198
|
+
*,
|
|
199
|
+
scope: Scope,
|
|
200
|
+
theses: list[dict[str, Any]] | None,
|
|
201
|
+
target_pct: dict[str, float] | None,
|
|
202
|
+
band: float | None,
|
|
203
|
+
) -> dict[str, Any]:
|
|
204
|
+
if not theses:
|
|
205
|
+
return bundle
|
|
206
|
+
validated = validate_theses(theses)
|
|
207
|
+
if not validated:
|
|
208
|
+
return bundle
|
|
209
|
+
|
|
210
|
+
baseline_bundles = _baseline_bundles_for_theses(
|
|
211
|
+
conn,
|
|
212
|
+
config,
|
|
213
|
+
scope=scope,
|
|
214
|
+
theses=validated,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
effective_target, effective_band = _effective_allocation_inputs(
|
|
218
|
+
config,
|
|
219
|
+
target_pct=target_pct,
|
|
220
|
+
band=band,
|
|
221
|
+
)
|
|
222
|
+
review = build_expectation_review(
|
|
223
|
+
baseline_bundles=baseline_bundles,
|
|
224
|
+
current_bundle=bundle,
|
|
225
|
+
theses=validated,
|
|
226
|
+
target_pct=effective_target,
|
|
227
|
+
band=effective_band,
|
|
228
|
+
)
|
|
229
|
+
result = dict(bundle)
|
|
230
|
+
result["expectation_review"] = review
|
|
231
|
+
return result
|
|
232
|
+
|
|
233
|
+
|
|
119
234
|
def _attach_allocation_analysis(
|
|
120
235
|
bundle: dict[str, Any],
|
|
121
236
|
config,
|
|
@@ -389,6 +504,7 @@ def get_context_bundle(
|
|
|
389
504
|
assets: list[str] | None = None,
|
|
390
505
|
target_pct: dict[str, float] | None = None,
|
|
391
506
|
band: float | None = None,
|
|
507
|
+
theses: list[dict[str, Any]] | None = None,
|
|
392
508
|
) -> dict[str, Any]:
|
|
393
509
|
now = (as_of or utc_now()).replace(microsecond=0)
|
|
394
510
|
if now.tzinfo is None:
|
|
@@ -426,6 +542,13 @@ def get_context_bundle(
|
|
|
426
542
|
save_snapshot=False,
|
|
427
543
|
alt_symbols=alt_symbols,
|
|
428
544
|
)
|
|
545
|
+
validated_theses = validate_theses(theses) if theses else []
|
|
546
|
+
need_allocation = theses_need_allocation_fit(validated_theses)
|
|
547
|
+
effective_target, effective_band = _effective_allocation_inputs(
|
|
548
|
+
config,
|
|
549
|
+
target_pct=target_pct,
|
|
550
|
+
band=band,
|
|
551
|
+
)
|
|
429
552
|
if target_pct is not None or band is not None:
|
|
430
553
|
bundle = _attach_allocation_analysis(
|
|
431
554
|
bundle,
|
|
@@ -433,8 +556,24 @@ def get_context_bundle(
|
|
|
433
556
|
target_pct=target_pct,
|
|
434
557
|
band=band,
|
|
435
558
|
)
|
|
559
|
+
elif need_allocation and effective_target is not None:
|
|
560
|
+
bundle = _attach_allocation_analysis(
|
|
561
|
+
bundle,
|
|
562
|
+
config,
|
|
563
|
+
target_pct=effective_target,
|
|
564
|
+
band=effective_band,
|
|
565
|
+
)
|
|
436
566
|
bundle = apply_assets_filter_to_bundle(bundle, view_assets)
|
|
437
567
|
bundle = _attach_regime(bundle, config)
|
|
568
|
+
bundle = _attach_expectation_review(
|
|
569
|
+
conn,
|
|
570
|
+
config,
|
|
571
|
+
bundle,
|
|
572
|
+
scope=scope,
|
|
573
|
+
theses=theses,
|
|
574
|
+
target_pct=target_pct,
|
|
575
|
+
band=band,
|
|
576
|
+
)
|
|
438
577
|
if target_pct is not None:
|
|
439
578
|
bundle["target_pct"] = validate_target_pct(target_pct)
|
|
440
579
|
if band is not None:
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import contextlib
|
|
4
|
-
import json
|
|
5
4
|
import os
|
|
6
5
|
from collections.abc import AsyncIterator
|
|
7
6
|
from typing import Any
|
|
@@ -17,7 +16,6 @@ from alloccontext.mcp.bazaar import (
|
|
|
17
16
|
build_well_known_x402,
|
|
18
17
|
resolve_public_base_url,
|
|
19
18
|
)
|
|
20
|
-
from alloccontext.mcp.glama import build_glama_well_known
|
|
21
19
|
from alloccontext.mcp.server import create_server
|
|
22
20
|
from alloccontext.mcp.x402_config import (
|
|
23
21
|
CDP_FACILITATOR_URL,
|
|
@@ -131,17 +129,6 @@ def _well_known_mcp_server_card() -> JSONResponse:
|
|
|
131
129
|
return JSONResponse(build_mcp_server_card(version=__version__))
|
|
132
130
|
|
|
133
131
|
|
|
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
|
-
|
|
145
132
|
def _is_loopback_host(host: str) -> bool:
|
|
146
133
|
normalized = host.strip().lower()
|
|
147
134
|
return normalized in {"127.0.0.1", "localhost", "::1"}
|
|
@@ -224,7 +211,6 @@ def build_http_app(
|
|
|
224
211
|
Route("/llms.txt", lambda req: _llms_txt(settings)),
|
|
225
212
|
Route("/.well-known/x402.json", lambda req: _well_known_x402(settings)),
|
|
226
213
|
Route("/.well-known/mcp/server-card.json", lambda req: _well_known_mcp_server_card()),
|
|
227
|
-
Route("/.well-known/glama.json", lambda req: _well_known_glama()),
|
|
228
214
|
]
|
|
229
215
|
|
|
230
216
|
if not settings.enabled:
|
|
@@ -5,7 +5,9 @@ PRODUCT_INSTRUCTIONS = (
|
|
|
5
5
|
"keys or a public EVM wallet address (keyless). Default responses include "
|
|
6
6
|
"holdings[], market, sentiment, macro, regime, and delta. Allocation "
|
|
7
7
|
"drift and rebalance are opt-in via target_pct or allocation_analysis. "
|
|
8
|
-
"
|
|
8
|
+
"Optional theses[] attach expectation_review (pass-through beliefs; "
|
|
9
|
+
"nothing stored). Privacy: nothing stored; one-time read-only; "
|
|
10
|
+
"pass-through only. "
|
|
9
11
|
"setup objects explain missing config."
|
|
10
12
|
)
|
|
11
13
|
|
|
@@ -26,6 +26,7 @@ from alloccontext.mcp.tool_fields import (
|
|
|
26
26
|
MatchMode,
|
|
27
27
|
NavUsd,
|
|
28
28
|
OptionalTargetPct,
|
|
29
|
+
OptionalTheses,
|
|
29
30
|
PriorAsOf,
|
|
30
31
|
Scenarios,
|
|
31
32
|
Scope,
|
|
@@ -117,6 +118,7 @@ def create_server(
|
|
|
117
118
|
assets: Assets = None,
|
|
118
119
|
target_pct: OptionalTargetPct = None,
|
|
119
120
|
band: BandOptional = None,
|
|
121
|
+
theses: OptionalTheses = None,
|
|
120
122
|
) -> dict[str, Any]:
|
|
121
123
|
"""Return the full deterministic context bundle for daily or weekly scope."""
|
|
122
124
|
validated_scope = handlers.validate_scope(scope)
|
|
@@ -131,6 +133,7 @@ def create_server(
|
|
|
131
133
|
assets=assets,
|
|
132
134
|
target_pct=target_pct,
|
|
133
135
|
band=band,
|
|
136
|
+
theses=theses,
|
|
134
137
|
)
|
|
135
138
|
finally:
|
|
136
139
|
conn.close()
|
|
@@ -118,6 +118,63 @@ BAND_SCHEMA: dict[str, Any] = {
|
|
|
118
118
|
"description": "Drift band width as a fraction (e.g. 0.15 = 15% outside target).",
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
THESES_SCHEMA: dict[str, Any] = {
|
|
122
|
+
"type": "array",
|
|
123
|
+
"description": (
|
|
124
|
+
"Optional local thesis entries for expectation_review (pass-through; "
|
|
125
|
+
"nothing stored). Each entry requires id, recorded_at, and claims[]."
|
|
126
|
+
),
|
|
127
|
+
"items": {
|
|
128
|
+
"type": "object",
|
|
129
|
+
"properties": {
|
|
130
|
+
"id": {
|
|
131
|
+
"type": "string",
|
|
132
|
+
"description": "Stable thesis identifier for claim results.",
|
|
133
|
+
},
|
|
134
|
+
"recorded_at": {
|
|
135
|
+
"type": "string",
|
|
136
|
+
"description": "Thesis anchor ISO timestamp (baseline snapshot).",
|
|
137
|
+
},
|
|
138
|
+
"asset": {
|
|
139
|
+
"type": "string",
|
|
140
|
+
"description": "Optional context symbol for the thesis (not scored).",
|
|
141
|
+
},
|
|
142
|
+
"claims": {
|
|
143
|
+
"type": "array",
|
|
144
|
+
"description": "Structured claim objects scored deterministically.",
|
|
145
|
+
"items": {
|
|
146
|
+
"type": "object",
|
|
147
|
+
"properties": {
|
|
148
|
+
"type": {
|
|
149
|
+
"type": "string",
|
|
150
|
+
"description": "Claim type (e.g. RELATIVE_STRENGTH).",
|
|
151
|
+
},
|
|
152
|
+
"asset": {
|
|
153
|
+
"type": "string",
|
|
154
|
+
"description": "Held asset symbol for asset-scoped claims.",
|
|
155
|
+
},
|
|
156
|
+
"benchmark": {
|
|
157
|
+
"type": "string",
|
|
158
|
+
"description": "Benchmark symbol for RELATIVE_STRENGTH.",
|
|
159
|
+
},
|
|
160
|
+
"direction": {
|
|
161
|
+
"type": "string",
|
|
162
|
+
"description": "Expected direction (UP/DOWN, IMPROVING, etc.).",
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
"required": ["type"],
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
"rationale": {
|
|
169
|
+
"type": "array",
|
|
170
|
+
"items": {"type": "string"},
|
|
171
|
+
"description": "Agent context only — never scored.",
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
"required": ["id", "recorded_at", "claims"],
|
|
175
|
+
},
|
|
176
|
+
}
|
|
177
|
+
|
|
121
178
|
MATCH_SCHEMA: dict[str, Any] = {
|
|
122
179
|
"type": "string",
|
|
123
180
|
"enum": ["exact", "at_or_before"],
|
|
@@ -216,7 +273,9 @@ TOOL_DESCRIPTIONS: dict[str, str] = {
|
|
|
216
273
|
"snapshot. Use get_market_context for market-only; use get_context_at "
|
|
217
274
|
"for a historical snapshot; use get_context_delta to compare two times. "
|
|
218
275
|
"Optional target_pct and band attach allocation_analysis (opt-in drift "
|
|
219
|
-
"math).
|
|
276
|
+
"math). Optional theses[] attaches expectation_review (deterministic "
|
|
277
|
+
"claim scoring vs recorded_at baseline; pass-through only). "
|
|
278
|
+
"freshness=cached uses the local ingest DB; freshness=live runs "
|
|
220
279
|
"ingest first (may add latency; needs ingest API keys on the host)."
|
|
221
280
|
),
|
|
222
281
|
"get_market_context": (
|
|
@@ -313,6 +372,7 @@ MCP_TOOL_SPECS: tuple[dict[str, Any], ...] = (
|
|
|
313
372
|
"assets": ASSET_FILTER_SCHEMA,
|
|
314
373
|
"target_pct": TARGET_PCT_SCHEMA,
|
|
315
374
|
"band": BAND_SCHEMA,
|
|
375
|
+
"theses": THESES_SCHEMA,
|
|
316
376
|
},
|
|
317
377
|
},
|
|
318
378
|
"example": {
|
|
@@ -63,6 +63,16 @@ BandOptional = Annotated[
|
|
|
63
63
|
),
|
|
64
64
|
),
|
|
65
65
|
]
|
|
66
|
+
OptionalTheses = Annotated[
|
|
67
|
+
list[dict[str, Any]] | None,
|
|
68
|
+
Field(
|
|
69
|
+
description=(
|
|
70
|
+
"Optional local thesis entries (id, recorded_at, claims[]) for "
|
|
71
|
+
"deterministic expectation_review scoring. Pass-through only — "
|
|
72
|
+
"nothing stored."
|
|
73
|
+
),
|
|
74
|
+
),
|
|
75
|
+
]
|
|
66
76
|
BandDefault = Annotated[
|
|
67
77
|
float,
|
|
68
78
|
Field(
|