Claude Media
Claude CodeにVal Townプラグインを入れてHTTP valとcronを作る

Claude CodeにVal Townプラグインを入れてHTTP valとcronを作る

Val Townプラグインは、ホスト型MCPサーバーとHTTP val・cron・SQLite・メールなどのスキルを1コマンドで入れます。導入と認証、作らせ方、確認の型をまとめます。

Val Townプラグインは、Val Townのホスト型MCPサーバーと、Val Town向けの作法をまとめたスキル群を1コマンドで入れるプラグインです。入れたあとは、Claude Codeに「HTTPで応答するval」や「毎朝動くcron」を頼むだけで、作成から実行確認までMCPツール経由で進められます。ここでは導入と認証、作らせ方、確認の型、つまずきやすい点を順に扱います。

このプラグインに入っているものは何か

プラグインの中身は2つです。1つはホスト型のMCPサーバー(https://api.val.town/v3/mcp)への接続設定、もう1つはMarkdownで書かれたスキルです。スキルには、HTTP val、cron、SQLite、メール、OAuthのほか、React UIや外部サービス連携の作法が入っています。

作成元はVal Town自身で、Claude Code用のほかにCodexとCursor向けも同じリポジトリから配られます。ライセンスはMITです。

リポジトリのスキルフォルダには、次のような名前が並んでいます。

スキル使いどころ
http-endpoints使いどころHTTPで応答するval。ハンドラの形、Hono、CORS、リダイレクト
cron-and-intervals使いどころ定期実行。cron式、UTC、前回実行時刻の扱い
sqlite-storage使いどころ構造化データの保存。std/sqlite、トランザクション
blob-storage使いどころJSONや画像などのキー・バリュー保存
email使いどころメール受信で動くvalと、std/emailでの送信
oauth使いどころVal Townアカウントでのログイン付与
restricted-access使いどころHTTPエンドポイントの公開範囲を絞る
react-ui使いどころ画面付きvalの書き方
third-party-integrations使いどころSlack、Stripe、GitHubなど外部APIとの接続

スキルのdescriptionは「Use when…」で始まる書式で、Claudeがどのスキルを読むかを決める手がかりになります。つまり、依頼文に「cron」や「webhook」が入っていれば、該当スキルが読み込まれやすくなります。

導入して認証するまでの手順

導入は2段階です。プラグインを入れ、次にVal Townアカウントの認証をブラウザで済ませます。

手順

導入から接続確認までの流れ

  1. 1

    プラグインを入れる

    ターミナルで下のコマンドを実行します。Claude Codeのセッション内なら/plugin installでも入ります。

  2. 2

    プラグインを読み込む

    セッションで/reload-pluginsを実行するか、Claude Codeを再起動します。

  3. 3

    OAuthで認証する

    /mcpでVal Townのサーバーを選び、表示されたリンクからサインインして許可します。

  4. 4

    接続を確かめる

    新しいセッションで、作成や変更を伴わない依頼を1つ送ります。

claude plugin install valtown@claude-plugins-official

セッション内で入れるときは、スラッシュコマンドの形になります。

/plugin install valtown@claude-plugins-official
/reload-plugins
/mcp

認証はOAuthなので、APIキーを控えて貼る作業はありません。MCPツールを初めて使った時点でブラウザが開く流れで、/mcpから先に済ませておくこともできます。

接続確認には、書き込みの起きない依頼を使います。

Val Townを使って、自分のvalの一覧を出して。何も作成・変更しないで。

valが0件でも、一覧が返れば接続は成功です。Claudeがツールを呼ばず手順の説明だけで返してきたら、「Val Townのツールを使って」と言い直します。

HTTP valを作らせる

最初の題材はHTTP valが向いています。ハンドラを1つ書けば、そのまま公開URLで動くからです。

依頼文には、何を返すか、どう確かめるかを入れます。

Val TownでHTTP valを作って。GET /healthでJSONの
{ ok: true } を返す。作ったらエンドポイントに
実際にリクエストして、レスポンスを見せて。

スキルが示すハンドラの基本形は次のとおりです。

export default async function (req: Request): Promise<Response> {
  return Response.json({ ok: true });
}

Honoを使う場合は、appではなくapp.fetchをexportします。ここを間違えるとハンドラとして動きません。

import { Hono } from "npm:hono";
 
const app = new Hono();
app.get("/health", (c) => c.json({ ok: true }));
 
// エラー時にスタックトレース全体を残す
app.onError((err) => Promise.reject(err));
 
export default app.fetch;

スキルには、Val Town固有の制約も書かれています。依頼文やCLAUDE.mdに重ねて書かなくても、スキルが読まれれば反映される部分です。

  • 公開URLはClaudeが組み立てず、list_filesやcreate_fileの応答のlinks.endpointを使う
  • Response.redirectは使えないため、Locationヘッダー付きのResponseかHonoのc.redirectで代える
  • CORSヘッダーは既定で付くので、自分で1つでも設定すると既定分がすべて外れる
  • WebSocketの受信はできないため、ポーリングかServer-Sent Eventsにする
  • HonoのserveStaticは使えない

スキルにはもう1つ、「編集したら実際にfetchして確かめる」という規則があり、これが作らせ方の核です。スキルには、HTTP valを直したらレスポンスを取得して確認し、確認なしに完了を報告しない旨が書かれています。依頼文にも同じ一文を入れておくと、確認の抜けが減ります。

cron valを定期実行で動かす

定期実行はinterval型のvalで、cron式で周期を決めます。注意点は時刻の扱いです。

cron式はUTCで評価されます。サマータイムは考慮されないので、「日本時間の朝9時」を頼むときは、UTCの0時に換算した式になります。依頼文に時刻を書くときは、タイムゾーンも添えます。

Val Townで毎朝日本時間の9時に動くcron valを作って。
UTCに換算した式を使い、前回実行以降に追加された
項目だけを処理する。作成後にrun_fileで1回動かして。

スキルが示す差分処理の型は、前回の実行時刻を使うものです。

export default async function (interval: Interval) {
  const since = interval.lastRunAt ?? new Date(Date.now() - 24 * 60 * 60 * 1000);
  const newItems = await fetchItemsSince(since);
  for (const item of newItems) {
    await handle(item);
  }
}

lastRunAtは前回成功した実行の時刻で、初回はundefinedです。この形にしておくと、実行が飛んだ日があっても取りこぼさず、同じ項目を再処理することも避けられます。

動作確認は次の実行を待たず、run_fileでハンドラを手動で呼びます。周期の変更や一時停止はwrite_interval_settings、現在の設定の確認はread_interval_settingsの担当です。

SQLite・メール・OAuthを足す

HTTP valとcronに慣れたら、保存・受信・認証を足していきます。どれも、スキルが読まれる前提で依頼文を短くできます。

SQLiteで状態を持たせる

std/sqliteのsqlite.executeで読み書きし、複数文を1つのトランザクションにまとめるときはsqlite.batchを使います。ユーザー入力に由来する値はargsで渡し、SQLに文字列を埋め込みません。

import { sqlite } from "https://esm.town/v/std/sqlite/main.ts";
 
await sqlite.execute(`CREATE TABLE IF NOT EXISTS notes (
  id INTEGER PRIMARY KEY,
  body TEXT NOT NULL
)`);
 
await sqlite.execute({
  sql: "INSERT INTO notes (body) VALUES (?)",
  args: ["最初のメモ"],
});

main.tsはそのval専用のデータベース、global.tsは同じアカウントの全valで共有されるデータベースを指します。既存のvalでimport先を切り替えると、読み書きする先が変わるので注意が必要です。

メールで動かす

メール型のvalは、受信するとEmailオブジェクトを受け取ります。受信アドレスは応答のlinks.emailから読み、Claudeに組み立てさせません。1通は添付込みで30MBまでです。送信はstd/emailのemail関数を直接呼びます。

ログインを付ける

Val Townアカウントでのログインを求めるなら、std/oauthのミドルウェアを使います。SlackやGoogleなど外部サービスの認証は別の話で、third-party-integrationsスキルの範囲です。

公開範囲を絞ったvalで起きること

HTTP valのURLは、既定では誰でもアクセスできます。公開範囲を絞る設定(httpPrivacyをrestrictedにする)は、この機能が有効な組織向けのもので、有効な組織で作ったvalは最初からrestrictedになっていることがあります。

絞ったvalでは、未認証のリクエストはvalに届く前に、ログイン画面への302で返されます。次の場面で混乱しやすい点です。

  • fetch_val_endpointがリダイレクトを追わず、そこで止まる
  • curlで試すと、応答の代わりにval.townへの302が返る
  • APIクライアントがJSONを期待しているのに、ログイン画面のHTMLが返る

どれもコードの不具合ではありません。まずhttpPrivacyの値を確かめます。外部のwebhookを受けたい場合は、そのval専用のバイパストークン(create_bypass_token)を作り、X-Val-Town-Accessヘッダーで渡します。トークンの値は作成時に一度しか表示されません。

oauthスキルのstd/oauthとrestricted-accessは役割が違います。前者はval内部で動くログイン、後者は入口で止める仕組みです。重ねて入れると、訪問者が二重に認証することになります。

動かないときの切り分け

接続まわりの不調は、次の順に確かめると原因を絞れます。

切り分け

Val Townのツールが使えないときの確認順

  1. Qプラグインを入れたあと、/reload-pluginsか再起動をしたか?
    答えがまだ

    /reload-pluginsを実行するか、Claude Codeを再起動します。

    答えがはい
  2. Q/mcpにVal Townのサーバーが出ているか?
    答えが出ていない

    導入に失敗しています。Claude Codeのバージョンと、組織のプラグインポリシーを確認します。

    答えがはい
  3. Qサーバーの認証は完了しているか?
    答えが未完了

    /mcpでサーバーを選び、サインインして許可します。

    答えがはい

それでも使われないとき

新しいセッションで「Val Townのツールを使って」と明示して依頼します。

プラグインの導入そのものが失敗する場合は、ターミナルのコマンドとセッション内のスラッシュコマンドを取り違えていないかを見ます。/plugin installはClaude Codeの中、claude plugin installは外で実行するものです。

組織の管理設定でプラグイン以外のMCP追加が止められている環境では、claude mcp addが失敗します。その場合の原因と対処はclaude mcp addが失敗する理由で扱っています。

MCPだけを入れる選択肢と、二重接続の落とし穴

スキルが不要で、接続だけ欲しい場合は、MCPのみの追加ができます。

claude mcp add --transport http --scope user \
  val-town https://api.val.town/v3/mcp

--scope userを付けると、ローカルの全プロジェクトで使えます。入れたあとは/mcpでval-townを選んでサインインします。

ここで気をつけたいのは二重接続です。プラグインにはMCPサーバーの設定が含まれているため、プラグインを入れたうえでclaude mcp addも実行すると、同じサーバーが2系統で並びます。どちらか一方だけを残します。

プラグインがコンテキストをどれだけ使うかはclaude plugin detailsでの測り方で確かめられます。

Claude側に持たせる運用ルール

スキルが読まれても、確認の抜けや時刻の取り違えは起こりえます。プロジェクトのCLAUDE.mdに、次のような短い規約を置いておくと、毎回の依頼文を短くできます。公式の例ではなく、スキルの内容をもとにした筆者の書き方です。

## Val Town
- valを作成・編集したら、必ずエンドポイントをfetchするか
  run_fileで実行して結果を示してから完了を報告する
- cronはUTCで評価される。日本時間で依頼されたらUTCに換算した式を書く
- 公開URL・受信メールアドレスは応答の links から読み、自分で組み立てない
- 外部サービスのシークレットはコードに書かず、環境変数で渡す

この規約は、スキル側にある「確認してから報告する」「URLは組み立てない」「cronはUTC」を、プロジェクトの言葉で言い直したものです。スキルが読まれなかった場合の保険にもなります。

プラグインの仕組みとMCP自作への道

プラグインの仕組みそのもの、つまりマーケットプレイスの登録や自作はClaude Codeプラグイン完全ガイドにまとまっています。MCPサーバーを自分で作る側に回るなら、mcp-server-devプラグインが入口になります。

この記事を共有:XはてブLinkedIn