メインコンテンツまでスキップ

コマンドを叩いてローカル MCP の動きを見てみる

· 約3分
ひかり
Main bloger

処理の流れ

MCP を使用した場合、LLM クライアントでどのような流れで処理が行われるかを Copilot くんに聞きました。

以下、シーケンス図です。

ここで重要なのは、

  1. ①② ツール定義の取得
  2. ④ ツール定義の送信
  3. ⑦ ツール呼び出し要求
  4. ⑧⑨⑩⑪ ツール呼び出し
  5. ⑫ 実行結果の送信

です。

MCP にかかわる部分は、1. と 4. で、 Function calling とほぼ同じ部分が 2. と 3. と 5. です。

コマンドを叩いて呼び出してみる

ローカル MCP を標準入力で使用してみましょう。

Windows PowerShell を用いてコマンドをたたきます。

ツール一覧を取得

例として @modelcontextprotocol/server-filesystem のツール一覧を取得します。

> @{ jsonrpc = "2.0"; method = "tools/list"; id = 1 } | ConvertTo-Json -Compress | npx @modelcontextprotocol/server-filesystem $HOME | ConvertFrom-Json | ConvertTo-Json -Depth 10
Secure MCP Filesystem Server running on stdio
Allowed directories: [ 'C:\\Users\\hikari' ]
{
"result": {
"tools": [
{
"name": "read_file",
"description": "Read the complete contents of a file from the file system. Handles various text encodings and provides detailed error messages if the file cannot be read. Use this tool when you need to examine the contents of a single file. Only works within allowed directories.",
"inputSchema": {
"type": "object",
"properties": {
"path": {
"type": "string"
}
},
"required": [
"path"
],
"additionalProperties": false,
"$schema": "http://json-schema.org/draft-07/schema#"
}
},
...
]
}
}

tools/list を標準入力で与えることで、ツール一覧を JSON 形式で取得できます。

ツール呼び出し

ツールの情報をもとに呼び出してみます。

> @{ jsonrpc = "2.0"; method = "tools/call"; params = @{name = "read_file"; arguments = @{path = ".gitconfig"}}; id = 2 } | ConvertTo-Json -Compress -Depth 10 | npx @modelcontextprotocol/server-filesystem $HOME | ConvertFrom-Json | ConvertTo-Json -Depth 10
Secure MCP Filesystem Server running on stdio
Allowed directories: [ 'C:\\Users\\hikari' ]
{
"result": {
"content": [
{
"type": "text",
"text": "..."
}
]
},
"jsonrpc": "2.0",
"id": 2
}

Rocky Linux 8.10 を WSL にインストールする

· 約2分
ひかり
Main bloger

Rocky Linux 8.10 のイメージをダウンロードする

$dest = Join-Path $env:TEMP "Rocky-8-Container-Base.latest.x86_64.tar.xz"
Invoke-WebRequest -Uri "https://dl.rockylinux.org/pub/rocky/8/images/x86_64/Rocky-8-Container-Base.latest.x86_64.tar.xz" -OutFile $dest

インポート

wsl --import RockyLinux-8.10 $HOME $dest

passwd のインストール

wsl -d RockyLinux-8.10 -u root dnf update -y `&`& dnf install -y passwd

ユーザーの作成

$username = "hikari" # 好きなユーザー名を設定
wsl -d RockyLinux-8.10 -u root useradd -mG wheel $username
wsl -d RockyLinux-8.10 -u root passwd -d $username # ユーザーのパスワードを消去

sudo のインストール

wsl -d RockyLinux-8.10 -u root dnf update -y `&`& dnf install sudo -y

デフォルトユーザーの設定

$username = "hikari" # 好きなユーザー名を設定
$uid = wsl -d RockyLinux-8.10 id $username -u
if (-not $uid) {
Write-Error "UID の取得に失敗しました。ユーザー '$username' が存在しない可能性があります。"
exit 1
}

$basePath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss"

$targetKey = Get-ChildItem $basePath | Where-Object {
(Get-ItemProperty $_.PSPath).DistributionName -eq "RockyLinux-8.10"
}
if (-not $targetKey) {
Write-Error "DistributionName 'RockyLinux-8.10' が見つかりませんでした。"
exit 1
}

