alloc-context 0.2.3__tar.gz → 0.2.4__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.3 → alloc_context-0.2.4}/PKG-INFO +15 -5
- {alloc_context-0.2.3 → alloc_context-0.2.4}/README.md +13 -4
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloc_context.egg-info/PKG-INFO +15 -5
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloc_context.egg-info/SOURCES.txt +3 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloc_context.egg-info/requires.txt +1 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/__init__.py +1 -1
- alloc_context-0.2.4/alloccontext/integrations/__init__.py +1 -0
- alloc_context-0.2.4/alloccontext/integrations/langchain.py +125 -0
- alloc_context-0.2.4/alloccontext/mcp/bazaar.py +338 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/mcp/bridge.py +22 -8
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/mcp/http.py +48 -15
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/mcp/server.py +14 -70
- alloc_context-0.2.4/alloccontext/mcp/tool_catalog.py +661 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/mcp/x402_config.py +41 -5
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/x402_production_check.py +14 -4
- {alloc_context-0.2.3 → alloc_context-0.2.4}/pyproject.toml +2 -1
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_deploy.py +6 -0
- alloc_context-0.2.4/tests/test_langchain_integration.py +115 -0
- alloc_context-0.2.4/tests/test_mcp_x402.py +290 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_tool_catalog.py +30 -2
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_x402_production_check.py +27 -0
- alloc_context-0.2.3/alloccontext/mcp/bazaar.py +0 -646
- alloc_context-0.2.3/alloccontext/mcp/tool_catalog.py +0 -311
- alloc_context-0.2.3/tests/test_mcp_x402.py +0 -139
- {alloc_context-0.2.3 → alloc_context-0.2.4}/LICENSE +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloc_context.egg-info/dependency_links.txt +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloc_context.egg-info/entry_points.txt +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloc_context.egg-info/top_level.txt +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/__main__.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/config.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/constants.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/horizon.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/__init__.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/alt_quote_registry.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/alt_quote_store.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/alt_quotes.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/asset_registry.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/cf_benchmarks.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/cf_history.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/coinbase_client.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/coinbase_portfolio.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/coingecko.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/coinmarketcap.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/env_keys.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/etf_flows.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/exchange/__init__.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/exchange/coinbase_adapter.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/exchange/kraken_adapter.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/exchange/live.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/exchange/portfolio.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/exchange/registry.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/exchange/types.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/exchange_http.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/fear_greed.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/fred.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/http_errors.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/kalshi.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/kalshi_api.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/kalshi_client.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/kalshi_files.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/kalshi_state.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/kraken_client.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/kraken_portfolio.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/macro_calendar.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/macro_normalize.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/market_snapshots.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/outcome.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/parse_helpers.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/portfolio_holdings.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/quote_resolver.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/ingest/runner.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/mcp/__init__.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/mcp/assets.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/mcp/bridge_portfolio.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/mcp/contracts.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/mcp/glama.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/mcp/handlers.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/mcp/instructions.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/mcp/payer.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/mcp/payment_middleware.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/mcp/setup.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/mcp/staleness.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/mcp/tool_fields.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/mcp/upstream.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/mcp/validation.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/mcp/x402_bazaar_dynamic.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/mcp/x402_pricing.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/mcp/x402_stables.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/rollup/__init__.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/rollup/allocation_analysis.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/rollup/band.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/rollup/breadth.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/rollup/cf_math.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/rollup/cluster.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/rollup/cluster_config.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/rollup/comparison.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/rollup/context.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/rollup/delta.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/rollup/etf.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/rollup/fear_greed.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/rollup/macro.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/rollup/portfolio.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/rollup/portfolio_payload.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/rollup/rebalance.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/rollup/regime.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/rollup/sentiment.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/rollup/snapshots.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/rollup/tape.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/status_report.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/store/__init__.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/store/db.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/store/jsonutil.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/store/meta.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/store/retention.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/store/status.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/timeutil.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/user_config.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/alloccontext/x402_smoke_redact.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/setup.cfg +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_backup_sqlite.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_bridge.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_bridge_portfolio.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_bump_version.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_check_pypi_release_json.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_coinbase_portfolio.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_config_cli.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_context_bundle_schema.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_context_snapshots.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_db_schema.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_dev_stack.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_etf.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_exchanges_config.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_fear_greed.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_fred.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_glama_well_known.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_holdings_scoped_delta_regime.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_holdings_scoped_market.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_horizon.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_ingest_outcome.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_ingest_runner.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_ingest_store_integration.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_kalshi_api.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_kraken_portfolio.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_live_ingest_handlers.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_macro.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_market_breadth.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_mcp_assets_regime.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_mcp_bazaar.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_mcp_contracts.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_mcp_data_staleness.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_mcp_handlers.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_mcp_health.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_mcp_http_lifecycle.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_mcp_live_portfolio.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_mcp_server.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_mcp_validation.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_mcp_x402_http.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_mcp_x402_pricing.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_mcp_x402_stables.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_payer.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_portfolio_holdings.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_quote_resolver.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_rebalance.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_rollup.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_script_runtime.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_security_hardening.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_server_json.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_setup.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_snapshots_and_delta.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_status_report.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_user_config.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_workflows.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/tests/test_x402_bazaar_dynamic.py +0 -0
- {alloc_context-0.2.3 → alloc_context-0.2.4}/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.4
|
|
4
4
|
Summary: Portfolio-aware crypto context for agents — holdings-scoped market, sentiment, optional allocation analysis
|
|
5
5
|
License: Elastic-2.0
|
|
6
6
|
Project-URL: Homepage, https://mcp.alloc-context.com/llms.txt
|
|
@@ -40,12 +40,12 @@ Requires-Dist: mcp>=1.6; extra == "dev"
|
|
|
40
40
|
Requires-Dist: x402[evm,fastapi]>=2.0; extra == "dev"
|
|
41
41
|
Requires-Dist: cdp-sdk>=1.46; extra == "dev"
|
|
42
42
|
Requires-Dist: uvicorn[standard]>=0.30; extra == "dev"
|
|
43
|
+
Requires-Dist: langchain-core>=0.3.0; extra == "dev"
|
|
43
44
|
Dynamic: license-file
|
|
44
45
|
|
|
45
46
|
# AllocContext
|
|
46
47
|
|
|
47
|
-
[](https://glama.ai/mcp/servers/AllocContext/alloc-context)
|
|
48
|
+
[](https://smithery.ai/servers/alloccontext/alloc-context)
|
|
49
49
|
|
|
50
50
|
mcp-name: io.github.AllocContext/alloc-context
|
|
51
51
|
|
|
@@ -54,6 +54,10 @@ holdings, holdings-scoped market data, sentiment, macro, and regime; optional
|
|
|
54
54
|
allocation analysis when you supply targets. Deterministic JSON over MCP with
|
|
55
55
|
x402 pay-per-call on Base.
|
|
56
56
|
|
|
57
|
+
**New here?** [Agent on-ramp (~2 min)](docs/agent-onramp.md) — copy-paste path to
|
|
58
|
+
your first ContextBundle. **Organization:**
|
|
59
|
+
[AllocContext on GitHub](https://github.com/AllocContext) — overview and integration links.
|
|
60
|
+
|
|
57
61
|
> **Privacy:** nothing stored · one-time read-only · pass-through only — your
|
|
58
62
|
> keys and portfolio never persist on our servers. See [USE.md](docs/USE.md).
|
|
59
63
|
|
|
@@ -143,8 +147,10 @@ See [mcp.md](docs/mcp.md) for arguments, pricing, and resources.
|
|
|
143
147
|
## Self-host and development
|
|
144
148
|
|
|
145
149
|
Run ingest and MCP entirely on your machine — no x402 upstream required.
|
|
146
|
-
See [self-hosting.md](docs/self-hosting.md) (`self_host: true` in user config)
|
|
147
|
-
|
|
150
|
+
See [self-hosting.md](docs/self-hosting.md) (`self_host: true` in user config),
|
|
151
|
+
[local-dev.md](docs/local-dev.md) for the native dev stack (`./scripts/dev-up.sh`),
|
|
152
|
+
or `./docker/up.sh` / [docker-self-host.md](docs/docker-self-host.md) for Docker
|
|
153
|
+
on loopback `:8000`.
|
|
148
154
|
|
|
149
155
|
```bash
|
|
150
156
|
git clone git@github.com:AllocContext/alloc-context.git
|
|
@@ -173,6 +179,10 @@ HTTP MCP + x402: [mcp-http.md](docs/mcp-http.md). CLI entry point:
|
|
|
173
179
|
|
|
174
180
|
| Document | Purpose |
|
|
175
181
|
|----------|---------|
|
|
182
|
+
| [docs/agent-onramp.md](docs/agent-onramp.md) | **Start here** — ~2 min to first ContextBundle |
|
|
183
|
+
| [docs/deterministic-context-mcp-pattern.md](docs/deterministic-context-mcp-pattern.md) | Reusable ingest → rollup → MCP → x402 pattern |
|
|
184
|
+
| [docs/langchain-integration.md](docs/langchain-integration.md) | LangChain tools for hosted MCP (x402) |
|
|
185
|
+
| [docs/docker-self-host.md](docs/docker-self-host.md) | Docker Compose self-host evaluation |
|
|
176
186
|
| [docs/cursor-mcp.md](docs/cursor-mcp.md) | Cursor stdio MCP (bridge default) |
|
|
177
187
|
| [docs/user-config.md](docs/user-config.md) | Bridge `user.yaml` reference |
|
|
178
188
|
| [docs/mcp.md](docs/mcp.md) | MCP tools and x402 |
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# AllocContext
|
|
2
2
|
|
|
3
|
-
[](https://glama.ai/mcp/servers/AllocContext/alloc-context)
|
|
3
|
+
[](https://smithery.ai/servers/alloccontext/alloc-context)
|
|
5
4
|
|
|
6
5
|
mcp-name: io.github.AllocContext/alloc-context
|
|
7
6
|
|
|
@@ -10,6 +9,10 @@ holdings, holdings-scoped market data, sentiment, macro, and regime; optional
|
|
|
10
9
|
allocation analysis when you supply targets. Deterministic JSON over MCP with
|
|
11
10
|
x402 pay-per-call on Base.
|
|
12
11
|
|
|
12
|
+
**New here?** [Agent on-ramp (~2 min)](docs/agent-onramp.md) — copy-paste path to
|
|
13
|
+
your first ContextBundle. **Organization:**
|
|
14
|
+
[AllocContext on GitHub](https://github.com/AllocContext) — overview and integration links.
|
|
15
|
+
|
|
13
16
|
> **Privacy:** nothing stored · one-time read-only · pass-through only — your
|
|
14
17
|
> keys and portfolio never persist on our servers. See [USE.md](docs/USE.md).
|
|
15
18
|
|
|
@@ -99,8 +102,10 @@ See [mcp.md](docs/mcp.md) for arguments, pricing, and resources.
|
|
|
99
102
|
## Self-host and development
|
|
100
103
|
|
|
101
104
|
Run ingest and MCP entirely on your machine — no x402 upstream required.
|
|
102
|
-
See [self-hosting.md](docs/self-hosting.md) (`self_host: true` in user config)
|
|
103
|
-
|
|
105
|
+
See [self-hosting.md](docs/self-hosting.md) (`self_host: true` in user config),
|
|
106
|
+
[local-dev.md](docs/local-dev.md) for the native dev stack (`./scripts/dev-up.sh`),
|
|
107
|
+
or `./docker/up.sh` / [docker-self-host.md](docs/docker-self-host.md) for Docker
|
|
108
|
+
on loopback `:8000`.
|
|
104
109
|
|
|
105
110
|
```bash
|
|
106
111
|
git clone git@github.com:AllocContext/alloc-context.git
|
|
@@ -129,6 +134,10 @@ HTTP MCP + x402: [mcp-http.md](docs/mcp-http.md). CLI entry point:
|
|
|
129
134
|
|
|
130
135
|
| Document | Purpose |
|
|
131
136
|
|----------|---------|
|
|
137
|
+
| [docs/agent-onramp.md](docs/agent-onramp.md) | **Start here** — ~2 min to first ContextBundle |
|
|
138
|
+
| [docs/deterministic-context-mcp-pattern.md](docs/deterministic-context-mcp-pattern.md) | Reusable ingest → rollup → MCP → x402 pattern |
|
|
139
|
+
| [docs/langchain-integration.md](docs/langchain-integration.md) | LangChain tools for hosted MCP (x402) |
|
|
140
|
+
| [docs/docker-self-host.md](docs/docker-self-host.md) | Docker Compose self-host evaluation |
|
|
132
141
|
| [docs/cursor-mcp.md](docs/cursor-mcp.md) | Cursor stdio MCP (bridge default) |
|
|
133
142
|
| [docs/user-config.md](docs/user-config.md) | Bridge `user.yaml` reference |
|
|
134
143
|
| [docs/mcp.md](docs/mcp.md) | MCP tools and x402 |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: alloc-context
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: Portfolio-aware crypto context for agents — holdings-scoped market, sentiment, optional allocation analysis
|
|
5
5
|
License: Elastic-2.0
|
|
6
6
|
Project-URL: Homepage, https://mcp.alloc-context.com/llms.txt
|
|
@@ -40,12 +40,12 @@ Requires-Dist: mcp>=1.6; extra == "dev"
|
|
|
40
40
|
Requires-Dist: x402[evm,fastapi]>=2.0; extra == "dev"
|
|
41
41
|
Requires-Dist: cdp-sdk>=1.46; extra == "dev"
|
|
42
42
|
Requires-Dist: uvicorn[standard]>=0.30; extra == "dev"
|
|
43
|
+
Requires-Dist: langchain-core>=0.3.0; extra == "dev"
|
|
43
44
|
Dynamic: license-file
|
|
44
45
|
|
|
45
46
|
# AllocContext
|
|
46
47
|
|
|
47
|
-
[](https://glama.ai/mcp/servers/AllocContext/alloc-context)
|
|
48
|
+
[](https://smithery.ai/servers/alloccontext/alloc-context)
|
|
49
49
|
|
|
50
50
|
mcp-name: io.github.AllocContext/alloc-context
|
|
51
51
|
|
|
@@ -54,6 +54,10 @@ holdings, holdings-scoped market data, sentiment, macro, and regime; optional
|
|
|
54
54
|
allocation analysis when you supply targets. Deterministic JSON over MCP with
|
|
55
55
|
x402 pay-per-call on Base.
|
|
56
56
|
|
|
57
|
+
**New here?** [Agent on-ramp (~2 min)](docs/agent-onramp.md) — copy-paste path to
|
|
58
|
+
your first ContextBundle. **Organization:**
|
|
59
|
+
[AllocContext on GitHub](https://github.com/AllocContext) — overview and integration links.
|
|
60
|
+
|
|
57
61
|
> **Privacy:** nothing stored · one-time read-only · pass-through only — your
|
|
58
62
|
> keys and portfolio never persist on our servers. See [USE.md](docs/USE.md).
|
|
59
63
|
|
|
@@ -143,8 +147,10 @@ See [mcp.md](docs/mcp.md) for arguments, pricing, and resources.
|
|
|
143
147
|
## Self-host and development
|
|
144
148
|
|
|
145
149
|
Run ingest and MCP entirely on your machine — no x402 upstream required.
|
|
146
|
-
See [self-hosting.md](docs/self-hosting.md) (`self_host: true` in user config)
|
|
147
|
-
|
|
150
|
+
See [self-hosting.md](docs/self-hosting.md) (`self_host: true` in user config),
|
|
151
|
+
[local-dev.md](docs/local-dev.md) for the native dev stack (`./scripts/dev-up.sh`),
|
|
152
|
+
or `./docker/up.sh` / [docker-self-host.md](docs/docker-self-host.md) for Docker
|
|
153
|
+
on loopback `:8000`.
|
|
148
154
|
|
|
149
155
|
```bash
|
|
150
156
|
git clone git@github.com:AllocContext/alloc-context.git
|
|
@@ -173,6 +179,10 @@ HTTP MCP + x402: [mcp-http.md](docs/mcp-http.md). CLI entry point:
|
|
|
173
179
|
|
|
174
180
|
| Document | Purpose |
|
|
175
181
|
|----------|---------|
|
|
182
|
+
| [docs/agent-onramp.md](docs/agent-onramp.md) | **Start here** — ~2 min to first ContextBundle |
|
|
183
|
+
| [docs/deterministic-context-mcp-pattern.md](docs/deterministic-context-mcp-pattern.md) | Reusable ingest → rollup → MCP → x402 pattern |
|
|
184
|
+
| [docs/langchain-integration.md](docs/langchain-integration.md) | LangChain tools for hosted MCP (x402) |
|
|
185
|
+
| [docs/docker-self-host.md](docs/docker-self-host.md) | Docker Compose self-host evaluation |
|
|
176
186
|
| [docs/cursor-mcp.md](docs/cursor-mcp.md) | Cursor stdio MCP (bridge default) |
|
|
177
187
|
| [docs/user-config.md](docs/user-config.md) | Bridge `user.yaml` reference |
|
|
178
188
|
| [docs/mcp.md](docs/mcp.md) | MCP tools and x402 |
|
|
@@ -56,6 +56,8 @@ alloccontext/ingest/exchange/live.py
|
|
|
56
56
|
alloccontext/ingest/exchange/portfolio.py
|
|
57
57
|
alloccontext/ingest/exchange/registry.py
|
|
58
58
|
alloccontext/ingest/exchange/types.py
|
|
59
|
+
alloccontext/integrations/__init__.py
|
|
60
|
+
alloccontext/integrations/langchain.py
|
|
59
61
|
alloccontext/mcp/__init__.py
|
|
60
62
|
alloccontext/mcp/assets.py
|
|
61
63
|
alloccontext/mcp/bazaar.py
|
|
@@ -130,6 +132,7 @@ tests/test_ingest_runner.py
|
|
|
130
132
|
tests/test_ingest_store_integration.py
|
|
131
133
|
tests/test_kalshi_api.py
|
|
132
134
|
tests/test_kraken_portfolio.py
|
|
135
|
+
tests/test_langchain_integration.py
|
|
133
136
|
tests/test_live_ingest_handlers.py
|
|
134
137
|
tests/test_macro.py
|
|
135
138
|
tests/test_market_breadth.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Optional framework integrations (LangChain, etc.)."""
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""LangChain tools for the hosted AllocContext MCP (x402 on Base)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from dataclasses import replace
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from alloccontext.mcp.bazaar import OFFICIAL_HOSTED_MCP_URL, mcp_tool_specs
|
|
10
|
+
from alloccontext.mcp.upstream import call_upstream_tool
|
|
11
|
+
from alloccontext.user_config import (
|
|
12
|
+
DEFAULT_UPSTREAM_URL,
|
|
13
|
+
UserConfig,
|
|
14
|
+
load_user_config,
|
|
15
|
+
resolve_user_config_path,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
DEFAULT_HOSTED_TOOLS = (
|
|
19
|
+
"get_market_context",
|
|
20
|
+
"get_context_bundle",
|
|
21
|
+
"get_rebalance_plan",
|
|
22
|
+
"check_allocation_band",
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def hosted_user_config(*, upstream_url: str | None = None) -> UserConfig:
|
|
27
|
+
"""Minimal bridge user config for hosted-only LangChain calls."""
|
|
28
|
+
base = UserConfig.empty()
|
|
29
|
+
return replace(
|
|
30
|
+
base,
|
|
31
|
+
upstream=upstream_url or DEFAULT_UPSTREAM_URL,
|
|
32
|
+
self_host=False,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def resolve_hosted_user_config(*, upstream_url: str | None = None) -> UserConfig:
|
|
37
|
+
"""Hosted bridge config: ``user.yaml`` when present, else env-only defaults.
|
|
38
|
+
|
|
39
|
+
Loads ``~/.config/alloc-context/user.yaml`` (or ``ALLOC_CONTEXT_USER_CONFIG``)
|
|
40
|
+
so ``x402.payer_private_key_file`` and inline payer keys work like the stdio
|
|
41
|
+
bridge. Exchange blocks in ``user.yaml`` are ignored for hosted tool calls.
|
|
42
|
+
"""
|
|
43
|
+
path = resolve_user_config_path()
|
|
44
|
+
if path is None or not path.is_file():
|
|
45
|
+
return hosted_user_config(upstream_url=upstream_url)
|
|
46
|
+
|
|
47
|
+
loaded = load_user_config(path)
|
|
48
|
+
upstream = upstream_url or loaded.upstream
|
|
49
|
+
if loaded.self_host:
|
|
50
|
+
return replace(
|
|
51
|
+
hosted_user_config(upstream_url=upstream),
|
|
52
|
+
x402=loaded.x402,
|
|
53
|
+
path=loaded.path,
|
|
54
|
+
)
|
|
55
|
+
return replace(loaded, upstream=upstream, self_host=False)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _args_model(tool_name: str, input_schema: dict[str, Any]) -> type[Any]:
|
|
59
|
+
from pydantic import Field, create_model
|
|
60
|
+
|
|
61
|
+
properties = input_schema.get("properties") or {}
|
|
62
|
+
required = frozenset(input_schema.get("required") or [])
|
|
63
|
+
field_defs: dict[str, Any] = {}
|
|
64
|
+
for key, prop in properties.items():
|
|
65
|
+
description = str(prop.get("description") or "")
|
|
66
|
+
if key in required:
|
|
67
|
+
field_defs[key] = (Any, Field(description=description))
|
|
68
|
+
else:
|
|
69
|
+
field_defs[key] = (Any | None, Field(default=None, description=description))
|
|
70
|
+
model_name = "".join(part.title() for part in tool_name.split("_")) + "Input"
|
|
71
|
+
return create_model(model_name, **field_defs)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def build_hosted_langchain_tools(
|
|
75
|
+
user: UserConfig | None = None,
|
|
76
|
+
*,
|
|
77
|
+
tool_names: tuple[str, ...] | None = None,
|
|
78
|
+
) -> list[Any]:
|
|
79
|
+
"""Return LangChain tools that call the hosted MCP with x402 payment.
|
|
80
|
+
|
|
81
|
+
Requires ``langchain-core`` and ``alloc-context[hosted]``. Configure an x402
|
|
82
|
+
payer via ``EVM_PRIVATE_KEY``, ``user.yaml`` (``x402.payer_private_key_file``),
|
|
83
|
+
or pass a ``UserConfig`` explicitly.
|
|
84
|
+
"""
|
|
85
|
+
try:
|
|
86
|
+
from langchain_core.tools import StructuredTool
|
|
87
|
+
except ImportError as exc:
|
|
88
|
+
raise ImportError(
|
|
89
|
+
"LangChain integration requires langchain-core: pip install langchain-core"
|
|
90
|
+
) from exc
|
|
91
|
+
|
|
92
|
+
user = user or resolve_hosted_user_config()
|
|
93
|
+
selected = frozenset(tool_names or DEFAULT_HOSTED_TOOLS)
|
|
94
|
+
specs = {spec["tool_name"]: spec for spec in mcp_tool_specs()}
|
|
95
|
+
missing = selected - specs.keys()
|
|
96
|
+
if missing:
|
|
97
|
+
raise ValueError(f"Unknown MCP tool(s): {', '.join(sorted(missing))}")
|
|
98
|
+
|
|
99
|
+
tools: list[Any] = []
|
|
100
|
+
for name in tool_names or DEFAULT_HOSTED_TOOLS:
|
|
101
|
+
spec = specs[name]
|
|
102
|
+
|
|
103
|
+
def _invoke(*, _tool_name: str = name, **kwargs: Any) -> str:
|
|
104
|
+
arguments = {key: value for key, value in kwargs.items() if value is not None}
|
|
105
|
+
payload = call_upstream_tool(user, _tool_name, arguments)
|
|
106
|
+
if payload.get("reason") == "upstream_payment_required":
|
|
107
|
+
message = payload.get("message") or (
|
|
108
|
+
"Configure an x402 payer wallet to call hosted upstream."
|
|
109
|
+
)
|
|
110
|
+
raise RuntimeError(message)
|
|
111
|
+
return json.dumps(payload, separators=(",", ":"))
|
|
112
|
+
|
|
113
|
+
tools.append(
|
|
114
|
+
StructuredTool(
|
|
115
|
+
name=name,
|
|
116
|
+
description=spec["description"],
|
|
117
|
+
func=_invoke,
|
|
118
|
+
args_schema=_args_model(name, spec["input_schema"]),
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
return tools
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def official_hosted_mcp_url() -> str:
|
|
125
|
+
return OFFICIAL_HOSTED_MCP_URL
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from x402.extensions.bazaar import (
|
|
7
|
+
DeclareMcpDiscoveryConfig,
|
|
8
|
+
OutputConfig,
|
|
9
|
+
declare_discovery_extension,
|
|
10
|
+
declare_mcp_discovery_extension,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from alloccontext.mcp.tool_catalog import (
|
|
14
|
+
MCP_SERVER_PROMPTS,
|
|
15
|
+
MCP_SERVER_RESOURCES,
|
|
16
|
+
MCP_TOOL_NAMES,
|
|
17
|
+
MCP_TOOL_SPECS,
|
|
18
|
+
mcp_tool_specs,
|
|
19
|
+
server_card_tool_entry,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
SERVICE_NAME = "AllocContext"
|
|
23
|
+
OFFICIAL_HOSTED_MCP_URL = "https://mcp.alloc-context.com/mcp"
|
|
24
|
+
USE_DOCS_PATH = "docs/USE.md"
|
|
25
|
+
|
|
26
|
+
PRIVACY_COMPACT_COPY = (
|
|
27
|
+
"Privacy: nothing stored. One-time read-only fetch. Pass-through only — "
|
|
28
|
+
"your keys and portfolio never persist on our servers."
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
PRIVACY_PILLAR_MARKERS = (
|
|
32
|
+
"nothing stored",
|
|
33
|
+
"one-time read-only",
|
|
34
|
+
"pass-through only",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
LICENSE_DISCOVERY_LINE = (
|
|
38
|
+
"Source-available (Elastic License 2.0). Self-host friendly. Official hosted "
|
|
39
|
+
f"MCP only at {OFFICIAL_HOSTED_MCP_URL} — see {USE_DOCS_PATH}."
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
LICENSE_MARKERS = (
|
|
43
|
+
"elastic license",
|
|
44
|
+
"self-host",
|
|
45
|
+
USE_DOCS_PATH.lower(),
|
|
46
|
+
OFFICIAL_HOSTED_MCP_URL.lower(),
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
SERVICE_TITLE = (
|
|
50
|
+
"AllocContext — portfolio-aware crypto context for agents (MCP + x402)"
|
|
51
|
+
)
|
|
52
|
+
# CDP Bazaar indexes service_name (≤32 chars) and up to five tags from payments.
|
|
53
|
+
BAZAAR_SERVICE_NAME = "AllocContext portfolio MCP"
|
|
54
|
+
SERVICE_TAGS = (
|
|
55
|
+
"crypto",
|
|
56
|
+
"cryptocurrency",
|
|
57
|
+
"bitcoin",
|
|
58
|
+
"btc",
|
|
59
|
+
"ethereum",
|
|
60
|
+
"eth",
|
|
61
|
+
"holdings",
|
|
62
|
+
"portfolio",
|
|
63
|
+
"allocation",
|
|
64
|
+
"rebalance",
|
|
65
|
+
"sentiment",
|
|
66
|
+
"macro",
|
|
67
|
+
"coinbase",
|
|
68
|
+
"kraken",
|
|
69
|
+
"agent-tools",
|
|
70
|
+
"mcp",
|
|
71
|
+
"x402",
|
|
72
|
+
)
|
|
73
|
+
BAZAAR_INDEX_TAGS = (
|
|
74
|
+
"crypto",
|
|
75
|
+
"cryptocurrency",
|
|
76
|
+
"portfolio",
|
|
77
|
+
"holdings",
|
|
78
|
+
"btc",
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
DISCOVERY_KEYWORD_MARKERS = (
|
|
82
|
+
"crypto",
|
|
83
|
+
"cryptocurrency",
|
|
84
|
+
"digital assets",
|
|
85
|
+
"crypto portfolio",
|
|
86
|
+
"portfolio allocation",
|
|
87
|
+
"portfolio context",
|
|
88
|
+
"allocation drift",
|
|
89
|
+
"rebalance plan",
|
|
90
|
+
"fear and greed",
|
|
91
|
+
"etf flows",
|
|
92
|
+
"holdings",
|
|
93
|
+
"holdings-scoped",
|
|
94
|
+
"coinbase",
|
|
95
|
+
"kraken",
|
|
96
|
+
"market context",
|
|
97
|
+
"sentiment",
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
LISTING_DESCRIPTION = (
|
|
101
|
+
"Portfolio-aware crypto context for AI agents: discover holdings and "
|
|
102
|
+
"holdings-scoped market, sentiment, macro, and regime; optional allocation "
|
|
103
|
+
"analysis and rebalance math. Fused backdrop (Fear & Greed, Kalshi, ETF "
|
|
104
|
+
"flows), optional live portfolio reads (e.g. Coinbase, Kraken). Structured "
|
|
105
|
+
"JSON only — no LLM. "
|
|
106
|
+
f"{PRIVACY_COMPACT_COPY} "
|
|
107
|
+
"Source-available (Elastic License 2.0); self-host friendly; official hosted "
|
|
108
|
+
f"MCP at {OFFICIAL_HOSTED_MCP_URL} — see {USE_DOCS_PATH}."
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
def smoke_tool_arguments(tool_name: str) -> dict[str, Any]:
|
|
112
|
+
"""Example args for paid smoke / re-index scripts (hosted-safe defaults)."""
|
|
113
|
+
from datetime import datetime, timedelta, timezone
|
|
114
|
+
|
|
115
|
+
for spec in MCP_TOOL_SPECS:
|
|
116
|
+
if spec["tool_name"] != tool_name:
|
|
117
|
+
continue
|
|
118
|
+
args = dict(spec["example"])
|
|
119
|
+
now = datetime.now(timezone.utc)
|
|
120
|
+
if tool_name == "get_context_at":
|
|
121
|
+
args["as_of"] = now.strftime("%Y-%m-%dT%H:%M:%S+00:00")
|
|
122
|
+
args["match"] = "at_or_before"
|
|
123
|
+
args.setdefault("scope", "daily")
|
|
124
|
+
elif tool_name == "get_context_delta":
|
|
125
|
+
# Hosted ingest keeps recent hourly snapshots; 2h back finds a prior point.
|
|
126
|
+
prior = now - timedelta(hours=2)
|
|
127
|
+
args["prior_as_of"] = prior.strftime("%Y-%m-%dT%H:%M:%S+00:00")
|
|
128
|
+
args.setdefault("scope", "daily")
|
|
129
|
+
return args
|
|
130
|
+
raise KeyError(f"unknown MCP tool: {tool_name}")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def public_mcp_url(*, base_url: str, mcp_path: str) -> str:
|
|
134
|
+
return f"{base_url.rstrip('/')}{mcp_path}"
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def resolve_public_base_url() -> str | None:
|
|
138
|
+
for key in ("X402_PUBLIC_URL", "ALLOC_CONTEXT_MCP_PUBLIC_URL"):
|
|
139
|
+
value = os.environ.get(key, "").strip()
|
|
140
|
+
if value:
|
|
141
|
+
return value.rstrip("/")
|
|
142
|
+
return None
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def build_mcp_tool_extensions() -> dict[str, dict[str, Any]]:
|
|
146
|
+
"""Per-tool Bazaar MCP extensions keyed by tool name."""
|
|
147
|
+
extensions: dict[str, dict[str, Any]] = {}
|
|
148
|
+
for spec in MCP_TOOL_SPECS:
|
|
149
|
+
extensions[spec["tool_name"]] = declare_mcp_discovery_extension(
|
|
150
|
+
DeclareMcpDiscoveryConfig(
|
|
151
|
+
tool_name=spec["tool_name"],
|
|
152
|
+
description=spec["description"],
|
|
153
|
+
transport="streamable-http",
|
|
154
|
+
input_schema=spec["input_schema"],
|
|
155
|
+
example=spec["example"],
|
|
156
|
+
output=OutputConfig(example=spec["output_example"]),
|
|
157
|
+
)
|
|
158
|
+
)
|
|
159
|
+
return extensions
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def build_http_route_extensions() -> dict[str, Any]:
|
|
163
|
+
"""Bazaar extension for the paid POST /mcp streamable HTTP endpoint."""
|
|
164
|
+
primary = MCP_TOOL_SPECS[0]
|
|
165
|
+
return declare_discovery_extension(
|
|
166
|
+
input={
|
|
167
|
+
"jsonrpc": "2.0",
|
|
168
|
+
"method": "tools/call",
|
|
169
|
+
"params": {
|
|
170
|
+
"name": primary["tool_name"],
|
|
171
|
+
"arguments": primary["example"],
|
|
172
|
+
},
|
|
173
|
+
"id": 1,
|
|
174
|
+
},
|
|
175
|
+
input_schema={
|
|
176
|
+
"type": "object",
|
|
177
|
+
"properties": {
|
|
178
|
+
"jsonrpc": {"type": "string", "const": "2.0"},
|
|
179
|
+
"method": {
|
|
180
|
+
"type": "string",
|
|
181
|
+
"description": "MCP JSON-RPC method (e.g. tools/call).",
|
|
182
|
+
},
|
|
183
|
+
"params": {
|
|
184
|
+
"type": "object",
|
|
185
|
+
"properties": {
|
|
186
|
+
"name": {
|
|
187
|
+
"type": "string",
|
|
188
|
+
"enum": list(MCP_TOOL_NAMES),
|
|
189
|
+
"description": (
|
|
190
|
+
"AllocContext tool: get_market_context, "
|
|
191
|
+
"get_context_bundle, get_rebalance_plan, "
|
|
192
|
+
"get_portfolio_state, check_allocation_band, "
|
|
193
|
+
"get_context_at, get_context_delta, or "
|
|
194
|
+
"check_allocation_bands."
|
|
195
|
+
),
|
|
196
|
+
},
|
|
197
|
+
"arguments": {
|
|
198
|
+
"type": "object",
|
|
199
|
+
"description": "Tool-specific arguments object.",
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
"required": ["name"],
|
|
203
|
+
},
|
|
204
|
+
"id": {"type": ["integer", "string"]},
|
|
205
|
+
},
|
|
206
|
+
"required": ["jsonrpc", "method", "params"],
|
|
207
|
+
},
|
|
208
|
+
body_type="json",
|
|
209
|
+
output=OutputConfig(
|
|
210
|
+
example={
|
|
211
|
+
"jsonrpc": "2.0",
|
|
212
|
+
"id": 1,
|
|
213
|
+
"result": primary["output_example"],
|
|
214
|
+
}
|
|
215
|
+
),
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def build_llms_txt(
|
|
220
|
+
*,
|
|
221
|
+
public_url: str,
|
|
222
|
+
mcp_path: str,
|
|
223
|
+
accepted_stables: tuple[str, ...] = ("USDC", "EURC"),
|
|
224
|
+
) -> str:
|
|
225
|
+
endpoint = public_mcp_url(base_url=public_url, mcp_path=mcp_path)
|
|
226
|
+
tool_lines = "\n".join(
|
|
227
|
+
f"- `{spec['tool_name']}` — {spec['description']}" for spec in MCP_TOOL_SPECS
|
|
228
|
+
)
|
|
229
|
+
return f"""# {SERVICE_TITLE}
|
|
230
|
+
|
|
231
|
+
{LISTING_DESCRIPTION}
|
|
232
|
+
|
|
233
|
+
## Privacy
|
|
234
|
+
|
|
235
|
+
Nothing stored. One-time read-only fetch. Pass-through only — your keys and
|
|
236
|
+
portfolio never persist on our servers.
|
|
237
|
+
|
|
238
|
+
## License
|
|
239
|
+
|
|
240
|
+
{LICENSE_DISCOVERY_LINE}
|
|
241
|
+
|
|
242
|
+
## Paid MCP (x402, Base stablecoins)
|
|
243
|
+
|
|
244
|
+
- Endpoint: `{endpoint}`
|
|
245
|
+
- Transport: streamable HTTP (`POST {mcp_path}`)
|
|
246
|
+
- Health: `{public_url.rstrip('/')}/health` (free)
|
|
247
|
+
- Payment: x402 exact scheme on Base; payer picks one of
|
|
248
|
+
**{", ".join(accepted_stables)}** (USD-pegged; bridge to Base first).
|
|
249
|
+
- Pricing: **$0.02** cached context/math; **$0.05** live ingest or live portfolio.
|
|
250
|
+
|
|
251
|
+
## Tools
|
|
252
|
+
|
|
253
|
+
{tool_lines}
|
|
254
|
+
|
|
255
|
+
## Search keywords
|
|
256
|
+
|
|
257
|
+
bitcoin, ethereum, btc, eth, crypto, cryptocurrency, digital assets, altcoin,
|
|
258
|
+
stablecoin, crypto portfolio, portfolio allocation, portfolio context, holdings,
|
|
259
|
+
holdings-scoped, coinbase, kraken, market context, market data, sentiment,
|
|
260
|
+
macro calendar, etf flows, allocation drift, allocation bands, rebalance plan,
|
|
261
|
+
fear and greed, fear greed index, nav, agent tools, ai agents, mcp, x402,
|
|
262
|
+
model context protocol, context bundle
|
|
263
|
+
|
|
264
|
+
## Examples
|
|
265
|
+
|
|
266
|
+
Redacted tool JSON samples (evaluate before paying):
|
|
267
|
+
https://github.com/AllocContext/alloc-context/blob/main/docs/examples.md
|
|
268
|
+
"""
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def build_well_known_x402(
|
|
272
|
+
*,
|
|
273
|
+
public_url: str,
|
|
274
|
+
mcp_path: str,
|
|
275
|
+
pay_to: str,
|
|
276
|
+
price_light: str = "$0.02",
|
|
277
|
+
price_heavy: str = "$0.05",
|
|
278
|
+
network: str = "eip155:84532",
|
|
279
|
+
accepted_stables: tuple[str, ...] = ("USDC", "EURC"),
|
|
280
|
+
) -> dict[str, Any]:
|
|
281
|
+
endpoint = public_mcp_url(base_url=public_url, mcp_path=mcp_path)
|
|
282
|
+
return {
|
|
283
|
+
"name": SERVICE_NAME,
|
|
284
|
+
"title": SERVICE_TITLE,
|
|
285
|
+
"description": LISTING_DESCRIPTION,
|
|
286
|
+
"tags": list(SERVICE_TAGS),
|
|
287
|
+
"resources": [
|
|
288
|
+
{
|
|
289
|
+
"url": endpoint,
|
|
290
|
+
"type": "http",
|
|
291
|
+
"description": LISTING_DESCRIPTION,
|
|
292
|
+
"tools": [
|
|
293
|
+
{
|
|
294
|
+
"name": spec["tool_name"],
|
|
295
|
+
"description": spec["description"],
|
|
296
|
+
"inputSchema": spec["input_schema"],
|
|
297
|
+
}
|
|
298
|
+
for spec in MCP_TOOL_SPECS
|
|
299
|
+
],
|
|
300
|
+
}
|
|
301
|
+
],
|
|
302
|
+
"payment": {
|
|
303
|
+
"scheme": "exact",
|
|
304
|
+
"payTo": pay_to,
|
|
305
|
+
"pricing": {
|
|
306
|
+
"cached_context_and_math": price_light,
|
|
307
|
+
"live_ingest_or_portfolio": price_heavy,
|
|
308
|
+
"network": network,
|
|
309
|
+
"assets": list(accepted_stables),
|
|
310
|
+
"note": (
|
|
311
|
+
"Payer chooses one listed stable on Base; amounts are "
|
|
312
|
+
"USD-pegged per call tier."
|
|
313
|
+
),
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def build_mcp_server_card(*, version: str) -> dict[str, Any]:
|
|
320
|
+
"""Smithery static server card (SEP-1649) — free metadata when POST /mcp is x402."""
|
|
321
|
+
return {
|
|
322
|
+
"serverInfo": {
|
|
323
|
+
"name": SERVICE_TITLE,
|
|
324
|
+
"version": version,
|
|
325
|
+
"description": LISTING_DESCRIPTION,
|
|
326
|
+
},
|
|
327
|
+
"authentication": {
|
|
328
|
+
"required": True,
|
|
329
|
+
"schemes": ["x402"],
|
|
330
|
+
"description": (
|
|
331
|
+
"x402 exact payment on Base mainnet (USDC or EURC) per tool call; "
|
|
332
|
+
"see /.well-known/x402.json for pricing."
|
|
333
|
+
),
|
|
334
|
+
},
|
|
335
|
+
"tools": [server_card_tool_entry(spec) for spec in MCP_TOOL_SPECS],
|
|
336
|
+
"resources": list(MCP_SERVER_RESOURCES),
|
|
337
|
+
"prompts": list(MCP_SERVER_PROMPTS),
|
|
338
|
+
}
|