Skip to content

Conversation

@fentz26
Copy link
Collaborator

@fentz26 fentz26 commented Jan 19, 2026

User description

🔗 MCP Router Phase 2: Integration

Refs #89

Summary

Integrates the MCP Tool Router (from Phase 1) into the scheduler and control plane, enabling automatic MCP selection for dispatched tasks and exposing a REST API for routing decisions.

Changes

File Description
internal/scheduler/scheduler.go Add mcpRouter field, SetMCPRouter(), route tasks in pollAndDispatch(), log to PDR
internal/controlplane/server.go Add MCPRouter interface, SetMCPRouter(), POST /mcp/route endpoint
cmd/neona/daemon.go Initialize MCP router on startup, wire to scheduler and server

New API Endpoint

POST /mcp/route
Content-Type: application/json

{"title": "Create a GitHub PR", "description": "for the feature branch"}

Response:

{
  "selected_mcps": [
    {"name": "github", "tool_count": 30},
    {"name": "git", "tool_count": 15},
    {"name": "filesystem", "tool_count": 10}
  ],
  "matched_rules": ["github,pr"],
  "total_tools": 55,
  "tool_budget": 80
}

Testing

  • ✅ All scheduler tests pass
  • ✅ All controlplane tests pass
  • ✅ All mcp tests pass
  • ✅ Build verified with CGO_ENABLED=0

PR Type

Enhancement


Description

  • Integrates MCP router into scheduler for automatic tool selection

  • Adds POST /mcp/route REST API endpoint for routing decisions

  • Logs selected MCPs to PDR for task dispatch auditing

  • Initializes MCP router on daemon startup with configuration


Diagram Walkthrough

flowchart LR
  daemon["Daemon Startup"]
  mcpInit["Initialize MCP Router"]
  scheduler["Scheduler"]
  server["Control Plane Server"]
  route["Route Task"]
  api["POST /mcp/route Endpoint"]
  pdr["PDR Audit Log"]
  
  daemon -- "loads config" --> mcpInit
  mcpInit -- "wires to" --> scheduler
  mcpInit -- "wires to" --> server
  scheduler -- "routes on dispatch" --> route
  route -- "logs to" --> pdr
  server -- "handles requests" --> api
  api -- "uses router" --> route
Loading

File Walkthrough

Relevant files
Enhancement
daemon.go
Initialize and wire MCP router on startup                               

cmd/neona/daemon.go

  • Imports MCP package for router initialization
  • Loads MCP configuration from home directory with fallback to defaults
  • Creates MCP registry and initializes router with configuration
  • Wires MCP router to both scheduler and server instances
+16/-0   
server.go
Add MCP routing REST API endpoint                                               

internal/controlplane/server.go

  • Defines MCPRouter interface for routing tasks to MCPs
  • Adds mcpRouter field to Server struct
  • Implements SetMCPRouter method for dependency injection
  • Adds POST /mcp/route endpoint handler with request/response types
  • Handles routing requests with title and description, returns selected
    MCPs and tool counts
+91/-0   
scheduler.go
Integrate MCP routing into task dispatch                                 

internal/scheduler/scheduler.go

  • Imports MCP package for task routing
  • Adds mcpRouter field to Scheduler struct
  • Implements SetMCPRouter method for dependency injection
  • Routes tasks during pollAndDispatch with error handling
  • Logs selected MCPs and tool counts to PDR audit records
+36/-0   

Note

Integrates MCP tool routing across the runtime and exposes a minimal API for routing decisions.

  • Initialize MCP router on daemon startup (loads config, registers defaults) and wire it to both scheduler and server
  • Server: add MCPRouter interface, SetMCPRouter, and POST /mcp/route handler that returns selected_mcps, matched_rules, total_tools, and tool_budget
  • Scheduler: add mcpRouter field and SetMCPRouter; route tasks during dispatch and record selections to PDR

Written by Cursor Bugbot for commit 48d970a. This will update automatically on new commits. Configure here.

- Add MCP router to scheduler for task dispatch routing
- Log selected MCPs to PDR for each task
- Add POST /mcp/route API endpoint
- Initialize MCP router in daemon startup
- Wire router to scheduler and control plane

Refs: #89
@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Jan 19, 2026

PR Compliance Guide 🔍

(Compliance updated until commit 7c26930)

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Double response encode: The handler encodes the response twice and partially ignores encoding failures, which can
produce malformed HTTP responses and hide the real error condition.

