mcpField
C
candidate-sourcing

candidate-sourcing

github.com/crustdataその他11611 回インストール2 週間前に更新
リポジトリ

candidate-sourcing について

Candidate Sourcing Pipelineは、採用要件から候補者向けのGmail下書きを作成するスキルです。候補者の発見、プロフィールURLの確認、メール検索、個別文面の作成を順に行い、最後に人が確認して送信します。

主な機能

  1. 01採用要件と候補者条件の整理
  2. 02技術的な成果を基に候補者を検索
  3. 03検索によるプロフィールURLの確認
  4. 04業務用または個人メールの検索
  5. 05個別化したGmail下書きの作成

活用シーン

  1. 01明確な職務要件に沿った候補者探し
  2. 02連絡前の候補者プロフィール確認
  3. 03確認用の個別採用メールの準備

MCP 対応

このスキルはCode Modeの`execute`を介してCrustdata MCPツールを利用します。候補者の発見、プロフィール確認、連絡先情報の補完に`person_search`、`person_enrich`、`web_search_live`などを使います。

インストール

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

$npx skills add https://github.com/crustdata/skills --skill candidate-sourcing

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

同梱ファイルevals/evals.jsonREADME.md

詳細

候補者ソーシングパイプライン

採用する職種をもとに、有力な候補者にすぐ送信できる Gmail の下書きを作成する、エンドツーエンドのスキルです。候補者の発掘、プロフィール URL の確認、メールアドレスの補完、パーソナライズした文面の作成、下書きの作成までを、一連のワークフローで処理します。

このパイプラインは5つのフェーズで構成されています。各フェーズの結果を次のフェーズに引き継ぎ、最小限の人手で順番に実行できるよう設計されています。ユーザーは完成した Gmail の下書きを確認し、送信ボタンをクリックします。


フェーズ 1:検索条件を定めて候補者を見つける

採用企業と職種を明確にする

検索を始める前に、以下の詳細をユーザーから聞き出すか、確認してください。これらの情報を推測してはいけません。提示されていない場合は、必ず確認してください。

採用企業について(フェーズ 4 で候補者に連絡する際に必要):

  • 会社名
  • 会社の事業内容(1~2文)
  • 事業段階/実績(例:「シリーズ A、1,000万ドル調達済み」「従業員500人の上場企業」)
  • 所在地/リモート勤務の方針
  • 送信者の氏名と役職(メールの署名に使用)

職種と理想の候補者について:

  • 職種名(例:「創業期の ML エンジニア」)
  • 職務に関わる主要な技術的課題 2~3 件
  • 検索対象の企業、研究機関、コミュニティ
  • 優先する学校/卒業生のつながり(例:「IIIT Hyderabad の卒業生」)
  • 勤務地の希望や制約
  • このバッチでユーザーが希望する候補者の人数

理想の候補者像について:

  • 経験レベル(例:「3~7年」「シニア」「スタッフ以上」)
  • 必須の技術スキルや専門分野
  • あれば望ましい要素(オープンソースへの貢献、論文、特定のフレームワーク)
  • 除外条件やフィルター(例:「FAANG に長く在籍した人は対象外」「スタートアップでの経験必須」)

企業と職種の詳細を使って、この企業が候補者にとって魅力的な理由を調査してください。Crustdata MCP の web_search_live ツールを使い、企業のウェブサイト、最近の資金調達、製品発表、報道記事を確認します。この調査結果は、フェーズ 4 で会社を紹介する文章を作成する際に役立ちます。

職種名ではなく、技術的な成果を軸に検索する

経験年数や企業の知名度よりも、次の3つの要素が重要です。

成果の証拠 — 執筆した論文、実際に利用されているツールやリポジトリ、技術的に掘り下げたブログ記事、オープンソースへの貢献、公開したデモやベンチマーク。

意欲 — 公開の場で発信した技術的な見解、ブログ記事やニュースレターの記事、カンファレンスでの講演、他者からの引用、コミットだけにとどまらない積極的なオープンソース活動。

