Skip to content

Commit c1d96b8

Browse files
authored
Merge pull request #8 from microcmsio/feature/add-tools
対応APIを追加
2 parents 58e6bb1 + 7635cf0 commit c1d96b8

13 files changed

+481
-5
lines changed

manifest.json

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"manifest_version": "0.2",
33
"name": "microcms-mcp-server",
44
"display_name": "microCMS MCP Server",
5-
"version": "0.5.0",
5+
"version": "0.6.0",
66
"description": "microCMSのMCP Serverです。LLMから直接コンテンツの取得や入稿ができます。",
77
"icon": "assets/icon.png",
88
"author": {
@@ -28,10 +28,18 @@
2828
"name": "microcms_get_list",
2929
"description": "Get a list of content from microCMS with filtering and search capabilities"
3030
},
31+
{
32+
"name": "microcms_get_list_meta",
33+
"description": "Get a list of contents with metadata from microCMS Management API. Use ONLY when user message contains \"メタ\" or \"メタ情報\". Returns metadata like status, createdBy, updatedBy, reservationTime, closedAt, and customStatus."
34+
},
3135
{
3236
"name": "microcms_get_content",
3337
"description": "Get a specific content item from microCMS"
3438
},
39+
{
40+
"name": "microcms_get_content_meta",
41+
"description": "Get a specific content with metadata from microCMS Management API. Use ONLY when user message contains \"メタ\" or \"メタ情報\". Returns metadata like status, createdBy, updatedBy, reservationTime, closedAt, and customStatus."
42+
},
3543
{
3644
"name": "microcms_create_content_published",
3745
"description": "Create new published content in microCMS"
@@ -52,6 +60,14 @@
5260
"name": "microcms_patch_content",
5361
"description": "Partially update content in microCMS"
5462
},
63+
{
64+
"name": "microcms_patch_content_status",
65+
"description": "Change content publication status in microCMS (Management API). Can change status between PUBLISH and DRAFT"
66+
},
67+
{
68+
"name": "microcms_patch_content_created_by",
69+
"description": "Change content creator in microCMS (Management API). Updates the createdBy field of a content item to a specified member ID"
70+
},
5571
{
5672
"name": "microcms_delete_content",
5773
"description": "Delete content from microCMS"
@@ -67,6 +83,18 @@
6783
{
6884
"name": "microcms_get_api_info",
6985
"description": "Get API information from microCMS"
86+
},
87+
{
88+
"name": "microcms_get_api_list",
89+
"description": "Get list of all available APIs (endpoints) from microCMS Management API"
90+
},
91+
{
92+
"name": "microcms_get_member",
93+
"description": "Get a specific member from microCMS Management API. Returns member information including ID, name, email, and MFA status"
94+
},
95+
{
96+
"name": "microcms_delete_media",
97+
"description": "Delete media files from microCMS (Management API). Supports deletion of both images and files"
7098
}
7199
],
72100
"user_config": {

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "microcms-mcp-server",
3-
"version": "0.5.1",
3+
"version": "0.6.0",
44
"description": "microCMS MCP Bundle - microCMSのコンテンツAPIやマネジメントAPIを利用し、LLMからコンテンツ管理ができるMCP Bundle",
55
"main": "dist/index.js",
66
"type": "module",
@@ -65,4 +65,4 @@
6565
"publishConfig": {
6666
"access": "public"
6767
}
68-
}
68+
}

src/client.ts

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,5 +124,133 @@ export async function getApiList(): Promise<any> {
124124
throw new Error(`Failed to get API list: ${response.status} ${response.statusText} - ${errorText}`);
125125
}
126126