Set-ItemProperty -Path $targetKey.PSPath -Name "DefaultUid" -Value ([int]$uid)

EPEL の有効化

wsl -d RockyLinux-8.10 -u root dnf update -y `&`& dnf install -y epel-release

起動

wsl -d RockyLinux-8.10

デフォルトのディストリビューションにする場合

wsl --set-default RockyLinux-8.10

FastFetch のインストール

wsl -d RockyLinux-8.10 -u root dnf update -y `&`& dnf install fastfetch

FastFetch の実行

> wsl -d RockyLinux-8.10 -u root fastfetch
__wgliliiligw_, root@DESKTOP-MS-7C56-B550
_williiiiiiliilililw, -------------------------
_%iiiiiilililiiiiiiiiiii_ OS: Rocky Linux 8.10 x86_64
.Qliiiililiiiiiiililililiilm. Host: Windows Subsystem for Linux (2.0.14.0)
_iiiiiliiiiiililiiiiiiiiiiliil, Kernel: 5.15.133.1-microsoft-standard-WSL2
.lililiiilililiiiilililililiiiii, Uptime: 8 mins
_liiiiiiliiiiiiiliiiiiF{iiiiiilili, Packages: 285 (rpm)
jliililiiilililiiili@` ~ililiiiiiL Shell: bash 4.4.20
iiiliiiiliiiiiiili>` ~liililii Display 1: 1920x1080 @ 60Hz
liliiiliiilililii` -9liiiil Display 2: 1920x1080 @ 60Hz
iiiiiliiliiiiii~ "4lili WM: WSLg (Wayland)
4ililiiiiilil~| -w, )4lf Terminal: Windows Terminal
-liiiiililiF' _liig, )' CPU: AMD Ryzen 9 3900X (24) @ 3.800018 GHz
)iiiliii@` _QIililig, GPU: Microsoft Corporation Basic Render Driver
)iiii>` .Qliliiiililw Memory: 458.57 MiB / 62.76 GiB (0%)
)<>~ .mliiiiiliiiiiil, Disk (/): 51.72 GiB / 1007 GiB (5%)
_gllilililiililii~ Locale: C.UTF-8
giliiiiiiiiiiiiT`
-^~$ililili@~~' ████████████████████████
████████████████████████

RHEL 10.0 で epel-release を有効にする

· 約1分
ひかり
Main bloger
sudo dnf update -y
sudo subscription-manager repos --enable codeready-builder-for-rhel-10-x86_64-rpms
sudo dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm

EC2 Instance Connect が鍵なしで Windows から繋がらない

· 約1分
ひかり
Main bloger

Windows で Instance Connect へ接続できない

PS C:\> aws ec2-instance-connect ssh --instance-id i-0aa38de21acf2aa1c --region ap-south-1
Bad permissions. Try removing permissions for user: \\OWNER RIGHTS (S-1-3-4) on file C:/Users/hikari/AppData/Local/Temp/tmpm9m1bf7j/private-key.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: UNPROTECTED PRIVATE KEY FILE! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions for 'C:\\Users\\hikari\\AppData\\Local\\Temp\\tmpm9m1bf7j\\private-key' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "C:\\Users\\hikari\\AppData\\Local\\Temp\\tmpm9m1bf7j\\private-key": bad permissions
[email protected]: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).

2025/06/11 時点での検証。

WSL からだとログイン可能

PS C:\> wsl -- aws ec2-instance-connect ssh --instance-id i-0aa38de21acf2aa1c --region ap-south-1
, #_
~\_ ####_ Amazon Linux 2023
~~ \_#####\
~~ \###|
~~ \#/ ___ https://aws.amazon.com/linux/amazon-linux-2023
~~ V~' '->
~~~ /
~~._. _/
_/ _/
_/m/'
Last login: Tue Jun 10 22:50:33 2025 from 192.168.0.183
[ec2-user@ip-192-168-0-4 ~]$

なぜ?

追記

ダウングレードすれば接続できた。

治してほしいな。

参考: https://github.com/aws/aws-cli/issues/9114

msiexec.exe /i https://awscli.amazonaws.com/AWSCLIV2-2.17.35.msi

EC2 Instance Connect まとめ

