fd-open-data-mcp 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. fd_open_data_mcp-0.2.0/PKG-INFO +218 -0
  2. fd_open_data_mcp-0.2.0/README.md +188 -0
  3. fd_open_data_mcp-0.2.0/fd_open_data_mcp/__init__.py +9 -0
  4. fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/__init__.py +74 -0
  5. fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/agriculture.py +4 -0
  6. fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/akshare.py +356 -0
  7. fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/amac-fund.py +4 -0
  8. fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/amac_fund.py +70 -0
  9. fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/chemicals.py +54 -0
  10. fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/cisa-industry.py +4 -0
  11. fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/cisa_industry.py +74 -0
  12. fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/cme_agricultural.py +61 -0
  13. fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/dce_agricultural.py +57 -0
  14. fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/electronics.py +53 -0
  15. fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/fin_platforms.py +80 -0
  16. fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/flowers_kifc.py +54 -0
  17. fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/nbs-gdp.py +4 -0
  18. fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/nbs_gdp.py +212 -0
  19. fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/nonferrous.py +73 -0
  20. fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/sac_securities.py +28 -0
  21. fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/shfe_futures.py +74 -0
  22. fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/steel-assoc.py +4 -0
  23. fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/__init__.py +1 -0
  24. fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/cnreport_rules.py +80 -0
  25. fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/enrich.py +50 -0
  26. fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/importer.py +172 -0
  27. fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/providers.py +101 -0
  28. fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/readers.py +290 -0
  29. fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/register.py +164 -0
  30. fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/seeds/__init__.py +1 -0
  31. fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/seeds/cn_report.py +86 -0
  32. fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/seeds/edgar.py +89 -0
  33. fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/seeds/wbgapi.py +80 -0
  34. fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/upstream.py +153 -0
  35. fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/wbgapi_enumerate.py +111 -0
  36. fd_open_data_mcp-0.2.0/fd_open_data_mcp/cli.py +265 -0
  37. fd_open_data_mcp-0.2.0/fd_open_data_mcp/crawl/__init__.py +8 -0
  38. fd_open_data_mcp-0.2.0/fd_open_data_mcp/crawl/migrate.py +120 -0
  39. fd_open_data_mcp-0.2.0/fd_open_data_mcp/crawl/plan.py +67 -0
  40. fd_open_data_mcp-0.2.0/fd_open_data_mcp/crawl/planner.py +110 -0
  41. fd_open_data_mcp-0.2.0/fd_open_data_mcp/db.py +118 -0
  42. fd_open_data_mcp-0.2.0/fd_open_data_mcp/entities/__init__.py +1 -0
  43. fd_open_data_mcp-0.2.0/fd_open_data_mcp/entities/resolver.py +172 -0
  44. fd_open_data_mcp-0.2.0/fd_open_data_mcp/entities/taxonomy.py +69 -0
  45. fd_open_data_mcp-0.2.0/fd_open_data_mcp/errors.py +30 -0
  46. fd_open_data_mcp-0.2.0/fd_open_data_mcp/fetch/__init__.py +1 -0
  47. fd_open_data_mcp-0.2.0/fd_open_data_mcp/fetch/cache.py +76 -0
  48. fd_open_data_mcp-0.2.0/fd_open_data_mcp/fetch/dispatch.py +172 -0
  49. fd_open_data_mcp-0.2.0/fd_open_data_mcp/fetch/runner.py +210 -0
  50. fd_open_data_mcp-0.2.0/fd_open_data_mcp/migrate.py +32 -0
  51. fd_open_data_mcp-0.2.0/fd_open_data_mcp/models.py +307 -0
  52. fd_open_data_mcp-0.2.0/fd_open_data_mcp/ranking/__init__.py +1 -0
  53. fd_open_data_mcp-0.2.0/fd_open_data_mcp/ranking/scorer.py +122 -0
  54. fd_open_data_mcp-0.2.0/fd_open_data_mcp/refresh/__init__.py +1 -0
  55. fd_open_data_mcp-0.2.0/fd_open_data_mcp/refresh/runner.py +66 -0
  56. fd_open_data_mcp-0.2.0/fd_open_data_mcp/refresh/scheduler.py +48 -0
  57. fd_open_data_mcp-0.2.0/fd_open_data_mcp/semantic/__init__.py +1 -0
  58. fd_open_data_mcp-0.2.0/fd_open_data_mcp/semantic/bindings.py +121 -0
  59. fd_open_data_mcp-0.2.0/fd_open_data_mcp/semantic/concepts.py +65 -0
  60. fd_open_data_mcp-0.2.0/fd_open_data_mcp/semantic/mapper_llm.py +118 -0
  61. fd_open_data_mcp-0.2.0/fd_open_data_mcp/server.py +314 -0
  62. fd_open_data_mcp-0.2.0/fd_open_data_mcp.egg-info/PKG-INFO +218 -0
  63. fd_open_data_mcp-0.2.0/fd_open_data_mcp.egg-info/SOURCES.txt +86 -0
  64. fd_open_data_mcp-0.2.0/fd_open_data_mcp.egg-info/dependency_links.txt +1 -0
  65. fd_open_data_mcp-0.2.0/fd_open_data_mcp.egg-info/entry_points.txt +2 -0
  66. fd_open_data_mcp-0.2.0/fd_open_data_mcp.egg-info/requires.txt +20 -0
  67. fd_open_data_mcp-0.2.0/fd_open_data_mcp.egg-info/top_level.txt +1 -0
  68. fd_open_data_mcp-0.2.0/pyproject.toml +47 -0
  69. fd_open_data_mcp-0.2.0/setup.cfg +4 -0
  70. fd_open_data_mcp-0.2.0/tests/test_adapters.py +409 -0
  71. fd_open_data_mcp-0.2.0/tests/test_catalog.py +94 -0
  72. fd_open_data_mcp-0.2.0/tests/test_cli.py +74 -0
  73. fd_open_data_mcp-0.2.0/tests/test_cnreport.py +64 -0
  74. fd_open_data_mcp-0.2.0/tests/test_e2e.py +97 -0
  75. fd_open_data_mcp-0.2.0/tests/test_edgar.py +72 -0
  76. fd_open_data_mcp-0.2.0/tests/test_enrich_concept.py +89 -0
  77. fd_open_data_mcp-0.2.0/tests/test_entities_ranking.py +42 -0
  78. fd_open_data_mcp-0.2.0/tests/test_integration.py +44 -0
  79. fd_open_data_mcp-0.2.0/tests/test_planner.py +105 -0
  80. fd_open_data_mcp-0.2.0/tests/test_ranking_cache.py +74 -0
  81. fd_open_data_mcp-0.2.0/tests/test_readers_upstream.py +125 -0
  82. fd_open_data_mcp-0.2.0/tests/test_refresh.py +75 -0
  83. fd_open_data_mcp-0.2.0/tests/test_register.py +60 -0
  84. fd_open_data_mcp-0.2.0/tests/test_runners.py +22 -0
  85. fd_open_data_mcp-0.2.0/tests/test_server.py +26 -0
  86. fd_open_data_mcp-0.2.0/tests/test_wbgapi.py +60 -0
  87. fd_open_data_mcp-0.2.0/tests/test_wbgapi_enumerate.py +78 -0
  88. fd_open_data_mcp-0.2.0/tests/test_yfinance_reader.py +48 -0