Referred Code
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(resp); err != nil {
    log.Printf("Failed to encode MCP route response: %v", err)
}
	json.NewEncoder(w).Encode(resp)
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status:
Unstructured sensitive logs: New logging uses unstructured log.Printf and includes user-controlled content like
task.Title and routing-derived details, increasing the risk of sensitive data exposure and
making auditing harder.

Referred Code
		log.Printf("MCP routing error for task %s: %v", task.ID, err)
	} else {
		// Log selected MCPs
		mcpNames := make([]string, len(result.SelectedMCPs))
		for i, m := range result.SelectedMCPs {
			mcpNames[i] = m.Name
		}
		sch.pdr.Record("task.mcp_route", map[string]interface{}{
			"task_id":       task.ID,
			"selected_mcps": mcpNames,
			"total_tools":   result.TotalTools,
			"matched_rules": result.MatchedRules,
		}, "success", task.ID, fmt.Sprintf("Routed to %d MCPs with %d tools", len(mcpNames), result.TotalTools))
		log.Printf("Task %s routed to MCPs: %v (%d tools)", task.ID, mcpNames, result.TotalTools)
	}
}

log.Printf("Dispatched task %s (%s) to worker %s", task.ID, task.Title, workerID)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Missing request audit: The new POST /mcp/route endpoint performs a routing decision but does not emit an
audit/PDR entry containing actor identity (e.g., user/service) and outcome, making it
difficult to reconstruct who invoked routing decisions.

