Skip to content
Merged
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
2 changes: 1 addition & 1 deletion MCP.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ nais alpha mcp serve [flags]
| Flag | Default | Description |
|------|---------|-------------|
| `--transport`, `-t` | `stdio` | Transport: `stdio`, `http`, or `sse` |
| `--listen`, `-l` | `:8080` | Listen address (for http/sse) |
| `--listen`, `-l` | `127.0.0.1:8080` | Listen address (for http/sse) |
| `--rate-limit`, `-r` | `10` | Max requests per minute (0 = unlimited) |
| `--log-file` | - | Write logs to file instead of stderr |

Expand Down
2 changes: 1 addition & 1 deletion internal/mcp/command/flag/flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func (t *Transport) AutoComplete(context.Context, *naistrix.Arguments, string, a
type Serve struct {
*MCP
Transport Transport `name:"transport" usage:"Transport type (stdio, http, sse)."`
ListenAddr string `name:"listen" short:"l" usage:"Address to listen on (for http/sse transports)."`
ListenAddr string `name:"listen" short:"l" usage:"Address to listen on (for http/sse transports). The server has no authentication; only bind to non-loopback addresses on trusted networks."`
RateLimit int `name:"rate-limit" short:"r" usage:"Maximum requests per minute (0 = unlimited)."`
LogFile string `name:"log-file" usage:"Write logs to file instead of stderr."`
}
23 changes: 20 additions & 3 deletions internal/mcp/command/mcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"io"
"log/slog"
"net"
"os"

alpha "github.com/nais/cli/internal/alpha/command/flag"
Expand All @@ -29,7 +30,7 @@ func serveCommand(parentFlags *flag.MCP) *naistrix.Command {
flags := &flag.Serve{
MCP: parentFlags,
Transport: "stdio",
ListenAddr: ":8080",
ListenAddr: "127.0.0.1:8080",
RateLimit: 25,
}

Expand All @@ -43,10 +44,10 @@ Examples:
nais alpha mcp serve

# Start with HTTP transport on a specific port
nais alpha mcp serve --transport http --listen :8080
nais alpha mcp serve --transport http --listen 127.0.0.1:8080

# Start with SSE transport
nais alpha mcp serve --transport sse --listen :8080
nais alpha mcp serve --transport sse --listen 127.0.0.1:8080

# Restrict to specific teams
nais alpha mcp serve --team my-team --team other-team
Expand Down Expand Up @@ -106,8 +107,24 @@ func runServe(ctx context.Context, flags *flag.Serve, out *naistrix.OutputWriter
out.Println("Starting MCP server with transport:", string(flags.Transport))
if flags.Transport == "http" || flags.Transport == "sse" {
out.Println("Listening on:", flags.ListenAddr)
if !isLoopbackAddr(flags.ListenAddr) {
out.Println("WARNING: the MCP server has no authentication and uses your Nais credentials. Anyone who can reach this address can query the Nais API as you.")
}
}
}

return server.Serve(ctx)
}

// isLoopbackAddr reports whether addr (host:port) only binds to a loopback interface.
func isLoopbackAddr(addr string) bool {
host, _, err := net.SplitHostPort(addr)
if err != nil {
return false
}
if host == "localhost" {
return true
}
ip := net.ParseIP(host)
return ip != nil && ip.IsLoopback()
}
20 changes: 20 additions & 0 deletions internal/mcp/command/mcp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package command

import "testing"

func TestIsLoopbackAddr(t *testing.T) {
for addr, want := range map[string]bool{
"127.0.0.1:8080": true,
"localhost:8080": true,
"[::1]:8080": true,
":8080": false,
"0.0.0.0:8080": false,
"10.0.0.1:8080": false,
"example.com:80": false,
"invalid": false,
} {
if got := isLoopbackAddr(addr); got != want {
t.Errorf("isLoopbackAddr(%q) = %v, want %v", addr, got, want)
}
}
}
2 changes: 1 addition & 1 deletion internal/mcp/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ type Option func(*Options)
func DefaultOptions() *Options {
return &Options{
Transport: TransportStdio,
ListenAddr: ":8080",
ListenAddr: "127.0.0.1:8080",
RateLimit: 10,
Logger: slog.Default(),
}
Expand Down