関連性 — その人の具体的な取り組みが、隣接分野にとどまらず、職務上の実際の課題に対応していること。

検索ソースとパターン

Crustdata の person_search(Code Mode の execute ツール経由)で、条件を指定して検索します。

// model query: find ML engineers at Company Name
const r = await callTool("person_search", {
  filters: { op: "and", conditions: [
    { field: "experience.employment_details.current.title", type: "(.)", value: "ML Engineer" },
    { field: "experience.employment_details.current.company_name", type: "[.]", value: "Company Name" },
  ]},
  limit: 20,
});
if (!r.ok) return { error: r.message };
return r.data.profiles.map(p => ({
  name: p.basic_profile?.name,
  title: p.basic_profile?.current_title,
  url: p.social_handles?.professional_network_identifier?.profile_url,
}));

研究論文 — 中核となる技術的課題に関する学術論文やプレプリントを検索します。第一著者と第二著者を確認し、特にトップ 5 の研究機関に所属していない人に注目します。

オープンソースのリポジトリ — トピックやキーワードでリポジトリを検索し、実質的な貢献をした人(メンテナーだけではない人)を確認します。

Crustdata のウェブ検索(web_search_live)— "[technical problem]" site:arxiv.org、"[name]" "[company]" github、"[community]" alumni engineer ML。

フェーズ 1 の成果

各候補者について、氏名、現在の役職、現在の勤務先、学校/コミュニティとのつながり、関連性を示す具体的な成果物(論文、リポジトリ、ツール、投稿)を記載した候補者リストを作成します。この「成果の証拠」のメモは重要です。フェーズ 4 で候補者に送るメールの書き出しの根拠になります。


フェーズ 2:プロフィール URL を確認する

プロフィール URL を推測したり、組み立てたりしてはいけません。 これは候補者への連絡で最もよくあるエラーの原因です。firstname-lastname や firstname-lastname-school のように推測して作った URL は、頻繁に 404 になるか、別人のページにつながります。実際のスラッグ(linkedin.com/in/ の後の部分)は自動生成され、david-park-086833264 や hongjunchoi92 のような形式です。氏名から予測することはできません。

ウォーターフォール方式

この優先順位をそのまま使用してください。確度の高い一致が見つかったら、そこで終了します。

ステップ 1:person_search(必ず最初に試してください)

// model query: verify the profile URL for Person Name at Company Name
const r = await callTool("person_search", {
  filters: { op: "and", conditions: [
    { field: "basic_profile.name", type: "[.]", value: "Person Name" },
    { field: "experience.employment_details.current.company_name", type: "[.]", value: "Company Name" },
  ]},
  limit: 3,
});
if (!r.ok) return { error: r.message };
return r.data.profiles.map(p => ({
  name: p.basic_profile?.name,
  headline: p.basic_profile?.headline,
  url: p.social_handles?.professional_network_identifier?.profile_url,
}));

social_handles.professional_network_identifier.profile_url を抽出します。これは検証済みの正規プロフィール URL です。

ヒント:

  • 会社名には表記のバリエーションがあります(「Google DeepMind」、「DeepMind」、「Google」など)。まず短い名前を試し、その後に別の表記を試してください。
  • 結果が 0 件の場合は、以前の勤務先(experience.employment_details.past.company_name)を試すか、より広いフィルターでその人の名前だけを検索してください。
  • 結果が複数ある場合は、見出し、所在地、学歴で照合してください。
  • 効率化のため、独立した検索を parallelMap で並行処理する単一の execute スクリプトとして実行してください(候補リストをまとめてからマッピングします)。

ステップ 2:web_search_live(ステップ 1 の結果が 0 件の場合のフォールバック)

// model query: find the profile URL for Person Name at Company
const r = await callTool("web_search_live", {
  query: "Person Name Company distinctive-keyword",
  site: "linkedin.com",
});
if (!r.ok) return { error: r.message };
return r.data.results;

