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
1 change: 1 addition & 0 deletions agent-manager-service/api/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ func MakeHTTPHandler(params *wiring.AppParams, extraAPIRoutes func(*http.ServeMu
RegisterLLMProxyAPIKeyRoutes(apiMux, params.LLMProxyAPIKeyController)
RegisterAgentAPIKeyRoutes(apiMux, params.AgentAPIKeyController)
RegisterLLMProxyDeploymentRoutes(apiMux, params.LLMProxyDeploymentController)
RegisterMCPProxyRoutes(apiMux, params.MCPProxyController)
RegisterAgentConfigRoutes(apiMux, params.AgentConfigurationController)
RegisterMonitorPublisherRoutes(apiMux, params.MonitorScoresPublisherController)
RegisterGitSecretRoutes(apiMux, params.GitSecretController)
Expand Down
3 changes: 3 additions & 0 deletions agent-manager-service/api/gateway_internal_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,7 @@ func RegisterGatewayInternalRoutes(mux *http.ServeMux, ctrl controllers.GatewayI

// LLM Proxy endpoints
mux.HandleFunc("GET /llm-proxies/{proxyId}", ctrl.GetLLMProxy)

// MCP Proxy endpoints
mux.HandleFunc("GET /mcp-proxies/{proxyId}", ctrl.GetMCPProxy)
}
35 changes: 35 additions & 0 deletions agent-manager-service/api/mcp_proxy_routes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com).
//
// WSO2 LLC. licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except
// in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package api

import (
"net/http"

"github.com/wso2/agent-manager/agent-manager-service/controllers"
"github.com/wso2/agent-manager/agent-manager-service/middleware"
)

// RegisterMCPProxyRoutes registers MCP proxy routes.
func RegisterMCPProxyRoutes(mux *http.ServeMux, ctrl controllers.MCPProxyController) {
middleware.HandleFuncWithValidation(mux, "POST /orgs/{orgName}/mcp-proxies/fetch-server-info", ctrl.FetchServerInfo)
middleware.HandleFuncWithValidation(mux, "GET /orgs/{orgName}/mcp-proxies/policies", ctrl.ListAvailableMCPPolicies)
middleware.HandleFuncWithValidation(mux, "POST /orgs/{orgName}/mcp-proxies", ctrl.CreateMCPProxy)
middleware.HandleFuncWithValidation(mux, "GET /orgs/{orgName}/mcp-proxies", ctrl.ListMCPProxies)
middleware.HandleFuncWithValidation(mux, "GET /orgs/{orgName}/mcp-proxies/{proxyId}", ctrl.GetMCPProxy)
middleware.HandleFuncWithValidation(mux, "PUT /orgs/{orgName}/mcp-proxies/{proxyId}", ctrl.UpdateMCPProxy)
middleware.HandleFuncWithValidation(mux, "DELETE /orgs/{orgName}/mcp-proxies/{proxyId}", ctrl.DeleteMCPProxy)
}
105 changes: 101 additions & 4 deletions agent-manager-service/controllers/gateway_internal_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
package controllers

import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"time"

Expand All @@ -34,6 +34,7 @@ import (
type GatewayInternalController interface {
GetLLMProvider(w http.ResponseWriter, r *http.Request)
GetLLMProxy(w http.ResponseWriter, r *http.Request)
GetMCPProxy(w http.ResponseWriter, r *http.Request)
GetLLMProviderAPIKeys(w http.ResponseWriter, r *http.Request)
GetLLMProxyAPIKeys(w http.ResponseWriter, r *http.Request)
GetAPIKeys(w http.ResponseWriter, r *http.Request)
Expand Down Expand Up @@ -194,6 +195,66 @@ func (c *gatewayInternalController) GetLLMProxy(w http.ResponseWriter, r *http.R
}
}

// GetMCPProxy handles GET /api/internal/v1/mcp-proxies/:proxyId
func (c *gatewayInternalController) GetMCPProxy(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
log := logger.GetLogger(ctx)
clientIP := getClientIP(r)

apiKey := r.Header.Get("api-key")
if apiKey == "" {
log.Warn("Unauthorized access attempt - Missing API key", "ip", clientIP)
http.Error(w, "API key is required. Provide 'api-key' header.", http.StatusUnauthorized)
return
}

gateway, err := c.gatewayService.VerifyToken(apiKey)
if err != nil {
log.Warn("Authentication failed", "ip", clientIP, "error", err)
http.Error(w, "Invalid or expired API key", http.StatusUnauthorized)
return
}

orgName := gateway.OrganizationName
gatewayID := gateway.UUID.String()
proxyID := r.PathValue("proxyId")
if proxyID == "" {
http.Error(w, "Proxy ID is required", http.StatusBadRequest)
return
}

proxy, err := c.gatewayInternalService.GetActiveMCPProxyDeploymentByGateway(ctx, proxyID, orgName, gatewayID)
if err != nil {
if errors.Is(err, utils.ErrDeploymentNotActive) {
http.Error(w, "No active deployment found for this MCP proxy on this gateway", http.StatusNotFound)
return
}
if errors.Is(err, utils.ErrMCPProxyNotFound) {
http.Error(w, "MCP proxy not found", http.StatusNotFound)
return
}
log.Error("Failed to get MCP proxy", "error", err)
http.Error(w, "Failed to get MCP proxy", http.StatusInternalServerError)
return
}

zipData, err := utils.CreateMCPProxyYamlZip(proxy)
if err != nil {
log.Error("Failed to create ZIP file for MCP proxy", "proxyID", proxyID, "error", err)
http.Error(w, "Failed to create MCP proxy package", http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "application/zip")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"mcp-proxy-%s.zip\"", proxyID))
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(zipData)))