· 約4分
ひかり
Main bloger

EC2 Instance Connect とは

EC2 Instance Connect は、AWS EC2 インスタンスへの SSH 接続を簡素化するためのサービス。

従来の SSH 接続方法では、インスタンスに公開鍵を事前に配置する必要があったが、 EC2 Instance Connect を使用すると、一時的な SSH 公開鍵をインスタンスに送信し、接続を確立する。(ただし、一部の AMI を除き Instance Connect のパッケージをインストールする必要がある)

インスタンスへの接続方法

インスタンスへの接続方法は何パターンか存在する。

① インターネットから直接 (インスタンス接続は関係ない)

インターネットから直接接続する方法は、インターネットゲートウェイを経由するか NAT ゲートウェイを経由する必要がある。また、パブリック IP アドレスが必要で閉域網に限定する場合は使えない。

ssh コマンドが使用可能なため、一番簡単である。

ssh <ユーザー名>@<パブリック IP アドレス>

② EC2 Instance Connect エンドポイントを経由した接続

AWS CLI を使用して、EC2 Instance Connect エンドポイントを使用して接続すれば、 パブリック IP アドレスは不要である。

また、その分の料金 (月数百円) を節約することができる。

AWS CLI を用いて、以下のようなコマンドで接続できるが、あらかじめキーペアをインポートして置き、インスタンスにキーペアを設定する必要がある。

具体的な接続方法は、例えば以下のコマンドから可能である。

aws ec2-instance-connect ssh --private-key-file .ssh/id_ed25519 --os-user <ユーザー名> --instance-id <インスタンス ID> --connection-type eice

※ただし、あらかじめアクセスキーを取得しておき、aws configure で設定しておく。

インターネットに接続したくないかつ公式ではない AMI を使用したいという場合にこの接続方法を使用するのが最も良い。

③ AWS マネジメントコンソールから Instance Connect 接続

Amazon Linux や Ubuntu は Instance Connect エンドポイントを作成しておけば、 マネジメントコンソール上からインスタンスに接続が可能である。

ただし、一部の AMI を除いて、Instance Connect のパッケージをインストールする必要がある。

詳しくは、https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-instance-connect-set-up.html

④ その他の接続方法

セッションマネージャーからの接続

セッションマネージャー用にエンドポイントを 2 つ設置、加えてインスタンスにセッションマネージャーからの接続を許可する IAM ロールをアタッチする必要がある。

また、一部の AMI を除いて、Session Manager のパッケージのインストールが必要である。

EC2 シリアルコンソールからの接続

シリアルコンソールを用いると直接インスタンスへ接続が可能である。 パスワードが設定されていない場合はログインすらできないので注意である。

セキュリティ設定

ネットワーク ACL (インスタンスのあるサブネット)

デフォルトですべての通信が許可されているので、 デフォルトで使用する場合は特に設定は必要ない。

最低限必要な設定を以下に示す。

インバウンドルール

インバウンドルールは、SSH (22) を許可する必要がある。

インスタンスの SSH サーバーのポート番号が 22 であるため、これを許可する。

アウトバウンドルール

アウトバウンドルールは、カスタム TCP (1024-65535) を許可する必要がある。

1024-65535 は SSH 接続時にクライアント側が使用するポート範囲である。

セキュリティグループ (インスタンス)

インバウンドルール

インバウンドルールは、SSH (22) を許可する必要がある。

この設定は必ず必要である。

アウトバウンドルール

セキュリティグループは通信を記憶 (ステートフル) しているため、通常はアウトバウンドルールの設定は不要である。

セキュリティグループ (EC2 Instance Connect エンドポイント)

インバウンドルール

ステートフルのため不要である。

アウトバウンドルール

SSH (22) を許可する必要がある。

インスタンスの 22 番ポートに対し通信するため、これを許可する。

AWS で公式 Rocky Linux イメージを使う

· 約5分
ひかり
Main bloger

AMI の選び方

公式ページから AMI を入手します。

https://rockylinux.org/ja-JP/download

インスタンスに設定するアーキテクチャー (ARM (aarch64)) を選び、 Cloud Images の AWS AMI を選択。

alt text

バージョン番号でフィルターを掛け、条件にあったものを探す。

alt text

