CUBE SUGAR CONTAINER

技術系のこと書きます。

自分で MCP (Model Context Protocol) を喋ってみる

Model Context Protocol (MCP) は、LLM が外部のシステムから情報を得る、または操作するのに用いられるプロトコルのひとつ。 昨今の扱われ方からすると、今後のデファクトとなりつつあるように感じられる。

今回は、そんな MCP がどういったプロトコルなのか仕様を読みながら自分で喋ってみることにした。 詳しくは後述するものの、MCP はあくまで JSON-RPC 2.0 をベースにしたプロトコルに過ぎない。 そのため、LLM とは関係なく独立して扱うことができる。

使った環境は次のとおり。 MCP Server と Client の実装には MCP 公式の Python SDK を用いた。

$ sw_vers
ProductName:        macOS
ProductVersion:     15.6
BuildVersion:       24G84
$ uname -srm                      
Darwin 24.6.0 arm64
$ uv --version                    
uv 0.8.4 (Homebrew 2025-07-30)

もくじ

MCP について

本題に入る前に、まずは MCP の概要についてざっくりと説明していく。

なお、特に断りがない限りは執筆の時点で最新のバージョン (2025-06-18) のプロトコル仕様を元にしている。 プロトコルの仕様は、以下の Web サイトで確認できる。

modelcontextprotocol.io

アーキテクチャ

MCP のアーキテクチャには、次のようなコンポーネントが登場する。

  • MCP Server
  • MCP Client
  • MCP Host

まず、MCP Server は実際にツールを実行したりデータを提供したりするコンポーネントになっている。 そのため、MCP Server が MCP の主役といっていい。 そして MCP Client は MCP Server に対してツールの実行やデータの提供を依頼する。

MCP Host は LLM やそれを利用するアプリケーションとほぼイコールと考えて良い。 MCP Host には MCP Server (+Client) を登録する。 登録した MCP Server が提供する機能を、MCP Host の LLM が使えるようになる。 具体例を挙げると、Claude Desktop や LM Studio が MCP Host として動作する。 例として挙げた MCP Host には MCP Client の機能が同梱されている。

機能

MCP Server が提供する機能には、次のような種類がある。

  • Tools
  • Resources
  • Prompts

Tools は読んで字のごとくツールの呼び出しに使用する。 LLM はツールを呼び出すことで外部から情報を得たり、外部の世界に何らかの副作用を生じさせたりする。 たとえば外部の何らかの API の呼び出しや、データベースへのクエリの発行、計算の実行など。 これが MCP のユースケースとしてはイメージしやすいと思う。

参考: Tools - Model Context Protocol

Resources は URI を使って識別される何らかのリソースの読み取りに使用する。 LLM はリソースを読み取ることで後続の推論に必要な情報を得る。 分かりやすいところでいうとファイルシステムの読み取りがある。 たとえば file:///read/to/path のような URI を読み取ることで、そのファイルの内容が得られる。

参考: Resources - Model Context Protocol

Prompts は、LLM の推論に用いるプロンプトを得るのに使用する。 この機能は LLM が直接利用するのではなく、MCP Host やそれを操作するユーザが主に利用する機能らしい。 MCP Server から提供されたプロンプトを使って、ユーザが LLM に指示を出すイメージか。

参考: Prompts - Model Context Protocol

なお、 MCP Client も提供する機能がいくつかあるが、ここでは扱わない。

メッセージ

プロトコルとしての MCP は、メッセージングに JSON-RPC 2.0 を用いる。 つまり、MCP Server と Client の間では JSON-RPC 2.0 のメッセージがやり取りされる。

JSON-RPC 2.0 は、その名の通り JSON を表現に使った RPC (Remote Procedure Call) になっている。 仕様は以下の Web サイトで確認できる。

www.jsonrpc.org

メッセージとしては、次の 3 つが定義されている。

  • Requests
  • Responses
  • Notifications

上記で Requests と Responses は対になっている。 機能の要求が Requests で、それに対する応答が Responses になる。 Notifications は一方通行の通知で、Server と Client のいずれも他方に送信することがある。

参考: Overview - Model Context Protocol

トランスポート

MCP Server と Client の間で JSON-RPC 2.0 をやり取りするための通信路をトランスポートという。 仕様には次の 3 つのトランスポートが存在する。

  • stdio
  • Streamable HTTP
  • ​Custom

stdio は文字通り stdio (標準入出力) を使って MCP Server と Client が通信する。 典型的には MCP Server の機能がローカルホストで完結する場合や、開発の用途で用いられる。

Streamable HTTP も文字通り HTTP を使って MCP Server と Client が通信する。 この場合、MCP Server はイコール HTTP サーバになる。 MCP Server から Client に対して非同期にメッセージを送りたいときは、オプションで SSE (Server-Sent Events) を用いる。

Custom は独自に実装したトランスポートになる。 具体例を挙げると、WebSocket や UNIX ドメインソケットでメッセージをやり取りするような場合だろう。 その場合は MCP Server と Client の両方が、その独自のトランスポートに対応している必要がある。

参考: Transports - Model Context Protocol

なお、今回はトランスポートとして stdio だけを使う。

下準備

これで、必要な前提知識は大体説明できた。

次は実際に MCP Server と MCP Client を書いて動かしてみよう。 ただし、最初から自分で MCP を喋ることはしない。 まずは、Server と Client がそれぞれちゃんと動くことを確認する。

下準備として Homebrew で uv をインストールしておく。

$ brew install uv

Tools

まずは Tools から見ていこう。

MCP Server

公式の Python SDK を使って実装した MCP Server のサンプルコードを次に示す。 この MCP Server では add()server_time() という 2 つの Tools を実装している。 add は引数を取って足し算をする。 server_time は引数なしでサーバの時刻を返す。

# /// script
# requires-python = ">=3.9"
# dependencies = [
#     "mcp[cli]",
# ]
# ///

"""サンプルの Tools を実装した MCP サーバ"""

from datetime import datetime

from mcp.server.fastmcp import FastMCP

mcp = FastMCP()


@mcp.tool(description="足し算をするツールです")
def add(a: int, b: int) -> int:
    return a + b


@mcp.tool(description="サーバのシステム時刻を返すツールです")
def server_time() -> str:
    now = datetime.now()
    return now.isoformat(sep=" ", timespec="seconds")


if __name__ == "__main__":
    mcp.run(transport="stdio")

