Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .semaphore/semaphore.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2634,7 +2634,7 @@ blocks:
- name: "MCP Server: Provision Prod Image"
dependencies: []
run:
when: "change_in('/mcp_server', {pipeline_file: 'ignore', default_branch: 'main'})"
when: "change_in('/mcp_server', {pipeline_file: 'ignore', default_branch: 'main'}) or change_in('/docs/docs', {default_branch: 'main'}) or change_in('/docs/versioned_docs', {default_branch: 'main'})"
task:
env_vars:
- name: DOCKER_BUILDKIT
Expand All @@ -2653,7 +2653,7 @@ blocks:
- name: "MCP Server: Deployment Preconditions"
dependencies: ["MCP Server: Provision Prod Image"]
run:
when: "change_in('/mcp_server', {pipeline_file: 'ignore', default_branch: 'main'})"
when: "change_in('/mcp_server', {pipeline_file: 'ignore', default_branch: 'main'}) or change_in('/docs/docs', {default_branch: 'main'}) or change_in('/docs/versioned_docs', {default_branch: 'main'})"
task:
env_vars:
- name: DOCKER_BUILDKIT
Expand All @@ -2669,7 +2669,7 @@ blocks:
jobs:
- name: "Check code"
commands:
- make check.go.code
- make check.go.code CHECK_CODE_OPTS='--config-options "-exclude-generated"'
- name: "Check dependencies"
commands:
- make check.go.deps
Expand All @@ -2680,7 +2680,7 @@ blocks:
- name: "MCP Server: QA"
dependencies: ["MCP Server: Provision Prod Image"]
run:
when: "change_in('/mcp_server', {pipeline_file: 'ignore', default_branch: 'main'})"
when: "change_in('/mcp_server', {pipeline_file: 'ignore', default_branch: 'main'}) or change_in('/docs/docs', {default_branch: 'main'}) or change_in('/docs/versioned_docs', {default_branch: 'main'})"
task:
env_vars:
- name: DOCKER_BUILDKIT
Expand Down
6 changes: 6 additions & 0 deletions mcp_server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Binaries (in root directory only)
/mcp_server
/indexer

# Generated search index
/index/
22 changes: 16 additions & 6 deletions mcp_server/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ ENV CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64

COPY go.mod go.sum ./
COPY mcp_server/go.mod mcp_server/go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
go mod download

COPY cmd ./cmd
COPY pkg ./pkg
COPY test ./test
COPY mcp_server/cmd ./cmd
COPY mcp_server/pkg ./pkg
COPY mcp_server/test ./test

FROM base AS dev
RUN --mount=type=cache,target=/root/.cache/go-build \
Expand All @@ -27,14 +27,24 @@ CMD ["sh", "-c", "while sleep 1000; do :; done"]

FROM base AS builder
RUN --mount=type=cache,target=/root/.cache/go-build \
go build -o /tmp/mcp_server ./cmd/mcp_server
go build -o /tmp/mcp_server ./cmd/mcp_server && \
go build -o /tmp/indexer ./cmd/indexer

# Build the documentation search index
# Docs are expected at /docs in this stage (copied from build context or external source)
FROM builder AS indexer
COPY docs /docs
RUN /tmp/indexer -docs=/docs -output=/tmp/docssearch_index

FROM alpine:${ALPINE_VERSION} AS runner
RUN adduser -D -H -s /sbin/nologin appuser
USER appuser
WORKDIR /app

COPY --from=builder /tmp/mcp_server /usr/local/bin/mcp_server
COPY --from=indexer --chown=appuser:appuser /tmp/docssearch_index /app/docssearch/index
COPY --from=indexer --chown=appuser:appuser /docs /app/docssearch/docs

USER appuser

EXPOSE 3001

Expand Down
10 changes: 9 additions & 1 deletion mcp_server/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ include ../Makefile
APP_NAME=mcp_server
APP_ENV=prod
CHECK_CODE_OPTS?=--config-options "-exclude-generated"
DOCKER_BUILD_PATH=..

INTERNAL_API_BRANCH?=master
TMP_REPO_DIR ?= /tmp/internal_api
INTERNAL_API_MODULES?=include/internal_api/status,include/internal_api/response_status,plumber_w_f.workflow,plumber.pipeline,server_farm.job,loghub,loghub2,user,repository_integrator,rbac,organization,projecthub,feature,artifacthub
PROTOC_IMAGE?=golang:1.24-alpine

.PHONY: tidy test test.setup lint pb.gen dev.run
.PHONY: tidy test test.setup lint pb.gen dev.run index

tidy:
go mod tidy
Expand Down Expand Up @@ -46,3 +47,10 @@ dev.run:
echo "air not found, falling back to go run"; \
MCP_USE_STUBS=true go run ./cmd/mcp_server -http :3001; \
fi

# Build the documentation search index
# Usage: make index DOCS_ROOT=../docs INDEX_PATH=./index
DOCS_ROOT ?= ../docs
INDEX_PATH ?= ./index
index:
go run ./cmd/indexer -docs=$(DOCS_ROOT) -output=$(INDEX_PATH)
58 changes: 58 additions & 0 deletions mcp_server/cmd/indexer/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package main

import (
"flag"
"fmt"
"log"
"os"

"github.com/semaphoreio/semaphore/mcp_server/pkg/docssearch/indexer"
"github.com/semaphoreio/semaphore/mcp_server/pkg/docssearch/loader"
)