Referred Code
// handleMCPRoute handles POST /mcp/route
func (s *Server) handleMCPRoute(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		return
	}

	if s.mcpRouter == nil {
		http.Error(w, "MCP router not configured", http.StatusServiceUnavailable)
		return
	}

	var req mcpRouteRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "invalid json", http.StatusBadRequest)
		return
	}

	if req.Title == "" {
		http.Error(w, "title is required", http.StatusBadRequest)
		return


 ... (clipped 35 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Missing authn/authz checks: The new POST /mcp/route endpoint accepts external input and executes routing without any
visible authentication/authorization enforcement in the handler, which may allow
unauthorized access unless enforced elsewhere.

Referred Code
// handleMCPRoute handles POST /mcp/route
func (s *Server) handleMCPRoute(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		return
	}

	if s.mcpRouter == nil {
		http.Error(w, "MCP router not configured", http.StatusServiceUnavailable)
		return
	}

	var req mcpRouteRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "invalid json", http.StatusBadRequest)
		return
	}

	if req.Title == "" {
		http.Error(w, "title is required", http.StatusBadRequest)
		return


 ... (clipped 13 lines)

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

Previous compliance checks

Compliance check up to commit 117b901
Security Compliance
Unauthenticated endpoint exposure

Description: The new POST /mcp/route endpoint appears to expose internal routing/metadata without any
visible authentication/authorization or request-size limits, which could allow
unauthenticated probing or resource exhaustion if the control plane is reachable by
untrusted clients.
server.go [451-503]

Referred Code
// handleMCPRoute handles POST /mcp/route
func (s *Server) handleMCPRoute(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		return
	}

	if s.mcpRouter == nil {
		http.Error(w, "MCP router not configured", http.StatusServiceUnavailable)
		return
	}

	var req mcpRouteRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "invalid json", http.StatusBadRequest)
		return
	}

	if req.Title == "" {
		http.Error(w, "title is required", http.StatusBadRequest)
		return


 ... (clipped 32 lines)
Ticket Compliance
🟡
🎫 #89
🟢 Log/audit which MCPs were routed per task in PDR.
Provide a preview command/endpoint to show routing decision for a given task description
(e.g., neona mcp route "...").
Validate router configuration on startup.
🔴 Support skill-based routing where skills can declare required MCPs (integration with
Skills Router).
Support context-aware routing using conversation/project/file context, with LLM fallback
for ambiguous cases.
Support manual override to force specific MCPs (e.g., `--mcp=server1,server2`).
Enforce a configurable tool budget so tools exposed stay under `max_tools_per_task`.
Avoid starting unused MCPs via lazy loading.
Add performance improvements such as route caching and tool pruning within MCPs.
Provide CLI commands to list/enable/disable MCPs (e.g., neona mcp list, neona mcp
enable/disable).
Implement an MCP Tool Router that dynamically selects and exposes only relevant MCP
servers/tools based on task context.
Provide keyword-based routing rules to map task keywords to MCP servers/tools.
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Missing audit logging: The new POST /mcp/route endpoint processes routing decisions without emitting an
audit/PDR-style record including actor/user identity and outcome, making the action hard
to reconstruct for compliance.

Referred Code
// handleMCPRoute handles POST /mcp/route
func (s *Server) handleMCPRoute(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		return
	}

	if s.mcpRouter == nil {
		http.Error(w, "MCP router not configured", http.StatusServiceUnavailable)
		return
	}

	var req mcpRouteRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "invalid json", http.StatusBadRequest)
		return
	}

	if req.Title == "" {
		http.Error(w, "title is required", http.StatusBadRequest)
		return


 ... (clipped 32 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Unlogged route failures: When routing fails, the handler returns an HTTP 500 but does not log contextual details
(e.g., request correlation/task fields) to internal logs, reducing debuggability and
observability for production incidents.

Referred Code
result, err := s.mcpRouter.Route(r.Context(), task)
if err != nil {
	http.Error(w, err.Error(), http.StatusInternalServerError)
	return
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
Raw error exposure: The POST /mcp/route endpoint returns err.Error() directly to clients on failures,
potentially leaking internal implementation details.

Referred Code
result, err := s.mcpRouter.Route(r.Context(), task)
if err != nil {
	http.Error(w, err.Error(), http.StatusInternalServerError)
	return
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Missing authz checks: The new POST /mcp/route endpoint accepts external input and performs routing without any
visible authentication/authorization enforcement in the handler, which may be insecure
unless enforced by middleware not shown in the diff.

Referred Code
// handleMCPRoute handles POST /mcp/route
func (s *Server) handleMCPRoute(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		return
	}

	if s.mcpRouter == nil {
		http.Error(w, "MCP router not configured", http.StatusServiceUnavailable)
		return
	}

	var req mcpRouteRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "invalid json", http.StatusBadRequest)
		return
	}

	if req.Title == "" {
		http.Error(w, "title is required", http.StatusBadRequest)
		return


 ... (clipped 32 lines)

Learn more about managing compliance generic rules or creating your own custom rules

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Jan 19, 2026

PR Code Suggestions ✨

Latest suggestions up to 7c26930

CategorySuggestion                                                                                                                                    Impact
Incremental [*]
Prevent duplicate response writes

Fix a bug where the JSON response is encoded twice. Remove the duplicate
json.NewEncoder(w).Encode(resp) call and add a return statement in the error
case.

internal/controlplane/server.go [501-504]

 w.Header().Set("Content-Type", "application/json")
 if err := json.NewEncoder(w).Encode(resp); err != nil {
-    log.Printf("Failed to encode MCP route response: %v", err)
+	log.Printf("Failed to encode MCP route response: %v", err)
+	return
 }
  • Apply / Chat
Suggestion importance[1-10]: 9

__

Why: This suggestion correctly identifies a critical bug where the HTTP response is encoded twice, which would lead to incorrect behavior, and provides a correct fix.

High
Fix error-path formatting

Correct the inconsistent indentation within the error handling block for
s.mcpRouter.Route. This improves code style and readability.

internal/controlplane/server.go [479-484]

 result, err := s.mcpRouter.Route(r.Context(), task)
 if err != nil {
-	    log.Printf("MCP routing failed: %v", err)
-	    http.Error(w, "internal server error", http.StatusInternalServerError)
-	    return
-	}
+	log.Printf("MCP routing failed: %v", err)
+	http.Error(w, "internal server error", http.StatusInternalServerError)
+	return
+}
  • Apply / Chat
Suggestion importance[1-10]: 3

__

Why: The suggestion correctly identifies and fixes inconsistent indentation in an error handling block, which improves code style and readability.

Low
Security
Limit request body size

Add a request body size limit using http.MaxBytesReader before decoding JSON to
prevent potential denial-of-service attacks.

internal/controlplane/server.go [463-467]

+r.Body = http.MaxBytesReader(w, r.Body, 1<<20) // 1 MiB
+
 var req mcpRouteRequest
 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
 	http.Error(w, "invalid json", http.StatusBadRequest)
 	return
 }
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a potential denial-of-service vulnerability and proposes a standard best-practice solution to mitigate it, enhancing the new endpoint's security.

Medium
Possible issue
Add timeout to routing

Add a timeout to the mcpRouter.Route call within the scheduler to prevent it
from blocking indefinitely and stalling task dispatching.

internal/scheduler/scheduler.go [169-185]

-result, err := sch.mcpRouter.Route(sch.ctx, mcpTask)
+routeCtx, cancel := context.WithTimeout(sch.ctx, 2*time.Second)
+defer cancel()
+
+result, err := sch.mcpRouter.Route(routeCtx, mcpTask)
 if err != nil {
 	log.Printf("MCP routing error for task %s: %v", task.ID, err)
 } else {
 	// Log selected MCPs
 	mcpNames := make([]string, len(result.SelectedMCPs))
 	for i, m := range result.SelectedMCPs {
 		mcpNames[i] = m.Name
 	}
 	sch.pdr.Record("task.mcp_route", map[string]interface{}{
 		"task_id":       task.ID,
 		"selected_mcps": mcpNames,
 		"total_tools":   result.TotalTools,
 		"matched_rules": result.MatchedRules,
 	}, "success", task.ID, fmt.Sprintf("Routed to %d MCPs with %d tools", len(mcpNames), result.TotalTools))
 	log.Printf("Task %s routed to MCPs: %v (%d tools)", task.ID, mcpNames, result.TotalTools)
 }
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out that the Route call could block indefinitely, and proposes adding a timeout to improve the scheduler's robustness and prevent it from stalling.

Medium
  • Update

Previous suggestions

✅ Suggestions up to commit 117b901
CategorySuggestion                                                                                                                                    Impact
Possible issue
Handle MCP routing errors properly

Handle MCP routing errors by recording a failure in the PDR and stopping the
task dispatch to prevent execution in a misconfigured environment.

internal/scheduler/scheduler.go [162-186]

 // Route MCPs for this task if router is configured
 if sch.mcpRouter != nil {
 	mcpTask := mcp.Task{
 		ID:          task.ID,
 		Title:       task.Title,
 		Description: task.Description,
 	}
 	result, err := sch.mcpRouter.Route(sch.ctx, mcpTask)
 	if err != nil {
 		log.Printf("MCP routing error for task %s: %v", task.ID, err)
-	} else {
-		// Log selected MCPs
-		mcpNames := make([]string, len(result.SelectedMCPs))
-		for i, m := range result.SelectedMCPs {
-			mcpNames[i] = m.Name
-		}
 		sch.pdr.Record("task.mcp_route", map[string]interface{}{
-			"task_id":       task.ID,
-			"selected_mcps": mcpNames,
-			"total_tools":   result.TotalTools,
-			"matched_rules": result.MatchedRules,
-		}, "success", task.ID, fmt.Sprintf("Routed to %d MCPs with %d tools", len(mcpNames), result.TotalTools))
-		log.Printf("Task %s routed to MCPs: %v (%d tools)", task.ID, mcpNames, result.TotalTools)
+			"task_id": task.ID,
+			"error":   err.Error(),
+		}, "failure", task.ID, "MCP routing failed")
+		// NOTE: Consider how to handle the claimed task. Releasing it might be appropriate.
+		return // Stop dispatching this task
 	}