127+
return await response.json();
128+
}
129+
130+
export async function getMember(memberId: string): Promise<any> {
131+
const url = `https://${config.serviceDomain}.microcms-management.io/api/v1/members/${memberId}`;
132+
133+
const response = await fetch(url, {
134+
method: 'GET',
135+
headers: {
136+
'X-MICROCMS-API-KEY': config.apiKey,
137+
},
138+
});
139+
140+
if (!response.ok) {
141+
const errorText = await response.text();
142+
throw new Error(`Failed to get member: ${response.status} ${response.statusText} - ${errorText}`);
143+
}
144+
145+
return await response.json();
146+
}
147+
148+
export async function deleteMedia(mediaUrl: string): Promise<{ id: string }> {
149+
const url = `https://${config.serviceDomain}.microcms-management.io/api/v2/media?url=${encodeURIComponent(mediaUrl)}`;
150+
151+
const response = await fetch(url, {
152+
method: 'DELETE',
153+
headers: {
154+
'X-MICROCMS-API-KEY': config.apiKey,
155+
},
156+
});
157+
158+
if (!response.ok) {
159+
const errorText = await response.text();
160+
throw new Error(`Failed to delete media: ${response.status} ${response.statusText} - ${errorText}`);
161+
}
162+
163+
return await response.json();
164+
}
165+
166+
export async function patchContentStatus(
167+
endpoint: string,
168+
contentId: string,
169+
status: 'PUBLISH' | 'DRAFT'
170+
): Promise<void> {
171+
const url = `https://${config.serviceDomain}.microcms-management.io/api/v1/contents/${endpoint}/${contentId}/status`;
172+
173+
const response = await fetch(url, {
174+
method: 'PATCH',
175+
headers: {
176+
'X-MICROCMS-API-KEY': config.apiKey,
177+
'Content-Type': 'application/json',
178+
},
179+
body: JSON.stringify({ status: [status] }),
180+
});
181+
182+
if (!response.ok) {
183+
const errorText = await response.text();
184+
throw new Error(`Failed to patch content status: ${response.status} ${response.statusText} - ${errorText}`);
185+
}
186+
}
187+
188+
export async function patchContentCreatedBy(
189+
endpoint: string,
190+
contentId: string,
191+
memberId: string
192+
): Promise<{ id: string }> {
193+
const url = `https://${config.serviceDomain}.microcms-management.io/api/v1/contents/${endpoint}/${contentId}/createdBy`;
194+
195+
const response = await fetch(url, {
196+
method: 'PATCH',
197+
headers: {
198+
'X-MICROCMS-API-KEY': config.apiKey,
199+
'Content-Type': 'application/json',
200+
},
201+
body: JSON.stringify({ createdBy: memberId }),
202+
});
203+
204+
if (!response.ok) {
205+
const errorText = await response.text();
206+
throw new Error(`Failed to patch content createdBy: ${response.status} ${response.statusText} - ${errorText}`);
207+
}
208+
209+
return await response.json();
210+
}
211+
212+
export async function getListMeta(
213+
endpoint: string,
214+
options?: { limit?: number; offset?: number }
215+
): Promise<any> {
216+
const queryParams = new URLSearchParams();
217+
if (options?.limit) queryParams.append('limit', options.limit.toString());
218+
if (options?.offset) queryParams.append('offset', options.offset.toString());
219+
220+
const url = `https://${config.serviceDomain}.microcms-management.io/api/v1/contents/${endpoint}${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;
221+
222+
const response = await fetch(url, {
223+
method: 'GET',
224+
headers: {
225+
'X-MICROCMS-API-KEY': config.apiKey,
226+
},
227+
});
228+
229+
if (!response.ok) {
230+
const errorText = await response.text();
231+
throw new Error(`Failed to get contents list: ${response.status} ${response.statusText} - ${errorText}`);
232+
}
233+
234+
return await response.json();
235+
}
236+
237+
export async function getContentManagement(
238+
endpoint: string,
239+
contentId: string
240+
): Promise<any> {
241+
const url = `https://${config.serviceDomain}.microcms-management.io/api/v1/contents/${endpoint}/${contentId}`;
242+
243+
const response = await fetch(url, {
244+
method: 'GET',
245+
headers: {
246+
'X-MICROCMS-API-KEY': config.apiKey,
247+
},
248+
});
249+
250+
if (!response.ok) {
251+
const errorText = await response.text();
252+
throw new Error(`Failed to get content: ${response.status} ${response.statusText} - ${errorText}`);
253+
}
254+
127255
return await response.json();
128256
}

src/server.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,23 @@ import {
66
} from '@modelcontextprotocol/sdk/types.js';
77

88
import { getListTool, handleGetList } from './tools/get-list.js';
9+
import { getListMetaTool, handleGetListMeta as handleGetListMeta } from './tools/get-list-meta.js';
910
import { getContentTool, handleGetContent } from './tools/get-content.js';
11+
import { getContentMetaTool, handleGetContentMeta } from './tools/get-content-meta.js';
1012
import { createContentPublishedTool, handleCreateContentPublished } from './tools/create-content-published.js';
1113
import { createContentDraftTool, handleCreateContentDraft } from './tools/create-content-draft.js';
1214
import { updateContentPublishedTool, handleUpdateContentPublished } from './tools/update-content-published.js';
1315
import { updateContentDraftTool, handleUpdateContentDraft } from './tools/update-content-draft.js';
1416
import { patchContentTool, handlePatchContent } from './tools/patch-content.js';
17+
import { patchContentStatusTool, handlePatchContentStatus } from './tools/patch-content-status.js';
18+
import { patchContentCreatedByTool, handlePatchContentCreatedBy } from './tools/patch-content-created-by.js';
1519
import { deleteContentTool, handleDeleteContent } from './tools/delete-content.js';
1620
import { getMediaTool, handleGetMedia } from './tools/get-media.js';
1721
import { uploadMediaTool, handleUploadMedia } from './tools/upload-media.js';
22+
import { deleteMediaTool, handleDeleteMedia } from './tools/delete-media.js';
1823
import { getApiInfoTool, handleGetApiInfo } from './tools/get-api-info.js';
1924
import { getApiListTool, handleGetApiList } from './tools/get-apis-list.js';
25+
import { getMemberTool, handleGetMember } from './tools/get-member.js';
2026
import type { ToolParameters, MediaToolParameters } from './types.js';
2127