MCP Client

続いて、同じように公式の SDK で実装した MCP Client のサンプルコードを以下に示す。 まずは公式の SDK を使って振る舞いを確認する。 サンプルコードは次のような流れになっている。

  • セッションを初期化する
  • 使用できる Tools のリストを得る
  • Tool を呼び出す
# /// script
# requires-python = ">=3.9"
# dependencies = [
#     "mcp[cli]",
# ]
# ///

"""サンプルの Tools を実装した MCP サーバを呼び出す MCP クライアント"""

import asyncio

from mcp import ClientSession
from mcp import StdioServerParameters
from mcp.client.stdio import stdio_client

server_params = StdioServerParameters(
    command="uv",
    args=[
        # MCP サーバのスクリプトが別のディレクトリにある場合は以下で指定する
        # "--directory",
        # "/path/to/server/script/directory",
        "run",
        "toolmcpserver.py",
    ],
)


async def main():
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            # セッションを初期化する
            await session.initialize()

            # 利用できるツールの一覧を得る
            items = await session.list_tools()
            print("Available tools:", [item.name for item in items.tools])

            # ツール (add) を呼び出す
            response = await session.call_tool("add", {"a": 1, "b": 2})
            result = response.structuredContent.get("result")
            print("Tool (add) result:", result)

            # ツール (server_time) を呼び出す
            response = await session.call_tool("server_time")
            result = response.structuredContent.get("result")
            print("Tool (server_time) result:", result)


if __name__ == "__main__":
    asyncio.run(main())

それでは、実装した MCP Client のスクリプトを uv で実行してみよう。

$ uv run --script toolmcpclient.py
[07/27/25 00:55:04] INFO     Processing request of type ListToolsRequest                                               server.py:625
Available tools: ['add', 'server_time']
                    INFO     Processing request of type CallToolRequest                                                server.py:625
Tool (add) result: 3
                    INFO     Processing request of type CallToolRequest                                                server.py:625
Tool (server_time) result: 2025-07-27 00:55:04

ちゃんと MCP Server から処理に対応した結果が得られている。

自分で喋ってみる

続いては今回の本題である MCP を自分で喋ってみることにしよう。 要するに、先ほどの MCP Client を自分が代わりにやる。

それには、実装した MCP Server を uv で実行する。

$ uv run --script toolmcpserver.py

これで MCP Server と stdio トランスポートで会話できる。 stdio トランスポートの場合、それぞれのメッセージは改行で区切られる。

まずはクライアント側から initialize メソッドを呼び出す。 このメッセージで、クライアント側が対応しているプロトコルのバージョンや機能などの情報をサーバへ伝える。 なお、id は単に Requests と Responses の対応を取るためのもので、別に連番になっている必要はない。

> { "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": { "name": "Human", "title": "Human Powered Client", "version": "0.0.1" }}}

また、実際に試す際には先頭の > は必要ない。 これは分かりやすさのためにつけている。 以降はクライアントからサーバへのメッセージには > をつける。 また、サーバからクライアントへのメッセージには < をつける。

すると、initialize メソッドに対する返答がサーバから返ってくる。 このメッセージには、サーバが対応しているプロトコルのバージョンや機能の情報が含まれている。

