| 1 |
|
−import React from 'react' |
| 2 |
|
−// Placeholder — implemented by the owning agent (see App.jsx header). Keep the default export name. |
|
1 |
+// Integrations — MCP server, Claude Code / Cursor / Codex setup, skills pack. Owner: mcp-skills. |
|
2 |
+// Sub-routes: /integrations, /integrations/mcp, /claude-code, /cursor, /codex, /skills. |
|
3 |
+import React, { createContext, useContext, useMemo, useState } from 'react' |
|
4 |
+import { Link, NavLink, Route, Routes } from 'react-router-dom' |
|
5 |
+import IntCopyBlock, { IntCopyChip } from '../../components/IntCopyBlock.jsx' |
|
6 |
+import { PUBLIC_BASE, CONTACT_EMAIL } from '../../app/api.js' |
|
7 |
+import './integrations.css' |
|
8 |
+ |
|
9 |
+// --------------------------------------------------------------------------------------------- |
|
10 |
+// Constants (bump MCP_VERSION together with mcp/package.json and the tarball in public/downloads) |
|
11 |
+// --------------------------------------------------------------------------------------------- |
|
12 |
+export const MCP_VERSION = '1.0.0' |
|
13 |
+export const TARBALL_URL = `${PUBLIC_BASE}/downloads/hfmarketdata-mcp-${MCP_VERSION}.tgz` |
|
14 |
+export const SKILLS_ZIP_URL = `${PUBLIC_BASE}/downloads/hfmarketdata-skills.zip` |
|
15 |
+const KEY_PLACEHOLDER = 'hfmd_live_YOUR_KEY' |
|
16 |
+ |
|
17 |
+const TOOLS = [ |
|
18 |
+ ['search_symbols', 'Find tickers, futures roots or optionable underlyings', '/v1/{asset}/tickers · /v1/futures/roots · /v1/options/tickers'], |
|
19 |
+ ['get_bars', 'OHLCV 1min…1day for one or many symbols; vendor continuous; individual contracts (ESZ25)', '/v1/bars/… · /v1/futures/contract/{symbol}/bars'], |
|
20 |
+ ['get_futures_contracts', 'Every expiration of a root: expiry, FND, status, volume, OI', '/v1/futures/{root}/contracts'], |
|
21 |
+ ['get_futures_chain', 'Listed contracts as of a date, front month first', '/v1/futures/{root}/chain'], |
|
22 |
+ ['get_continuous', 'Server-built continuous series: roll, adjust, depth (roll dates in meta)', '/v1/futures/{root}/continuous'], |
|
23 |
+ ['get_term_structure', 'The curve on a date — spreads, OI, contango / backwardation', '/v1/futures/{root}/term-structure'], |
|
24 |
+ ['get_options_chain', 'EOD chain with IV + Greeks, filters by expiry / strikes / side', '/v1/options/chain/{t} · /expirations/{t}'], |
|
25 |
+ ['get_coverage', 'What exists for a contract or a ticker’s fundamentals, gaps included', '/v1/futures/contract/{s}/coverage · /v1/fundamentals/{t}/coverage'], |
|
26 |
+ ['get_financial_statements', 'Income / balance / cash-flow, annual or quarterly, point-in-time; XBRL facts', '/v1/fundamentals/{t}/statements · /facts/{concept}'], |
|
27 |
+ ['get_ratios', 'Latest P/E, P/B, EV/EBITDA, FCF yield, ROE, margins, leverage', '/v1/fundamentals/{t}/ratios'], |
|
28 |
+ ['get_ratios_daily', 'Daily ratio history with the fundamentals known that day', '/v1/fundamentals/{t}/ratios/daily'], |
|
29 |
+ ['screen_fundamentals', 'filters="pe<15,roe>0.15" · sort="fcf_yield:desc" · as_of', '/v1/fundamentals/screener'], |
|
30 |
+ ['get_filings', '10-K / 10-Q / 8-K / 4 / 13F… with URLs', '/v1/fundamentals/{t}/filings'], |
|
31 |
+ ['subscribe_filings', 'Bounded listen on the live filings websocket (N messages or T seconds)', 'wss://…/v1/stream'], |
|
32 |
+] |
|
33 |
+ |
|
34 |
+const PROMPTS = [ |
|
35 |
+ ['Plot me the CL term structure', 'get_term_structure → ASCII/inline chart, contango or backwardation, annualised roll yield per leg.'], |
|
36 |
+ ['Compare ESZ25 and ESH26 over 30 days', 'get_coverage ×2 → get_bars(asset=futures_contract) ×2 → returns, calendar spread trend, volume migration.'], |
|
37 |
+ ['Screen US stocks with PE<15 and FCF yield>6% and show their last 3 quarters', 'screen_fundamentals → get_financial_statements per hit, with filed_at dates.'], |
|
38 |
+] |
|
39 |
+ |
|
40 |
+const SKILLS = [ |
|
41 |
+ ['hfmd-data-analysis', 'Bars → pandas → CAGR, vol, Sharpe, drawdowns, monthly table, chart', 'fetch_bars.py · analyze.py'], |
|
42 |
+ ['hfmd-quick-backtest', 'MA-crossover template with honest walk-forward, costs, benchmark, per-block parameters', 'ma_crossover.py'], |
|
43 |
+ ['hfmd-continuous-futures', 'Build/compare continuous series (roll × adjust × depth) vs vendor; local stitcher', 'continuous_compare.py · stitch_local.py'], |
|
44 |
+ ['hfmd-fundamentals-screen', 'Screener → ratios → last quarters, with point-in-time notes', 'screen.py'], |
|
45 |
+ ['hfmd-term-structure', 'Curve, contango/backwardation verdict, roll yield, two-date comparison, chart', 'term_structure.py'], |
|
46 |
+] |
|
47 |
+ |
|
48 |
+// --------------------------------------------------------------------------------------------- |
|
49 |
+// Config generators (pure functions — also unit-testable) |
|
50 |
+// --------------------------------------------------------------------------------------------- |
|
51 |
+export function serverConfig(mode, key) { |
|
52 |
+ const cfg = mode === 'npm' ? { command: 'npx', args: ['-y', 'hfmarketdata-mcp'] } : { command: 'hfmarketdata-mcp', args: [] } |
|
53 |
+ if (key) cfg.env = { HFMD_API_KEY: key } |
|
54 |
+ return cfg |
|
55 |
+} |
|
56 |
+export function mcpJson(mode, key, wrapper = 'mcpServers') { |
|
57 |
+ return JSON.stringify({ [wrapper]: { hfmarketdata: serverConfig(mode, key) } }, null, 2) |
|
58 |
+} |
|
59 |
+export function claudeAddCommand(mode, key, scope) { |
|
60 |
+ const env = key ? ` -e HFMD_API_KEY=${key}` : '' |
|
61 |
+ const sc = scope ? ` --scope ${scope}` : '' |
|
62 |
+ return mode === 'npm' ? `claude mcp add hfmarketdata${sc}${env} -- npx -y hfmarketdata-mcp` : `claude mcp add hfmarketdata${sc}${env} -- hfmarketdata-mcp` |
|
63 |
+} |
|
64 |
+export function codexToml(mode, key) { |
|
65 |
+ const c = serverConfig(mode, key) |
|
66 |
+ const lines = ['[mcp_servers.hfmarketdata]', `command = "${c.command}"`, `args = [${c.args.map((a) => `"${a}"`).join(', ')}]`] |
|
67 |
+ if (key) lines.push(`env = { HFMD_API_KEY = "${key}" }`) |
|
68 |
+ return lines.join('\n') |
|
69 |
+} |
|
70 |
+function b64(s) { |
|
71 |
+ const bytes = new TextEncoder().encode(s) |
|
72 |
+ let bin = '' |
|
73 |
+ bytes.forEach((b) => { bin += String.fromCharCode(b) }) |
|
74 |
+ return btoa(bin) |
|
75 |
+} |
|
76 |
+export function cursorDeeplink(mode, key) { |
|
77 |
+ return `cursor://anysphere.cursor-deeplink/mcp/install?name=hfmarketdata&config=${encodeURIComponent(b64(JSON.stringify(serverConfig(mode, key))))}` |
|
78 |
+} |
|
79 |
+export const installCommand = (mode) => (mode === 'npm' ? 'npm i -g hfmarketdata-mcp' : `npm i -g ${TARBALL_URL}`) |
|
80 |
+ |
|
81 |
+// --------------------------------------------------------------------------------------------- |
|
82 |
+// Shared state: install mode + optional key (never persisted, never sent anywhere) |
|
83 |
+// --------------------------------------------------------------------------------------------- |
|
84 |
+const Ctx = createContext(null) |
|
85 |
+const useInt = () => useContext(Ctx) |
|
86 |
+ |
|
87 |
+function InstallControls({ showKey = true }) { |
|
88 |
+ const { mode, setMode, key, setKey } = useInt() |
|
89 |
+ return ( |
|
90 |
+ <div> |
|
91 |
+ <div className="int-toggle" role="tablist" aria-label="Install source"> |
|
92 |
+ <button type="button" role="tab" aria-selected={mode === 'tarball'} className={mode === 'tarball' ? 'on' : ''} onClick={() => setMode('tarball')}>Tarball (available now)</button> |
|
93 |
+ <button type="button" role="tab" aria-selected={mode === 'npm'} className={mode === 'npm' ? 'on' : ''} onClick={() => setMode('npm')}>npm registry (npx)</button> |
|
94 |
+ </div> |
|
95 |
+ {showKey && ( |
|
96 |
+ <div className="int-field"> |
|
97 |
+ <label htmlFor="int-key">API key (optional)</label> |
|
98 |
+ <input id="int-key" value={key} onChange={(e) => setKey(e.target.value.trim())} placeholder={KEY_PLACEHOLDER} autoComplete="off" spellCheck="false" /> |
|
99 |
+ <span className="hint">Stays in this tab — only used to fill the snippets. No key = keyless mode (30 req/h).</span> |
|
100 |
+ </div> |
|
101 |
+ )} |
|
102 |
+ {mode === 'npm' && <div className="int-callout warn">The npm package is being published; until it resolves on the registry, use the tarball tab (same binary, same version {MCP_VERSION}).</div>} |
|
103 |
+ </div> |
|
104 |
+ ) |
|
105 |
+} |
|
106 |
+ |
|
107 |
+const SUBNAV = [ |
|
108 |
+ ['/integrations', 'Overview'], ['/integrations/mcp', 'MCP server'], ['/integrations/claude-code', 'Claude Code'], |
|
109 |
+ ['/integrations/cursor', 'Cursor'], ['/integrations/codex', 'Codex'], ['/integrations/skills', 'Skills pack'], |
|
110 |
+] |
|
111 |
+ |
| 3 |
112 |
export default function Integrations() { |
| 4 |
|
− return <main className="page"><h1>Integrations</h1><p className="muted">Coming soon.</p></main> |
|
113 |
+ const [mode, setMode] = useState('tarball') |
|
114 |
+ const [key, setKey] = useState('') |
|
115 |
+ const value = useMemo(() => ({ mode, setMode, key, setKey, keyOrPlaceholder: key || KEY_PLACEHOLDER }), [mode, key]) |
|
116 |
+ return ( |
|
117 |
+ <Ctx.Provider value={value}> |
|
118 |
+ <main className="page int"> |
|
119 |
+ <Hero /> |
|
120 |
+ <nav className="int-subnav" aria-label="Integrations"> |
|
121 |
+ {SUBNAV.map(([to, label]) => <NavLink key={to} to={to} end={to === '/integrations'} className={({ isActive }) => (isActive ? 'active' : '')}>{label}</NavLink>)} |
|
122 |
+ </nav> |
|
123 |
+ <Routes> |
|
124 |
+ <Route index element={<Overview />} /> |
|
125 |
+ <Route path="mcp" element={<McpPage />} /> |
|
126 |
+ <Route path="claude-code" element={<ClaudeCodePage />} /> |
|
127 |
+ <Route path="cursor" element={<CursorPage />} /> |
|
128 |
+ <Route path="codex" element={<CodexPage />} /> |
|
129 |
+ <Route path="skills" element={<SkillsPage />} /> |
|
130 |
+ <Route path="*" element={<Overview />} /> |
|
131 |
+ </Routes> |
|
132 |
+ </main> |
|
133 |
+ </Ctx.Provider> |
|
134 |
+ ) |
|
135 |
+} |
|
136 |
+ |
|
137 |
+// --------------------------------------------------------------------------------------------- |
|
138 |
+function Hero() { |
|
139 |
+ const { mode, key } = useInt() |
|
140 |
+ return ( |
|
141 |
+ <section className="int-hero"> |
|
142 |
+ <div> |
|
143 |
+ <div className="int-kicker">Integrations · MCP · Skills</div> |
|
144 |
+ <h1>Market data where your agent already works.</h1> |
|
145 |
+ <p className="lead"> |
|
146 |
+ One MCP server gives Claude Code, Cursor, Codex and Claude Desktop 14 read-only tools over the whole HF Market Data API — |
|
147 |
+ bars since 2007, individual futures contracts and custom continuous series, term structures, options with Greeks, point-in-time SEC fundamentals |
|
148 |
+ and a live filings stream. Plus five Claude skills with ready-to-run Python. |
|
149 |
+ </p> |
|
150 |
+ <div className="int-actions"> |
|
151 |
+ <Link to="/integrations/claude-code" className="btn btn-primary">Set up in Claude Code</Link> |
|
152 |
+ <a href={cursorDeeplink(mode, key)} className="btn" title="Best effort: opens Cursor’s MCP install dialog (Cursor ≥ 1.0)">Add to Cursor</a> |
|
153 |
+ <a href={TARBALL_URL} className="btn btn-ghost" download>Download MCP tarball</a> |
|
154 |
+ <a href={SKILLS_ZIP_URL} className="btn btn-ghost" download>Download skills (.zip)</a> |
|
155 |
+ </div> |
|
156 |
+ <div className="int-badges"> |
|
157 |
+ <span className="int-badge ok">hfmarketdata-mcp v{MCP_VERSION}</span> |
|
158 |
+ <span className="int-badge">14 tools · 2 resources · 3 prompts</span> |
|
159 |
+ <span className="int-badge">Node ≥ 20 · zero heavy deps</span> |
|
160 |
+ <span className="int-badge">MIT</span> |
|
161 |
+ <span className="int-badge">Keyless works · free key = 120 req/min</span> |
|
162 |
+ </div> |
|
163 |
+ </div> |
|
164 |
+ <DemoTerminal /> |
|
165 |
+ </section> |
|
166 |
+ ) |
|
167 |
+} |
|
168 |
+ |
|
169 |
+function DemoTerminal() { |
|
170 |
+ return ( |
|
171 |
+ <div className="int-demo" aria-label="Example session"> |
|
172 |
+ <div className="int-demo-bar"><i /><i /><i /></div> |
|
173 |
+ <pre> |
|
174 |
+ <span className="u">› Plot me the CL term structure</span>{'\n\n'} |
|
175 |
+ <span className="t">⏺ get_term_structure(root="CL")</span>{'\n'} |
|
176 |
+ <span className="a"> CLX25 64.12 · CLZ25 63.71 · CLF26 63.35 · CLG26 63.04 …</span>{'\n'} |
|
177 |
+ <span className="g"> ▇▆▅▅▄▄▃▃▃▂▂▂ backwardation (front −1.7 % to 6th)</span>{'\n'} |
|
178 |
+ <span className="a"> Roll yield for a long front-month ≈ +7.6 %/yr; OI concentrates in X25/Z25.</span>{'\n\n'} |
|
179 |
+ <span className="u">› Compare ESZ25 and ESH26 over 30 days</span>{'\n\n'} |
|
180 |
+ <span className="t">⏺ get_coverage(kind="futures_contract", symbol="ESZ25") … get_bars(asset="futures_contract", …)</span>{'\n'} |
|
181 |
+ <span className="a"> Spread H26−Z25: 61.5 → 58.0 pts (−3.5); volume 96 % still in Z25;{'\n'} 1 missing session (2025-09-01) left as null.</span>{'\n\n'} |
|
182 |
+ <span className="u">› Screen US stocks with PE<15 and FCF yield>6 % …</span>{'\n'} |
|
183 |
+ <span className="t">⏺ screen_fundamentals(filters="pe<15,fcf_yield>0.06", sort="fcf_yield:desc")</span> |
|
184 |
+ </pre> |
|
185 |
+ </div> |
|
186 |
+ ) |
|
187 |
+} |
|
188 |
+ |
|
189 |
+// --------------------------------------------------------------------------------------------- |
|
190 |
+function Overview() { |
|
191 |
+ return ( |
|
192 |
+ <> |
|
193 |
+ <section className="int-grid"> |
|
194 |
+ <Link to="/integrations/mcp" className="card int-card"><h3><span className="int-logo">MCP</span>MCP server</h3><p>What the 14 tools do, how outputs are bounded, resources and prompts, install without npm.</p><span className="go">Read →</span></Link> |
|
195 |
+ <Link to="/integrations/claude-code" className="card int-card"><h3><span className="int-logo g">CC</span>Claude Code</h3><p>One <code>claude mcp add</code> command, or a shared <code>.mcp.json</code> for your project.</p><span className="go">Set up →</span></Link> |
|
196 |
+ <Link to="/integrations/cursor" className="card int-card"><h3><span className="int-logo b">Cu</span>Cursor</h3><p><code>.cursor/mcp.json</code> snippet and a one-click deeplink.</p><span className="go">Set up →</span></Link> |
|
197 |
+ <Link to="/integrations/codex" className="card int-card"><h3><span className="int-logo w">Cx</span>Codex</h3><p><code>~/.codex/config.toml</code> block, ready to paste.</p><span className="go">Set up →</span></Link> |
|
198 |
+ <Link to="/integrations/skills" className="card int-card"><h3><span className="int-logo">Sk</span>Skills pack</h3><p>Five Claude skills: analysis, walk-forward backtest, continuous futures, fundamentals screen, term structure.</p><span className="go">Download →</span></Link> |
|
199 |
+ </section> |
|
200 |
+ |
|
201 |
+ <section className="int-section"> |
|
202 |
+ <h2>Three prompts to try first</h2> |
|
203 |
+ <div className="int-prompts"> |
|
204 |
+ {PROMPTS.map(([q, how]) => <div key={q} className="card int-prompt"><q>{q}</q><small>{how}</small><IntCopyChip text={q} label="Copy prompt" /></div>)} |
|
205 |
+ </div> |
|
206 |
+ </section> |
|
207 |
+ |
|
208 |
+ <section className="int-section"> |
|
209 |
+ <h2>How it behaves</h2> |
|
210 |
+ <ul> |
|
211 |
+ <li><strong>Bounded outputs.</strong> Compact <code>{'{columns, rows}'}</code> tables; above 200 rows the tool returns count, first/last rows, numeric ranges and a hint to narrow — an agent cannot flood its context by accident.</li> |
|
212 |
+ <li><strong>Quota-aware.</strong> Every result ends with the remaining requests/rows from <code>X-RateLimit-*</code>; a 429 explains <code>Retry-After</code> and how to get a key.</li> |
|
213 |
+ <li><strong>Honest data.</strong> Missing values stay <code>null</code>; coverage and gaps are first-class (<code>get_coverage</code>). Intraday timestamps are US/Eastern, daily bars are dates.</li> |
|
214 |
+ <li><strong>Read-only.</strong> No tool writes anything. The API key is read from <code>HFMD_API_KEY</code> and never logged or echoed.</li> |
|
215 |
+ </ul> |
|
216 |
+ </section> |
|
217 |
+ </> |
|
218 |
+ ) |
|
219 |
+} |
|
220 |
+ |
|
221 |
+// --------------------------------------------------------------------------------------------- |
|
222 |
+function McpPage() { |
|
223 |
+ const { mode, keyOrPlaceholder } = useInt() |
|
224 |
+ return ( |
|
225 |
+ <> |
|
226 |
+ <section className="int-section"> |
|
227 |
+ <h2>hfmarketdata-mcp</h2> |
|
228 |
+ <p>TypeScript, stdio transport, built on the official <code>@modelcontextprotocol/sdk</code> with strict zod input schemas. Native <code>fetch</code>, no heavy dependencies, Node ≥ 20 (Node ≥ 22 for the websocket tool).</p> |
|
229 |
+ <InstallControls showKey={false} /> |
|
230 |
+ <IntCopyBlock lang="bash" title="Install & check" lines={[installCommand(mode), 'hfmarketdata-mcp --selftest', '# OK https://www.hfmarketdata.io (312 ms, keyless) — datasets: stock, etf, futures, futures_contracts, crypto, index, fx, options']} /> |
|
231 |
+ <div className="int-dl"> |
|
232 |
+ <div className="card"><strong>MCP server tarball</strong><span className="mono">hfmarketdata-mcp-{MCP_VERSION}.tgz</span><span className="size">≈ 26 kB · npm pack output · sha512 in the npm metadata</span><a className="btn" href={TARBALL_URL} download>Download .tgz</a></div> |
|
233 |
+ <div className="card"><strong>Source & README</strong><span>Directory <code>mcp/</code> of the hfmarketdata repository (MIT).</span><span className="size">README covers Claude Code, Cursor, Codex, Claude Desktop, development and release.</span><a className="btn btn-ghost" href={`mailto:${CONTACT_EMAIL}?subject=hfmarketdata-mcp`}>Ask for access</a></div> |
|
234 |
+ </div> |
|
235 |
+ </section> |
|
236 |
+ |
|
237 |
+ <section className="int-section"> |
|
238 |
+ <h2>Environment variables</h2> |
|
239 |
+ <div className="int-table"><table> |
|
240 |
+ <thead><tr><th>Variable</th><th>Default</th><th>Purpose</th></tr></thead> |
|
241 |
+ <tbody> |
|
242 |
+ <tr><td><code>HFMD_API_KEY</code></td><td><em>(unset = keyless)</em></td><td>Bearer key from your <Link to="/dashboard">dashboard</Link>. Keyless: 30 requests/hour per IP, 5 000 rows/request. Free account: 120 req/min, 50 000 rows/request.</td></tr> |
|
243 |
+ <tr><td><code>HFMD_BASE_URL</code></td><td><code>{PUBLIC_BASE}</code></td><td>Point the server at a staging or self-hosted API. The websocket URL is derived (<code>wss://…/v1/stream</code>).</td></tr> |
|
244 |
+ </tbody> |
|
245 |
+ </table></div> |
|
246 |
+ </section> |
|
247 |
+ |
|
248 |
+ <section className="int-section"> |
|
249 |
+ <h2>Tools</h2> |
|
250 |
+ <div className="int-table"><table> |
|
251 |
+ <thead><tr><th>Tool</th><th>What it does</th><th>Endpoint(s)</th></tr></thead> |
|
252 |
+ <tbody>{TOOLS.map(([n, d, e]) => <tr key={n}><td><code>{n}</code></td><td>{d}</td><td className="mono muted" style={{ fontSize: 12 }}>{e}</td></tr>)}</tbody> |
|
253 |
+ </table></div> |
|
254 |
+ <p><strong>Resources:</strong> <code>hfmarketdata://status</code> (dataset inventory) · <code>hfmarketdata://limits</code> (tiers + live headroom). <strong>Prompts:</strong> <code>term-structure-analysis</code>, <code>compare-contracts</code>, <code>fundamentals-snapshot</code>.</p> |
|
255 |
+ <div className="int-callout">The futures v2 and fundamentals endpoints roll out progressively on the API. Until an endpoint is live, the corresponding tool returns a clear <code>404 NOT_FOUND</code> message pointing to <code>hfmarketdata://status</code> — nothing is fabricated.</div> |
|
256 |
+ </section> |
|
257 |
+ |
|
258 |
+ <section className="int-section"> |
|
259 |
+ <h2>Generic MCP client config</h2> |
|
260 |
+ <p>Any client that reads the standard <code>mcpServers</code> JSON (Claude Desktop, Windsurf, Zed, Continue…):</p> |
|
261 |
+ <InstallControls /> |
|
262 |
+ <IntCopyBlock lang="json" title="mcpServers" code={mcpJson(mode, keyOrPlaceholder)} note="Claude Desktop: Settings → Developer → Edit Config (claude_desktop_config.json)." /> |
|
263 |
+ <h3>Example prompts</h3> |
|
264 |
+ <div className="int-prompts">{PROMPTS.map(([q, how]) => <div key={q} className="card int-prompt"><q>{q}</q><small>{how}</small><IntCopyChip text={q} label="Copy prompt" /></div>)}</div> |
|
265 |
+ </section> |
|
266 |
+ </> |
|
267 |
+ ) |
|
268 |
+} |
|
269 |
+ |
|
270 |
+// --------------------------------------------------------------------------------------------- |
|
271 |
+function ClaudeCodePage() { |
|
272 |
+ const { mode, keyOrPlaceholder } = useInt() |
|
273 |
+ return ( |
|
274 |
+ <section className="int-section"> |
|
275 |
+ <h2>Claude Code</h2> |
|
276 |
+ <p>Two ways: a one-liner for your user scope, or a <code>.mcp.json</code> committed with the project so the whole team gets the tools.</p> |
|
277 |
+ <InstallControls /> |
|
278 |
+ <ol className="int-steps"> |
|
279 |
+ <li><strong>Install the server</strong><IntCopyBlock compact lang="bash" title="Terminal" code={installCommand(mode)} note={mode === 'npm' ? 'With npx you can skip this step — Claude Code downloads the package on first use.' : 'Installs the hfmarketdata-mcp binary globally from the tarball served by this site.'} /></li> |
|
280 |
+ <li><strong>Register it</strong><IntCopyBlock compact lang="bash" title="Terminal" code={claudeAddCommand(mode, keyOrPlaceholder, 'user')} note="Drop the -e flag for keyless mode. Use --scope project to write .mcp.json instead." /></li> |
|
281 |
+ <li><strong>Or commit a project config</strong><IntCopyBlock compact lang="json" title=".mcp.json" code={mcpJson(mode, keyOrPlaceholder)} note="Prefer ${HFMD_API_KEY} expansion in shared files rather than a literal key: Claude Code expands environment variables in .mcp.json." /></li> |
|
282 |
+ <li><strong>Check & use</strong><IntCopyBlock compact lang="text" title="Inside Claude Code" lines={['/mcp # hfmarketdata should show as connected', 'Plot me the CL term structure', 'Compare ESZ25 and ESH26 over 30 days', 'Screen US stocks with PE<15 and FCF yield>6% and show their last 3 quarters']} /></li> |
|
283 |
+ </ol> |
|
284 |
+ <div className="int-callout">Pair it with the <Link to="/integrations/skills">skills pack</Link>: the MCP tools answer questions live, the skills give Claude Code reproducible Python (pandas) workflows for backtests and charts.</div> |
|
285 |
+ </section> |
|
286 |
+ ) |
|
287 |
+} |
|
288 |
+ |
|
289 |
+function CursorPage() { |
|
290 |
+ const { mode, key, keyOrPlaceholder } = useInt() |
|
291 |
+ const link = cursorDeeplink(mode, key) |
|
292 |
+ return ( |
|
293 |
+ <section className="int-section"> |
|
294 |
+ <h2>Cursor</h2> |
|
295 |
+ <p>Cursor reads <code>.cursor/mcp.json</code> in the project (shared) or <code>~/.cursor/mcp.json</code> (global). Tools appear in Agent mode.</p> |
|
296 |
+ <InstallControls /> |
|
297 |
+ <div className="int-actions"> |
|
298 |
+ <a href={link} className="btn btn-primary" title="Opens Cursor’s MCP install dialog">Add to Cursor (one click)</a> |
|
299 |
+ <IntCopyChip text={link} label="Copy deeplink" /> |
|
300 |
+ </div> |
|
301 |
+ <p className="muted" style={{ fontSize: 13 }}>Best effort: the deeplink (<code>cursor://anysphere.cursor-deeplink/mcp/install?name=…&config=<base64 JSON></code>) is supported by Cursor ≥ 1.0; if nothing happens, paste the JSON below.</p> |
|
302 |
+ <ol className="int-steps"> |
|
303 |
+ <li><strong>Install the server</strong><IntCopyBlock compact lang="bash" title="Terminal" code={installCommand(mode)} /></li> |
|
304 |
+ <li><strong>Add the config</strong><IntCopyBlock compact lang="json" title=".cursor/mcp.json" code={mcpJson(mode, keyOrPlaceholder)} note="Cursor → Settings → MCP → the server should list 14 tools. Toggle it on for the Agent." /></li> |
|
305 |
+ <li><strong>Try</strong><IntCopyBlock compact lang="text" title="Agent chat" lines={PROMPTS.map(([q]) => q)} /></li> |
|
306 |
+ </ol> |
|
307 |
+ </section> |
|
308 |
+ ) |
|
309 |
+} |
|
310 |
+ |
|
311 |
+function CodexPage() { |
|
312 |
+ const { mode, keyOrPlaceholder } = useInt() |
|
313 |
+ return ( |
|
314 |
+ <section className="int-section"> |
|
315 |
+ <h2>Codex</h2> |
|
316 |
+ <p>OpenAI Codex CLI reads MCP servers from <code>~/.codex/config.toml</code>. Add a table for <code>hfmarketdata</code>:</p> |
|
317 |
+ <InstallControls /> |
|
318 |
+ <ol className="int-steps"> |
|
319 |
+ <li><strong>Install the server</strong><IntCopyBlock compact lang="bash" title="Terminal" code={installCommand(mode)} /></li> |
|
320 |
+ <li><strong>Add to the config</strong><IntCopyBlock compact lang="toml" title="~/.codex/config.toml" code={codexToml(mode, keyOrPlaceholder)} note="Remove the env line for keyless mode. Restart codex; `/mcp` lists the connected servers." /></li> |
|
321 |
+ <li><strong>Try</strong><IntCopyBlock compact lang="text" title="codex" lines={PROMPTS.map(([q]) => q)} /></li> |
|
322 |
+ </ol> |
|
323 |
+ </section> |
|
324 |
+ ) |
|
325 |
+} |
|
326 |
+ |
|
327 |
+// --------------------------------------------------------------------------------------------- |
|
328 |
+function SkillsPage() { |
|
329 |
+ return ( |
|
330 |
+ <> |
|
331 |
+ <section className="int-section"> |
|
332 |
+ <h2>Skills pack for Claude Code / Claude</h2> |
|
333 |
+ <p>Five folders, each a <code>SKILL.md</code> (when to use, step-by-step, examples, gotchas) plus small Python scripts on <code>requests</code> + <code>pandas</code> (charts with <code>matplotlib</code>). No other dependency; every script prints the exact date range it received and never fills gaps.</p> |
|
334 |
+ <div className="int-dl"> |
|
335 |
+ <div className="card"><strong>hfmarketdata-skills.zip</strong><span className="size">≈ 42 kB · 5 skills · 18 files · deterministic build</span><a className="btn btn-primary" href={SKILLS_ZIP_URL} download>Download skills</a></div> |
|
336 |
+ <div className="card"><strong>Install</strong><span className="size">Personal (all projects) or project scope (commit it with your repo)</span> |
|
337 |
+ <IntCopyBlock compact lang="bash" title="Terminal" lines={[`curl -LO ${SKILLS_ZIP_URL}`, 'unzip -o hfmarketdata-skills.zip -d ~/.claude/skills/ # all projects', '# or: unzip -o hfmarketdata-skills.zip -d .claude/skills/ # this project only', 'pip install requests pandas matplotlib', 'export HFMD_API_KEY=hfmd_live_… # optional']} /> |
|
338 |
+ </div> |
|
339 |
+ </div> |
|
340 |
+ <div className="int-table"><table> |
|
341 |
+ <thead><tr><th>Skill</th><th>What it does</th><th>Scripts</th></tr></thead> |
|
342 |
+ <tbody>{SKILLS.map(([n, d, s]) => <tr key={n}><td><code>{n}</code></td><td>{d}</td><td className="mono muted" style={{ fontSize: 12 }}>{s}</td></tr>)}</tbody> |
|
343 |
+ </table></div> |
|
344 |
+ </section> |
|
345 |
+ <section className="int-section"> |
|
346 |
+ <h2>Example</h2> |
|
347 |
+ <IntCopyBlock lang="bash" title="hfmd-quick-backtest" lines={[ |
|
348 |
+ 'python3 ~/.claude/skills/hfmd-quick-backtest/scripts/ma_crossover.py --asset etf --ticker SPY --start 2010-01-01 \\', |
|
349 |
+ ' --grid 10,20,50 --grid-slow 100,150,200 --train-years 3 --test-years 1 --cost-bps 5 --plot spy_wf.png', |
|
350 |
+ '# OUT-OF-SAMPLE 2013-01-04 → 2026-08-07 (3,418 bars, 14 blocks): strategy Sharpe 0.82 vs buy&hold 0.91, max DD −23 % vs −34 %', |
|
351 |
+ ]} note="Parameters are chosen on the preceding training window only, applied to the next bar — the numbers shown are out-of-sample." /> |
|
352 |
+ <p>Ask in plain words instead: “backtest a 20/100 moving-average cross on CL with a walk-forward” — Claude Code picks the skill.</p> |
|
353 |
+ </section> |
|
354 |
+ </> |
|
355 |
+ ) |
| 5 |
356 |
} |