スニペットで勤務先や役職が一致するかを確認し、結果を検証してください。最初に見つかったプロフィール URL をそのまま取得しないでください。

ステップ 3:未検証としてマークする(両方とも失敗した場合)

既存の URL はそのまま保持しますが、「LinkedIn URL は未検証です。」という注記を追加してください。URL を捏造してはいけません。

スラッグの比較

既存の URL を検証する場合は、スラッグ(linkedin.com/in/ の後の部分)を抽出し、末尾のスラッシュを削除して、大文字と小文字を区別せずに比較します。スラッグが異なる場合は URL が誤っているため、代わりに social_handles.professional_network_identifier.profile_url の検証済み URL を使用してください。

これで防げる実際の間違い

以下は、実際のアウトリーチキャンペーンで発生したエラーです。

推測したスラッグ実際のスラッグ発生したこと
david-park-princetondavid-park-086833264404 — 架空のスラッグ
vincent-chen-mitvincent-chen-662a031b5404 — 学校名の接尾辞は機能しない
benoit-rostykusbenoitrostykus別人 — 異なるプロフィール
hongjun-choihongjunchoi92別人 — 異なるプロフィール
abhay-gupta-cmugupta-abhay別人 — スラッグの形式がまったく異なる
benedict-arockiarajbenedictflorance別人 — LinkedIn では別の名前を使用している
initeshmethaninitesh-methani-7554b3121別人 — 推測したスラッグのタイプミス
URL 形式

必ず https://www.linkedin.com/in/{slug} を使用してください。プロトコルのない linkedin.com/in/ は使用しないでください。ü などの文字は URL エンコードが必要です(%C3%BC)。


フェーズ 3:メールアドレスを探す

Gmail の下書きには候補者全員のメールアドレスが必要です。以下の優先順位に従ってください。

3A:Crustdata エンリッチメントによる業務用メールアドレス

person_enrich(Code Mode の execute ツール経由)を使用します。入力には professional_network_profile_urls の LinkedIn URL 配列(1 回あたり 25 件以下。chunk(urls, 25) で分割してから parallelMap)と、フィールド名のグループ(リーフ名ではありません)を fields に指定します。contact グループはメールアドレスと電話番号を返します。

// model query: enrich business emails for a batch of candidate LinkedIn URLs
const urls = ["url1", "url2", "url3"]; // from Phase 2
const batches = chunk(urls, 25);
const results = await parallelMap(batches, async (batch) => {
  const r = await callTool("person_enrich", {
    professional_network_profile_urls: batch,
    fields: ["basic_profile", "contact"],
  });
  return r.ok ? r.data : [];
});
// each match: matches[0].person_data.contact.business_emails[].email
return results.flat();

contact フィールドグループは重要です。デフォルトのレスポンス(basic_profile のみ)にはメールアドレスが含まれません。各結果は matched_on を使って入力 URL に対応付けてください。

期待されるヒット率: 既知の企業に勤務するプロフェッショナルは約 70~80%、独立して活動する人は約 40~50%、職務の合間にいる人は約 20~30%です。

3B:Crustdata の連絡先エンリッチメントによる個人用メールアドレス

連絡先情報のみが必要な場合は、料金を抑えるために person_enrich より person_contact_enrich を優先してください。 person_enrich は contact グループを通じて個人用メールアドレス(+2)と電話番号(+2)を返すことがありますが、基本プロフィール料金(基本 1 + 連絡先の各ティア、上限 7)も請求されます。person_contact_enrich は連絡先専用のツールです。基本プロフィール料金がかからないため(上限 5)、連絡先データだけが必要な場合はこちらのほうが安価です。コミット履歴の検索(3C)に進む前に、こちらを試してください。