@@ -0,0 +1,218 @@
1
+ Metadata-Version: 2.4
2
+ Name: fd-open-data-mcp
3
+ Version: 0.2.0
4
+ Summary: Open-data ontology MCP: a semantic concept layer over multi-datasource financial/economic data
5
+ Author: FindDataOfficial
6
+ License: MIT
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Requires-Python: >=3.10
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: click>=8.0
13
+ Requires-Dist: sqlalchemy>=2.0
14
+ Requires-Dist: pydantic>=2.0
15
+ Requires-Dist: fastmcp>=2.0
16
+ Requires-Dist: pandas>=2.0
17
+ Requires-Dist: fd-open-data-protocol
18
+ Requires-Dist: pyyaml>=6.0
19
+ Provides-Extra: data
20
+ Requires-Dist: akshare>=1.17; extra == "data"
21
+ Requires-Dist: yfinance>=0.2; extra == "data"
22
+ Requires-Dist: world_bank_data>=0.1; extra == "data"
23
+ Requires-Dist: edgartools>=2.0; extra == "data"
24
+ Requires-Dist: wbgapi>=1.0; extra == "data"
25
+ Requires-Dist: requests>=2.31; extra == "data"
26
+ Requires-Dist: beautifulsoup4>=4.12; extra == "data"
27
+ Requires-Dist: scrapling>=0.4.7; extra == "data"
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=8.0; extra == "dev"
30
+
31
+ # fd-open-data-mcp
32
+
33
+ An **open-data ontology MCP**: a semantic concept layer over multi-datasource
34
+ financial/economic data. You ask for data in **concepts + entities** (e.g.
35
+ "price.close for Moutai", "GDP for China"); the system resolves the concept to
36
+ physical columns across datasources, ranks candidate sources by quality +
37
+ accessibility, fetches from the best one (with failover), caches by concept,
38
+ and refreshes on a per-concept frequency.
39
+
40
+ It consumes the finddata `fd-*` datasource registries and
41
+ `fd-entities-indicators` **read-only** and adds the unifying layers on top.
42
+
43
+ ## Architecture
44
+
45
+ ```
46
+ CONSUMED (read-only) ADDED by fd-open-data-mcp
47
+ fd-akshare/yfinance/world/ concept_bindings (column -> concept)
48
+ cn-report/cn-gov registries entity_source_identifiers (per-source id)
49
+ fd-entities-indicators source_rankings (quality × access × freshness)
50
+ indicator_defs (926 concepts) semantic_observations (read-through cache)
51
+ countries/cities/symbols/sw_industries fetch_log / schedules / executions
52
+
53
+ TRANSFORMERS: import_catalog, consume_concepts, propose_bindings,
54
+ seed_entity_identifiers, generate_refresh_schedules
55
+
56
+ RUNTIME: read() -> cache hit? : dispatch (ranked, failover) -> cache -> log
57
+ ```
58
+
59
+ Six capabilities (see `openspec/changes/add-fd-open-data-mcp/specs/`):
60
+ `open-data-catalog`, `semantic-layer`, `entity-identity`, `source-ranking`,
61
+ `concept-fetch`, `scheduled-refresh`.
62
+
63
+ ## Install
64
+
65
+ ```bash
66
+ cd /Users/chengsishi/finddata/fd-open-data-mcp
67
+ uv sync # base install
68
+ uv sync --extra data # + akshare / yfinance / world_bank_data (for real fetches)
69
+ ```
70
+
71
+ The DB path defaults to `fd_open_data_mcp/metadata/daas.db`; override with
72
+ `FD_OPEN_DATA_MCP_DATABASE_URL`. `FINDDATA_ROOT` (default: the parent
73
+ `finddata/` dir) locates the `fd-*` providers. `EDGAR_IDENTITY` (an email)
74
+ is required by the SEC before any edgar fetch - the runner refuses to call
75
+ anonymously if it is unset.
76
+
77
+ ## Quickstart
78
+
79
+ ```bash
80
+ # 1. create the ontology tables
81
+ fd-open-data-mcp migrate
82
+
83
+ # 2. import the catalogs (akshare 673, yfinance 12, cn-gov 11, cn-report 44, edgar 6, ...)
84
+ fd-open-data-mcp import-catalog
85
+ # or one provider: fd-open-data-mcp import-catalog akshare
86
+
87
+ # 3. consume the 926 indicator_defs as concepts + propose column->concept bindings
88
+ fd-open-data-mcp consume-concepts
89
+ fd-open-data-mcp propose-bindings
90
+
91
+ # 4. seed per-source entity identifiers (akshare/yfinance for stocks, worldbank for countries)
92
+ fd-open-data-mcp seed-entities
93
+
94
+ # 5. generate per-concept refresh schedules from indicator_defs.frequency
95
+ fd-open-data-mcp generate-schedules
96
+
97
+ # 6. read data by concept + entity (read-through cache + ranked dispatch + failover)
98
+ fd-open-data-mcp read --concept-id 234 --entity-type stock --entity-id 1 --date 2024-07-26
99
+ ```
100
+
101
+ ## MCP server
102
+
103
+ ```bash
104
+ fd-open-data-mcp serve # FastMCP, stdio transport
105
+ ```
106
+
107
+ 16 tools: `import_catalog`, `consume_concepts`, `propose_bindings`,
108
+ `list_concepts`, `list_bindings`, `review_bindings`, `confirm_binding`,
109
+ `seed_entity_identifiers`, `resolve_entity`, `add_entity_identifier`,
110
+ `rank_sources`, `read`, `fetch`, `generate_refresh_schedules`,
111
+ `list_schedules`, `run_schedule`.
112
+
113
+ ## Tests
114
+
115
+ ```bash
116
+ uv run --with pytest pytest -q
117
+ ```
118
+
119
+ ## Design notes / v1 limitations
120
+
121
+ - **Propose-and-confirm**: column->concept bindings carry `confidence` +
122
+ `provenance`; below-threshold bindings are withheld from dispatch (review
123
+ queue). A real fetch promotes a binding to `sample-confirmed`.
124
+ - **Ranking** is per `(source × concept)`, self-tuning from `fetch_log`
125
+ (bounded so one failure can't remove a source).
126
+ - **Conflict policy**: one cached value per `(concept, entity, date)` with
127
+ `source_used` attached; values are never merged across sources.
128
+ - **LLM provider** for meaning-enrichment / cross-language concept mapping is
129
+ an open question (`design.md`); v1 uses a rule table + `semantic_type` hints.
130
+ - `_build_params` / `_extract_value` in the fetch runner are best-effort; a
131
+ production runner refines per-function date-format / payload-shape quirks.
132
+
133
+ See `openspec/changes/add-fd-open-data-mcp/` for the full spec.
134
+
135
+ ---
136
+
137
+ ### Usage Examples
138
+
139
+ #### Query NBS GDP Data
140
+ ```python
141
+ from fd_open_data_mcp.fetch.runner import run_upstream
142
+
143
+ result = run_upstream(
144
+ source='nbs-gdp',
145
+ command='get_gdp_quarterly',
146
+ params={'start_year': 2020}
147
+ )
148
+ print(result.head())
149
+ ```
150
+
151
+ #### Query Steel Industry Production
152
+ ```python
153
+ result = run_upstream(
154
+ source='cisa-industry',
155
+ command='get_steel_production',
156
+ params={}
157
+ )
158
+ print(result.head())
159
+ ```
160
+
161
+ #### Query Metal Futures Pricing
162
+ ```python
163
+ result = run_upstream(
164
+ source='shfe-metal-futures',
165
+ command='get_metal_pricing',
166
+ params={}
167
+ )
168
+ print(result.head())
169
+ ```
170
+
171
+ ### CLI Usage
172
+ ```bash
173
+ # List all data sources
174
+ fd-open-data-mcp list-sources
175
+
176
+ # Read specific data
177
+ fd-open-data-mcp read \
178
+ --source nbs-gdp \
179
+ --function get_gdp_quarterly \
180
+ --params '{"start_year": 2020}'
181
+ ```
182
+
183
+ ### MCP Server Mode
184
+ ```bash
185
+ uv run fd-open-data-mcp serve
186
+ # Then connect from Claude/Codex/etc.
187
+ ```
188
+
189
+
190
+ ---
191
+
192
+ ## Rate Limits & Best Practices
193
+
194
+ ### Recommended Refresh Intervals
195
+
196
+ | Data Source Type | Refresh Interval | Notes |
197
+ |------------------|------------------|-------|
198
+ | **GDP/Macro** | Weekly | Stable data, updates quarterly/monthly |
199
+ | **Industry Stats** | Daily | Can change frequently |
200
+ | **Futures Prices** | Hourly during market hours | Volatile pricing |
201
+ | **Fund Statistics** | Monthly | Updates monthly |
202
+ | **Market Indices** | Real-time | High volatility |
203
+
204
+ ### API Rate Limiting
205
+
206
+ - **Government APIs**: Respect 10 requests/minute default limits
207
+ - **Exchange APIs**: Follow exchange-specific rate policies
208
+ - **Third-party Data**: Check individual terms of service
209
+ - **Recommendation**: Implement exponential backoff on 429 errors
210
+
211
+ ### Caching Strategy
212
+
213
+ All fetch results are automatically cached based on data frequency:
214
+ - High-frequency data (futures): Cache for 1 hour
215
+ - Medium-frequency data (industry stats): Cache for 24 hours
216
+ - Low-frequency data (GDP, annual reports): Cache for 1 week
217
+
218
+ Use `fd-open-data-mcp read` to check cache status.
@@ -0,0 +1,188 @@
1
+ # fd-open-data-mcp
2
+
3
+ An **open-data ontology MCP**: a semantic concept layer over multi-datasource
4
+ financial/economic data. You ask for data in **concepts + entities** (e.g.
5
+ "price.close for Moutai", "GDP for China"); the system resolves the concept to
6
+ physical columns across datasources, ranks candidate sources by quality +
7
+ accessibility, fetches from the best one (with failover), caches by concept,
8
+ and refreshes on a per-concept frequency.
9
+
10
+ It consumes the finddata `fd-*` datasource registries and
11
+ `fd-entities-indicators` **read-only** and adds the unifying layers on top.
12
+
13
+ ## Architecture
14
+
15
+ ```
16
+ CONSUMED (read-only) ADDED by fd-open-data-mcp
17
+ fd-akshare/yfinance/world/ concept_bindings (column -> concept)
18
+ cn-report/cn-gov registries entity_source_identifiers (per-source id)
19
+ fd-entities-indicators source_rankings (quality × access × freshness)
20
+ indicator_defs (926 concepts) semantic_observations (read-through cache)
21
+ countries/cities/symbols/sw_industries fetch_log / schedules / executions
22
+
23
+ TRANSFORMERS: import_catalog, consume_concepts, propose_bindings,
24
+ seed_entity_identifiers, generate_refresh_schedules
25
+
26
+ RUNTIME: read() -> cache hit? : dispatch (ranked, failover) -> cache -> log
27
+ ```
28
+
29
+ Six capabilities (see `openspec/changes/add-fd-open-data-mcp/specs/`):
30
+ `open-data-catalog`, `semantic-layer`, `entity-identity`, `source-ranking`,
31
+ `concept-fetch`, `scheduled-refresh`.
32
+
33
+ ## Install
34
+
35
+ ```bash
36
+ cd /Users/chengsishi/finddata/fd-open-data-mcp
37
+ uv sync # base install
38
+ uv sync --extra data # + akshare / yfinance / world_bank_data (for real fetches)
39
+ ```
40
+
41
+ The DB path defaults to `fd_open_data_mcp/metadata/daas.db`; override with
42
+ `FD_OPEN_DATA_MCP_DATABASE_URL`. `FINDDATA_ROOT` (default: the parent
43
+ `finddata/` dir) locates the `fd-*` providers. `EDGAR_IDENTITY` (an email)
44
+ is required by the SEC before any edgar fetch - the runner refuses to call
45
+ anonymously if it is unset.
46
+
47
+ ## Quickstart
48
+
49
+ ```bash
50
+ # 1. create the ontology tables
51
+ fd-open-data-mcp migrate
52
+
53
+ # 2. import the catalogs (akshare 673, yfinance 12, cn-gov 11, cn-report 44, edgar 6, ...)
54
+ fd-open-data-mcp import-catalog
55
+ # or one provider: fd-open-data-mcp import-catalog akshare
56
+
57
+ # 3. consume the 926 indicator_defs as concepts + propose column->concept bindings
58
+ fd-open-data-mcp consume-concepts
59
+ fd-open-data-mcp propose-bindings
60
+
61
+ # 4. seed per-source entity identifiers (akshare/yfinance for stocks, worldbank for countries)
62
+ fd-open-data-mcp seed-entities
63
+
64
+ # 5. generate per-concept refresh schedules from indicator_defs.frequency
65
+ fd-open-data-mcp generate-schedules
66
+
67
+ # 6. read data by concept + entity (read-through cache + ranked dispatch + failover)
68
+ fd-open-data-mcp read --concept-id 234 --entity-type stock --entity-id 1 --date 2024-07-26
69
+ ```
70
+
71
+ ## MCP server
72
+
73
+ ```bash
74
+ fd-open-data-mcp serve # FastMCP, stdio transport
75
+ ```
76
+
77
+ 16 tools: `import_catalog`, `consume_concepts`, `propose_bindings`,
78
+ `list_concepts`, `list_bindings`, `review_bindings`, `confirm_binding`,
79
+ `seed_entity_identifiers`, `resolve_entity`, `add_entity_identifier`,
80
+ `rank_sources`, `read`, `fetch`, `generate_refresh_schedules`,
81
+ `list_schedules`, `run_schedule`.
82
+
83
+ ## Tests
84
+
85
+ ```bash
86
+ uv run --with pytest pytest -q
87
+ ```
88
+
89
+ ## Design notes / v1 limitations
90
+
91
+ - **Propose-and-confirm**: column->concept bindings carry `confidence` +
92
+ `provenance`; below-threshold bindings are withheld from dispatch (review
93
+ queue). A real fetch promotes a binding to `sample-confirmed`.
94
+ - **Ranking** is per `(source × concept)`, self-tuning from `fetch_log`
95
+ (bounded so one failure can't remove a source).
96
+ - **Conflict policy**: one cached value per `(concept, entity, date)` with
97
+ `source_used` attached; values are never merged across sources.
98
+ - **LLM provider** for meaning-enrichment / cross-language concept mapping is
99
+ an open question (`design.md`); v1 uses a rule table + `semantic_type` hints.
100
+ - `_build_params` / `_extract_value` in the fetch runner are best-effort; a
101
+ production runner refines per-function date-format / payload-shape quirks.
102
+
103
+ See `openspec/changes/add-fd-open-data-mcp/` for the full spec.
104
+
105
+ ---
106
+
107
+ ### Usage Examples
108
+
109
+ #### Query NBS GDP Data
110
+ ```python
111
+ from fd_open_data_mcp.fetch.runner import run_upstream
112
+
113
+ result = run_upstream(
114
+ source='nbs-gdp',
115
+ command='get_gdp_quarterly',
116
+ params={'start_year': 2020}
117
+ )
118
+ print(result.head())
119
+ ```
120
+
121
+ #### Query Steel Industry Production
122
+ ```python
123
+ result = run_upstream(
124
+ source='cisa-industry',
125
+ command='get_steel_production',
126
+ params={}
127
+ )
128
+ print(result.head())
129
+ ```
130
+
131
+ #### Query Metal Futures Pricing
132
+ ```python
133
+ result = run_upstream(
134
+ source='shfe-metal-futures',
135
+ command='get_metal_pricing',
136
+ params={}
137
+ )
138
+ print(result.head())
139
+ ```
140
+
141
+ ### CLI Usage
142
+ ```bash
143
+ # List all data sources
144
+ fd-open-data-mcp list-sources
145
+
146
+ # Read specific data
147
+ fd-open-data-mcp read \
148
+ --source nbs-gdp \
149
+ --function get_gdp_quarterly \
150
+ --params '{"start_year": 2020}'
151
+ ```
152
+
153
+ ### MCP Server Mode
154
+ ```bash
155
+ uv run fd-open-data-mcp serve
156
+ # Then connect from Claude/Codex/etc.
157
+ ```
158
+
159
+
160
+ ---
161
+
162
+ ## Rate Limits & Best Practices
163
+
164
+ ### Recommended Refresh Intervals
165
+
166
+ | Data Source Type | Refresh Interval | Notes |
167
+ |------------------|------------------|-------|
168
+ | **GDP/Macro** | Weekly | Stable data, updates quarterly/monthly |
169
+ | **Industry Stats** | Daily | Can change frequently |
170
+ | **Futures Prices** | Hourly during market hours | Volatile pricing |
171
+ | **Fund Statistics** | Monthly | Updates monthly |
172
+ | **Market Indices** | Real-time | High volatility |
173
+
174
+ ### API Rate Limiting
175
+
176
+ - **Government APIs**: Respect 10 requests/minute default limits
177
+ - **Exchange APIs**: Follow exchange-specific rate policies
178
+ - **Third-party Data**: Check individual terms of service
179
+ - **Recommendation**: Implement exponential backoff on 429 errors
180
+
181
+ ### Caching Strategy
182
+
183
+ All fetch results are automatically cached based on data frequency:
184
+ - High-frequency data (futures): Cache for 1 hour
185
+ - Medium-frequency data (industry stats): Cache for 24 hours
186
+ - Low-frequency data (GDP, annual reports): Cache for 1 week
187
+
188
+ Use `fd-open-data-mcp read` to check cache status.
@@ -0,0 +1,9 @@
1
+ """fd-open-data-mcp: open-data ontology MCP.
2
+
3
+ A semantic concept layer over multi-datasource financial/economic data.
4
+ Consumes the fd-* datasource registries and fd-entities-indicators read-only,
5
+ and adds: a unified catalog, concept<->column bindings, cross-source entity
6
+ identity, ranked dispatch, a read-through concept-keyed cache, and
7
+ frequency-driven auto-refresh.
8
+ """
9
+ __version__ = "0.2.0"
@@ -0,0 +1,74 @@
1
+ """Per-function adapter registry: concrete param-building + value-extraction per
2
+ ``(source, command)``, shared by ``read()`` dispatch and the bulk crawl executor.
3
+
4
+ Replaces the best-effort ``_build_params`` / ``_extract_value`` in
5
+ ``fetch/dispatch.py`` (design.md D4). Seeded by porting per-function logic from the
6
+ ``scraw-*`` projects (akshare first - tasks 2.2/2.3). Where no adapter is registered,
7
+ callers fall back to the legacy best-effort mapping (coexistence during migration).
8
+
9
+ An adapter is registered for a ``(source, command)`` key and implements two methods:
10
+
11
+ ``build_params(fn, identifier, date, binding) -> dict``
12
+ Build the concrete kwargs for the upstream callable. ``fn`` is the
13
+ ``Function`` row (with ``.parameters``); ``identifier`` is the per-source
14
+ entity id; ``binding`` is the ``ConceptBinding`` (its ``.column.name`` is used
15
+ for indicator-style params, e.g. wbgapi ``indicator=column.name``).
16
+
17
+ ``extract_value(result, column_name, date) -> value | None``
18
+ Pull the value for ``(date, column_name)`` from the upstream result.
19
+
20
+ An optional ``call(command, params)`` method wraps the upstream callable (e.g. with a
21
+ native timeout + retry); ``fetch/runner.py`` opts into it when present.
22
+
23
+ Dispatch checks ``adapter_for(source, command)``; if present it delegates, otherwise
24
+ it uses the legacy best-effort path. This keeps working reads from regressing while
25
+ adapters are ported one function at a time.
26
+ """
27
+ from __future__ import annotations
28
+
29
+ from typing import Any, Optional, Protocol
30
+
31
+ # Typed loosely to avoid an import cycle with fetch.dispatch / models.
32
+ FunctionLike = Any
33
+ BindingLike = Any
34
+
35
+
36
+ class Adapter(Protocol):
37
+ """Per-function fetch mechanics: param mapping + value extraction."""
38
+
39
+ def build_params(
40
+ self, fn: FunctionLike, identifier: str, date: str, binding: BindingLike,
41
+ ) -> dict: ...
42
+
43
+ def extract_value(
44
+ self, result: Any, column_name: str, date: str,
45
+ ) -> Any: ...
46
+
47
+
48
+ _REGISTRY: dict[tuple[str, str], Adapter] = {}
49
+
50
+
51
+ def register(source: str, command: str, adapter: Adapter) -> Adapter:
52
+ """Register an adapter for a ``(source, command)`` key (idempotent overwrite)."""
53
+ _REGISTRY[(source, command)] = adapter
54
+ return adapter
55
+
56
+
57
+ def adapter_for(source: str, command: str) -> Optional[Adapter]:
58
+ """Return the registered adapter for ``(source, command)``, or ``None``."""
59
+ return _REGISTRY.get((source, command))
60
+
61
+
62
+ def has_adapter(source: str, command: str) -> bool:
63
+ """Whether a registered adapter exists for ``(source, command)``."""
64
+ return (source, command) in _REGISTRY
65
+
66
+
67
+ def registered() -> list[tuple[str, str]]:
68
+ """All registered ``(source, command)`` keys (for introspection/debugging)."""
69
+ return sorted(_REGISTRY.keys())
70
+
71
+
72
+ # Load akshare adapters so they register at package import. Imported last so
73
+ # `register` is defined before akshare.py imports it (no cycle).
74
+ from fd_open_data_mcp.adapters import akshare as _akshare_adapters # noqa: E402,F401
@@ -0,0 +1,4 @@
1
+
2
+ def run_agriculture(command: str, params: dict):
3
+ """Stub runner for agriculture. Replace with actual implementation."""
4
+ raise NotImplementedError(f"{source} runner not yet implemented")