taiwan-data-mcp 0.5.0 → 0.7.0

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.
package/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
  | `taiwan_company_search` | 用公司名搜尋,拿統一編號與負責人 | [inc.com.tw](https://inc.com.tw)(經濟部公司登記) |
13
13
  | `taiwan_company_profile` | 用統編查公司完整登記資料(含董監事) | inc.com.tw |
14
14
  | `taiwan_person_companies` | 用人名查他擔任負責人/董監事的公司 | inc.com.tw |
15
- | `taiwan_company_risk` | 公司風險查核:拒絕往來/勞動/環保裁罰紅旗 | inc.com.tw |
15
+ | `taiwan_company_risk` | 公司風險查核(統編或公司名):解散/拒絕往來/國際制裁/金管會/司法/勞動/環保紅旗+上市櫃即時股價 | inc.com.tw |
16
16
  | `taiwan_realprice_search` | 搜尋實價登錄的地址 / 路段 / 行政區 | [housetw.com](https://housetw.com)(內政部實價登錄) |
17
17
  | `taiwan_realprice_locate` | 用經緯度反查行政區與行情頁 | housetw.com |
18
18
  | `taiwan_realprice_area` | 查某縣市 / 行政區成交行情統計 | housetw.com |
@@ -22,6 +22,7 @@
22
22
  | `taiwan_drug_info` | 用許可證字號查藥品成分/適應症/健保價/回收/短缺 | health-hub |
23
23
  | `taiwan_gov_tender_by_company` | 查某公司投標/得標的政府採購案 | 政府電子採購網(g0v PCC API) |
24
24
  | `taiwan_gov_tender_search` | 用標案名稱關鍵字搜尋政府採購案 | 政府電子採購網(g0v PCC API) |
25
+ | `taiwan_farm_price` | 查蔬果批發市場最新行情(菜價) | 農業部 data.moa.gov.tw |
25
26
 
26
27
  跨工具串接是重點:例如「查這家公司 → 看它登記地址那區的房價 → 查它官網是不是詐騙」,一次問答內 AI 自己串起來。
27
28
 
@@ -78,6 +79,7 @@ node test/e2e.mjs # MCP 協定層測試
78
79
  - 165 防詐查詢 — **[fraud.tw](https://fraud.tw)**(防詐雷達)
79
80
  - 藥品/健康查詢 — **[health-hub](https://health-hub-epx.pages.dev)**(衛福部食藥署資料)
80
81
  - 政府標案查詢 — 政府電子採購網開放資料(透過 [g0v PCC API](https://pcc.g0v.ronny.tw))
82
+ - 農產批發行情 — 農業部開放資料([data.moa.gov.tw](https://data.moa.gov.tw))
81
83
 
82
84
  三站皆為聚合台灣政府開放資料的免費查詢服務。
83
85
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "taiwan-data-mcp",
3
- "version": "0.5.0",
4
- "description": "MCP server for Taiwan public data — 實價登錄、公司登記查核、165 防詐、藥品查詢、政府標案。Lets AI assistants (Claude, Cursor, etc.) query real Taiwan data.",
3
+ "version": "0.7.0",
4
+ "description": "MCP server for Taiwan public data — 實價登錄、公司登記查核、165 防詐、藥品查詢、政府標案、農產行情。Query real Taiwan data from Claude, Cursor, etc.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "taiwan-data-mcp": "src/server.mjs"
package/src/server.mjs CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  realpriceSearch, realpriceLocate, realpriceArea, realpriceEstimate, realpriceRoad,
8
8
  drugSearch, drugInfo,
9
9
  tenderByCompany, tenderSearch,
10
+ farmPrice,
10
11
  } from './sources.mjs';
11
12
 
12
13
  const TOOLS = [
@@ -57,13 +58,15 @@ const TOOLS = [
57
58
  {
58
59
  name: 'taiwan_company_risk',
59
60
  description:
60
- '公司風險查核(盡職調查紅旗):用 8 位統編查該公司有無政府採購拒絕往來、勞動法令裁罰、環保裁罰,回傳風險等級與紅旗清單。資料來源:inc.com.tw(聚合政府公開資料)。',
61
+ '公司風險查核(盡職調查紅旗):用 8 位統編或公司名稱(簡稱如「台積電」自動解析成正主)查該公司有無解散、政府採購拒絕往來、國際制裁名單(OFAC/UN)、金管會重大裁罰、司法案件、勞動法令裁罰、環保裁罰,回傳風險等級、紅旗清單與負責人,上市櫃並附即時股價。資料來源:inc.com.tw(聚合政府公開資料)。',
61
62
  inputSchema: {
62
63
  type: 'object',
63
- properties: { unified_business_no: { type: 'string', description: '8 位統一編號,例如 22099131' } },
64
- required: ['unified_business_no'],
64
+ properties: {
65
+ unified_business_no: { type: 'string', description: '8 位統一編號(與 name 二擇一),例如 22099131' },
66
+ name: { type: 'string', description: '公司名稱或簡稱(與統編二擇一),例如「台積電」「鴻海」' },
67
+ },
65
68
  },
66
- run: (a) => companyRisk(a.unified_business_no),
69
+ run: (a) => companyRisk(a.unified_business_no || a.name),
67
70
  },
68
71
  {
69
72
  name: 'taiwan_realprice_search',
@@ -183,10 +186,24 @@ const TOOLS = [
183
186
  },
184
187
  run: (a) => tenderSearch(a.keyword, a.page),
185
188
  },
189
+ {
190
+ name: 'taiwan_farm_price',
191
+ description:
192
+ '查台灣農產品批發市場最新交易行情:某蔬果的平均、最高、最低批發價(元/公斤)與交易量。颱風季菜價查詢常用。涵蓋蔬果花卉,不含禽蛋肉品。資料來源:農業部 data.moa.gov.tw。',
193
+ inputSchema: {
194
+ type: 'object',
195
+ properties: {
196
+ crop: { type: 'string', description: '蔬果名稱,例如「高麗菜」「香蕉」「青蔥」「番茄」' },
197
+ market: { type: 'string', description: '批發市場(可選,預設「台北一」),例如「台北一」「台北二」「台中」「三重」「高雄」' },
198
+ },
199
+ required: ['crop'],
200
+ },
201
+ run: (a) => farmPrice(a.crop, a.market),
202
+ },
186
203
  ];
187
204
 
188
205
  const server = new Server(
189
- { name: 'taiwan-data-mcp', version: '0.1.0' },
206
+ { name: 'taiwan-data-mcp', version: '0.7.0' },
190
207
  { capabilities: { tools: {} } }
191
208
  );
192
209
 
@@ -207,4 +224,4 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
207
224
 
208
225
  const transport = new StdioServerTransport();
209
226
  await server.connect(transport);
210
- console.error('taiwan-data-mcp running (stdio) — 14 tools: 防詐 / 公司登記 / 實價登錄 / 藥品健康 / 政府標案');
227
+ console.error('taiwan-data-mcp running (stdio) — 15 tools: 防詐 / 公司登記 / 實價登錄 / 藥品健康 / 政府標案 / 農產行情');
package/src/sources.mjs CHANGED
@@ -77,36 +77,100 @@ export async function personCompanies(name) {
77
77
  source: '台灣公司登記網 inc.com.tw' };
78
78
  }
79
79
 
80
- export async function companyRisk(id) {
81
- const tin = String(id || '').replace(/\D/g, '');
82
- if (tin.length !== 8) return { error: '統一編號必須是 8 位數字' };
83
- const { ok, status, body } = await fetchJson(`https://inc.com.tw/api/company/${tin}`, { timeout: 20000 });
84
- if (status === 404) return { error: `查無此統一編號 ${tin}` };
85
- if (!ok || !body) return { error: '查詢失敗(inc.com.tw)', unified_business_no: tin };
80
+ // 風險查核:用統編或公司名(簡稱會自動解析成正主,如「台積電」→台積電本尊)。
81
+ // inc.com.tw/api/card:比 /api/company flags 更完整(含解散/金管裁罰/司法/國際制裁),上市櫃並附即時股價。
82
+ export async function companyRisk(idOrName) {
83
+ const raw = String(idOrName || '').trim();
84
+ if (!raw) return { error: '請提供統一編號或公司名稱' };
85
+ const tin = raw.replace(/\D/g, '');
86
+ const url = /^\d{8}$/.test(tin)
87
+ ? `https://inc.com.tw/api/card/${tin}`
88
+ : `https://inc.com.tw/api/card?name=${encodeURIComponent(raw)}`;
89
+ const { ok, status, body } = await fetchJson(url, { timeout: 20000 });
90
+ if (status === 404) return { error: `查無公司:${raw}` };
91
+ if (!ok || !body || body.error) return { error: '查詢失敗(inc.com.tw)', query: raw };
86
92
  const f = body.flags || {};
87
93
  const redFlags = [];
88
- if (f.government_debarment) redFlags.push('政府採購拒絕往來');
89
- if (f.labor_penalties) redFlags.push(`勞動法令裁罰 ${f.labor_penalties} 筆`);
90
- if (f.environmental_penalties) redFlags.push(`環保裁罰 ${f.environmental_penalties} 筆`);
91
- const level = f.government_debarment ? 'high' : redFlags.length ? 'medium' : 'low';
94
+ if (f.dissolved) redFlags.push(`登記狀態:${body.status}`);
95
+ if (f.reject) redFlags.push('政府採購拒絕往來');
96
+ if (f.sanction) redFlags.push(`命中國際制裁名單 ${f.sanction} 筆(OFAC/UN 等)`);
97
+ if (f.fsc) redFlags.push(`金管會重大裁罰 ${f.fsc} 件`);
98
+ if (f.judicial) redFlags.push(`司法案件 ${f.judicial} 筆`);
99
+ if (f.labor) redFlags.push(`勞動法令裁罰 ${f.labor} 筆`);
100
+ if (f.env) redFlags.push(`環保裁罰 ${f.env} 筆`);
101
+ // 重大紅旗(解散/拒往/制裁/司法/金管)→ high;僅勞動或環保 → medium;皆無 → low
102
+ const major = f.dissolved || f.reject || f.sanction || f.judicial || f.fsc;
103
+ const level = major ? 'high' : (f.labor || f.env) ? 'medium' : 'low';
92
104
  return {
93
- unified_business_no: tin,
105
+ unified_business_no: body.id,
94
106
  name: body.name,
107
+ representative: body.rep || null,
95
108
  status: body.status,
96
- listing: body.listing || null,
109
+ capital: body.capital ?? null,
110
+ listing: body.listed || null,
111
+ stock_price: body.price || null,
97
112
  risk_level: level,
98
113
  red_flags: redFlags,
99
114
  detail: {
100
- government_debarment: !!f.government_debarment,
101
- labor_penalties: f.labor_penalties || 0,
102
- environmental_penalties: f.environmental_penalties || 0,
115
+ dissolved: !!f.dissolved,
116
+ government_debarment: f.reject || 0,
117
+ international_sanction: f.sanction || 0,
118
+ fsc_major_penalty: f.fsc || 0,
119
+ judicial_cases: f.judicial || 0,
120
+ labor_penalties: f.labor || 0,
121
+ environmental_penalties: f.env || 0,
103
122
  },
104
- note: '裁罰以公司名稱比對,可能含同名同稱;僅供參考,以政府原始公告為準',
105
- profile_url: `https://inc.com.tw/c/${tin}`,
123
+ note: '裁罰/名單以公司名稱比對,可能含同名同稱;僅供參考,以政府原始公告為準',
124
+ profile_url: body.url || `https://inc.com.tw/c/${body.id}`,
106
125
  source: '台灣公司登記網 inc.com.tw',
107
126
  };
108
127
  }
109
128
 
129
+ // ── 農產批發行情 MOA(農業部農產品交易行情)──────────────────────
130
+ function _rocToAd(s) {
131
+ const m = String(s || '').match(/^(\d{2,3})\.(\d{2})\.(\d{2})$/);
132
+ return m ? `${Number(m[1]) + 1911}-${m[2]}-${m[3]}` : s || null;
133
+ }
134
+
135
+ // 常見俗名 → 農業部資料作物名(資料用正式名,民眾用俗名)
136
+ const CROP_ALIAS = { 高麗菜: '甘藍', 蕃茄: '番茄', 西紅柿: '番茄', 小蕃茄: '小番茄', 聖女番茄: '小番茄' };
137
+
138
+ export async function farmPrice(crop, market) {
139
+ let c = String(crop || '').trim();
140
+ if (!c) return { error: '請提供農產品名稱,例如「高麗菜」「香蕉」「青蔥」' };
141
+ if (CROP_ALIAS[c]) c = CROP_ALIAS[c];
142
+ const mkt = String(market || '台北一').trim();
143
+ const { ok, body } = await fetchJson(
144
+ `https://data.moa.gov.tw/Service/OpenData/FromM/FarmTransData.aspx?Market=${encodeURIComponent(mkt)}`,
145
+ { timeout: 20000 }
146
+ );
147
+ if (!ok || !Array.isArray(body)) return { error: '查詢失敗(農業部農產行情)', crop: c, market: mkt };
148
+ const matched = body
149
+ .filter((r) => String(r['作物名稱'] || '').includes(c))
150
+ .map((r) => ({
151
+ crop: r['作物名稱'],
152
+ date: _rocToAd(r['交易日期']),
153
+ market: r['市場名稱'],
154
+ avg_price_ntd_kg: r['平均價'],
155
+ high_ntd_kg: r['上價'],
156
+ low_ntd_kg: r['下價'],
157
+ volume_kg: r['交易量'],
158
+ }));
159
+ if (!matched.length) {
160
+ return { crop: c, market: mkt, count: 0,
161
+ note: `${mkt}市場最新交易日查無「${c}」,可換關鍵字或市場(如 台北一/台北二/台中/三重/高雄)`,
162
+ source: '農業部農產品批發市場交易行情 data.moa.gov.tw' };
163
+ }
164
+ return {
165
+ crop: c,
166
+ market: mkt,
167
+ count: matched.length,
168
+ items: matched.slice(0, 30),
169
+ note: '價格單位:元/公斤;交易量:公斤;為該市場最新交易日資料',
170
+ source: '農業部農產品批發市場交易行情 data.moa.gov.tw',
171
+ };
172
+ }
173
+
110
174
  // ── 政府標案 PCC(g0v 政府採購開放資料)─────────────────────────
111
175
  const PCC_BASE = 'https://pcc-api.openfun.app';
112
176