person_contact_enrich では同じ professional_network_profile_urls 配列(1 回の呼び出しにつき最大 25 件)を指定でき、business_emails、personal_emails、phone_numbers を含む contact オブジェクトが返されます。ドット区切りの fields でティアを選択します。

// model query: enrich personal emails + phone for candidate LinkedIn URLs
const r = await callTool("person_contact_enrich", {
  professional_network_profile_urls: chunk(urls, 25)[0],
  fields: ["contact.personal_emails", "contact.phone_numbers"],
});
if (!r.ok) return { error: r.message };
// per match: matches[0].person_data.contact.personal_emails[].email (objects), .phone_numbers[] (bare strings)
return r.data;

レスポンスの person_data.contact オブジェクトには、オブジェクトの配列({ email, status }。.email を読み取ります。business_emails と同様です。Gmail、ProtonMail など)である personal_emails と、文字列のみの配列である phone_numbers が含まれます。

クレジット料金: person_contact_enrich には基本料金がなく、連絡先のティアごとに料金が設定されています。目安は、ビジネス用メールアドレスが約 +1、個人用メールアドレスが約 +2、電話番号が約 +2 で、プロフィールあたり最大 5 です。これらのティアごとの数値は、追加料金の目安/上限であり、上限が保証されるわけではありません。ドット区切りのティアを 1 つだけリクエストしても、そのティアの追加分だけに請求が確実に限定されるわけではありません。fields: ["contact.personal_emails"] の呼び出しで、5 クレジットの上限全額が請求され、3 つのティアすべてが返された例が確認されています。基本プロフィール料金がかからないため、連絡先データについては person_enrich より安価ですが、ドット区切りのフィールドを 1 つ指定すれば料金が上限を下回るとは考えないでください。

一括処理の上限: 1 回の呼び出しにつき LinkedIn URL を最大 25 件指定できます。

推奨する方法: person_contact_enrich はビジネス用メールアドレス、個人用メールアドレス、電話番号を 1 回の呼び出しで返します。往復処理を減らすため、必要なティアをまとめてリクエストしてください。person_enrich(フェーズ 3A)でも contact グループを通じて 3 つすべての連絡先ティアを返せますが、基本プロフィール料金が加算されるため、連絡先データだけが必要な場合は person_contact_enrich を優先してください。

個人用メールアドレスが見つかった場合、コールドアウトリーチではビジネス用メールアドレスより優先してください(返信率が高く、企業のスパムフィルターに弾かれにくいため)。候補者の個人用メールアドレスが返されない場合は、以下のコミット履歴の検索に進んでください。

3C:公開コミット履歴による個人用メールアドレスの特定(3B で見つからなかった場合の代替手段)

これは、技術職の候補者に対して最も効果的な手法です。Git では、各コミットに作成者のメールアドレスが記録されます。

ステップ 1:GitHub のユーザー名を特定する

  • まず標準の方法を試します。fields に social_handles を指定して person_enrich を呼び出すと、開発者のハンドルである dev_platform_identifier が返されます。さらに dev_platform_profiles(開発者向けアドオン、+1 クレジット)を追加すると、リポジトリ、組織のメンバーシップ、場合によっては公開 email を含む、開発者の完全なプロフィールが返されます。その email が存在し、noreply アドレスでない場合は、それを使用してステップ 5 に進みます
  • すでに特定した人物については、dev_platform_enrich({ crustdata_person_id }) を呼び出すと同じ情報を個別に取得できます
  • ウェブ検索:"[Name] [Company] GitHub site:github.com"
  • 個人のウェブサイトやソーシャルプロフィールの自己紹介欄を確認します

ステップ 2:GitHub プロフィールが本人のものか確認する 次のうち少なくとも 2 つを確認します。自己紹介に既知の会社名や役職が記載されている、プロフィール名が一致している、リポジトリのトピックが既知の専門分野と一致している、ウェブ検索で関連性を確認できる。

ステップ 3:最も古いフォークではないリポジトリを探す