2228
const server = new Server(
@@ -36,17 +42,23 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
3642
return {
3743
tools: [
3844
getListTool,
45+
getListMetaTool,
3946
getContentTool,
47+
getContentMetaTool,
4048
createContentPublishedTool,
4149
createContentDraftTool,
4250
updateContentPublishedTool,
4351
updateContentDraftTool,
4452
patchContentTool,
53+
patchContentStatusTool,
54+
patchContentCreatedByTool,
4555
deleteContentTool,
4656
getMediaTool,
4757
uploadMediaTool,
58+
deleteMediaTool,
4859
getApiInfoTool,
4960
getApiListTool,
61+
getMemberTool,
5062
],
5163
};
5264
});
@@ -62,9 +74,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
6274
case 'microcms_get_list':
6375
result = await handleGetList(params);
6476
break;
77+
case 'microcms_get_list_meta':
78+
result = await handleGetListMeta(params);
79+
break;
6580
case 'microcms_get_content':
6681
result = await handleGetContent(params);
6782
break;
83+
case 'microcms_get_content_meta':
84+
result = await handleGetContentMeta(params);
85+
break;
6886
case 'microcms_create_content_published':
6987
result = await handleCreateContentPublished(params);
7088
break;
@@ -80,6 +98,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
8098
case 'microcms_patch_content':
8199
result = await handlePatchContent(params);
82100
break;
101+
case 'microcms_patch_content_status':
102+
result = await handlePatchContentStatus(params as ToolParameters & { status: 'PUBLISH' | 'DRAFT' });
103+
break;
104+
case 'microcms_patch_content_created_by':
105+
result = await handlePatchContentCreatedBy(params as ToolParameters & { createdBy: string });
106+
break;
83107
case 'microcms_delete_content':
84108
result = await handleDeleteContent(params);
85109
break;
@@ -89,12 +113,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
89113
case 'microcms_upload_media':
90114
result = await handleUploadMedia(params as unknown as MediaToolParameters);
91115
break;
116+
case 'microcms_delete_media':
117+
result = await handleDeleteMedia(params as unknown as MediaToolParameters & { url: string });
118+
break;
92119
case 'microcms_get_api_info':
93120
result = await handleGetApiInfo(params);
94121
break;
95122
case 'microcms_get_api_list':
96123
result = await handleGetApiList(params);
97124
break;
125+
case 'microcms_get_member':
126+
result = await handleGetMember(params as ToolParameters & { memberId: string });
127+
break;
98128
default:
99129
throw new Error(`Unknown tool: ${name}`);
100130
}

src/tools/delete-media.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Tool } from '@modelcontextprotocol/sdk/types.js';
2+
import { deleteMedia } from '../client.js';
3+
import type { MediaToolParameters } from '../types.js';
4+
5+
export const deleteMediaTool: Tool = {
6+
name: 'microcms_delete_media',
7+
description: 'Delete media files from microCMS (Management API). Supports deletion of both images and files. Requires media deletion permissions. Note: Media referenced by content cannot be deleted.',
8+
inputSchema: {
9+
type: 'object',
10+
properties: {
11+
url: {
12+
type: 'string',
13+
description: 'URL of the media to delete (e.g., "https://images.microcms-assets.io/assets/xxxxx/yyyyy/hoge.jpg" or "https://files.microcms-assets.io/assets/xxxxx/yyyyy/hoge.pdf"). Custom domain URLs are also supported.',
14+
},
15+
},
16+
required: ['url'],
17+
},
18+
};
19+
20+
export async function handleDeleteMedia(params: MediaToolParameters & { url: string }) {
21+
const { url } = params;
22+
23+
if (!url) {
24+
throw new Error('url is required');
25+
}
26+
27+
return await deleteMedia(url);
28+
}
29+

src/tools/get-content-meta.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Tool } from '@modelcontextprotocol/sdk/types.js';
2+
import { getContentManagement } from '../client.js';
3+
import type { ToolParameters } from '../types.js';
4+
5+
export const getContentMetaTool: Tool = {
6+
name: 'microcms_get_content_meta',
7+
description: 'Get a specific content with metadata from microCMS Management API. IMPORTANT: Use this tool ONLY when the user message contains "メタ" (meta) or "メタ情報" (metadata). This API returns metadata information such as status, createdBy, updatedBy, reservationTime, closedAt, and customStatus that are not available in the regular content API. For regular content retrieval, use microcms_get_content instead.',
8+
inputSchema: {
9+
type: 'object',
10+
properties: {
11+
endpoint: {
12+
type: 'string',
13+
description: 'Content type name (e.g., "blogs", "news")',
14+
},
15+
contentId: {
16+
type: 'string',
17+
description: 'Content ID to retrieve',
18+
},
19+
},
20+
required: ['endpoint', 'contentId'],
21+
},
22+
};
23+
24+
export async function handleGetContentMeta(params: ToolParameters) {
25+
const { endpoint, contentId } = params;
26+
27+
if (!endpoint) {
28+
throw new Error('endpoint is required');
29+
}
30+
31+
if (!contentId) {
32+
throw new Error('contentId is required');
33+
}
34+
35+
return await getContentManagement(endpoint, contentId);
36+
}
37+

0 commit comments

Comments
 (0)