mcpField
S
slack-agent

slack-agent

github.com/vercel-labsエージェントワークフロー23834 回インストール15 週間前に更新
リポジトリ

slack-agent について

eve と Vercel Connect を使った Slack エージェント開発を支援するスキルです。プロジェクトの作成、設定、テスト、デプロイに加え、Slack イベント、スレッドコンテキスト、人間による承認フローを扱います。

主な機能

  1. 01Slack エージェントの作成ウィザード
  2. 02Vercel Connect による認証情報の設定
  3. 03Slack イベントとスレッドコンテキストの処理
  4. 04承認インタラクションとプロアクティブセッション
  5. 05コード品質とテスト要件

活用シーン

  1. 01Slack エージェントを新規作成する
  2. 02既存の eve プロジェクトに Slack チャネルを追加する
  3. 03イベント処理やスレッドコンテキスト、ツール承認を設定する
  4. 04Slack エージェントをデプロイしてテストする

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/vercel-labs/slack-agent-skill --skill slack-agent

インストール後、Claude Code で/slack-agentを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

LICENSE
README.md
.gitignore
patterns/
testing-patterns.md
slack-patterns.md
reference/
vercel-setup.md
agent-archetypes.md
env-vars.md
slack-setup.md
templates/eve/
test-setup.ts
vitest.config.ts
templates/eve/sample-tests/
agent.test.ts
tools.test.ts
wizard/
2-create-slack-app.md
1b-approve-plan.md
5-deploy-production.md
1-project-setup.md
3-configure-environment.md
6-setup-testing.md
4-test-locally.md
README.md

詳細

Slack エージェント開発スキル

このスキルでは、Slack の認証情報に Vercel Connect を使用して、eve(永続性のあるバックエンドエージェント向けの、ファイルシステム優先の Vercel フレームワーク)で Slack エージェントを構築します。

  • eve(eve パッケージ)— エージェントのランタイム、ツール、チャネル、永続性
  • @vercel/connect — ブローカー経由の短期間有効な Slack トークン。ボットトークンや署名シークレットを管理する必要はありません

スキル呼び出しの処理

このスキルが /slack-agent 経由で呼び出されたときは、引数を確認して適切な処理に振り分けます。

コマンド引数
引数アクション
newフェーズ 1 からセットアップウィザードを実行します。 ./wizard/1-project-setup.md を読み、新しい Slack エージェントの作成をユーザーに案内します。
configure既存のプロジェクト向けに、フェーズ 2 または 3 からウィザードを開始します
deploy本番環境へのデプロイに向けて、フェーズ 5 からウィザードを開始します
testテストをセットアップするため、フェーズ 6 からウィザードを開始します
(引数なし)プロジェクトの状態に基づいて自動検出します(以下を参照)
自動検出(引数なし)

引数なしで呼び出された場合は、プロジェクトの状態を検出し、適切な処理に振り分けます。

  1. eve を含む package.json がなく、agent/ ディレクトリもない → new として扱い、フェーズ 1 を開始します
  2. eve プロジェクトはあるが agent/channels/slack.ts がない → フェーズ 2(Slack コネクターとチャネル)を開始します
  3. Slack チャネルはあるが SLACK_CONNECTOR が設定されていない → フェーズ 3 を開始します
  4. 設定済みだがデプロイされていない → フェーズ 5 を開始します(Slack サーフェスが動作するのはデプロイ後のみです)
  5. デプロイ済みだがテストがない → フェーズ 6 を開始します
  6. それ以外 → このスキルのパターンを使って、一般的な支援を提供します
プロジェクトの検出

以下のいずれかの条件で eve プロジェクトを検出します。

  • package.json に依存関係として "eve" が含まれている
  • instructions.md および/または agent.ts を含む agent/ ディレクトリがある

どちらにも該当しない場合は新規プロジェクトです。npx eve@latest init でひな形を作成します(Node 24 以降が必要です)。

ウィザードのフェーズ

ウィザードは ./wizard/ にあり、次のフェーズで構成されています。

  • 1-project-setup.md - 目的を把握し、カスタム実装計画を生成して、npx eve@latest init でひな形を作成します
  • 1b-approve-plan.md - ひな形を作成する前に、ユーザーに計画を提示して承認を得ます
  • 2-create-slack-app.md - Vercel Connect で Slack コネクターを作成し、Slack チャネルを追加します
  • 3-configure-environment.md - 環境変数(SLACK_CONNECTOR、モデルの認証情報)を設定します
  • 4-test-locally.md - eve dev TUI でエージェントのロジックをローカルでテストします(Slack サーフェスのテストはデプロイ後に行います)
  • 5-deploy-production.md - eve deploy でデプロイし、Slack サーフェスを検証します
  • 6-setup-testing.md - Vitest の設定

