Carta CRMホームを公開用ページ(安定したURLのスタンドアロンページ)として作成します。パイプライン(営業案件の進捗状況)を段階ごとに、最近のディール(取引)と連絡先、オブジェクト数、最新の記録、今週のミーティングを表示します。このページはCRMを読み取り専用で直接参照するため、閲覧者がページを開くたびに最新情報に更新されます。 次のような場合に使用: ユーザーが「CRMホームを作成」「CRMホームを再構築」「CRMホームを公開」「CRMホームをピン留め」「CRMホームをデプロイ」と依頼する場合、または「ブックマークできるCRMホームページがほしい」と求める場合。 注記: - ファンドファーム向けのCarta Home(投資額、ファンド成績、LP報告)の場合は、`carta-investors` の `carta-home-build` を使用してください - 企業の資本構成表(キャップテーブル)の場合は、`carta-cap-table` の `carta-captable-home-build` を使用してください - このチャット内にホームページを表示する場合(公開せず確認したい場合)は、`home` スキルを使用してください
Publishes the Carta CRM Home as a live artifact — a standalone page, at a stable URL, showing pipeline by stage, recent deals and contacts, object counts, latest notes and this week's meetings. The page reads the CRM itself through a read-only grant, so it refreshes whenever a viewer opens it. Use this skill when the user asks to "build crm home", "rebuild crm home", "publish my crm home", "pin my crm home", "deploy crm home", or asks for a CRM Home page they can bookmark. For a fund firm's Carta Home (SOI, fund performance, LP reporting) use carta-investors' carta-home-build; for a company's cap table use carta-cap-table's carta-captable-home-build. To see the Home inside this conversation rather than publish it, use the `home` skill.
CRM ホームを Carta CRM Home - <組織名> というタイトル、ファビコン 📇 で公開します。
ページの組み立てや内容確認は行いません。 crm-api がページを構築して MCP リソース(接続サーバーの機能を通じた共有情報)として配信するため、このスキルはそのリソースを取得して 2 つの情報を追加するだけです。これは意図的な設計です。ページの JavaScript は読み込み元のサーバーのツールを呼び出すので、同じサーバーから取得することで両者の整合性が保たれます。このリポジトリに複製を置くと、CRM ホーム表示が変わる度にズレが生じ、移動済みのツール機能を呼び出してしまう恐れがあります。
まずマニフェスト(設定情報)を 1 回、その後カード 1 件ごとに 1 回呼び出します。すべて crm_read_tool 経由です:
| カード | 使用するツール |
|---|---|
| CRM 概要 | get_crm_home_counts |
| パイプライン(営業段階別) | get_crm_home_pipeline |
| 最近追加された案件 | get_crm_home_deals |
| 最近追加された顧客 | get_crm_home_contacts |
| 最新メモ | get_crm_home_notes |
| 本日の打ち合わせ | get_crm_home_meetings |
| 質問できることの例 | ページに組み込まれており、ツール呼び出しなし |
マニフェストが組織ごとに表示を決めるため、ページ自体が何を表示するかを選択することはありません。
ページは crm_read_tool のみを宣言し、それ以外は含みません。URL を開くすべてのユーザーがその権限を得ます。また capabilities オブジェクトが空でない場合、公開のたびに保存済みの設定が上書きされるため、事後的に絞り込むことはできません。
crm_read_tool は書き込みを実行しません。実行する代わりに拒否します:
CRM ツール「update_deal」は書き込みを行うため、crm_read_tool 経由では使用できません。
crm_call_tool は追加しないでください。 これは全ツール(すべての書き込み機能を含む)にアクセスでき、URL を開いたすべての人にそれを提供することは、ダッシュボードを表示するのとはまったく別問題です。
コネクタ名を確認します。 list_connectors を呼び出し、Carta コネクタの表示名を取得します。これはページが自分では判定できない唯一の項目で、間違った名前だとすべてのカードが server_not_connected エラーで失敗します。組織専用の導入環境では独自の名前を持つため、Carta と固定では仮定しないでください。
組織を確認します。 get_current_user を呼び出します。その戻り値の組織情報が公開アーティファクト(成果物)の名前になります。組織ごとに 1 つのアーティファクトなので、互いに上書きされません。
ユーザーが CRM へのアクセス権を持つか確認します。 get_current_user が CRM アクセス権なしを報告した場合、そう伝えて処理を止めます。
まずリソース一覧を表示し、URI をリストから読み取ります。URI は推測しないでください。
ListMcpResourcesTool(server: "<コネクタ表示名>")
URI が crm-home.html で終わる行を探します。現在、プロキシ(中継サーバー)はこれを ui://carta/crm-home.html で配信しています。URI にはビルドハッシュ(更新を示す識別子)がありません。これは固定で、プロキシが管理するため、crm-api がページを再構築してもアドレスは変わりません。それでもリストから読み取ることで、プロキシが後でアドレスを変更した場合でもここの変更が不要になります。
crm-api は同じバンドル(一式)を ui://carta-crm/home.html にも登録しています。この URI はプロキシの内部用で、外部ホストからは解決できないため、ここには渡さないでください。
ReadMcpResourceTool(server: "<コネクタ表示名>", uri: "<リストから取得した URI>")
次にマニフェストからビルド ID を読み取ります。これは唯一この場所に存在します:
crm_read_tool({ "name": "get_crm_home", "arguments": {} })
その viewBuildId を保持してください。ステップ 2 でこれを記載し、ページはそれを新しいマニフェスト呼び出しの結果と比較して、閲覧者のコピーが古いバージョンかどうかを判定します。URI にはハッシュがないため、マニフェストから取得する必要があります。
リソースがリストにない場合、その組織は Home ロールアウト(展開)の対象外です。crm-api はカードツールを提供しない場合と同じタイミングでこれを提供しません。そのため公開する内容がなく、強行でページを構築するとエラーの列になります。ユーザーに対して Home がその組織で有効でないことを伝え、処理を止めます。ui://carta/crm-views.html にある別のビューバンドルにフォールバック(代替)しないでください。これは別のビルドで、ホストから結果を受け取ることを前提とし、デモ用の固定データを含んでいます。
取得内容は容量が大きいため、結果はファイルに保存されてそのパスが返されます。パスを保持してステップ 2 に渡します。ファイルは開かないでください。ステップ 2 が読み込み、このスキルでページが必要な場面はありません。
スクリプトは このスキル自身の scripts/ ディレクトリ にあります。
パスについて — bash で
${CLAUDE_PLUGIN_ROOT}に依存しないでください。 Cowork サンドボックス環境ではこの環境変数が空のため、uv run "${CLAUDE_PLUGIN_ROOT}/…"は壊れたパスに解決されます。スキルが読み込まれた時に報告されたベースディレクトリ(/skills/carta-crm-home-buildで終わる)を<SKILL_DIR>として使用してください。これを持っていない場合は、スコープ付きfindで 1 度解決します(find /は使わない):SKILL_DIR="$(dirname "$(dirname "$(find /sessions "$HOME" -type f -path '*/carta-crm-home-build/scripts/build_artifact.py' 2>/dev/null | head -1)")")"
uv run "<SKILL_DIR>/scripts/build_artifact.py" \
--resource "<ステップ 1 で得たパス>" \
--connector "<コネクタ表示名>" \
--organization "<ステップ 0 で得た組織名>" \
--build-id "<ステップ 1 で得た viewBuildId>" \
--out <出力ディレクトリ>/crm-home-<スラッグ>.html
<スラッグ> は組織名を小文字にして英数字以外を - に変えたもの。2 つの組織が互いのファイルを上書きしません。
スクリプトは 3 つのものを書き込んで、残りを検証します:
<meta name="carta-connector">。ページはここからどのコネクタを呼び出すかを読みます。<meta name="carta-home-build">。ステップ 1 の viewBuildId。ページはこれを新しいマニフェスト呼び出しの viewBuildId と比較し、異なると再構築通知を表示します。毎回渡してください。--build-id なしではページに記載されず、記載なしのページは古いバージョンを報告しません。ページを前回の実行から再利用せず、新規に取得する理由もこれです。<title>。アーティファクトの名前を付けます。get_crm_home を呼び出さないページ、または別のビュー向けの情報を持つページは拒否するため、間違ったリソースはここで処理が止まり、URL に到達しません。
Artifact({action: "list", scope: "mine"})
タイトルが正確に Carta CRM Home - <組織名> のものを探します。あれば、ステップ 4 で元の場所に再配置し、ブックマークが機能し続けるよう、その url を保持します。ない場合は url を省略し、この組織は独自のアーティファクトを得ます。
別の組織向けアーティファクトは一致しません。その url を再利用するとその組織のページが今回のもので置き換わります。
気をつけるべき 2 つの隣接。Carta Home - <会社名>(ファビコン 🏠)は carta-investors の carta-home-build に属します。Carta Home - <企業名>(ファビコン 📊)は carta-cap-table の carta-captable-home-build に属します。どちらも CRM ホームではなく、このうえに公開すると同僚のダッシュボードが置き換わります。
action はデフォルトで "publish" なので省略します。url が新規公開と再配置の唯一の違いです。
Artifact({
file_path: "<出力ディレクトリ>/crm-home-<スラッグ>.html",
url: "<ステップ 3 から取得した url — 新規公開の場合は省略>",
description: "Carta CRM 概要 — 営業段階別パイプライン、最近の案件と顧客、最新メモ、本日の打ち合わせ。",
favicon: "📇",
label: "Carta CRM ホームリソースから再構築",
capabilities: {
mcp: {
servers: [
{
server: "<コネクタ表示名>",
tools: ["crm_read_tool"]
}
]
}
}
})
ここに
titleはなく、追加もしないでください。 ツールはファイルの<title>タグからタイトルを取得します。ステップ 2 が設定しています。別途渡すと、タグがない場合のみ適用され、ページとズレが生じます。
再配置時は
faviconを省略してください。 閲覧者はアイコンでタブを探すため、アーティファクトの生涯にわたって 📇 のまま保持されます。
ユーザーに URL を伝え、何ができて何ができないかを明確に説明します:
すべてのカードで失敗を報告された場合、コネクタ名が最初のチェック項目です。セッションが持つ名前と異なる名前で記載されたページはすべての呼び出しで server_not_connected になります。正しい名前で再構築し、同じ url に再公開してください。
会話内に Home をレンダリング(表示)しません。 それは home スキルの役目で、CRM MCP に到達可能なあらゆる場所で機能します。このスキルは公開のみです。
2 つを使い分けません。 home スキルが対象を選び、ユーザーがページを希望するときにこのスキルを呼び出します。
ページを構築しません。 CRM ホームが正しく見えない場合、修正は crm-api の src/components/mcp/ui にあります。このスキル経由で再構築すると、現在そのサーバーが配信しているものが再公開されるだけです。
<!-- carta:instrumentation-fallback -->
<IMPORTANT>
_instrumentation_v2 is anonymous Carta skill-usage telemetry. ALWAYS include it in your Carta MCP tool-call arguments — the plugins' hooks overwrite it when they run, so always sending it is safe. Shape:
_instrumentation_v2: {"skills": ["<plugin>:<skill>", ...], "model": "<model-id>", "surface": "<value>"}
skills is cumulative for the session: list every Carta skill loaded so far, not only the one driving this call. Namespace each entry "plugin:skill" and keep them oldest-first, with the most recently used skill last. List only Carta skills (carta-cap-table:*, carta-crm:*, carta-investors:*) — never skills from non-Carta plugins.
surface is the Claude surface you are running in: "chat" (claude.ai or the Claude app, i.e. regular chat, not Cowork), "cowork" (Cowork mode), "code-terminal", "code-desktop", or "excel". Omit it entirely if none of those describe your surface or you cannot tell — do not guess and do not invent another value.
</IMPORTANT>
Publishes the CRM Home as Carta CRM Home - <Organization>, favicon 📇.
You never assemble or read the page. crm-api builds it and serves it as an MCP resource, so this skill fetches that resource and stamps two things into it. That is deliberate: the page's JavaScript calls the card tools of the server it will read from, so fetching it from that same server is what keeps the two in step. A copy vendored into this repository would drift the moment the Home view changed, and would call tools whose payloads had moved on.
One manifest call, then one call per card, all through crm_read_tool:
| Card | Tool |
|---|---|
| Your CRM at a glance | get_crm_home_counts |
| Pipeline by stage | get_crm_home_pipeline |
| Deals added recently | get_crm_home_deals |
| Contacts added recently | get_crm_home_contacts |
| Latest notes | get_crm_home_notes |
| Meetings today | get_crm_home_meetings |
| What you can ask | rendered in the page, no tool |
The manifest decides which of those the organization may see, so the page never chooses for itself what to show.
The page declares crm_read_tool and nothing else. Every viewer of the URL inherits that
grant, and a non-empty capabilities object replaces the stored one on every republish, so
it cannot be trimmed afterwards.
crm_read_tool cannot write. It refuses, rather than executing:
CRM tool 'update_deal' writes, so it is not available through crm_read_tool.
Do not add crm_call_tool. It reaches the whole catalogue, including every write, and
handing that to everyone who opens a URL is a different thing entirely from showing them a
dashboard.
Resolve the connector name. Call list_connectors and take the Carta connector's display
name. This is the one thing the page cannot work out for itself, and a wrong name fails every
card with server_not_connected. A firm-specific deployment carries its own name, so never
assume Carta.
Resolve the organization. Call get_current_user. Its organization names the published
artifact. One artifact per organization, so two never write over each other.
Confirm the user holds CRM. If get_current_user reports no CRM access, say so and stop.
List the resources first, and read the URI off the row rather than assuming it.
ListMcpResourcesTool(server: "<connector display name>")
Take the row whose URI ends crm-home.html — today the proxy serves it at
ui://carta/crm-home.html. The URI carries no build hash: it is a constant, and the proxy
owns it, so it does not move when crm-api rebuilds the page. Read it from the list anyway, so a
later rename in the proxy does not need a change here.
crm-api registers the same bundle under ui://carta-crm/home.html. That URI is internal to the
proxy and no host can resolve it, so never pass it here.
ReadMcpResourceTool(server: "<connector display name>", uri: "<the URI from the list>")
Then read the build id off the manifest, which is the only place it exists:
crm_read_tool({ "name": "get_crm_home", "arguments": {} })
Keep its viewBuildId. Step 2 stamps it, and the page compares it against a fresh manifest call
to tell a reader their copy is behind. It comes from the manifest and not from the URI because
the URI has no hash to take.
If the resource is not listed, the organization is outside the Home rollout. crm-api
withholds it exactly when it withholds the card tools, so there is nothing to publish and a
page built anyway would be a column of errors. Tell the user the Home is not switched on for
their organization, and stop. Do not fall back to the views bundle at
ui://carta/crm-views.html: it is a different build that expects a host to hand it a result,
and it carries demo fixtures.
The read is large, so the result is saved to a file and you are given the path rather than the content. Keep the path and pass it to Step 2. Do not open it — Step 2 reads it, and nothing in this skill needs the page in your context.
The script lives in this skill's own scripts/ directory.
Path — do NOT rely on
${CLAUDE_PLUGIN_ROOT}in bash. In the Cowork sandbox that env var is empty, souv run "${CLAUDE_PLUGIN_ROOT}/…"resolves to a broken path. Use the base directory reported for this skill when it loaded (it ends in/skills/carta-crm-home-build) as<SKILL_DIR>. If you do not have it, resolve it once with a scopedfind(NOTfind /):SKILL_DIR="$(dirname "$(dirname "$(find /sessions "$HOME" -type f -path '*/carta-crm-home-build/scripts/build_artifact.py' 2>/dev/null | head -1)")")"
uv run "<SKILL_DIR>/scripts/build_artifact.py" \
--resource "<path from Step 1>" \
--connector "<connector display name>" \
--organization "<organization from Step 0>" \
--build-id "<viewBuildId from Step 1>" \
--out <outputs-directory>/crm-home-<slug>.html
<slug> is the organization lowercased with non-alphanumerics collapsed to -, so two
organizations never write over each other's file.
The script writes three things and validates the rest:
<meta name="carta-connector">, where the page reads which connector to call.<meta name="carta-home-build">, the viewBuildId from Step 1. The page compares it against
viewBuildId on a fresh manifest call and shows a rebuild notice when the two disagree. Pass
it every time: without --build-id the page carries no stamp, and a page with no stamp never
reports being behind. This is also why the page must be fetched fresh rather than reused from
a previous run.<title> that names the artifact.It refuses a page that never calls get_crm_home, or one carrying the other views, so a wrong
resource stops here rather than reaching a URL.
Artifact({action: "list", scope: "mine"})
Look for one titled exactly Carta CRM Home - <Organization>. If it is there, keep its
url so Step 4 redeploys in place and the bookmark keeps working. If there is none, omit
url and this organization gets its own artifact.
An artifact for a different organization is not a match — reusing its url would replace
that organization's page with this one.
Two neighbours to leave alone. Carta Home - <firm> (favicon 🏠) belongs to
carta-investors' carta-home-build. Carta Home - <company> (favicon 📊) belongs to
carta-cap-table's carta-captable-home-build. Neither is a CRM Home, and publishing over one
would replace a colleague's dashboard.
action defaults to "publish", so it is omitted. url is the only difference between a
first publish and a redeploy.
Artifact({
file_path: "<outputs-directory>/crm-home-<slug>.html",
url: "<url from Step 3 — omit entirely on a first publish>",
description: "Your Carta CRM at a glance — pipeline by stage, recent deals and contacts, latest notes, and today's meetings.",
favicon: "📇",
label: "Rebuilt from the Carta CRM Home resource",
capabilities: {
mcp: {
servers: [
{
server: "<connector display name>",
tools: ["crm_read_tool"]
}
]
}
}
})
No
titlehere, and do not add one. The tool takes the title from the file's own<title>tag, which Step 2 set. Passing one as well would only apply if the tag were missing, and it would drift from the page.
Omit
faviconon a redeploy. A viewer finds their tab by its icon, so it stays 📇 for the life of the artifact.
Give the user the URL and tell them what it will and will not do:
If they report every card failing, the connector name is the first thing to check: a page
stamped with a name their session does not carry gets server_not_connected on every call.
Rebuild with the right name and republish to the same url.
It does not render the Home in the conversation. That is the home skill, and it is the
one that works everywhere the CRM MCP is reachable. This skill only publishes.
It does not choose between the two. The home skill selects a target and calls this one
when the user wants a page.
It does not build the page. If the Home looks wrong, the fix is in crm-api's
src/components/mcp/ui, not here. Rebuilding through this skill republishes whatever that
server currently serves.
原文・著作権は Anthropic および各プラグイン作者に帰属します。日本語訳は Claude API による自動翻訳です。