ステップ 1 で開発者のエンリッチメントを実行した場合は、その結果の dev_platform_profiles[].repos エントリーに full_name、is_fork、github_created_at がすでに含まれています。追加の呼び出しは不要です。最も古いフォークではないリポジトリを選んでください。それ以外の場合は、GitHub API を使って一覧を取得します。

// model query: list oldest repos for GitHub user {username}
const r = await callTool("web_enrich_live", {
  urls: ["https://api.github.com/users/{username}/repos?sort=created&direction=asc&per_page=5"],
});
if (!r.ok) return { error: r.message };
return r.data; // [{ success, url, title, content }]

"fork": false である最初のリポジトリを選びます。

ステップ 4:コミットからメールアドレスを抽出する

方法 A — Commits API:

// model query: read first commit of {owner}/{repo}
const r = await callTool("web_enrich_live", {
  urls: ["https://api.github.com/repos/{owner}/{repo}/commits?per_page=1"],
});

取得した content 内の [0].commit.author.email を確認します。

方法 B — .patch エンドポイント:

// model query: read commit patch for {owner}/{repo}@{sha}
const r = await callTool("web_enrich_live", {
  urls: ["https://github.com/{owner}/{repo}/commit/{sha}.patch"],
});

取得した content 内の From: Name <email> ヘッダー行から抽出します。

ステップ 5:検証する — *@users.noreply.github.com、[email protected]、および noreply を含むメールアドレスは除外します。

3D: Web検索によるフォールバック

Crustdata の個人メールアドレスのエンリッチメントとコミット検索の両方がうまくいかない場合は、次の順に試してください。

  1. コードリポジトリ(issue、README):"[name]" "@gmail.com" site:github.com
  2. 競技プログラミング:"[name]" site:codeforces.com
  3. 個人ウェブサイト:"[name]" "[company]" email contact
  4. カンファレンスの登壇者ページ:"[name]" "[company]" speaker email
  5. 学術プロフィール:"[name]" site:scholar.google.com
3E: GitHub API のレート制限

GitHub REST API にはレート制限があります。フェッチ時にレート制限のメッセージ(「API rate limit exceeded」)が返された場合は、残りのバッチではコミット検索を中止してください。その候補者を 3D に回すか、no_contact として記録し、制限がリセットされるまで再試行しないでください。

メールアドレスの優先順位

候補者のメールアドレスが複数ある場合は、次の順に優先してください。

  1. 個人の Gmail/ProtonMail — コールドアウトリーチで最も返信率が高い
  2. 大学のメールアドレス — まだアカデミアにいる場合
  3. 会社のメールアドレス — コールドアウトリーチでは最後の手段(企業のスパムフィルターに弾かれることが多いため)

Phase 4: パーソナライズしたアウトリーチメールを作成する

重要なルール

職名や暗黙の能力ではなく、具体的な仕事を褒めてください。

「Mistral で応用科学を率いているなら、ML を研究から本番環境へ移行する方法をご存じでしょう」と書くと、「あなたの LinkedIn の肩書きを見て、テンプレートに当てはめました」と伝わってしまいます。代わりに、相手が実際に作ったものを一つ見つけ、それについて率直な感想を伝えてください。

メールの構成

すべてのメールで、次のテンプレートをそのまま使用します。

Hi {first_name},

[1-sentence opener: say what impressed you — casual, no analysis]

[1-sentence pitch: role + company + location]

[3-line company blurb]

{sender_name},
{sender_title}

簡潔にしてください。見出しや箇条書き、複数段落にわたる説明は使いません。ユーザーからまだ提供されていない場合は、名前、肩書き、会社名、会社についての2~3行の説明を尋ねてください。

書き出しの作成

Phase 1 の「実績の証拠」に関するメモを使います。これは候補者の発掘時に特定した、具体的な論文、リポジトリ、ツール、投稿を指します。

