@sgrsoft/page24-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/dist/api-client.d.ts +13 -0
- package/dist/api-client.js +32 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +6 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +27 -0
- package/dist/tools/forms.d.ts +3 -0
- package/dist/tools/forms.js +14 -0
- package/dist/tools/members.d.ts +3 -0
- package/dist/tools/members.js +18 -0
- package/dist/tools/menus.d.ts +3 -0
- package/dist/tools/menus.js +19 -0
- package/dist/tools/posts.d.ts +3 -0
- package/dist/tools/posts.js +72 -0
- package/dist/tools/site.d.ts +3 -0
- package/dist/tools/site.js +11 -0
- package/package.json +27 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface ApiResponse<T = any> {
|
|
2
|
+
code: number;
|
|
3
|
+
message: string | null;
|
|
4
|
+
result: T;
|
|
5
|
+
}
|
|
6
|
+
export declare class Page24ApiClient {
|
|
7
|
+
private client;
|
|
8
|
+
private siteId;
|
|
9
|
+
constructor(apiUrl: string, apiKey: string, siteId: string);
|
|
10
|
+
get<T = any>(path: string, params?: Record<string, any>): Promise<ApiResponse<T>>;
|
|
11
|
+
post<T = any>(path: string, data?: Record<string, any>): Promise<ApiResponse<T>>;
|
|
12
|
+
getSiteId(): string;
|
|
13
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
export class Page24ApiClient {
|
|
3
|
+
client;
|
|
4
|
+
siteId;
|
|
5
|
+
constructor(apiUrl, apiKey, siteId) {
|
|
6
|
+
this.siteId = siteId;
|
|
7
|
+
this.client = axios.create({
|
|
8
|
+
baseURL: apiUrl,
|
|
9
|
+
headers: {
|
|
10
|
+
"x-p24-api-key": apiKey,
|
|
11
|
+
"Content-Type": "application/json",
|
|
12
|
+
},
|
|
13
|
+
timeout: 30000,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
async get(path, params = {}) {
|
|
17
|
+
const res = await this.client.get(path, {
|
|
18
|
+
params: { siteId: this.siteId, ...params },
|
|
19
|
+
});
|
|
20
|
+
return res.data;
|
|
21
|
+
}
|
|
22
|
+
async post(path, data = {}) {
|
|
23
|
+
const res = await this.client.post(path, {
|
|
24
|
+
siteId: this.siteId,
|
|
25
|
+
...data,
|
|
26
|
+
});
|
|
27
|
+
return res.data;
|
|
28
|
+
}
|
|
29
|
+
getSiteId() {
|
|
30
|
+
return this.siteId;
|
|
31
|
+
}
|
|
32
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { Page24ApiClient } from "./api-client.js";
|
|
3
|
+
import { registerPostTools } from "./tools/posts.js";
|
|
4
|
+
import { registerMenuTools } from "./tools/menus.js";
|
|
5
|
+
import { registerSiteTools } from "./tools/site.js";
|
|
6
|
+
import { registerMemberTools } from "./tools/members.js";
|
|
7
|
+
import { registerFormTools } from "./tools/forms.js";
|
|
8
|
+
export function createServer() {
|
|
9
|
+
const apiUrl = process.env.PAGE24_API_URL;
|
|
10
|
+
const apiKey = process.env.PAGE24_API_KEY;
|
|
11
|
+
const siteId = process.env.PAGE24_SITE_ID;
|
|
12
|
+
if (!apiUrl || !apiKey || !siteId) {
|
|
13
|
+
console.error("환경변수가 설정되지 않았습니다: PAGE24_API_URL, PAGE24_API_KEY, PAGE24_SITE_ID");
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
const api = new Page24ApiClient(apiUrl, apiKey, siteId);
|
|
17
|
+
const server = new McpServer({
|
|
18
|
+
name: "page24",
|
|
19
|
+
version: "0.1.0",
|
|
20
|
+
});
|
|
21
|
+
registerMenuTools(server, api);
|
|
22
|
+
registerPostTools(server, api);
|
|
23
|
+
registerSiteTools(server, api);
|
|
24
|
+
registerMemberTools(server, api);
|
|
25
|
+
registerFormTools(server, api);
|
|
26
|
+
return server;
|
|
27
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerFormTools(server, api) {
|
|
3
|
+
server.tool("list_form_submissions", "폼(문의/신청) 응답 목록을 조회합니다.", {
|
|
4
|
+
mcode: z.string().describe("폼이 속한 메뉴 코드"),
|
|
5
|
+
pageNo: z.number().optional().default(1).describe("페이지 번호"),
|
|
6
|
+
limit: z.number().optional().default(20).describe("페이지당 개수"),
|
|
7
|
+
}, async ({ mcode, pageNo, limit }) => {
|
|
8
|
+
const res = await api.get("/form/list", { mcode, pageNo, limit });
|
|
9
|
+
if (res.code !== 200) {
|
|
10
|
+
return { content: [{ type: "text", text: `오류: ${res.message}` }] };
|
|
11
|
+
}
|
|
12
|
+
return { content: [{ type: "text", text: JSON.stringify(res.result, null, 2) }] };
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerMemberTools(server, api) {
|
|
3
|
+
server.tool("list_members", "사이트 회원 목록을 조회합니다.", {
|
|
4
|
+
pageNo: z.number().optional().default(1).describe("페이지 번호"),
|
|
5
|
+
limit: z.number().optional().default(20).describe("페이지당 개수"),
|
|
6
|
+
keyword: z.string().optional().describe("검색어 (닉네임, 아이디)"),
|
|
7
|
+
}, async ({ pageNo, limit, keyword }) => {
|
|
8
|
+
const res = await api.get("/member/memberManageList", {
|
|
9
|
+
pageNo,
|
|
10
|
+
limit,
|
|
11
|
+
keyword: keyword || "",
|
|
12
|
+
});
|
|
13
|
+
if (res.code !== 200) {
|
|
14
|
+
return { content: [{ type: "text", text: `오류: ${res.message}` }] };
|
|
15
|
+
}
|
|
16
|
+
return { content: [{ type: "text", text: JSON.stringify(res.result, null, 2) }] };
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function registerMenuTools(server, api) {
|
|
2
|
+
server.tool("list_menus", "사이트의 메뉴(페이지) 목록을 조회합니다. 각 메뉴의 mcode를 확인할 수 있습니다.", {}, async () => {
|
|
3
|
+
const res = await api.get("/menu/listAll");
|
|
4
|
+
if (res.code !== 200) {
|
|
5
|
+
return { content: [{ type: "text", text: `오류: ${res.message}` }] };
|
|
6
|
+
}
|
|
7
|
+
const menus = res.result;
|
|
8
|
+
const summary = Array.isArray(menus)
|
|
9
|
+
? menus.map((m) => ({
|
|
10
|
+
mcode: m.mcode,
|
|
11
|
+
title: m.title,
|
|
12
|
+
type: m.type,
|
|
13
|
+
state: m.state,
|
|
14
|
+
scode: m.scode,
|
|
15
|
+
}))
|
|
16
|
+
: menus;
|
|
17
|
+
return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] };
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerPostTools(server, api) {
|
|
3
|
+
server.tool("list_posts", "게시글 목록을 조회합니다. mcode(메뉴코드)가 필요합니다. list_menus로 먼저 메뉴 목록을 확인하세요.", {
|
|
4
|
+
mcode: z.string().describe("메뉴 코드 (list_menus로 확인)"),
|
|
5
|
+
pageNo: z.number().optional().default(1).describe("페이지 번호"),
|
|
6
|
+
limit: z.number().optional().default(10).describe("페이지당 개수"),
|
|
7
|
+
keyword: z.string().optional().describe("검색어"),
|
|
8
|
+
}, async ({ mcode, pageNo, limit, keyword }) => {
|
|
9
|
+
const res = await api.get("/board/List", {
|
|
10
|
+
mcode,
|
|
11
|
+
pageNo,
|
|
12
|
+
limit,
|
|
13
|
+
keyword: keyword || "",
|
|
14
|
+
});
|
|
15
|
+
if (res.code !== 200) {
|
|
16
|
+
return { content: [{ type: "text", text: `오류: ${res.message}` }] };
|
|
17
|
+
}
|
|
18
|
+
return { content: [{ type: "text", text: JSON.stringify(res.result, null, 2) }] };
|
|
19
|
+
});
|
|
20
|
+
server.tool("get_post", "게시글 상세 내용을 조회합니다.", {
|
|
21
|
+
mcode: z.string().describe("메뉴 코드"),
|
|
22
|
+
board_id: z.number().describe("게시글 번호"),
|
|
23
|
+
}, async ({ mcode, board_id }) => {
|
|
24
|
+
const res = await api.get("/board/read", { mcode, board_id });
|
|
25
|
+
if (res.code !== 200) {
|
|
26
|
+
return { content: [{ type: "text", text: `오류: ${res.message}` }] };
|
|
27
|
+
}
|
|
28
|
+
return { content: [{ type: "text", text: JSON.stringify(res.result, null, 2) }] };
|
|
29
|
+
});
|
|
30
|
+
server.tool("create_post", "새 게시글을 작성합니다. (readwrite 권한 필요)", {
|
|
31
|
+
mcode: z.string().describe("메뉴 코드"),
|
|
32
|
+
title: z.string().describe("게시글 제목"),
|
|
33
|
+
content: z.string().describe("게시글 내용 (HTML)"),
|
|
34
|
+
category: z.string().optional().describe("카테고리"),
|
|
35
|
+
}, async ({ mcode, title, content, category }) => {
|
|
36
|
+
const data = { mcode, title, content };
|
|
37
|
+
if (category)
|
|
38
|
+
data.category = category;
|
|
39
|
+
const res = await api.post("/board/write", data);
|
|
40
|
+
if (res.code !== 200) {
|
|
41
|
+
return { content: [{ type: "text", text: `오류: ${res.message}` }] };
|
|
42
|
+
}
|
|
43
|
+
return { content: [{ type: "text", text: `게시글 작성 완료: ${JSON.stringify(res.result)}` }] };
|
|
44
|
+
});
|
|
45
|
+
server.tool("update_post", "기존 게시글을 수정합니다. (readwrite 권한 필요)", {
|
|
46
|
+
mcode: z.string().describe("메뉴 코드"),
|
|
47
|
+
board_id: z.number().describe("게시글 번호"),
|
|
48
|
+
title: z.string().optional().describe("수정할 제목"),
|
|
49
|
+
content: z.string().optional().describe("수정할 내용 (HTML)"),
|
|
50
|
+
}, async ({ mcode, board_id, title, content }) => {
|
|
51
|
+
const data = { mcode, board_id };
|
|
52
|
+
if (title)
|
|
53
|
+
data.title = title;
|
|
54
|
+
if (content)
|
|
55
|
+
data.content = content;
|
|
56
|
+
const res = await api.post("/board/update", data);
|
|
57
|
+
if (res.code !== 200) {
|
|
58
|
+
return { content: [{ type: "text", text: `오류: ${res.message}` }] };
|
|
59
|
+
}
|
|
60
|
+
return { content: [{ type: "text", text: `게시글 수정 완료: ${JSON.stringify(res.result)}` }] };
|
|
61
|
+
});
|
|
62
|
+
server.tool("delete_post", "게시글을 삭제합니다. (readwrite 권한 필요)", {
|
|
63
|
+
mcode: z.string().describe("메뉴 코드"),
|
|
64
|
+
board_id: z.number().describe("게시글 번호"),
|
|
65
|
+
}, async ({ mcode, board_id }) => {
|
|
66
|
+
const res = await api.post("/board/delete", { mcode, board_id });
|
|
67
|
+
if (res.code !== 200) {
|
|
68
|
+
return { content: [{ type: "text", text: `오류: ${res.message}` }] };
|
|
69
|
+
}
|
|
70
|
+
return { content: [{ type: "text", text: `게시글 삭제 완료` }] };
|
|
71
|
+
});
|
|
72
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function registerSiteTools(server, api) {
|
|
2
|
+
server.tool("get_site_config", "사이트 설정 정보를 조회합니다.", {}, async () => {
|
|
3
|
+
const res = await api.get("/siteConfig/info", {
|
|
4
|
+
hostid: api.getSiteId(),
|
|
5
|
+
});
|
|
6
|
+
if (res.code !== 200) {
|
|
7
|
+
return { content: [{ type: "text", text: `오류: ${res.message}` }] };
|
|
8
|
+
}
|
|
9
|
+
return { content: [{ type: "text", text: JSON.stringify(res.result, null, 2) }] };
|
|
10
|
+
});
|
|
11
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sgrsoft/page24-mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for page24 CMS",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"page24-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsx src/index.ts",
|
|
13
|
+
"prepublishOnly": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
17
|
+
"axios": "^1.7.9",
|
|
18
|
+
"zod": "^3.24.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"typescript": "^5.7.0",
|
|
22
|
+
"tsx": "^4.19.0",
|
|
23
|
+
"@types/node": "^22.0.0"
|
|
24
|
+
},
|
|
25
|
+
"files": ["dist"],
|
|
26
|
+
"license": "MIT"
|
|
27
|
+
}
|