ragalgo-mcp-server 1.0.4
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/.dockerignore +4 -0
- package/.mcpregistry_github_token +1 -0
- package/.mcpregistry_registry_token +1 -0
- package/Dockerfile +23 -0
- package/README.md +73 -0
- package/android-chrome-192x192.png +0 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +440 -0
- package/dist/tools/chart.d.ts +83 -0
- package/dist/tools/chart.js +32 -0
- package/dist/tools/financials.d.ts +67 -0
- package/dist/tools/financials.js +21 -0
- package/dist/tools/news.d.ts +100 -0
- package/dist/tools/news.js +31 -0
- package/dist/tools/research.d.ts +34 -0
- package/dist/tools/research.js +18 -0
- package/dist/tools/snapshots.d.ts +48 -0
- package/dist/tools/snapshots.js +29 -0
- package/dist/tools/tags.d.ts +64 -0
- package/dist/tools/tags.js +27 -0
- package/dist/tools/trends.d.ts +33 -0
- package/dist/tools/trends.js +15 -0
- package/dist/utils/api.d.ts +6 -0
- package/dist/utils/api.js +59 -0
- package/mcp-publisher.exe +0 -0
- package/package.json +39 -0
- package/server.json +35 -0
- package/smithery.yaml +7 -0
- package/src/index.ts +477 -0
- package/src/tools/chart.ts +90 -0
- package/src/tools/financials.ts +67 -0
- package/src/tools/news.ts +67 -0
- package/src/tools/research.ts +40 -0
- package/src/tools/snapshots.ts +55 -0
- package/src/tools/tags.ts +61 -0
- package/src/tools/trends.ts +35 -0
- package/src/utils/api.ts +76 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 뉴스 관련 MCP Tools
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { callApi } from '../utils/api.js';
|
|
7
|
+
|
|
8
|
+
// 뉴스 파라미터 스키마
|
|
9
|
+
export const NewsParamsSchema = z.object({
|
|
10
|
+
tag: z.string().optional().describe('태그 코드 (search_tags 결과값, 예: STK005930, THM001)'),
|
|
11
|
+
source: z.string().optional().describe('소스 필터 (예: 한경, 매경)'),
|
|
12
|
+
search: z.string().optional().describe('제목 검색어'),
|
|
13
|
+
from_date: z.string().optional().describe('시작일 (YYYY-MM-DD)'),
|
|
14
|
+
to_date: z.string().optional().describe('종료일 (YYYY-MM-DD)'),
|
|
15
|
+
limit: z.number().min(1).max(100).default(20).describe('결과 수 (기본: 20, 최대: 100)'),
|
|
16
|
+
offset: z.number().min(0).default(0).describe('페이지네이션 오프셋'),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// 점수 포함 뉴스 파라미터 스키마
|
|
20
|
+
export const NewsScoredParamsSchema = NewsParamsSchema.extend({
|
|
21
|
+
min_score: z.number().min(-10).max(10).optional().describe('최소 감정 점수 (-10~10)'),
|
|
22
|
+
max_score: z.number().min(-10).max(10).optional().describe('최대 감정 점수 (-10~10)'),
|
|
23
|
+
verdict: z.enum(['bullish', 'bearish', 'neutral']).optional().describe('판정 필터'),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export type NewsParams = z.infer<typeof NewsParamsSchema>;
|
|
27
|
+
export type NewsScoredParams = z.infer<typeof NewsScoredParamsSchema>;
|
|
28
|
+
|
|
29
|
+
// 뉴스 조회 (점수 제외)
|
|
30
|
+
export async function getNews(params: NewsParams) {
|
|
31
|
+
const result = await callApi<{
|
|
32
|
+
success: boolean;
|
|
33
|
+
data: Array<{
|
|
34
|
+
id: number;
|
|
35
|
+
title: string;
|
|
36
|
+
summary: string;
|
|
37
|
+
tags: string[];
|
|
38
|
+
url: string;
|
|
39
|
+
source: string;
|
|
40
|
+
created_at: string;
|
|
41
|
+
}>;
|
|
42
|
+
meta: { count: number; tier: string };
|
|
43
|
+
}>('news', params);
|
|
44
|
+
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 뉴스 조회 (점수 포함)
|
|
49
|
+
export async function getNewsScored(params: NewsScoredParams) {
|
|
50
|
+
const result = await callApi<{
|
|
51
|
+
success: boolean;
|
|
52
|
+
data: Array<{
|
|
53
|
+
id: number;
|
|
54
|
+
title: string;
|
|
55
|
+
summary: string;
|
|
56
|
+
tags: string[];
|
|
57
|
+
sentiment_score: number;
|
|
58
|
+
verdict: string;
|
|
59
|
+
url: string;
|
|
60
|
+
source: string;
|
|
61
|
+
created_at: string;
|
|
62
|
+
}>;
|
|
63
|
+
meta: { count: number; tier: string };
|
|
64
|
+
}>('news-scored', params);
|
|
65
|
+
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 컨설팅 보고서 관련 MCP Tools
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { callApi } from '../utils/api.js';
|
|
7
|
+
|
|
8
|
+
const API_BASE = 'https://ragalgo-research-production.up.railway.app'; // 혹시 몰라 남겨룸, 하지만 callApi 사용시 Supabase Edge Function 호출
|
|
9
|
+
|
|
10
|
+
// 보고서 조회 파라미터 스키마
|
|
11
|
+
export const ResearchParamsSchema = z.object({
|
|
12
|
+
tag_code: z.string().describe('태그 코드 (필수). search_tags로 먼저 조회하세요.'),
|
|
13
|
+
limit: z.number().min(1).max(10).default(5).describe('보고서 수 (기본 5, 최대 10)'),
|
|
14
|
+
source: z.string().optional().describe('출처 필터 (선택): mckinsey, goldman, bcg, lg_research 등'),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export type ResearchParams = z.infer<typeof ResearchParamsSchema>;
|
|
18
|
+
|
|
19
|
+
// 보고서 조회
|
|
20
|
+
export async function getResearch(params: ResearchParams) {
|
|
21
|
+
// Supabase Edge Function 호출
|
|
22
|
+
const result = await callApi<{
|
|
23
|
+
tag_code: string;
|
|
24
|
+
tag_name: string | null;
|
|
25
|
+
research_count: number;
|
|
26
|
+
research: Array<{
|
|
27
|
+
id: number;
|
|
28
|
+
title: string;
|
|
29
|
+
source: string;
|
|
30
|
+
content_type: string;
|
|
31
|
+
published_at: string | null;
|
|
32
|
+
chunks: Array<{
|
|
33
|
+
section: string;
|
|
34
|
+
content: string;
|
|
35
|
+
}>;
|
|
36
|
+
}>;
|
|
37
|
+
}>('research', params); // 'research' function 호출
|
|
38
|
+
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 스냅샷 관련 MCP Tools
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { callApi } from '../utils/api.js';
|
|
7
|
+
|
|
8
|
+
// 스냅샷 파라미터 스키마
|
|
9
|
+
export const SnapshotsParamsSchema = z.object({
|
|
10
|
+
tag_code: z.string().optional().describe('태그 코드 (예: STK005930)'),
|
|
11
|
+
date: z.string().optional().describe('날짜 (YYYY-MM-DD)'),
|
|
12
|
+
days: z.number().min(1).max(30).default(7).describe('최근 N일 (기본: 7)'),
|
|
13
|
+
limit: z.number().min(1).max(100).default(50).describe('결과 수'),
|
|
14
|
+
offset: z.number().min(0).default(0).describe('페이지네이션 오프셋'),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export type SnapshotsParams = z.infer<typeof SnapshotsParamsSchema>;
|
|
18
|
+
|
|
19
|
+
// 스냅샷 조회
|
|
20
|
+
// [IMPORTANT] Snapshots are generated daily at 17:00 KST (market close).
|
|
21
|
+
// If you request 'today' and get no results (because it's morning in KST),
|
|
22
|
+
// you MUST:
|
|
23
|
+
// 1. Fetch 'yesterday's snapshot for context.
|
|
24
|
+
// 2. Call 'get_news_scored' to get REAL-TIME news for the current day.
|
|
25
|
+
export async function getSnapshots(params: SnapshotsParams) {
|
|
26
|
+
const endpoint = 'snapshots';
|
|
27
|
+
const { tag_code, ...queryParams } = params;
|
|
28
|
+
// 태그 코드가 있으면 쿼리 파라미터에 추가
|
|
29
|
+
if (tag_code) {
|
|
30
|
+
(queryParams as any).tag_code = tag_code;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const result = await callApi<{
|
|
34
|
+
success: boolean;
|
|
35
|
+
data: Array<{
|
|
36
|
+
tag_code: string;
|
|
37
|
+
snapshot_date: string;
|
|
38
|
+
news_count: number;
|
|
39
|
+
news_avg_score: number;
|
|
40
|
+
news_max_score: number;
|
|
41
|
+
news_min_score: number;
|
|
42
|
+
news_bullish_count: number;
|
|
43
|
+
news_bearish_count: number;
|
|
44
|
+
news_neutral_count: number;
|
|
45
|
+
research_count?: number; // Added
|
|
46
|
+
research_outlook?: string; // Added
|
|
47
|
+
chart_score: number;
|
|
48
|
+
chart_zone: string;
|
|
49
|
+
last_price: number;
|
|
50
|
+
}>;
|
|
51
|
+
meta: { count: number; date?: string; days?: number };
|
|
52
|
+
}>(endpoint, queryParams);
|
|
53
|
+
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 태그 관련 MCP Tools
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { callApi, callApiPost } from '../utils/api.js';
|
|
7
|
+
|
|
8
|
+
// 태그 검색 파라미터 스키마
|
|
9
|
+
export const TagsSearchParamsSchema = z.object({
|
|
10
|
+
q: z.string().describe('검색어 (예: 삼성, 반도체)'),
|
|
11
|
+
type: z.enum(['STOCK', 'SECTOR', 'THEME', 'CRYPTO']).optional().describe('태그 타입'),
|
|
12
|
+
limit: z.number().min(1).max(50).default(20).describe('결과 수'),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// 태그 매칭 파라미터 스키마
|
|
16
|
+
export const TagsMatchParamsSchema = z.object({
|
|
17
|
+
text: z.string().describe('매칭할 텍스트 (예: 삼성전자 HBM 대박)'),
|
|
18
|
+
types: z.array(z.enum(['STOCK', 'SECTOR', 'THEME', 'CRYPTO'])).optional().describe('태그 타입 필터'),
|
|
19
|
+
limit: z.number().min(1).max(20).default(10).describe('결과 수'),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export type TagsSearchParams = z.infer<typeof TagsSearchParamsSchema>;
|
|
23
|
+
export type TagsMatchParams = z.infer<typeof TagsMatchParamsSchema>;
|
|
24
|
+
|
|
25
|
+
// 태그 검색
|
|
26
|
+
export async function searchTags(params: TagsSearchParams) {
|
|
27
|
+
const result = await callApi<{
|
|
28
|
+
success: boolean;
|
|
29
|
+
data: Array<{
|
|
30
|
+
tag_code: string;
|
|
31
|
+
tag_type: string;
|
|
32
|
+
name: string;
|
|
33
|
+
name_en?: string;
|
|
34
|
+
match_type: string;
|
|
35
|
+
usage_count: number;
|
|
36
|
+
}>;
|
|
37
|
+
meta: { query: string; count: number };
|
|
38
|
+
}>('tags/search', params);
|
|
39
|
+
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 태그 매칭 (텍스트 → 태그)
|
|
44
|
+
export async function matchTags(params: TagsMatchParams) {
|
|
45
|
+
const result = await callApiPost<{
|
|
46
|
+
success: boolean;
|
|
47
|
+
data: {
|
|
48
|
+
matches: Array<{
|
|
49
|
+
tag_code: string;
|
|
50
|
+
tag_type: string;
|
|
51
|
+
name: string;
|
|
52
|
+
match_type: string;
|
|
53
|
+
}>;
|
|
54
|
+
keywords_extracted: string[];
|
|
55
|
+
keywords_matched: string[];
|
|
56
|
+
};
|
|
57
|
+
meta: { input_length: number; keywords_count: number };
|
|
58
|
+
}>('tags-match', params);
|
|
59
|
+
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 트렌드 관련 MCP Tools
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { callApi } from '../utils/api.js';
|
|
7
|
+
|
|
8
|
+
// 트렌드 파라미터 스키마
|
|
9
|
+
export const TrendsParamsSchema = z.object({
|
|
10
|
+
tag_code: z.string().describe('태그 코드 (예: STK005930)'),
|
|
11
|
+
days: z.number().min(1).max(30).default(7).describe('최근 N일 (기본: 7)'),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export type TrendsParams = z.infer<typeof TrendsParamsSchema>;
|
|
15
|
+
|
|
16
|
+
// 트렌드 조회
|
|
17
|
+
export async function getTrends(params: TrendsParams) {
|
|
18
|
+
const result = await callApi<{
|
|
19
|
+
success: boolean;
|
|
20
|
+
tag: {
|
|
21
|
+
code: string;
|
|
22
|
+
name: string;
|
|
23
|
+
type: string;
|
|
24
|
+
name_en?: string;
|
|
25
|
+
};
|
|
26
|
+
trend: Array<{
|
|
27
|
+
date: string;
|
|
28
|
+
count: number;
|
|
29
|
+
avg_score: number;
|
|
30
|
+
}>;
|
|
31
|
+
meta: { days: number; total_news: number };
|
|
32
|
+
}>('trends', params);
|
|
33
|
+
|
|
34
|
+
return result;
|
|
35
|
+
}
|
package/src/utils/api.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RagAlgo API 유틸리티
|
|
3
|
+
* Supabase Edge Functions 호출
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const SUPABASE_URL = 'https://xunrsikkybgxkybjzrgz.supabase.co/functions/v1';
|
|
7
|
+
const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh1bnJzaWtreWJneGt5Ymp6cmd6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjQ0NTExNTgsImV4cCI6MjA4MDAyNzE1OH0.SsXri828-Rf0gHlu4Bls-pewhfMNNII4mbiuLnc9ACs';
|
|
8
|
+
|
|
9
|
+
// 환경변수에서 API 키 가져오기
|
|
10
|
+
const getApiKey = (): string => {
|
|
11
|
+
const key = process.env.RAGALGO_API_KEY;
|
|
12
|
+
if (!key) {
|
|
13
|
+
throw new Error('RAGALGO_API_KEY 환경변수가 설정되지 않았습니다.');
|
|
14
|
+
}
|
|
15
|
+
return key;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// API 호출 기본 함수
|
|
19
|
+
export async function callApi<T>(
|
|
20
|
+
endpoint: string,
|
|
21
|
+
params?: Record<string, string | number | undefined>
|
|
22
|
+
): Promise<T> {
|
|
23
|
+
const apiKey = getApiKey();
|
|
24
|
+
|
|
25
|
+
// 쿼리 파라미터 생성
|
|
26
|
+
const url = new URL(`${SUPABASE_URL}/${endpoint}`);
|
|
27
|
+
if (params) {
|
|
28
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
29
|
+
if (value !== undefined) {
|
|
30
|
+
url.searchParams.append(key, String(value));
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const response = await fetch(url.toString(), {
|
|
36
|
+
method: 'GET',
|
|
37
|
+
headers: {
|
|
38
|
+
'Authorization': `Bearer ${SUPABASE_ANON_KEY}`,
|
|
39
|
+
'x-api-key': apiKey,
|
|
40
|
+
'Content-Type': 'application/json',
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
const error = await response.text();
|
|
46
|
+
throw new Error(`API 호출 실패: ${response.status} - ${error}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return response.json();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// POST API 호출
|
|
53
|
+
export async function callApiPost<T>(
|
|
54
|
+
endpoint: string,
|
|
55
|
+
body: Record<string, unknown>
|
|
56
|
+
): Promise<T> {
|
|
57
|
+
const apiKey = getApiKey();
|
|
58
|
+
const url = `${SUPABASE_URL}/${endpoint}`;
|
|
59
|
+
|
|
60
|
+
const response = await fetch(url, {
|
|
61
|
+
method: 'POST',
|
|
62
|
+
headers: {
|
|
63
|
+
'Authorization': `Bearer ${SUPABASE_ANON_KEY}`,
|
|
64
|
+
'x-api-key': apiKey,
|
|
65
|
+
'Content-Type': 'application/json',
|
|
66
|
+
},
|
|
67
|
+
body: JSON.stringify(body),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (!response.ok) {
|
|
71
|
+
const error = await response.text();
|
|
72
|
+
throw new Error(`API 호출 실패: ${response.status} - ${error}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return response.json();
|
|
76
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"outDir": "./dist",
|
|
9
|
+
"rootDir": "./src",
|
|
10
|
+
"declaration": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true
|
|
13
|
+
},
|
|
14
|
+
"include": [
|
|
15
|
+
"src/**/*"
|
|
16
|
+
],
|
|
17
|
+
"exclude": [
|
|
18
|
+
"node_modules",
|
|
19
|
+
"dist"
|
|
20
|
+
]
|
|
21
|
+
}
|