まくろぐ
更新: / 作成:

何をやるか?

/p/s3subpp/img-001.png
図: 小指で IME の ON/OFF

Mac や Windows の日本語入力のオン/オフ (IME ON/OFF) 切り替えを、左手の小指だけでできるようにしてみます。

これまでずっと、Cmd + Space などのキーコンビネーションで IME の ON/OFF をしていたのですが、やはり IME ON/OFF 切り替えは別々のキーに割り当てた方が望ましい と思い、このたびがんばって設定することにしました。

新しい入力方法に慣れれば、「今 IME ON だっけ? OFF だっけ?」と悩むことがなくなるので、毎日のストレスがほんの少しだけ軽減すると思います。 また、Cmd + Space などのキーコンビネーションが、アプリケーションのショートカットキーとバッティングしてしまうトラブルも避けられます。

前提条件は次の通りです。

  • macOS と Windows の両方で同じようにキー操作できること
  • キーコンビネーションではなく、異なる単独キーで IME ON/OFF できること
  • US(英字配列)キーボードを使うので、「かな」「英数」などのキーがなくてもよいこと
  • 無料のソフトで設定できること

最終的には、次のように設定することにしました。

  • 使用するアプリケーション
  • キー設定
    • CapsLock キーの単押し → IME OFF
    • Shift キーの単押し → IME ON

今回は左小指で押せるキーを使いましたが、スペースキーの左右にある Cmd キーや Alt キーに割り当てるのもありだと思います。 最初は左右の Shift キーに割り当ててみたのですが、どうしても右側の Shift キーが押しにくく、上記のようなキー割り当てに落ち着きました。

macOS の設定

macOS では Karabiner-Elements を使って設定します。 メニューから、Complex ModificationsAdd your own rule を選択し、下記のように設定して保存すれば完了です。

Karabiner-Elements 用の設定ファイル
{
    "description": "CapsLockキー単押しでIMEオフ、左Shiftキー単押しでIMEオン",
    "manipulators": [
        {
            "from": {
                "key_code": "caps_lock",
                "modifiers": { "optional": ["any"] }
            },
            "parameters": {
                "basic.to_if_alone_timeout_milliseconds": 250,
                "basic.to_if_held_down_threshold_milliseconds": 250
            },
            "to": [
                {
                    "key_code": "caps_lock",
                    "lazy": true
                }
            ],
            "to_if_alone": [{ "key_code": "japanese_eisuu" }],
            "to_if_held_down": [{ "key_code": "caps_lock" }],
            "type": "basic"
        },
        {
            "from": {
                "key_code": "left_shift",
                "modifiers": { "optional": ["any"] }
            },
            "parameters": {
                "basic.to_if_alone_timeout_milliseconds": 250,
                "basic.to_if_held_down_threshold_milliseconds": 250
            },
            "to": [
                {
                    "key_code": "left_shift",
                    "lazy": true
                }
            ],
            "to_if_alone": [{ "key_code": "japanese_kana" }],
            "to_if_held_down": [{ "key_code": "left_shift" }],
            "type": "basic"
        }
    ]
}

Karabiner-Elements は本当に色々な設定ができるので、使いこなせるようになったらすごく便利そうです。 でもやりすぎは禁物。

Windows の設定

Windows の場合は、AutoHotkey というソフトウェアを使います。 macOS のときより、若干手順は複雑です。

1. CapsLock キーと Control キーと入れ替える

まず、A キーの左にある CapsLock キーを Control キーに入れ替えておきます。 Happy Hacking Keyboard など、ハードウェアスイッチがあるキーボードの場合はそれで設定すれば OK です。

キーの入れ替えは、Microsoft が提供している下記のいずれかのツールで簡単に設定できます。

  • Ctrl2Cap.exe
    • キー入れ替え専用のツールです。
  • PowerToys
    • いろんな便利ツールが入ってるので、この中の Keyboard Manager という機能を使います。

2. IME の設定を変更す

