apick-mcp 3.4.0 → 3.5.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # 변경 기록
2
2
 
3
+ ## 3.5.0 — 2026-09-28
4
+
5
+ - 간편인증 데이터 조회 5종의 접수·결과 Tool 10개 계약을 추가했습니다. 전체 106개, Business 25개, 상태 변경 Tool 24개입니다. 원격 서버의 대응 패치 배포가 필요합니다.
6
+ - Document ten request/result tools for five simple-auth data products (106 total, 25 Business, 24 non-read-only). Requires the corresponding remote-server deployment.
7
+ - SDK와 동일한 입력·응답 이름, 승인 대기 흐름, 최초 결과 과금·무료 재조회와 PCCC 계약 차이를 명시했습니다.
8
+ - Preserve the bridge protocol; verify metadata, tool discovery, and request/result forwarding without live data calls.
9
+
10
+ ## 3.4.1 — 2026-09-27
11
+
12
+ - 이미지 생성·편집 요금을 장당 25포인트에서 40포인트로 인상했습니다.
13
+ - Image generation/editing price increased from 25 to 40 points per image.
14
+
3
15
  ## 3.4.0 — 2026-09-19
4
16
 
5
17
  - 개인통관고유부호 조회가 문자(SMS) 인증번호 방식에서 간편인증 방식으로 바뀌었습니다. 기존 `rrn1`·`rrn2`·`auth_key`·`answer` 인자는 더 이상 쓰지 않습니다.
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 APICK
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.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 APICK
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.
package/README.md CHANGED
@@ -2,7 +2,10 @@
2
2
 
3
3
  <img src="https://raw.githubusercontent.com/lead788/apick-mcp/main/assets/logo-400.png" alt="APICK" width="88" height="88">
4
4
 
5
- # APICK MCP — 96 Korean Data, AI, Image & Video Tools
5
+ # APICK MCP — 106 Korean Data, AI, Image & Video Tools
6
+
7
+ > 3.5.0 카탈로그: 106개 Tool(Business 25개). 신규 10개 Tool을 사용하려면 대응하는 원격 서버 버전이 필요합니다. 브릿지 설치만으로 활성화되지 않으며 실제 사용 가능 목록은 연결한 서버의 `tools/list`로 확인하세요.
8
+ > Catalog for 3.5.0: 106 tools (25 Business). The 10 new tools require the matching remote-server deployment; installing this bridge alone does not enable them. Check the connected server’s `tools/list` for availability.
6
9
 
7
10
  **Korean business registry, ID verification, OCR, parcel tracking, file conversion, web intelligence and LLM — as MCP tools for any AI agent.**
8
11
 
@@ -22,9 +25,9 @@
22
25
 
23
26
  ## What is this? / 이게 뭔가요?
24
27
 
25
- **EN** — APICK is a Korean data and AI API platform. This MCP server exposes **96 tools** for Korean business data, identity verification, OCR, parcel tracking, image and video generation, file conversion, web intelligence, and LLM calls.
28
+ **EN** — APICK is a Korean data and AI API platform. This MCP server exposes **106 tools** for Korean business data, identity verification, OCR, parcel tracking, image and video generation, file conversion, web intelligence, and LLM calls.
26
29
 
27
- **KO** — 에이픽(APICK)은 대한민국 데이터·AI API 플랫폼입니다. 이 MCP 서버는 **Tool 96개**로 사업자 조회, 신분증 진위확인, 택배 배송조회, OCR, 이미지·영상 생성, 파일 변환, 웹 검색과 LLM 호출을 **인증키 하나로** 제공합니다.
30
+ **KO** — 에이픽(APICK)은 대한민국 데이터·AI API 플랫폼입니다. 이 MCP 서버는 **Tool 106개**로 사업자 조회, 신분증 진위확인, 택배 배송조회, OCR, 이미지·영상 생성, 파일 변환, 웹 검색과 LLM 호출을 **인증키 하나로** 제공합니다.
28
31
 
29
32
  **The server is hosted by APICK. Nothing to install, build, or keep running.**
30
33
  **서버는 에이픽이 운영합니다. 설치할 것도, 띄워둘 것도 없습니다.**
