@roottale/cms-mcp 0.53.1 → 0.55.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.
@@ -137,10 +137,11 @@ console.log(page.items.map((p) => p.slug));
137
137
 
138
138
  `401` 에러(`invalid_key`)면 키 값/환경변수 로딩을 확인하세요.
139
139
 
140
- ## 5. MCP로 글과 이미지 자동화
140
+ ## 5. MCP로 전체 CMS 자동화
141
141
 
142
- MCP 설정의 `ROOTTALE_API_KEY`에 `read_write` 키를 넣으면 다음 tool을 쓸 수
143
- 있습니다.
142
+ MCP 설정의 `ROOTTALE_API_KEY`에 `full_management` 키를 넣으면 모델·필드·항목·
143
+ 노출·미디어 도구를 모두 쓸 수 있습니다. 글과 이미지만 관리한다면 기존
144
+ `read_write` 키로 충분합니다.
144
145
 
145
146
  1. `getSiteKnowledge`로 브랜드 보이스와 금지어 확인
146
147
  2. `uploadCmsMedia`로 썸네일 또는 본문 삽화 업로드
@@ -148,6 +149,11 @@ MCP 설정의 `ROOTTALE_API_KEY`에 `read_write` 키를 넣으면 다음 tool을
148
149
  4. 필요하면 `setCmsPostTerms`로 카테고리·태그 연결
149
150
  5. 검토 후 `publishCmsPost`로 발행
150
151
 
152
+ 정보 모델을 새로 자동화할 때는 `createCmsContentModel` →
153
+ `createCmsFieldGroup` → `createCmsPost(modelKey, fieldValues)` 순서로 진행합니다.
154
+ 배너·팝업은 `listCmsExposureSlots`로 FRONT 계약을 먼저 읽고
155
+ `createCmsExposure` → `publishCmsExposure` 순서로 진행합니다.
156
+
151
157
  썸네일은 업로드 응답의 `id`를 `featuredMediaId`에 넣습니다. 본문 삽화는
152
158
  응답의 `url`을 Tiptap image 노드에 넣습니다.
153
159
 
@@ -175,31 +181,35 @@ export ROOTTALE_API_KEY=rtlk_cust_xxxxxxxxxxxxxxxxxxxxxxxx
175
181
  npx -y @roottale/cms-mcp cli media upload ./thumbnail.webp \
176
182
  --alt "글 대표 이미지"
177
183
 
178
- npx -y @roottale/cms-mcp cli posts create \
184
+ npx -y @roottale/cms-mcp cli entries create \
179
185
  --title "새 글" \
180
186
  --slug "new-post" \
181
187
  --body-file ./post.json \
182
188
  --featured-media-id "<업로드 응답의 id>"
183
189
 
184
- npx -y @roottale/cms-mcp cli posts publish "<글 id>"
190
+ npx -y @roottale/cms-mcp cli entries publish "<글 id>"
185
191
  ```
186
192
 
187
- 상위 `cli --help`는 `posts`, `media` 그룹만 보여줍니다. 전체 하위 명령은
193
+ 상위 `cli --help`는 `models`, `fields`, `entries`, `exposure-slots`,
194
+ `exposures`, `media` 그룹을 보여줍니다. 전체 하위 명령은
188
195
  다음 도움말에서 확인하세요.
189
196
 
190
197
  ```bash
191
- npx -y @roottale/cms-mcp cli posts --help
198
+ npx -y @roottale/cms-mcp cli entries --help
199
+ npx -y @roottale/cms-mcp cli models --help
200
+ npx -y @roottale/cms-mcp cli exposures --help
192
201
  npx -y @roottale/cms-mcp cli media --help
193
202
 
194
203
  # 특정 명령의 모든 옵션
195
- npx -y @roottale/cms-mcp cli posts create --help
204
+ npx -y @roottale/cms-mcp cli entries create --help
196
205
  npx -y @roottale/cms-mcp cli media upload --help
