🍞

MCPサーバを安全に動かすノウハウやさらに強いランナーなど|Productivity Weekly(2025-04-16)

こんにちは。サイボウズ株式会社 生産性向上チーム@korosuke613です。

僕たち生産性向上チームは毎週水曜日に Productivity Weekly という「1 週間の間に発見された開発者の生産性向上に関するネタを共有する会」を社内で開催しています。
本記事はその時のネタをまとめたものです。

2023-01-25 号から、基本的に隔週で連載することとしました。たまに単独でも投稿するかもしれません。
今週は 2025-04-16 単独号です。

今回が第 186 回目です。過去の記事はこちら

news 📺

GitHub Actions hosted runner fleet now includes 96 vCPU larger runner - GitHub Changelog

https://github.blog/changelog/2025-04-09-github-actions-hosted-runner-fleet-now-incudes-96-vcpu-larger-runner/

GitHub Action において、GitHub-hosted runner として新たに 96 vCPU の larger runner が GA になりました。

RAM、SSD は固定のようで、次のような構成になっています。

  • architecture: x64
  • vCPU: 96 コア
  • RAM: 384 GB
  • SSD: 2040 GiB
  • OS: Linux or Windows

なかなかオーバースペックで必要とする人は少ないと思います。長時間実行されるビルドやテストがあるプロジェクトでは、このスペックに限らず、高性能なランナーを活用することで実行時間を大幅に短縮できる可能性があります。特に、並列処理が可能な大規模なテストスイートや複雑なビルドプロセスを持つプロジェクトで試してみるといいかもしれません。

本項の執筆者: @korosuke613

VSCode Copilot agent mode in Codespaces - GitHub Changelog

https://github.blog/changelog/2025-04-11-vscode-copilot-agent-mode-in-codespaces/

GitHub Issue から直接 Copilot エージェントモードを実行する Codespaces を起動できるようになりました(public preview)。

具体的には、GitHub Issue を閲覧している際に、右側の「Development」セクションに「Code with Copilot Agent Mode」ボタンが表示されるようになりました。このボタンをクリックすると、次の操作が自動的に行われます:

  1. 新しい Codespace が初期化される
  2. 新しいタブで Codespace が開かれる
  3. VSCode の Copilot エージェントモードが有効化され、Issue の内容がコンテキストとして読み込まれる

Copilot はその後、ローカルで命令を出したかのように Issue の内容に沿った変更をユーザーに提案し、そこから開発を進めることができます。

ローカル開発環境でも同じことができますが、この機能の嬉しいところは Issue からの実装作業をスムーズに開始できる点でしょうか。Issue の内容もあらかじめ読み込んでくれるため、命令を与える手間も省けますね(そのプロンプトで十分かは別)。

僕も実際に試してみました。僕のホームページのリポジトリで適当な Issue を作成し、「Code with Copilot Agent Mode」をクリック。

Codespaces が立ち上がったのですが、各種拡張機能のインストールなんかも始めて Codespaces の立ち上がりに結構時間がかかり、初回は Copilot の提案がそもそも表示されませんでした。ならばすでに環境が立ち上がっている Codespaces でやればいいじゃん、ということで、再度「Code with Copilot Agent Mode」をクリックしましたが、残念ながら新たな Codespaces 環境が立ち上がってしまい、またもや時間がかかりました。

2 回目ではちょっと待つと無事 Copilot Agent へ Issue の内容が渡されました。


おお!やってるやってる

最終的に変更は作ってくれたのですが、Codespaces のスペック(2 コア!そら遅いわけだ)を調整してなかったからかとにかく時間がかかりました。こればっかりはしょうがないですね。
さすがに 2 コアは時間がかかりすぎるのでデフォルトで立ち上がるマシンスペックを調整できるようにしてほしいです...

「Code with Copilot Agent Mode」の結果

korosuke613

  • /random を開くとランダムな記事ページの URL とその概要が表示される
  • ランダムな記事ページには Posts と Blogs の両方が含まれる

Copilot