次に、IME の設定を変更し、無変換 キーを「IME-オフ」、変換 キーを「IME-オン」に割り当てておきます。 Windows の設定から次のように開き、

  1. IME で検索
  2. 日本語 IME 設定 を選択
  3. キーとタッチのカスタマイズ を選択

次のように設定します。

/p/s3subpp/img-002.png
図: Windows の IME 設定

3. AutoHotkey のスクリプトの作成

最後に、AutoHotkey 用のスクリプトファイルを作成します。 .ahk という拡張子を付けて適当なディレクトリに保存してください。

C:\config\maku-keyconfig.ahk
#Requires AutoHotkey v2.0
; https://maku.blog/p/s3subpp/

; 左Controlキーが単独で離されたときの処理
~LControl Up::
{
    if (A_PriorKey = "LControl") {
        Send "{vk1Dsc07B}"  ; 「無変換」キーをエミュレート (IME OFF)
    }
}

; 左Shiftキーが単独で離されたときの処理
~LShift Up::
{
    if (A_PriorKey = "LShift") {
        Send "{vk1Csc079}"  ; 「変換」キーをエミュレート (IME ON)
    }
}

ここでは、左 Control キーを単独で押しときに 無変換 キーを押したかのように振る舞い、左 Shift キーを押したときに 変換 キーを押したかのように振る舞うように設定しています。

AutoHotkey がインストールされた環境でこのスクリプトファイルをダブルクリックして起動すると、キーの入れ替えが有効になります。 無事に起動すると、タスクトレイに アイコンが表示されます。

Windows の起動時にこの設定を有効にしたいときは、このファイルのショートカットを、Windows のスタートアップフォルダに作成しておきます。 スタートアップフォルダは、Win + Rshell:startup で開くことができます。

関連記事

更新: / 作成:

MCP とは

MCP (Model Context Protocol) は、AI アプリケーションが外部のツールと連携するためのプロトコルで、Anthropic によってオープンソース化されました。 この標準化されたプロトコルに従って MCP サーバーを作っておくと、Claude Desktop や VS Code(GitHub Copilot 拡張)などの AI アプリケーション (MCP host) から簡単に接続できるようになります。 2025 年に入った頃から MCP はエージェント連携のためのプロトコルとしてデファクトスタンダードとみなされており、今後はさまざまな MCP サーバー、およびそれらをサポートする AI アプリケーションが登場してくると思われます。

有名どころの MCP サーバー実装は、下記リンク先にまとめられていたりします。

MCP サーバーは Python や Typescript 用のライブラリを使うと簡単に実装することができます。 MCP がどのような仕組みで動いているのかを理解するには、Hello World 的な MCP サーバーを作ってしまうのが一番早いです。 ここでは、Python で簡単な MCP サーバーを作って、AI アプリ(Claude Desktop と VS Code)と連携させてみます。

プロジェクトの作成

まず、Python のプロジェクトを作成します。 ここでは、プロジェクト管理ツールとして uv を使用しますuv は Python スクリプト実行時に必要なライブラリを自動でインストールしてくれるので、複数の MCP サーバーを立ち上げるようなケースでは特に便利です(MCP サーバーごとに手動で venv 環境に入らなくて済みます)。

$ uv init my-mcp-server
$ cd my-mcp-server

次に、必要な MCP 関連ライブラリをインストールします。 mcp[cli] と指定することで、基本ライブラリに加えて開発用の mcp コマンド(CLI ツール)が使えるようになります。

$ uv add "mcp[cli]"

MCP サーバーの実装

下記は、2つの整数の足し算を行うだけの簡単なツール (add) を提供する MCP サーバーの実装例です。

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("Demo")

# ツールの実装
@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b

if __name__ == "__main__":
    # MCPサーバーを起動
    mcp.run(transport="stdio")

簡単ですね!

@mcp.tool() デコレーターは、この MCP サーバー(mcp オブジェクト)が add というツール(≒関数)を提供することを示しています。 1 つの MCP サーバーで複数のツールを公開することができます。 @mcp.tool() というデコレーターを使うには、先に mcp オブジェクトを生成しておく必要があることに注意してください。