+
+	// Log selected MCPs
+	mcpNames := make([]string, len(result.SelectedMCPs))
+	for i, m := range result.SelectedMCPs {
+		mcpNames[i] = m.Name
+	}
+	sch.pdr.Record("task.mcp_route", map[string]interface{}{
+		"task_id":       task.ID,
+		"selected_mcps": mcpNames,
+		"total_tools":   result.TotalTools,
+		"matched_rules": result.MatchedRules,
+	}, "success", task.ID, fmt.Sprintf("Routed to %d MCPs with %d tools", len(mcpNames), result.TotalTools))
+	log.Printf("Task %s routed to MCPs: %v (%d tools)", task.ID, mcpNames, result.TotalTools)
 }
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a potential bug where task dispatch continues after a critical MCP routing failure, and proposes a robust fix by logging the failure and halting dispatch.

Medium
Avoid hardcoding the tool budget

Replace the hardcoded ToolBudget value in the mcpRouteResponse with the actual
budget from the routing result to reflect the current configuration.

internal/controlplane/server.go [494-499]

 resp := mcpRouteResponse{
 	SelectedMCPs: mcps,
 	MatchedRules: result.MatchedRules,
 	TotalTools:   result.TotalTools,
-	ToolBudget:   80, // Default budget
+	ToolBudget:   result.ToolBudget,
 }
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies a hardcoded value that should be derived from the configuration, which prevents the API from returning misleading information if the configuration is changed.

Low
General
Close request body on handler

Add defer r.Body.Close() in the handleMCPRoute function to prevent resource
leaks by ensuring the request body is always closed.

internal/controlplane/server.go [463-467]

+defer r.Body.Close()
 var req mcpRouteRequest
 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
     http.Error(w, "invalid json", http.StatusBadRequest)
     return
 }
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out a missing call to close the request body, which prevents a potential resource leak and is a standard best practice in Go HTTP handlers.