重要: new プロジェクトの場合、必ず次の手順を実行してください。

  1. まず ./wizard/1-project-setup.md を読みます
  2. どのようなエージェントを作りたいかユーザーに尋ねます
  3. ./reference/agent-archetypes.md を使ってカスタム実装計画を生成します
  4. プロジェクトのひな形を作成する前に、フェーズ 1b で計画を提示して承認を得ます
  5. 計画が承認された後にのみ、ひな形の作成に進みます

開発全般のガイダンス

eve で構築された Slack エージェントプロジェクトを開発します。すべてのコード変更で、次の必須プラクティスに従ってください。

プロジェクトの技術スタック

  • フレームワーク:eve(ファイルシステム優先のエージェントフレームワーク。Node 24 以降)
  • Slack チャネル:eve/channels/slack + 認証情報用の @vercel/connect
  • AI:デフォルトでは Vercel AI Gateway 経由でモデルをルーティングします(anthropic/claude-sonnet-5)。ツールのスキーマには zod を使用します
  • 永続性:内部では Workflow SDK を使用します(Vercel にデプロイする場合は Vercel Workflows)
  • リンティング:Biome
  • パッケージマネージャー:pnpm(または npm — npx eve@latest init は npm でインストールします)
{
  "engines": { "node": "24.x" },
  "dependencies": {
    "eve": "latest",
    "ai": "latest",
    "zod": "^3.x",
    "@vercel/connect": "latest"
  }
}
ファイルシステム優先のレイアウト

eve では、ファイルの場所からその役割がわかり、通常はパスからファイル名もわかります。エージェント全体は agent/ 以下に配置されます。

agent/
├── instructions.md        # Always-on system prompt
├── agent.ts               # Runtime config (defineAgent): model, reasoning, compaction
├── tools/                 # Tools — filename (snake_case ASCII) = tool name the model sees
│   ├── get_weather.ts
│   └── search_docs.ts
├── skills/                # Load-on-demand instructions (*.md with description frontmatter)
│   └── incident-triage.md
├── channels/
│   └── slack.ts           # Slack channel — filename registers it at /eve/v1/slack
├── connections/           # MCP / OpenAPI connections (optional)
└── hooks/                 # Subscribe to runtime stream events (optional)

2 ファイル(instructions.md + agent.ts)だけのエージェントでも、デフォルトのハーネスにより、ファイル、シェル、ウェブ、委任の各ツールが最初から利用できます。eve のインストール後、完全なドキュメントが node_modules/eve/docs/ に同梱されます。ここで説明されていない詳細については、そちらを読んでください。

// agent/agent.ts
import { defineAgent } from "eve";

export default defineAgent({
  model: "anthropic/claude-sonnet-5", // routed via Vercel AI Gateway
});

品質基準(必須)

これらの品質要件は、すべてのコード変更で必ず守ってください。例外はありません。

ファイルを変更するたびに
  1. 直ちにリンターを実行します:

    pnpm lint
    
    • エラーがある場合は、pnpm lint --write を実行して自動修正します
    • 残りの問題を手動で修正します
    • pnpm lint を再実行して確認します
  2. 対応するテストファイルを確認します:

    • foo.ts を変更した場合は、foo.test.ts があるか確認します
    • テストファイルがなく、そのファイルが関数をエクスポートしている場合は、新しく作成します
タスクを完了する前に

タスクを完了としてマークする前に、必ずすべての品質チェックを実行し、問題があれば修正してください:

# 1. TypeScript compilation - must pass
pnpm typecheck

# 2. Linting - must pass with no errors
pnpm lint

# 3. Tests - all tests must pass
pnpm test

これらのいずれかに失敗した場合は、タスクを完了しないでください。 まず問題を修正してください。

ユニットテストが必須です

コードを変更する場合は、必ずユニットテストを作成または更新してください。

  • 配置場所:同じディレクトリにある *.test.ts ファイル(例:agent/tools/get_weather.test.ts)
  • フレームワーク:Vitest
  • カバレッジ:すべてのエクスポートされた関数と、すべてのツールの execute()(エラーパスを含む)をテストする必要があります

テスト構成の例:

import { describe, it, expect, vi } from 'vitest';
import { myFunction } from './my-module';

describe('myFunction', () => {
  it('should handle normal input', () => {
    expect(myFunction('input')).toBe('expected');
  });

  it('should handle edge cases', () => {
    expect(myFunction('')).toBe('default');
  });
});
ユーザー向けの変更には E2E テストが必要です

次を変更する場合:

  • ディスパッチフック(onAppMention、onDirectMessage、onInteraction)
  • カスタムチャネルのイベントハンドラー
  • Slack メッセージへの応答としてエージェントが呼び出すツール
  • 配信動作(Slack に投稿される内容)

フロー全体を検証するテストを必ず追加または更新してください。Slack の画面自体はローカルで操作できないことに注意してください(「注意点」を参照)。そのため、E2E カバレッジには、ハンドラー周辺のユニットテストまたは統合テストに加え、デプロイ後のスモークテストが含まれます。


ボットのセットアップパターン(重要)

Slack チャネル(agent/channels/slack.ts)

Slack チャネルは単一のファイルです。ファイル名によって slack チャネルが登録され、/eve/v1/slack で配信されます。これは、このスキル内のあらゆる場所で使用される標準のトリガーパスです。

// agent/channels/slack.ts
import { connectSlackCredentials } from "@vercel/connect/eve";
import { slackChannel } from "eve/channels/slack";

export default slackChannel({
  credentials: connectSlackCredentials(process.env.SLACK_CONNECTOR!),
});

connectSlackCredentials(connectorUid) は { botToken, webhookVerifier } を返します:

  • botToken — 実行時に Vercel Connect を通じて解決される、短期間有効なアプリスコープのトークンです。Connect がローテーションと複数ワークスペースのテナント管理を処理します
  • webhookVerifier — 転送された各イベントが実際に Connect から送信されたことを確認します(Slack 標準の署名チェックに代わるものです)

このスタックには SLACK_BOT_TOKEN も SLACK_SIGNING_SECRET もありません。Slack 関連の環境変数は SLACK_CONNECTOR(コネクター UID。例:slack/my-agent)のみです。

Vercel Connect のセットアップ

Slack コネクターを作成し、そのトリガーを eve の Slack ルートに設定します:

npm install -g vercel@latest

# Create the connector with event forwarding enabled
vercel connect create slack --triggers

# Attach the project as a trigger destination on eve's route
# (the default trigger path is /slack — set it explicitly):
vercel connect attach <uid> --triggers --trigger-path /eve/v1/slack --yes

--triggers は必須です。 指定しないと、Slack のイベントサブスクリプションが転送されず、app_mention / message.im イベントがまったく届きません。デプロイ自体は正常に見えても、ボットは応答しません。

eve channels add slack を実行してチャネルを追加することもできます。このコマンドを実行すると、agent/channels/slack.ts が自動生成されます。

デプロイ
eve deploy
# wraps: vercel deploy --prod

次に、ボットをチャネルに招待して、@メンションしてください。Slack の ACK セマンティクス、URL 検証、バックグラウンド処理は eve が処理するため、独自に記述する Webhook ルートはありません。


イベント処理パターン

ディスパッチフック(受信)

Slack チャネルはディスパッチフックを使って、どの受信イベントでセッションを開始または継続するかを決定します。各フックは、ディスパッチする場合は { auth }、イベントを破棄する場合は null、セッションにバックグラウンドコンテキストを注入する場合は { auth, context } を返します:

export default slackChannel({
  credentials: connectSlackCredentials(process.env.SLACK_CONNECTOR!),

  // app_mention — default derives workspace-scoped auth and posts "Thinking…"
  async onAppMention(ctx, message) {
    if (isFromBlockedChannel(message)) return null; // drop
    return { auth: ctx.defaultAuth };
  },

  // message.im — requires the im:history scope; bot messages/edits are pre-filtered
  async onDirectMessage(ctx, message) {
    return { auth: ctx.defaultAuth };
  },

  // block_actions not consumed by HITL prompts
  async onInteraction(action, ctx) {
    return { auth: ctx.defaultAuth };
  },
});

トリガーとなった Slack ユーザーの ID は、話者の帰属を複数ユーザーのスレッドでも維持できるよう、モデルメッセージに自動的に付与されます。

カスタムイベントハンドラー(送信配信)

events マップを使って、ストリームイベントごとに配信を上書きします。ハンドラーは (eventData, channel, ctx) を受け取り、channel.thread と channel.slack のハンドルを利用できます:

export default slackChannel({
  credentials: connectSlackCredentials(process.env.SLACK_CONNECTOR!),
  events: {
    "message.completed"(eventData, channel, ctx) {
      if (eventData.finishReason === "tool-calls") return;
      if (eventData.message) channel.thread.post(eventData.message);
    },
  },
});

主なストリームイベント:session.started、actions.requested、action.result、message.completed、session.completed。増分イベントの reasoning.appended / message.appended は任意です。

スレッドのコンテキスト

スレッドの途中でエージェントが起動された場合は、それ以前のメッセージをエージェントに渡します。

export default slackChannel({
  credentials: connectSlackCredentials(process.env.SLACK_CONNECTOR!),
  threadContext: { since: "last-agent-reply" },
});

since オプション:

  • "thread-root" — それ以前のすべてのメッセージ(スレッドのコンテキストが有効な場合のデフォルト)
  • "last-agent-reply" — 増分取得。エージェントが最後に発言してからのメッセージのみ
  • カスタムの区切り条件としての述語 (message: SlackThreadMessage) => boolean — 最後に一致したメッセージ以降のメッセージを含みます(任意のフィルタリングには loadThreadContextMessages を使用できます)

コスト:トリガーとなった返信ごとに conversations.replies API を1回呼び出します。コネクターに対応する履歴スコープが必要です。

Human-in-the-Loop(HITL)

承認が必要なツール呼び出しとサインインのチャレンジは、Slack 上でネイティブに表示されます。

  • 承認プロンプトはボタン/セレクトメニューとして表示され、ユーザーの応答後、永続的に一時停止されていたセッションが再開されます
  • サインインのチャレンジ(OAuth URL、デバイスコード)は、トリガーとなったユーザーにのみエフェメラルメッセージで送信されます。公開のステータスメッセージがスレッドに投稿され、authorization.completed で更新されます
  • HITL ハンドラーのコンテキストで利用できるのは意図的に postEphemeral、postDirectMessage(im:write が必要)、および state のみです。公開の post や生の API アクセスは利用できません
プロアクティブセッション(スケジュール)

スケジュールなどを使って、受信イベントをトリガーとせずに Slack に投稿するセッションを開始します。

import { receive } from "eve";
import slack from "../channels/slack";

await receive(slack, {
  message: "Post the daily standup summary for #eng.",
  target: { channelId: "C0123456789" },
  auth,
});
  • threadTs のないセッションには一時的な継続トークンが付与されます。最初の投稿でスレッドが作成されます
  • initialMessage(省略可能な Card)と threadTs は同時に指定できません
  • 一定間隔でプロアクティブセッションをトリガーするには、eve のスケジュールを使用します(https://eve.dev/docs/schedules を参照)
Slack API への直接アクセス
  • ハンドラー内:ctx.slack.request(operation, body)
  • ハンドラー外(スケジュール、ツール):eve/channels/slack の callSlackApi({ botToken, operation, body }) と resolveSlackBotToken

これらはリクエストボディをフォームエンコードします。Slack の JSON サポートは一部に限られるため、自前で fetch を実装するより、これらのヘルパーを優先してください。


実装時の注意点

1. Slack の画面はローカルでテストできない

Vercel Connect は Slack イベントをデプロイ済みの環境にのみ転送し、localhost には転送しません。このスタックでは ngrok/Socket Mode による回避策もありません。ローカル開発では、次の方法を使用します。

  • npx eve dev — エージェントのロジック、ツール、スキルを試すための HMR サーバーと、ターミナルの TUI/REPL
  • eve dev --no-ui — スクリプトによる検証用のバックグラウンドモード
  • eve dev https://your-app.vercel.app — デプロイ済みのアプリを対話的に操作

@メンションと DM をテストするには、プレビューまたは本番環境にデプロイし、実際の Slack でテストしてください。

2. Connect の転送ではイベントの重複配信が排除されない

Connect は同じ転送イベントを複数回配信することがあります。ハンドラーと副作用は冪等でなければなりません。重複によって問題が起こる場合は、処理済みのイベント ID を記録し、破壊的なツール操作には承認を必須にしてください(AI Integration を参照)。

3. --triggers が必要です。指定しないとイベントは届きません

--triggers を付けずに作成した Slack コネクター、またはトリガーパスを指定せずにアタッチしたコネクターは、認証には成功しますが、何も転送しません。ボットが @メンションに応答しない場合は、次の点を確認してください。

  1. コネクターの作成時またはアタッチ時に --triggers を指定したこと
  2. トリガーパスが /eve/v1/slack であること
  3. ボットがチャンネルに招待されており、デプロイが完了していること
4. 本番環境では placeholderAuth() はすべてのリクエストを拒否します

スキャフォールドされたプロジェクトの HTTP API には placeholderAuth() が含まれており、本番環境ではすべてのリクエストを拒否します。デプロイする前に、実際の認証関数で置き換えてください。使用できる関数は httpBasic()、jwtHmac()、jwtEcdsa()、oidc()、vercelOidc()、またはカスタムの AuthFn です。(Slack チャンネルへの受信イベントの検証は別であり、Connect の webhookVerifier が処理します。)

5. サンドボックスの事前ウォームアップに失敗するとビルドも失敗します

Vercel のビルドでは eve のサンドボックステンプレートが事前にウォームアップされます(キャッシュキーを使用し、ビルドログに reused cached または built と表示されます)。事前ウォームアップに失敗すると、ビルド全体が失敗します。コードの問題だと判断する前に、ビルドログでサンドボックステンプレートのエラーを確認してください。

6. プライベートチャンネルへのアクセス

ボットは、招待されていないプライベートチャンネルのメッセージを読んだり、投稿したりできません。チャンネルに後から投稿する機能(スケジュールに基づくプロアクティブセッションなど)を作成する場合は、事前にアクセスを検証し、channel_not_found/not_in_channel の際に「ボットを招待してください」というメッセージをわかりやすく表示してください。

7. チャンネルコンテキストのグレースフルデグラデーション

AI 機能でチャンネルコンテキストを取得する場合(例:ctx.slack.request("conversations.history", ...))、try/catch で囲み、適切にフォールバックしてください。スコープの不足や、チャンネルに招待されていないことは例外的な事態ではなく、よくあることです。

8. Vercel Cron エンドポイントの認証

独自の cron エンドポイント(eve のスケジュール以外)を追加する場合は、CRON_SECRET で保護してください。

export async function GET(request: Request) {
  const authHeader = request.headers.get("authorization");
  if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
    return Response.json({ error: "Unauthorized" }, { status: 401 });
  }
  // Run cron job logic...
  return Response.json({ success: true });
}
9. vercel.json の Cron 設定
{
  "crons": [
    {
      "path": "/api/cron/my-job",
      "schedule": "0 * * * *"
    }
  ]
}

エージェント主導の定期的な処理には eve のスケジュールを優先し、単純な HTTP ジョブには Vercel の cron を使用してください。

10. Vercel 上の AWS 認証情報(OIDC を使用)

Vercel から AWS サービスに接続する場合、fromNodeProviderChain() は使用しないでください。Vercel の OIDC メカニズムを使用してください。

import { awsCredentialsProvider } from "@vercel/functions/oidc";

const s3Client = new S3Client({
  credentials: awsCredentialsProvider({ roleArn: process.env.AWS_ROLE_ARN! }),
});

AI の統合

モデルの設定(Gateway 優先)

eve はモデル ID の文字列を Vercel AI Gateway 経由でルーティングします。Vercel 上ではプロジェクトの OIDC によって自動的に認証されるため、AI API キーは不要です。

// agent/agent.ts
import { defineAgent } from "eve";

export default defineAgent({
  model: "anthropic/claude-sonnet-5", // string → AI Gateway → OIDC auth on Vercel
});

model を省略した場合、eve はデフォルトで anthropic/claude-sonnet-5 を使用します。Vercel 以外では、AI_GATEWAY_API_KEY を設定してください。

重要:モデル ID を記憶に頼って使用しないでください。 モデル ID は頻繁に変更されます。モデルを固定するコードを書く前に、curl -s https://ai-gateway.vercel.sh/v1/models を実行して最新の一覧を取得し、用途に適した最新バージョンを使用してください。

