Claude-skill-registry-data mcp-development
MCPサーバー開発を支援します。プロトコル準拠、Pydanticスキーマ設計、Playwright統合のベストプラクティスを提供します。
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry-data
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry-data "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/mcp-development" ~/.claude/skills/majiayu000-claude-skill-registry-data-mcp-development && rm -rf "$T"
manifest:
data/mcp-development/SKILL.mdsource content
MCP Development スキル
このスキルは、Constitution Article 3: MCP Protocol Compliance を支援し、MCPサーバー開発のベストプラクティスを提供します。
起動条件
以下の状況で起動します:
- MCPツール実装時: 新しいMCPツールを追加する際
- スキーマ設計時: 入出力スキーマを定義する際
- Playwright統合時: ブラウザ自動化を実装する際
- セッション管理実装時: 認証状態を管理する際
- エラーハンドリング設計時: MCPエラーレスポンスを設計する際
MCPプロトコル要件
ツール定義
MCPツールは以下の構造を持つ必要があります:
from mcp.types import Tool, TextContent from pydantic import BaseModel, Field class CreateDraftInput(BaseModel): """下書き作成の入力パラメータ""" title: str = Field(..., description="記事のタイトル") body: str = Field(..., description="記事の本文(Markdown形式)") tags: list[str] | None = Field(None, description="記事のタグ一覧") # ツール定義 create_draft_tool = Tool( name="create_draft", description="note.comに記事の下書きを作成します", inputSchema=CreateDraftInput.model_json_schema(), )
Pydanticモデル設計
必須ルール:
- すべての入力パラメータはPydanticモデルで検証
- Fieldに説明を必ず付与
- 型アノテーションを明確に指定
from pydantic import BaseModel, Field, field_validator class ArticleContent(BaseModel): """記事コンテンツのスキーマ""" title: str = Field(..., min_length=1, max_length=200, description="記事タイトル") body: str = Field(..., min_length=1, description="記事本文") status: str = Field("draft", pattern="^(draft|published)$", description="記事状態") @field_validator("title") @classmethod def title_not_empty(cls, v: str) -> str: if not v.strip(): raise ValueError("タイトルは空にできません") return v.strip()
エラーレスポンス
MCPエラーは適切な形式で返す必要があります:
from mcp.types import TextContent, ErrorData from mcp.shared.exceptions import McpError class NoteApiError(McpError): """note.com API関連のエラー""" pass class AuthenticationError(NoteApiError): """認証エラー""" def __init__(self, message: str = "認証が必要です"): super().__init__(ErrorData(code=-32001, message=message)) class SessionExpiredError(NoteApiError): """セッション期限切れエラー""" def __init__(self): super().__init__(ErrorData( code=-32002, message="セッションの有効期限が切れました。再ログインしてください。" ))
Playwright統合
ブラウザライフサイクル管理
from playwright.async_api import async_playwright, Browser, Page from contextlib import asynccontextmanager class BrowserManager: """ブラウザインスタンスのライフサイクル管理""" def __init__(self) -> None: self._browser: Browser | None = None self._page: Page | None = None async def get_page(self) -> Page: """既存のページを再利用、なければ新規作成""" if self._page is not None and not self._page.is_closed(): return self._page if self._browser is None: playwright = await async_playwright().start() self._browser = await playwright.chromium.launch(headless=False) self._page = await self._browser.new_page() return self._page async def close(self) -> None: """リソースのクリーンアップ""" if self._page: await self._page.close() if self._browser: await self._browser.close()
作業ウィンドウの再利用
async def show_preview(self, article_id: str) -> None: """プレビューを表示(既存ウィンドウを再利用)""" page = await self.browser_manager.get_page() # 既にプレビューページにいる場合はリロード current_url = page.url preview_url = f"https://note.com/api/v1/text_notes/{article_id}/preview" if current_url == preview_url: await page.reload() else: await page.goto(preview_url)
セッション管理
セキュアなセッション保存
OSのキーチェーン/資格情報マネージャーを使用:
import keyring from dataclasses import dataclass from datetime import datetime import json SERVICE_NAME = "note-mcp" @dataclass class Session: """セッション情報""" cookies: dict[str, str] user_id: str expires_at: datetime def is_valid(self) -> bool: """セッションが有効かチェック""" return datetime.now() < self.expires_at def save_session(session: Session) -> None: """セッションをセキュアに保存""" keyring.set_password( SERVICE_NAME, "session", json.dumps({ "cookies": session.cookies, "user_id": session.user_id, "expires_at": session.expires_at.isoformat(), }) ) def load_session() -> Session | None: """保存されたセッションを読み込み""" data = keyring.get_password(SERVICE_NAME, "session") if data is None: return None parsed = json.loads(data) session = Session( cookies=parsed["cookies"], user_id=parsed["user_id"], expires_at=datetime.fromisoformat(parsed["expires_at"]), ) if not session.is_valid(): keyring.delete_password(SERVICE_NAME, "session") return None return session
セッション期限切れ処理
async def execute_with_session(self, operation: Callable) -> Any: """セッションを確認して操作を実行""" session = load_session() if session is None: raise AuthenticationError("ログインが必要です") if not session.is_valid(): raise SessionExpiredError() try: return await operation() except SessionExpiredError: # セッションをクリアして再認証を促す keyring.delete_password(SERVICE_NAME, "session") raise
実装チェックリスト
MCPツール作成時
- Pydanticモデルで入力を定義
- Fieldに説明を付与
- ツール定義にdescriptionを記載
- 適切なエラーレスポンスを実装
Playwright統合時
- ブラウザライフサイクルを管理
- 作業ウィンドウを再利用
- クリーンアップ処理を実装
- エラー時のリカバリを考慮
セッション管理時
- OSのセキュアストレージを使用
- 有効期限チェックを実装
- 期限切れ時のエラーメッセージを明確に
- 再認証フローを提供
参考リソース
- MCP Protocol仕様: https://modelcontextprotocol.io/
- Pydantic v2ドキュメント: https://docs.pydantic.dev/
- Playwright Pythonドキュメント: https://playwright.dev/python/
注意事項
- note.comの非公式APIを使用するため、仕様変更で動作しなくなる可能性あり
- レート制限を遵守(目安: 10リクエスト/分)
- 自己責任・無保証での公開を前提