197
206
  ```
198
207
 
199
- 글 명령은 `list`, `create`, `update`, `publish`, `unpublish`, `set-terms`,
208
+ 항목 명령은 `list`, `get`, `create`, `update`, `publish`, `unpublish`,
209
+ `set-terms`, `delete`,
200
210
  미디어 명령은 `list`, `upload`, `update`, `delete`를 제공합니다.
201
211
 
202
- 내부 운영 저장소에서는 같은 기능을 `rt cms posts ...`,
212
+ 내부 운영 저장소에서는 같은 기능을 `rt cms entries ...`,
203
213
  `rt cms media ...` 명령으로도 실행할 수 있습니다.
204
214
 
205
215
  ## 다음 단계
@@ -0,0 +1,70 @@
1
+ // 글 미리보기 — ROOT-ADMIN 편집기의 '미리보기'·'공유 링크'가 여는 주소.
2
+ // 관리자 발급 토큰(1시간, 글 1건 전용)으로 편집 중인 내용을 받아 발행 글과
3
+ // **같은 템플릿**(RootTaleBlogPost)으로 그린다 → 미리보기 = 발행 결과.
4
+ // 라우트를 배포한 뒤 관리자 설정 › 외부 연결 › "글 미리보기 여는 곳"에서
5
+ // "홈페이지에서 열기"를 켜면 편집기가 이 주소를 연다.
6
+ import type { Metadata } from "next";
7
+ import { notFound } from "next/navigation";
8
+ import {
9
+ fetchPostPreview,
10
+ isPreviewExpiredError,
11
+ } from "@roottale/cms-client/server";
12
+ import {
13
+ RootTaleBlogPost,
14
+ RootTalePreviewNotice,
15
+ } from "@roottale/cms-renderer-next/server";
16
+ import { buildPreviewMetadata } from "@roottale/cms-renderer-next/routes";
17
+
18
+ // 초안이 ISR·CDN 에 남으면 안 된다 — 항상 동적 렌더.
19
+ export const dynamic = "force-dynamic";
20
+ export const revalidate = 0;
21
+
22
+ type Props = {
23
+ params: Promise<{ id: string }>;
24
+ searchParams: Promise<{ token?: string }>;
25
+ };
26
+
27
+ function loadPreview(token: string) {
28
+ return fetchPostPreview({
29
+ apiKey: process.env.ROOTTALE_API_KEY!,
30
+ baseUrl: process.env.ROOTTALE_API_BASE,
31
+ token,
32
+ });
33
+ }
34
+
35
+ export async function generateMetadata({ searchParams }: Props): Promise<Metadata> {
36
+ const { token } = await searchParams;
37
+ const post = token ? await loadPreview(token).catch(() => null) : null;
38
+ // 항상 noindex/nofollow — 초안이 색인되거나 발행 주소와 중복되면 안 된다.
39
+ return buildPreviewMetadata({ title: post?.title });
40
+ }
41
+
42
+ export default async function PostPreviewPage({ params, searchParams }: Props) {
43
+ const { id } = await params;
44
+ const { token } = await searchParams;
45
+ if (!token) notFound(); // 주소만으로는 아무것도 보이지 않는다.
46
+
47
+ let expiresAt: string | undefined;
48
+ try {
49
+ const post = await loadPreview(token);
50
+ if (!post || post.id !== id) notFound(); // 토큰은 글 1건 전용
51
+ expiresAt = post.preview.expiresAt;
52
+ } catch (error) {
53
+ if (!isPreviewExpiredError(error)) throw error; // 만료는 컴포넌트가 안내
54
+ }
55
+
56
+ return (
57
+ <main className="container">
58
+ <RootTalePreviewNotice expiresAt={expiresAt} />
59
+ {/* 발행 글 상세(app/blog/[slug]/page.tsx)와 같은 props 를 쓰되
60
+ slugOrId 대신 previewToken 만 넘긴다. */}
61
+ <RootTaleBlogPost
62
+ apiKey={process.env.ROOTTALE_API_KEY!}
63
+ baseUrl={process.env.ROOTTALE_API_BASE}
64
+ previewToken={token}
65
+ relatedPostsCount={3}
66
+ breadcrumb={{ siteUrl: process.env.NEXT_PUBLIC_SITE_URL }}
67
+ />
68
+ </main>
69
+ );
70
+ }
@@ -3,6 +3,7 @@
3
3
  import {
4
4
  BLOG_SETTINGS_CACHE_TAG,
5
5
  fetchBlogSettings,
6
+ fetchCategoryCounts,
6
7
  fetchPost,
7
8
  type CmsPostContent,
8
9
  } from "@roottale/cms-client/server";
@@ -31,6 +32,7 @@ export interface BlogPostMeta {
31
32
  categorySlug: string; // "" = 카테고리 미지정 — /blog/categories/{slug} 허브 필터링용
32
33
  tags: { name: string; slug: string }[];
33
34
  image: string | null;
35
+ authorProfileId?: string | null;
34
36
  authorName?: string;
35
37
  authorSlug?: string | null; // /blog/author/{slug} — 미발급 작가는 null
36
38
  seo?: {
@@ -60,6 +62,7 @@ function toMeta(post: CmsPostContent): BlogPostMeta {
60
62
  ?.filter((t) => t.taxonomy === "tag")
61
63
  .map((t) => ({ name: t.name, slug: t.slug })) ?? [],
62
64
  image: post.featuredImageUrl ?? null,
65
+ authorProfileId: post.authorProfileId,
63
66
  authorName: post.authorName ?? undefined,
64
67
  authorSlug: post.authorSlug,
65
68
  seo: meta.seo as BlogPostMeta["seo"],
@@ -96,6 +99,25 @@ export async function getPostsByCategory(
96
99
  return posts.filter((p) => p.categorySlug === categorySlug);
97
100
  }
98
101
 
102
+ // 카테고리 허브의 소개문·검색 문구·공유 이미지는 카테고리 집계 응답에 함께 옵니다.
103
+ // 구버전 API와 이미지 미설정을 고려해 nullable 값으로 다룹니다.
104
+ export async function getCategoryArchiveMeta(categorySlug: string) {
105
+ const { categories } = await fetchCategoryCounts({
106
+ apiKey: getApiKey(),
107
+ baseUrl,
108
+ excludeCollections: true,
109
+ });
110
+ const category = categories.find((item) => item.slug === categorySlug);
111
+ if (!category) return null;
112
+ return {
113
+ title: category.seoTitle ?? `${category.name} 글 모음`,
114
+ description: category.seoDescription ?? category.description,
115
+ image: category.imageUrl
116
+ ? { url: category.imageUrl, alt: category.imageAlt ?? category.name }
117
+ : null,
118
+ };
119
+ }
120
+
99
121
  // 색인 위생 — 어드민 "내 사이트 > 카테고리 > 검색에 이 분류 페이지 노출" 을 켠
100
122
  // 분류인지. 켜지 않은 분류의 모음 페이지는 noindex + 사이트맵 제외가 됩니다
101
123
  // (seo.md "색인 위생" 절).
@@ -0,0 +1,12 @@
1
+ import { fetchContentModels, fetchPosts } from "@roottale/cms-client/server";
2
+
3
+ const apiKey = process.env.ROOTTALE_API_KEY!;
4
+
5
+ export async function getTeamMembers() {
6
+ const models = await fetchContentModels({ apiKey });
7
+ const model = models.models.find((candidate) => candidate.key === "team-member");
8
+ if (!model || model.preset !== "entity") return [];
9
+
10
+ const response = await fetchPosts({ apiKey, modelKey: model.key, limit: 100 });
11
+ return response.items;
12
+ }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@roottale/cms-mcp",
3
- "version": "0.53.1",
3
+ "version": "0.55.0",
4
4
  "type": "module",
5
- "description": "RootTale CMS MCP server and CLI for post publishing, media uploads, integration docs, and public API access.",
5
+ "description": "RootTale CMS MCP server and CLI for models, entries, exposures, media, integration docs, and public API access.",
6
6
  "bin": {
7
7
  "roottale-cms-mcp": "dist/index.js"
8
8
  },