@apps-in-toss/ax 0.7.1

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 ADDED
@@ -0,0 +1,149 @@
1
+ # AX (AppsInToss eXperience)
2
+
3
+ AI 어시스턴트가 AppsInToss 미니앱 개발을 도울 수 있도록 설계된 MCP(Model Context Protocol) 서버입니다.
4
+
5
+ ## 주요 기능
6
+
7
+ AX는 AI 어시스턴트에게 AppsInToss Developer Center의 문서를 제공하여, 토스 미니앱 개발에 대한 정확하고 컨텍스트 기반의 지원을 가능하게 합니다.
8
+
9
+ ### MCP Tools
10
+
11
+ | 도구 | 설명 |
12
+ |------|------|
13
+ | `search_docs` | AppsInToss 문서 검색 |
14
+ | `get_doc` | 검색 결과의 문서 전체 내용 조회 |
15
+ | `search_tds_rn_docs` | TDS React Native 문서 검색 |
16
+ | `get_tds_rn_doc` | TDS React Native 문서 전체 내용 조회 |
17
+ | `search_tds_web_docs` | TDS Web 문서 검색 |
18
+ | `get_tds_web_doc` | TDS Web 문서 전체 내용 조회 |
19
+
20
+ ### 지원 문서
21
+
22
+ - **AppsInToss Developer Center** - 미니앱 개발 가이드
23
+ - **TDS React Native** - 토스 디자인 시스템 (React Native)
24
+ - **TDS Web** - 토스 디자인 시스템 (WebView)
25
+
26
+ ## 설치
27
+
28
+ ### Homebrew (macOS/Linux)
29
+
30
+ ```bash
31
+ brew tap toss/tap
32
+ brew install ax
33
+ ```
34
+
35
+ ### Scoop (Windows)
36
+
37
+ ```powershell
38
+ scoop bucket add toss https://github.com/toss/scoop-bucket
39
+ scoop install ax
40
+ ```
41
+
42
+ ### npm
43
+
44
+ ```bash
45
+ npm install -g @apps-in-toss/ax
46
+ ```
47
+
48
+ ## 사용법
49
+
50
+ ### MCP 서버 시작
51
+
52
+ ```bash
53
+ ax mcp
54
+ ax mcp --disable-usage-stats
55
+ ```
56
+
57
+ AX는 제품 개선과 안정적인 운영을 위해 명령 실행, MCP 도구 호출, 성공 여부, 실행 시간 같은 사용 통계를 수집합니다.
58
+ 사용 통계 수집을 원하지 않으면 `--disable-usage-stats` 옵션을 추가하세요.
59
+
60
+ ### Cursor/Claude에서 사용
61
+
62
+ [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=apps-in-toss&config=eyJjb21tYW5kIjoiYXgiLCJhcmdzIjpbIm1jcCJdfQ==)
63
+
64
+ 또는 `.cursor/mcp.json` / `claude_desktop_config.json`에 다음을 추가하세요:
65
+
66
+ ```json
67
+ {
68
+ "mcpServers": {
69
+ "apps-in-toss": {
70
+ "command": "ax",
71
+ "args": [
72
+ "mcp"
73
+ ]
74
+ }
75
+ }
76
+ }
77
+ ```
78
+
79
+ ## 프로젝트 구조
80
+
81
+ ```
82
+ apps-in-toss-ax/
83
+ ├── cmd/ # CLI 명령어 정의
84
+ ├── internal/ # 내부 패키지
85
+ │ ├── httputil/ # HTTP 유틸리티
86
+ │ └── utils/ # 공통 유틸리티
87
+ ├── pkg/ # 핵심 패키지
88
+ │ ├── app/ # 애플리케이션 진입점
89
+ │ ├── docs/ # 문서 관리
90
+ │ ├── docid/ # 문서 ID 생성
91
+ │ ├── features/ # 기능 구성
92
+ │ ├── fetcher/ # HTTP 클라이언트
93
+ │ ├── instrumentation/ # 사용 통계 수집
94
+ │ ├── llms/ # llms.txt 파서
95
+ │ ├── mcp/ # MCP 서버 구현
96
+ │ └── search/ # 문서 검색 엔진
97
+ ├── tools/ # 배포 도구
98
+ │ ├── publish/ # 패키지 매니저 배포
99
+ │ └── shared/ # 공유 유틸리티
100
+ ├── scripts/ # 스크립트
101
+ ├── main.go # 메인 진입점
102
+ ├── go.mod # Go 모듈 정의
103
+ ├── Makefile # 빌드 명령어
104
+ └── package.json # npm 패키지 정의
105
+ ```
106
+
107
+ ## 개발
108
+
109
+ ### 빌드
110
+
111
+ ```bash
112
+ make build
113
+ # 또는
114
+ go build -o ax
115
+ ```
116
+
117
+ ### 테스트
118
+
119
+ ```bash
120
+ go test ./...
121
+ ```
122
+
123
+ ### 로컬 실행
124
+
125
+ ```bash
126
+ ./ax mcp
127
+ ```
128
+
129
+ ## AppsInToss란?
130
+
131
+ **AppsInToss**는 토스 앱 내에서 미니앱을 제공할 수 있는 플랫폼입니다. 3,000만 토스 사용자에게 서비스를 노출하고, SDK와 API를 활용해 빠르게 개발할 수 있습니다.
132
+
133
+ ### 주요 개념
134
+
135
+ | 용어 | 설명 |
136
+ |------|------|
137
+ | **Granite** | 미니앱 개발 프레임워크 (구 Bedrock, v1.0+) |
138
+ | **TDS** | Toss Design System - 비게임 미니앱 필수 |
139
+
140
+ ### 개발 방식
141
+
142
+ - **React Native**: 네이티브에 가까운 성능
143
+ - **WebView**: 웹 기술 활용
144
+
145
+ ## 관련 링크
146
+
147
+ - [AppsInToss Developer Center](https://developers-apps-in-toss.toss.im)
148
+ - [TDS React Native](https://tossmini-docs.toss.im/tds-react-native)
149
+ - [TDS Web](https://tossmini-docs.toss.im/tds-mobile)
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@apps-in-toss/ax",
3
+ "version": "0.7.1",
4
+ "description": "MCP server that provides AppsInToss documentation to AI assistants",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=22.9.0",
8
+ "npm": ">=8"
9
+ },
10
+ "repository": "toss/apps-in-toss-ax",
11
+ "homepage": "https://github.com/toss/apps-in-toss-ax#readme",
12
+ "bugs": "https://github.com/toss/apps-in-toss-ax/issues",
13
+ "scripts": {
14
+ "prepack": "node -e \"require('fs').rmSync('bin', { recursive: true, force: true })\"",
15
+ "postinstall": "node scripts/postinstall.js"
16
+ },
17
+ "dependencies": {
18
+ "bin-links": "^6.0.0",
19
+ "https-proxy-agent": "^7.0.2",
20
+ "node-fetch": "^3.3.2",
21
+ "tar": "7.5.16"
22
+ },
23
+ "bin": {
24
+ "ax": "bin/ax"
25
+ },
26
+ "files": [
27
+ "scripts/postinstall.js"
28
+ ],
29
+ "os": [
30
+ "darwin",
31
+ "linux",
32
+ "win32"
33
+ ],
34
+ "cpu": [
35
+ "x64",
36
+ "arm64"
37
+ ],
38
+ "keywords": [
39
+ "apps-in-toss",
40
+ "toss",
41
+ "mcp",
42
+ "mcp-server",
43
+ "mini-app",
44
+ "cli"
45
+ ],
46
+ "author": "Viva Republica, Inc.",
47
+ "license": "ISC",
48
+ "publishConfig": {
49
+ "access": "public"
50
+ }
51
+ }
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Ref 1: https://github.com/sanathkr/go-npm
4
+ // Ref 2: https://medium.com/xendit-engineering/how-we-repurposed-npm-to-publish-and-distribute-our-go-binaries-for-internal-cli-23981b80911b
5
+ "use strict";
6
+
7
+ import binLinks from "bin-links";
8
+ import { createHash } from "crypto";
9
+ import fs from "fs";
10
+ import fetch from "node-fetch";
11
+ import { Agent } from "https";
12
+ import { HttpsProxyAgent } from "https-proxy-agent";
13
+ import path from "path";
14
+ import { extract } from "tar";
15
+ import zlib from "zlib";
16
+
17
+ // Mapping from Node's `process.arch` to Golang's `$GOARCH`
18
+ const ARCH_MAPPING = {
19
+ x64: "amd64",
20
+ arm64: "arm64",
21
+ };
22
+
23
+ // Mapping between Node's `process.platform` to Golang's
24
+ const PLATFORM_MAPPING = {
25
+ darwin: "darwin",
26
+ linux: "linux",
27
+ win32: "windows",
28
+ };
29
+
30
+ const arch = ARCH_MAPPING[process.arch];
31
+ const platform = PLATFORM_MAPPING[process.platform];
32
+
33
+ // TODO: import pkg from "../package.json" assert { type: "json" };
34
+ const readPackageJson = async () => {
35
+ const contents = await fs.promises.readFile("package.json");
36
+ return JSON.parse(contents);
37
+ };
38
+
39
+ // Release artifacts are named after the binary (GoReleaser project name),
40
+ // not the scoped npm package name.
41
+ const getBinaryName = (packageJson) => Object.keys(packageJson.bin)[0];
42
+
43
+ // Build the download url from package.json
44
+ const getDownloadUrl = (packageJson) => {
45
+ const binName = getBinaryName(packageJson);
46
+ const version = packageJson.version;
47
+ const repo = packageJson.repository;
48
+ const url = `https://github.com/${repo}/releases/download/v${version}/${binName}_${platform}_${arch}.tar.gz`;
49
+ return url;
50
+ };
51
+
52
+ const fetchAndParseCheckSumFile = async (packageJson, agent) => {
53
+ const version = packageJson.version;
54
+ const binName = getBinaryName(packageJson);
55
+ const repo = packageJson.repository;
56
+ const checksumFileUrl = `https://github.com/${repo}/releases/download/v${version}/${binName}_${version}_checksums.txt`;
57
+
58
+ // Fetch the checksum file
59
+ console.info("Downloading", checksumFileUrl);
60
+ const response = await fetch(checksumFileUrl, { agent });
61
+ if (response.ok) {
62
+ const checkSumContent = await response.text();
63
+ const lines = checkSumContent.split("\n");
64
+
65
+ const checksums = {};
66
+ for (const line of lines) {
67
+ const [checksum, packageName] = line.split(/\s+/);
68
+ checksums[packageName] = checksum;
69
+ }
70
+
71
+ return checksums;
72
+ } else {
73
+ console.error(
74
+ "Could not fetch checksum file",
75
+ response.status,
76
+ response.statusText
77
+ );
78
+ }
79
+ };
80
+
81
+ const errChecksum = "Checksum mismatch. Downloaded data might be corrupted.";
82
+ const errUnsupported = `Installation is not supported for ${process.platform} ${process.arch}`;
83
+
84
+ /**
85
+ * Reads the configuration from application's package.json,
86
+ * downloads the binary from package url and stores at
87
+ * ./bin in the package's root.
88
+ *
89
+ * See: https://docs.npmjs.com/files/package.json#bin
90
+ */
91
+ async function main() {
92
+ if (!arch || !platform) {
93
+ throw errUnsupported;
94
+ }
95
+
96
+ // Read from package.json and prepare for the installation.
97
+ const pkg = await readPackageJson();
98
+ const binCmd = getBinaryName(pkg);
99
+ if (platform === "windows") {
100
+ // Update bin path in package.json
101
+ pkg.bin[binCmd] += ".exe";
102
+ }
103
+
104
+ // Prepare the installation path by creating the directory if it doesn't exist.
105
+ const binPath = pkg.bin[binCmd];
106
+ const binDir = path.dirname(binPath);
107
+ await fs.promises.mkdir(binDir, { recursive: true });
108
+
109
+ // Create the agent that will be used for all the fetch requests later.
110
+ const proxyUrl =
111
+ process.env.npm_config_https_proxy ||
112
+ process.env.npm_config_http_proxy ||
113
+ process.env.npm_config_proxy;
114
+ // Keeps the TCP connection alive when sending multiple requests
115
+ // Ref: https://github.com/node-fetch/node-fetch/issues/1735
116
+ const agent = proxyUrl
117
+ ? new HttpsProxyAgent(proxyUrl, { keepAlive: true })
118
+ : new Agent({ keepAlive: true });
119
+
120
+ // First, fetch the checksum map.
121
+ const checksumMap = await fetchAndParseCheckSumFile(pkg, agent);
122
+
123
+ // Then, download the binary.
124
+ const url = getDownloadUrl(pkg);
125
+ console.info("Downloading", url);
126
+ const resp = await fetch(url, { agent });
127
+ if (!resp.ok) {
128
+ throw `Failed to download ${url}: ${resp.status} ${resp.statusText}`;
129
+ }
130
+ const hash = createHash("sha256");
131
+ const pkgNameWithPlatform = `${binCmd}_${platform}_${arch}.tar.gz`;
132
+
133
+ // Then, decompress the binary -- we will first Un-GZip, then we will untar.
134
+ const ungz = zlib.createGunzip();
135
+ const binName = path.basename(binPath);
136
+ const untar = extract({ cwd: binDir }, [binName]);
137
+
138
+ // Update the hash with the binary data as it's being downloaded.
139
+ resp.body
140
+ .on("data", (chunk) => {
141
+ hash.update(chunk);
142
+ })
143
+ // Pipe the data to the ungz stream.
144
+ .pipe(ungz);
145
+
146
+ // After the ungz stream has ended, verify the checksum.
147
+ ungz
148
+ .on("end", () => {
149
+ const expectedChecksum = checksumMap?.[pkgNameWithPlatform];
150
+ // Skip verification if we can't find the file checksum
151
+ if (!expectedChecksum) {
152
+ console.warn("Skipping checksum verification");
153
+ return;
154
+ }
155
+ const calculatedChecksum = hash.digest("hex");
156
+ if (calculatedChecksum !== expectedChecksum) {
157
+ throw errChecksum;
158
+ }
159
+ console.info("Checksum verified.");
160
+ })
161
+ // Pipe the data to the untar stream.
162
+ .pipe(untar);
163
+
164
+ // Wait for the untar stream to finish.
165
+ await new Promise((resolve, reject) => {
166
+ untar.on("error", reject);
167
+ untar.on("end", () => resolve());
168
+ });
169
+
170
+ // npm links bins before postinstall runs and silently skips the
171
+ // then-missing binary, so re-link here. global/top make the link land in
172
+ // the global bin dir on `npm install -g`.
173
+ const isGlobalInstall = process.env.npm_config_global === "true";
174
+ await binLinks({
175
+ path: path.resolve("."),
176
+ pkg: { ...pkg, bin: { [binCmd]: binPath } },
177
+ global: isGlobalInstall,
178
+ top: isGlobalInstall,
179
+ force: true,
180
+ });
181
+
182
+ console.info("Installed AppsInToss CLI successfully");
183
+ }
184
+
185
+ await main();