良い書き出しのパターン:

  • 「[具体的な論文/プロジェクト]についてのあなたの成果を拝見しました。[率直な感想]。」
  • 「あなたの[具体的なツール/リポジトリ]は、[具体的な詳細を含む率直な評価]ですね。」
  • 「[具体的な経歴の出発点A]から[B]へ至る道のりは、素晴らしいですね。」

悪い書き出しのパターン(絶対に使わないでください):

  • [相手の仕事] + [自社もXをしているため関連性がある] — 褒め言葉が売り込みに変わってしまいます
  • [会社での肩書き] + [したがってXのスキルがある] — 仕事ではなく肩書きに基づいています
  • [相手の研究] + [まさに当社が必要としている考え方です] — 相手を勝手に評価しています
  • 長い括弧書きの説明 — 気軽な流れを損ないます
売り込みの一文

「[Company] の [location] で [role] を募集しています。ご興味はありますか?」

率直に伝えてください。書き出しで必要なことはすでに伝わっています。

会社の説明

ユーザーからまだ提供されていない場合は、会社についての2~3行の説明を尋ねてください。実績、ミッション、今このタイミングである理由を強調します。同じメールが並ばないよう、バッチ内では表現を少しずつ変えてください。

件名

デフォルト:"[Role] @ [Company]"


Phase 5: Gmail の下書きを作成し、トラッカーに記録する

Gmail の下書きを作成する

メールアドレスを確認できた候補者ごとに、gmail_create_draft を使います。

  • to:候補者のメールアドレス
  • subject:件名
  • body:メール全文(書き出し+売り込み+会社の説明+署名)

これが最終ステップです。Gmail の MCP は下書きを作成できますが、送信はできません。ユーザーが内容を確認し、手動で送信ボタンをクリックします。

アウトリーチトラッカーに記録する

下書きを作成するたびに、職種ごとの CSV トラッカーに行を追加します。

/sessions/{session}/mnt/outputs/outreach_tracker_{role_slug}.csv

role_slug は小文字とハイフンで表記します(例:「Founding ML Engineer」→ founding_ml_engineer)。

CSV のヘッダー:

date,role_hiring_for,candidate_name,current_role,company,school_connection,linkedin_url,email,channel,subject,message_opener,status,notes,message_body
import csv, datetime, os

tracker_path = "outreach_tracker_{role_slug}.csv"
fields = ["date","role_hiring_for","candidate_name","current_role","company",
          "school_connection","linkedin_url","email","channel","subject",
          "message_opener","status","notes","message_body"]

row = {
    "date": datetime.date.today().isoformat(),
    "role_hiring_for": "<role name>",
    "candidate_name": "<full name>",
    "current_role": "<title>",
    "company": "<company>",
    "school_connection": "<school or empty>",
    "linkedin_url": "<verified linkedin url>",
    "email": "<email address>",
    "channel": "email",
    "subject": "<subject line>",
    "message_opener": "<first sentence of email body>",
    "status": "drafted",
    "notes": "<email type + source: e.g. Personal Gmail via commit history>",
    "message_body": "<full email body>"
}

file_exists = os.path.isfile(tracker_path)
with open(tracker_path, "a", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=fields)
    if not file_exists:
        writer.writeheader()
    writer.writerow(row)

ステータス値: drafted(Gmail の下書きを作成済み)、sent(ユーザーが送信ボタンをクリック済み)、replied、no_contact(メールアドレスが見つからなかった)。

チャネル値: email

メールアドレスが見つからない候補者

エンリッチメントの全手順を試してもメールアドレスが見つからなかった場合は、試した内容を説明するメモを添えて、status: "no_contact" としてトラッカーに記録します。他のチャネルを手動で試すかどうかは、ユーザーが判断できます。


効率化のためのバッチワークフロー