こんにちは!「/random」にアクセスするとランダムな記事ページ(Posts と Blogs 両方から)を表示する機能を実装しましょう。まずは現在のプロジェクト構造をよく理解するために、関連するファイルを確認していきます。検索結果から、このプロジェクトは Astro.js を使ったウェブサイトで、Posts と Blogs という 2 種類のコンテンツを含んでいることがわかりました。まずはランダムな記事ページを表示する機能を実装するために、必要なファイルを作成していきます。

最初に、ランダムな記事を取得する機能を実装し、その後に /random エンドポイントを作成します。

1. まず、ランダムな記事を取得するためのユーティリティ関数を作成ユーティリティファイルを確認しました。記事のランダム表示機能を実装するために、まずはutilsディレクトリに新しいユーティリティファイルを作成し、その後 /random ページを作成していきましょう。

1. まず、ランダムな記事を取得するユーティリティ関数を作成

ランダムな記事(Posts と Blogs の両方)を取得する機能を /src/utils/Random.ts に実装します。

import { getCollection } from "astro:content";
import { getSortedBlogData } from "./Blog";
import type { BlogData } from "@/types/IBlogPage";
import type { IPost } from "@/types/IArticleFrontmatter";
import { AppConfig } from "./AppConfig";
import path from "node:path";

export type RandomArticle = {
  title: string;
  description: string;
  url: string;
  type: "post" | "blog";
};

/**
 * PostsとBlogsからランダムな記事を1つ取得する
 */
export const getRandomArticle = async (): Promise<RandomArticle> => {
  // Postsのデータを取得
  const posts = await getCollection("posts");
  const postsData: IPost[] = posts.map((post) => ({
    data: post.data,
    slug: post.slug,
    collection: post.collection,
    id: post.id,
  }));

  // Blogsのデータを取得
  const blogsData = await getSortedBlogData();

  // 記事の合計数を計算
  const totalArticles = postsData.length + blogsData.length;

  // ランダムなインデックスを生成
  const randomIndex = Math.floor(Math.random() * totalArticles);

  // インデックスに基づいて記事を選択
  if (randomIndex < postsData.length) {
    // Postsから記事を選択
    const post = postsData[randomIndex];
    return {
      title: post.data.title,
      description: post.data.description || "",
      url: path.join(AppConfig.base, "posts", post.slug),
      type: "post",
    };
  } else {
    // Blogsから記事を選択
    const blog = blogsData[randomIndex - postsData.length];
    return {
      title: blog.title,
      description: "",
      url: blog.url,
      type: "blog",
    };
  }
};

変更を行いました。

ランダムな記事を取得するためのユーティリティ関数を作成しました。次に、このユーティリティを使用して /random ページを作成します。

2. /random ページの作成

Astro では、src/pages/random.astro を作成することで /random のエンドポイントを実装できます。このページにアクセスすると、ランダムな記事に自動的にリダイレクトするように設計します。

---
import { getRandomArticle } from "@/utils/Random";
import { Base } from "@/templates/Base";

// ランダムな記事を取得
const randomArticle = await getRandomArticle();
---

<Base head={{ title: "ランダム記事", description: "ランダムな記事ページにリダイレクトします" }}>
  <div class="flex flex-col items-center justify-center min-h-screen py-10">
    <div class="text-center max-w-xl">
      <h1 class="text-3xl font-bold mb-4">ランダム記事にリダイレクト中...</h1>
      <p class="mb-8">
        <span class="block mb-2">以下の記事に移動します:</span>
        <span class="font-medium text-xl">{randomArticle.title}</span>
        {randomArticle.description && (
          <span class="block mt-2 text-gray-300">{randomArticle.description}</span>
        )}
      </p>
      <a 
        href={randomArticle.url} 
        class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded transition-colors"
      >
        今すぐ記事に移動
      </a>
      <p class="mt-8 text-sm text-gray-400">
        自動的にリダイレクトされない場合は、上のボタンをクリックしてください。
      </p>
    </div>
  </div>
</Base>

