Skip to content

Commit 1d601d4

Browse files
authored
Merge pull request #33 from linuxfoundation/arch-350-debug-endpoint
feat: add expvar debug endpoint and build version injection
2 parents bef6f7f + f69abd7 commit 1d601d4

9 files changed

Lines changed: 91 additions & 25 deletions

File tree

.github/workflows/ko-build-main.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ jobs:
2727
- uses: ko-build/setup-ko@d006021bd0c28d1ce33a07e7943d48b079944c8d # v0.9
2828
with:
2929
version: v0.18.0
30-
- run: |
30+
- name: Build and publish lfx-mcp-server image
31+
env:
32+
VERSION: development-${{ github.sha }}
33+
run: |
3134
ko build github.com/linuxfoundation/lfx-mcp/cmd/lfx-mcp-server \
3235
-B \
3336
--platform linux/amd64,linux/arm64 \

.github/workflows/ko-build-tag.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ jobs:
5858

5959
- name: Build and publish lfx-mcp-server image
6060
id: build
61+
env:
62+
VERSION: ${{ github.ref_name }}
6163
run: |
6264
set -euo pipefail
6365
ko build github.com/linuxfoundation/lfx-mcp/cmd/lfx-mcp-server \

.ko.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright The Linux Foundation and contributors.
2+
# SPDX-License-Identifier: MIT
3+
builds:
4+
- id: lfx-mcp-server
5+
dir: ./cmd/lfx-mcp-server
6+
ldflags:
7+
- -X=main.Version={{.Env.VERSION}}

Dockerfile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ RUN go mod download
2121
# Copy the code into the container
2222
COPY . .
2323

24-
# Build the packages
25-
RUN go build -o /go/bin/lfx-mcp-server -trimpath -ldflags="-w -s" github.com/linuxfoundation/lfx-mcp/cmd/lfx-mcp-server
24+
# Build the packages. VERSION may be passed via --build-arg for local Docker builds;
25+
# CI uses ko with .ko.yaml ldflags instead.
26+
ARG VERSION=dev
27+
RUN go build -o /go/bin/lfx-mcp-server -trimpath -ldflags="-w -s -X main.Version=${VERSION}" github.com/linuxfoundation/lfx-mcp/cmd/lfx-mcp-server
2628

2729
# Run our go binary standalone
2830
FROM cgr.dev/chainguard/static:latest

Makefile

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
# Copyright The Linux Foundation and each contributor to LFX.
22
# SPDX-License-Identifier: MIT
33

4-
.PHONY: all build clean check fmt vet lint test test-coverage run help deps install-tools docker-build
4+
.PHONY: all build clean check fmt vet lint test test-coverage run help deps install-tools docker-build ko-build
55

66
# Build variables
77
BINARY_NAME=lfx-mcp-server
88
CMD_DIR=./cmd/lfx-mcp-server
99
BUILD_DIR=./bin
1010
GO_FILES=$(shell find . -name "*.go" -type f)
1111

12+
# Version string: clean tag on a tagged commit, tag+offset+hash between tags,
13+
# with a -dirty suffix if there are uncommitted changes.
14+
VERSION := $(shell git describe --tags --dirty --always 2>/dev/null || echo "dev")
15+
1216
# Build flags
13-
LDFLAGS=-ldflags="-s -w"
17+
LDFLAGS=-ldflags="-s -w -X main.Version=$(VERSION)"
1418

15-
# Docker variables
16-
DOCKER_IMAGE=linuxfoundation/lfx-mcp
17-
DOCKER_TAG=latest
19+
# Docker/ko variables
20+
DOCKER_IMAGE=linuxfoundation/lfx-mcp/lfx-mcp-server
21+
DOCKER_TAG=local
1822

1923
# Default target
2024
all: clean check build
@@ -93,9 +97,16 @@ install-tools:
9397
# Build Docker image
9498
docker-build:
9599
@echo "Building Docker image..."
96-
docker build -t $(DOCKER_IMAGE):$(DOCKER_TAG) -f Dockerfile .
100+
docker build --build-arg VERSION=$(VERSION) -t $(DOCKER_IMAGE):$(DOCKER_TAG) -f Dockerfile .
97101
@echo "Docker image built: $(DOCKER_IMAGE):$(DOCKER_TAG)"
98102

