Skip to content

Commit bc06e11

Browse files
committed
WIP, Introduce MCP server for podcvd
1 parent 75e592d commit bc06e11

5 files changed

Lines changed: 112 additions & 8 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ cuttlefish-*_*.*.*.tar.xz
2323
/*/debian/*.log
2424

2525
container/src/podcvd/podcvd
26+
container/src/podcvd/podcvd_mcp_server
2627

2728
frontend/src/operator/webui/src/environments/version.ts
2829

container/src/podcvd/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,27 @@ describes how to build `cuttlefish-podcvd` debian package.
4646

4747
Execute `sudo apt install ./cuttlefish-podcvd_*.deb` to install it on your
4848
machine.
49+
50+
### Manually build podcvd_mcp_server
51+
52+
Execute `go build ./cmd/podcvd_mcp_server` from `container/src/podcvd`
53+
directory.
54+
55+
### Test podcvd_mcp_server on your local machine
56+
57+
Execute `podcvd_mcp_server`, then it'll be running on port `8080`.
58+
59+
With Gemini CLI for example, append this JSON object into
60+
`$HOME/.gemini/settings.json`.
61+
```json
62+
{
63+
"mcpServers": {
64+
"podcvd-mcp-server": {
65+
"url": "http://localhost:8080/mcp"
66+
}
67+
}
68+
}
69+
```
70+
71+
Afterwards, `podcvd_mcp_server` is getting to be applied with any further
72+
Gemini CLI sessions.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright (C) 2026 The Android Open Source Project
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package main
16+
17+
import (
18+
"context"
19+
"log"
20+
"net/http"
21+
22+
"github.com/google/android-cuttlefish/container/src/podcvd/internal"
23+
24+
"github.com/modelcontextprotocol/go-sdk/mcp"
25+
)
26+
27+
type CreateArgs struct{}
28+
29+
func main() {
30+
server := mcp.NewServer(&mcp.Implementation{Name: "podcvd-mcp-server", Version: "v0.0.1"}, nil)
31+
32+
mcp.AddTool(server, &mcp.Tool{
33+
Name: "create",
34+
Description: "Create a Cuttlefish instance group within the container instance",
35+
}, func(ctx context.Context, req *mcp.CallToolRequest, args CreateArgs) (*mcp.CallToolResult, any, error) {
36+
internal.Main([]string{"create", "--vhost_user_vsock=true", "--report_anonymous_usage_stats=n"})
37+
38+
return &mcp.CallToolResult{
39+
Content: []mcp.Content{
40+
&mcp.TextContent{Text: "created a Cuttlefish instance group successfully"},
41+
},
42+
}, nil, nil
43+
})
44+
45+
handler := mcp.NewSSEHandler(func(r *http.Request) *mcp.Server {
46+
return server
47+
}, &mcp.SSEOptions{})
48+
49+
http.Handle("/mcp", handler)
50+
51+
log.Println("Starting podcvd-mcp-server on http://localhost:8080/mcp")
52+
if err := http.ListenAndServe(":8080", nil); err != nil {
53+
log.Fatalf("HTTP Server failed: %v", err)
54+
}
55+
}

container/src/podcvd/go.mod

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,42 @@ toolchain go1.24.8
66

77
replace github.com/google/android-cuttlefish/container/src/libcfcontainer => ../libcfcontainer
88

9-
require github.com/google/android-cuttlefish/container/src/libcfcontainer v0.0.0-00010101000000-000000000000
9+
require (
10+
github.com/containerd/errdefs v1.0.0
11+
github.com/docker/docker v28.5.2+incompatible
12+
github.com/docker/go-connections v0.6.0
13+
github.com/go-playground/validator/v10 v10.30.1
14+
github.com/google/android-cuttlefish/container/src/libcfcontainer v0.0.0-00010101000000-000000000000
15+
github.com/modelcontextprotocol/go-sdk v1.4.0
16+
github.com/vishvananda/netlink v1.3.1
17+
)
1018

1119
require (
1220
dario.cat/mergo v1.0.2 // indirect
1321
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
1422
github.com/Microsoft/go-winio v0.6.2 // indirect
1523
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
1624
github.com/cespare/xxhash/v2 v2.3.0 // indirect
17-
github.com/containerd/errdefs v1.0.0 // indirect
1825
github.com/containerd/errdefs/pkg v0.3.0 // indirect
1926
github.com/distribution/reference v0.6.0 // indirect
20-
github.com/docker/docker v28.5.2+incompatible // indirect
21-
github.com/docker/go-connections v0.6.0 // indirect
2227
github.com/docker/go-units v0.5.0 // indirect
2328
github.com/felixge/httpsnoop v1.0.4 // indirect
2429
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
2530
github.com/go-logr/logr v1.4.3 // indirect
2631
github.com/go-logr/stdr v1.2.2 // indirect
2732
github.com/go-playground/locales v0.14.1 // indirect
2833
github.com/go-playground/universal-translator v0.18.1 // indirect
29-
github.com/go-playground/validator/v10 v10.30.1 // indirect
34+
github.com/google/jsonschema-go v0.4.2 // indirect
3035
github.com/leodido/go-urn v1.4.0 // indirect
3136
github.com/moby/docker-image-spec v1.3.1 // indirect
3237
github.com/moby/sys/sequential v0.6.0 // indirect
3338
github.com/opencontainers/go-digest v1.0.0 // indirect
3439
github.com/opencontainers/image-spec v1.1.1 // indirect
3540
github.com/pkg/errors v0.9.1 // indirect
36-
github.com/vishvananda/netlink v1.3.1 // indirect
41+
github.com/segmentio/asm v1.1.3 // indirect
42+
github.com/segmentio/encoding v0.5.3 // indirect
3743
github.com/vishvananda/netns v0.0.5 // indirect
44+
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
3845
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
3946
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect
4047
go.opentelemetry.io/otel v1.39.0 // indirect
@@ -44,6 +51,7 @@ require (
4451
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
4552
golang.org/x/crypto v0.47.0 // indirect
4653
golang.org/x/net v0.49.0 // indirect
54+
golang.org/x/oauth2 v0.34.0 // indirect
4755
golang.org/x/sys v0.41.0 // indirect
4856
golang.org/x/text v0.33.0 // indirect
4957
google.golang.org/protobuf v1.36.10 // indirect

container/src/podcvd/go.sum

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,20 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
3333
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
3434
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
3535
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
36+
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
37+
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
3638
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
3739
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
3840
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
3941
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
4042
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
4143
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
44+
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
45+
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
4246
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
4347
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
48+
github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8=
49+
github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
4450
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
4551
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
4652
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
@@ -55,6 +61,8 @@ github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7z
5561
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
5662
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
5763
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
64+
github.com/modelcontextprotocol/go-sdk v1.4.0 h1:u0kr8lbJc1oBcawK7Df+/ajNMpIDFE41OEPxdeTLOn8=
65+
github.com/modelcontextprotocol/go-sdk v1.4.0/go.mod h1:Nxc2n+n/GdCebUaqCOhTetptS17SXXNu9IfNTaLDi1E=
5866
github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ=
5967
github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw=
6068
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
@@ -65,6 +73,10 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
6573
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
6674
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
6775
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
76+
github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc=
77+
github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg=
78+
github.com/segmentio/encoding v0.5.3 h1:OjMgICtcSFuNvQCdwqMCv9Tg7lEOXGwm1J5RPQccx6w=
79+
github.com/segmentio/encoding v0.5.3/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0=
6880
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
6981
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
7082
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
@@ -73,6 +85,8 @@ github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW
7385
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
7486
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
7587
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
88+
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
89+
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
7690
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
7791
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
7892
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y=
@@ -97,17 +111,19 @@ golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
97111
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
98112
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
99113
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
114+
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
115+
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
100116
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
101117
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
102118
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
103-
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
104-
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
105119
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
106120
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
107121
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
108122
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
109123
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
110124
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
125+
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
126+
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
111127
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
112128
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
113129
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE=

0 commit comments

Comments
 (0)