@sazuapp/mcp-server 0.1.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 +78 -0
- package/dist/client.d.ts +23 -0
- package/dist/client.js +57 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +120 -0
- package/dist/tools.d.ts +106 -0
- package/dist/tools.js +81 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# @sazuapp/mcp-server
|
|
2
|
+
|
|
3
|
+
[SAZU API](https://sazu.app/manse-api) 의 **MCP (Model Context Protocol)** 서버.
|
|
4
|
+
AI 에이전트 (Claude Code, Cursor, Windsurf 등) 가 사주·만세력 API 를 직접 호출합니다.
|
|
5
|
+
|
|
6
|
+
## 누가 사용하나
|
|
7
|
+
|
|
8
|
+
**Pro 플랜 구독자만**. Free 사용자는 [docs 페이지](https://sazu.app/manse-api/docs) 의 curl 예시를 사용하세요.
|
|
9
|
+
|
|
10
|
+
## 설치
|
|
11
|
+
|
|
12
|
+
### Claude Code
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
claude mcp add sazu --env SAZU_API_KEY=sazu_pro_xxx -- npx -y @sazuapp/mcp-server
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Cursor / Windsurf
|
|
19
|
+
|
|
20
|
+
`~/.cursor/mcp.json` 또는 `.windsurf/mcp.json`:
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"mcpServers": {
|
|
25
|
+
"sazu": {
|
|
26
|
+
"command": "npx",
|
|
27
|
+
"args": ["-y", "@sazuapp/mcp-server"],
|
|
28
|
+
"env": {
|
|
29
|
+
"SAZU_API_KEY": "sazu_pro_xxx"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## API 키 발급
|
|
37
|
+
|
|
38
|
+
[https://sazu.app/manse-api/dashboard/keys](https://sazu.app/manse-api/dashboard/keys) → Pro 키 발급 → `SAZU_API_KEY` 환경변수에 설정.
|
|
39
|
+
|
|
40
|
+
## 노출 도구
|
|
41
|
+
|
|
42
|
+
| 도구 | 설명 |
|
|
43
|
+
|---|---|
|
|
44
|
+
| `sazu_calculate` | 사주팔자 + 14개 명리 분석 (오행·합형충파해·격국·용신·12운성·신살·허자·대운 등) |
|
|
45
|
+
| `sazu_calendar_convert` | 양력 ↔ 음력 변환 (200년 만세력) |
|
|
46
|
+
|
|
47
|
+
## 사용 예시
|
|
48
|
+
|
|
49
|
+
AI 에이전트에게 자연어로:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
"1985년 3월 15일 14시 출생 남자의 사주를 분석해줘"
|
|
53
|
+
"1990년 음력 4월 8일을 양력으로 변환해줘"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
→ AI 가 `sazu_calculate` / `sazu_calendar_convert` 도구를 자동 호출.
|
|
57
|
+
|
|
58
|
+
## 환경변수
|
|
59
|
+
|
|
60
|
+
| 변수 | 기본값 | 설명 |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `SAZU_API_KEY` | (필수) | Pro 플랜 API 키 |
|
|
63
|
+
| `SAZU_API_BASE` | `https://api.sazu.app` | API 베이스 URL (커스텀 환경용) |
|
|
64
|
+
|
|
65
|
+
## 보안
|
|
66
|
+
|
|
67
|
+
- 시작 시 `/v1/me` 호출로 tier 검증 → Pro 가 아니면 즉시 종료
|
|
68
|
+
- 모든 요청에 `X-Client-Type: mcp` 헤더 부착 (서버 측 통계 분리)
|
|
69
|
+
- 응답에 `X-Response-Id` watermark — 유출 시 추적 가능
|
|
70
|
+
|
|
71
|
+
## 제한
|
|
72
|
+
|
|
73
|
+
- 운세 텍스트 생성 기능은 노출하지 않습니다 (별도 라이선스 필요)
|
|
74
|
+
- 인증·결제 엔드포인트는 노출하지 않습니다
|
|
75
|
+
|
|
76
|
+
## 문의
|
|
77
|
+
|
|
78
|
+
contact@sazu.app
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SAZU API HTTP 클라이언트 (MCP server 내부 사용).
|
|
3
|
+
*
|
|
4
|
+
* - 환경변수 SAZU_API_KEY 필수
|
|
5
|
+
* - 환경변수 SAZU_API_BASE 선택 (기본 https://api.sazu.app)
|
|
6
|
+
* - 모든 요청에 X-Client-Type: mcp 헤더 부착 → 서버 측 통계 분리·추후 정책 적용 근거
|
|
7
|
+
*/
|
|
8
|
+
export declare class SazuApiError extends Error {
|
|
9
|
+
status: number;
|
|
10
|
+
code: string | undefined;
|
|
11
|
+
constructor(status: number, code: string | undefined, message: string);
|
|
12
|
+
}
|
|
13
|
+
export declare function callApi<T = unknown>(path: string, init?: RequestInit): Promise<T>;
|
|
14
|
+
export interface MeResponse {
|
|
15
|
+
success: boolean;
|
|
16
|
+
data: {
|
|
17
|
+
tier: 'free' | 'pro' | 'event';
|
|
18
|
+
keyPrefix: string;
|
|
19
|
+
rateLimitPerMinute?: number;
|
|
20
|
+
monthlyQuota?: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export declare function fetchMe(): Promise<MeResponse>;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SAZU API HTTP 클라이언트 (MCP server 내부 사용).
|
|
3
|
+
*
|
|
4
|
+
* - 환경변수 SAZU_API_KEY 필수
|
|
5
|
+
* - 환경변수 SAZU_API_BASE 선택 (기본 https://api.sazu.app)
|
|
6
|
+
* - 모든 요청에 X-Client-Type: mcp 헤더 부착 → 서버 측 통계 분리·추후 정책 적용 근거
|
|
7
|
+
*/
|
|
8
|
+
const DEFAULT_BASE = 'https://api.sazu.app';
|
|
9
|
+
export class SazuApiError extends Error {
|
|
10
|
+
status;
|
|
11
|
+
code;
|
|
12
|
+
constructor(status, code, message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.status = status;
|
|
15
|
+
this.code = code;
|
|
16
|
+
this.name = 'SazuApiError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function getApiKey() {
|
|
20
|
+
const key = process.env.SAZU_API_KEY;
|
|
21
|
+
if (!key) {
|
|
22
|
+
throw new SazuApiError(0, 'NO_API_KEY', 'SAZU_API_KEY 환경변수가 필요합니다. https://sazu.app/manse-api/dashboard/keys 에서 Pro 키를 발급받으세요.');
|
|
23
|
+
}
|
|
24
|
+
return key;
|
|
25
|
+
}
|
|
26
|
+
function getBase() {
|
|
27
|
+
return process.env.SAZU_API_BASE?.replace(/\/$/, '') ?? DEFAULT_BASE;
|
|
28
|
+
}
|
|
29
|
+
export async function callApi(path, init = {}) {
|
|
30
|
+
const apiKey = getApiKey();
|
|
31
|
+
const url = `${getBase()}${path.startsWith('/') ? path : `/${path}`}`;
|
|
32
|
+
const res = await fetch(url, {
|
|
33
|
+
...init,
|
|
34
|
+
headers: {
|
|
35
|
+
'Content-Type': 'application/json',
|
|
36
|
+
'x-api-key': apiKey,
|
|
37
|
+
'X-Client-Type': 'mcp',
|
|
38
|
+
...(init.headers ?? {}),
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
const text = await res.text();
|
|
42
|
+
let body = null;
|
|
43
|
+
try {
|
|
44
|
+
body = text ? JSON.parse(text) : null;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
/* 비 JSON 응답 */
|
|
48
|
+
}
|
|
49
|
+
if (!res.ok) {
|
|
50
|
+
const errObj = body?.error;
|
|
51
|
+
throw new SazuApiError(res.status, errObj?.code, errObj?.message ?? `HTTP ${res.status}: ${text.slice(0, 200)}`);
|
|
52
|
+
}
|
|
53
|
+
return body;
|
|
54
|
+
}
|
|
55
|
+
export async function fetchMe() {
|
|
56
|
+
return callApi('/v1/me');
|
|
57
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* SAZU MCP server — stdio transport
|
|
4
|
+
*
|
|
5
|
+
* AI 에이전트 (Claude Code / Cursor / Windsurf 등) 가 사주·만세력 API 를 직접 호출.
|
|
6
|
+
*
|
|
7
|
+
* 보안 정책:
|
|
8
|
+
* - SAZU_API_KEY 환경변수 필수
|
|
9
|
+
* - 시작 시 /v1/me 호출로 tier 확인 → Pro 가 아니면 즉시 종료
|
|
10
|
+
* - Free 사용자는 https://sazu.app/manse-api/docs 의 curl 예시 활용
|
|
11
|
+
*
|
|
12
|
+
* 노출 도구:
|
|
13
|
+
* - sazu_calculate : 사주팔자 + 14개 명리 분석
|
|
14
|
+
* - sazu_calendar_convert : 양/음력 변환
|
|
15
|
+
*
|
|
16
|
+
* 비노출 (영구):
|
|
17
|
+
* - 운세 텍스트 생성 (Fortune Complete)
|
|
18
|
+
* - 인증 엔드포인트 (/auth/*)
|
|
19
|
+
* - 결제 엔드포인트
|
|
20
|
+
*
|
|
21
|
+
* 설치:
|
|
22
|
+
* claude mcp add sazu npx -y @sazuapp/mcp-server
|
|
23
|
+
*
|
|
24
|
+
* Cursor / Windsurf:
|
|
25
|
+
* 설정 파일에 동일 명령 등록
|
|
26
|
+
*/
|
|
27
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* SAZU MCP server — stdio transport
|
|
4
|
+
*
|
|
5
|
+
* AI 에이전트 (Claude Code / Cursor / Windsurf 등) 가 사주·만세력 API 를 직접 호출.
|
|
6
|
+
*
|
|
7
|
+
* 보안 정책:
|
|
8
|
+
* - SAZU_API_KEY 환경변수 필수
|
|
9
|
+
* - 시작 시 /v1/me 호출로 tier 확인 → Pro 가 아니면 즉시 종료
|
|
10
|
+
* - Free 사용자는 https://sazu.app/manse-api/docs 의 curl 예시 활용
|
|
11
|
+
*
|
|
12
|
+
* 노출 도구:
|
|
13
|
+
* - sazu_calculate : 사주팔자 + 14개 명리 분석
|
|
14
|
+
* - sazu_calendar_convert : 양/음력 변환
|
|
15
|
+
*
|
|
16
|
+
* 비노출 (영구):
|
|
17
|
+
* - 운세 텍스트 생성 (Fortune Complete)
|
|
18
|
+
* - 인증 엔드포인트 (/auth/*)
|
|
19
|
+
* - 결제 엔드포인트
|
|
20
|
+
*
|
|
21
|
+
* 설치:
|
|
22
|
+
* claude mcp add sazu npx -y @sazuapp/mcp-server
|
|
23
|
+
*
|
|
24
|
+
* Cursor / Windsurf:
|
|
25
|
+
* 설정 파일에 동일 명령 등록
|
|
26
|
+
*/
|
|
27
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
28
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
29
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
30
|
+
import { fetchMe, SazuApiError } from './client.js';
|
|
31
|
+
import { TOOL_DEFINITIONS, executeSazuCalculate, executeCalendarConvert, } from './tools.js';
|
|
32
|
+
const SERVER_NAME = '@sazuapp/mcp-server';
|
|
33
|
+
const SERVER_VERSION = '0.1.0';
|
|
34
|
+
async function main() {
|
|
35
|
+
// ── 1) Pro 자격 사전 검증 ──
|
|
36
|
+
// 정책: tier === 'pro' 만 허용. event(이벤트·관리자 부여) 도 MCP 미허용 — 결제 기반 Pro 만.
|
|
37
|
+
let tier = 'free';
|
|
38
|
+
try {
|
|
39
|
+
const me = await fetchMe();
|
|
40
|
+
tier = me.data.tier;
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
if (err instanceof SazuApiError) {
|
|
44
|
+
console.error(`[sazu-mcp] 인증 실패 (${err.status} ${err.code ?? ''}): ${err.message}`);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
console.error(`[sazu-mcp] /v1/me 호출 실패: ${err instanceof Error ? err.message : String(err)}`);
|
|
48
|
+
}
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
if (tier !== 'pro') {
|
|
52
|
+
console.error('[sazu-mcp] Pro 플랜 전용 기능입니다. 현재 등급: ' +
|
|
53
|
+
tier +
|
|
54
|
+
'\n→ Pro 업그레이드: https://sazu.app/manse-api/pricing' +
|
|
55
|
+
'\n→ Free 사용자는 https://sazu.app/manse-api/docs 의 curl 예시 활용');
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
// ── 2) MCP server 초기화 ──
|
|
59
|
+
const server = new Server({
|
|
60
|
+
name: SERVER_NAME,
|
|
61
|
+
version: SERVER_VERSION,
|
|
62
|
+
}, {
|
|
63
|
+
capabilities: {
|
|
64
|
+
tools: {},
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
// ── 3) 도구 목록 ──
|
|
68
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
69
|
+
tools: TOOL_DEFINITIONS.map((t) => ({
|
|
70
|
+
name: t.name,
|
|
71
|
+
description: t.description,
|
|
72
|
+
inputSchema: t.inputSchema,
|
|
73
|
+
})),
|
|
74
|
+
}));
|
|
75
|
+
// ── 4) 도구 실행 ──
|
|
76
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
77
|
+
const { name, arguments: args } = req.params;
|
|
78
|
+
try {
|
|
79
|
+
let result;
|
|
80
|
+
if (name === 'sazu_calculate') {
|
|
81
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
82
|
+
result = await executeSazuCalculate(args);
|
|
83
|
+
}
|
|
84
|
+
else if (name === 'sazu_calendar_convert') {
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
86
|
+
result = await executeCalendarConvert(args);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
return {
|
|
90
|
+
isError: true,
|
|
91
|
+
content: [{ type: 'text', text: `알 수 없는 도구: ${name}` }],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
content: [
|
|
96
|
+
{ type: 'text', text: JSON.stringify(result, null, 2) },
|
|
97
|
+
],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
const message = err instanceof SazuApiError
|
|
102
|
+
? `[${err.status} ${err.code ?? 'ERR'}] ${err.message}`
|
|
103
|
+
: err instanceof Error
|
|
104
|
+
? err.message
|
|
105
|
+
: String(err);
|
|
106
|
+
return {
|
|
107
|
+
isError: true,
|
|
108
|
+
content: [{ type: 'text', text: `호출 실패: ${message}` }],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
// ── 5) stdio transport 연결 ──
|
|
113
|
+
const transport = new StdioServerTransport();
|
|
114
|
+
await server.connect(transport);
|
|
115
|
+
console.error(`[sazu-mcp] ready (tier=${tier}, tools=${TOOL_DEFINITIONS.length}). docs: https://sazu.app/manse-api/docs`);
|
|
116
|
+
}
|
|
117
|
+
main().catch((err) => {
|
|
118
|
+
console.error('[sazu-mcp] fatal:', err);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
});
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP 도구 정의 — SAZU API 의 데이터 엔드포인트만 노출.
|
|
3
|
+
*
|
|
4
|
+
* 비공개 (절대 노출 금지):
|
|
5
|
+
* - 운세 텍스트 (Fortune Complete) → LLM 출력 자산이므로 MCP 미노출
|
|
6
|
+
* - 인증 엔드포인트 (/auth/*) → MCP 와 무관
|
|
7
|
+
*
|
|
8
|
+
* 공개:
|
|
9
|
+
* - sazu_calculate : 사주팔자 + 14개 모듈 분석
|
|
10
|
+
* - sazu_calendar_convert : 양력 ↔ 음력 변환
|
|
11
|
+
*/
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
export declare const SazuCalculateInputSchema: z.ZodObject<{
|
|
14
|
+
birthDate: z.ZodString;
|
|
15
|
+
birthTime: z.ZodOptional<z.ZodString>;
|
|
16
|
+
gender: z.ZodEnum<{
|
|
17
|
+
male: "male";
|
|
18
|
+
female: "female";
|
|
19
|
+
}>;
|
|
20
|
+
calendar: z.ZodDefault<z.ZodEnum<{
|
|
21
|
+
solar: "solar";
|
|
22
|
+
lunar: "lunar";
|
|
23
|
+
}>>;
|
|
24
|
+
isLeapMonth: z.ZodOptional<z.ZodBoolean>;
|
|
25
|
+
location: z.ZodOptional<z.ZodString>;
|
|
26
|
+
}, z.core.$strip>;
|
|
27
|
+
export type SazuCalculateInput = z.infer<typeof SazuCalculateInputSchema>;
|
|
28
|
+
export declare const CalendarConvertInputSchema: z.ZodObject<{
|
|
29
|
+
date: z.ZodString;
|
|
30
|
+
from: z.ZodEnum<{
|
|
31
|
+
solar: "solar";
|
|
32
|
+
lunar: "lunar";
|
|
33
|
+
}>;
|
|
34
|
+
to: z.ZodEnum<{
|
|
35
|
+
solar: "solar";
|
|
36
|
+
lunar: "lunar";
|
|
37
|
+
}>;
|
|
38
|
+
isLeapMonth: z.ZodOptional<z.ZodBoolean>;
|
|
39
|
+
}, z.core.$strip>;
|
|
40
|
+
export type CalendarConvertInput = z.infer<typeof CalendarConvertInputSchema>;
|
|
41
|
+
export declare function executeSazuCalculate(input: SazuCalculateInput): Promise<unknown>;
|
|
42
|
+
export declare function executeCalendarConvert(input: CalendarConvertInput): Promise<unknown>;
|
|
43
|
+
export declare const TOOL_DEFINITIONS: readonly [{
|
|
44
|
+
readonly name: "sazu_calculate";
|
|
45
|
+
readonly description: "생년월일·시간·성별을 입력하면 사주팔자(연·월·일·시주) + 14개 명리 분석 모듈(오행·합형충파해·격국·용신·12운성·신살·허자·대운 등) 을 반환합니다. 한국 명리학 정밀 계산.";
|
|
46
|
+
readonly inputSchema: {
|
|
47
|
+
readonly type: "object";
|
|
48
|
+
readonly properties: {
|
|
49
|
+
readonly birthDate: {
|
|
50
|
+
readonly type: "string";
|
|
51
|
+
readonly description: "생년월일 (YYYY-MM-DD)";
|
|
52
|
+
};
|
|
53
|
+
readonly birthTime: {
|
|
54
|
+
readonly type: "string";
|
|
55
|
+
readonly description: "생시 (HH:MM, 24시간제). 시간 미상이면 생략.";
|
|
56
|
+
};
|
|
57
|
+
readonly gender: {
|
|
58
|
+
readonly type: "string";
|
|
59
|
+
readonly enum: readonly ["male", "female"];
|
|
60
|
+
readonly description: "성별";
|
|
61
|
+
};
|
|
62
|
+
readonly calendar: {
|
|
63
|
+
readonly type: "string";
|
|
64
|
+
readonly enum: readonly ["solar", "lunar"];
|
|
65
|
+
readonly description: "양/음력 (기본 solar)";
|
|
66
|
+
readonly default: "solar";
|
|
67
|
+
};
|
|
68
|
+
readonly isLeapMonth: {
|
|
69
|
+
readonly type: "boolean";
|
|
70
|
+
readonly description: "윤달 여부 (lunar 일 때만)";
|
|
71
|
+
};
|
|
72
|
+
readonly location: {
|
|
73
|
+
readonly type: "string";
|
|
74
|
+
readonly description: "출생 도시 (선택, 진태양시 보정)";
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
readonly required: readonly ["birthDate", "gender"];
|
|
78
|
+
};
|
|
79
|
+
}, {
|
|
80
|
+
readonly name: "sazu_calendar_convert";
|
|
81
|
+
readonly description: "양력 ↔ 음력 변환. 200년 범위 만세력 기반. 윤달 처리 포함.";
|
|
82
|
+
readonly inputSchema: {
|
|
83
|
+
readonly type: "object";
|
|
84
|
+
readonly properties: {
|
|
85
|
+
readonly date: {
|
|
86
|
+
readonly type: "string";
|
|
87
|
+
readonly description: "변환할 날짜 (YYYY-MM-DD)";
|
|
88
|
+
};
|
|
89
|
+
readonly from: {
|
|
90
|
+
readonly type: "string";
|
|
91
|
+
readonly enum: readonly ["solar", "lunar"];
|
|
92
|
+
readonly description: "입력 달력";
|
|
93
|
+
};
|
|
94
|
+
readonly to: {
|
|
95
|
+
readonly type: "string";
|
|
96
|
+
readonly enum: readonly ["solar", "lunar"];
|
|
97
|
+
readonly description: "출력 달력";
|
|
98
|
+
};
|
|
99
|
+
readonly isLeapMonth: {
|
|
100
|
+
readonly type: "boolean";
|
|
101
|
+
readonly description: "입력이 음력이고 윤달인 경우 true";
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
readonly required: readonly ["date", "from", "to"];
|
|
105
|
+
};
|
|
106
|
+
}];
|
package/dist/tools.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP 도구 정의 — SAZU API 의 데이터 엔드포인트만 노출.
|
|
3
|
+
*
|
|
4
|
+
* 비공개 (절대 노출 금지):
|
|
5
|
+
* - 운세 텍스트 (Fortune Complete) → LLM 출력 자산이므로 MCP 미노출
|
|
6
|
+
* - 인증 엔드포인트 (/auth/*) → MCP 와 무관
|
|
7
|
+
*
|
|
8
|
+
* 공개:
|
|
9
|
+
* - sazu_calculate : 사주팔자 + 14개 모듈 분석
|
|
10
|
+
* - sazu_calendar_convert : 양력 ↔ 음력 변환
|
|
11
|
+
*/
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
import { callApi } from './client.js';
|
|
14
|
+
// ── 입력 스키마 (AI 가 파싱하기 쉬운 한국어 description) ──
|
|
15
|
+
export const SazuCalculateInputSchema = z.object({
|
|
16
|
+
birthDate: z
|
|
17
|
+
.string()
|
|
18
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/, 'YYYY-MM-DD 형식'),
|
|
19
|
+
birthTime: z
|
|
20
|
+
.string()
|
|
21
|
+
.regex(/^\d{2}:\d{2}$/, 'HH:MM (24시간제)')
|
|
22
|
+
.optional(),
|
|
23
|
+
gender: z.enum(['male', 'female']),
|
|
24
|
+
calendar: z.enum(['solar', 'lunar']).default('solar'),
|
|
25
|
+
isLeapMonth: z.boolean().optional(),
|
|
26
|
+
location: z.string().optional(),
|
|
27
|
+
});
|
|
28
|
+
export const CalendarConvertInputSchema = z.object({
|
|
29
|
+
date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, 'YYYY-MM-DD'),
|
|
30
|
+
from: z.enum(['solar', 'lunar']),
|
|
31
|
+
to: z.enum(['solar', 'lunar']),
|
|
32
|
+
isLeapMonth: z.boolean().optional(),
|
|
33
|
+
});
|
|
34
|
+
// ── 도구 실행 ──
|
|
35
|
+
export async function executeSazuCalculate(input) {
|
|
36
|
+
const parsed = SazuCalculateInputSchema.parse(input);
|
|
37
|
+
return callApi('/v1/sazu/calculate', {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
body: JSON.stringify(parsed),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
export async function executeCalendarConvert(input) {
|
|
43
|
+
const parsed = CalendarConvertInputSchema.parse(input);
|
|
44
|
+
return callApi('/v1/calendar/convert', {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
body: JSON.stringify(parsed),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
// ── 도구 메타데이터 (MCP server 등록용) ──
|
|
50
|
+
export const TOOL_DEFINITIONS = [
|
|
51
|
+
{
|
|
52
|
+
name: 'sazu_calculate',
|
|
53
|
+
description: '생년월일·시간·성별을 입력하면 사주팔자(연·월·일·시주) + 14개 명리 분석 모듈(오행·합형충파해·격국·용신·12운성·신살·허자·대운 등) 을 반환합니다. 한국 명리학 정밀 계산.',
|
|
54
|
+
inputSchema: {
|
|
55
|
+
type: 'object',
|
|
56
|
+
properties: {
|
|
57
|
+
birthDate: { type: 'string', description: '생년월일 (YYYY-MM-DD)' },
|
|
58
|
+
birthTime: { type: 'string', description: '생시 (HH:MM, 24시간제). 시간 미상이면 생략.' },
|
|
59
|
+
gender: { type: 'string', enum: ['male', 'female'], description: '성별' },
|
|
60
|
+
calendar: { type: 'string', enum: ['solar', 'lunar'], description: '양/음력 (기본 solar)', default: 'solar' },
|
|
61
|
+
isLeapMonth: { type: 'boolean', description: '윤달 여부 (lunar 일 때만)' },
|
|
62
|
+
location: { type: 'string', description: '출생 도시 (선택, 진태양시 보정)' },
|
|
63
|
+
},
|
|
64
|
+
required: ['birthDate', 'gender'],
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'sazu_calendar_convert',
|
|
69
|
+
description: '양력 ↔ 음력 변환. 200년 범위 만세력 기반. 윤달 처리 포함.',
|
|
70
|
+
inputSchema: {
|
|
71
|
+
type: 'object',
|
|
72
|
+
properties: {
|
|
73
|
+
date: { type: 'string', description: '변환할 날짜 (YYYY-MM-DD)' },
|
|
74
|
+
from: { type: 'string', enum: ['solar', 'lunar'], description: '입력 달력' },
|
|
75
|
+
to: { type: 'string', enum: ['solar', 'lunar'], description: '출력 달력' },
|
|
76
|
+
isLeapMonth: { type: 'boolean', description: '입력이 음력이고 윤달인 경우 true' },
|
|
77
|
+
},
|
|
78
|
+
required: ['date', 'from', 'to'],
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
];
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sazuapp/mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "SAZU API MCP server — AI agents (Claude Code/Cursor/Windsurf) 가 사주·만세력 API 를 직접 호출.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"sazu-mcp-server": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc -p tsconfig.json",
|
|
15
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
16
|
+
"start": "node dist/index.js",
|
|
17
|
+
"dev": "tsc -p tsconfig.json --watch"
|
|
18
|
+
},
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=20"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"mcp",
|
|
24
|
+
"model-context-protocol",
|
|
25
|
+
"saju",
|
|
26
|
+
"sazu",
|
|
27
|
+
"fortune",
|
|
28
|
+
"manse",
|
|
29
|
+
"korean-fortune"
|
|
30
|
+
],
|
|
31
|
+
"author": "Inavan",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"homepage": "https://sazu.app/manse-api",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/ainvention/sazu-app-manse-api.git",
|
|
37
|
+
"directory": "mcp-server"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@modelcontextprotocol/sdk": "^1.0.4"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"typescript": "^5.5.0",
|
|
44
|
+
"@types/node": "^20.11.0"
|
|
45
|
+
}
|
|
46
|
+
}
|