< {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-06-18","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":false}},"serverInfo":{"name":"FastMCP","version":"1.12.2"}}}

なお、サーバ側が出力するログの情報も本来は一緒に表示されるがここでは省略する。

次にクライアントからサーバに対して初期化が完了した旨の通知を送る。 これは先述した Notifications なので、一方通行であってサーバからの返答は無い。

> { "jsonrpc": "2.0", "method": "notifications/initialized" }

ちなみに、初期化しないまま後続のメッセージをやり取りしようとすると次のようなエラーメッセージになる。

< {"jsonrpc":"2.0","id":1,"error":{"code":-32602,"message":"Invalid request parameters","data":""}}

次に MCP Server が提供する Tools の一覧を得る。 これには tools/list メソッドを呼び出す。

> { "jsonrpc": "2.0", "id": 2, "method": "tools/list" }

すると、利用できる Tools の一覧が返答として得られる。 ここには説明や、呼び出す際の引数の情報などが含まれている。

< {"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"add","description":"足し算をするツールです","inputSchema":{"properties":{"a":{"title":"A","type":"integer"},"b":{"title":"B","type":"integer"}},"required":["a","b"],"title":"addArguments","type":"object"},"outputSchema":{"properties":{"result":{"title":"Result","type":"integer"}},"required":["result"],"title":"addOutput","type":"object"}},{"name":"server_time","description":"サーバのシステム時刻を返すツールです","inputSchema":{"properties":{},"title":"server_timeArguments","type":"object"},"outputSchema":{"properties":{"result":{"title":"Result","type":"string"}},"required":["result"],"title":"server_timeOutput","type":"object"}}]}}

実際に Tools を呼び出すには tools/call メソッドを使う。 ここでは引数のない server_time を呼んでいる。

> { "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "server_time" }}

呼び出すと、次のようにサーバ側で処理された結果が返ってくる。

< {"jsonrpc":"2.0","id":3,"result":{"content":[{"type":"text","text":"2025-07-27 01:23:07"}],"structuredContent":{"result":"2025-07-27 01:23:07"},"isError":false}}

引数があるものについては次のように arguments をつけて呼び出す。

> { "jsonrpc": "2.0", "id": 4, "method": "tools/call", "params": { "name": "add", "arguments": { "a": 1, "b": 2 }}}

結果については先ほどと変わらない。

< {"jsonrpc":"2.0","id":4,"result":{"content":[{"type":"text","text":"3"}],"structuredContent":{"result":3},"isError":false}}

これで先ほどの MCP Client と同じ流れを自分で体験できた。

Resources

次は Resources についても同じ流れを試す。

MCP Server

次の MCP Server のコードでは greeting()echo() という 2 つの Resources を実装している。 greeting は定数の文字列を返す。 echo は URI に埋め込まれた内容をそのまま返す。

# /// script
# requires-python = ">=3.9"
# dependencies = [
#     "mcp[cli]",
# ]
# ///

"""サンプルの Resources を実装した MCP サーバ"""

from mcp.server.fastmcp import FastMCP

mcp = FastMCP()


@mcp.resource("greeting://", description="挨拶をするリソースです")
def greeting():
    return "Hello, World!"


@mcp.resource("echo://{message}", description="エコーを返すリソースです")
def echo(message: str) -> str:
    return message


if __name__ == "__main__":
    mcp.run(transport="stdio")

MCP Client

対応する MCP Client のサンプルコードを以下に示す。 Tools と同様に以下の流れになっている。

  • 初期化する
  • 利用できる Resources のリストを得る
  • Resources を読み取る

なお、URI に何らかの情報が含まれる Resources は Resource Templates として別物になっている。

# /// script
# requires-python = ">=3.9"
# dependencies = [
#     "mcp[cli]",
# ]
# ///

"""サンプルの Resources を実装した MCP サーバを呼び出す MCP クライアント"""

import asyncio

from mcp import ClientSession
from mcp import StdioServerParameters
from mcp.client.stdio import stdio_client

server_params = StdioServerParameters(
    command="uv",
    args=[
        # "--directory",
        # "/path/to/server/script/directory",
        "run",
        "resourcemcpserver.py",
    ],
)


async def main():
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            # セッションを初期化する
            await session.initialize()

            # 利用できるリソースの一覧を得る
            items = await session.list_resources()
            print("Available resources:", [item.name for item in items.resources])

            # 利用できるリソーステンプレートの一覧を得る
            items = await session.list_resource_templates()
            print("Available resource templates:", [item.name for item in items.resourceTemplates])

            # リソース (greeting) を呼び出す
            response = await session.read_resource("greeting://")
            print("Resource (greeting) response:", response)

            # リソーステンプレート (echo) を呼び出す
            response = await session.read_resource("echo://hello")
            print("Resource (echo) response:", response)


if __name__ == "__main__":
    asyncio.run(main())

実装した MCP Client を実行してみよう。

$ uv run --script resourcemcpclient.py
[08/04/25 03:43:45] INFO     Processing request of type ListResourcesRequest                                           server.py:625
Available resources: ['greeting']
                    INFO     Processing request of type ListResourceTemplatesRequest                                   server.py:625
Available resource templates: ['echo']
                    INFO     Processing request of type ReadResourceRequest                                            server.py:625
Resource (greeting) response: meta=None contents=[TextResourceContents(uri=AnyUrl('greeting://'), mimeType='text/plain', meta=None, text='Hello, World!')]
                    INFO     Processing request of type ReadResourceRequest                                            server.py:625
Resource (echo) response: meta=None contents=[TextResourceContents(uri=AnyUrl('echo://hello'), mimeType='text/plain', meta=None, text='hello')]

ちゃんと Resources のリストの取得や読み取りができている。

自分で喋ってみる

次は、先ほどと同様に自分が MCP Client となって Server とやり取りしてみよう。

uv を使って MCP Server のスクリプトを起動する。

$ uv run --script resourcemcpserver.py

まずは Tools の場合と同様に初期化する。

> { "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": { "name": "Human", "title": "Human Powered Client", "version": "0.0.1" }}}
< {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-06-18","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":false}},"serverInfo":{"name":"FastMCP","version":"1.12.2"}}}
> { "jsonrpc": "2.0", "method": "notifications/initialized" }

次に利用できる Resources の一覧を resources/list メソッドで得る。

> { "jsonrpc": "2.0", "id": 2, "method": "resources/list" }
< {"jsonrpc":"2.0","id":2,"result":{"resources":[{"name":"greeting","uri":"greeting://","description":"挨拶をするリソースです","mimeType":"text/plain"}]}}

同様に、利用できる Resource Templates の一覧を resources/templates/list で得る。

> { "jsonrpc": "2.0", "id": 3, "method": "resources/templates/list" }
< {"jsonrpc":"2.0","id":3,"result":{"resourceTemplates":[{"name":"echo","uriTemplate":"echo://{message}","description":"エコーを返すリソースです"}]}}

greeting:// の Resources を resources/read メソッドで読み取る。

> { "jsonrpc": "2.0", "id": 4, "method": "resources/read", "params": { "uri": "greeting://" }}
< {"jsonrpc":"2.0","id":4,"result":{"contents":[{"uri":"greeting://","mimeType":"text/plain","text":"Hello, World!"}]}}

結果として定数の文字列が返ってきた。

同様に echo:// の Resource Templates を読み取る。 読み取る場合のメソッドは Resources と Resource Templates で違いはないようだ。

> { "jsonrpc": "2.0", "id": 5, "method": "resources/read", "params": { "uri": "echo://hello" }}
< {"jsonrpc":"2.0","id":5,"result":{"contents":[{"uri":"echo://hello","mimeType":"text/plain","text":"hello"}]}}

Prompts

次は Prompts でも同じ流れを確認する。

MCP Server

以下に Prompts を実装した MCP Server のサンプルコードを示す。 サンプルコードでは summarize というプロンプトを実装している。

# /// script
# requires-python = ">=3.9"
# dependencies = [
#     "mcp[cli]",
# ]
# ///

"""サンプルの Prompts を実装した MCP サーバ"""

from mcp.server.fastmcp import FastMCP

mcp = FastMCP()


@mcp.prompt(description="要約に使うプロンプトです")
def summarize(topic: str) -> str:
    return f"{topic}について分かりやすく要約してください"


if __name__ == "__main__":
    mcp.run(transport="stdio")

MCP Client

対応する MCP Client のサンプルコードを以下に示す。 Tools や Resources と同様に以下の流れになっている。

  • 初期化する
  • 利用できる Prompts のリストを得る
  • Promts を得る
# /// script
# requires-python = ">=3.9"
# dependencies = [
#     "mcp[cli]",
# ]
# ///

"""サンプルの Prompts を実装した MCP サーバを呼び出す MCP クライアント"""

import asyncio

from mcp import ClientSession
from mcp import StdioServerParameters
from mcp.client.stdio import stdio_client

server_params = StdioServerParameters(
    command="uv",
    args=[
        # "--directory",
        # "/path/to/server/script/directory",
        "run",
        "promptmcpserver.py",
    ],
)


async def main():
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            # セッションを初期化する
            await session.initialize()

            # 利用できるプロンプトの一覧を得る
            items = await session.list_prompts()
            print("Available prompts:", [item.name for item in items.prompts])

            # プロンプト (summarize) を呼び出す
            response = await session.get_prompt("summarize", {"topic": "生命、宇宙、そして万物についての究極の疑問の答え"})
            print("Prompts (summarize) response:", response)


if __name__ == "__main__":
    asyncio.run(main())

上記を uv で実行してみよう。

$ uv run --script promptmcpclient.py
[08/04/25 04:12:23] INFO     Processing request of type ListPromptsRequest                                                                               server.py:625
Available prompts: ['summarize']
                    INFO     Processing request of type GetPromptRequest                                                                                 server.py:625
Prompts (summarize) response: meta=None description='要約に使うプロンプトです' messages=[PromptMessage(role='user', content=TextContent(type='text', text='生命、宇宙、そして万物についての究極の疑問の答えについて分かりやすく要約してください', annotations=None, meta=None))]

クライアントからの入力に基づいてプロンプトが得られている。

自分で喋ってみる

次は先ほどと同様に自分が MCP Client となって Server とやり取りしてみよう。

uv を使って MCP Server のスクリプトを起動する。

$ uv run --script promptmcpserver.py

まずは初期化する。

> { "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": { "name": "Human", "title": "Human Powered Client", "version": "0.0.1" }}}
< {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-06-18","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":false}},"serverInfo":{"name":"FastMCP","version":"1.12.2"}}}
> { "jsonrpc": "2.0", "method": "notifications/initialized" }

利用できるプロンプトの一覧を得る。

> { "jsonrpc": "2.0", "id": 2, "method": "prompts/list" }
< {"jsonrpc":"2.0","id":2,"result":{"prompts":[{"name":"summarize","description":"要約に使うプロンプトです","arguments":[{"name":"topic","required":true}]}]}}

プロンプトの summarize を得る。

> { "jsonrpc": "2.0", "id": 3, "method": "prompts/get", "params": { "name": "summarize", "arguments": { "topic": "生命、宇宙、そして万物についての究極の疑問の答え" }}}
< {"jsonrpc":"2.0","id":3,"result":{"description":"要約に使うプロンプトです","messages":[{"role":"user","content":{"type":"text","text":"生命、宇宙、そして万物についての究極の疑問の答えについて分かりやすく要約してください"}}]}}

入力した内容が埋め込まれたプロンプトが得られた。

まとめ

今回は Model Context Protocol (MCP) をプロトコルという側面から扱った。 MCP は JSON-RPC 2.0 をベースにしたプロトコルで、Server / Client / Host という 3 つのコンポーネントが登場する。 今回はその中で Server と Client を実装した上で、Client になりきって自分で MCP を喋ってみた。 実際に自分でサーバとメッセージをやり取りすることで、どのようなプロトコルなのか理解を深めることができた。

OpenAI の Web API を使い始める

最近は OpenAI 互換の Web API が、LLM の機能を提供する上でデファクトに近い方法となっているように思う。 そこで、今回は本家の OpenAI の Web API を使い始めるまでの内容を自分用のメモを兼ねて残しておく。

使った環境は次のとおり。

$ sw_vers         
ProductName:        macOS
ProductVersion:     15.5
BuildVersion:       24F74
$ uname -srm
Darwin 24.5.0 arm64
$ curl --version
curl 8.7.1 (x86_64-apple-darwin24.0) libcurl/8.7.1 (SecureTransport) LibreSSL/3.3.6 zlib/1.2.12 nghttp2/1.64.0
Release-Date: 2024-03-27
Protocols: dict file ftp ftps gopher gophers http https imap imaps ipfs ipns ldap ldaps mqtt pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: alt-svc AsynchDNS GSS-API HSTS HTTP2 HTTPS-proxy IPv6 Kerberos Largefile libz MultiSSL NTLM SPNEGO SSL threadsafe UnixSockets
$ python -V
Python 3.12.11
$ pip list | grep -i openai               
openai            1.90.0

もくじ

OpenAI Platform について

OpenAI の Web API を利用する際の料金体系は、ChatGPT のサブスクリプションとは分離されている。 また、Web API に関する管理は OpenAI Platform というサービスを使うことになる。

platform.openai.com

Web API を利用するには、上記のサービスにあらかじめ前払いでクレジットを登録しておく必要がある。 Web API を利用することで生じた料金は、前払いしたクレジットの中から支払われる。

Organization を作る

OpenAI Platform を利用するには、まず Organization を作成する必要があるようだ。 現時点 (2025-06-21) の UI であれば、右上にある「Start Bulding」のボタンを押下する。 特に何も入力しないでウィザードを進めると Organization name が「Personal」になる。 個人による利用であれば、それで特に問題ないはず。 また、Organization の配下にデフォルトのプロジェクトとして「Default」も同時に作られるようだ。

クレジットを追加する

前述した通り Web API はクレジットが無いと使えない。 そこで、まずは「Settings > Billing」の画面でクレジットを追加する。

platform.openai.com

「Add payment details」ボタンを押下するとクレジットカードの登録画面が出てくる。 自身のカードの情報を入力したら、追加するクレジットをドルで指定する。 クレジットが少なくなったときに自動でチャージする設定は不要であればオフにする。

API トークンを作る

Web API を呼び出す際には OpenAI Platform のアカウントとの紐づけが必要になる。 そのために、API トークンを作成する。

platform.openai.com

現時点の UI であれば右上の「Create new secret key」を押下して作成する。 API トークンは作成したタイミングで一度しか表示されない。 忘れずにパスワードマネージャなどに記録しておこう。

curl(1) から Web API を呼び出す

まずは curl(1) を使って呼び出してみる。

先ほど発行したトークンを、シェルのセッション変数に取り込む。 パスワードなどのセンシティブな情報を扱うときは read コマンドと -s オプションを用いると良い。 こうすれば入力した内容がコマンドの履歴に残らない。

$ read -s OPENAI_API_KEY

上記を実行したら、先ほど記録したトークンをターミナルにペーストする。

現時点で最もベーシックな Web API として Chat Completions を試してみよう。 Chat Completions API のリファレンスは以下にある。

platform.openai.com

なお、今だと状態 (ステート) を API 側で管理してもらえる Chat Completions with Responses という API もあるようだ。 従来の API は状態を持たないステートレスだったので、クライアント側ですべて管理する必要があった。 Responses の API では、その点が改善されているようだ。 ただし、今回は使わない。

リファレンスの内容を元に curl(1) で HTTP リクエストを作る。 先ほどセッション変数に入れたトークンは Authorization ヘッダの Bearer 以降に指定する。 model キーにはモデルの名称を入力する。 messages にはロール毎の指示を入力する。 たとえば "role": "developer" の指示は、"role": "user" の入力に関係なく従うべき内容と解釈される。

$ curl -X POST https://api.openai.com/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "model": "gpt-4.1",
    "messages": [
      {
        "role": "developer",
        "content": "あなたは親切なアシスタントです"
      },
      {
        "role": "user",
        "content": "こんにちは!"
      }
    ]
  }'

結果は次のような JSON として得られる。

{
  "id": "chatcmpl-XXX...",
  "object": "chat.completion",
  "created": 1750480953,
  "model": "gpt-4.1-2025-04-14",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "こんにちは!😊  \nどうぞ、なんでもお気軽にご相談ください。",
        "refusal": null,
        "annotations": []
      },
      "logprobs": null,
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 23,
    "completion_tokens": 16,
    "total_tokens": 39,
    "prompt_tokens_details": {
      "cached_tokens": 0,
      "audio_tokens": 0
    },
    "completion_tokens_details": {
      "reasoning_tokens": 0,
      "audio_tokens": 0,
      "accepted_prediction_tokens": 0,
      "rejected_prediction_tokens": 0
    }
  },
  "service_tier": "default",
  "system_fingerprint": "fp_51e1070cf2"
}

なお、クレジットが無い状態で呼び出すと次のようなエラーになる。

{
    "error": {
        "message": "You exceeded your current quota, please check your plan and billing details. For more information on this error, read the docs: https://platform.openai.com/docs/guides/error-codes/api-errors.",
        "type": "insufficient_quota",
        "param": null,
        "code": "insufficient_quota"
    }
}

Python から Web API を呼び出す

次は公式の Python パッケージを使って Web API を呼んでみよう。

まずは openai パッケージをインストールする。

$ pip install openai

また、先ほどトークンをセッション変数 OPENAI_API_KEY に格納してあった。 これを、プロセスが fork した後の Python プロセスからも使えるように環境変数にする。

$ export OPENAI_API_KEY

openai パッケージは環境変数 OPENAI_API_KEY が定義されていると自動的に読んでくれる。

そして、Python インタプリタを起動する。

$ python

openai.OpenAI クラスをインスタンス化する。

>>> from openai import OpenAI
>>> client = OpenAI()

openai.OpenAI#chat.completions.create() メソッドを使って Chat Completions API を呼び出す。

>>> completion = client.chat.completions.create(
...   model="gpt-4.1",
...   messages=[
...     {"role": "developer", "content": "あなたは親切なアシスタントです"},
...     {"role": "user", "content": "こんにちは!"}
...   ]
... )

