diff --git a/.github/scripts/release.sh b/.github/scripts/release.sh
index 35568a2..2a1f6a9 100644
--- a/.github/scripts/release.sh
+++ b/.github/scripts/release.sh
@@ -19,7 +19,7 @@ GIT_TOKEN=$1
WORK_DIR=$2
VERSION_TYPE=$3 # possible values: major, minor, patch
- Check if GIT_TOKEN is empty
+# Check if GIT_TOKEN is empty
if [ -z "$GIT_TOKEN" ]; then
echo "β Error: GIT_TOKEN is not set."
exit 1
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
new file mode 100644
index 0000000..775003e
--- /dev/null
+++ b/.github/workflows/ci.yaml
@@ -0,0 +1,71 @@
+name: Build and Push container
+run-name: Build and Push container
+on:
+ workflow_dispatch:
+ #schedule:
+ # - cron: "0 10 * * *"
+ push:
+ branches:
+ - 'main'
+ - 'master'
+ tags:
+ - 'v*'
+ pull_request:
+ branches:
+ - 'main'
+ - 'master'
+env:
+ IMAGE: git.kvant.cloud/${{github.repository}}
+jobs:
+ build_concierge_backend:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Set current time
+ uses: https://github.com/gerred/actions/current-time@master
+ id: current_time
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
+ - name: Login to git.kvant.cloud registry
+ uses: docker/login-action@v3
+ with:
+ registry: git.kvant.cloud
+ username: ${{ vars.ORG_PACKAGE_WRITER_USERNAME }}
+ password: ${{ secrets.ORG_PACKAGE_WRITER_TOKEN }}
+
+ - name: Docker meta
+ id: meta
+ uses: docker/metadata-action@v5
+ with:
+ # list of Docker images to use as base name for tags
+ images: |
+ ${{env.IMAGE}}
+ # generate Docker tags based on the following events/attributes
+ tags: |
+ type=schedule
+ type=ref,event=branch
+ type=ref,event=pr
+ type=semver,pattern={{version}}
+
+ - name: Build and push to gitea registry
+ uses: docker/build-push-action@v6
+ with:
+ push: ${{ github.event_name != 'pull_request' }}
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
+ context: .
+ provenance: mode=max
+ sbom: true
+ build-args: |
+ BUILD_DATE=${{ steps.current_time.outputs.time }}
+ cache-from: |
+ type=registry,ref=${{ env.IMAGE }}:buildcache
+ type=registry,ref=${{ env.IMAGE }}:${{ github.ref_name }}
+ type=registry,ref=${{ env.IMAGE }}:main
+ cache-to: type=registry,ref=${{ env.IMAGE }}:buildcache,mode=max,image-manifest=true
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index e55f6b6..0c51bc7 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -32,7 +32,7 @@ jobs:
with:
ref: 'main'
fetch-depth: 0
- token: ${{ secrets.GIT_BOT_PAT }}
+ token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/checkout@v2
- name: Set up Go 1.x
@@ -60,5 +60,5 @@ jobs:
- name: Update artifact version, package, commit, and create release.
env:
- GITHUB_TOKEN: ${{ secrets.GIT_BOT_PAT }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: bash ./.github/scripts/release.sh $GITHUB_TOKEN ${{ github.workspace }} ${{ github.event.inputs.version_type }}
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..dc468b1
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,48 @@
+FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.24@sha256:d9db32125db0c3a680cfb7a1afcaefb89c898a075ec148fdc2f0f646cc2ed509 AS build
+
+ARG TARGETPLATFORM
+ARG BUILDPLATFORM
+ARG TARGETOS
+ARG TARGETARCH
+
+WORKDIR /workspace
+
+RUN apt update -qq && apt install -qq -y git bash curl g++
+
+# Download libraries
+ADD go.* .
+RUN go mod download
+
+# Build
+ADD cmd cmd
+ADD internal internal
+RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o webhook -ldflags '-w -extldflags "-static"' -o openmcpauthproxy ./cmd/proxy
+
+#Test
+RUN CGO_ENABLED=1 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go test -v -race ./...
+
+
+# Build production container
+FROM --platform=${BUILDPLATFORM:-linux/amd64} ubuntu:24.04
+
+RUN apt-get update \
+ && apt-get install --no-install-recommends -y \
+ python3-pip \
+ python-is-python3 \
+ npm \
+ && apt-get autoremove \
+ && apt-get clean \
+ && rm -rf /var/lib/apt/lists/*
+
+RUN pip install uvenv --break-system-packages
+
+WORKDIR /app
+COPY --from=build /workspace/openmcpauthproxy /app/
+
+ADD config.yaml /app
+
+
+ENTRYPOINT ["/app/openmcpauthproxy"]
+
+ARG IMAGE_SOURCE
+LABEL org.opencontainers.image.source=$IMAGE_SOURCE
diff --git a/Makefile b/Makefile
index c9ef883..b0d0926 100644
--- a/Makefile
+++ b/Makefile
@@ -30,19 +30,19 @@ build: clean test build-linux build-linux-arm build-darwin
build-linux:
mkdir -p $(BUILD_DIR)/linux
- GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -x -ldflags "-X main.version=$(BUILD_VERSION) \
+ GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -x -ldflags "-X main.version=$(BUILD_VERSION)" \
-o $(BUILD_DIR)/linux/openmcpauthproxy $(PROJECT_ROOT)/cmd/proxy
cp config.yaml $(BUILD_DIR)/linux
build-linux-arm:
mkdir -p $(BUILD_DIR)/linux-arm
- GOOS=linux GOARCH=arm CGO_ENABLED=0 go build -x -ldflags "-X main.version=$(BUILD_VERSION) \
+ GOOS=linux GOARCH=arm CGO_ENABLED=0 go build -x -ldflags "-X main.version=$(BUILD_VERSION)" \
-o $(BUILD_DIR)/linux-arm/openmcpauthproxy $(PROJECT_ROOT)/cmd/proxy
cp config.yaml $(BUILD_DIR)/linux-arm
build-darwin:
mkdir -p $(BUILD_DIR)/darwin
- GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -x -ldflags "-X main.version=$(BUILD_VERSION) \
+ GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -x -ldflags "-X main.version=$(BUILD_VERSION)" \
-o $(BUILD_DIR)/darwin/openmcpauthproxy $(PROJECT_ROOT)/cmd/proxy
cp config.yaml $(BUILD_DIR)/darwin
diff --git a/README.md b/README.md
index f13d709..6be3ece 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,12 @@
# Open MCP Auth Proxy
-A lightweight authorization proxy for Model Context Protocol (MCP) servers that enforces authorization according to the [MCP authorization specification](https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/authorization/).
+A lightweight authorization proxy for Model Context Protocol (MCP) servers that enforces authorization according to the [MCP authorization specification](https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/authorization/)
+
+[](https://github.com/wso2/open-mcp-auth-proxy/actions/workflows/release.yml)
+[](https://stackoverflow.com/questions/tagged/wso2is)
+[](https://discord.gg/wso2)
+[](https://twitter.com/intent/follow?screen_name=wso2)
+[](https://github.com/wso2/product-is/blob/master/LICENSE)

@@ -19,28 +25,29 @@ Open MCP Auth Proxy sits between MCP clients and your MCP server to:
* Go 1.20 or higher
* A running MCP server
+
+> If you don't have an MCP server, you can use the included example:
+>
+> 1. Navigate to the `resources` directory
+> 2. Set up a Python environment:
+>
+> ```bash
+> python3 -m venv .venv
+> source .venv/bin/activate
+> pip3 install -r requirements.txt
+> ```
+>
+> 3. Start the example server:
+>
+> ```bash
+> python3 echo_server.py
+> ```
+
* An MCP client that supports MCP authorization
-### Installation
-
-```bash
-git clone https://github.com/wso2/open-mcp-auth-proxy
-cd open-mcp-auth-proxy
-go get github.com/golang-jwt/jwt/v4 gopkg.in/yaml.v2
-go build -o openmcpauthproxy ./cmd/proxy
-```
-
### Basic Usage
-1. The repository comes with a default `config.yaml` file that contains the basic configuration:
-
-```yaml
-listen_port: 8080
-base_url: "http://localhost:8000" # Your MCP server URL
-paths:
- sse: "/sse"
- messages: "/messages/"
-```
+1. Download the latest release from [Github releases](https://github.com/wso2/open-mcp-auth-proxy/releases/latest).
2. Start the proxy in demo mode (uses pre-configured authentication with Asgardeo sandbox):
@@ -48,31 +55,30 @@ paths:
./openmcpauthproxy --demo
```
+> The repository comes with a default `config.yaml` file that contains the basic configuration:
+>
+> ```yaml
+> listen_port: 8080
+> base_url: "http://localhost:8000" # Your MCP server URL
+> paths:
+> sse: "/sse"
+> messages: "/messages/"
+> ```
+
3. Connect using an MCP client like [MCP Inspector](https://github.com/shashimalcse/inspector)(This is a temporary fork with fixes for authentication [issues](https://github.com/modelcontextprotocol/typescript-sdk/issues/257) in the original implementation)
-## Identity Provider Integration
+## Connect an Identity Provider
-### Demo Mode
+### Asgardeo
-For quick testing, use the `--demo` flag which includes pre-configured authentication and authorization with an Asgardeo sandbox.
-
-```bash
-./openmcpauthproxy --demo
-```
-
-### Asgardeo Integration
-
-To enable authorization through your own Asgardeo organization:
+To enable authorization through your Asgardeo organization:
1. [Register](https://asgardeo.io/signup) and create an organization in Asgardeo
2. Create an [M2M application](https://wso2.com/asgardeo/docs/guides/applications/register-machine-to-machine-app/)
1. [Authorize this application](https://wso2.com/asgardeo/docs/guides/applications/register-machine-to-machine-app/#authorize-the-api-resources-for-the-app) to invoke "Application Management API" with the `internal_application_mgt_create` scope

- 2. Update the existing `config.yaml` with your Asgardeo details:
-
-#### Configure the Auth Proxy
-
-Create a configuration file config.yaml with the following parameters:
+
+3. Update `config.yaml` with the following parameters.
```yaml
base_url: "http://localhost:8000" # URL of your MCP server
@@ -84,7 +90,7 @@ asgardeo:
client_secret: "" # Client secret of the M2M app
```
-3. Start the proxy with Asgardeo integration:
+4. Start the proxy with Asgardeo integration:
```bash
./openmcpauthproxy --asgardeo
@@ -95,25 +101,6 @@ asgardeo:
- [Auth0](docs/integrations/Auth0.md)
- [Keycloak](docs/integrations/keycloak.md)
-## Testing with an Example MCP Server
-
-If you don't have an MCP server, you can use the included example:
-
-1. Navigate to the `resources` directory
-2. Set up a Python environment:
-
-```bash
-python3 -m venv .venv
-source .venv/bin/activate
-pip3 install -r requirements.txt
-```
-
-3. Start the example server:
-
-```bash
-python3 echo_server.py
-```
-
# Advanced Configuration
### Transport Modes
@@ -228,3 +215,12 @@ asgardeo:
client_id: ""
client_secret: ""
```
+
+### Build from source
+
+```bash
+git clone https://github.com/wso2/open-mcp-auth-proxy
+cd open-mcp-auth-proxy
+go get github.com/golang-jwt/jwt/v4 gopkg.in/yaml.v2
+go build -o openmcpauthproxy ./cmd/proxy
+```
diff --git a/cmd/proxy/main.go b/cmd/proxy/main.go
index 6424f18..c43dd7d 100644
--- a/cmd/proxy/main.go
+++ b/cmd/proxy/main.go
@@ -12,7 +12,7 @@ import (
"github.com/wso2/open-mcp-auth-proxy/internal/authz"
"github.com/wso2/open-mcp-auth-proxy/internal/config"
"github.com/wso2/open-mcp-auth-proxy/internal/constants"
- "github.com/wso2/open-mcp-auth-proxy/internal/logging"
+ logger "github.com/wso2/open-mcp-auth-proxy/internal/logging"
"github.com/wso2/open-mcp-auth-proxy/internal/proxy"
"github.com/wso2/open-mcp-auth-proxy/internal/subprocess"
"github.com/wso2/open-mcp-auth-proxy/internal/util"
@@ -58,7 +58,7 @@ func main() {
logger.Warn("%v", err)
logger.Warn("Subprocess may fail to start due to missing dependencies")
}
-
+
procManager = subprocess.NewManager()
if err := procManager.Start(cfg); err != nil {
logger.Warn("Failed to start subprocess: %v", err)
@@ -95,7 +95,7 @@ func main() {
// 5. Build the main router
mux := proxy.NewRouter(cfg, provider)
- listen_address := fmt.Sprintf(":%d", cfg.ListenPort)
+ listen_address := fmt.Sprintf("0.0.0.0:%d", cfg.ListenPort)
// 6. Start the server
srv := &http.Server{
diff --git a/config.yaml b/config.yaml
index 5621195..ef70fbb 100644
--- a/config.yaml
+++ b/config.yaml
@@ -6,29 +6,23 @@ base_url: "http://localhost:8000" # Base URL for the MCP server
port: 8000 # Port for the MCP server
timeout_seconds: 10
-# Path configuration
-paths:
- sse: "/sse" # SSE endpoint path
- messages: "/messages/" # Messages endpoint path
# Transport mode configuration
-transport_mode: "sse" # Options: "sse" or "stdio"
+transport_mode: "stdio" # Options: "sse" or "stdio"
# stdio-specific configuration (used only when transport_mode is "stdio")
stdio:
enabled: true
- user_command: "npx -y @modelcontextprotocol/server-github"
+ user_command: uvx mcp-server-time --local-timezone=Europe/Zurich
+ #user_command: "npx -y @modelcontextprotocol/server-github"
work_dir: "" # Working directory (optional)
# env: # Environment variables (optional)
# - "NODE_ENV=development"
-# Path mapping (optional)
-path_mapping:
-
-# CORS configuration
+# CORS settings
cors:
allowed_origins:
- - "http://localhost:5173"
+ - "http://localhost:6274" # Origin of your frontend/client app
allowed_methods:
- "GET"
- "POST"
@@ -40,8 +34,32 @@ cors:
- "mcp-protocol-version"
allow_credentials: true
-# Demo configuration for Asgardeo
-demo:
- org_name: "openmcpauthdemo"
- client_id: "N0U9e_NNGr9mP_0fPnPfPI0a6twa"
- client_secret: "qFHfiBp5gNGAO9zV4YPnDofBzzfInatfUbHyPZvM0jka"
+# Keycloak endpoint path mappings
+path_mapping:
+ sse: "/sse" # SSE endpoint path
+ messages: "/messages/" # Messages endpoint path
+ /token: /realms/master/protocol/openid-connect/token
+ /register: /realms/master/clients-registrations/openid-connect
+
+# Keycloak configuration block
+default:
+ base_url: "https://iam.phoenix-systems.ch"
+ jwks_url: "https://iam.phoenix-systems.ch/realms/kvant/protocol/openid-connect/certs"
+ path:
+ /.well-known/oauth-authorization-server:
+ response:
+ issuer: "https://iam.phoenix-systems.ch/realms/kvant"
+ jwks_uri: "https://iam.phoenix-systems.ch/realms/kvant/protocol/openid-connect/certs"
+ authorization_endpoint: "https://iam.phoenix-systems.ch/realms/kvant/protocol/openid-connect/auth"
+ response_types_supported:
+ - "code"
+ grant_types_supported:
+ - "authorization_code"
+ - "refresh_token"
+ code_challenge_methods_supported:
+ - "S256"
+ - "plain"
+ /token:
+ addBodyParams:
+ - name: "audience"
+ value: "mcp_proxy"
\ No newline at end of file
diff --git a/pull_request_template.md b/pull_request_template.md
index 9b32185..c401a06 100644
--- a/pull_request_template.md
+++ b/pull_request_template.md
@@ -1,52 +1,11 @@
## Purpose
-> Describe the problems, issues, or needs driving this feature/fix and include links to related issues in the following format: Resolves issue1, issue2, etc.
+
-## Goals
-> Describe the solutions that this feature/fix will introduce to resolve the problems described above
-
-## Approach
-> Describe how you are implementing the solutions. Include an animated GIF or screenshot if the change affects the UI (email documentation@wso2.com to review all UI text). Include a link to a Markdown file or Google doc if the feature write-up is too long to paste here.
-
-## User stories
-> Summary of user stories addressed by this change>
-
-## Release note
-> Brief description of the new feature or bug fix as it will appear in the release notes
-
-## Documentation
-> Link(s) to product documentation that addresses the changes of this PR. If no doc impact, enter βN/Aβ plus brief explanation of why thereβs no doc impact
-
-## Training
-> Link to the PR for changes to the training content in https://github.com/wso2/WSO2-Training, if applicable
-
-## Certification
-> Type βSentβ when you have provided new/updated certification questions, plus four answers for each question (correct answer highlighted in bold), based on this change. Certification questions/answers should be sent to certification@wso2.com and NOT pasted in this PR. If there is no impact on certification exams, type βN/Aβ and explain why.
-
-## Marketing
-> Link to drafts of marketing content that will describe and promote this feature, including product page changes, technical articles, blog posts, videos, etc., if applicable
-
-## Automation tests
- - Unit tests
- > Code coverage information
- - Integration tests
- > Details about the test cases and coverage
-
-## Security checks
- - Followed secure coding standards in http://wso2.com/technical-reports/wso2-secure-engineering-guidelines? yes/no
- - Ran FindSecurityBugs plugin and verified report? yes/no
- - Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets? yes/no
-
-## Samples
-> Provide high-level details about the samples related to this feature
+## Related Issues
+
## Related PRs
-> List any other related PRs
+
## Migrations (if applicable)
-> Describe migration steps and platforms on which migration has been tested
-
-## Test environment
-> List all JDK versions, operating systems, databases, and browser/versions on which this feature/fix was tested
-
-## Learning
-> Describe the research phase and any blog posts, patterns, libraries, or add-ons you used to solve the problem.
\ No newline at end of file
+