<script define:vars={{ url: randomArticle.url }}>
// ページ読み込み時に自動的にリダイレクトする
window.onload = () => {
  setTimeout(() => {
    window.location.href = url;
  }, 1500); // 1.5秒後にリダイレクト
};
</script>

変更を行いました。

ランダム記事機能の実装ができました。次に、Navbar(ナビゲーションバー)にランダム機能へのリンクを追加しましょう。ユーザーがナビバーから簡単にアクセスできるようにします。

3. Navbarにランダム記事へのリンクを追加

Navbar のコンポーネントを確認して、ランダム記事へのリンクを追加します。Navbar コンポーネントの構造を確認しました。次に、実際に Navbar がどのように使われているか確認してから、ランダム記事へのリンクを追加しましょう。Navbar の構造を確認できました。次に、Navbar.astro ファイルにランダム記事へのリンクを追加します。

<NavMenu>
        <NavMenuItem href="posts">Posts</NavMenuItem>
        <NavMenuItem href="blogs">Blogs</NavMenuItem>
        <NavMenuItem href="random">Random</NavMenuItem>
        <NavMenuExternalLink
          title="GitHub"
          url="https://github.com/korosuke613"
        />
        <NavMenuExternalLink
          title="Twitter"
          url="https://twitter.com/shitimi_613"
        />

変更を行いました。

これでランダム記事機能の実装が完了しました!実装したのは次の 3 つの部分です:

  1. ランダム記事を取得するユーティリティ関数 (/src/utils/Random.ts):

    • Posts と Blogs の両方から記事をロードし、ランダムに 1 つ選ぶ関数を作成
    • 選ばれた記事のタイトル、説明文、URL、タイプ(post か blog)を返す
  2. ランダム記事ページ (/src/pages/random.astro):

    • /random にアクセスするとランダムな記事を取得
    • 記事のタイトルと説明を表示し、自動的に記事ページへリダイレクト
    • 1.5 秒間の待機時間を設けて、ユーザーが何が起きているか理解できるようにしています
    • 自動リダイレクトが機能しない場合に備えて、手動クリックできるボタンも用意
  3. ナビゲーションバーへのリンク追加:

    • 「Posts」と「Blogs」のメニュー項目の間に「Random」リンクを追加
    • これにより、サイトのどのページからでも簡単にランダム記事機能にアクセスできます

この実装により、ユーザーは「Random」ボタンをクリックするだけで、サイト内のどこからでもランダムな記事を閲覧できるようになりました。Posts と Blogs の両方から記事がランダムに選ばれるため、ユーザーは意外な記事と出会う機会が増え、サイトの利用体験が向上します。

ある程度はいい感じに作れました。あとは調整するという感じでしたね。
自分は Codespaces を普段使いしてないため、そもそも環境構築を高速にするための策を練るのが良いんだろうなと思いましたが、そういう意味ではこれをどのリポジトリでも手軽に行うのはむずそうだなという印象でした。結局ローカルでやった方が早いってなりそう。とはいえ普段から触れてないリポジトリで行う分には環境を用意しなくていいの楽かもしれませんね。

また、結局 Codespaces 上でのやり取りが必要なため、どうせなら裏側で全部やってさっさとプルリク作ってくれた方が嬉しい説あるなと思いました。
それがまさに先日取り上げた「Project Padawan」かもしれませんね。

本項の執筆者: @korosuke613

GitHub Actions token integration now generally available in GitHub Models - GitHub Changelog

https://github.blog/changelog/2025-04-14-github-actions-token-integration-now-generally-available-in-github-models/

GitHub Actions にて、GITHUB_TOKEN で GitHub Models を使用できるようになりました。
実行には次の権限が必要ですので、忘れずに明記しましょう。

permissions:
  models: read

実装の例はこちらにあります。

https://docs.github.com/en/github-models/integrating-ai-models-into-your-development-workflow#using-ai-models-with-github-actions

例では固定のプロンプトになっていますが、例えば直前のジョブが失敗した時にエラーログを入力として与えて説明させるとか、いろんな活用方法が考えられそうです。