func main() {
docsRoot := flag.String("docs", "../docs", "Path to docs directory")
outputPath := flag.String("output", "./index", "Path for output index")
flag.Parse()

// Remove existing index if present
if _, err := os.Stat(*outputPath); err == nil {
log.Printf("Removing existing index at %s", *outputPath)
if err := os.RemoveAll(*outputPath); err != nil {
log.Fatalf("Failed to remove existing index: %v", err)
}
}

log.Printf("Loading docs from %s", *docsRoot)
l := loader.New(*docsRoot)
docs, err := l.LoadAll()
if err != nil {
log.Fatalf("Failed to load docs: %v", err)
}
log.Printf("Loaded %d documents", len(docs))

// Print some stats
versionCounts := make(map[string]int)
docTypeCounts := make(map[string]int)
for _, d := range docs {
versionCounts[d.Version]++
docTypeCounts[d.DocType]++
}
fmt.Println("\nDocuments by version:")
for v, c := range versionCounts {
fmt.Printf(" %s: %d\n", v, c)
}
fmt.Println("\nDocuments by type:")
for t, c := range docTypeCounts {
fmt.Printf(" %s: %d\n", t, c)
}
fmt.Println()

log.Printf("Building index at %s", *outputPath)
idx := indexer.New(*outputPath)
if err := idx.BuildIndex(docs); err != nil {
log.Fatalf("Failed to build index: %v", err)
}

log.Printf("Index built successfully with %d documents", len(docs))
}
5 changes: 5 additions & 0 deletions mcp_server/cmd/mcp_server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ import (
"github.com/mark3labs/mcp-go/server"
"github.com/sirupsen/logrus"

"github.com/semaphoreio/semaphore/mcp_server/pkg/config"
"github.com/semaphoreio/semaphore/mcp_server/pkg/internalapi"
"github.com/semaphoreio/semaphore/mcp_server/pkg/logging"
"github.com/semaphoreio/semaphore/mcp_server/pkg/prompts"
"github.com/semaphoreio/semaphore/mcp_server/pkg/tools"
"github.com/semaphoreio/semaphore/mcp_server/pkg/tools/docs"
"github.com/semaphoreio/semaphore/mcp_server/pkg/tools/jobs"
"github.com/semaphoreio/semaphore/mcp_server/pkg/tools/organizations"
"github.com/semaphoreio/semaphore/mcp_server/pkg/tools/pipelines"
Expand Down Expand Up @@ -77,6 +79,8 @@ func main() {
bootstrapLog := logging.ForComponent("bootstrap")
if strings.EqualFold(os.Getenv("MCP_USE_STUBS"), "true") {
bootstrapLog.Info("using stubbed internal API clients (MCP_USE_STUBS=true)")
config.SetDevMode(true)
bootstrapLog.Info("dev mode enabled - skipping X-Semaphore-User-ID validation")
provider = support.New()
} else {
cfg, err := internalapi.LoadConfig()
Expand Down Expand Up @@ -116,6 +120,7 @@ func main() {

// Register prompts for agent configuration guidance
prompts.Register(srv)
docs.Register(srv)

mux := http.NewServeMux()
streamable := server.NewStreamableHTTPServer(
Expand Down
28 changes: 27 additions & 1 deletion mcp_server/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.25

require (
github.com/allegro/bigcache/v3 v3.1.0
github.com/blevesearch/bleve/v2 v2.5.5
github.com/eko/gocache/lib/v4 v4.2.2
github.com/eko/gocache/store/bigcache/v4 v4.2.3
github.com/golang/protobuf v1.5.4
Expand All @@ -12,7 +13,7 @@ require (
github.com/mark3labs/mcp-go v0.41.1
github.com/renderedtext/go-watchman v0.0.0-20221222100224-451a6f3c8d92
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.9.0
github.com/stretchr/testify v1.10.0
google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f
google.golang.org/grpc v1.75.1
google.golang.org/protobuf v1.36.10
Expand All @@ -22,13 +23,37 @@ require (
require gopkg.in/alexcesaro/statsd.v2 v2.0.0 // indirect

require (
github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.22.0 // indirect
github.com/blevesearch/bleve_index_api v1.2.11 // indirect
github.com/blevesearch/geo v0.2.4 // indirect
github.com/blevesearch/go-faiss v1.0.26 // indirect
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
github.com/blevesearch/gtreap v0.1.1 // indirect
github.com/blevesearch/mmap-go v1.0.4 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.3.13 // indirect
github.com/blevesearch/segment v0.9.1 // indirect
github.com/blevesearch/snowballstem v0.9.0 // indirect
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
github.com/blevesearch/vellum v1.1.0 // indirect
github.com/blevesearch/zapx/v11 v11.4.2 // indirect
github.com/blevesearch/zapx/v12 v12.4.2 // indirect
github.com/blevesearch/zapx/v13 v13.4.2 // indirect
github.com/blevesearch/zapx/v14 v14.4.2 // indirect
github.com/blevesearch/zapx/v15 v15.4.2 // indirect
github.com/blevesearch/zapx/v16 v16.2.7 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/invopop/jsonschema v0.13.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mschoch/smat v0.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.19.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
Expand All @@ -37,6 +62,7 @@ require (
github.com/spf13/cast v1.7.1 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
go.etcd.io/bbolt v1.4.0 // indirect
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/sync v0.15.0 // indirect
Expand Down
Loading