103+
# Build ko image locally: loads into local Docker daemon with a :local tag, matching docker-build.
104+
# KO_DOCKER_REPO is the parent path; ko appends the binary name to produce the full image name.
105+
# VERSION is exported so the .ko.yaml {{.Env.VERSION}} template resolves correctly.
106+
ko-build:
107+
@echo "Building ko image..."
108+
KO_DOCKER_REPO=$(DOCKER_IMAGE) VERSION=$(VERSION) ko build -L --bare --tags local ./cmd/lfx-mcp-server
109+
99110
# Show help
100111
help:
101112
@echo "Available targets:"
@@ -112,4 +123,5 @@ help:
112123
@echo " deps - Download and tidy dependencies"
113124
@echo " install-tools - Install development tools"
114125
@echo " docker-build - Build Docker image"
126+
@echo " ko-build - Build ko image locally with :local tag"
115127
@echo " help - Show this help message"

cmd/lfx-mcp-server/main.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ package main
77
import (
88
"context"
99
"errors"
10+
"expvar"
1011
"flag"
1112
"fmt"
1213
"log/slog"
14+
"net"
1315
"net/http"
1416
"net/url"
1517
"os"
@@ -57,6 +59,15 @@ type MCPAPIConfig struct {
5759
Scopes []string `koanf:"scopes"`
5860
}
5961

62+
// Build-time variables set via ldflags.
63+
var (
64+
// Version is the application version, set at build time via -ldflags.
65+
// On a tagged commit this is the tag (e.g. v0.4.1); between tags it includes
66+
// the offset and short hash (e.g. v0.4.1-3-gabcdef0); a -dirty suffix is
67+
// appended when there are uncommitted changes.
68+
Version = "dev"
69+
)
70+
6071
const errKey = "error"
6172

6273
// defaultTools is the list of tools enabled by default.
@@ -104,6 +115,10 @@ func main() {
104115

105116
// Define flags.
106117
f := flag.NewFlagSet("lfx-mcp-server", flag.ExitOnError)
118+
f.Usage = func() {
119+
fmt.Fprintf(f.Output(), "lfx-mcp-server %s\n\nUsage:\n", Version)
120+
f.PrintDefaults()
121+
}
107122
f.String("mode", "stdio", "Transport mode: stdio or http")
108123
f.String("http.host", "127.0.0.1", "Host to bind to for HTTP transport")
109124
f.Int("http.port", 8080, "Port to listen on for HTTP transport")
@@ -300,7 +315,7 @@ func mcpLoggingMiddleware(serverLogger *slog.Logger) mcp.Middleware {
300315
func newServer(cfg Config) *mcp.Server {
301316
server := mcp.NewServer(&mcp.Implementation{
302317
Name: "lfx-mcp-server",
303-
Version: "0.1.0",
318+
Version: Version,
304319
}, &mcp.ServerOptions{
305320
Logger: logger,
306321
})
@@ -464,6 +479,10 @@ func runHTTPServer(cfg Config) {
464479
_, _ = w.Write([]byte("ok"))
465480
})
466481

482+
// Register expvar debug endpoint; restricted to loopback-only so it is
483+
// accessible via kubectl port-forward but blocked from ingress traffic.
484+
mux.Handle("/debug/vars", localhostOnly(expvar.Handler()))
485+
467486
// Apply OAuth bearer token middleware if auth servers are configured.
468487
var mcpHandler http.Handler = handler
469488
if len(cfg.MCPAPI.AuthServers) > 0 {
@@ -619,6 +638,26 @@ func runHTTPServer(cfg Config) {
619638
logger.Info("HTTP server stopped")
620639
}
621640

641+
// localhostOnly wraps h and returns 403 Forbidden for any request whose remote
642+
// address is not the IPv4 or IPv6 loopback address. This allows the handler to
643+
// be reached via kubectl port-forward (which arrives as 127.0.0.1) while
644+
// blocking traffic that originates from the ingress or other cluster sources.
645+
func localhostOnly(h http.Handler) http.Handler {
646+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
647+
host, _, err := net.SplitHostPort(r.RemoteAddr)
648+
if err != nil {
649+
http.Error(w, "forbidden", http.StatusForbidden)
650+
return
651+
}
652+
ip := net.ParseIP(host)
653+
if ip == nil || !ip.IsLoopback() {
654+
http.Error(w, "forbidden", http.StatusForbidden)
655+
return
656+
}
657+
h.ServeHTTP(w, r)
658+
})
659+
}
660+
622661
// httpDebugLogging returns middleware that logs all incoming HTTP requests and their
623662
// completion at DEBUG level, including paths not handled by any route (404s).
624663
func httpDebugLogging(logger *slog.Logger) func(http.Handler) http.Handler {

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ require (
1414
github.com/linuxfoundation/lfx-v2-member-service v0.3.0
1515
github.com/linuxfoundation/lfx-v2-project-service v0.5.7
1616
github.com/linuxfoundation/lfx-v2-query-service v0.4.12
17-
github.com/modelcontextprotocol/go-sdk v1.4.0
17+
github.com/modelcontextprotocol/go-sdk v1.4.1
1818
goa.design/goa/v3 v3.25.3
1919
)
2020

@@ -35,7 +35,7 @@ require (
3535
github.com/mitchellh/copystructure v1.2.0 // indirect
3636
github.com/mitchellh/reflectwalk v1.0.2 // indirect
3737
github.com/segmentio/asm v1.2.1 // indirect
38-
github.com/segmentio/encoding v0.5.3 // indirect
38+
github.com/segmentio/encoding v0.5.4 // indirect
3939
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
4040
golang.org/x/crypto v0.49.0 // indirect
4141
golang.org/x/oauth2 v0.36.0 // indirect

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,15 @@ github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa1
5353
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
5454
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
5555
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
56-
github.com/modelcontextprotocol/go-sdk v1.4.0 h1:u0kr8lbJc1oBcawK7Df+/ajNMpIDFE41OEPxdeTLOn8=
57-
github.com/modelcontextprotocol/go-sdk v1.4.0/go.mod h1:Nxc2n+n/GdCebUaqCOhTetptS17SXXNu9IfNTaLDi1E=
56+
github.com/modelcontextprotocol/go-sdk v1.4.1 h1:M4x9GyIPj+HoIlHNGpK2hq5o3BFhC+78PkEaldQRphc=
57+
github.com/modelcontextprotocol/go-sdk v1.4.1/go.mod h1:Bo/mS87hPQqHSRkMv4dQq1XCu6zv4INdXnFZabkNU6s=
5858
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
5959
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
6060
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
6161
github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0=
6262
github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
63-
github.com/segmentio/encoding v0.5.3 h1:OjMgICtcSFuNvQCdwqMCv9Tg7lEOXGwm1J5RPQccx6w=
64-
github.com/segmentio/encoding v0.5.3/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0=
63+
github.com/segmentio/encoding v0.5.4 h1:OW1VRern8Nw6ITAtwSZ7Idrl3MXCFwXHPgqESYfvNt0=
64+
github.com/segmentio/encoding v0.5.4/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0=
6565
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
6666
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
6767
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=

internal/tools/member_write.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright The Linux Foundation and contributors.
22
// SPDX-License-Identifier: MIT
33

4+
// Package tools provides MCP tool implementations for the LFX MCP server.
45
package tools
56

67
import (
@@ -167,15 +168,15 @@ func handleCreateMembershipKeyContact(ctx context.Context, req *mcp.CallToolRequ
167168

168169
version := "1"
169170
payload := &memberservice.CreateMembershipKeyContactPayload{
170-
Version: &version,
171-
ProjectUID: &args.ProjectUID,
172-
ID: &args.ID,
173-
Email: args.Email,
174-
FirstName: args.FirstName,
175-
LastName: args.LastName,
176-
Title: args.Title,
177-
Role: args.Role,
178-
Status: args.Status,
171+
Version: &version,
172+
ProjectUID: &args.ProjectUID,
173+
ID: &args.ID,
174+
Email: args.Email,
175+
FirstName: args.FirstName,
176+
LastName: args.LastName,
177+
Title: args.Title,
178+
Role: args.Role,
179+
Status: args.Status,
179180
BoardMember: args.BoardMember,
180181
PrimaryContact: args.PrimaryContact,
181182
}

0 commit comments

Comments
 (0)