レスポンスを確認すると、ちゃんと結果が得られていることが確認できる。

>>> completion
ChatCompletion(id='chatcmpl-XXX...', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='こんにちは!😊  \n何かお手伝いできることはありますか?', refusal=None, role='assistant', annotations=[], audio=None, function_call=None, tool_calls=None))], created=1750477841, model='gpt-4.1-2025-04-14', object='chat.completion', service_tier='default', system_fingerprint='fp_51e1070cf2', usage=CompletionUsage(completion_tokens=17, prompt_tokens=23, total_tokens=40, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0)))

いじょう。


[asin:B085BG8CH5:detai

Mac: LM Studio に Hugging Face Hub からダウンロードしたモデルを CLI で読み込む

LM Studio は、ローカル LLM を動かすためのソフトウェアのひとつ。

lmstudio.ai

基本的に LM Studio では、モデルをダウンロードする際には Discover タブから GUI で検索してダウンロードできる。 しかし、Hugging Face Hub 1 などから手動でダウンロードしたモデルをインポートする方法も用意されている。 今回はそのやり方について書く。

使った環境は次のとおり。

$ sw_vers
ProductName:        macOS
ProductVersion:     15.5
BuildVersion:       24F74
$ uname -srm
Darwin 24.5.0 arm64
$ sysctl machdep.cpu.brand_string
machdep.cpu.brand_string: Apple M2 Pro
$ huggingface-cli version
huggingface_hub version: 0.33.0
$ lms version  
   __   __  ___  ______          ___        _______   ____
  / /  /  |/  / / __/ /___ _____/ (_)__    / ___/ /  /  _/
 / /__/ /|_/ / _\ \/ __/ // / _  / / _ \  / /__/ /___/ /  
/____/_/  /_/ /___/\__/\_,_/\_,_/_/\___/  \___/____/___/  

lms - LM Studio CLI - v0.0.41
GitHub: https://github.com/lmstudio-ai/lmstudio-cli

もくじ

下準備

まずは Homebrew から LM Studio をインストールする。

$ brew install --cask lm-studio

インストールすると $HOME/.lmstudio 以下にディレクトリができる。 この中の bin ディレクトリには LM Studio を CLI で操作するためのバイナリが入っている。 そこで、ここにパスを通す。

$ export PATH="$PATH:$HOME/.lmstudio/bin"

必要に応じてシェルの設定ファイルなどに永続化すると良い。

パスを通すと lms コマンドが使えるようになる。

$ lms version  
   __   __  ___  ______          ___        _______   ____
  / /  /  |/  / / __/ /___ _____/ (_)__    / ___/ /  /  _/
 / /__/ /|_/ / _\ \/ __/ // / _  / / _ \  / /__/ /___/ /  
/____/_/  /_/ /___/\__/\_,_/\_,_/_/\___/  \___/____/___/  

lms - LM Studio CLI - v0.0.41
GitHub: https://github.com/lmstudio-ai/lmstudio-cli

続いて、Hugging Face Hub を操作するためのパッケージをPyPI からダウンロードする。

$ pip install "huggingface_hub[cli]"

これで huggingface-cli コマンドが使えるようになる。

$ huggingface-cli version
huggingface_hub version: 0.33.0

モデルをダウンロードする

次に Hugging Face Hub からモデルをダウンロードする。 LM Studio はデフォルトの実行ランタイムとして llama.cpp を使用する。 そのため、GGUF のモデルをダウンロードする。

$ huggingface-cli download "lmstudio-community/gemma-3-1B-it-qat-GGUF" --local-dir .

モデルをインポートする

ダウンロードしたファイルを lms import コマンドでインポートする。 このときインポートのやり方を対話的に確認される。

$ lms import gemma-3-1B-it-QAT-Q4_0.gguf

無事にインポートできると LM Studio の画面でモデルが確認できるようになるはず。

LM Studio でインポートしたモデルが確認できる

モデルの動作を確認する

サーバを起動するとモデルが WebAPI 経由で使えるようになる。

試しにモデル一覧を curl(1) で確認してみよう。

$ curl http://localhost:1234/v1/models
{
  "data": [
    {
      "id": "gemma-3-1b-it-qat",
      "object": "model",
      "owned_by": "organization_owner"
    },
    {
      "id": "text-embedding-nomic-embed-text-v1.5",
      "object": "model",
      "owned_by": "organization_owner"
    }
  ],
  "object": "list"
}

インポートした gemma-3-1b-it-qat が確認できる。

同様に completions の API も使ってみよう。

$ curl -X POST -s http://localhost:1234/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
  "model": "gemma-3-1b-it-qat",
  "messages": [
    { "role": "user", "content": "こんにちは!" }
  ]
}'       
{
  "id": "chatcmpl-kfep0etrq6dz9fbcth25uh",
  "object": "chat.completion",
  "created": 1749874667,
  "model": "gemma-3-1b-it-qat",
  "choices": [
    {
      "index": 0,
      "logprobs": null,
      "finish_reason": "stop",
      "message": {
        "role": "assistant",
        "content": "こんにちは!何かお手伝いできることはありますか? 😊 どんなことでもお気軽にご質問ください。"
      }
    }
  ],
  "usage": {
    "prompt_tokens": 11,
    "completion_tokens": 22,
    "total_tokens": 33
  },
  "stats": {},
  "system_fingerprint": "gemma-3-1b-it-qat"
}

