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
21 changes: 10 additions & 11 deletions cmd/mcp-front/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,15 @@ import (
"encoding/json"
"flag"
"fmt"
"log"
"os"

"github.com/dgellow/mcp-front/internal"
"github.com/dgellow/mcp-front/internal/config"
"github.com/dgellow/mcp-front/internal/log"
"github.com/dgellow/mcp-front/internal/server"
)

var BuildVersion = "dev"

func init() {
log.SetFlags(0)
log.SetOutput(os.Stderr)
log.SetPrefix("")
}

func generateDefaultConfig(path string) error {
defaultConfig := map[string]any{
"version": "v0.0.1-DEV_EDITION_EXPECT_CHANGES",
Expand Down Expand Up @@ -128,7 +121,7 @@ func main() {
}
if *configInit != "" {
if err := generateDefaultConfig(*configInit); err != nil {
internal.LogError("Failed to generate config: %v", err)
log.LogError("Failed to generate config: %v", err)
os.Exit(1)
}
fmt.Printf("Generated default config at: %s\n", *configInit)
Expand All @@ -154,12 +147,18 @@ func main() {

cfg, err := config.Load(*conf)
if err != nil {
internal.LogError("Failed to load config: %v", err)
log.LogError("Failed to load config: %v", err)
os.Exit(1)
}

log.LogInfoWithFields("main", "Starting mcp-front", map[string]interface{}{
"version": BuildVersion,
"config": *conf,
})

err = server.Run(cfg)
if err != nil {
internal.LogError("Failed to start server: %v", err)
log.LogError("Failed to start server: %v", err)
os.Exit(1)
}
}
30 changes: 20 additions & 10 deletions config-token.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,7 @@
"proxy": {
"baseURL": "http://localhost:8080",
"addr": ":8080",
"name": "mcp-front-dev",
"auth": {
"kind": "bearerToken",
"tokens": {
"postgres": ["dev-token-postgres-1", "dev-token-postgres-2"],
"notion": ["dev-token-notion-1"],
"git": ["dev-token-git-1", "dev-token-git-2", "dev-token-git-3"]
}
}
"name": "mcp-front-dev"
},
"mcpServers": {
"postgres": {
Expand All @@ -21,6 +13,12 @@
"run", "--rm", "-i", "--network", "host",
"mcp/postgres",
"postgresql://testuser:testpass@localhost:5432/testdb"
],
"serviceAuths": [
{
"type": "bearer",
"tokens": ["dev-token-postgres-1", "dev-token-postgres-2"]
}
]
},
"notion": {
Expand All @@ -29,7 +27,13 @@
"args": ["run", "--rm", "-i", "-e", "NOTION_TOKEN", "mcp/notion"],
"env": {
"NOTION_TOKEN": "test-notion-token"
}
},
"serviceAuths": [
{
"type": "bearer",
"tokens": ["dev-token-notion-1"]
}
]
},
"git": {
"transportType": "stdio",
Expand All @@ -38,6 +42,12 @@
"run", "--rm", "-i",
"-v", "/tmp/test-repos:/repos:ro",
"mcp/git"
],
"serviceAuths": [
{
"type": "bearer",
"tokens": ["dev-token-git-1", "dev-token-git-2", "dev-token-git-3"]
}
]
}
}
Expand Down
94 changes: 94 additions & 0 deletions integration/basic_auth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package integration

import (
"encoding/base64"
"net/http"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestBasicAuth(t *testing.T) {
// Start mcp-front with basic auth config
startMCPFront(t, "config/config.basic-auth-test.json",
"ADMIN_PASSWORD=adminpass123",
"USER_PASSWORD=userpass456",
)

// Wait for startup
waitForMCPFront(t)

t.Run("valid credentials", func(t *testing.T) {
req, err := http.NewRequest("GET", "http://localhost:8080/postgres/sse", nil)
require.NoError(t, err)
req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("admin:adminpass123")))
req.Header.Set("Accept", "text/event-stream")

client := &http.Client{}
resp, err := client.Do(req)
require.NoError(t, err)
defer resp.Body.Close()

// Should get 200 OK with SSE stream when auth passes
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "text/event-stream", resp.Header.Get("Content-Type"))
})

t.Run("invalid password", func(t *testing.T) {
req, err := http.NewRequest("GET", "http://localhost:8080/postgres/sse", nil)
require.NoError(t, err)
req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("admin:wrongpass")))

client := &http.Client{}
resp, err := client.Do(req)
require.NoError(t, err)
defer resp.Body.Close()

assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})

t.Run("unknown user", func(t *testing.T) {
req, err := http.NewRequest("GET", "http://localhost:8080/postgres/sse", nil)
require.NoError(t, err)
req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("unknown:adminpass123")))

client := &http.Client{}
resp, err := client.Do(req)
require.NoError(t, err)
defer resp.Body.Close()

assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})

t.Run("access MCP endpoint with basic auth", func(t *testing.T) {
// Test accessing a protected MCP endpoint
req, err := http.NewRequest("GET", "http://localhost:8080/postgres/sse", nil)
require.NoError(t, err)
req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("user:userpass456")))
req.Header.Set("Accept", "text/event-stream")

client := &http.Client{}
resp, err := client.Do(req)
require.NoError(t, err)
defer resp.Body.Close()

// Should get 200 OK with SSE stream when auth passes
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "text/event-stream", resp.Header.Get("Content-Type"))
})

t.Run("bearer token with basic auth configured", func(t *testing.T) {
// Server expects basic auth, bearer tokens should fail
req, err := http.NewRequest("GET", "http://localhost:8080/postgres/sse", nil)
require.NoError(t, err)
req.Header.Set("Authorization", "Bearer sometoken")

client := &http.Client{}
resp, err := client.Do(req)
require.NoError(t, err)
defer resp.Body.Close()

assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
}
34 changes: 34 additions & 0 deletions integration/config/config.basic-auth-test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"version": "v0.0.1-DEV_EDITION_EXPECT_CHANGES",
"proxy": {
"baseURL": "http://localhost:8080",
"addr": ":8080",
"name": "mcp-front-basic-auth-test"
},
"mcpServers": {
"postgres": {
"transportType": "stdio",
"command": "docker",
"args": [
"run",
"-i",
"--network",
"host",
"mcp/postgres",
"postgresql://testuser:testpass@localhost:15432/testdb"
],
"serviceAuths": [
{
"type": "basic",
"username": "admin",
"password": {"$env": "ADMIN_PASSWORD"}
},
{
"type": "basic",
"username": "user",
"password": {"$env": "USER_PASSWORD"}
}
]
}
}
}
16 changes: 8 additions & 8 deletions integration/config/config.demo-token.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,7 @@
"proxy": {
"baseURL": "http://localhost:8080",
"addr": ":8080",
"name": "mcp-front-demo",
"auth": {
"kind": "bearerToken",
"tokens": {
"postgres": ["test-token", "demo-token"]
}
}
"name": "mcp-front-demo"
},
"mcpServers": {
"postgres": {
Expand All @@ -22,7 +16,13 @@
],
"options": {
"logEnabled": true
}
},
"serviceAuths": [
{
"type": "bearer",
"tokens": ["test-token", "demo-token"]
}
]
}
}
}
18 changes: 8 additions & 10 deletions integration/config/config.inline-test.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,7 @@
"proxy": {
"baseURL": "http://localhost:8080",
"addr": ":8080",
"name": "mcp-front-inline-test",
"auth": {
"kind": "bearerToken",
"tokens": {
"test-inline": [
"inline-test-token"
]
}
}
"name": "mcp-front-inline-test"
},
"mcpServers": {
"test-inline": {
Expand Down Expand Up @@ -95,7 +87,13 @@
"timeout": "100ms"
}
]
}
},
"serviceAuths": [
{
"type": "bearer",
"tokens": ["inline-test-token"]
}
]
}
}
}
18 changes: 8 additions & 10 deletions integration/config/config.sse-test.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,18 @@
"proxy": {
"baseURL": "http://localhost:8080",
"addr": ":8080",
"name": "mcp-front-sse-test",
"auth": {
"kind": "bearerToken",
"tokens": {
"test-sse": [
"sse-test-token"
]
}
}
"name": "mcp-front-sse-test"
},
"mcpServers": {
"test-sse": {
"transportType": "sse",
"url": "http://localhost:3001/sse"
"url": "http://localhost:3001/sse",
"serviceAuths": [
{
"type": "bearer",
"tokens": ["sse-test-token"]
}
]
}
}
}
18 changes: 8 additions & 10 deletions integration/config/config.streamable-test.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,18 @@
"proxy": {
"baseURL": "http://localhost:8080",
"addr": ":8080",
"name": "mcp-front-streamable-test",
"auth": {
"kind": "bearerToken",
"tokens": {
"test-streamable": [
"streamable-test-token"
]
}
}
"name": "mcp-front-streamable-test"
},
"mcpServers": {
"test-streamable": {
"transportType": "streamable-http",
"url": "http://localhost:3002"
"url": "http://localhost:3002",
"serviceAuths": [
{
"type": "bearer",
"tokens": ["streamable-test-token"]
}
]
}
}
}
Loading
Loading