Skip to content

Commit 1547e66

Browse files
authored
feat(ophis) add http server cli cmd (#29)
* feat(ophis) add http server cli cmd * doc(readme) stream cli cmd * add graceful shutdown * chore: update deps
1 parent f73c16c commit 1547e66

File tree

16 files changed

+345
-234
lines changed

16 files changed

+345
-234
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,22 @@ func main() {
4545

4646
Your CLI commands are now available as MCP tools!
4747

48+
### Stream over HTTP
49+
50+
Expose your MCP server over HTTP for remote access:
51+
52+
```bash
53+
./my-cli mcp stream --host localhost --port 8080
54+
```
55+
4856
## Commands
4957

5058
The `ophis.Command(nil)` adds these subcommands to your CLI:
5159

5260
```
5361
mcp
5462
├── start # Start MCP server on stdio
63+
├── stream # Stream MCP server over HTTP
5564
├── tools # Export available MCP tools as JSON
5665
├── claude
5766
│ ├── enable # Add server to Claude Desktop config

config.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
package ophis
22

33
import (
4+
"context"
45
"log/slog"
6+
"net/http"
57
"os"
8+
"os/signal"
9+
"syscall"
10+
"time"
611

712
"github.com/modelcontextprotocol/go-sdk/mcp"
813
"github.com/spf13/cobra"
@@ -49,6 +54,36 @@ func (c *Config) serveStdio(cmd *cobra.Command) error {
4954
return c.server.Run(cmd.Context(), c.Transport)
5055
}
5156

57+
func (c *Config) serveHTTP(cmd *cobra.Command, addr string) error {
58+
c.registerTools(cmd)
59+
60+
// Create the streamable HTTP handler.
61+
handler := mcp.NewStreamableHTTPHandler(func(_ *http.Request) *mcp.Server {
62+
return c.server
63+
}, nil)
64+
65+
server := &http.Server{Addr: addr, Handler: handler}
66+
67+
// Shutdown gracefully
68+
ch := make(chan os.Signal, 1)
69+
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
70+
go func() {
71+
select {
72+
case <-ch:
73+
case <-cmd.Context().Done():
74+
}
75+
signal.Stop(ch)
76+
77+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
78+
defer cancel()
79+
if err := server.Shutdown(ctx); err != nil {
80+
slog.Error("error shutting down server", "error", err)
81+
}
82+
}()
83+
84+
return server.ListenAndServe()
85+
}
86+
5287
// registerTools fully initializes a MCP server and populates c.tools
5388
func (c *Config) registerTools(cmd *cobra.Command) {
5489
// slog to stderr

examples/argocd/go.mod

Lines changed: 61 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
module github.com/njayp/ophis/examples/argocd
22

3-
go 1.24.6
3+
go 1.25.0
44

55
replace github.com/njayp/ophis => ../../
66

77
require (
8-
github.com/argoproj/argo-cd/v3 v3.1.9
8+
github.com/argoproj/argo-cd/v3 v3.2.0
99
github.com/njayp/ophis v0.0.0-00010101000000-000000000000
1010
github.com/spf13/cobra v1.10.1
1111
k8s.io/klog/v2 v2.130.1
@@ -14,18 +14,18 @@ require (
1414
require (
1515
cloud.google.com/go/auth v0.15.0 // indirect
1616
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
17-
cloud.google.com/go/compute/metadata v0.6.0 // indirect
18-
code.gitea.io/sdk/gitea v0.21.0 // indirect
17+
cloud.google.com/go/compute/metadata v0.7.0 // indirect
18+
code.gitea.io/sdk/gitea v0.22.0 // indirect
1919
dario.cat/mergo v1.0.2 // indirect
20-
github.com/42wim/httpsig v1.2.2 // indirect
21-
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
22-
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 // indirect
23-
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
20+
github.com/42wim/httpsig v1.2.3 // indirect
21+
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 // indirect
22+
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0 // indirect
23+
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
2424
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
2525
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
2626
github.com/MakeNowJust/heredoc v1.0.0 // indirect
2727
github.com/Masterminds/goutils v1.1.1 // indirect
28-
github.com/Masterminds/semver/v3 v3.3.1 // indirect
28+
github.com/Masterminds/semver/v3 v3.4.0 // indirect
2929
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
3030
github.com/Microsoft/go-winio v0.6.2 // indirect
3131
github.com/OvyFlash/telegram-bot-api v0.0.0-20241219171906-3f2ca0c14ada // indirect
@@ -34,8 +34,8 @@ require (
3434
github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20240116134246-a8cbe886bab0 // indirect
3535
github.com/TomOnTime/utfutil v1.0.0 // indirect
3636
github.com/alicebob/miniredis/v2 v2.35.0 // indirect
37-
github.com/argoproj/gitops-engine v0.7.1-0.20250905160054-e48120133eec // indirect
38-
github.com/argoproj/notifications-engine v0.4.1-0.20250309174002-87bf0576a872 // indirect
37+
github.com/argoproj/gitops-engine v0.7.1-0.20251006172252-b89b0871b414 // indirect
38+
github.com/argoproj/notifications-engine v0.4.1-0.20250908182349-da04400446ff // indirect
3939
github.com/argoproj/pkg v0.13.6 // indirect
4040
github.com/argoproj/pkg/v2 v2.0.1 // indirect
4141
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
@@ -56,11 +56,11 @@ require (
5656
github.com/aws/smithy-go v1.22.2 // indirect
5757
github.com/beorn7/perks v1.0.1 // indirect
5858
github.com/blang/semver/v4 v4.0.0 // indirect
59-
github.com/bmatcuk/doublestar/v4 v4.8.1 // indirect
59+
github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect
6060
github.com/bombsimon/logrusr/v4 v4.1.0 // indirect
6161
github.com/bradleyfalzon/ghinstallation/v2 v2.16.0 // indirect
62-
github.com/casbin/casbin/v2 v2.107.0 // indirect
63-
github.com/casbin/govaluate v1.7.0 // indirect
62+
github.com/casbin/casbin/v2 v2.123.0 // indirect
63+
github.com/casbin/govaluate v1.10.0 // indirect
6464
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
6565
github.com/cespare/xxhash/v2 v2.3.0 // indirect
6666
github.com/chai2010/gettext-go v1.0.3 // indirect
@@ -80,10 +80,10 @@ require (
8080
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
8181
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
8282
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
83-
github.com/expr-lang/expr v1.17.5 // indirect
83+
github.com/expr-lang/expr v1.17.6 // indirect
8484
github.com/fatih/camelcase v1.0.0 // indirect
8585
github.com/felixge/httpsnoop v1.0.4 // indirect
86-
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
86+
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
8787
github.com/gfleury/go-bitbucket-v1 v0.0.0-20240917142304-df385efaac68 // indirect
8888
github.com/go-errors/errors v1.5.1 // indirect
8989
github.com/go-fed/httpsig v1.1.0 // indirect
@@ -109,11 +109,11 @@ require (
109109
github.com/gogits/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 // indirect
110110
github.com/gogo/protobuf v1.3.2 // indirect
111111
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
112-
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
112+
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
113113
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
114114
github.com/golang/protobuf v1.5.4 // indirect
115115
github.com/google/btree v1.1.3 // indirect
116-
github.com/google/gnostic-models v0.6.9 // indirect
116+
github.com/google/gnostic-models v0.7.0 // indirect
117117
github.com/google/go-cmp v0.7.0 // indirect
118118
github.com/google/go-github/v69 v69.2.0 // indirect
119119
github.com/google/go-github/v72 v72.0.0 // indirect
@@ -135,7 +135,7 @@ require (
135135
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect
136136
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
137137
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
138-
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
138+
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
139139
github.com/hashicorp/go-version v1.7.0 // indirect
140140
github.com/huandu/xstrings v1.5.0 // indirect
141141
github.com/improbable-eng/grpc-web v0.15.1-0.20230209220825-1d9bbb09a099 // indirect
@@ -151,7 +151,7 @@ require (
151151
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
152152
github.com/kevinburke/ssh_config v1.2.0 // indirect
153153
github.com/klauspost/compress v1.18.0 // indirect
154-
github.com/ktrysmt/go-bitbucket v0.9.86 // indirect
154+
github.com/ktrysmt/go-bitbucket v0.9.87 // indirect
155155
github.com/kylelemons/godebug v1.1.0 // indirect
156156
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
157157
github.com/mailru/easyjson v0.9.0 // indirect
@@ -164,9 +164,9 @@ require (
164164
github.com/mitchellh/reflectwalk v1.0.2 // indirect
165165
github.com/moby/spdystream v0.5.0 // indirect
166166
github.com/moby/term v0.5.2 // indirect
167-
github.com/modelcontextprotocol/go-sdk v1.0.0 // indirect
167+
github.com/modelcontextprotocol/go-sdk v1.1.0 // indirect
168168
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
169-
github.com/modern-go/reflect2 v1.0.2 // indirect
169+
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
170170
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
171171
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
172172
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
@@ -180,11 +180,11 @@ require (
180180
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
181181
github.com/pkg/errors v0.9.1 // indirect
182182
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
183-
github.com/prometheus/client_golang v1.22.0 // indirect
183+
github.com/prometheus/client_golang v1.23.2 // indirect
184184
github.com/prometheus/client_model v0.6.2 // indirect
185-
github.com/prometheus/common v0.64.0 // indirect
185+
github.com/prometheus/common v0.66.1 // indirect
186186
github.com/prometheus/procfs v0.16.1 // indirect
187-
github.com/r3labs/diff/v3 v3.0.1 // indirect
187+
github.com/r3labs/diff/v3 v3.0.2 // indirect
188188
github.com/redis/go-redis/v9 v9.8.0 // indirect
189189
github.com/robfig/cron/v3 v3.0.2-0.20210106135023-bc59245fe10e // indirect
190190
github.com/rs/cors v1.11.1 // indirect
@@ -209,60 +209,61 @@ require (
209209
github.com/xlab/treeprint v1.2.0 // indirect
210210
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
211211
github.com/yuin/gopher-lua v1.1.1 // indirect
212-
gitlab.com/gitlab-org/api/client-go v0.130.1 // indirect
212+
gitlab.com/gitlab-org/api/client-go v0.142.6 // indirect
213213
go.mongodb.org/mongo-driver v1.17.1 // indirect
214214
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
215-
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
215+
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect
216216
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
217-
go.opentelemetry.io/otel v1.36.0 // indirect
218-
go.opentelemetry.io/otel/metric v1.36.0 // indirect
219-
go.opentelemetry.io/otel/trace v1.36.0 // indirect
220-
go.uber.org/automaxprocs v1.6.0 // indirect
221-
golang.org/x/crypto v0.39.0 // indirect
222-
golang.org/x/net v0.41.0 // indirect
223-
golang.org/x/oauth2 v0.30.0 // indirect
224-
golang.org/x/sync v0.15.0 // indirect
225-
golang.org/x/sys v0.33.0 // indirect
226-
golang.org/x/term v0.32.0 // indirect
227-
golang.org/x/text v0.26.0 // indirect
228-
golang.org/x/time v0.12.0 // indirect
217+
go.opentelemetry.io/otel v1.38.0 // indirect
218+
go.opentelemetry.io/otel/metric v1.38.0 // indirect
219+
go.opentelemetry.io/otel/trace v1.38.0 // indirect
220+
go.yaml.in/yaml/v2 v2.4.2 // indirect
221+
go.yaml.in/yaml/v3 v3.0.4 // indirect
222+
golang.org/x/crypto v0.42.0 // indirect
223+
golang.org/x/net v0.44.0 // indirect
224+
golang.org/x/oauth2 v0.32.0 // indirect
225+
golang.org/x/sync v0.17.0 // indirect
226+
golang.org/x/sys v0.36.0 // indirect
227+
golang.org/x/term v0.35.0 // indirect
228+
golang.org/x/text v0.29.0 // indirect
229+
golang.org/x/time v0.13.0 // indirect
229230
gomodules.xyz/envconfig v1.3.1-0.20190308184047-426f31af0d45 // indirect
230231
gomodules.xyz/notify v0.1.1 // indirect
231232
google.golang.org/api v0.223.0 // indirect
232233
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
233-
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect
234-
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect
235-
google.golang.org/grpc v1.73.0 // indirect
236-
google.golang.org/protobuf v1.36.6 // indirect
234+
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
235+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
236+
google.golang.org/grpc v1.75.1 // indirect
237+
google.golang.org/protobuf v1.36.9 // indirect
237238
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
238239
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
239240
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
240241
gopkg.in/inf.v0 v0.9.1 // indirect
241242
gopkg.in/warnings.v0 v0.1.2 // indirect
242243
gopkg.in/yaml.v2 v2.4.0 // indirect
243244
gopkg.in/yaml.v3 v3.0.1 // indirect
244-
k8s.io/api v0.33.1 // indirect
245-
k8s.io/apiextensions-apiserver v0.33.1 // indirect
246-
k8s.io/apimachinery v0.33.1 // indirect
247-
k8s.io/apiserver v0.33.1 // indirect
248-
k8s.io/cli-runtime v0.33.1 // indirect
249-
k8s.io/client-go v0.33.1 // indirect
250-
k8s.io/component-base v0.33.1 // indirect
251-
k8s.io/component-helpers v0.33.1 // indirect
252-
k8s.io/controller-manager v0.33.1 // indirect
253-
k8s.io/kube-aggregator v0.33.1 // indirect
254-
k8s.io/kube-openapi v0.0.0-20250610211856-8b98d1ed966a // indirect
255-
k8s.io/kubectl v0.33.1 // indirect
256-
k8s.io/kubernetes v1.33.1 // indirect
245+
k8s.io/api v0.34.0 // indirect
246+
k8s.io/apiextensions-apiserver v0.34.0 // indirect
247+
k8s.io/apimachinery v0.34.0 // indirect
248+
k8s.io/apiserver v0.34.0 // indirect
249+
k8s.io/cli-runtime v0.34.0 // indirect
250+
k8s.io/client-go v0.34.0 // indirect
251+
k8s.io/component-base v0.34.0 // indirect
252+
k8s.io/component-helpers v0.34.0 // indirect
253+
k8s.io/controller-manager v0.34.0 // indirect
254+
k8s.io/kube-aggregator v0.34.0 // indirect
255+
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
256+
k8s.io/kubectl v0.34.0 // indirect
257+
k8s.io/kubernetes v1.34.0 // indirect
257258
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
258259
layeh.com/gopher-json v0.0.0-20190114024228-97fed8db8427 // indirect
259260
nhooyr.io/websocket v1.8.7 // indirect
260261
oras.land/oras-go/v2 v2.6.0 // indirect
261262
sigs.k8s.io/controller-runtime v0.21.0 // indirect
262263
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
263-
sigs.k8s.io/kustomize/api v0.19.0 // indirect
264-
sigs.k8s.io/kustomize/kyaml v0.19.0 // indirect
264+
sigs.k8s.io/kustomize/api v0.20.1 // indirect
265+
sigs.k8s.io/kustomize/kyaml v0.20.1 // indirect
265266
sigs.k8s.io/randfill v1.0.0 // indirect
266-
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
267-
sigs.k8s.io/yaml v1.4.0 // indirect
267+
sigs.k8s.io/structured-merge-diff/v6 v6.3.1-0.20251003215857-446d8398e19c // indirect
268+
sigs.k8s.io/yaml v1.6.0 // indirect
268269
)

0 commit comments

Comments
 (0)