@@ -42,8 +45,8 @@ https://apick.app/mcp/all
42
45
  Sign up at **[apick.app](https://apick.app)** and copy your license key from the dashboard. New accounts get **1,000 free points**.
43
46
  **[apick.app](https://apick.app)** 에서 가입하고 대시보드에서 인증키를 복사하세요. 신규 가입 시 **1,000포인트 무료**.
44
47
 
45
- > `tools/list` works **without** a key — a client can connect and discover all 96 tools before you sign up. Only `tools/call` validates the key and allowed IP.
46
- > `tools/list`는 **인증 없이** 동작합니다. 가입 전에도 클라이언트가 연결해 96개 Tool을 확인할 수 있고, 키와 허용 IP는 `tools/call`부터 검증합니다.
48
+ > `tools/list` works **without** a key — a client can connect and discover all 106 tools before you sign up. Only `tools/call` validates the key and allowed IP.
49
+ > `tools/list`는 **인증 없이** 동작합니다. 가입 전에도 클라이언트가 연결해 106개 Tool을 확인할 수 있고, 키와 허용 IP는 `tools/call`부터 검증합니다.
47
50
 
48
51
  Leave the allowed-IP list blank for unrestricted access. To restrict access, register the public IPv4 address seen by APICK as an exact address or CIDR such as `/32`. Changes apply immediately with no separate synchronization.
49
52
  마이페이지의 허용 IP가 공란이면 제한 없이 사용할 수 있습니다. 제한하려면 APICK에 도착하는 공인 IPv4를 단일 주소 또는 CIDR(`/32` 등)로 등록하세요. 저장 즉시 반영되며 별도 동기화는 필요하지 않습니다.
@@ -149,8 +152,8 @@ Connect to `all` for everything, or to one server to keep the tool list short an
149
152
 
150
153
  | Server 서버 | Endpoint | Tools | Coverage 범위 |
151
154
  | --- | --- | --- | --- |
152
- | **All 통합** | `https://apick.app/mcp/all` | **96** | 아래 전부 |
153
- | [Business 사업자·커머스](TOOLS.md#business) | `https://apick.app/mcp/business` | 16 | 사업자·법인 조회, 택배 배송조회, 부동산 실거래가, 차량 이력, 유효성 검사 |
155
+ | **All 통합** | `https://apick.app/mcp/all` | **106** | 아래 전부 |
156
+ | [Business 사업자·커머스](TOOLS.md#business) | `https://apick.app/mcp/business` | 25 | 사업자·법인 조회, 택배 배송조회, 부동산 실거래가, 차량 이력, 유효성 검사 |
154
157
  | [Identity 신분증](TOOLS.md#identity) | `https://apick.app/mcp/identity` | 16 | 주민등록증·운전면허증·여권·외국인등록증 진위확인, 실명확인, 개인정보 마스킹 |
155
158
  | [Convert 파일변환](TOOLS.md#convert) | `https://apick.app/mcp/convert` | 22 | PDF·DOCX·엑셀 변환, STT, 비동기 TTS, 워터마크 |
156
159
  | [Web 웹·검색](TOOLS.md#web) | `https://apick.app/mcp/web` | 13 | 도메인·IP·WHOIS, 웹페이지 수집, 구글 검색, 유튜브 |
@@ -161,13 +164,13 @@ Connect to `all` for everything, or to one server to keep the tool list short an
161
164
 
162
165
  ### Every tool / 전체 Tool
163
166
 
164
- **[→ TOOLS.md](TOOLS.md)** — all 96 tools with parameters, types, and copy-paste JSON-RPC examples.
165
- **[→ TOOLS.md](TOOLS.md)** — 96개 전체를 파라미터·타입·호출 예시까지 정리했습니다.
167
+ **[→ TOOLS.md](TOOLS.md)** — all 106 tools with parameters, types, and copy-paste JSON-RPC examples.
168
+ **[→ TOOLS.md](TOOLS.md)** — 106개 전체를 파라미터·타입·호출 예시까지 정리했습니다.
166
169
 
167
170
  <details>
168
171
  <summary><b>Tool names at a glance / Tool 이름 한눈에 보기</b></summary>
169
172
 
170
- **Business** `biz_detail` `venture_biz_info` `land_rt_price` `req_pccc` `get_pccc` `get_car_flooding` `get_car_scrap` `parcel_tracking` `parcel_tracking_auto` `check_email_valid` `check_phone_valid` `check_spam_number` `holiday_info` `search_juso` `info`
173
+ **Business** `biz_detail` `venture_biz_info` `land_rt_price` `req_pccc` `get_pccc` `req_employment` `get_employment` `req_personal_income` `get_personal_income` `req_nps_join_history` `get_nps_join_history` `req_driving_license` `get_driving_license` `req_health_checkup` `get_health_checkup` `get_car_flooding` `get_car_scrap` `parcel_tracking` `parcel_tracking_auto` `check_email_valid` `check_phone_valid` `check_spam_number` `holiday_info` `search_juso` `info`
171
174
 
172
175
  **Identity** `identi_card1`–`identi_card5` `identi_card_image1`–`identi_card_image5` `name_rrn_auth` `hide_rrn` `identity_document_id_card` `identity_document_driver_license` `identity_document_passport` `identity_document_residence_card`
173
176
 
@@ -280,19 +283,55 @@ Seedance 참조 소재 모드는 지원 버전에서 참조 이미지·영상·
280
283
  | **Transport** | Streamable HTTP — one endpoint per server, JSON-RPC 2.0 over HTTPS POST, stateless | 서버당 단일 엔드포인트, HTTPS POST로 JSON-RPC 2.0, 세션 없이 요청 단위 |
281
284
  | **Protocol** | MCP `2026-07-28`, auto-compatible with earlier client versions | MCP `2026-07-28` 기본, 이전 규격 클라이언트 자동 호환 |
282
285
  | **Discovery** | `tools/list` returns every tool with JSON Schema, description and live price — no key needed | `tools/list`가 스키마·설명·실시간 단가를 반환, 인증 불필요 |
283
- | **Annotations** | Every tool declares `title`, `readOnlyHint`, `openWorldHint`. 14 of 96 are not read-only | 전 Tool이 `title`·`readOnlyHint`·`openWorldHint` 선언. 96개 중 상태 변경 Tool은 14개 |
286
+ | **Annotations** | Every tool declares `title`, `readOnlyHint`, `openWorldHint`. 24 of 106 are not read-only | 전 Tool이 `title`·`readOnlyHint`·`openWorldHint` 선언. 106개 중 상태 변경 Tool은 24개 |
284
287
  | **Results** | Text (JSON) + `structuredContent`. Images as image content; files up to 8MB as base64 | 텍스트(JSON)와 `structuredContent` 동시 반환. 이미지는 이미지 콘텐츠, 8MB 이하 파일은 base64 |
285
288
  | **File input** | File-taking tools accept a public `https` URL (`image_url`, `pdf_url`, …) — APICK downloads and processes it | 파일 Tool은 공개 `https` URL을 받습니다. 에이픽 서버가 내려받아 처리합니다 |
286
289
  | **Errors** | Delivered via `isError`; identity masking also preserves `structuredContent.error_code` | `isError`로 전달되며 신분증 마스킹은 `structuredContent.error_code`도 보존합니다 |
287
290
  | **Auth** | `Authorization: Bearer <key>`; `X-API-Key` also accepted | `Authorization: Bearer 인증키`, `X-API-Key`도 지원 |
288
291
 
292
+ ## 간편인증 데이터 조회 / Simple-auth data lookups
293
+
294
+ Business 또는 All 서버에서 접수 Tool을 호출하고, 사용자가 휴대폰에서 승인한 뒤 같은 상품의 결과 Tool에 `transactionId`를 전달합니다. 접수는 알림 발송·과금을 동반하므로 사용자 확인 후 실행하며 승인 대기 중 자동으로 재접수하지 않습니다.
295
+
296
+ Call the request tool on Business or All, ask the user to approve on their phone, then pass `transactionId` to the matching result tool. Request calls send a notification and incur a charge; obtain confirmation and do not repeatedly submit while waiting.
297
+
298
+ | 기능 | SDK 메서드 | MCP Tool |
299
+ | --- | --- | --- |
300
+ | 재직·보험료 확인 | `requestEmployment` / `getEmployment` | `req_employment` / `get_employment` |
301
+ | 금융소득(이자·배당) 조회 | `requestPersonalIncome` / `getPersonalIncome` | `req_personal_income` / `get_personal_income` |
302
+ | 국민연금 가입내역 | `requestNpsJoinHistory` / `getNpsJoinHistory` | `req_nps_join_history` / `get_nps_join_history` |
303
+ | 운전면허 조회 | `requestDrivingLicense` / `getDrivingLicense` | `req_driving_license` / `get_driving_license` |
304
+ | 국가 건강검진 결과 | `requestHealthCheckup` / `getHealthCheckup` | `req_health_checkup` / `get_health_checkup` |
305
+
306
+ 공통 입력은 SDK와 같은 `name`, `birthDate`(YYYYMMDD), `phone`, `authProvider`입니다. 상품별 기간 옵션과 응답 상태·오류는 [Tool 계약](TOOLS.md#simple-auth-data)을 확인하세요. 최초 결과 반환 시 조회 범위에 따라 과금하며, 대기 중 조회와 유효기간 내 재조회는 무료입니다.
307
+
308
+ Common inputs match the SDK: `name`, `birthDate`, `phone`, and `authProvider`. The first result delivery is billed by query scope; waiting polls and repeat reads within the result lifetime are free. See the [tool contract](TOOLS.md#simple-auth-data) for options and response fields.
309
+
310
+ **PCCC 비교:** 승인 흐름은 같지만 `req_pccc`는 `birthday`·`provider`, `get_pccc`는 `tx_id`를 사용하며 결과 재조회도 과금됩니다. 신규 5개 상품의 입력 이름이나 무료 재조회 정책을 PCCC에 적용하지 마세요.
311
+
312
+ **PCCC comparison:** The approval flow is the same, but PCCC retains `birthday`/`provider` and `tx_id`, and charges for repeated result reads. Its contract is unchanged.
313
+
314
+ ---
315
+
289
316
  ### Tools with side effects / 부작용이 있는 Tool
290
317
 
291
- 82 of 96 tools are read-only. The other 14 change state, charge points, cancel work, or consume a result and carry `readOnlyHint: false` so your client can require approval:
292
- 96개 중 82개는 조회입니다. 나머지 14개는 과금·취소·결과 생성 등 상태를 바꾸므로 `readOnlyHint: false`가 붙습니다.
318
+ 82 of 106 tools are read-only. The other 24 change state, charge points, cancel work, or consume a result and carry `readOnlyHint: false` so your client can require approval:
319
+ 106개 중 82개는 조회입니다. 나머지 24개는 과금·취소·결과 생성 등 상태를 바꾸므로 `readOnlyHint: false`가 붙습니다.
293
320
 
294
321
  | Tool | What it does / 하는 일 |
295
322
  | --- | --- |
323
+ | `req_employment` | Requests phone approval and charges acceptance · 재직·보험료 확인 인증 요청·접수 과금 |
324
+ | `get_employment` | Collects and bills the first result; repeat reads are free · 결과 수집·최초 반환 과금, 재조회 무료 |
325
+ | `req_personal_income` | Requests phone approval and charges acceptance · 금융소득(이자·배당) 조회 인증 요청·접수 과금 |
326
+ | `get_personal_income` | Collects and bills the first result; repeat reads are free · 결과 수집·최초 반환 과금, 재조회 무료 |
327
+ | `req_nps_join_history` | Requests phone approval and charges acceptance · 국민연금 가입내역 인증 요청·접수 과금 |
328
+ | `get_nps_join_history` | Collects and bills the first result; repeat reads are free · 결과 수집·최초 반환 과금, 재조회 무료 |
329
+ | `req_driving_license` | Requests phone approval and charges acceptance · 운전면허 조회 인증 요청·접수 과금 |
330
+ | `get_driving_license` | Collects and bills the first result; repeat reads are free · 결과 수집·최초 반환 과금, 재조회 무료 |
331
+ | `req_health_checkup` | Requests phone approval and charges acceptance · 국가 건강검진 결과 인증 요청·접수 과금 |
332
+ | `get_health_checkup` | Collects and bills the first result; repeat reads are free · 결과 수집·최초 반환 과금, 재조회 무료 |
333
+ | `image_generate` / `image_edit` / `image_batch_create` | Creates images and charges points · 이미지 생성·편집·작업 접수 과금 |
334
+ | `tts_jobs_retry` | Resumes a TTS job · TTS 작업 상태 변경 |
296
335
  | `transfer_1won` | Deposits 1 KRW into a bank account · 실제로 1원을 입금합니다 |
297
336
  | `req_pccc` | Sends a simple-authentication request to the person's phone · 본인 휴대폰으로 간편인증 요청을 발송합니다 |
298
337
  | `get_pccc` | Reads the approval result by tx_id and charges once per result · tx_id 로 승인 결과를 조회하고 결과 1건마다 과금합니다 |
package/TOOLS.md CHANGED
@@ -1,7 +1,11 @@
1
1
  # APICK MCP — Full Tool Catalog / 전체 Tool 목록
2
2
 
3
- **96 tools** across **8 domain servers**, plus the combined `all` server.
4
- **Tool 96개**, 분야별 서버 8개와 통합 서버 `all`.
3
+ **106 tools** across **8 domain servers**, plus the combined `all` server.
4
+ **Tool 106개**, 분야별 서버 8개와 통합 서버 `all`.
5
+
6
+ > 3.5.0 카탈로그: 106개 Tool(Business 25개). 신규 10개 Tool을 사용하려면 대응하는 원격 서버 버전이 필요합니다. 브릿지 설치만으로 활성화되지 않으며 실제 사용 가능 목록은 연결한 서버의 `tools/list`로 확인하세요.
7
+ > Catalog for 3.5.0: 106 tools (25 Business). The 10 new tools require the matching remote-server deployment; installing this bridge alone does not enable them. Check the connected server’s `tools/list` for availability.
8
+
5
9
 
6
10
  Official site 공식 사이트: **<https://apick.app>** · Docs 연동 가이드: **<https://apick.app/dev_guide/mcp>**
7
11
 
@@ -12,7 +16,7 @@ Endpoint pattern: `https://apick.app/mcp/{server}` — connect to `all` for ever
12
16
 
13
17
  | Server 서버 | Endpoint | Tools | Coverage 범위 |
14
18
  | --- | --- | --- | --- |
15
- | [Business & Commerce · 사업자 · 커머스](#business) | `/mcp/business` | 15 | 사업자·법인 조회, 택배 배송조회, 부동산 실거래가, 차량 이력, 유효성 검사. |
19
+ | [Business & Commerce · 사업자 · 커머스](#business) | `/mcp/business` | 25 | 사업자·법인 조회, 택배 배송조회, 부동산 실거래가, 차량 이력, 유효성 검사. |
16
20
  | [Identity Verification · 신분증 진위확인 · 마스킹](#identity) | `/mcp/identity` | 16 | 주민등록증·운전면허증·여권·외국인등록증 진위확인, 실명확인, 개인정보 마스킹. |
17
21
  | [OCR · OCR 문자인식](#ocr) | `/mcp/ocr` | 6 | 이미지 텍스트 추출과 신분증 항목 추출. |
18
22
  | [Finance · 금융 · 계좌확인](#finance) | `/mcp/finance` | 3 | 계좌 예금주 실명조회와 1원 인증. |
@@ -20,11 +24,11 @@ Endpoint pattern: `https://apick.app/mcp/{server}` — connect to `all` for ever
20
24
  | [File Conversion · 파일 변환 · 워터마크](#convert) | `/mcp/convert` | 22 | PDF·DOCX·엑셀 변환, 음성인식(STT), 비동기 TTS, 워터마크. |
21
25
  | [Vision · 이미지 · 영상 분석](#vision) | `/mcp/vision` | 6 | 얼굴 검출, 이미지 유사도, 유해이미지 판별, 영상 추출. |
22
26
  | [AI & LLM · AI · LLM](#ai) | `/mcp/ai` | 15 | LLM 챗, 텍스트 도구, 이미지 생성·편집·대량 작업, 비동기 영상 생성. |
23
- | **All 통합** | `/mcp/all` | **96** | 아래 전부 |
27
+ | **All 통합** | `/mcp/all` | **106** | 아래 전부 |
24
28
 
25
- <details><summary><b>All 96 tool names / 전체 Tool 이름</b></summary>
29
+ <details><summary><b>All 106 tool names / 전체 Tool 이름</b></summary>
26
30
 
27
- `biz_detail` · `venture_biz_info` · `land_rt_price` · `req_pccc` · `get_pccc` · `get_car_flooding` · `get_car_scrap` · `parcel_tracking` · `parcel_tracking_auto` · `check_email_valid` · `check_phone_valid` · `check_spam_number` · `holiday_info` · `search_juso` · `info`
31
+ `biz_detail` · `venture_biz_info` · `land_rt_price` · `req_pccc` · `get_pccc` · `req_employment` · `get_employment` · `req_personal_income` · `get_personal_income` · `req_nps_join_history` · `get_nps_join_history` · `req_driving_license` · `get_driving_license` · `req_health_checkup` · `get_health_checkup` · `get_car_flooding` · `get_car_scrap` · `parcel_tracking` · `parcel_tracking_auto` · `check_email_valid` · `check_phone_valid` · `check_spam_number` · `holiday_info` · `search_juso` · `info`
28
32
 
29
33
  `identi_card1` · `identi_card2` · `identi_card3` · `identi_card4` · `identi_card5` · `identi_card_image1` · `identi_card_image2` · `identi_card_image3` · `identi_card_image4` · `identi_card_image5` · `name_rrn_auth` · `hide_rrn` · `identity_document_residence_card` · `identity_document_passport` · `identity_document_id_card` · `identity_document_driver_license`
30
34
 
@@ -48,7 +52,7 @@ Endpoint pattern: `https://apick.app/mcp/{server}` — connect to `all` for ever
48
52
 
49
53
  ## Business & Commerce · 사업자 · 커머스
50
54
 
51
- `https://apick.app/mcp/business` — 15 tools
55
+ `https://apick.app/mcp/business` — 25 tools
52
56
 
53
57
  Korean business registry, corporate credit, parcel tracking, real-estate prices, vehicle history, and input validation.
54
58
 
@@ -61,6 +65,16 @@ Korean business registry, corporate credit, parcel tracking, real-estate prices,
61
65
  | [`land_rt_price`](#land-rt-price) | 부동산 실거래가 조회 | `addr1`, `addr2`, `type`, `year` |
62
66
  | [`req_pccc`](#req-pccc) | 개인통관고유부호 인증 요청 | `name`, `birthday`, `phone`, `provider` |
63
67
  | [`get_pccc`](#get-pccc) | 개인통관고유부호 조회 | `tx_id` |
68
+ | [`req_employment`](#req-employment) | 재직·보험료 확인 인증 요청 | `name`, `birthDate`, `phone`, `authProvider` |
69
+ | [`get_employment`](#get-employment) | 재직·보험료 확인 상태·결과 | `transactionId` |
70
+ | [`req_personal_income`](#req-personal-income) | 금융소득(이자·배당) 조회 인증 요청 | `name`, `birthDate`, `phone`, `authProvider` |
71
+ | [`get_personal_income`](#get-personal-income) | 금융소득(이자·배당) 조회 상태·결과 | `transactionId` |
72
+ | [`req_nps_join_history`](#req-nps-join-history) | 국민연금 가입내역 인증 요청 | `name`, `birthDate`, `phone`, `authProvider` |
73
+ | [`get_nps_join_history`](#get-nps-join-history) | 국민연금 가입내역 상태·결과 | `transactionId` |
74
+ | [`req_driving_license`](#req-driving-license) | 운전면허 조회 인증 요청 | `name`, `birthDate`, `phone`, `authProvider` |
75
+ | [`get_driving_license`](#get-driving-license) | 운전면허 조회 상태·결과 | `transactionId` |
76
+ | [`req_health_checkup`](#req-health-checkup) | 국가 건강검진 결과 인증 요청 | `name`, `birthDate`, `phone`, `authProvider` |
77
+ | [`get_health_checkup`](#get-health-checkup) | 국가 건강검진 결과 상태·결과 | `transactionId` |
64
78
  | [`get_car_flooding`](#get-car-flooding) | 차량 침수차 여부 조회 | `type`, `value` |
65
79
  | [`get_car_scrap`](#get-car-scrap) | 차량 폐차사고처리 여부 조회 | `type`, `value` |
66
80
  | [`parcel_tracking`](#parcel-tracking) | 택배 배송조회 | `carrier`, `trackingNumber` |
@@ -170,6 +184,204 @@ req_pccc Tool 호출로 받은 tx_id 를 입력해 처리 상태를 확인합니
170
184
  {"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_pccc","arguments":{"tx_id":"<tx_id>"}}}
171
185
  ```
172
186
 
187
+ <a id="simple-auth-data"></a>
188
+
189
+ ### 간편인증 데이터 조회 공통 계약 / Shared data-lookup contract
190
+
191
+ 아래 5개 상품은 **접수 → 휴대폰 승인 → 결과 조회** 순서로 호출합니다. 접수 전에 알림 발송·과금과 조회 항목을 사용자에게 확인하고, 승인은 사용자가 휴대폰에서 직접 수행합니다. 승인 대기 중 접수를 반복하지 않습니다.
192
+
193
+ Use request → user approval on the phone → result lookup. Confirm the requested data and acceptance charge before submitting. Never approve on the user’s behalf or automatically repeat the authentication request.
194
+
195
+ | 접수 입력 | 형식 | 필수 |
196
+ | --- | --- | --- |
197
+ | `name` | 본인 이름, 2~40자 | 필수 |
198
+ | `birthDate` | 생년월일 숫자 8자리, YYYYMMDD | 필수 |
199
+ | `phone` | 본인 명의 휴대전화 번호, 숫자 10~11자리, 01로 시작 | 필수 |
200
+ | `authProvider` | `kakao`, `naver`, `toss`, `pass`, `samsung`, `kb`, `shinhan`, `hana`, `woori`, `ibk`, `nh`, `kakaobank`, `banksalad` | 필수 |
201
+
202
+ 결과 Tool은 **같은 상품**의 접수 응답에서 받은 `transactionId`(소문자 16진수 32자리)만 받습니다. 접수·결과 Tool 모두 `readOnlyHint: false`입니다. 접수는 `idempotentHint: false`, 결과 조회는 동일 거래 재조회 시 중복 과금하지 않아 `idempotentHint: true`입니다.
203
+
204
+ Result tools require the `transactionId` from the same product. Both stages are non-read-only because they may trigger notifications, collection or billing. Repeated result reads do not charge again.
205
+
206
+ | 상품 | 선택 입력 | 성공 결과 위치 |
207
+ | --- | --- | --- |
208
+ | 재직·보험료 확인 | insuranceYears: 정수 1~3 (선택, 기본 1) | `result.employment` |
209
+ | 금융소득(이자·배당) 조회 | incomeYears: 정수 1~5 (선택, 기본 1) | `result.personalIncome` |
210
+ | 국민연금 가입내역 | from, to: YYYY-MM (각각 선택) | `result.npsJoinHistory` |
211
+ | 운전면허 조회 | 없음 | `result.drivingLicense` |
212
+ | 국가 건강검진 결과 | 없음 | `result.healthCheckup` |
213
+
214
+ **응답:** MCP의 `structuredContent`와 JSON 텍스트에는 REST 응답의 `data`가 그대로 담깁니다. `schemaVersion`, `transactionId`, `product`, `status`, `resultAvailable`, `charged`, `sources`, `message`, `success`를 확인합니다. 접수에는 `expiresAt`와 선택적 `approvals`, 수집 중에는 `progress`, 결과에는 `checkedAt`, `resultExpiresAt`, `result`가 포함될 수 있습니다. 실제 차감 포인트는 `_meta["app.apick/cost"]`입니다.
215
+
216
+ **Response:** `structuredContent` and JSON text preserve the REST `data` envelope. Check `status`, `resultAvailable`, `charged`, and `errorCode`; transport success alone does not mean collection succeeded. Billing metadata is available under `_meta["app.apick/cost"]`.
217
+
218
+ | 상태 | 클라이언트 처리 |
219
+ | --- | --- |
220
+ | `AUTH_REQUESTED`, `AUTH_WAITING` | 사용자에게 휴대폰 승인을 안내하고 같은 ID 유지 |
221
+ | `AUTH_COMPLETED`, `COLLECTING`, `COLLECTED` | 완료 여부를 확인하며 간격을 두고 같은 결과 Tool 조회 |
222
+ | `SUCCESS`, `PARTIAL_SUCCESS` | `resultAvailable`과 `result` 확인; 부분 성공이면 누락 항목 확인 |
223
+ | `AUTH_REJECTED`, `AUTH_EXPIRED`, `FAILED` | `errorCode`와 `message` 확인; 자동 재접수 중단 |
224
+
225
+ `errorCode`는 `RESULT_EXPIRED`, `AUTH_EXPIRED`, `AUTH_REJECTED`, `COLLECT_FAILED`를 포함합니다. 만료된 결과는 다시 조회할 수 없으며 새로운 인증 접수가 필요합니다. 업무 상태 오류는 `isError: false`인 정상 MCP 응답에도 담길 수 있으므로 `status`와 `errorCode`를 함께 검사하세요.
226
+
227
+ 접수 시 정액 과금, 결과 최초 반환 시 조회 범위별 과금입니다. 승인 대기·수집 중 조회 및 `resultExpiresAt` 전 재조회는 무료입니다. **PCCC는 별도 계약**으로 `birthday`·`provider`·`tx_id`를 사용하고 결과 재조회도 과금됩니다.
228
+
229
+ Acceptance is billed separately; first result delivery is billed by scope. Waiting/collecting polls and repeat reads before `resultExpiresAt` are free. PCCC keeps its existing `birthday`/`provider`/`tx_id` contract and charges repeated result reads.
230
+
231
+ ```json
232
+ {
233
+ "jsonrpc": "2.0",
234
+ "id": 1,
235
+ "method": "tools/call",
236
+ "params": {
237
+ "name": "req_employment",
238
+ "arguments": {
239
+ "name": "홍길동",
240
+ "birthDate": "19900101",
241
+ "phone": "01012345678",
242
+ "authProvider": "kakao",
243
+ "insuranceYears": 1
244
+ }
245
+ }
246
+ }
247
+ ```
248
+
249
+ 사용자가 휴대폰에서 승인한 뒤 접수 응답의 실제 `transactionId`로 조회합니다:
250
+
251
+ ```json
252
+ {
253
+ "jsonrpc": "2.0",
254
+ "id": 2,
255
+ "method": "tools/call",
256
+ "params": {
257
+ "name": "get_employment",
258
+ "arguments": {
259
+ "transactionId": "0123456789abcdef0123456789abcdef"
260
+ }
261
+ }
262
+ }
263
+ ```
264
+
265
+ <a id="req-employment"></a>
266
+
267
+ ### `req_employment` — 재직·보험료 확인 인증 요청
268
+
269
+ REST: `POST /rest/req_employment` · SDK: `requestEmployment()`
270
+
271
+ 필수: `name`, `birthDate`, `phone`, `authProvider`. 선택: insuranceYears: 정수 1~3 (선택, 기본 1).
272
+
273
+ [공통 입력·응답·과금 계약](#simple-auth-data)을 따릅니다.
274
+
275
+ ---
276
+
277
+ <a id="get-employment"></a>
278
+
279
+ ### `get_employment` — 재직·보험료 확인 결과 조회
280
+
281
+ REST: `POST /rest/get_employment` · SDK: `getEmployment()`
282
+
283
+ 필수: `transactionId`. 성공 결과: `result.employment`.
284
+
285
+ [공통 입력·응답·과금 계약](#simple-auth-data)을 따릅니다.
286
+
287
+ ---
288
+
289
+ <a id="req-personal-income"></a>
290
+
291
+ ### `req_personal_income` — 금융소득(이자·배당) 조회 인증 요청
292
+
293
+ REST: `POST /rest/req_personal_income` · SDK: `requestPersonalIncome()`
294
+
295
+ 필수: `name`, `birthDate`, `phone`, `authProvider`. 선택: incomeYears: 정수 1~5 (선택, 기본 1).
296
+
297
+ [공통 입력·응답·과금 계약](#simple-auth-data)을 따릅니다.
298
+
299
+ ---
300
+
301
+ <a id="get-personal-income"></a>
302
+
303
+ ### `get_personal_income` — 금융소득(이자·배당) 조회 결과 조회
304
+
305
+ REST: `POST /rest/get_personal_income` · SDK: `getPersonalIncome()`
306
+
307
+ 필수: `transactionId`. 성공 결과: `result.personalIncome`.
308
+
309
+ [공통 입력·응답·과금 계약](#simple-auth-data)을 따릅니다.
310
+
311
+ ---
312
+
313
+ <a id="req-nps-join-history"></a>
314
+
315
+ ### `req_nps_join_history` — 국민연금 가입내역 인증 요청
316
+
317
+ REST: `POST /rest/req_nps_join_history` · SDK: `requestNpsJoinHistory()`
318
+
319
+ 필수: `name`, `birthDate`, `phone`, `authProvider`. 선택: from, to: YYYY-MM (각각 선택).
320
+
321
+ [공통 입력·응답·과금 계약](#simple-auth-data)을 따릅니다.
322
+
323
+ ---
324
+
325
+ <a id="get-nps-join-history"></a>
326
+
327
+ ### `get_nps_join_history` — 국민연금 가입내역 결과 조회
328
+
329
+ REST: `POST /rest/get_nps_join_history` · SDK: `getNpsJoinHistory()`
330
+
331
+ 필수: `transactionId`. 성공 결과: `result.npsJoinHistory`.
332
+
333
+ [공통 입력·응답·과금 계약](#simple-auth-data)을 따릅니다.
334
+
335
+ ---
336
+
337
+ <a id="req-driving-license"></a>
338
+
339
+ ### `req_driving_license` — 운전면허 조회 인증 요청
340
+
341
+ REST: `POST /rest/req_driving_license` · SDK: `requestDrivingLicense()`
342
+
343
+ 필수: `name`, `birthDate`, `phone`, `authProvider`. 선택: 없음.
344
+
345
+ [공통 입력·응답·과금 계약](#simple-auth-data)을 따릅니다.
346
+
347
+ ---
348
+
349
+ <a id="get-driving-license"></a>
350
+
351
+ ### `get_driving_license` — 운전면허 조회 결과 조회
352
+
353
+ REST: `POST /rest/get_driving_license` · SDK: `getDrivingLicense()`
354
+
355
+ 필수: `transactionId`. 성공 결과: `result.drivingLicense`.
356
+
357
+ [공통 입력·응답·과금 계약](#simple-auth-data)을 따릅니다.
358
+
359
+ ---
360
+
361
+ <a id="req-health-checkup"></a>
362
+
363
+ ### `req_health_checkup` — 국가 건강검진 결과 인증 요청
364
+
365
+ REST: `POST /rest/req_health_checkup` · SDK: `requestHealthCheckup()`
366
+
367
+ 필수: `name`, `birthDate`, `phone`, `authProvider`. 선택: 없음.
368
+
369
+ [공통 입력·응답·과금 계약](#simple-auth-data)을 따릅니다.
370
+
371
+ ---
372
+
373
+ <a id="get-health-checkup"></a>
374
+
375
+ ### `get_health_checkup` — 국가 건강검진 결과 결과 조회
376
+
377
+ REST: `POST /rest/get_health_checkup` · SDK: `getHealthCheckup()`
378
+
379
+ 필수: `transactionId`. 성공 결과: `result.healthCheckup`.
380
+
381
+ [공통 입력·응답·과금 계약](#simple-auth-data)을 따릅니다.
382
+
383
+ ---
384
+
173
385
  <a id="get-car-flooding"></a>
174
386
 
175
387
  ### `get_car_flooding` — 차량 침수차 여부 조회
@@ -1730,7 +1942,7 @@ LLM 챗(다중 모델), 텍스트 요약·교정, 비동기 AI 영상 생성.
1730
1942
  | [`kling_jobs_create`](#kling-jobs-create) | Kling 영상 작업 접수 | `prompt` |
1731
1943
  | [`kling_jobs_status`](#kling-jobs-status) | Kling 영상 작업 상태 | `job_id` |
1732
1944
 
1733
- 이미지 생성·편집은 한 장당 25포인트입니다. 작업 접수 시 요청 장수 전체 금액을 먼저 차감하고, 생성에 실패한 이미지가 있으면 해당 장수의 포인트를 즉시 환급합니다. 접수된 작업은 취소할 수 없습니다. `image_generate`에 `reference_image_url`을 더하면 참고 이미지의 구도·색감·제품 형태와 프롬프트를 함께 반영할 수 있습니다. 편집은 원본 이미지 한 장과 프롬프트만 받으며 마스크 파일은 지원하지 않습니다. 동기 Tool은 응답 크기를 위해 한 장만 반환하며, 대량 작업은 `image_count`에 1~50을 지정한 뒤 `image_batch_result`로 한 장씩 가져옵니다. 크기는 `1024x1024`, `1536x1024`, `1024x1536`, `1152x864`, `864x1152` 중에서 고릅니다. PNG·JPEG·WebP와 PNG/WebP 투명 배경 미리보기를 지원합니다. 프롬프트는 최대 28,000자이고 완료 결과는 24시간 동안 반복 조회할 수 있습니다. `idempotency_key`는 같은 요청의 중복 생성·과금을 막는 8~128자 안전번호이며, 동일 요청을 재전송할 때만 같은 값을 사용합니다.
1945
+ 이미지 생성·편집은 한 장당 40포인트입니다. 작업 접수 시 요청 장수 전체 금액을 먼저 차감하고, 생성에 실패한 이미지가 있으면 해당 장수의 포인트를 즉시 환급합니다. 접수된 작업은 취소할 수 없습니다. `image_generate`에 `reference_image_url`을 더하면 참고 이미지의 구도·색감·제품 형태와 프롬프트를 함께 반영할 수 있습니다. 편집은 원본 이미지 한 장과 프롬프트만 받으며 마스크 파일은 지원하지 않습니다. 동기 Tool은 응답 크기를 위해 한 장만 반환하며, 대량 작업은 `image_count`에 1~50을 지정한 뒤 `image_batch_result`로 한 장씩 가져옵니다. 크기는 `1024x1024`, `1536x1024`, `1024x1536`, `1152x864`, `864x1152` 중에서 고릅니다. PNG·JPEG·WebP와 PNG/WebP 투명 배경 미리보기를 지원합니다. 프롬프트는 최대 28,000자이고 완료 결과는 24시간 동안 반복 조회할 수 있습니다. `idempotency_key`는 같은 요청의 중복 생성·과금을 막는 8~128자 안전번호이며, 동일 요청을 재전송할 때만 같은 값을 사용합니다.
1734
1946
 
1735
1947
  예: `흰색 대리석 테이블 위의 무광 검정 텀블러, 부드러운 아침 자연광, 제품 전체가 프레임 안에 보이게, 이미지 안 글자 없음`처럼 피사체·배경·조명·구도·금지 요소를 구체적으로 적습니다. 글자를 넣을 때는 `상단 중앙에 '가을 산책'을 또렷한 짙은 남색 한글로, 다른 글자 없음`처럼 실제 문구와 위치를 함께 지정합니다.
1736
1948
 
package/package.json CHANGED
@@ -1,89 +1,89 @@
1
- {
2
- "name": "apick-mcp",
3
- "version": "3.4.0",
4
- "description": "APICK MCP — 96 Korean data, AI, image & video tools for AI agents. 사업자조회·신분증·OCR·배송조회·이미지 생성·편집·영상 생성·파일변환·검색·LLM.",
5
- "type": "module",
6
- "license": "MIT",
7
- "author": "APICK",
8
- "homepage": "https://apick.app/dev_guide/mcp",
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/lead788/apick-mcp.git"
12
- },
13
- "bugs": {
14
- "url": "https://github.com/lead788/apick-mcp/issues"
15
- },
16
- "mcpName": "app.apick/all",
17
- "bin": {
18
- "apick-mcp": "src/index.js"
19
- },
20
- "exports": {
21
- ".": "./src/bridge.js"
22
- },
23
- "files": [
24
- "src",
25
- "README.md",
26
- "TOOLS.md",
27
- "CHANGELOG.md",
28
- "LICENSE"
29
- ],
30
- "engines": {
31
- "node": ">=18"
32
- },
33
- "scripts": {
34
- "test": "node --test"
35
- },
36
- "keywords": [
37
- "mcp",
38
- "modelcontextprotocol",
39
- "model-context-protocol",
40
- "mcp-server",
41
- "mcp-tools",
42
- "claude",
43
- "claude-code",
44
- "claude-desktop",
45
- "cursor",
46
- "cline",
47
- "windsurf",
48
- "ai-agent",
49
- "llm",
50
- "apick",
51
- "korea",
52
- "korean",
53
- "korea-api",
54
- "business-registry",
55
- "business-registration-number",
56
- "identity-verification",
57
- "kyc",
58
- "kyb",
59
- "id-card-verification",
60
- "pii-masking",
61
- "ocr",
62
- "korean-ocr",
63
- "parcel-tracking",
64
- "delivery-tracking",
65
- "shipment-tracking",
66
- "bank-account-verification",
67
- "account-holder-lookup",
68
- "real-estate-price",
69
- "address-search",
70
- "holiday-api",
71
- "whois",
72
- "dns-lookup",
73
- "ip-geolocation",
74
- "web-scraping",
75
- "screenshot-api",
76
- "google-search",
77
- "pdf-conversion",
78
- "docx",
79
- "excel",
80
- "watermark",
81
- "speech-to-text",
82
- "face-detection",
83
- "face-blur",
84
- "nsfw-detection",
85
- "image-similarity",
86
- "image-generation",
87
- "image-editing"
88
- ]
89
- }
1
+ {
2
+ "name": "apick-mcp",
3
+ "version": "3.5.0",
4
+ "description": "APICK MCP — 106 Korean data, AI, image & video tools for AI agents. 사업자조회·신분증·OCR·배송조회·이미지 생성·편집·영상 생성·파일변환·검색·LLM.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "APICK",
8
+ "homepage": "https://apick.app/dev_guide/mcp",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/lead788/apick-mcp.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/lead788/apick-mcp/issues"
15
+ },
16
+ "mcpName": "app.apick/all",
17
+ "bin": {
18
+ "apick-mcp": "src/index.js"
19
+ },
20
+ "exports": {
21
+ ".": "./src/bridge.js"
22
+ },
23
+ "files": [
24
+ "src",
25
+ "README.md",
26
+ "TOOLS.md",
27
+ "CHANGELOG.md",
28
+ "LICENSE"
29
+ ],
30
+ "engines": {
31
+ "node": ">=18"
32
+ },
33
+ "scripts": {
34
+ "test": "node --test"
35
+ },
36
+ "keywords": [
37
+ "mcp",
38
+ "modelcontextprotocol",
39
+ "model-context-protocol",
40
+ "mcp-server",
41
+ "mcp-tools",
42
+ "claude",
43
+ "claude-code",
44
+ "claude-desktop",
45
+ "cursor",
46
+ "cline",
47
+ "windsurf",
48
+ "ai-agent",
49
+ "llm",
50
+ "apick",
51
+ "korea",
52
+ "korean",
53
+ "korea-api",
54
+ "business-registry",
55
+ "business-registration-number",
56
+ "identity-verification",
57
+ "kyc",
58
+ "kyb",
59
+ "id-card-verification",
60
+ "pii-masking",
61
+ "ocr",
62
+ "korean-ocr",
63
+ "parcel-tracking",
64
+ "delivery-tracking",
65
+ "shipment-tracking",
66
+ "bank-account-verification",
67
+ "account-holder-lookup",
68
+ "real-estate-price",
69
+ "address-search",
70
+ "holiday-api",
71
+ "whois",
72
+ "dns-lookup",
73
+ "ip-geolocation",
74
+ "web-scraping",
75
+ "screenshot-api",
76
+ "google-search",
77
+ "pdf-conversion",
78
+ "docx",
79
+ "excel",
80
+ "watermark",
81
+ "speech-to-text",
82
+ "face-detection",
83
+ "face-blur",
84
+ "nsfw-detection",
85
+ "image-similarity",
86
+ "image-generation",
87
+ "image-editing"
88
+ ]
89
+ }
package/src/bridge.js CHANGED
@@ -1,175 +1,175 @@
1
- // stdio <-> Streamable HTTP bridge for the APICK MCP server.
2
- //
3
- // APICK serves MCP over Streamable HTTP at https://apick.app/mcp/{server}. Clients that
4
- // speak remote HTTP should connect there directly. This bridge exists for clients that
5
- // only launch local stdio processes.
6
- //
7
- // It is a pump, not a protocol implementation: every JSON-RPC message read from stdin is
8
- // forwarded verbatim, and every message the server returns is written verbatim to stdout.
9
- // Capability and version negotiation, tool schemas and errors are all handled end to end
10
- // by the client and the server.
11
-
12
- export const DEFAULT_ENDPOINT = 'https://apick.app/mcp';
13
-
14
- export const SERVERS = [
15
- 'all',
16
- 'business',
17
- 'identity',
18
- 'ocr',
19
- 'finance',
20
- 'web',
21
- 'convert',
22
- 'vision',
23
- 'ai'
24
- ];
25
-
26
- // A key must never reach stdout, stderr or an error message.
27
- export function redact(text, apiKey) {
28
- if (!text) return text;
29
- let out = String(text);
30
- if (apiKey) out = out.split(apiKey).join('***');
31
- return out.replace(/(Bearer\s+)[\w.\-]+/gi, '$1***');
32
- }
33
-
34
- // Streamable HTTP replies with either a single JSON body or an SSE stream carrying one or
35
- // more JSON-RPC messages. Both shapes reduce to "a list of messages".
36
- function parseEventStream(text) {
37
- const messages = [];
38
- for (const block of text.split(/\r?\n\r?\n/)) {
39
- const data = block
40
- .split(/\r?\n/)
41
- .filter((line) => line.startsWith('data:'))
42
- .map((line) => line.slice(5).trim())
43
- .join('\n');
44
- if (!data) continue;
45
- try {
46
- messages.push(JSON.parse(data));
47
- } catch {
48
- // Keep-alive or comment frame - nothing to forward.
49
- }
50
- }
51
- return messages;
52
- }
53
-
54
- export function createBridge(options = {}) {
55
- const server = options.server || 'all';
56
- const apiKey = options.apiKey || '';
57
- const endpoint = (options.endpoint || DEFAULT_ENDPOINT).replace(/\/+$/, '') + '/' + server;
58
- const fetchImpl = options.fetch || globalThis.fetch;
59
- const write = options.write || ((line) => process.stdout.write(line + '\n'));
60
- const warn = options.warn || ((line) => process.stderr.write(line + '\n'));
61
-
62
- // The gateway is stateless, but honour a session id and the negotiated protocol
63
- // version if the server ever starts sending them.
64
- let sessionId = null;
65
- let protocolVersion = null;
66
-
67
- function headers() {
68
- const h = {
69
- 'content-type': 'application/json',
70
- accept: 'application/json, text/event-stream',
71
- 'user-agent': 'apick-mcp-bridge'
72
- };
73
- if (apiKey) h.authorization = 'Bearer ' + apiKey;
74
- if (sessionId) h['mcp-session-id'] = sessionId;
75
- if (protocolVersion) h['mcp-protocol-version'] = protocolVersion;
76
- return h;
77
- }
78
-
79
- function remember(message) {
80
- const negotiated = message && message.result && message.result.protocolVersion;
81
- if (negotiated) protocolVersion = negotiated;
82
- }
83
-
84
- async function send(message) {
85
- let res;
86
- try {
87
- res = await fetchImpl(endpoint, {
88
- method: 'POST',
89
- headers: headers(),
90
- body: JSON.stringify(message)
91
- });
92
- } catch (err) {
93
- return fail(message, -32001, 'Cannot reach ' + endpoint + ': ' + redact(err.message, apiKey));
94
- }
95
-
96
- const incomingSession = res.headers.get && res.headers.get('mcp-session-id');
97
- if (incomingSession) sessionId = incomingSession;
98
-
99
- const body = await res.text();
100
- if (!body) {
101
- // 202 for notifications and responses - nothing to forward.
102
- if (res.ok) return;
103
- return fail(message, -32002, 'HTTP ' + res.status + ' from ' + endpoint);
104
- }
105
-
106
- const contentType = (res.headers.get && res.headers.get('content-type')) || '';
107
- let messages;
108
- if (contentType.includes('text/event-stream')) {
109
- messages = parseEventStream(body);
110
- } else {
111
- try {
112
- const parsed = JSON.parse(body);
113
- messages = Array.isArray(parsed) ? parsed : [parsed];
114
- } catch {
115
- return fail(message, -32002, 'HTTP ' + res.status + ' from ' + endpoint + ': unexpected response body');
116
- }
117
- }
118
-
119
- for (const out of messages) {
120
- remember(out);
121
- write(JSON.stringify(out));
122
- }
123
- }
124
-
125
- // Only a request (one carrying an id) can be answered with an error; a failed
126
- // notification is reported on stderr so the client is not left waiting on a reply.
127
- function fail(message, code, text) {
128
- const detail = redact(text, apiKey);
129
- if (message && message.id !== undefined && message.id !== null) {
130
- write(JSON.stringify({ jsonrpc: '2.0', id: message.id, error: { code, message: detail } }));
131
- } else {
132
- warn('apick-mcp: ' + detail);
133
- }
134
- }
135
-
136
- // Newline-delimited JSON in, newline-delimited JSON out.
137
- function handleLine(line) {
138
- const trimmed = line.trim();
139
- if (!trimmed) return Promise.resolve();
140
- let message;
141
- try {
142
- message = JSON.parse(trimmed);
143
- } catch {
144
- warn('apick-mcp: ignoring unparseable input line');
145
- return Promise.resolve();
146
- }
147
- return send(message);
148
- }
149
-
150
- function attach(input) {
151
- let buffer = '';
152
- const pending = new Set();
153
- input.setEncoding('utf8');
154
- input.on('data', (chunk) => {
155
- buffer += chunk;
156
- let index;
157
- while ((index = buffer.indexOf('\n')) !== -1) {
158
- const line = buffer.slice(0, index);
159
- buffer = buffer.slice(index + 1);
160
- const task = handleLine(line).catch((err) => warn('apick-mcp: ' + redact(err.message, apiKey)));
161
- pending.add(task);
162
- task.finally(() => pending.delete(task));
163
- }
164
- });
165
- return new Promise((resolve) => {
166
- input.on('end', async () => {
167
- if (buffer.trim()) await handleLine(buffer).catch(() => {});
168
- await Promise.allSettled([...pending]);
169
- resolve();
170
- });
171
- });
172
- }
173
-
174
- return { endpoint, attach, handleLine };
175
- }
1
+ // stdio <-> Streamable HTTP bridge for the APICK MCP server.
2
+ //
3
+ // APICK serves MCP over Streamable HTTP at https://apick.app/mcp/{server}. Clients that
4
+ // speak remote HTTP should connect there directly. This bridge exists for clients that
5
+ // only launch local stdio processes.
6
+ //
7
+ // It is a pump, not a protocol implementation: every JSON-RPC message read from stdin is
8
+ // forwarded verbatim, and every message the server returns is written verbatim to stdout.
9
+ // Capability and version negotiation, tool schemas and errors are all handled end to end
10
+ // by the client and the server.
11
+
12
+ export const DEFAULT_ENDPOINT = 'https://apick.app/mcp';
13
+
14
+ export const SERVERS = [
15
+ 'all',
16
+ 'business',
17
+ 'identity',
18
+ 'ocr',
19
+ 'finance',
20
+ 'web',
21
+ 'convert',
22
+ 'vision',
23
+ 'ai'
24
+ ];
25
+
26
+ // A key must never reach stdout, stderr or an error message.
27
+ export function redact(text, apiKey) {
28
+ if (!text) return text;
29
+ let out = String(text);
30
+ if (apiKey) out = out.split(apiKey).join('***');
31
+ return out.replace(/(Bearer\s+)[\w.\-]+/gi, '$1***');
32
+ }
33
+
34
+ // Streamable HTTP replies with either a single JSON body or an SSE stream carrying one or
35
+ // more JSON-RPC messages. Both shapes reduce to "a list of messages".
36
+ function parseEventStream(text) {
37
+ const messages = [];
38
+ for (const block of text.split(/\r?\n\r?\n/)) {
39
+ const data = block
40
+ .split(/\r?\n/)
41
+ .filter((line) => line.startsWith('data:'))
42
+ .map((line) => line.slice(5).trim())
43
+ .join('\n');
44
+ if (!data) continue;
45
+ try {
46
+ messages.push(JSON.parse(data));
47
+ } catch {
48
+ // Keep-alive or comment frame - nothing to forward.
49
+ }
50
+ }
51
+ return messages;
52
+ }
53
+
54
+ export function createBridge(options = {}) {
55
+ const server = options.server || 'all';
56
+ const apiKey = options.apiKey || '';
57
+ const endpoint = (options.endpoint || DEFAULT_ENDPOINT).replace(/\/+$/, '') + '/' + server;
58
+ const fetchImpl = options.fetch || globalThis.fetch;
59
+ const write = options.write || ((line) => process.stdout.write(line + '\n'));
60
+ const warn = options.warn || ((line) => process.stderr.write(line + '\n'));
61
+
62
+ // The gateway is stateless, but honour a session id and the negotiated protocol
63
+ // version if the server ever starts sending them.
64
+ let sessionId = null;
65
+ let protocolVersion = null;
66
+
67
+ function headers() {
68
+ const h = {
69
+ 'content-type': 'application/json',
70
+ accept: 'application/json, text/event-stream',
71
+ 'user-agent': 'apick-mcp-bridge'
72
+ };
73
+ if (apiKey) h.authorization = 'Bearer ' + apiKey;
74
+ if (sessionId) h['mcp-session-id'] = sessionId;
75
+ if (protocolVersion) h['mcp-protocol-version'] = protocolVersion;
76
+ return h;
77
+ }
78
+
79
+ function remember(message) {
80
+ const negotiated = message && message.result && message.result.protocolVersion;
81
+ if (negotiated) protocolVersion = negotiated;
82
+ }
83
+
84
+ async function send(message) {
85
+ let res;
86
+ try {
87
+ res = await fetchImpl(endpoint, {
88
+ method: 'POST',
89
+ headers: headers(),
90
+ body: JSON.stringify(message)
91
+ });
92
+ } catch (err) {
93
+ return fail(message, -32001, 'Cannot reach ' + endpoint + ': ' + redact(err.message, apiKey));
94
+ }
95
+
96
+ const incomingSession = res.headers.get && res.headers.get('mcp-session-id');
97
+ if (incomingSession) sessionId = incomingSession;
98
+
99
+ const body = await res.text();
100
+ if (!body) {
101
+ // 202 for notifications and responses - nothing to forward.
102
+ if (res.ok) return;
103
+ return fail(message, -32002, 'HTTP ' + res.status + ' from ' + endpoint);
104
+ }
105
+
106
+ const contentType = (res.headers.get && res.headers.get('content-type')) || '';
107
+ let messages;
108
+ if (contentType.includes('text/event-stream')) {
109
+ messages = parseEventStream(body);
110
+ } else {
111
+ try {
112
+ const parsed = JSON.parse(body);
113
+ messages = Array.isArray(parsed) ? parsed : [parsed];
114
+ } catch {
115
+ return fail(message, -32002, 'HTTP ' + res.status + ' from ' + endpoint + ': unexpected response body');
116
+ }
117
+ }
118
+
119
+ for (const out of messages) {
120
+ remember(out);
121
+ write(JSON.stringify(out));
122
+ }
123
+ }
124
+
125
+ // Only a request (one carrying an id) can be answered with an error; a failed
126
+ // notification is reported on stderr so the client is not left waiting on a reply.
127
+ function fail(message, code, text) {
128
+ const detail = redact(text, apiKey);
129
+ if (message && message.id !== undefined && message.id !== null) {
130
+ write(JSON.stringify({ jsonrpc: '2.0', id: message.id, error: { code, message: detail } }));
131
+ } else {
132
+ warn('apick-mcp: ' + detail);
133
+ }
134
+ }
135
+
136
+ // Newline-delimited JSON in, newline-delimited JSON out.
137
+ function handleLine(line) {
138
+ const trimmed = line.trim();
139
+ if (!trimmed) return Promise.resolve();
140
+ let message;
141
+ try {
142
+ message = JSON.parse(trimmed);
143
+ } catch {
144
+ warn('apick-mcp: ignoring unparseable input line');
145
+ return Promise.resolve();
146
+ }
147
+ return send(message);
148
+ }
149
+
150
+ function attach(input) {
151
+ let buffer = '';
152
+ const pending = new Set();
153
+ input.setEncoding('utf8');
154
+ input.on('data', (chunk) => {
155
+ buffer += chunk;
156
+ let index;
157
+ while ((index = buffer.indexOf('\n')) !== -1) {
158
+ const line = buffer.slice(0, index);
159
+ buffer = buffer.slice(index + 1);
160
+ const task = handleLine(line).catch((err) => warn('apick-mcp: ' + redact(err.message, apiKey)));
161
+ pending.add(task);
162
+ task.finally(() => pending.delete(task));
163
+ }
164
+ });
165
+ return new Promise((resolve) => {
166
+ input.on('end', async () => {
167
+ if (buffer.trim()) await handleLine(buffer).catch(() => {});
168
+ await Promise.allSettled([...pending]);
169
+ resolve();
170
+ });
171
+ });
172
+ }
173
+
174
+ return { endpoint, attach, handleLine };
175
+ }
package/src/index.js CHANGED
@@ -1,92 +1,92 @@
1
- #!/usr/bin/env node
2
- // apick-mcp - run the APICK MCP server over stdio.
3
- //
4
- // Usage:
5
- // apick-mcp [--server <name>]
6
- //
7
- // Environment:
8
- // APICK_API_KEY APICK license key. Optional: tools/list works without one,
9
- // tools/call needs it.
10
- // APICK_MCP_SERVER Which server to expose (default: all).
11
- // APICK_MCP_URL Base URL override (default: https://apick.app/mcp).
12
-
13
- import { readFileSync } from 'node:fs';
14
- import { fileURLToPath } from 'node:url';
15
- import { createBridge, DEFAULT_ENDPOINT, SERVERS } from './bridge.js';
16
-
17
- const HELP = `apick-mcp - APICK MCP server over stdio
18
-
19
- apick-mcp [--server <name>]
20
-
21
- Options
22
- --server, -s <name> ${SERVERS.join(', ')} (default: all)
23
- --version, -v print version
24
- --help, -h print this message
25
-
26
- Environment
27
- APICK_API_KEY APICK license key (get one at https://apick.app)
28
- APICK_MCP_SERVER same as --server
29
- APICK_MCP_URL base URL override (default: ${DEFAULT_ENDPOINT})
30
-
31
- Clients that support remote MCP can skip this bridge and connect straight to
32
- ${DEFAULT_ENDPOINT}/all with an Authorization: Bearer header.
33
- Docs: https://apick.app/dev_guide/mcp
34
- `;
35
-
36
- function parseArgs(argv) {
37
- const args = { server: process.env.APICK_MCP_SERVER || 'all' };
38
- for (let i = 0; i < argv.length; i++) {
39
- const arg = argv[i];
40
- if (arg === '--help' || arg === '-h') args.help = true;
41
- else if (arg === '--version' || arg === '-v') args.version = true;
42
- else if (arg === '--server' || arg === '-s') args.server = argv[++i];
43
- else if (arg.startsWith('--server=')) args.server = arg.slice('--server='.length);
44
- else args.unknown = arg;
45
- }
46
- return args;
47
- }
48
-
49
- async function main() {
50
- const args = parseArgs(process.argv.slice(2));
51
-
52
- if (args.help) {
53
- process.stdout.write(HELP);
54
- return;
55
- }
56
- if (args.version) {
57
- const pkgPath = fileURLToPath(new URL('../package.json', import.meta.url));
58
- process.stdout.write(JSON.parse(readFileSync(pkgPath, 'utf8')).version + '\n');
59
- return;
60
- }
61
- if (args.unknown) {
62
- process.stderr.write('apick-mcp: unknown argument ' + args.unknown + '\n\n' + HELP);
63
- process.exitCode = 2;
64
- return;
65
- }
66
- if (!SERVERS.includes(args.server)) {
67
- process.stderr.write(
68
- 'apick-mcp: unknown server "' + args.server + '". Available: ' + SERVERS.join(', ') + '\n'
69
- );
70
- process.exitCode = 2;
71
- return;
72
- }
73
-
74
- const bridge = createBridge({
75
- server: args.server,
76
- apiKey: process.env.APICK_API_KEY,
77
- endpoint: process.env.APICK_MCP_URL
78
- });
79
-
80
- if (!process.env.APICK_API_KEY) {
81
- process.stderr.write(
82
- 'apick-mcp: no APICK_API_KEY set - tool discovery works, tool calls will ask you to sign in at https://apick.app\n'
83
- );
84
- }
85
-
86
- await bridge.attach(process.stdin);
87
- }
88
-
89
- main().catch((err) => {
90
- process.stderr.write('apick-mcp: ' + err.message + '\n');
91
- process.exitCode = 1;
92
- });
1
+ #!/usr/bin/env node
2
+ // apick-mcp - run the APICK MCP server over stdio.
3
+ //
4
+ // Usage:
5
+ // apick-mcp [--server <name>]
6
+ //
7
+ // Environment:
8
+ // APICK_API_KEY APICK license key. Optional: tools/list works without one,
9
+ // tools/call needs it.
10
+ // APICK_MCP_SERVER Which server to expose (default: all).
11
+ // APICK_MCP_URL Base URL override (default: https://apick.app/mcp).
12
+
13
+ import { readFileSync } from 'node:fs';
14
+ import { fileURLToPath } from 'node:url';
15
+ import { createBridge, DEFAULT_ENDPOINT, SERVERS } from './bridge.js';
16
+
17
+ const HELP = `apick-mcp - APICK MCP server over stdio
18
+
19
+ apick-mcp [--server <name>]
20
+
21
+ Options
22
+ --server, -s <name> ${SERVERS.join(', ')} (default: all)
23
+ --version, -v print version
24
+ --help, -h print this message
25
+
26
+ Environment
27
+ APICK_API_KEY APICK license key (get one at https://apick.app)
28
+ APICK_MCP_SERVER same as --server
29
+ APICK_MCP_URL base URL override (default: ${DEFAULT_ENDPOINT})
30
+
31
+ Clients that support remote MCP can skip this bridge and connect straight to
32
+ ${DEFAULT_ENDPOINT}/all with an Authorization: Bearer header.
33
+ Docs: https://apick.app/dev_guide/mcp
34
+ `;
35
+
36
+ function parseArgs(argv) {
37
+ const args = { server: process.env.APICK_MCP_SERVER || 'all' };
38
+ for (let i = 0; i < argv.length; i++) {
39
+ const arg = argv[i];
40
+ if (arg === '--help' || arg === '-h') args.help = true;
41
+ else if (arg === '--version' || arg === '-v') args.version = true;
42
+ else if (arg === '--server' || arg === '-s') args.server = argv[++i];
43
+ else if (arg.startsWith('--server=')) args.server = arg.slice('--server='.length);
44
+ else args.unknown = arg;
45
+ }
46
+ return args;
47
+ }
48
+
49
+ async function main() {
50
+ const args = parseArgs(process.argv.slice(2));
51
+
52
+ if (args.help) {
53
+ process.stdout.write(HELP);
54
+ return;
55
+ }
56
+ if (args.version) {
57
+ const pkgPath = fileURLToPath(new URL('../package.json', import.meta.url));
58
+ process.stdout.write(JSON.parse(readFileSync(pkgPath, 'utf8')).version + '\n');
59
+ return;
60
+ }
61
+ if (args.unknown) {
62
+ process.stderr.write('apick-mcp: unknown argument ' + args.unknown + '\n\n' + HELP);
63
+ process.exitCode = 2;
64
+ return;
65
+ }
66
+ if (!SERVERS.includes(args.server)) {
67
+ process.stderr.write(
68
+ 'apick-mcp: unknown server "' + args.server + '". Available: ' + SERVERS.join(', ') + '\n'
69
+ );
70
+ process.exitCode = 2;
71
+ return;
72
+ }
73
+
74
+ const bridge = createBridge({
75
+ server: args.server,
76
+ apiKey: process.env.APICK_API_KEY,
77
+ endpoint: process.env.APICK_MCP_URL
78
+ });
79
+
80
+ if (!process.env.APICK_API_KEY) {
81
+ process.stderr.write(
82
+ 'apick-mcp: no APICK_API_KEY set - tool discovery works, tool calls will ask you to sign in at https://apick.app\n'
83
+ );
84
+ }
85
+
86
+ await bridge.attach(process.stdin);
87
+ }
88
+
89
+ main().catch((err) => {
90
+ process.stderr.write('apick-mcp: ' + err.message + '\n');
91
+ process.exitCode = 1;
92
+ });