ツール(agent/tools/*.ts)

ツールはファイルとして定義します。ファイル名(スネークケースの ASCII 文字列)がモデルに提示されるツール名になります。agent/tools/get_weather.ts → get_weather。登録手順はありません。

// agent/tools/get_weather.ts
import { defineTool } from "eve/tools";
import { z } from "zod";

export default defineTool({
  description: "Get the current weather for a city.",
  inputSchema: z.object({ city: z.string().min(1) }), // required, even if empty
  outputSchema: z.object({
    city: z.string(),
    condition: z.string(),
    temperatureF: z.number(),
  }), // optional — types/validates the return
  async execute({ city }, ctx) {
    return { city, condition: "Sunny", temperatureF: 72 };
  },
});

ルールと機能:

  • ツールはサンドボックス内ではなく、process.env にすべてアクセスできるアプリのランタイムで実行されます
  • inputSchema は Zod、Standard Schema、または JSON Schema を受け付けます。ただし、入力がないツールでも必須です
  • 出力は JSON にシリアライズ可能でなければなりません。返す前にシークレットをフィルタリング/秘匿化してください
  • ctx には ctx.session(メタデータ、ターン、認証、系譜)、ctx.callId、ctx.toolName、ctx.abortSignal、ctx.getSandbox()、ctx.getSkill(id) が用意されています
承認ゲーティング

リスクのあるツールは approval フィールドでゲートしてください。ヘルパーは eve/tools/approval から利用できます。

import { defineTool } from "eve/tools";
import { always, once, never } from "eve/tools/approval";
import { z } from "zod";

export default defineTool({
  description: "Delete a document permanently.",
  inputSchema: z.object({ documentId: z.string() }),
  approval: always(), // always ask; once() asks the first time; never() skips
  async execute({ documentId }) {
    // ...
  },
});

入力に依存するポリシー関数もサポートされています。ゲートされた呼び出しは一時停止し、永続的に再開できます。Slack では、承認はボタンとして表示されます(上記の HITL を参照)。冪等でない副作用を伴う処理では、場当たり的な確認ロジックより承認ゲーティングを優先してください。

toModelOutput — モデルには見せない、リッチな Slack 出力

モデルには簡潔な投影を表示する一方で、チャンネル/フックには action.result で完全な出力を渡します。会話コンテキストに JSON ブロックを詰め込まずに、ツールの結果からリッチな Slack Block Kit を描画するのに適しています。

export default defineTool({
  description: "Look up an order.",
  inputSchema: z.object({ orderId: z.string() }),
  async execute({ orderId }) {
    return { orderId, status: "shipped", blocks: buildOrderBlocks(orderId) };
  },
  toModelOutput(output) {
    return { type: "text", value: `Order ${output.orderId}: ${output.status}` };
  },
});
スキル(agent/skills/*.md)

description のフロントマターを含む Markdown ファイルです。リクエストが説明に一致したとき、組み込みの load_skill ツールを通じて必要に応じて読み込まれます。スキルが追加するのは指示のみで、新しいアクションは追加しません。公開済みのスキルは npx skills add <owner>/<repo> でインストールしてください。

コネクション(agent/connections/*.ts)

エージェントが操作する外部 API(MCP サーバー、または OpenAPI で記述された HTTP API)には、Connect が仲介する認証を使うコネクションを使用してください。

// agent/connections/linear.ts
import { connect } from "@vercel/connect/eve";
import { defineMcpClientConnection } from "eve/connections";

export default defineMcpClientConnection({
  url: "https://mcp.linear.app/mcp",
  description: "Linear workspace: issues, projects, cycles, and comments.",
  auth: connect("linear/my-agent"),
});

コネクショントークンはモデルに渡されず、会話履歴にも保存されません。作成したツール内でトークンを解決するには await ctx.getToken(connect("...")) を使用し、下流で 401 が返された場合に同意を再度求めるには ctx.requireAuth(...) を呼び出してください。

LLM 呼び出しをツールでラップしないでください。 エージェント自体がすでに言語モデルです。要約、解析、分類、文案作成は、AI SDK を呼び出すツールではなく、instructions.md またはスキルに記述してください。ツールはデータの取得やアクションの実行に使います。会話に収まりきらない大量のデータを処理する場合は、eve のサブエージェント/委譲を使用してください。

状態と耐久性

eve のセッションは、オープンソースの Workflow SDK(Vercel にデプロイした場合は Vercel Workflows 上で実行)によって、デフォルトで耐久性を備えています。Redis やワークフローエンジンを自分で接続する必要はありません。

リプレイのセマンティクス(理解しておくこと)
  • 完了済みのステップは再実行されません。 再開/リプレイ時に、eve は記録済みの結果を返します。
  • 実行途中で中断されたステップは再実行されます。 プロセスが停止したときにツール呼び出しが実行中だった場合、再開時に再度実行されます。

コードへの影響:

  1. 冪等でない副作用は冪等にしてください(例:外部システムへの書き込みには、Slack のイベント ID または独自の冪等性キーを使用します)。
  2. または、承認(always()/once())の後に実行するようにして、リプレイされたステップで二重実行する代わりに人間の承認を待つようにしてください。
  3. これは Connect の少なくとも 1 回のイベント転送(注意点 #2)と重なるため、このスタックでは冪等性は必須です。
セッションの継続性
  • continuationToken はチャネルが管理します。Slack のスレッドは自動的にセッションに対応付けられます。
  • サブスクライブ済みのスレッドへのフォローアップメッセージは、同じ耐久性のあるセッションを継続します。
  • マルチターン会話のメモリはセッション自体から取得されるため、チャット履歴を手動で永続化する必要はありません。
推奨されるストレージソリューション

アプリケーションデータ用(エージェントのセッション状態ではありません。これは eve が管理します):

重要: Vercel KV は非推奨になりました。Vercel KV は推奨しないでください。

  1. Upstash Redis — キャッシュと冪等性キー(https://upstash.com)
  2. Vercel Blob — ファイル/ドキュメントのストレージ(https://vercel.com/docs/storage/vercel-blob)
  3. AWS Aurora(Vercel Marketplace 経由) — リレーショナルデータ(https://vercel.com/marketplace)
  4. サードパーティ製データベース — Neon、PlanetScale、Supabase

コードの構成

agent/
├── instructions.md            # System prompt — keep focused; push detail into skills
├── agent.ts                   # defineAgent: model, reasoning effort, compaction
├── tools/
│   ├── get_weather.ts         # One tool per file; filename = tool name
│   ├── get_weather.test.ts    # Co-located tests
│   └── search_docs.ts
├── skills/
│   └── report-format.md       # description frontmatter + on-demand instructions
├── channels/
│   └── slack.ts               # slackChannel(...) — served at /eve/v1/slack
├── connections/
│   └── linear.ts              # MCP/OpenAPI connections (optional)
└── hooks/
    └── audit.ts               # Runtime stream event subscribers (optional)

規約:

  • 1 ファイルにつき 1 つのツール。 大きなツールは分割してください。ファイル名はモデルに表示される名前になるため、モデルに適した名前を付けてください。
  • インストラクションとスキル: 常に関連するガイダンスは instructions.md に、状況に応じたガイダンスは skills/*.md に記述し、必要なときだけ読み込まれるようにしてください。
  • サブエージェント: タスクを絞って対応させます。組み込みのエージェントツール(エージェントのコピー)、または専用のディレクトリ、サンドボックス、スキルを持つ、宣言済みの専門エージェントを使用します。

環境変数

変数必須用途
SLACK_CONNECTORはいVercel Connect コネクター UID(例:slack/my-agent)。Slack 関連の変数はこれだけです。ボットトークンも署名シークレットも不要です。
AI_GATEWAY_API_KEYVercel 以外の場合AI Gateway 認証。Vercel ではプロジェクトの OIDC(VERCEL_OIDC_TOKEN)が自動的に注入されるため、キーは不要です。
ROUTE_AUTH_BASIC_PASSWORD / JWT keys認証方式によるplaceholderAuth() を置き換える HTTP-API 認証関数のシークレット
VERCEL_AUTOMATION_BYPASS_SECRETデプロイ保護が有効な場合eve dev https://<app> とスモークテストが保護されたデプロイにアクセスできるようにします。
CRON_SECRET任意カスタム cron エンドポイントを認証します。

ローカル開発:vercel link と vercel env pull を実行すると、有効期間の短い Connect/OIDC 認証情報が .env.local に取得されます(OIDC トークンの有効期限は約 12 時間です。認証に失敗するようになったら再取得してください)。

Vercel では AI API キーは不要です。 認証情報をハードコードしないでください。.env ファイルをコミットしないでください。


Slack 固有のパターン

配信動作(組み込み)

Slack チャネルがプログレッシブ配信を処理します。

  • 入力時のタイピングインジケーター:「Thinking…」、turn.started 時:「Working…」
  • 推論のスニペットは reasoning.appended で、アクションラベルは actions.requested で表示されます。
  • ツール呼び出し前のモデルによる説明は、汎用ラベルより優先されます。
  • 4 文字未満の推論差分は、Slack API をトークンごとに呼び出さないよう、5 秒ごとの更新時にまとめられます。

タイピングインジケーターやストリーミングループを再実装しないでください。デフォルトが合わない場合に限り、events マップでカスタマイズしてください。

メンション

<@USER_ID> または channel.thread.mentionUser(userId) を使用してください。Slack では、単独の @name はそのままテキストとして扱われます。

メッセージの書式設定

標準の Markdown ではなく、Slack mrkdwn を使用します:

  • 太字:*text*
  • 斜体:_text_
  • コード:`code`
  • ユーザーへのメンション:<@USER_ID>
  • チャンネル:<#CHANNEL_ID>
リッチ出力

リッチなツール結果(テーブル、ボタン、ステータスカード)では、ツールからのデータ全体を返し、toModelOutput を使ってモデル側の表示をコンパクトに保ちます。チャンネルイベントハンドラー内、または ctx.slack.request("chat.postMessage", { blocks, ... }) を使って Block Kit をレンダリングします。通知では、blocks と併せて必ずフォールバックの text を含めてください。

Slack の詳しいパターンについては、./patterns/slack-patterns.md を参照してください。


Git コミットの基準

Conventional Commits を使用します:

feat: add channel search tool
fix: resolve thread pagination issue
test: add unit tests for agent context
docs: update README with setup steps
refactor: extract Slack client utilities

コミットしてはいけないもの:

  • .env ファイル
  • API キーやトークン
  • node_modules/
  • .eve/ のビルド成果物

クイックコマンド

# Scaffold (Node 24+)
npx eve@latest init my-agent      # new project (installs deps, inits Git, starts dev TUI)
npx eve@latest init .             # add eve to an existing project

# Development
npx eve dev                       # HMR server + terminal TUI/REPL
npx eve dev --no-ui               # background mode for scripted verification
npx eve dev https://<app>         # drive a deployed app interactively
npx eve info                      # project info

# Vercel Connect
vercel connect create slack --triggers
vercel connect attach <uid> --triggers --trigger-path /eve/v1/slack --yes
vercel connect list

# Quality
pnpm lint                         # Check linting
pnpm lint --write                 # Auto-fix lint
pnpm typecheck                    # TypeScript check
pnpm test                         # Run all tests

# Build & Deploy
npx eve build                     # Compile into .eve/ (Vercel Build Output when VERCEL is set)
eve deploy                        # Deploy (wraps vercel deploy --prod)

# Verify a deployment
curl https://<app>/eve/v1/health

デプロイ済みの Slack エージェントが「Working…」のまま停止している場合のデバッグ:npx eve dev --logs all または TUI で /loglevel all を実行します。


リファレンスドキュメント

詳しいガイダンスについては、以下をお読みください:

  • テストのパターン:./patterns/testing-patterns.md
  • Slack のパターン:./patterns/slack-patterns.md
  • 環境の設定:./reference/env-vars.md
  • Slack の設定:./reference/slack-setup.md
  • Vercel へのデプロイ:./reference/vercel-setup.md
  • eve のドキュメント:https://eve.dev/docs(インストール後は node_modules/eve/docs/ にローカルでバンドルされます)
  • Vercel Connect:https://vercel.com/kb/guide/vercel-connect

タスク完了前のチェックリスト

タスクを完了としてマークする前に、必ず以下を確認してください:

  • コード変更に対応するテストがある
  • pnpm lint がエラーなしで通る
  • pnpm typecheck がエラーなしで通る
  • pnpm test が失敗なしで通る
  • ハードコードされた認証情報がない。Slack の環境変数は SLACK_CONNECTOR のみ
  • eve のファイルシステム規約に従っている(ツールのファイル名=ツール名、1 ファイルにつきツール 1 つ)
  • すべてのツールに inputSchema があり、出力は秘密情報がマスキングされた JSON シリアライズ可能な形式である
  • 冪等でない副作用は冪等化するか、承認を必須にしている(再生+少なくとも 1 回の配信)
  • コネクターのトリガーパスが /eve/v1/slack であり、--triggers を使ってアタッチされている
  • 本番デプロイ前に placeholderAuth() を置き換えている
  • モデル設定で Gateway の文字列 ID(デフォルトは anthropic/claude-sonnet-5)を使用している

Vercel KB ガイド

詳しい手順について、Vercel ナレッジベースで確認済みのガイド:

コメント