# Plume CMS MCP Server - API仕様書
このドキュメントは、Plume CMS用MCPサーバー(`plume-mcp-server`)を実装するための完全なAPI仕様書です。
## 📋 目次
1. [ベースURL](#ベースurl)
2. [認証](#認証)
3. [エンドポイント一覧](#エンドポイント一覧)
4. [型定義](#型定義)
5. [エラーハンドリング](#エラーハンドリング)
---
## ベースURL
**本番環境**: `https://plume-api-production.tomohirof.workers.dev`
**ローカル開発**: `http://localhost:8004`
---
## 認証
### 認証方式
**Bearer Token認証**を使用します。
```http
Authorization: Bearer <JWT_TOKEN>
```
### トークン取得
`POST /api/auth/login` でログインしてJWTトークンを取得します。
---
## エンドポイント一覧
### 🔐 認証関連
#### 1. ログイン
**エンドポイント**: `POST /api/auth/login`
**説明**: メールアドレスとパスワードでログインし、JWTトークンを取得
**リクエストボディ**:
```typescript
{
email: string; // メールアドレス
password: string; // パスワード
}
```
**レスポンス** (200 OK):
```typescript
{
token: string; // JWTトークン
user: {
id: number;
email: string;
name: string;
role: 'admin' | 'editor';
password_change_required: boolean;
created_at: string;
}
}
```
**エラー**:
- `400`: バリデーションエラー(email/password required)
- `401`: 認証失敗(Invalid credentials)
- `500`: サーバーエラー
---
#### 2. 現在のユーザー情報取得
**エンドポイント**: `GET /api/users/me`
**説明**: 現在ログイン中のユーザー情報を取得
**認証**: 必須(Bearer Token)
**レスポンス** (200 OK):
```typescript
{
id: number;
email: string;
name: string;
role: 'admin' | 'editor';
password_change_required: boolean;
created_at: string;
}
```
**エラー**:
- `401`: 認証エラー(トークンが無効または未提供)
- `404`: ユーザーが見つからない
- `500`: サーバーエラー
---
### 📝 記事管理
#### 3. 記事一覧取得
**エンドポイント**: `GET /api/blogs/:blogId/articles`
**説明**: 指定されたブログの記事一覧を取得(検索・フィルタリング可能)
**認証**: 必須(Bearer Token)
**パスパラメータ**:
- `blogId` (number): ブログID
**クエリパラメータ**:
- `search` (string, optional): タイトル・本文での検索キーワード
- `status` (string, optional): `draft` または `published` でフィルタ
- `categories` (string, optional): カテゴリIDのカンマ区切り(例: `1,2,3`)
- `tags` (string, optional): タグIDのカンマ区切り(例: `1,2,3`)
**レスポンス** (200 OK):
```typescript
Array<{
id: number;
blog_id: number;
title: string;
content: string;
slug: string;
status: 'draft' | 'published';
featured_image: string | null;
author_id: number;
excerpt: string | null;
created_at: string;
updated_at: string;
author?: {
id: number;
name: string;
email: string;
};
categories?: Array<{
id: number;
name: string;
slug: string;
}>;
tags?: Array<{
id: number;
name: string;
slug: string;
}>;
}>
```
**エラー**:
- `401`: 認証エラー
- `403`: 権限エラー(ブログへのアクセス権がない)
- `404`: ブログが見つからない
---
#### 4. 記事詳細取得
**エンドポイント**: `GET /api/blogs/:blogId/articles/:id`
**説明**: 指定されたIDの記事詳細を取得(メタデータ含む)
**認証**: 必須(Bearer Token)
**パスパラメータ**:
- `blogId` (number): ブログID
- `id` (number): 記事ID
**レスポンス** (200 OK):
```typescript
{
id: number;
blog_id: number;
title: string;
content: string; // Markdown形式
slug: string;
status: 'draft' | 'published';
featured_image: string | null;
author_id: number;
excerpt: string | null;
created_at: string;
updated_at: string;
author?: {
id: number;
name: string;
email: string;
};
categories?: Array<{
id: number;
name: string;
slug: string;
}>;
tags?: Array<{
id: number;
name: string;
slug: string;
}>;
}
```
**エラー**:
- `401`: 認証エラー
- `403`: 権限エラー
- `404`: 記事が見つからない
---
#### 5. 記事作成
**エンドポイント**: `POST /api/blogs/:blogId/articles`
**説明**: 新規記事を作成
**認証**: 必須(Bearer Token)
**パスパラメータ**:
- `blogId` (number): ブログID
**リクエストボディ**:
```typescript
{
title: string; // 必須: タイトル
content: string; // 必須: 本文(Markdown)
slug: string; // 必須: URL用スラッグ
status?: 'draft' | 'published'; // オプション: ステータス(デフォルト: 'draft')
featured_image?: string | null; // オプション: アイキャッチ画像URL
excerpt?: string | null; // オプション: 抜粋
author_id?: number; // オプション: 著者ID(デフォルト: 認証ユーザー)
category_ids?: number[]; // オプション: カテゴリID配列
tag_ids?: number[]; // オプション: タグID配列
}
```
**レスポンス** (201 Created):
```typescript
// 記事詳細取得と同じ形式
```
**エラー**:
- `400`: バリデーションエラー(必須項目不足等)
- `401`: 認証エラー
- `403`: 権限エラー
- `404`: ブログが見つからない
- `409`: slug重複エラー
---
#### 6. 記事更新
**エンドポイント**: `PUT /api/blogs/:blogId/articles/:id`
**説明**: 既存記事を更新
**認証**: 必須(Bearer Token)
**パスパラメータ**:
- `blogId` (number): ブログID
- `id` (number): 記事ID
**リクエストボディ**:
```typescript
{
title?: string; // オプション: タイトル
content?: string; // オプション: 本文(Markdown)
slug?: string; // オプション: URL用スラッグ
status?: 'draft' | 'published'; // オプション: ステータス
featured_image?: string | null; // オプション: アイキャッチ画像URL
excerpt?: string | null; // オプション: 抜粋
author_id?: number; // オプション: 著者ID
category_ids?: number[]; // オプション: カテゴリID配列
tag_ids?: number[]; // オプション: タグID配列
}
```
**レスポンス** (200 OK):
```typescript
// 記事詳細取得と同じ形式
```
**エラー**:
- `400`: バリデーションエラー
- `401`: 認証エラー
- `403`: 権限エラー
- `404`: 記事が見つからない
---
#### 7. 記事削除
**エンドポイント**: `DELETE /api/blogs/:blogId/articles/:id`
**説明**: 記事を削除
**認証**: 必須(Bearer Token)
**パスパラメータ**:
- `blogId` (number): ブログID
- `id` (number): 記事ID
**レスポンス** (204 No Content):
```
(レスポンスボディなし)
```
**エラー**:
- `401`: 認証エラー
- `403`: 権限エラー
- `404`: 記事が見つからない
---
### 📚 ブログ管理
#### 8. ブログ一覧取得
**エンドポイント**: `GET /api/blogs`
**説明**: 認証ユーザーのブログ一覧を取得
**認証**: 必須(Bearer Token)
**レスポンス** (200 OK):
```typescript
Array<{
id: number;
user_id: number;
name: string;
title: string;
slug: string;
description: string | null;
created_at: string;
deployment_project_name: string | null;
deployment_url: string | null;
last_deployed_at: string | null;
deployment_in_progress: number;
}>
```
**エラー**:
- `401`: 認証エラー
---
#### 9. ブログ詳細取得
**エンドポイント**: `GET /api/blogs/:id`
**説明**: 指定されたブログの詳細を取得
**認証**: 必須(Bearer Token)
**パスパラメータ**:
- `id` (number): ブログID
**レスポンス** (200 OK):
```typescript
{
id: number;
user_id: number;
name: string;
title: string;
slug: string;
description: string | null;
created_at: string;
deployment_project_name: string | null;
deployment_url: string | null;
last_deployed_at: string | null;
deployment_in_progress: number;
}
```
**エラー**:
- `401`: 認証エラー
- `403`: 権限エラー
- `404`: ブログが見つからない
---
### 🏷️ カテゴリ管理
#### 10. カテゴリ一覧取得
**エンドポイント**: `GET /api/blogs/:blogId/categories`
**説明**: 指定されたブログのカテゴリ一覧を取得
**認証**: 必須(Bearer Token)
**パスパラメータ**:
- `blogId` (number): ブログID
**レスポンス** (200 OK):
```typescript
Array<{
id: number;
blog_id: number;
name: string;
slug: string;
description: string | null;
created_at: string;
updated_at: string;
}>
```
**エラー**:
- `401`: 認証エラー
- `403`: 権限エラー
- `404`: ブログが見つからない
---
### 🔖 タグ管理
#### 11. タグ一覧取得
**エンドポイント**: `GET /api/blogs/:blogId/tags`
**説明**: 指定されたブログのタグ一覧を取得
**認証**: 必須(Bearer Token)
**パスパラメータ**:
- `blogId` (number): ブログID
**レスポンス** (200 OK):
```typescript
Array<{
id: number;
blog_id: number;
name: string;
slug: string;
created_at: string;
}>
```
**エラー**:
- `401`: 認証エラー
- `403`: 権限エラー
- `404`: ブログが見つからない
---
## 型定義
### User
```typescript
export interface User {
id: number;
email: string;
name: string;
role: 'admin' | 'editor';
password_change_required: boolean;
created_at: string;
}
```
### Blog
```typescript
export interface Blog {
id: number;
user_id: number;
name: string;
title: string;
slug: string;
description: string | null;
created_at: string;
deployment_project_name: string | null;
deployment_url: string | null;
last_deployed_at: string | null;
deployment_in_progress: number;
}
```
### Article
```typescript
export interface Article {
id: number;
blog_id: number;
title: string;
content: string;
slug: string;
status: 'draft' | 'published';
featured_image: string | null;
author_id: number;
excerpt: string | null;
created_at: string;
updated_at: string;
}
```
### ArticleWithMetadata
```typescript
export interface ArticleWithMetadata extends Article {
author?: {
id: number;
name: string;
email: string;
};
categories?: Array<{
id: number;
name: string;
slug: string;
}>;
tags?: Array<{
id: number;
name: string;
slug: string;
}>;
}
```
### Category
```typescript
export interface Category {
id: number;
blog_id: number;
name: string;
slug: string;
description: string | null;
created_at: string;
updated_at: string;
}
```
### Tag
```typescript
export interface Tag {
id: number;
blog_id: number;
name: string;
slug: string;
created_at: string;
}
```
---
## エラーハンドリング
### エラーレスポンス形式
すべてのエラーは以下の形式で返されます:
```typescript
{
error: string; // エラーメッセージ
status?: number; // HTTPステータスコード
}
```
### HTTPステータスコード
| コード | 意味 | 説明 |
|--------|------|------|
| `200` | OK | リクエスト成功 |
| `201` | Created | リソース作成成功 |
| `204` | No Content | リクエスト成功(レスポンスボディなし) |
| `400` | Bad Request | バリデーションエラー |
| `401` | Unauthorized | 認証エラー(トークンが無効または未提供) |
| `403` | Forbidden | 権限エラー(リソースへのアクセス権がない) |
| `404` | Not Found | リソースが見つからない |
| `409` | Conflict | リソースの重複(例: slug重複) |
| `500` | Internal Server Error | サーバーエラー |
---
## MCP実装時の注意点
### 1. 環境変数
MCPサーバーで以下の環境変数を設定する必要があります:
```bash
PLUME_API_URL=https://plume-api-production.tomohirof.workers.dev
PLUME_EMAIL=your-email@example.com
PLUME_PASSWORD=your-password
```
### 2. 認証フロー
1. MCPサーバー起動時に`POST /api/auth/login`でトークンを取得
2. 取得したトークンをメモリに保存
3. 以降の全リクエストで`Authorization: Bearer <token>`ヘッダーを付与
### 3. エラーハンドリング
- `401`エラーの場合は再ログインを試行
- `403`エラーの場合はユーザーに権限不足を通知
- `404`エラーの場合はリソースが存在しないことを通知
### 4. リクエスト例
#### ログイン
```typescript
const response = await fetch('https://plume-api-production.tomohirof.workers.dev/api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: 'user@example.com',
password: 'password123',
}),
});
const { token, user } = await response.json();
```
#### 記事一覧取得
```typescript
const response = await fetch('https://plume-api-production.tomohirof.workers.dev/api/blogs/1/articles', {
headers: {
'Authorization': `Bearer ${token}`,
},
});
const articles = await response.json();
```
#### 記事作成
```typescript
const response = await fetch('https://plume-api-production.tomohirof.workers.dev/api/blogs/1/articles', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
title: '新しい記事',
content: '記事の本文(Markdown)',
slug: 'new-article',
status: 'draft',
}),
});
const article = await response.json();
```
---
## 参考リンク
- [Plume CMS リポジトリ](https://github.com/tomohirof/plume-cms)
- [MCP公式ドキュメント](https://modelcontextprotocol.io/)
- [TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)