また、この変更に伴ってか、GitHub Actions から簡単に GitHub Models を呼び出せる公式のアクションも登場しています。

https://github.com/actions/ai-inference

なお、API の制限については、こちらのドキュメントに詳しく記載がありますので、使用前に確認すると良さそうです。

https://docs.github.com/ja/github-models/prototyping-with-ai-models

本項の執筆者: @defaultcf

know-how 🎓

MCPサーバーを安全に動かすための工夫

https://blog.lai.so/crashing-mcp-server/

MCP サーバーは Docker + Deno で動かそうぜ!という啓蒙記事です。

概要を簡単に説明します。まず、MCP の実装方法の 1 つである Standard Input/Output 方式では、npx や uvx を通じて、インターネットからダウンロードしたスクリプトをそのまま実行することがままあります。これは大変危険な行為で、スクリプト、あるいはスクリプトが依存するライブラリに悪意のあるコードが含まれる場合、簡単に攻撃を許してしまいます。そこで、ある程度 isolate された Docker 環境にて、パーミッションを細かく設定できる Deno で MCP サーバーを実行すれば、セキュリティ強度をより高められるのではないか、と提案されています。

Deno の面白い使い方だなと感じました。一方で、Weekly の中では、Deno だと MCP を実装するプログラムが TypeScript に限定されるという声や、Docker だけだとネットワークまで制限するのが簡単ではないとの指摘もありました。この領域でも Wasm 頑張って欲しいですね。

本項の執筆者: @ajfAfg

Terraform設計ガイドライン | Future Enterprise Arch Guidelines

https://future-architect.github.io/arch-guidelines/documents/forTerraform/terraform_guidelines.html

Terraform を実運用する上で迫られる数々の判断(e.g. ファイルはどう分ける?、Terraform はどこで実行する?)の論点と考え方を整理してまとめたドキュメントです。Tips 集って見方もできそう。

Terraform を使う上でベースラインとしたい知識が詰まってて良かったです。生産性向上チームの Terraform 運用ももっと良くしていけそうだなと感じました。

本項の執筆者: @ajfAfg

tool 🔨

tsconfig.json paths やめ方

https://zenn.dev/mkizka/articles/a6148876fa46bd

TypeScript のパスエイリアス(@/foo みたいなやつ)は最近推奨されてないがち[要出典]だけど、すでにプロジェクトに存在しているから使い続けるしかない!辛い!、というあなたに向けた処方箋の紹介記事です。紹介されているツールを使うと、パスエイリアスを相対パスに変換できるとのことです。

息の長いプロジェクトで活用できるかもです。動的 import にも対応してるのかは気になりました。

本項の執筆者: @ajfAfg

read more 🍘

Productivity Weekly で出たネタを全て紹介したいけど紹介する体力が持たなかったネタを一言程度で書くコーナーです。

  • news 📺
    • AWS IAM Identity Center now offers improved error messages and AWS CloudTrail logging for provisioning issues - AWS
      • AWS IAM Identity Center のユーザー/グループ同期時のエラーメッセージが改善され、より詳細なトラブルシューティング情報が提供されるようになりました
      • SCIM または AD 同期使用時に具体的なエラー内容(メールアドレス制約違反、重複グループ名など)が表示されるようになったようです
      • また、CloudTrail に各種 SCIM API 操作(CreateUser、DeleteGroup など)のイベントが記録され、監査やモニタリングが容易になったとか

本項の執筆者: @korosuke613

あとがき

遅れに遅れてすみません!!先週号でも書きましたが、4 月はアホみたいに他にやることがありました...
早くも 5 月ですね。2025 年も 1/3 が終わってしまいました。早いですね。

サイボウズの生産性向上チームでは社内エンジニアの開発生産性を上げるための活動を行なっています。そんな生産性向上チームが気になる方は下のリンクをクリック!
https://www.docswell.com/s/cybozu-tech/5R2X3N-engineering-productivity-team-recruitment-information


サイボウズ 生産性向上チーム 💪

Discussion