add 関数の Add two numbers. という docstring は、AI アプリがこのツールの役割を把握するために参照します。 できるだけ簡潔に分かりやすく記述するようにしてください。 今回の例くらいの簡単な関数では必要ありませんが、通常は各引数の説明も書いておくとよいです。

Claude Desktop からの使用

AI アプリ(= MCP ホスト)としては、まずは本家 Anthropic の Claude Desktop を使ってみるのがよいでしょう。 Claude Desktop の設定ファイル claude_desktop_config.json を開いて、上記の MCP サーバーの起動方法を記述するだけでツール連携できるようになります。 設定ファイルは下記のパスを直接エディタで開くか、Claude Desktop の「設定」メニューから開きます。

  • macOS/Linux: ~/Library/Application\ Support/Claude/claude_desktop_config.json
  • Windows: $env:AppData\Claude\claude_desktop_config.json

以下の設定例では、“my-mcp-server” という名前の MCP サーバーを uv run main.py コマンドで起動できるということを Claude Desktop に教えています。

claude_desktop_config.json
{
    "mcpServers": {
        "my-mcp-server": {
            "command": "uv",
            "args": [
                "--directory",
                "/Users/maku/my-mcp-server",
                "run",
                "main.py"
            ]
        }
    }
}

/Users/maku/my-mcp-server の部分は、実際のプロジェクトのディレクトリパスに置き換えてください。 設定ファイルを編集したら、Claude Desktop を再起動して反映させます。

☝️ 絶対パスでの指定 プロジェクトのディレクトリパスは上記のように絶対パスで指定する必要があるようです(Claude バージョン 0.9.2 で確認)。 ホームディレクトリを示す ~/ なども使えません。 Claude Desktop の起動時に Spawn uv ENOENT のようなエラーが出た場合は、uv コマンドを見つけられていません。 この場合は、uv コマンドの絶対パスを which uv で調べて command プロパティに指定してください(例: /Users/maku/.cargo/bin/uv)。

Claude Desktop が起動したら、123 + 321 の計算をお願いしてみます。 最近の LLM は賢くなっていて、単純な計算は自力で計算してしまうことが多いので、ここでは MCP サーバーを使って と明示しています。

/p/5c9y6vt/img-001.png
図: Claude Desktop で MCP サーバーを使用する

応答の中のアコーディオンを開くと、MCP サーバー (my-mcp-server) にどのような入力をして、どのような出力が得られたかを確認することができます。

VS Code(GitHub Copilot 拡張)からの使用

Visual Studio Code の GitHub Copilot 拡張機能でも MCP サーバーを使用することができます。

下記のような設定ファイルを VS Code 上で作成し、エディタ上に表示される「起動」ボタンを押すと MCP サーバーが起動します。

.vscode/mcp.json
{
  "servers": {
    "my-mcp-server": {
      "command": "uv",
      "args": [
        "--directory",
        "/Users/maku/my-mcp-server",
        "run",
        "main.py"
      ]
    }
  }
}

これで、GitHub Copilot 拡張のチャットウィンドウから MCP サーバーの機能にアクセスできるようになります。 ちなみに、プロンプト内に #add のようにツール名を明記すると確実にそのツールを使ってくれます。

/p/5c9y6vt/img-002.png
図: VS Code(GitHub Copilot 拡張)で MCP サーバーを使用する

できた! ٩(๑❛ᴗ❛๑)۶ わーぃ

関連記事

更新: / 作成:

何をするか?

Svelte 5 のコンポーネントで使用する状態変数は $state ルーンを使って定義できますが、ページをリロードするとその値はリセットされてしまいます。 この記事では、$state ルーンで定義した状態変数の値を localStoragesessionStorage と同期させる方法を紹介します。 localStorage と同期させた場合は、ブラウザを開き直したときにも以前の状態を復帰できます。 sessionStorage と同期させた場合は、ブラウザのタブを開いている間だけ状態を保持できます。