AMI ID はコピーできないので、デプロイボタンをクリックし、AWS コンソールからコピーする。

AMI ID で検索を掛けると、でてくる

alt text

所有者でフィルタリングしたほうが良いかも。

所有者 = 792107900819

alt text

事前準備

  • キーペアの登録
    • あらかじめ ssh-keygen -t ed25519 コマンドを実行て公開鍵を作成し、.ssh/id_ed25519.pub をキーペアにインポートしておく
  • AWS CLI の導入
    • CLI のインストール
    • アクセスキーの設定 (aws configure)

ネットワークを建てる

NAT ゲートウェイよりも、パブリック IP アドレスのほうが安いので、Elastic IP を作成。

図にするとこんな感じで建てる。

EC2 Instance Connect エンドポイントを作成

alt text

EC2 Instance Connect エンドポイントを作成することで AWS CLI からログインできる。

インスタンスを建てる

  • ping を要求を受け入れるため ICMP (エコー要求) を許可 (セキュリティグループ)
  • SSH 接続ができるようにこれを許可する (セキュリティグループ)
  • ムンバイリージョンと、arm64 が安い
  • vCPU あたり 1.5 GiB の RAM が必要 (最低でも t4g.medium)

というわけで、以下の条件で建てた。

  • ムンバイリージョン
  • アーキテクチャ: arm64
  • AMI: RHEL 8.10 (LVM, aarch64); ami-0415efd8380284dc4
  • インスタンスタイプ: t4g.medium
  • キーペア: PC で作った公開鍵 (.ssh/id_ed25519.pub)
  • ネットワーク: パブリックサブネット (インターネットゲートウェイへのルートが定義されているルートテーブルが関連付けされている)
  • セキュリティグループ: セキュリティグループを作成 (名前はデフォルト)
    • ssh, 0.0.0.0/0
    • カスタム ICMP - IPv4 (エコー要求), 0.0.0.0/0
  • ストレージ: 1x 10GiB, gp3

接続

PC のターミナルを開き、以下を実行。

aws ec2-instance-connect ssh --private-key-file .ssh/id_ed25519 --os-user rocky --instance-id i-*****************

Instance Connect パッケージのインストール

Rocky Linux の AMI イメージには、Instance Connect パッケージがなく、マネジメントコンソールからの接続ができない。 そのため、パッケージをインストールする。

https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-instance-connect-set-up.html を参考にパッケージをダウンロード。

  • ※RHEL のパッケージを選択
  • ※OS のメジャーバージョンやアーキテクチャーが違うと正常に動作しないので注意

curl https://amazon-ec2-instance-connect-us-west-2.s3.us-west-2.amazonaws.com/latest/linux_arm64/ec2-instance-connect.rhel8.rpm -o /tmp/ec2-instance-connect.rpm
curl https://amazon-ec2-instance-connect-us-west-2.s3.us-west-2.amazonaws.com/latest/linux_amd64/ec2-instance-connect-selinux.noarch.rpm -o /tmp/ec2-instance-connect-selinux.rpm
sudo dnf install -y /tmp/ec2-instance-connect.rpm /tmp/ec2-instance-connect-selinux.rpm

インストールが完了すると、マネコン上からアクセスできるようになる

alt text

CDK (typescript)

CDK 作ったので載せておく。参考までに。

keyName (キーペア) の名前は変えておく。

import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';

export interface RockyLinuxStackProps extends cdk.StackProps {
}

export class RockyLinuxStack extends cdk.Stack {
public constructor(scope: cdk.App, id: string, props: RockyLinuxStackProps = {}) {
super(scope, id, props);

// Resources
const ec2dhcpOptions = new ec2.CfnDHCPOptions(this, 'EC2DHCPOptions', {
domainName: 'ap-south-1.compute.internal',
domainNameServers: [
'AmazonProvidedDNS',
],
tags: [
],
});
ec2dhcpOptions.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;

const ec2InternetGateway = new ec2.CfnInternetGateway(this, 'EC2InternetGateway', {
tags: [
{
value: 'igw',
key: 'Name',
},
],
});
ec2InternetGateway.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;

const ec2vpc = new ec2.CfnVPC(this, 'EC2VPC', {
cidrBlock: '10.0.0.0/16',
enableDnsSupport: true,
instanceTenancy: 'default',
enableDnsHostnames: true,
tags: [
{
value: 'vpc',
key: 'Name',
},
],
});
ec2vpc.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;

const ec2VPCGatewayAttachment = new ec2.CfnVPCGatewayAttachment(this, 'EC2VPCGatewayAttachment', {
vpcId: ec2vpc.ref,
internetGatewayId: ec2InternetGateway.ref,
});
ec2VPCGatewayAttachment.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;

const ec2NetworkAcl = new ec2.CfnNetworkAcl(this, 'EC2NetworkAcl', {
vpcId: ec2vpc.ref,
tags: [
],
});
ec2NetworkAcl.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;

const ec2RouteTable = new ec2.CfnRouteTable(this, 'EC2RouteTable', {
vpcId: ec2vpc.ref,
});
ec2RouteTable.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;

const ec2SecurityGroup = new ec2.CfnSecurityGroup(this, 'EC2SecurityGroup', {
groupDescription: 'launch-wizard-1 created 2025-04-27T00:11:58.641Z',
groupName: 'launch-wizard-1',
vpcId: ec2vpc.ref,
securityGroupIngress: [
{
cidrIp: '0.0.0.0/0',
ipProtocol: 'tcp',
fromPort: 22,
toPort: 22,
},
{
cidrIp: '0.0.0.0/0',
ipProtocol: 'icmp',
fromPort: 8,
toPort: -1,
},
],
securityGroupEgress: [
{
cidrIp: '0.0.0.0/0',
ipProtocol: '-1',
fromPort: -1,
toPort: -1,
},
],
});
ec2SecurityGroup.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;

const ec2Subnet = new ec2.CfnSubnet(this, 'EC2Subnet', {
vpcId: ec2vpc.ref,
mapPublicIpOnLaunch: false,
enableDns64: false,
availabilityZoneId: 'aps1-az1',
privateDnsNameOptionsOnLaunch: {
EnableResourceNameDnsARecord: false,
HostnameType: 'ip-name',
EnableResourceNameDnsAAAARecord: false,
},
cidrBlock: '10.0.0.0/20',
ipv6Native: false,
tags: [
{
value: 'subnet-public1-ap-south-1a',
key: 'Name',
},
],
});
ec2Subnet.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;

const ec2InstanceConnectEndpoint = new ec2.CfnInstanceConnectEndpoint(this, 'EC2InstanceConnectEndpoint', {
preserveClientIp: false,
securityGroupIds: [
ec2SecurityGroup.attrGroupId,
],
subnetId: ec2Subnet.attrSubnetId,
});
ec2InstanceConnectEndpoint.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;

const ec2vpcdhcpOptionsAssociation = new ec2.CfnVPCDHCPOptionsAssociation(this, 'EC2VPCDHCPOptionsAssociation', {
vpcId: ec2vpc.ref,
dhcpOptionsId: ec2dhcpOptions.ref,
});
ec2vpcdhcpOptionsAssociation.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;

const ec2RouteHg = new ec2.CfnRoute(this, 'EC2RouteHG', {
routeTableId: ec2RouteTable.ref,
destinationCidrBlock: '0.0.0.0/0',
gatewayId: ec2InternetGateway.ref,
});
ec2RouteHg.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;

const ec2SubnetNetworkAclAssociation = new ec2.CfnSubnetNetworkAclAssociation(this, 'EC2SubnetNetworkAclAssociation', {
networkAclId: ec2NetworkAcl.ref,
subnetId: ec2Subnet.ref,
});
ec2SubnetNetworkAclAssociation.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;

const ec2SubnetRouteTableAssociation = new ec2.CfnSubnetRouteTableAssociation(this, 'EC2SubnetRouteTableAssociation', {
routeTableId: ec2RouteTable.ref,
subnetId: ec2Subnet.ref,
});
ec2SubnetRouteTableAssociation.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;

const ec2Instance = new ec2.CfnInstance(this, 'EC2Instance', {
tenancy: 'default',
instanceInitiatedShutdownBehavior: 'stop',
cpuOptions: {
threadsPerCore: 1,
coreCount: 2,
},
blockDeviceMappings: [
{
ebs: {
volumeType: 'gp3',
iops: 3000,
volumeSize: 10,
encrypted: false,
deleteOnTermination: true,
},
deviceName: '/dev/sda1',
},
],
availabilityZone: 'ap-south-1a',
privateDnsNameOptions: {
enableResourceNameDnsARecord: false,
hostnameType: 'ip-name',
enableResourceNameDnsAaaaRecord: false,
},
ebsOptimized: true,
disableApiTermination: false,
keyName: 'hikari',
sourceDestCheck: true,
placementGroupName: '',
networkInterfaces: [
{
privateIpAddresses: [
{
privateIpAddress: '10.0.3.59',
primary: true,
},
],
secondaryPrivateIpAddressCount: 0,
deviceIndex: '0',
groupSet: [
ec2SecurityGroup.ref,
],
ipv6Addresses: [
],
subnetId: ec2Subnet.ref,
associatePublicIpAddress: true,
deleteOnTermination: true,
},
],
imageId: 'ami-0415efd8380284dc4',
instanceType: 't4g.medium',
monitoring: false,
tags: [
],
creditSpecification: {
cpuCredits: 'unlimited',
},
});
ec2Instance.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;

const ec2ElasticIp = new ec2.CfnEIP(this, 'EC2ElasticIp', {
domain: 'vpc',
tags: [
{
key: 'Name',
value: 'elastic-ip',
},
],
});
ec2ElasticIp.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;

const ec2EipAssociation = new ec2.CfnEIPAssociation(this, 'EC2EipAssociation', {
eip: ec2ElasticIp.ref,
instanceId: ec2Instance.ref,
});
ec2EipAssociation.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.DELETE;
}
}

パソコンセットアップ時に入れておきたいフリーソフトまとめ

· 約1分
ひかり
Main bloger

※ここでいうフリーソフトは原則無料で使えるものであり、オープンソースソフトウェアとは別物です。

通話・チャット

画像編集

音声編集

音声合成

3D モデリング

配信

ベンチマーク・システム情報

開発ツール系

Hyper-V の Ubuntu にシリアルコンソールで接続する方法

· 約2分
ひかり
Main bloger

VM の設定

「名前付きパイプ」を選択し、パイプ名を「COM1」に設定

Hyper-V Serial Settings

Ubuntu の設定

GRUB の設定

sudo nano /etc/default/grub で GRUB の設定ファイルを開き、

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash console=ttyS0,115200n8"

に変更する。

保存後、

sudo update-grub

で GRUB の設定を適用する。

シリアルポートの有効化

シリアルポートでログインできるようにサービスを設定する。

sudo systemctl enable [email protected]
sudo systemctl start [email protected]

接続

管理者権限で起動する

Tera Term から接続

Connect Serial port of Ubuntu on Hyper-V from Tera Term

PuTTY から接続

管理者権限で起動する

Serial lineSpeedConnection type:
\.\pipe\COM1115200Serial

に設定。

Connect Serial port of Ubuntu on Hyper-V from PuTTy

Connect Serial port of Ubuntu on Hyper-V from PuTTy

管理者権限で起動する

Connect Serial port of Ubuntu on Hyper-V from plink.exe on WindowsTerminal