候補者を多数(5人超)処理する場合は、所要時間を最小限に抑えるため、次の順序で進めます。

  1. 候補者を検索して収集する(フェーズ 1)— まず候補者の完全なリストを作成します
  2. プロフィール URL をまとめて検証する(フェーズ 2)— 1 つの execute スクリプト内で parallelMap を使って person_search の検索を並行して実行します。失敗した場合はウェブ検索(web_search_live)をフォールバックとして使用します
  3. メール情報をまとめて補完する(フェーズ 3A + 3B)— person_enrich(fields: ["basic_profile", "contact"])でプロフィールとビジネスメールを取得し、続いて person_contact_enrich(fields: ["contact.personal_emails", "contact.phone_numbers"])で個人メールと電話番号を取得します。連絡先情報のみが必要な場合は、基本プロフィールの料金が発生しないため、こちらのほうが割安です。どちらも 1 回の呼び出しで最大 25 件の LinkedIn URL をまとめて処理できます(chunk(urls, 25) の後に parallelMap を実行)
  4. コミット検索の対象を選別する(フェーズ 3C)— まだメールアドレスが見つかっておらず、GitHub プロフィールを持っている可能性が高いエンジニアを特定し、コミットからのメールアドレス抽出を優先します
  5. コミット検索をまとめて実行する(フェーズ 3C)— 複数の GitHub URL を指定した web_enrich_live を呼び出します(urls: [...])
  6. ウェブ検索をフォールバックとして実行する(フェーズ 3D)— Crustdata による情報補完とコミット検索の両方に失敗した候補者が対象です
  7. メール文面をすべて作成する(フェーズ 4)— フェーズ 1 でまとめた実績をもとに、すべての書き出し文を一度に作成します
  8. Gmail の下書きをすべて作成する(フェーズ 5)— 下書きを作成し、トラッカーに記録します
大きな API レスポンスの処理

Crustdata による情報補完と person_search の呼び出しは、大きなペイロードを返すことがあります。Code Mode では、レスポンスを返す前に execute スクリプト内でサイズを縮小してください。モデルに届くのは return した内容だけです。必要なフィールドだけを残すために project/pick/compact を使い、完全なペイロードを後続処理で再解析する必要がないようにします。

// model query: search candidates and return a slim projection
const r = await callTool("person_search", { filters, limit: 25 });
if (!r.ok) return { error: r.message };
return r.data.profiles.map(p => ({
  name: p.basic_profile?.name,
  url: p.social_handles?.professional_network_identifier?.profile_url,
}));

生のレスポンスを返すのではなく、スクリプト内で必ずデータを絞り込んでください(プロフィール全体ではなく、ID/URL/メールアドレスを返します)。


ツールの依存関係

このスキルには次のものが必要です。

  • Crustdata MCP サーバー(install.crustdata.com/mcp):list_tools、get_schema、execute を公開する単一の Code Mode MCP です。Crustdata のデータツールはすべて、await callTool(name, params) を介して execute({ code }) のプレーンな JavaScript スクリプト内から呼び出します。get_schema が提供する TypeScript 型付きのインターフェイスに沿って作成しますが、本文に型注釈は記述しません。ここで使用するツール:person_search、person_enrich(プロフィールとビジネスメール)、person_contact_enrich(個人メールと電話番号)、company_enrich、dev_platform_enrich(Dev Platform:開発者プロフィールとリポジトリ)、web_search_live、web_enrich_live
  • Gmail MCP:gmail_create_draft
  • Python(スプレッドシートの入出力には openpyxl、トラッカーには csv)
  • ウェブ検索(Crustdata の web_search_live):メールアドレスを見つけられない場合に使用

創業者を除外するルール

現在、創業者、共同創業者、CEO、CTO を務めている人には連絡しないでください。自身の会社を積極的に経営している人が対象です。退職する可能性が低く、連絡すると候補者枠を無駄にするうえ、配慮に欠ける印象を与える可能性があります。

創業者の見分け方

フェーズ 1 で候補者の現在の役職を確認します。役職に次のいずれかが含まれる人にはフラグを立てます:Founder、Co-Founder、Co-founder、CEO、CTO、または「Chief」と「Officer」の組み合わせ(例:Chief Technology Officer)。