$state ルーンを使って状態変数を共有する方法は、下記の記事を参照してください。

実装例

/p/tpjvh3q/img-001.png
図: サインイン状態を管理する AuthState クラスの実装と使用例

ここでは、サインイン状態を管理する AuthState クラスを作成し、その userName プロパティ(状態変数)を localStorage に保存する例を示します。 サインイン状態を示す signedIn プロパティも用意していますが、こちらは $derived ルーンで定義されており、userName プロパティの値に応じて自動的に値が決まるようになっています(userName がセットされているときに true になります)。

src/lib/auth-state.svelte.ts
import { onMount } from 'svelte';

const STORAGE_KEY = 'userName';

// 複数のコンポーネントで状態を共有するため、リアクティブな変数をグローバルに定義します
let userName = $state('');
let signedIn = $derived(!!userName);

export class AuthState {
	/** サインインしているユーザー名を参照するためのプロパティ (reactive) */
	get userName(): string {
		return userName;
	}

	/** サインインしているかどうかを示すプロパティ (reactive) */
	get signedIn(): boolean {
		return signedIn;
	}

	constructor() {
		// ページが読み込まれたときに localStorage の値で状態変数を初期化します
		// (このコードはコンポーネントの初期化中に呼び出す必要があります)
		onMount(() => {
			userName = localStorage.getItem(STORAGE_KEY) || '';
		});
	}

	signIn(): void {
		// 実際には正しいユーザー情報やトークンを取得して保存します
		userName = 'Maku';
		localStorage.setItem(STORAGE_KEY, userName);
	}

	signOut(): void {
		userName = '';
		localStorage.removeItem(STORAGE_KEY);
	}
}

AuthState クラスのコンストラクターでは、Svelte のライフサイクルフックである onMount() 関数を使って、ページが読み込まれたときに localStorage から userName の値を取得しています。 これにより、ページをリロードしたときに以前の状態を復元できます。 Svelte の仕組み上、onMount 関数はコンポーネントの初期化中にしか呼び出せないため、new AuthState() というインスタンス化コードはコンポーネント側に記述する必要があります(後述)。

signIn() および signOut() メソッドは、userName 状態変数の値を変更すると同時に localStorage にもその値を保存することで、次回のページ読み込みに備えています。 ここでは、主な状態変数としてユーザー名だけを扱っていますが、実際のアプリケーションでは、サインイン時に取得したアクセストークンなどの情報を保存することが一般的です。

次に、この AuthState クラスを利用するコンポーネントを作成します。 サインイン/アウトのボタンを持つサイトヘッダーが典型的な例です。

src/lib/components/SiteHeader.svelte
<script lang="ts">
	import { AuthState } from '$lib/auth-state.svelte';
	const authState = new AuthState();  // 内部で onMount フックがセットされる
</script>

<header>
	<h1>My Site</h1>
	{#if authState.signedIn}
		<div>Welcome, {authState.userName}!</div>
		<button onclick={() => authState.signOut()}>Sign out</button>
	{:else}
		<button onclick={() => authState.signIn()}>Sign in</button>
	{/if}
</header>

<style>
	* {
		margin: 0;
	}
	header {
		display: flex;
		justify-content: space-between;
		align-items: center;
		padding: 0.5rem 1rem;
		background-color: #f0f0f0;
	}
	button {
		font-size: 1rem;
		padding: 0.5rem 1rem;
		border: none;
		border-radius: 0.25rem;
		background-color: #007bff;
		color: white;
		cursor: pointer;
	}
	button:hover {
		background: #0065d8;
	}
</style>

このコンポーネントを +layout.svelte に組み込むことで、全ページにサイトヘッダーを表示できます。

src/routes/+layout.svelte
<script lang="ts">
  import SiteHeader from '$lib/components/SiteHeader.svelte';
</script>

<SiteHeader />
<slot />

できたー ٩(๑❛ᴗ❛๑)۶ わーぃ

関連記事

メニュー

まくろぐ
サイトマップまくへのメッセージ