問題ないようだ。


Mac: Ollama でローカル LLM を動かす

Ollama 1 はローカル LLM を動かすためのソフトウェアのひとつ。 一般的なラップトップやデスクトップマシンで動かすようなユースケースが主に想定されているように思う。 LLM の機能を不特定多数に提供するというよりは、マシンを操作しているユーザ自身が使用するイメージだろう。 似たようなユースケースで用いられるソフトウェアには、他にも LM Studio 2 などがある。 今回は、そんな Ollama を Mac から使う方法についてメモしておく。

使った環境は次のとおり。

$ sw_vers
ProductName:        macOS
ProductVersion:     15.5
BuildVersion:       24F74
$ uname -srm                
Darwin 24.5.0 arm64
$ sysctl machdep.cpu.brand_string
machdep.cpu.brand_string: Apple M2 Pro
$ ollama --version
ollama version is 0.9.0

もくじ

下準備

まず、Ollama は Homebrew を使ってインストールできる。

$ brew install --formula ollama

サーバを起動する

Ollama を利用するには、何をするにもまずはサーバのインスタンスを立ち上げる必要がある。

そのためには、Homebrew でインストールした場合には Ollama をサービスとして起動すれば良い。

$ brew services start ollama

あるいは、フォアグラウンドで実行したいときは単に ollama serve コマンドを叩いても良い。

$ ollama serve

設定を変更する

Ollama は、OLLAMA_ から始まる名前のシェル変数・環境変数を使って動作を変更できる。

たとえば、Homebrew のサービスからサーバを起動する場合には launchctl setenv で設定を変更する。 以下ではグローバルなコンテキスト長を 32768 トークンに変更している。

$ launchctl setenv OLLAMA_CONTEXT_LENGTH 32768

あるいは ollama serve コマンドで起動しているときは、単純にセッション変数として指定すれば良い。

$ OLLAMA_CONTEXT_LENGTH=32768 ollama serve

モデルをダウンロードする

続いてモデルをダウンロードするには ollama pull コマンドを使う。

以下では例として Gemma3 の 1B モデルを指定している。

$ ollama pull gemma3:1b
pulling manifest 
pulling 7cd4618c1faf: 100% ▕██████████████████▏ 815 MB                         
pulling e0a42594d802: 100% ▕██████████████████▏  358 B                         
pulling dd084c7d92a3: 100% ▕██████████████████▏ 8.4 KB                         
pulling 3116c5225075: 100% ▕██████████████████▏   77 B                         
pulling 120007c81bf8: 100% ▕██████████████████▏  492 B                         
verifying sha256 digest 
writing manifest 
success

Ollama が公式が提供しているモデルについては以下の Web ページで検索できる。

ollama.com

ダウンロードしたモデルは ollama ls コマンドで確認できる。

$ ollama ls           
NAME         ID              SIZE      MODIFIED    
gemma3:1b    8648f39daa8f    815 MB    6 hours ago

モデルの情報を得る

モデルの詳しい情報は ollama show コマンドで確認できる。

$ ollama show gemma3:1b
  Model
    architecture        gemma3     
    parameters          999.89M    
    context length      32768      
    embedding length    1152       
    quantization        Q4_K_M     

  Capabilities
    completion    

  Parameters
    stop           "<end_of_turn>"    
    temperature    1                  
    top_k          64                 
    top_p          0.95               

  License
    Gemma Terms of Use                  
    Last modified: February 21, 2024    
    ...                                 

ターミナルでモデルと対話する

ollama run コマンドを使うと、ターミナルを使ってモデルと対話できる。

$ ollama run gemma3:1b
>>> こんにちは!
こんにちは!何かお手伝いできることはありますか?😊 

どんなことでも構いません。例えば:

*   質問に答える
*   文章を作成する
*   アイデアを出す
*   情報検索をする

など、お気軽にお申し付けください。

なお、ollama ps コマンドを使うと、現在どのモデルがメモリ上で動作しているか確認できる。

$ ollama ps                     
NAME         ID              SIZE      PROCESSOR    UNTIL              
gemma3:1b    8648f39daa8f    2.2 GB    100% GPU     4 minutes from now

OpenAI-like な WebAPI を利用する

Ollama は OpenAI-like な Web API を提供している。 この Web API を通してモデルの機能を利用できる。

たとえば completions の API を curl(1) で叩いてみよう。 API はループバックアドレスの 11434 ポートで提供されている。

$ curl -s http://localhost:11434/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
  "model": "gemma3:1b",
  "messages": [
    { "role": "user", "content": "ご機嫌いかがですか?" }
  ]
}' | jq .

すると JSON で結果が得られる。

$ curl -s http://localhost:11434/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
  "model": "gemma3:1b",
  "messages": [
    { "role": "user", "content": "ご機嫌いかがですか?" }
  ]
}' | jq .
{
  "id": "chatcmpl-578",
  "object": "chat.completion",
  "created": 1749812263,
  "model": "gemma3:1b",
  "system_fingerprint": "fp_ollama",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "元気です、ありがとう! 😊 あなたはいかがですか? \n\n何かお手伝いできることはありますか?\n"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 16,
    "completion_tokens": 24,
    "total_tokens": 40
  }
}

すべての API のドキュメントは以下の Web ページで確認できる。

github.com

そして、API を通して様々なツールと連携できる。 連携できるツールは以下のページにまとめられている。

github.com

必要に応じて、先ほど叩いた API のエンドポイントをツールに設定することで連携が可能になる。

不要になったモデルを削除する

LLM のファイルはサイズが大きいので、不要になったときは消したくなることもある。

そんなときは ollama rm コマンドを使って削除できる。

$ ollama rm gemma3:1b 
deleted 'gemma3:1b'

いじょう。


Linux でスワップファイルを使ってスワップ領域のサイズを柔軟に変更する

一昔前だと、スワップ領域といえば専用のパーティションを用意して作るものというイメージがあった。 しかし、どうやら最近はファイルシステム上に作成したファイルを使ったスワップファイルの利用も盛んなようだ。 スワップファイルには、サイズを柔軟に変更できるメリットがある。 今回はスワップファイルを使ってスワップ領域のサイズを変更する方法について見ていく。

使った環境は次のとおり。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 24.04.2 LTS
Release:    24.04
Codename:   noble
$ uname -srm
Linux 6.8.0-59-generic x86_64
$ mkswap --version
mkswap from util-linux 2.39.3
$ swapon --version
swapon from util-linux 2.39.3
$ fallocate --version
fallocate from util-linux 2.39.3

もくじ

スワップ領域の状況を確認する

スワップ領域の状況は procfs から確認できる。 具体的には /proc/swaps から使用しているスワップパーティションやスワップファイルの情報が得られる。

$ cat /proc/swaps 
Filename                Type        Size        Used    Priority
/swap.img                               file        3043324        0  -2

今回使用した環境では /swap.img というスワップファイルがスワップ領域に使われている。 サイズは 3GB ほどのようだ。

スワップファイルを使ってスワップ領域を増やす

現状を確認したところで、試しにスワップ領域を 4GB に増やしてみよう。

まずは fallocate(1) や dd(1) を使って特定のサイズを持ったファイルを作る。

$ sudo fallocate -l 4G /swapfile
$ ls -alF /swapfile 
-rw-r--r-- 1 root root 4294967296 May 13 10:46 /swapfile

上記では /swapfile というファイルパスに 4GB のサイズでファイルを作っている。

スーパーユーザ以外に読み書きできないようにパーミッションを変更する。

$ sudo chmod 600 /swapfile

mkswap(1) でスワップ領域として使用できるように初期化する。

$ sudo mkswap /swapfile
Setting up swapspace version 1, size = 4 GiB (4294963200 bytes)
no label, UUID=b724dc3f-02ec-4b2d-b137-8072132179e7

新しく作ったスワップファイルを swapon(1) でスワップ領域として有効化する。

$ sudo swapon /swapfile

古いスワップファイルは swapoff(1) でスワップ領域として無効化しておく。

$ sudo swapoff /swap.img

これでスワップ領域が 4GB になる。

$ cat /proc/swaps 
Filename                Type        Size        Used        Priority
/swapfile                               file        4194300        0      -2

スワップ領域の設定を fstab(5) で永続化する

先ほどのやり方では変更したスワップ領域の設定が永続化されない。 そのためマシンを再起動すると状況が元に戻ってしまう。

試しに再起動してみよう。

$ sudo shutdown -r now

もう一度ログインして確認すると /swap.img が使われている。

$ cat /proc/swaps 
Filename                Type        Size        Used        Priority
/swap.img                               file        3043324        0      -2

これは fstab(5) に設定を書き込んでいなかったために生じる。

$ grep "/swap.img" /etc/fstab 
/swap.img   none    swap    sw  0  0

そこで fstab(5) を書き換えてスワップファイルとして /swapfile が使われるようにしてみよう。

$ sudo sed -i.bak s:swap.img:swapfile: /etc/fstab

次のように /swapfile を使う形になる。

$ grep "/swapfile" /etc/fstab
/swapfile   none    swap    sw  0  0

以前のファイルは /etc/fstab.bak に残る。 何かあったときはこちらから元に戻そう。

$ grep "/swap.img" /etc/fstab.bak 
/swap.img   none    swap    sw  0  0

この状態でマシンを再起動する。

$ sudo shutdown -r now

もう一度ログインして確認すると、ちゃんとスワップ領域に /swapfile が使われている。

$ cat /proc/swaps 
Filename                Type        Size        Used        Priority
/swapfile                               file        4194300        0      -2

ばっちり。

まとめ

今回はスワップファイルを使ってスワップ領域のサイズを柔軟に変更する方法について確認した。 スワップファイルであれば、たとえば後から増設した高速な SSD を使ってスワップ領域を構築するといったこともやりやすい。


textlint を使って日本語の文章を校正する

textlint 1 は自然言語向けの Linter のひとつ。 対象とする文章を静的解析して、特定のルールに抵触していないか確認できる。 今回は macOS で textlint を使い始めるまでについてメモしておく。

使った環境は次のとおり。

$ sw_vers
ProductName:        macOS
ProductVersion:     15.4.1
BuildVersion:       24E263
$ uname -srm
Darwin 24.4.0 arm64
$ node --version  
v23.11.0
$ npm --version 
10.9.2
$ npx textlint --version    
v14.7.1

もくじ

下準備

textlint は npm で配布されている。 そこで、まずは Homebrew で Node.js をインストールする。

$ brew install node

textlint をインストールする

チェックしたい文章のある場所で npm を使って textlint をインストールする。 このとき --save-dev オプションをつけると package.json ファイルが作られる。

$ npm install --save-dev textlint

package.json には依存関係が書かれている。

$ cat package.json 
{
  "devDependencies": {
    "textlint": "^14.7.1"
  }
}

また、node_modules というディレクトリに textlint と依存パッケージがインストールされる。

$ ls -1 node_modules | head
@azu
@isaacs
@keyv
@pkgjs
@textlint
@types
ajv
ansi-regex
ansi-styles
argparse

これで textlint の本体がインストールできた。

校正用のプリセットルールをインストールする