[System.Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding("utf-8")
[System.Console]::InputEncoding = [System.Text.Encoding]::GetEncoding("utf-8")
plink.exe -serial \\.\pipe\COM1 -sercfg 115200,8,n,1,N

Ctrl + C で終了

VOICEVOX の API を観察してみる

· 約4分
ひかり
Main bloger

VOICEVOX はエディターとエンジンとコアで構成されているとあります。

参考: 全体構成

エディターがアプリで、エンジンが http サーバー、コアが音声合成の処理を行うモジュールになっているらしいです。

つまり、エディターがエンジンに対して REST API (以下 API) を叩いているというわけですね。

というわけで、その API の内容を観察してみようという記事になります。

API のキャプチャーには Wireshark を用いました。

起動時の通信

http and tcp.port == 50021 でフィルターをかけてみた結果が、こちら。

起動時には以下の情報を読み取っているようです。

  • バージョン情報 /version
  • エンジンのマニフェスト情報 /engine_manifest
  • スピーカー情報 /speakers (ずんだもんなどのキャラクターリスト)
  • シンガー情報 /singers (〃)

スピーカー・シンガー取得後は各キャラクターの情報をより詳細に取得しているようですね。(/speaker_info?speaker_uuid=xxx, /singer_info?speaker_uuid=xxx)

音声合成リクエスト時の通信

では実際にずんだもんで音声合成のリクエストを送って、API を覗いてみました。

以下のような流れで音声が取得されているみたいです。

  1. /accent_phrases でアクセントの情報
  2. /synthesis?speaker=3 でずんだもんの声を合成

(2.) で送っているリクエストボディの内容は、以下のように (1.) のレスポンスと似たようなものになっています。

したがって、(1.) でアクセントを取得し、(2.) でアクセントから音声を合成するという流れのようです。

実際に API を叩いてみる

httpie というツールを用いて API を叩きました。

  1. スピーカー情報を取得

ずんだもん (ノーマル) の id は 3 であることがわかりました。

  1. アクセント情報を取得

ずんだもんなのだ でアクセント情報を取得してみました。 (スピーカー情報と異なり、POST リクエストで取得)

  1. 音声合成

以下のようなリクエストボディーを作成します。

{
"accent_phrases": </accent_phrases で取得したデータ>,
"speedScale": 1,
"pitchScale": 0,
"intonationScale": 1,
"volumeScale": 1,
"prePhonemeLength": 0.1,
"postPhonemeLength": 0.1,
"outputSamplingRate": 24000,
"outputStereo": false,
"kana": ""
}

httpie では wav が扱えないようなので、PowerShell でリクエストを送ります。

# URLとJSONデータを定義
$url = 'http://localhost:50021/synthesis?speaker=3'
$jsonBody = @"
{
"accent_phrases": [
{
"moras": [
{
"text": "ズ",
"consonant": "z",
"consonant_length": 0.12722788751125336,
"vowel": "u",
"vowel_length": 0.11318323761224747,
"pitch": 5.773037910461426
},
{
"text": "ン",
"consonant": null,
"consonant_length": null,
"vowel": "N",
"vowel_length": 0.09306197613477707,
"pitch": 6.108947277069092
},
{
"text": "ダ",
"consonant": "d",
"consonant_length": 0.04249810427427292,
"vowel": "a",
"vowel_length": 0.09372275322675705,
"pitch": 6.09743070602417
},
{
"text": "モ",
"consonant": "m",
"consonant_length": 0.07012023776769638,
"vowel": "o",
"vowel_length": 0.1172478124499321,
"pitch": 5.932623386383057
},
{
"text": "ン",
"consonant": null,
"consonant_length": null,
"vowel": "N",
"vowel_length": 0.06496299058198929,
"pitch": 5.745952129364014
},
{
"text": "ナ",
"consonant": "n",
"consonant_length": 0.038462959229946136,
"vowel": "a",
"vowel_length": 0.08576127141714096,
"pitch": 5.5794854164123535
}
],
"accent": 1,
"pause_mora": null,
"is_interrogative": false
},
{
"moras": [
{
"text": "ノ",
"consonant": "n",
"consonant_length": 0.05504273623228073,
"vowel": "o",
"vowel_length": 0.0903041884303093,
"pitch": 5.551316261291504
},
{
"text": "ダ",
"consonant": "d",
"consonant_length": 0.05024997144937515,
"vowel": "a",
"vowel_length": 0.20450790226459503,
"pitch": 5.633930206298828
}
],
"accent": 2,
"pause_mora": null,
"is_interrogative": false
}
],
"speedScale": 1,
"pitchScale": 0,
"intonationScale": 1,
"volumeScale": 1,
"prePhonemeLength": 0.1,
"postPhonemeLength": 0.1,
"outputSamplingRate": 24000,
"outputStereo": false,
"kana": ""
}
"@

# HTTPヘッダーを作成
$headers = @{
'Content-Type' = 'application/json'
}

# POSTリクエストを送信し、レスポンスを取得
$response = Invoke-WebRequest -Uri $url -Method Post -Headers $headers -Body $jsonBody -OutFile "output.wav"

# 開いて再生
start output.wav

VOICEVOX: ずんだもん

以上!