w.WriteHeader(http.StatusOK)
if _, err := w.Write(zipData); err != nil {
log.Error("Failed to write ZIP response", "proxyID", proxyID, "error", err)
}
}

// GetLLMProviderAPIKeys handles GET /api/internal/v1/llm-providers/api-keys
func (c *gatewayInternalController) GetLLMProviderAPIKeys(w http.ResponseWriter, r *http.Request) {
c.getAPIKeysByKind(w, r, models.KindLLMProvider)
Expand Down Expand Up @@ -278,10 +339,46 @@ func (c *gatewayInternalController) GetSubscriptionPlans(w http.ResponseWriter,
// PushGatewayManifest handles POST /api/internal/v1/gateways/{gatewayId}/manifest
// Receives the gateway's installed policy manifest.
func (c *gatewayInternalController) PushGatewayManifest(w http.ResponseWriter, r *http.Request) {
// Drain and discard the request body
_, _ = io.Copy(io.Discard, r.Body)
log := logger.GetLogger(r.Context())
gatewayID := r.PathValue("gatewayId")
log.Info("Received gateway manifest push", "gatewayId", gatewayID)
if gatewayID == "" {
http.Error(w, "Gateway ID is required", http.StatusBadRequest)
return
}

apiKey := r.Header.Get("api-key")
if apiKey == "" {
log.Warn("Unauthorized gateway manifest push - Missing API key", "gatewayId", gatewayID)
http.Error(w, "API key is required. Provide 'api-key' header.", http.StatusUnauthorized)
return
}
gateway, err := c.gatewayService.VerifyToken(apiKey)
if err != nil {
log.Warn("Gateway manifest authentication failed", "gatewayId", gatewayID, "error", err)
http.Error(w, "Invalid or expired API key", http.StatusUnauthorized)
return
}
if gateway.UUID.String() != gatewayID {
log.Warn("Gateway manifest token does not match gateway", "gatewayId", gatewayID, "tokenGatewayId", gateway.UUID)
http.Error(w, "Gateway token does not match gateway ID", http.StatusForbidden)
return
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

var manifest map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&manifest); err != nil {
log.Error("Failed to decode gateway manifest", "gatewayId", gatewayID, "error", err)
http.Error(w, "Invalid gateway manifest", http.StatusBadRequest)
return
}
if err := c.gatewayService.SaveGatewayPolicyManifest(gatewayID, manifest); err != nil {
if errors.Is(err, utils.ErrGatewayNotFound) {
http.Error(w, "Gateway not found", http.StatusNotFound)
return
}
log.Error("Failed to store gateway manifest", "gatewayId", gatewayID, "error", err)
http.Error(w, "Failed to store gateway manifest", http.StatusInternalServerError)
return
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

w.WriteHeader(http.StatusNoContent)
}
Loading
Loading