Github-Oauth MCP Server

import { GhostPage, PagePaginationParams, PostFormat, PostInclude, ToolResponse, CreatePageParams, UpdatePageParams } from '../types/index.js'; import { handleGhostApiError } from '../utils/error.js'; import { createGhostApi } from '../config/config.js'; import type { BrowseParams, ReadParams } from '@tryghost/admin-api'; const ghostApi = createGhostApi(); export const getPagesSchema = { name: 'get_pages', description: 'Get a list of pages', inputSchema: { type: 'object', properties: { limit: { type: 'number', description: '取得するページ数(デフォルト: 10)', minimum: 1, maximum: 100 }, page: { type: 'number', description: 'ページ番号(デフォルト: 1)', minimum: 1 }, order: { type: 'string', description: '並び順(デフォルト: published_at DESC)', enum: [ 'published_at DESC', 'published_at ASC', 'created_at DESC', 'created_at ASC', 'updated_at DESC', 'updated_at ASC' ] }, formats: { type: 'array', description: '取得するコンテンツフォーマット', items: { type: 'string', enum: ['html', 'mobiledoc', 'lexical'] } }, include: { type: 'array', description: '含める関連データ', items: { type: 'string', enum: ['authors', 'tags'] } } } }, }; export const getPageSchema = { name: 'get_page', description: '特定のページを取得', inputSchema: { type: 'object', properties: { id: { type: 'string', description: 'ページのID' }, formats: { type: 'array', description: '取得するコンテンツフォーマット', items: { type: 'string', enum: ['html', 'mobiledoc', 'lexical'] } }, include: { type: 'array', description: '含める関連データ', items: { type: 'string', enum: ['authors', 'tags'] } } }, required: ['id'] }, }; export const createPageSchema = { name: 'create_page', description: '新しいページを作成', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'ページのタイトル' }, html: { type: 'string', description: 'HTML形式のコンテンツ' }, lexical: { type: 'string', description: 'Lexical形式のコンテンツ' }, status: { type: 'string', description: 'ページの状態', enum: ['published', 'draft', 'scheduled'] }, visibility: { type: 'string', description: '公開範囲', enum: ['public', 'members', 'paid', 'tiers'] }, published_at: { type: 'string', description: '公開日時(スケジュール投稿用)' }, tags: { type: 'array', description: 'タグのID配列', items: { type: 'string' } }, authors: { type: 'array', description: '著者のID配列', items: { type: 'string' } }, featured: { type: 'boolean', description: 'おすすめページとして設定' } }, required: ['title'] } }; export const updatePageSchema = { name: 'update_page', description: 'ページを更新', inputSchema: { type: 'object', properties: { id: { type: 'string', description: 'ページのID' }, title: { type: 'string', description: 'ページのタイトル' }, html: { type: 'string', description: 'HTML形式のコンテンツ' }, lexical: { type: 'string', description: 'Lexical形式のコンテンツ' }, status: { type: 'string', description: 'ページの状態', enum: ['published', 'draft', 'scheduled'] }, visibility: { type: 'string', description: '公開範囲', enum: ['public', 'members', 'paid', 'tiers'] }, published_at: { type: 'string', description: '公開日時(スケジュール投稿用)' }, tags: { type: 'array', description: 'タグのID配列(既存のタグは置換)', items: { type: 'string' } }, authors: { type: 'array', description: '著者のID配列(既存の著者は置換)', items: { type: 'string' } }, featured: { type: 'boolean', description: 'おすすめページとして設定' } }, required: ['id'] } }; export const deletePageSchema = { name: 'delete_page', description: 'ページを削除', inputSchema: { type: 'object', properties: { id: { type: 'string', description: 'ページのID' } }, required: ['id'] } }; export const getPageBySlugSchema = { name: 'get_page_by_slug', description: 'スラッグでページを取得', inputSchema: { type: 'object', properties: { slug: { type: 'string', description: 'ページのスラッグ' }, formats: { type: 'array', description: '取得するコンテンツフォーマット', items: { type: 'string', enum: ['html', 'mobiledoc', 'lexical'] } }, include: { type: 'array', description: '含める関連データ', items: { type: 'string', enum: ['authors', 'tags'] } } }, required: ['slug'] } }; export const getPages = async ({ limit = 10, page = 1, order, formats, include }: PagePaginationParams): Promise<ToolResponse> => { try { const params: BrowseParams = { limit, page }; if (order) params.order = order; if (formats?.length) params.formats = formats.join(','); if (include?.length) params.include = include.join(','); const pages = await ghostApi.pages.browse(params); return { content: [ { type: 'text', text: JSON.stringify(pages, null, 2), }, ], }; } catch (error) { throw handleGhostApiError(error); } }; export const getPage = async ({ id, formats, include }: { id: string; formats?: PostFormat[]; include?: PostInclude[]; }): Promise<ToolResponse> => { try { const params: ReadParams = { id }; if (formats?.length) params.formats = formats.join(','); if (include?.length) params.include = include.join(','); const page = await ghostApi.pages.read(params); return { content: [ { type: 'text', text: JSON.stringify(page, null, 2), }, ], }; } catch (error) { throw handleGhostApiError(error); } }; export const createPage = async (params: CreatePageParams): Promise<ToolResponse> => { try { const page = await ghostApi.pages.add(params); return { content: [ { type: 'text', text: JSON.stringify(page, null, 2), }, ], }; } catch (error) { throw handleGhostApiError(error); } }; export const updatePage = async ({ id, ...params }: { id: string } & UpdatePageParams): Promise<ToolResponse> => { try { // 現在のページの情報を取得 const currentPage = await ghostApi.pages.read({ id }); // 現在のupdated_atを使用 params.updated_at = currentPage.updated_at || new Date().toISOString(); const page = await ghostApi.pages.edit({ id, ...params }); return { content: [ { type: 'text', text: JSON.stringify(page, null, 2), }, ], }; } catch (error) { throw handleGhostApiError(error); } }; export const deletePage = async ({ id }: { id: string }): Promise<ToolResponse> => { try { await ghostApi.pages.delete({ id }); return { content: [ { type: 'text', text: 'ページが正常に削除されました', }, ], }; } catch (error) { throw handleGhostApiError(error); } }; export const getPageBySlug = async ({ slug, formats, include }: { slug: string; formats?: PostFormat[]; include?: PostInclude[]; }): Promise<ToolResponse> => { try { const params: BrowseParams = { filter: `slug:${slug}` }; if (formats?.length) params.formats = formats.join(','); if (include?.length) params.include = include.join(','); const [page] = await ghostApi.pages.browse(params); if (!page) { throw new Error(`スラッグ "${slug}" のページが見つかりませんでした`); } return { content: [ { type: 'text', text: JSON.stringify(page, null, 2), }, ], }; } catch (error) { throw handleGhostApiError(error); } };