vanoe-intelligence-mcp 0.1.0__py3-none-any.whl

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.
mcp_server/__init__.py ADDED
File without changes
mcp_server/__main__.py ADDED
@@ -0,0 +1,4 @@
1
+ from mcp_server.server import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
mcp_server/client.py ADDED
@@ -0,0 +1,72 @@
1
+ """Thin HTTP client for the Vanoe Market Intelligence API — one function per
2
+ /v1 GET endpoint.
3
+
4
+ Auth: Authorization: Bearer <VANOE_API_KEY>. Base URL: VANOE_API_BASE_URL
5
+ (defaults to https://api.vanoe.ai; point it at a local instance for dev).
6
+ The older INTEL_API_KEY / INTEL_API_BASE_URL names are still honoured.
7
+
8
+ On any non-2xx response, raises IntelAPIError carrying the API's own
9
+ structured {"error": {code, message, request_id}} body when present, so a
10
+ tool caller sees the SAME message a developer would get calling the API
11
+ directly (e.g. "Monthly quota exceeded..."), not a raw HTTP status.
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import os
16
+
17
+ import httpx
18
+
19
+ DEFAULT_BASE_URL = "https://api.vanoe.ai"
20
+ SIGNUP_URL = "https://api.vanoe.ai/signup"
21
+ _TIMEOUT_SECONDS = 20
22
+
23
+
24
+ class IntelAPIError(Exception):
25
+ def __init__(self, status_code: int, code: str, message: str):
26
+ self.status_code = status_code
27
+ self.code = code
28
+ self.message = message
29
+ super().__init__(f"{message} (code={code}, http_status={status_code})")
30
+
31
+
32
+ class MissingApiKeyError(Exception):
33
+ def __init__(self):
34
+ super().__init__(
35
+ f"VANOE_API_KEY is not set. Get a free key at {SIGNUP_URL} and set the "
36
+ "VANOE_API_KEY environment variable before starting this MCP server."
37
+ )
38
+
39
+
40
+ def _env(new: str, old: str) -> str | None:
41
+ return os.environ.get(new) or os.environ.get(old)
42
+
43
+
44
+ def _base_url() -> str:
45
+ return (_env("VANOE_API_BASE_URL", "INTEL_API_BASE_URL") or DEFAULT_BASE_URL).rstrip("/")
46
+
47
+
48
+ def _headers() -> dict:
49
+ key = _env("VANOE_API_KEY", "INTEL_API_KEY")
50
+ if not key:
51
+ raise MissingApiKeyError()
52
+ return {"Authorization": f"Bearer {key}"}
53
+
54
+
55
+ async def get(path: str, params: dict | None = None) -> dict:
56
+ url = f"{_base_url()}{path}"
57
+ async with httpx.AsyncClient(timeout=_TIMEOUT_SECONDS) as http:
58
+ try:
59
+ r = await http.get(url, params=params, headers=_headers())
60
+ except httpx.RequestError as e:
61
+ raise IntelAPIError(0, "connection_error",
62
+ f"Could not reach the API at {_base_url()}: {e}") from e
63
+
64
+ if r.status_code >= 400:
65
+ try:
66
+ body = r.json()
67
+ err = body.get("error", {})
68
+ raise IntelAPIError(r.status_code, err.get("code", "unknown_error"),
69
+ err.get("message", r.text))
70
+ except ValueError:
71
+ raise IntelAPIError(r.status_code, "unknown_error", r.text)
72
+ return r.json()
mcp_server/server.py ADDED
@@ -0,0 +1,129 @@
1
+ """Vanoe Market Intelligence MCP server — exposes the API's signals as MCP
2
+ tools so any MCP-capable AI assistant (Claude Desktop, Claude Code, etc.) can
3
+ call them directly.
4
+
5
+ This is a thin authenticated client, not a free bypass: every tool call
6
+ makes a real, metered call against your plan using your own VANOE_API_KEY,
7
+ exactly like calling the REST API yourself.
8
+
9
+ Setup:
10
+ VANOE_API_KEY your sk_live_... key (required) — free at https://api.vanoe.ai/signup
11
+ VANOE_API_BASE_URL defaults to https://api.vanoe.ai; point at a local
12
+ instance for development
13
+
14
+ Run: vanoe-intelligence-mcp (or: python -m mcp_server) — stdio transport,
15
+ for Claude Desktop / Claude Code style MCP client configs.
16
+
17
+ Tools never raise — every failure (missing key, network error, an API
18
+ 4xx/5xx) is caught and returned as a {"error": {...}} JSON payload in the
19
+ tool result, not an exception. This is deliberate: a raised exception from a
20
+ tool crashes with UnexpectedToolError rather than being cleanly surfaced to
21
+ the caller (confirmed against the installed SDK version), so tools return a
22
+ predictable JSON shape either way instead of depending on that behavior.
23
+ """
24
+ from __future__ import annotations
25
+
26
+ import json
27
+
28
+ from mcp.server.mcpserver import MCPServer
29
+
30
+ from mcp_server import client
31
+
32
+ mcp = MCPServer(
33
+ name="vanoe-market-intelligence",
34
+ version="0.1.0",
35
+ instructions=(
36
+ "Vanoe Market Intelligence signals: four-stage trend analysis, Point & "
37
+ "Figure patterns, sector breadth, a composite verdict score, macro "
38
+ "regime, insider filings, and the economic calendar. All output is "
39
+ "informational data only — not financial advice, and not a "
40
+ "recommendation, solicitation, or offer to buy or sell any security."
41
+ ),
42
+ )
43
+
44
+
45
+ async def _call(path: str, params: dict | None = None) -> str:
46
+ """GET the API and return the JSON result as a string, or a
47
+ {"error": {...}} JSON string on any failure — tools never raise."""
48
+ try:
49
+ data = await client.get(path, params)
50
+ return json.dumps(data, indent=2)
51
+ except client.MissingApiKeyError as e:
52
+ return json.dumps({"error": {"code": "missing_api_key", "message": str(e)}}, indent=2)
53
+ except client.IntelAPIError as e:
54
+ return json.dumps(
55
+ {"error": {"code": e.code, "message": e.message, "http_status": e.status_code}},
56
+ indent=2,
57
+ )
58
+ except Exception as e: # last-resort guard — tools must never raise
59
+ return json.dumps({"error": {"code": "mcp_server_error", "message": str(e)}}, indent=2)
60
+
61
+
62
+ @mcp.tool(description="Macro regime (risk_on/neutral/risk_off), rates, inflation, yield curve, positioning.")
63
+ async def get_macro() -> str:
64
+ return await _call("/v1/macro")
65
+
66
+
67
+ @mcp.tool(description=(
68
+ "Stage analysis (1=Basing, 2=Advancing, 3=Topping, 4=Declining) "
69
+ "for up to 50 tickers. Pass tickers as a comma-separated string, e.g. "
70
+ "'AAPL,MSFT,NVDA'."
71
+ ))
72
+ async def get_stage(tickers: str) -> str:
73
+ return await _call("/v1/signals/stage", {"tickers": tickers})
74
+
75
+
76
+ @mcp.tool(description=(
77
+ "Historical weekly trend-stage series for one ticker, walk-forward "
78
+ "computed (no lookahead) — the only commercial source of trend "
79
+ "stages as a backtest-safe data series. weeks defaults to 104, max 520."
80
+ ))
81
+ async def get_stage_history(ticker: str, weeks: int = 104) -> str:
82
+ return await _call("/v1/signals/stage/history", {"ticker": ticker, "weeks": weeks})
83
+
84
+
85
+ @mcp.tool(description=(
86
+ "Point & Figure signal, breakout pattern, and relative strength vs SPY, "
87
+ "for up to 50 tickers. Pass tickers as a comma-separated string."
88
+ ))
89
+ async def get_pnf(tickers: str) -> str:
90
+ return await _call("/v1/signals/pnf", {"tickers": tickers})
91
+
92
+
93
+ @mcp.tool(description="Bullish-percent breadth by sector — the % of names in each sector on a Point & Figure buy signal.")
94
+ async def get_sector_breadth() -> str:
95
+ return await _call("/v1/sector-breadth")
96
+
97
+
98
+ @mcp.tool(description=(
99
+ "Composite verdict for one ticker: fuses trend stage, Point & "
100
+ "Figure, insider-transaction clusters, and macro regime into a 0-100 "
101
+ "score, an accumulate/hold/distribute/avoid enum, and a plain-English "
102
+ "rationale. One call replaces separately calling stage, pnf, filings, "
103
+ "and macro."
104
+ ))
105
+ async def get_verdict(ticker: str) -> str:
106
+ return await _call(f"/v1/verdict/{ticker}")
107
+
108
+
109
+ @mcp.tool(description="Recent SEC insider transactions (Form 4) and 8-K filings for one ticker.")
110
+ async def get_filings(ticker: str) -> str:
111
+ return await _call(f"/v1/filings/{ticker}")
112
+
113
+
114
+ @mcp.tool(description="Upcoming macro events (FOMC, CPI, payrolls) within a look-ahead window in days (default 10, max 60).")
115
+ async def get_calendar(within_days: int = 10) -> str:
116
+ return await _call("/v1/calendar", {"within_days": within_days})
117
+
118
+
119
+ @mcp.tool(description="Your current billing-period usage and remaining credit quota on your plan. Free — never counts against quota.")
120
+ async def get_usage() -> str:
121
+ return await _call("/v1/usage")
122
+
123
+
124
+ def main() -> None:
125
+ mcp.run()
126
+
127
+
128
+ if __name__ == "__main__":
129
+ main()
@@ -0,0 +1,143 @@
1
+ Metadata-Version: 2.4
2
+ Name: vanoe-intelligence-mcp
3
+ Version: 0.1.0
4
+ Summary: MCP server for the Vanoe Market Intelligence API: composite verdicts, trend-stage analysis, Point & Figure, sector breadth, macro regime and SEC filings as tools for Claude and other MCP clients.
5
+ Author-email: VanoeAI Ltd <support@vanoe.ai>
6
+ License: MIT
7
+ Project-URL: Homepage, https://api.vanoe.ai
8
+ Project-URL: Documentation, https://api.vanoe.ai/docs
9
+ Project-URL: Get an API key, https://api.vanoe.ai/signup
10
+ Keywords: mcp,model-context-protocol,finance,stocks,trading,signals,claude
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Financial and Insurance Industry
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Office/Business :: Financial :: Investment
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: mcp<3.0,>=2.0
25
+ Requires-Dist: httpx>=0.27
26
+ Provides-Extra: test
27
+ Requires-Dist: pytest>=8.0; extra == "test"
28
+ Requires-Dist: pytest-asyncio>=0.24; extra == "test"
29
+ Dynamic: license-file
30
+
31
+ # Vanoe Market Intelligence MCP server
32
+
33
+ Exposes the [Vanoe Market Intelligence API](https://api.vanoe.ai) as tools any
34
+ MCP-capable AI assistant (Claude Desktop, Claude Code, Cursor, etc.) can call
35
+ directly: a composite 0–100 verdict per ticker, trend-stage analysis (current
36
+ and a backtest-safe weekly history), Point & Figure signals, sector breadth,
37
+ the macro risk regime, SEC insider and 8-K filings, the macro calendar, and
38
+ your usage.
39
+
40
+ This is a thin authenticated client, not a free tier: every tool call is a
41
+ real, metered call against your plan using your own API key, exactly like
42
+ calling the REST API yourself. Get a free key (1,000 credits a month, no card)
43
+ at https://api.vanoe.ai/signup.
44
+
45
+ > Informational data only. Not financial advice. See https://api.vanoe.ai/terms.
46
+
47
+ ## Install
48
+
49
+ No install step is needed with [uv](https://docs.astral.sh/uv/) — `uvx` fetches
50
+ and runs the package on demand:
51
+
52
+ ```
53
+ uvx vanoe-intelligence-mcp
54
+ ```
55
+
56
+ Or install it permanently with `pipx install vanoe-intelligence-mcp` or
57
+ `pip install vanoe-intelligence-mcp`, which puts a `vanoe-intelligence-mcp`
58
+ command on your PATH.
59
+
60
+ ## Configure your MCP client
61
+
62
+ Set one environment variable, `VANOE_API_KEY`. `VANOE_API_BASE_URL` is optional
63
+ and defaults to `https://api.vanoe.ai`.
64
+
65
+ **Claude Desktop** (`claude_desktop_config.json`) and most other clients:
66
+
67
+ ```json
68
+ {
69
+ "mcpServers": {
70
+ "vanoe": {
71
+ "command": "uvx",
72
+ "args": ["vanoe-intelligence-mcp"],
73
+ "env": { "VANOE_API_KEY": "sk_live_your_key_here" }
74
+ }
75
+ }
76
+ }
77
+ ```
78
+
79
+ **Claude Code**:
80
+
81
+ ```
82
+ claude mcp add vanoe -e VANOE_API_KEY=sk_live_your_key_here -- uvx vanoe-intelligence-mcp
83
+ ```
84
+
85
+ Then ask your assistant things like *"What's the verdict on NVDA and why?"*,
86
+ *"Which stage are AAPL, MSFT and AMD in?"*, or *"Is the macro regime risk-on
87
+ right now?"* — it will call the tools below.
88
+
89
+ ## Tools
90
+
91
+ | Tool | API call | Credits |
92
+ |---|---|---|
93
+ | `get_verdict(ticker)` | `GET /v1/verdict/{ticker}` | 5 |
94
+ | `get_stage(tickers)` | `GET /v1/signals/stage` | 1 per ticker |
95
+ | `get_stage_history(ticker, weeks)` | `GET /v1/signals/stage/history` | 5 |
96
+ | `get_pnf(tickers)` | `GET /v1/signals/pnf` | 1 per ticker |
97
+ | `get_sector_breadth()` | `GET /v1/sector-breadth` | 1 |
98
+ | `get_macro()` | `GET /v1/macro` | 1 |
99
+ | `get_filings(ticker)` | `GET /v1/filings/{ticker}` | 1 |
100
+ | `get_calendar()` | `GET /v1/calendar` | 1 |
101
+ | `get_usage()` | `GET /v1/usage` | 0 |
102
+
103
+ Tools never raise. Any failure — missing key, network error, quota exceeded,
104
+ an unknown ticker — comes back as `{"error": {"code", "message",
105
+ "http_status"}}` carrying the API's own message, so the assistant can explain
106
+ what happened instead of crashing the call.
107
+
108
+ Webhook management is deliberately not exposed: it is account administration,
109
+ not something to do mid-conversation. Use the REST API for that.
110
+
111
+ ## Development
112
+
113
+ ```
114
+ cd mcp_server
115
+ python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
116
+ pip install -e '.[test]'
117
+ pytest tests
118
+ VANOE_API_BASE_URL=http://localhost:8000 VANOE_API_KEY=sk_live_… vanoe-intelligence-mcp
119
+ ```
120
+
121
+ Install it in its own virtualenv, separate from the API server's own
122
+ `requirements.txt`: the `mcp` SDK pulls in a newer `starlette` than the API's
123
+ FastAPI pin tolerates, and sharing one environment breaks the API server with
124
+ `Router.__init__() got an unexpected keyword argument 'on_startup'`. End users
125
+ never hit this (the MCP server runs on their machine, next to their assistant).
126
+
127
+ `tests/manual_e2e_smoke.py` runs the real server as a subprocess over stdio
128
+ against a reachable API with a real key; it is not part of `pytest`.
129
+
130
+ The MCP Python SDK is on v2: the server class is
131
+ `mcp.server.mcpserver.MCPServer` (older tutorials show `FastMCP` at a
132
+ different import path).
133
+
134
+ ## Releasing
135
+
136
+ ```
137
+ cd mcp_server
138
+ python -m build
139
+ twine upload dist/*
140
+ ```
141
+
142
+ Bump `version` in `pyproject.toml` first. The older `INTEL_API_KEY` /
143
+ `INTEL_API_BASE_URL` variable names are still accepted for compatibility.
@@ -0,0 +1,10 @@
1
+ mcp_server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ mcp_server/__main__.py,sha256=N_V5rRv2sQid3ZFBCCg4gQWGqkx1Q0ngJD-AOILW2MY,74
3
+ mcp_server/client.py,sha256=DlxLYY2SNanT98KvoY3nXdZU3fR7dYB49fl6NouCFV8,2511
4
+ mcp_server/server.py,sha256=IPIMhjIxcdZ_tuuQ7-0URmT_EABQt7g8sE2KBCnT8lk,5128
5
+ vanoe_intelligence_mcp-0.1.0.dist-info/licenses/LICENSE,sha256=KXcMg0Wuenl580V7_EaZN63N1Uerdg4cSsz6qJOvkn0,1068
6
+ vanoe_intelligence_mcp-0.1.0.dist-info/METADATA,sha256=m5qjGSJnT9zIDBmx8ENGPAqT9biH5iBGwb_4htXnlCQ,5328
7
+ vanoe_intelligence_mcp-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
8
+ vanoe_intelligence_mcp-0.1.0.dist-info/entry_points.txt,sha256=4Ys3dMpnOERNXK4DaDUMubQnOZYGSKqUYYaGF268-TA,66
9
+ vanoe_intelligence_mcp-0.1.0.dist-info/top_level.txt,sha256=R49ZBwHkJvJ-AJ8o9wy6MuCs0rvZpyCEuvdwhI4PpHs,11
10
+ vanoe_intelligence_mcp-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ vanoe-intelligence-mcp = mcp_server.server:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 VanoeAI Ltd
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ mcp_server