Medium
Use an interface for router dependency

*Change the mcpRouter field in the Scheduler struct from the concrete type
mcp.KeywordRouter to the MCPRouter interface to improve decoupling and
testability.

internal/scheduler/scheduler.go [37-38]

+// MCPRouter provides MCP routing.
+MCPRouter interface {
+	Route(ctx context.Context, task mcp.Task) (*mcp.RoutingResult, error)
+}
+
+// ... in Scheduler struct ...
 // MCP router for tool selection
-mcpRouter *mcp.KeywordRouter
+mcpRouter MCPRouter
Suggestion importance[1-10]: 5

__

Why: The suggestion correctly proposes using an interface instead of a concrete type for the mcpRouter field, which improves modularity and testability by decoupling the scheduler from a specific router implementation.

Low
Handle JSON encoding errors
Suggestion Impact:The commit added an if err := json.NewEncoder(w).Encode(resp); err != nil { log.Printf(...) } block to handle and log JSON encoding errors. However, it also left an additional unconditional json.NewEncoder(w).Encode(resp) call afterward, resulting in encoding being attempted twice.

code diff:

-	w.Header().Set("Content-Type", "application/json")
+w.Header().Set("Content-Type", "application/json")
+if err := json.NewEncoder(w).Encode(resp); err != nil {
+    log.Printf("Failed to encode MCP route response: %v", err)
+}
 	json.NewEncoder(w).Encode(resp)

Add error handling for the json.NewEncoder(w).Encode(resp) call in the
handleMCPRoute handler to log any failures during JSON response encoding.

internal/controlplane/server.go [501-502]

 w.Header().Set("Content-Type", "application/json")
-json.NewEncoder(w).Encode(resp)
+if err := json.NewEncoder(w).Encode(resp); err != nil {
+    log.Printf("Failed to encode MCP route response: %v", err)
+}

[Suggestion processed]

Suggestion importance[1-10]: 5

__

Why: The suggestion correctly points out that a potential error from JSON encoding is being ignored, and recommends adding error handling to improve the robustness and observability of the handler.

Low
Security
Hide internal errors from clients
Suggestion Impact:Replaced returning err.Error() to the client with a server-side log of the routing error and a generic internal server error HTTP response.

code diff:

 	result, err := s.mcpRouter.Route(r.Context(), task)
 	if err != nil {
-		http.Error(w, err.Error(), http.StatusInternalServerError)
-		return
+	    log.Printf("MCP routing failed: %v", err)
+	    http.Error(w, "internal server error", http.StatusInternalServerError)
+	    return

In the handleMCPRoute handler, log the detailed routing error internally and
return a generic "internal server error" message to the client to avoid leaking
implementation details.

internal/controlplane/server.go [479-483]

 result, err := s.mcpRouter.Route(r.Context(), task)
 if err != nil {
-    http.Error(w, err.Error(), http.StatusInternalServerError)
+    log.Printf("MCP routing failed: %v", err)
+    http.Error(w, "internal server error", http.StatusInternalServerError)
     return
 }

[Suggestion processed]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that internal error details are being exposed to the client, and proposes a more secure approach by logging the details and returning a generic error message.

Medium

Co-authored-by: qodo-code-review[bot] <151058649+qodo-code-review[bot]@users.noreply.github.com>
Co-authored-by: qodo-code-review[bot] <151058649+qodo-code-review[bot]@users.noreply.github.com>
cursor[bot]

This comment was marked as outdated.

@fentz26 fentz26 changed the title 🔗 MCP Router Phase 2: Scheduler Integration & API Endpoint MCP Router Phase 2: Scheduler Integration & API Endpoint Jan 19, 2026
@fentz26 fentz26 merged commit 20b3231 into master Jan 20, 2026
4 checks passed
@fentz26 fentz26 deleted the feature/mcp-router-phase2 branch January 20, 2026 20:30
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

This is the final PR Bugbot will review for you during this billing cycle

Your free Bugbot reviews will reset on February 18

Details

You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

SelectedMCPs: mcps,
MatchedRules: result.MatchedRules,
TotalTools: result.TotalTools,
ToolBudget: 80, // Default budget
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API returns hardcoded tool budget ignoring configuration

Low Severity

The ToolBudget field in the /mcp/route response is hardcoded to 80, but the actual budget used by the router comes from config.MaxToolsPerTask which users can customize via their mcp.yaml configuration. If a user configures a different budget (e.g., 50 or 100), the API response still shows 80, providing misleading metadata about the actual tool budget being enforced.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants