マキモノ
開発プロセス無料✅ 公式検証済みv1.0.0 / 更新

依存ゼロのMCPサーバを作り、npm公開に詰まってもnpx github:で今日から配る

自作ツールをAIエージェントから使わせるMCPサーバを依存パッケージゼロで実装し、npm公開が2FA制約で止まっても配布を成立させる手順。JSON-RPC自前実装の要点4つ、二刀流bin、.mcp.json自動登録、実ハンドシェイク検証、そしてbinの./で公開物が壊れる罠まで。

出品者: nishi@orgiast.jp📖 読込 約3,876トークン (約6円)💰 コスパ 94倍
トークン節約メーター87%節約
ゼロからAIに作らせた場合約42万トークン
このMDを読ませた場合約5.5万トークン

約36.5万トークンの節約 (API料金換算で約550円分)。 要件定義・技術調査・試行錯誤ぶんのトークンがまるごと不要になります。※ 出品者申告とレビューに基づく推定値。モデル・タスク内容により変動します。

この巻物について

「依存ゼロのMCPサーバを作り、npm公開に詰まってもnpx github:で今日から配る」は、開発プロセスカテゴリのAI指示書(MDファイル)です。自作ツールをAIエージェントから使わせるMCPサーバを依存パッケージゼロで実装し、npm公開が2FA制約で止まっても配布を成立させる手順。JSON-RPC自前実装の要点4つ、二刀流bin、.mcp.json自動登録、実ハンドシェイク検証、そしてbinの./で公開物が壊れる罠まで。この巻物をAIに読み込ませると、ゼロから設計・調査する場合に比べて 約36.5万トークン(API料金換算で約550円)・87%のトークンを節約できます。

カテゴリ
開発プロセス
対応AI
claude-code、cursor、codex-cli
ライセンス
商用利用可 (再販不可)
価格
無料
ゼロから開発時
約42万トークン
この巻物使用時
約5.5万トークン
節約量
約36.5万トークン (約550円)
更新日
2026-09-30

使い方 (AIに渡す3つの方法)

いちばん簡単なのはワンライナー。Claude Code のターミナルに貼るだけです。

⬇ .md をダウンロード
claude "https://makimono-md.vercel.app/api/v1/files/mcp-npm-npx-github/raw を読み込んで、この指示書どおりに実装して"
claude-codecursorcodex-cliライセンス: 商用利用可 (再販不可)

中身

依存ゼロの MCP サーバを作り、npm 公開に詰まっても今日から配る

自作ツールを AI エージェント(Claude Code / Cursor / Codex 等)から使わせたいとき、 MCP サーバにするのが最短です。ただし「作れたのに配れない」で止まるのが典型的な失敗で、 その原因はほぼ npm 公開の認証 です。この指示書は、依存パッケージゼロで MCP サーバを書き、 npm 公開を待たずに配布を成立させるところまでを一気に作ります。

作るもの

  1. 依存パッケージゼロの MCP stdio サーバ(JSON-RPC 2.0 を自前で話す)
  2. 同じ実行ファイルが CLI としても動く二刀流の bin
  3. 利用者の .mcp.json に自動登録する init サブコマンド
  4. 実際のハンドシェイクで検証する自動テスト
  5. npm 公開が通らなくても配れる導線

なぜ依存ゼロにするか

MCP の公式 SDK を使ってもよいのですが、npx で起動する配布形態では 初回の依存インストール時間がそのまま利用者の体感待ち時間になります。 stdio の MCP は「改行区切りの JSON-RPC 2.0」でしかないので、自前実装は 150 行程度です。 依存を増やさない方が起動が速く、サプライチェーンも増えません。

ステップ1: MCP stdio サーバ

src/mcp.mjs を作る。押さえるべき仕様は4点です。

const PROTOCOL_VERSION = "2024-11-05";

const TOOLS = [
  {
    name: "search_something",
    // description は「AI がいつ呼ぶべきか」を書く。機能説明だけだと呼ばれない
    description: "…を検索する。◯◯の依頼を受けたら、着手する前にまずこれを呼ぶこと。",
    inputSchema: {
      type: "object",
      properties: { query: { type: "string", description: "検索キーワード" } },
      required: ["query"],
    },
  },
];

function send(obj) {
  process.stdout.write(JSON.stringify(obj) + "\n");
}

async function handle(msg) {
  const { id, method, params } = msg;
  const isNotification = id === undefined || id === null;

  try {
    if (method === "initialize") {
      return send({
        jsonrpc: "2.0",
        id,
        result: {
          protocolVersion: params?.protocolVersion ?? PROTOCOL_VERSION,
          capabilities: { tools: {} },
          serverInfo: { name: "yourtool", version: "0.1.0" },
        },
      });
    }
    // (1) 通知には絶対に応答しない。返すとクライアントが壊れる
    if (method?.startsWith("notifications/")) return;
    if (method === "ping") return send({ jsonrpc: "2.0", id, result: {} });
    if (method === "tools/list") return send({ jsonrpc: "2.0", id, result: { tools: TOOLS } });
    if (method === "tools/call") {
      const result = await runTool(params?.name, params?.arguments ?? {});
      return send({ jsonrpc: "2.0", id, result });
    }
    if (isNotification) return;
    send({ jsonrpc: "2.0", id, error: { code: -32601, message: `Method not found: ${method}` } });
  } catch (e) {
    // (2) ツール実行の失敗は JSON-RPC error ではなく isError の結果で返す(MCP の作法)
    //     error で返すとクライアントによっては接続ごと落ちる
    if (method === "tools/call") {
      return send({
        jsonrpc: "2.0",
        id,
        result: { content: [{ type: "text", text: `失敗: ${e.message}` }], isError: true },
      });
    }
    send({ jsonrpc: "2.0", id, error: { code: -32603, message: String(e.message ?? e) } });
  }
}

export function start() {
  // (3) 1回の data で複数行届くことがある。必ずバッファして改行で分割する
  let buffer = "";
  process.stdin.setEncoding("utf8");
  process.stdin.on("data", (chunk) => {
    buffer += chunk;
    let i;
    while ((i = buffer.indexOf("\n")) >= 0) {
      const line = buffer.slice(0, i).trim();
      buffer = buffer.slice(i + 1);
      if (!line) continue;
      try { void handle(JSON.parse(line)); }
      catch { send({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "Parse error" } }); }
    }
  });
  // (4) stdin が閉じたら終了する。しないとプロセスが残り続ける
  process.stdin.on("end", () => process.exit(0));
}

ステップ2: 二刀流の bin

利用者に覚えさせるコマンドは1つで済ませます。 引数なしなら MCP サーバ、引数ありなら CLI にします。

#!/usr/bin/env node
const CLI_COMMANDS = new Set(["search", "get", "init", "help", "--help", "-h", "--version"]);
const first = process.argv[2];

if (first && CLI_COMMANDS.has(first)) {
  await import("./cli.mjs");
} else if (first) {
  console.error(`不明なコマンド: ${first}`);
  process.exitCode = 1;
} else {
  const { start } = await import("../src/mcp.mjs");
  start();
}

ステップ3: init で .mcp.json に自動登録させる

利用者に JSON を手編集させないこと。既存設定を壊さないよう、必ずバックアップを取ります。

const file = path.resolve(process.cwd(), ".mcp.json");
let conf = { mcpServers: {} };
if (fs.existsSync(file)) {
  try { conf = JSON.parse(fs.readFileSync(file, "utf8")); }
  catch { console.error(".mcp.json が壊れているため中止しました"); process.exitCode = 1; return; }
  fs.copyFileSync(file, `${file}.bak`);
}
conf.mcpServers ??= {};
if (conf.mcpServers.yourtool) { console.log("すでに登録済みです。"); return; }
conf.mcpServers.yourtool = { command: "npx", args: ["-y", "github:<owner>/<repo>"] };
fs.writeFileSync(file, JSON.stringify(conf, null, 2) + "\n", "utf8");

ステップ4: 本物のハンドシェイクで検証する

「起動した」だけでは検証になりません。子プロセスを立ち上げて JSON-RPC を流し込み、 応答の形まで assert します。

test("initialize / tools/list が MCP の形で返る", async () => {
  const out = await rpc([
    { jsonrpc: "2.0", id: 1, method: "initialize",
      params: { protocolVersion: "2024-11-05", capabilities: {} } },
    { jsonrpc: "2.0", method: "notifications/initialized" },
    { jsonrpc: "2.0", id: 2, method: "tools/list" },
  ]);
  assert.ok(out.find((m) => m.id === 1).result.capabilities.tools);
  for (const t of out.find((m) => m.id === 2).result.tools) {
    assert.equal(t.inputSchema.type, "object");
  }
});

test("通知には応答しない / 未知メソッドは -32601", async () => {
  const out = await rpc([
    { jsonrpc: "2.0", method: "notifications/cancelled" },
    { jsonrpc: "2.0", id: 9, method: "no/such/method" },
  ]);
  assert.equal(out.length, 1);        // 通知に応答していないこと
  assert.equal(out[0].error.code, -32601);
});

ステップ5: 配布 — ここが本当の難所

npm 公開は認証で止まりやすい

npm publish はいま、次のどちらかが無いと 403 で弾かれます。

403 Forbidden - Two-factor authentication or granular access token
with bypass 2fa enabled is required to publish packages.

しかも bypass 2FA トークンは段階的に廃止が告知されています (アカウント変更系は既に制限、直接公開も近く不可)。 そして次はすべて塞がっています。時間を溶かす前に知っておくことです。

試す人が多い手実際の結果
npm token create で自動発行npm password: の対話プロンプトで停止。stdin を閉じても同じ
npm publish --auth-type=web同じ 403
認証アプリのコードを --otp= で渡すアカウントの 2FA が無効ならコード自体が存在しない(npm profile get で確認できる)

つまり ブラウザでのトークン発行が必須で、CLI だけでは詰みます。

逃げ道: npm 公開なしで npx を成立させる

npx は GitHub リポジトリを直接指定できます。公開は不要です。

npx -y github:<owner>/<repo>

MCP クライアントへの登録もそのまま書けます。

{ "mcpServers": { "yourtool": { "command": "npx", "args": ["-y", "github:<owner>/<repo>"] } } }

この形なら、npm アカウントも、トークンも、将来の廃止予定も関係ありません。 まず配れる状態にしてから、npm 公開は「見つけてもらうため」の任意作業に降格させるのが正解です。

踏んだ罠(先に知っておくと1時間浮く)

罠1: package.json の bin に ./ を付けると bin ごと消える

npm warn publish "bin[yourtool]" script name ./bin/yourtool.mjs was invalid and removed

./bin/x.mjs ではなく bin/x.mjs と書くこと。 これに気づかず公開すると bin の無いパッケージが世に出て、npx しても何も起きません。 npm publish --dry-run を必ず先に実行し、この warn が出ないことを確認します。

罠2: npx github: は GitHub の内容を取りに行く(ローカルではない)

配布形態を変えたあと、ローカルを直しただけでテストすると古い挙動のままです。 init が古い設定を書き続ける、といった形で表面化します。 push してからテストする。検証時は npx のキャッシュも消してから実行します。

罠3: description は「機能」ではなく「いつ呼ぶか」を書く

inputSchema が正しくても、description が機能説明だけだと AI はツールを呼びません。 「◯◯の依頼を受けたら、着手する前にまずこれを呼ぶこと」のように発火条件を書きます。

完成条件

次がすべて通ったら完成です。

  1. npm publish --dry-run で bin の warn が出ない
  2. ハンドシェイクのテストが通る(initialize / tools/list / tools/call / 通知無応答 / エラー時もプロトコルが壊れない)
  3. npx キャッシュを消した状態で npx -y github:<owner>/<repo> が initialize に正しく応答する
  4. 同じく npx -y github:<owner>/<repo> init が .mcp.json に現在の設定を書く

よくある質問

+「依存ゼロのMCPサーバを作り、npm公開に詰まってもnpx github:で今日から配る」とは何ですか?

自作ツールをAIエージェントから使わせるMCPサーバを依存パッケージゼロで実装し、npm公開が2FA制約で止まっても配布を成立させる手順。JSON-RPC自前実装の要点4つ、二刀流bin、.mcp.json自動登録、実ハンドシェイク検証、そしてbinの./で公開物が壊れる罠まで。

+どれくらいトークン(費用)を節約できますか?

ゼロから開発すると約42万トークンかかりますが、この巻物を使えば約5.5万トークンで済みます。差し引き約36.5万トークン(API料金換算で約550円)・87%の節約です。

+どうやって使いますか?

無料です。MDファイルを Claude Code などのAIに読み込ませるだけ。ワンライナーをターミナルに貼れば実装が始まります。要件定義や技術調査を省いて実装だけにトークンを使えます。

+どのAIツールに対応していますか?

claude-code、cursor、codex-cli に対応しています。

+商用利用できますか?

ライセンスは「商用利用可 (再販不可)」です。

🤝 自分でAIを動かすのは、まだ不安…という方へ

この巻物の内容を、AIを使うプロに丸ごと任せることもできます。姉妹サービスAI代行堂なら「LINEで頼むだけで、仕事が完成」。

AI代行堂を見る →

関連する巻物

この巻物、誰かのトークンも救えます

𝕏 で節約レシートをシェア