次にチェックする具体的な内容の書かれたプリセットルールをインストールする。

ここでは例として日本語の技術文章向けのプリセットの textlint-rule-preset-ja-technical-writing を入れる。 プリセットも npm でインストールできる。

$ npm install --save-dev textlint-rule-preset-ja-technical-writing

その他にも textlint-ja というコミュニティのリポジトリを見ると色々なプリセットがある。

github.com

textlint の設定ファイルを用意する

次に、先ほどインストールしたプリセットを使う textlint の設定ファイルを用意する。 設定ファイルの名前は .textlintrc で、フォーマットは JSON になっている。

cat << 'EOF' > .textlintrc
{
    "rules": {
        "preset-ja-technical-writing": true
    }
}
EOF

文章をチェックする

サンプルとなる文章を用意する。

$ cat << 'EOF' > helloworld.md
吾輩は猫である。名前はまだ無い。
EOF

npx コマンドを使って textlint を呼び出して上記のファイルをチェックする。 すると ja-technical-writing/no-mix-dearu-desumasu というルールに抵触する箇所が見つかる。

$ npx textlint helloworld.md                                      

/Users/amedama/Documents/temporary/helloworld.md
  1:5  error  本文: "ですます"調 でなければなりません
=> "ですます"調 であるべき箇所に、次の "である"調 の箇所があります: "である。"
Total:
である  : 1
ですます: 0
  ja-technical-writing/no-mix-dearu-desumasu

✖ 1 problem (1 error, 0 warnings)

なお、npx コマンドを使わないパターンとして、コマンドに PATH を通してしまうやり方もある。 インストール先の bin ディレクトリは $(npm root) 以下の .bin になる。 つまり、以下のようにすれば良い。

$ PATH=$(npm root)/.bin:$PATH textlint helloworld.md

/Users/amedama/Documents/temporary/helloworld.md
  1:5  error  本文: "ですます"調 でなければなりません
=> "ですます"調 であるべき箇所に、次の "である"調 の箇所があります: "である。"
Total:
である  : 1
ですます: 0
  ja-technical-writing/no-mix-dearu-desumasu

✖ 1 problem (1 error, 0 warnings)

いじょう。


Homebrew のパッケージの情報を調べる

今回は Homebrew のパッケージについて諸々を調べる方法について。 毎回調べている気がするのでメモしておく。

使った環境は次のとおり。

$ sw_vers
ProductName:        macOS
ProductVersion:     15.4.1
BuildVersion:       24E263
$ uname -srm  
Darwin 24.4.0 arm64
$ brew --version                            
Homebrew 4.5.2

もくじ

下準備

あらかじめ Homebrew をインストールしておく。 やり方は公式の Web サイト 1 を参照のこと。

基本的な情報を確認する

まずはパッケージの基本的な情報について知りたいときは brew info <package> を使う。 バージョンやライセンス、Webサイト、インストール用の Ruby スクリプトの場所など色々と確認できる。

$ brew info jq
==> jq: stable 1.7.1 (bottled), HEAD
Lightweight and flexible command-line JSON processor
https://jqlang.github.io/jq/
Not installed
Bottle Size: 525.0KB
Installed Size: 1.4MB
From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/j/jq.rb
License: MIT
==> Dependencies
Required: oniguruma ✘
==> Options
--HEAD
    Install HEAD version
==> Analytics
install: 34,169 (30 days), 103,697 (90 days), 519,279 (365 days)
install-on-request: 33,728 (30 days), 102,033 (90 days), 511,906 (365 days)
build-error: 0 (30 days)

インストール先のディレクトリを確認する

インストールされるディレクトリを確認するには brew --cellar <package> を使う。

$ brew --cellar jq
/opt/homebrew/Cellar/jq

このコマンドはインストールした後にパッケージに含まれるファイルを使って作業するときにも使うことがある。

パッケージに含まれるファイルを確認する

続いてはインストールした後にパッケージに含まれるファイルを確認する方法について。 まずは調査したいパッケージはインストールしておく。

$ brew install jq

そして brew list -v <package を実行する。

$ brew list -v jq
/opt/homebrew/Cellar/jq/1.7.1/INSTALL_RECEIPT.json
/opt/homebrew/Cellar/jq/1.7.1/bin/jq
/opt/homebrew/Cellar/jq/1.7.1/.brew/jq.rb
/opt/homebrew/Cellar/jq/1.7.1/ChangeLog
/opt/homebrew/Cellar/jq/1.7.1/AUTHORS
/opt/homebrew/Cellar/jq/1.7.1/NEWS.md
/opt/homebrew/Cellar/jq/1.7.1/include/jv.h
/opt/homebrew/Cellar/jq/1.7.1/include/jq.h
/opt/homebrew/Cellar/jq/1.7.1/sbom.spdx.json
/opt/homebrew/Cellar/jq/1.7.1/README.md
/opt/homebrew/Cellar/jq/1.7.1/COPYING
/opt/homebrew/Cellar/jq/1.7.1/lib/libjq.a
/opt/homebrew/Cellar/jq/1.7.1/lib/pkgconfig/libjq.pc
/opt/homebrew/Cellar/jq/1.7.1/lib/libjq.dylib
/opt/homebrew/Cellar/jq/1.7.1/lib/libjq.1.dylib
/opt/homebrew/Cellar/jq/1.7.1/share/man/man1/jq.1
/opt/homebrew/Cellar/jq/1.7.1/share/doc/jq/AUTHORS
/opt/homebrew/Cellar/jq/1.7.1/share/doc/jq/NEWS.md
/opt/homebrew/Cellar/jq/1.7.1/share/doc/jq/README.md
/opt/homebrew/Cellar/jq/1.7.1/share/doc/jq/COPYING

インストールに使われたスクリプトの内容を確認する

インストールしたパッケージは brew cat <package> でスクリプトの内容を確認できる。 もちろん、先ほど brew info にあったファイルを確認しても良い。

$ brew cat jq | head      
class Jq < Formula
  desc "Lightweight and flexible command-line JSON processor"
  homepage "https://jqlang.github.io/jq/"
  url "https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-1.7.1.tar.gz"
  sha256 "478c9ca129fd2e3443fe27314b455e211e0d8c60bc8ff7df703873deeee580c2"
  license "MIT"

  livecheck do
    url :stable
    regex(/^(?:jq[._-])?v?(\d+(?:\.\d+)+)$/i)

いじょう。