縮小している会社に限った例外

現在の創業者に連絡する価値があるのは、その会社が明らかに失敗しつつある場合だけです。次の3つの条件すべてを満たす必要があります。

  1. 従業員数が減少している — 会社のドメインを指定して company_enrich を使用します:

    // model query: check headcount trend for example.com
    const r = await callTool("company_enrich", {
      domains: ["example.com"],
      fields: ["basic_info", "headcount", "web_traffic"],
    });
    if (!r.ok) return { error: r.message };
    // per match: matches[0].company_data.headcount.{ total, growth_percent }
    return r.data;
    

    headcount.growth_percent(キーは mom、qoq、six_months、yoy、two_years)を確認し、前月比(mom)と前四半期比(qoq)の成長率がマイナスになっているかを調べます。

  2. ウェブサイトのトラフィックが減少している — 月間訪問者数が減少傾向にあるかを確認します。

    月間訪問者数の推移を確認するには、company_enrich から web_traffic.domain_traffic を読み取ります(fields: ["web_traffic"] をリクエスト)。seo グループと news グループも同じ方法で利用できます。

  3. 会社が持続できる速度を上回って人が辞めている — 過去3か月間に辞めた元従業員の数が、現在の従業員数を上回っています。これは、単に横ばいなのではなく、会社から実際に人が流出していることを示します。

3つの条件すべてを満たす場合、創業者は新しい機会に関心を持つ可能性があります。いずれかの条件を満たさない場合(例:会社の規模は小さいものの、成長中)、候補者を除外し、トラッカーで status: "skipped" として、"Excluded — active founder (company not shrinking)" のようなメモを付けます。

エンリッチメントのレスポンスの処理

company_enrich は { matched_on, match_type, matches: [{ confidence_score, company_data }] } の配列を返します。execute スクリプト内で必要な従業員数のシグナルだけに絞り込んで返します:

// model query: project headcount growth signals for founder-company check
const r = await callTool("company_enrich", { domains: ["example.com"], fields: ["basic_info", "headcount"] });
if (!r.ok) return { error: r.message };
return r.data.map(m => {
  const cd = m.matches?.[0]?.company_data;
  const hc = cd?.headcount ?? {};
  const g = hc.growth_percent ?? {};
  return { name: cd?.basic_info?.name, total: hc.total, mom: g.mom ?? 0, qoq: g.qoq ?? 0 };
});
実例
候補者会社結果
Di Jin(共同創業者)Eigen AI(従業員12人、前月比-7.7%)わずかに減少しているものの、退職した元従業員が十分に多くないため、除外
Sai Surbehera(共同創業者/CTO)Lapis Labs(従業員5人、前月比+25%)会社が成長中のため、除外
Aayush Anand(共同創業者)Level.game(従業員9人、安定)会社が事業を継続しているため、除外

やってはいけないこと

  • プロフィールのURLを推測しない — 必ず Crustdata を使って確認します。URLを推測した結果、91人を対象としたキャンペーンで12件のエラーが発生しました。
  • メールを送信しない — Gmail MCP で作成できるのは下書きだけです。ユーザーが確認し、手動で送信します。
  • 役職をもとにした書き出しをしない — 候補者が実際に行った具体的な仕事に必ず触れます。
  • メールアドレスの検証を省略しない — noreply アドレスは除外し、GitHub プロフィールが適切な人物のものかを確認します。
  • 同じ書き出しを二度使わない — 候補者が行った具体的な仕事に基づいて、それぞれに固有の書き出しを用意します。
  • 現役の創業者に連絡しない — 会社が明らかに失敗しつつある場合(従業員数の減少、トラフィックの減少、会社が持続できる速度を上回る人の流出)を除き、現在の創業者/共同創業者/CEO/CTOは除外します。上記の「創業者を除外するルール」セクションを参照してください。

コメント