Skip to content

feat: add MCP Plugin#93

Closed
Pratham-Mishra04 wants to merge 1 commit intomainfrom
06-17-feat_mcp_plugin_added
Closed

feat: add MCP Plugin#93
Pratham-Mishra04 wants to merge 1 commit intomainfrom
06-17-feat_mcp_plugin_added

Conversation

@Pratham-Mishra04
Copy link
Copy Markdown
Collaborator

Add MCP Safe Mode and Agentic Mode Support

This PR adds support for Model-Controlled Programs (MCP) with two key modes:

  1. Safe Mode: Requires user approval before executing MCP tools. When enabled, tool calls are presented to the user for approval before execution.

  2. Agentic Mode: Enables a more sophisticated conversation flow where tool results are sent back to the LLM for synthesis into a comprehensive response.

Key Changes:

  • Added MCPTools field to BifrostRequest to support approved tools in safe mode
  • Added PendingMCPTools field to BifrostResponseExtraFields to store tools awaiting approval
  • Created PendingMCPTool struct to represent tools requiring user approval
  • Implemented a new MCP plugin with support for both safe and agentic modes
  • Added a CLI chatbot demo showcasing the new MCP capabilities
  • Integrated with external MCP servers (Maxim MCP, Serper, Context7)
  • Added comprehensive tests for all new functionality

The implementation allows for a more secure and interactive experience when using MCP tools, giving users control over tool execution while maintaining a natural conversation flow.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 17, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a CLI chatbot integrating multiple external tools with safe mode approval and agentic response synthesis.
    • Added support for connecting to and managing tools from local and external MCP servers, including user approval workflows for tool execution.
    • Enabled agentic mode, allowing the assistant to synthesize responses using tool outputs.
  • Bug Fixes

    • Improved concurrency safety and error handling in tool execution and client management.
  • Documentation

    • Added comprehensive user and developer documentation for the MCP plugin, including setup, configuration, usage examples, and troubleshooting.
  • Tests

    • Added integration tests for local and external tool execution, agentic mode, and safe mode approval flows.
  • Chores

    • Added dependency management for the MCP plugin.

Summary by CodeRabbit

  • New Features

    • Introduced a command-line chatbot with multi-tool integration, safe mode approval flow, agentic mode support, and conversation history management.
    • Added support for hosting and connecting to MCP tools and servers, enabling dynamic tool execution and user approval workflows.
    • Enhanced request and response handling to track approved and pending MCP tools for improved interaction safety.
  • Tests

    • Added comprehensive tests covering tool integration, external MCP server connectivity, agentic mode behavior, and end-to-end chat flows.
  • Chores

    • Added module dependency management for the MCP plugin.

Walkthrough

This update introduces a new MCP plugin system for the Bifrost framework, including a CLI chatbot, plugin infrastructure, and comprehensive integration tests. The schema is extended to support tracking approved and pending MCP tools. New files implement the MCP host, CLI chatbot, Go module definitions, and plugin tests, while schemas are updated for tool approval flows.

Changes

File(s) Change Summary
core/schemas/bifrost.go Extended BifrostRequest and BifrostResponseExtraFields structs to support MCP tool tracking; added PendingMCPTool struct.
plugins/mcp/chat/chatbot.go Added CLI chatbot with Bifrost/MCP integration, user approval for tools, agentic/safe modes, and conversation management.
plugins/mcp/go.mod Added Go module file specifying dependencies, Go version, and local module replacement for development.
plugins/mcp/main.go Implemented MCP plugin: local/external tool hosting, tool execution, safe/agentic modes, Bifrost integration, and plugin lifecycle.
plugins/mcp/plugin_test.go Added tests for MCP plugin: local tool, Context7/Maxim integration, agentic/non-agentic flows, and helper functions.
plugins/mcp/utils.go Added utility functions for MCP client management, tool execution, schema conversion, and policy enforcement.
plugins/mcp/README.md Added comprehensive documentation detailing plugin features, usage, architecture, configuration, and troubleshooting.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI_Chatbot
    participant Bifrost
    participant MCP_Plugin
    participant MCP_Server

    User->>CLI_Chatbot: Enter message
    CLI_Chatbot->>Bifrost: Send chat request
    Bifrost->>MCP_Plugin: PreHook (inject tools)
    MCP_Plugin->>MCP_Server: (if tool call) Execute tool(s)
    MCP_Server-->>MCP_Plugin: Tool response(s)
    MCP_Plugin->>Bifrost: PostHook (handle tool results)
    Bifrost->>CLI_Chatbot: Return response (may include pending tools)
    CLI_Chatbot->>User: Display response / prompt for tool approval
    User->>CLI_Chatbot: Approve tool(s) (if needed)
    CLI_Chatbot->>Bifrost: Send approved tool request
    Bifrost->>MCP_Plugin: PreHook/PostHook (as above)
    Bifrost->>CLI_Chatbot: Return final response
    CLI_Chatbot->>User: Display final answer
Loading

Suggested reviewers

  • danpiths
  • akshaydeo

Poem

🐇 In code’s green fields I hop and play,
New tools and chats come out to sway.
Approval gates and agentic cheer,
Safe mode whispers, “No fear, no fear!”
With tests and schema shining bright,
The rabbit’s joy takes flight tonight! ✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate Unit Tests
  • Create PR with Unit Tests
  • Post Copyable Unit Tests in Comment
  • Commit Unit Tests in branch 06-17-feat_mcp_plugin_added

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Collaborator Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5bdc42f and 03e6be1.

⛔ Files ignored due to path filters (1)
  • plugins/mcp/go.sum is excluded by !**/*.sum
📒 Files selected for processing (5)
  • core/schemas/bifrost.go (3 hunks)
  • plugins/mcp/chat/chatbot.go (1 hunks)
  • plugins/mcp/go.mod (1 hunks)
  • plugins/mcp/main.go (1 hunks)
  • plugins/mcp/plugin_test.go (1 hunks)
🧰 Additional context used
🪛 golangci-lint (1.64.8)
plugins/mcp/plugin_test.go

591-591: Error return value of plugin.Cleanup is not checked

(errcheck)


688-688: Error return value of plugin.Cleanup is not checked

(errcheck)


174-174: func printChoicesAsJSON is unused

(unused)

plugins/mcp/chat/chatbot.go

429-429: Error return value of s.mcpPlugin.Cleanup is not checked

(errcheck)


416-416: SA1019: strings.Title has been deprecated since Go 1.18 and an alternative has been available since Go 1.0: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead.

(staticcheck)

plugins/mcp/main.go

125-125: Error return value of p.startLocalMCPServer is not checked

(errcheck)


132-132: Error return value of plugin.setupLocalHost is not checked

(errcheck)


95-95: ineffectual assignment to serverPort

(ineffassign)

🔇 Additional comments (2)
plugins/mcp/go.mod (1)

3-3: Verify the Go version specification.

Go version 1.24.1 seems unusual. As of recent knowledge, Go versions were around 1.22.x. Please verify this is a valid Go version for June 2025.

What is the latest stable version of Go programming language?
core/schemas/bifrost.go (1)

72-73: Schema extensions are well-designed.

The new MCP-related fields are properly structured with:

  • Optional pointers for backward compatibility
  • Clear JSON tags and field documentation
  • Appropriate data types for the safe mode approval flow

Also applies to: 407-413, 439-446

Comment thread plugins/mcp/plugin_test.go Outdated
Comment thread plugins/mcp/main.go Outdated
Comment thread plugins/mcp/main.go Outdated
Comment thread plugins/mcp/main.go
Comment on lines +93 to +218
serverPort := config.ServerPort
if serverPort == "" {
serverPort = DefaultServerPort
}

clientTransport := httpTransport.NewHTTPClientTransport("/mcp")
clientTransport.WithBaseURL(fmt.Sprintf("http://localhost%s", config.ServerPort))
client := mcp_golang.NewClientWithInfo(clientTransport, mcp_golang.ClientInfo{
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove ineffectual assignment.

The initial assignment to serverPort on line 93 is immediately overwritten on line 95.

-	serverPort := config.ServerPort
-	if serverPort == "" {
+	serverPort := config.ServerPort
+	if serverPort == "" {
		serverPort = DefaultServerPort
	}

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 golangci-lint (1.64.8)

95-95: ineffectual assignment to serverPort

(ineffassign)

🤖 Prompt for AI Agents
In plugins/mcp/main.go around lines 93 to 100, the variable serverPort is
assigned from config.ServerPort and then immediately overwritten if empty.
Remove the initial assignment on line 93 and directly assign serverPort with a
conditional expression that sets it to config.ServerPort if not empty, otherwise
to DefaultServerPort. Then use serverPort in the fmt.Sprintf call to ensure the
value is consistent and not overwritten unnecessarily.

Comment thread plugins/mcp/main.go Outdated
Comment on lines +428 to +435
if s.mcpPlugin != nil {
s.mcpPlugin.Cleanup()
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Check error return from cleanup.

The error from s.mcpPlugin.Cleanup() should be handled or at least logged.

	if s.mcpPlugin != nil {
-		s.mcpPlugin.Cleanup()
+		if err := s.mcpPlugin.Cleanup(); err != nil {
+			fmt.Printf("Warning: Failed to cleanup MCP plugin: %v\n", err)
+		}
	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if s.mcpPlugin != nil {
s.mcpPlugin.Cleanup()
}
if s.mcpPlugin != nil {
if err := s.mcpPlugin.Cleanup(); err != nil {
fmt.Printf("Warning: Failed to cleanup MCP plugin: %v\n", err)
}
}
🧰 Tools
🪛 golangci-lint (1.64.8)

429-429: Error return value of s.mcpPlugin.Cleanup is not checked

(errcheck)

🤖 Prompt for AI Agents
In plugins/mcp/chat/chatbot.go around lines 428 to 430, the call to
s.mcpPlugin.Cleanup() returns an error that is currently ignored. Modify the
code to capture this error and handle it appropriately, such as logging the
error using a logger or returning it if applicable, to ensure any cleanup issues
are not silently ignored.

Comment on lines +579 to +584
systemPrompt := session.history[0] // Assuming first message is system
session.history = []schemas.BifrostMessage{systemPrompt}
fmt.Println("🧹 Conversation history cleared!")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential index out of bounds error.

Assuming session.history[0] is the system prompt could cause a panic if history is empty.

 case "/clear":
 	// Keep system prompt but clear conversation history
-	systemPrompt := session.history[0] // Assuming first message is system
-	session.history = []schemas.BifrostMessage{systemPrompt}
+	if len(session.history) > 0 && session.history[0].Role == schemas.ModelChatMessageRoleSystem {
+		systemPrompt := session.history[0]
+		session.history = []schemas.BifrostMessage{systemPrompt}
+	} else {
+		session.history = []schemas.BifrostMessage{}
+	}
 	fmt.Println("🧹 Conversation history cleared!")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
systemPrompt := session.history[0] // Assuming first message is system
session.history = []schemas.BifrostMessage{systemPrompt}
fmt.Println("🧹 Conversation history cleared!")
case "/clear":
// Keep system prompt but clear conversation history
if len(session.history) > 0 && session.history[0].Role == schemas.ModelChatMessageRoleSystem {
systemPrompt := session.history[0]
session.history = []schemas.BifrostMessage{systemPrompt}
} else {
session.history = []schemas.BifrostMessage{}
}
fmt.Println("🧹 Conversation history cleared!")
🤖 Prompt for AI Agents
In plugins/mcp/chat/chatbot.go around lines 579 to 581, the code assumes
session.history has at least one element when accessing session.history[0],
which can cause a panic if the slice is empty. Add a check to ensure
session.history is not empty before accessing the first element. If it is empty,
handle that case appropriately, such as skipping the reset or initializing with
a default system prompt.

@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-17-feat_mcp_plugin_added branch from 03e6be1 to d67cb2a Compare June 18, 2025 05:12
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (3)
plugins/mcp/chat/chatbot.go (3)

421-421: Deprecated strings.Title – see earlier review; still unresolved.


434-435: Check error from Cleanup

Swallowing the error masks failures in terminating external MCP processes.

-		s.mcpPlugin.Cleanup()
+		if err := s.mcpPlugin.Cleanup(); err != nil {
+			fmt.Printf("Warning: MCP plugin cleanup failed: %v\n", err)
+		}

581-584: Potential slice-bounds panic on /clear

When the user clears history at the very start of a session, session.history[0] will panic.

Guard the access:

-	systemPrompt := session.history[0]
-	session.history = []schemas.BifrostMessage{systemPrompt}
+	if len(session.history) > 0 && session.history[0].Role == schemas.ModelChatMessageRoleSystem {
+		session.history = []schemas.BifrostMessage{session.history[0]}
+	} else {
+		session.history = nil
+	}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 03e6be1 and d67cb2a.

⛔ Files ignored due to path filters (1)
  • plugins/mcp/go.sum is excluded by !**/*.sum
📒 Files selected for processing (5)
  • core/schemas/bifrost.go (3 hunks)
  • plugins/mcp/chat/chatbot.go (1 hunks)
  • plugins/mcp/go.mod (1 hunks)
  • plugins/mcp/main.go (1 hunks)
  • plugins/mcp/plugin_test.go (1 hunks)
🧰 Additional context used
🪛 golangci-lint (1.64.8)
plugins/mcp/plugin_test.go

590-590: Error return value of plugin.Cleanup is not checked

(errcheck)


687-687: Error return value of plugin.Cleanup is not checked

(errcheck)


174-174: func printChoicesAsJSON is unused

(unused)

plugins/mcp/chat/chatbot.go

434-434: Error return value of s.mcpPlugin.Cleanup is not checked

(errcheck)


421-421: SA1019: strings.Title has been deprecated since Go 1.18 and an alternative has been available since Go 1.0: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead.

(staticcheck)

plugins/mcp/main.go

908-908: Error return value of client.StdioCommand.Process.Kill is not checked

(errcheck)


909-909: Error return value of client.StdioCommand.Wait is not checked

(errcheck)

🔇 Additional comments (1)
plugins/mcp/go.mod (1)

3-4: Verify required Go tool-chain version

go 1.24.1 is not released yet. Building in CI with the latest stable (currently 1.22.x) will fail.

Confirm the minimum version actually needed; otherwise downgrade the directive to the highest released version.

Comment thread core/schemas/bifrost.go
Comment on lines +174 to +182
func printChoicesAsJSON(t *testing.T, choices []schemas.BifrostResponseChoice) {
t.Helper()
jsonData, err := json.MarshalIndent(choices, "", " ")
if err != nil {
t.Errorf("Failed to marshal choices to JSON: %v", err)
return
}
fmt.Printf("--- Response Choices (JSON) ---\n%s\n-----------------------------\n", string(jsonData))
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Remove dead code

printChoicesAsJSON is never invoked and is flagged by golangci-lint.
Delete it to keep the test file tidy.

🧰 Tools
🪛 golangci-lint (1.64.8)

174-174: func printChoicesAsJSON is unused

(unused)

🤖 Prompt for AI Agents
In plugins/mcp/plugin_test.go around lines 174 to 182, the function
printChoicesAsJSON is defined but never used, causing a lint warning. Remove the
entire printChoicesAsJSON function to eliminate dead code and keep the test file
clean.

Comment thread plugins/mcp/plugin_test.go Outdated
Comment on lines +590 to +584
plugin.Cleanup()
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle Cleanup errors

plugin.Cleanup() returns an error that is currently ignored; this hides failures such as orphaned child processes.

-	plugin.Cleanup()
+	if err := plugin.Cleanup(); err != nil {
+		t.Errorf("plugin cleanup failed: %v", err)
+	}

Also applies to: 687-688

🧰 Tools
🪛 golangci-lint (1.64.8)

590-590: Error return value of plugin.Cleanup is not checked

(errcheck)

🤖 Prompt for AI Agents
In plugins/mcp/plugin_test.go at lines 590-591 and similarly at 687-688, the
call to plugin.Cleanup() ignores the returned error, which can hide failures
like orphaned child processes. Modify the code to capture the error returned by
plugin.Cleanup() and handle it appropriately, such as logging the error or
failing the test if cleanup fails, to ensure any issues during cleanup are not
silently ignored.

Comment thread plugins/mcp/main.go Outdated
Comment on lines +904 to +1032
// Clean up STDIO commands
for _, client := range p.clientMap {
if client.StdioCommand != nil && client.StdioCommand.Process != nil {
fmt.Printf("Terminating STDIO process: %d\n", client.StdioCommand.Process.Pid)
client.StdioCommand.Process.Kill()
client.StdioCommand.Wait() // Wait for cleanup
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Propagate errors from process termination

Process.Kill() and cmd.Wait() return errors that should be surfaced to avoid zombie processes.

-			client.StdioCommand.Process.Kill()
-			client.StdioCommand.Wait() // Wait for cleanup
+			if err := client.StdioCommand.Process.Kill(); err != nil {
+				fmt.Printf("failed to kill process %d: %v\n", client.StdioCommand.Process.Pid, err)
+			}
+			if err := client.StdioCommand.Wait(); err != nil {
+				fmt.Printf("process %d did not exit cleanly: %v\n", client.StdioCommand.Process.Pid, err)
+			}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Clean up STDIO commands
for _, client := range p.clientMap {
if client.StdioCommand != nil && client.StdioCommand.Process != nil {
fmt.Printf("Terminating STDIO process: %d\n", client.StdioCommand.Process.Pid)
client.StdioCommand.Process.Kill()
client.StdioCommand.Wait() // Wait for cleanup
}
// Clean up STDIO commands
for _, client := range p.clientMap {
if client.StdioCommand != nil && client.StdioCommand.Process != nil {
fmt.Printf("Terminating STDIO process: %d\n", client.StdioCommand.Process.Pid)
- client.StdioCommand.Process.Kill()
- client.StdioCommand.Wait() // Wait for cleanup
+ if err := client.StdioCommand.Process.Kill(); err != nil {
+ fmt.Printf("failed to kill process %d: %v\n", client.StdioCommand.Process.Pid, err)
+ }
+ if err := client.StdioCommand.Wait(); err != nil {
+ fmt.Printf("process %d did not exit cleanly: %v\n", client.StdioCommand.Process.Pid, err)
+ }
}
}
🧰 Tools
🪛 golangci-lint (1.64.8)

908-908: Error return value of client.StdioCommand.Process.Kill is not checked

(errcheck)


909-909: Error return value of client.StdioCommand.Wait is not checked

(errcheck)

🤖 Prompt for AI Agents
In plugins/mcp/main.go around lines 904 to 910, the calls to Process.Kill() and
cmd.Wait() do not handle or propagate errors, which can lead to unnoticed
failures and potential zombie processes. Modify the code to capture and handle
the errors returned by Process.Kill() and Wait(), logging or returning these
errors appropriately to ensure any issues during process termination are
surfaced and managed.

@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-17-feat_mcp_plugin_added branch from d67cb2a to bbf99c3 Compare June 18, 2025 06:11
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (5)
core/schemas/bifrost.go (2)

72-73: Avoid pointer-to-slice; plain slice already nil-able

*[]Tool adds an unnecessary level of indirection and complicates marshal/unmarshal logic.
Use []Tool instead.


407-414: Same pointer-to-slice issue for PendingMCPTools

Make the field a plain slice:

-	PendingMCPTools *[]PendingMCPTool `json:"pending_mcp_tools,omitempty"`
+	PendingMCPTools []PendingMCPTool `json:"pending_mcp_tools,omitempty"`
plugins/mcp/chat/chatbot.go (2)

433-435: Handle Cleanup error

mcpPlugin.Cleanup() returns an error that is silently ignored—failures (e.g., zombie processes) will go unnoticed.

-	s.mcpPlugin.Cleanup()
+	if err := s.mcpPlugin.Cleanup(); err != nil {
+		fmt.Printf("Warning: MCP cleanup failed: %v\n", err)
+	}

582-583: Potential panic when history is empty

session.history[0] assumes at least one element. Guard against empty slice:

if len(session.history) > 0 && session.history[0].Role == schemas.ModelChatMessageRoleSystem {
    systemPrompt := session.history[0]
    session.history = []schemas.BifrostMessage{systemPrompt}
} else {
    session.history = nil
}
plugins/mcp/main.go (1)

991-995: Check errors from Process.Kill / Wait

Ignoring these errors can leave orphaned processes:

-	client.StdioCommand.Process.Kill()
-	client.StdioCommand.Wait()
+if err := client.StdioCommand.Process.Kill(); err != nil {
+	p.logger.Warn(fmt.Sprintf("%s failed to kill process %d: %v", LogPrefix, client.StdioCommand.Process.Pid, err))
+}
+if err := client.StdioCommand.Wait(); err != nil {
+	p.logger.Warn(fmt.Sprintf("%s process %d did not exit cleanly: %v", LogPrefix, client.StdioCommand.Process.Pid, err))
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d67cb2a and bbf99c3.

⛔ Files ignored due to path filters (1)
  • plugins/mcp/go.sum is excluded by !**/*.sum
📒 Files selected for processing (5)
  • core/schemas/bifrost.go (3 hunks)
  • plugins/mcp/chat/chatbot.go (1 hunks)
  • plugins/mcp/go.mod (1 hunks)
  • plugins/mcp/main.go (1 hunks)
  • plugins/mcp/plugin_test.go (1 hunks)
🧰 Additional context used
🪛 golangci-lint (1.64.8)
plugins/mcp/plugin_test.go

174-174: func printChoicesAsJSON is unused

(unused)

plugins/mcp/main.go

993-993: Error return value of client.StdioCommand.Process.Kill is not checked

(errcheck)


994-994: Error return value of client.StdioCommand.Wait is not checked

(errcheck)

plugins/mcp/chat/chatbot.go

434-434: Error return value of s.mcpPlugin.Cleanup is not checked

(errcheck)


421-421: SA1019: strings.Title has been deprecated since Go 1.18 and an alternative has been available since Go 1.0: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead.

(staticcheck)

🔇 Additional comments (1)
plugins/mcp/go.mod (1)

3-3: Unreleased Go version declared

go 1.24.1 is not a publicly-released toolchain; using it will break go mod tidy and CI on all standard runners.
Pin to the latest stable major (1.22) or omit the patch suffix.

⛔ Skipped due to learnings
Learnt from: Pratham-Mishra04
PR: maximhq/bifrost#81
File: tests/core-providers/go.mod:3-4
Timestamp: 2025-06-16T03:55:16.949Z
Learning: Go 1.24 was released in February 2025 and is stable and available for use in go.mod files.
Learnt from: Pratham-Mishra04
PR: maximhq/bifrost#81
File: tests/core-providers/go.mod:3-4
Timestamp: 2025-06-16T04:27:53.538Z
Learning: In Go module files, `go 1.24.1` (with patch version) can work fine in some setups, contrary to the general rule that go directives should only include major.minor versions.

Comment thread plugins/mcp/go.mod
gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace github.com/maximhq/bifrost/core => ../../core
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Local replace blocks external consumers

replace github.com/maximhq/bifrost/core => ../../core prevents downstream users from go get-ing the module.
Keep the replace only in a private development overlay (e.g. go.work) and remove it from committed go.mod.

🤖 Prompt for AI Agents
In plugins/mcp/go.mod at line 62, the local replace directive pointing
github.com/maximhq/bifrost/core to a relative path ../../core should be removed
from the committed go.mod file because it blocks external consumers from
fetching the module. Instead, move this replace directive to a private
development overlay file such as go.work to keep local development convenience
without affecting downstream users.

Comment on lines +173 to +182
// Helper function to print choices as JSON
func printChoicesAsJSON(t *testing.T, choices []schemas.BifrostResponseChoice) {
t.Helper()
jsonData, err := json.MarshalIndent(choices, "", " ")
if err != nil {
t.Errorf("Failed to marshal choices to JSON: %v", err)
return
}
fmt.Printf("--- Response Choices (JSON) ---\n%s\n-----------------------------\n", string(jsonData))
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Remove dead helper printChoicesAsJSON

The function is never called; it triggers unused lint warnings.
Delete it or invoke it from the tests.

🧰 Tools
🪛 golangci-lint (1.64.8)

174-174: func printChoicesAsJSON is unused

(unused)

🤖 Prompt for AI Agents
In plugins/mcp/plugin_test.go around lines 173 to 182, the helper function
printChoicesAsJSON is defined but never used, causing unused lint warnings. To
fix this, either remove the entire printChoicesAsJSON function if it is not
needed, or add calls to this function in relevant test cases where printing
choices as JSON would be helpful for debugging or output verification.

@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-17-feat_mcp_plugin_added branch from bbf99c3 to 876358a Compare June 18, 2025 06:28
@Pratham-Mishra04 Pratham-Mishra04 changed the title feat: add MCP tools safe mode with user approval flow feat: add MCP Plugin Jun 18, 2025
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

♻️ Duplicate comments (6)
plugins/mcp/go.mod (1)

62-62: Remove the committed local replace

The replace github.com/maximhq/bifrost/core => ../../core directive blocks external users from go get-ing the module.
Move it to a private go.work / go.mod.local instead.

core/schemas/bifrost.go (1)

72-74: Drop pointer-to-slice indirection

Both MCPTools *[]Tool and PendingMCPTools *[]PendingMCPTool add an unnecessary level of indirection—nil vs empty slice is already distinguishable.
Use plain slices to simplify marshaling and call-site code:

-	MCPTools *[]Tool `json:"mcp_tools,omitempty"`
+	MCPTools []Tool `json:"mcp_tools,omitempty"`

-	PendingMCPTools *[]PendingMCPTool `json:"pending_mcp_tools,omitempty"`
+	PendingMCPTools []PendingMCPTool `json:"pending_mcp_tools,omitempty"`

Also applies to: 407-414

plugins/mcp/plugin_test.go (2)

168-170: Check Cleanup() errors

client.Cleanup() returns an error that is silently ignored in multiple places.
Capture and fail the test when cleanup fails to avoid masking resource-leak issues.

- client.Cleanup()
+ if err := client.Cleanup(); err != nil {
+     t.Errorf("cleanup failed: %v", err)
+ }

Also applies to: 393-396, 497-499, 583-585, 679-681


173-182: Remove dead helper printChoicesAsJSON

The function is still unused and keeps triggering unused linter warnings.
Delete it or call it from the tests.

plugins/mcp/chat/chatbot.go (2)

582-584: Prevent possible slice-bounds panic in /clear

session.history[0] assumes the slice is non-empty.
Guard the access to avoid panics on an empty history.


430-435: Handle cleanup errors

Both s.client.Cleanup() and s.mcpPlugin.Cleanup() return errors that are ignored.
Log or propagate them so resource-release failures aren’t missed.

if s.client != nil {
-    s.client.Cleanup()
+    if err := s.client.Cleanup(); err != nil {
+        fmt.Printf("warning: client cleanup failed: %v\n", err)
+    }
}
if s.mcpPlugin != nil {
-    s.mcpPlugin.Cleanup()
+    if err := s.mcpPlugin.Cleanup(); err != nil {
+        fmt.Printf("warning: MCP plugin cleanup failed: %v\n", err)
+    }
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bbf99c3 and 876358a.

⛔ Files ignored due to path filters (1)
  • plugins/mcp/go.sum is excluded by !**/*.sum
📒 Files selected for processing (6)
  • core/schemas/bifrost.go (3 hunks)
  • plugins/mcp/chat/chatbot.go (1 hunks)
  • plugins/mcp/go.mod (1 hunks)
  • plugins/mcp/main.go (1 hunks)
  • plugins/mcp/plugin_test.go (1 hunks)
  • plugins/mcp/utils.go (1 hunks)
🧰 Additional context used
🪛 golangci-lint (1.64.8)
plugins/mcp/plugin_test.go

174-174: func printChoicesAsJSON is unused

(unused)

plugins/mcp/chat/chatbot.go

434-434: Error return value of s.mcpPlugin.Cleanup is not checked

(errcheck)


421-421: SA1019: strings.Title has been deprecated since Go 1.18 and an alternative has been available since Go 1.0: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead.

(staticcheck)

plugins/mcp/main.go

690-690: Error return value of client.StdioCommand.Process.Kill is not checked

(errcheck)


691-691: Error return value of client.StdioCommand.Wait is not checked

(errcheck)

🔇 Additional comments (2)
plugins/mcp/go.mod (1)

3-3: Non-existent Go toolchain version

go 1.24.1 targets a Go release that does not (yet) exist.
Stick to the latest stable (e.g. go 1.22) or the minimum version required by the codebase to avoid broken go commands in CI and downstream consumers.

⛔ Skipped due to learnings
Learnt from: Pratham-Mishra04
PR: maximhq/bifrost#81
File: tests/core-providers/go.mod:3-4
Timestamp: 2025-06-16T03:55:16.949Z
Learning: Go 1.24 was released in February 2025 and is stable and available for use in go.mod files.
plugins/mcp/main.go (1)

529-536: STDIO transport type appears inverted

NewStdioServerTransportWithIO is intended for servers, yet this code builds a client (mcp_golang.NewClientWithInfo).
Using the server-side transport on the client side can swap stdin/stdout directions and break the protocol.

Verify the library API; it likely offers NewStdioClientTransportWithIO (or similar). Replace accordingly:

-stdioTransport := stdio.NewStdioServerTransportWithIO(stdout, stdin)
+stdioTransport := stdio.NewStdioClientTransportWithIO(stdin, stdout)

Failing to correct this will cause the handshake to stall.

Comment thread plugins/mcp/utils.go
Comment on lines +196 to +204
func (p *MCPPlugin) shouldSkipTool(toolName, clientName string) bool {
// ConnectToExternalMCP function already has the mutex lock
client, exists := p.clientMap[clientName]
if !exists {
return false // Don't skip if client config doesn't exist
}

return slices.Contains(client.ExecutionConfig.ToolsToSkip, toolName)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Possible data race on p.clientMap

shouldSkipTool accesses p.clientMap without acquiring the mutex, unlike other helpers that use p.mu.RLock().
If the map is mutated concurrently (e.g. during dynamic client registration) this can panic.

func (p *MCPPlugin) shouldSkipTool(toolName, clientName string) bool {
-	client, exists := p.clientMap[clientName]
+	p.mu.RLock()
+	client, exists := p.clientMap[clientName]
+	p.mu.RUnlock()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (p *MCPPlugin) shouldSkipTool(toolName, clientName string) bool {
// ConnectToExternalMCP function already has the mutex lock
client, exists := p.clientMap[clientName]
if !exists {
return false // Don't skip if client config doesn't exist
}
return slices.Contains(client.ExecutionConfig.ToolsToSkip, toolName)
}
func (p *MCPPlugin) shouldSkipTool(toolName, clientName string) bool {
// ConnectToExternalMCP function already has the mutex lock
p.mu.RLock()
client, exists := p.clientMap[clientName]
p.mu.RUnlock()
if !exists {
return false // Don't skip if client config doesn't exist
}
return slices.Contains(client.ExecutionConfig.ToolsToSkip, toolName)
}
🤖 Prompt for AI Agents
In plugins/mcp/utils.go around lines 196 to 204, the method shouldSkipTool
accesses the shared map p.clientMap without acquiring the mutex lock, which can
cause a data race and potential panic if the map is modified concurrently. To
fix this, add a read lock using p.mu.RLock() before accessing p.clientMap and
defer p.mu.RUnlock() immediately after acquiring the lock to ensure safe
concurrent access.

content = strings.Join(textParts, "\n")
}

role := strings.Title(string(msg.Role))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Replace deprecated strings.Title

strings.Title is deprecated. Use cases.Title from golang.org/x/text/cases instead.

import "golang.org/x/text/cases"
import "golang.org/x/text/language"

caser := cases.Title(language.English)
role := caser.String(string(msg.Role))
🧰 Tools
🪛 golangci-lint (1.64.8)

421-421: SA1019: strings.Title has been deprecated since Go 1.18 and an alternative has been available since Go 1.0: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead.

(staticcheck)

🤖 Prompt for AI Agents
In plugins/mcp/chat/chatbot.go at line 421, replace the deprecated strings.Title
function with cases.Title from golang.org/x/text/cases. Import the packages
"golang.org/x/text/cases" and "golang.org/x/text/language", create a caser using
cases.Title(language.English), and then use caser.String(string(msg.Role)) to
get the title-cased role string.

Comment thread plugins/mcp/main.go
Comment on lines +836 to +840
// Execute the tool
toolMsg, err := p.executeSingleTool(context.Background(), toolCall)
if err != nil {
return nil, &schemas.BifrostResponse{}, err
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Execution loses caller context

executeSingleTool is invoked with context.Background(); any timeouts, cancellation, or tracing information carried in the incoming ctx are discarded.

-toolMsg, err := p.executeSingleTool(context.Background(), toolCall)
+toolMsg, err := p.executeSingleTool(*ctx, toolCall)

Preserving the original context avoids hung tool calls when the request is cancelled upstream.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In plugins/mcp/main.go around lines 836 to 840, replace the use of
context.Background() in the call to executeSingleTool with the original context
variable (likely named ctx) passed into the function. This preserves
cancellation, timeout, and tracing information from the caller, preventing hung
tool calls when the request is cancelled upstream.

Comment thread plugins/mcp/main.go
Comment on lines +688 to +692
if client.StdioCommand != nil && client.StdioCommand.Process != nil {
p.logger.Info(fmt.Sprintf(LogPrefix+" Terminating STDIO process: %d", client.StdioCommand.Process.Pid))
client.StdioCommand.Process.Kill()
client.StdioCommand.Wait() // Wait for cleanup
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle errors from process termination

The return values of Process.Kill() and Wait() are ignored. If either fails you may leave zombie processes.

-client.StdioCommand.Process.Kill()
-client.StdioCommand.Wait()
+if err := client.StdioCommand.Process.Kill(); err != nil {
+    p.logger.Warn(fmt.Sprintf(LogPrefix+" failed to kill process %d: %v", client.StdioCommand.Process.Pid, err))
+}
+if err := client.StdioCommand.Wait(); err != nil {
+    p.logger.Warn(fmt.Sprintf(LogPrefix+" process %d did not exit cleanly: %v", client.StdioCommand.Process.Pid, err))
+}

This also satisfies errcheck linter warnings.

🧰 Tools
🪛 golangci-lint (1.64.8)

690-690: Error return value of client.StdioCommand.Process.Kill is not checked

(errcheck)


691-691: Error return value of client.StdioCommand.Wait is not checked

(errcheck)

🤖 Prompt for AI Agents
In plugins/mcp/main.go around lines 688 to 692, the calls to Process.Kill() and
Wait() ignore their returned errors, which can lead to zombie processes if
termination fails. Modify the code to capture and handle errors returned by both
Kill() and Wait(), logging any failures appropriately to ensure proper process
cleanup and satisfy the errcheck linter.

Comment thread plugins/mcp/main.go
Comment on lines +283 to +307
go func() {
if err := p.server.Serve(); err != nil && err != http.ErrServerClosed {
p.logger.Error(fmt.Errorf(LogPrefix+" MCP server error: %w", err))
p.mu.Lock()
p.serverRunning = false
p.mu.Unlock()
}
}()

// Mark server as running
p.serverRunning = true

// Initialize the client connection to the server
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

if _, ok := p.clientMap[BifrostClientKey]; !ok {
return fmt.Errorf("bifrost client not found")
}

_, err := p.clientMap[BifrostClientKey].Conn.Initialize(ctx)
if err != nil {
p.serverRunning = false
return fmt.Errorf("failed to initialize MCP client: %v", err)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Server keeps running when client init fails – shut it down on error

serverRunning is set to true before the client is initialised.
If the Initialize call (lines 303-306) fails, the flag is flipped back to false, but the goroutine started at 283 is still serving, leaving an orphaned HTTP server.

-	go func() {
-		if err := p.server.Serve(); err != nil && err != http.ErrServerClosed {
-			...
-		}
-	}()
-	p.serverRunning = true
+	go func() {
+		if err := p.server.Serve(); err != nil && err != http.ErrServerClosed {
+			p.logger.Error(fmt.Errorf(LogPrefix+" MCP server error: %w", err))
+		}
+	}()
+
+	p.serverRunning = true
 ...
 	if _, ok := p.clientMap[BifrostClientKey]; !ok {
 		return fmt.Errorf("bifrost client not found")
 	}
 
 	_, err := p.clientMap[BifrostClientKey].Conn.Initialize(ctx)
 	if err != nil {
-		p.serverRunning = false
+		p.serverRunning = false
+		_ = p.server.Shutdown(ctx) // ensure goroutine terminates
 		return fmt.Errorf("failed to initialize MCP client: %v", err)
 	}

This prevents a leaked goroutine and a port left listening on failure.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
go func() {
if err := p.server.Serve(); err != nil && err != http.ErrServerClosed {
p.logger.Error(fmt.Errorf(LogPrefix+" MCP server error: %w", err))
p.mu.Lock()
p.serverRunning = false
p.mu.Unlock()
}
}()
// Mark server as running
p.serverRunning = true
// Initialize the client connection to the server
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if _, ok := p.clientMap[BifrostClientKey]; !ok {
return fmt.Errorf("bifrost client not found")
}
_, err := p.clientMap[BifrostClientKey].Conn.Initialize(ctx)
if err != nil {
p.serverRunning = false
return fmt.Errorf("failed to initialize MCP client: %v", err)
}
go func() {
if err := p.server.Serve(); err != nil && err != http.ErrServerClosed {
p.logger.Error(fmt.Errorf(LogPrefix+" MCP server error: %w", err))
}
}()
p.serverRunning = true
// Initialize the client connection to the server
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if _, ok := p.clientMap[BifrostClientKey]; !ok {
return fmt.Errorf("bifrost client not found")
}
_, err := p.clientMap[BifrostClientKey].Conn.Initialize(ctx)
if err != nil {
p.serverRunning = false
_ = p.server.Shutdown(ctx) // ensure the server goroutine exits
return fmt.Errorf("failed to initialize MCP client: %v", err)
}
🤖 Prompt for AI Agents
In plugins/mcp/main.go around lines 283 to 307, the serverRunning flag is set to
true before the client initialization, but if initialization fails, the server
goroutine continues running, causing a resource leak. To fix this, move the
assignment of serverRunning = true to after the client initialization succeeds.
Additionally, if initialization fails, ensure the server is properly shut down
or stopped to prevent the goroutine from leaking and the port from remaining
open.

@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-17-feat_mcp_plugin_added branch from 876358a to 6aacbb1 Compare June 18, 2025 06:52
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 06-17-feat_mcp_plugin_added branch from 6aacbb1 to 881a6be Compare June 18, 2025 06:55
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

♻️ Duplicate comments (8)
plugins/mcp/go.mod (1)

62-62: Local replace blocks external consumers

plugins/mcp/utils.go (1)

196-204: Possible data race on p.clientMap

plugins/mcp/plugin_test.go (1)

174-182: Remove dead code

plugins/mcp/chat/chatbot.go (3)

421-421: Replace deprecated strings.Title.
Use cases.Title from golang.org/x/text/cases as previously suggested.


581-583: Guard against empty history before indexing.
Accessing session.history[0] panics when history is empty; add a length check.


428-435: Handle errors from Cleanup() calls.
Check and log/propagate errors returned by both s.client.Cleanup() and s.mcpPlugin.Cleanup().

plugins/mcp/main.go (2)

837-838: Preserve caller context when executing tools.

executeSingleTool is still invoked with context.Background(), discarding cancellation & deadlines. Pass the incoming context instead:

- toolMsg, err := p.executeSingleTool(context.Background(), toolCall)
+ toolMsg, err := p.executeSingleTool(*ctx, toolCall)

686-692: Handle errors from process termination to avoid zombie processes.

Return values of Process.Kill and Wait remain unchecked; propagate or log them as previously suggested.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6aacbb1 and 881a6be.

⛔ Files ignored due to path filters (1)
  • plugins/mcp/go.sum is excluded by !**/*.sum
📒 Files selected for processing (7)
  • core/schemas/bifrost.go (3 hunks)
  • plugins/mcp/README.md (1 hunks)
  • plugins/mcp/chat/chatbot.go (1 hunks)
  • plugins/mcp/go.mod (1 hunks)
  • plugins/mcp/main.go (1 hunks)
  • plugins/mcp/plugin_test.go (1 hunks)
  • plugins/mcp/utils.go (1 hunks)
🧰 Additional context used
🪛 LanguageTool
plugins/mcp/README.md

[uncategorized] ~170-~170: Loose punctuation mark.
Context: ...ToolExecutionPolicyRequireApproval: Tool requires user approval before exec...

(UNLIKELY_OPENING_PUNCTUATION)


[duplication] ~491-~491: Possible typo: you repeated a word.
Context: ...ry**: Only applies when include list is empty 3. Empty filters: All configured clients are a...

(ENGLISH_WORD_REPEAT_RULE)


[grammar] ~533-~533: Possible subject-verb agreement error detected: Did you mean to use the plural form here?
Context: ...ContextKeyExcludeClients`) - Applied if no include list 3. **Config-level tool whi...

(SUBJECT_NUMBER)


[duplication] ~848-~848: Possible typo: you repeated a word.
Context: ...** - Tool results are sent back to the LLM - LLM synthesizes results into natural langua...

(ENGLISH_WORD_REPEAT_RULE)


[duplication] ~1049-~1049: Possible typo: you repeated a word.
Context: ...ew ToolsToSkip configuration for each client - Client connection issues: Verify external MC...

(ENGLISH_WORD_REPEAT_RULE)

🪛 markdownlint-cli2 (0.17.2)
plugins/mcp/README.md

16-16: Link fragments should be valid
null

(MD051, link-fragments)


303-303: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


335-335: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


813-813: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


825-825: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


834-834: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


842-842: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


861-861: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


877-877: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


886-886: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


897-897: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


986-986: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


999-999: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


1014-1014: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


1028-1028: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


1042-1042: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


1064-1064: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


1076-1076: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

🪛 golangci-lint (1.64.8)
plugins/mcp/chat/chatbot.go

16-16: could not import github.com/maximhq/bifrost/plugins/mcp (-: # github.com/maximhq/bifrost/plugins/mcp
./main.go:605:33: invalid operation: cannot indirect req.MCPTools (variable of type []schemas.Tool)
./main.go:813:24: invalid operation: cannot indirect req.MCPTools (variable of type []schemas.Tool)
./main.go:956:37: cannot use &pendingTools (value of type *[]schemas.PendingMCPTool) as []schemas.PendingMCPTool value in assignment)

(typecheck)


239-239: invalid operation: cannot indirect response.ExtraFields.PendingMCPTools (variable of type []schemas.PendingMCPTool)

(typecheck)


269-269: invalid operation: cannot indirect response.ExtraFields.PendingMCPTools (variable of type []schemas.PendingMCPTool)

(typecheck)


340-340: cannot use &approvedTools (value of type *[]schemas.Tool) as []schemas.Tool value in struct literal

(typecheck)

plugins/mcp/main.go

1-1: : # github.com/maximhq/bifrost/plugins/mcp [github.com/maximhq/bifrost/plugins/mcp.test]
./main.go:605:33: invalid operation: cannot indirect req.MCPTools (variable of type []schemas.Tool)
./main.go:813:24: invalid operation: cannot indirect req.MCPTools (variable of type []schemas.Tool)
./main.go:956:37: cannot use &pendingTools (value of type *[]schemas.PendingMCPTool) as []schemas.PendingMCPTool value in assignment

(typecheck)

🔇 Additional comments (2)
core/schemas/bifrost.go (1)

72-73: Schema additions look good!

The MCP-related schema additions are well-structured:

  • MCPTools in BifrostRequest for approved tools
  • PendingMCPTools in response for tools awaiting approval
  • PendingMCPTool struct properly captures tool metadata

Good choice using plain slices instead of pointer-to-slice.

Also applies to: 413-413, 439-444

plugins/mcp/plugin_test.go (1)

1-684: Comprehensive test coverage!

The tests effectively cover:

  • Local tool registration and execution
  • External MCP server integration (Context7, Maxim)
  • Both safe and agentic execution modes
  • Proper cleanup and error handling

Well-structured test suite.

Comment thread plugins/mcp/go.mod
@@ -0,0 +1,62 @@
module github.com/maximhq/bifrost/plugins/mcp

go 1.24.1
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix invalid Go version.

Go version 1.24.1 is invalid. Go uses two-part version numbers (e.g., 1.21, 1.22, 1.23).

Apply this diff to fix the Go version:

-go 1.24.1
+go 1.23
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
go 1.24.1
go 1.23
🤖 Prompt for AI Agents
In plugins/mcp/go.mod at line 3, the Go version is incorrectly specified as
1.24.1, which is invalid because Go versions use only two parts. Change the
version to a valid two-part version number such as 1.24 or the latest supported
version without the patch number.

Comment thread plugins/mcp/README.md

### Plugin Architecture

```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Add language specifiers to fenced code blocks.

Code blocks should specify a language for proper syntax highlighting.

For the diagram blocks, you can use:

-```
+```text

Also applies to: 335-335

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

303-303: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In plugins/mcp/README.md at lines 303 and 335, the fenced code blocks lack
language specifiers, which are needed for proper syntax highlighting. Update
these code blocks by adding the appropriate language identifier, such as "text"
for diagram blocks, immediately after the opening triple backticks.

Comment thread plugins/mcp/README.md

#### Key Components of Safe Execution

**1. Pending Tools Detection**
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Use proper headings instead of bold emphasis.

Several sections use bold text (text) instead of proper markdown headings.

Convert these to proper headings. For example:

-**1. Pending Tools Detection**
+##### 1. Pending Tools Detection

Also applies to: 825-825, 834-834, 842-842, 861-861, 877-877, 886-886, 897-897

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

813-813: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)

🤖 Prompt for AI Agents
In plugins/mcp/README.md at lines 813, 825, 834, 842, 861, 877, 886, and 897,
replace the bold text used for section titles with proper markdown headings by
prefixing the text with one or more '#' characters according to the heading
level desired. This will improve the document structure and readability.

Comment thread plugins/mcp/README.md
- [API Reference](#api-reference)
- [Advanced Features](#advanced-features)
- [Troubleshooting](#troubleshooting)
- [Contributing](#contributing)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Fix invalid link fragment.

The table of contents links to #contributing but there's no corresponding section in the document.

Either add a Contributing section or remove this link:

-- [Contributing](#contributing)

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

16-16: Link fragments should be valid
null

(MD051, link-fragments)

🤖 Prompt for AI Agents
In plugins/mcp/README.md at line 16, the table of contents contains a link to a
non-existent Contributing section (#contributing). To fix this, either add a
Contributing section with relevant content below the table of contents or remove
the link from the table of contents to avoid broken navigation.

Comment on lines +238 to +241
// Check if response contains pending tools (requiring user approval)
if response.ExtraFields.PendingMCPTools != nil && len(*response.ExtraFields.PendingMCPTools) > 0 {
return s.handlePendingTools(response)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix invalid pointer dereference – PendingMCPTools is a slice, not *[].

Dereferencing response.ExtraFields.PendingMCPTools leads to a compile-time “invalid operation: cannot indirect” error.
Use the slice directly:

-if response.ExtraFields.PendingMCPTools != nil && len(*response.ExtraFields.PendingMCPTools) > 0 {
+if len(response.ExtraFields.PendingMCPTools) > 0 {
     return s.handlePendingTools(response)
 }
...
-pendingTools := *response.ExtraFields.PendingMCPTools
+pendingTools := response.ExtraFields.PendingMCPTools

Also applies to: 269-270

🧰 Tools
🪛 golangci-lint (1.64.8)

239-239: invalid operation: cannot indirect response.ExtraFields.PendingMCPTools (variable of type []schemas.PendingMCPTool)

(typecheck)

🤖 Prompt for AI Agents
In plugins/mcp/chat/chatbot.go around lines 238 to 241 and also lines 269 to
270, the code incorrectly dereferences PendingMCPTools as a pointer to a slice,
but it is already a slice type. Remove the pointer dereference operator (*) and
use PendingMCPTools directly when checking its length and nil status to fix the
invalid pointer dereference compile error.

Comment on lines +333 to +341
// Create new request with approved tools
approvedRequest := &schemas.BifrostRequest{
Provider: s.config.Provider,
Model: s.config.Model,
Input: schemas.RequestInput{
ChatCompletionInput: &conversationForApproval,
},
MCPTools: &approvedTools,
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove superfluous &MCPTools field expects a slice value.

Passing &approvedTools (pointer) breaks type-checking. Assign the slice directly:

- approvedRequest := &schemas.BifrostRequest{
...
-     MCPTools: &approvedTools,
+ approvedRequest := &schemas.BifrostRequest{
...
+     MCPTools: approvedTools,
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Create new request with approved tools
approvedRequest := &schemas.BifrostRequest{
Provider: s.config.Provider,
Model: s.config.Model,
Input: schemas.RequestInput{
ChatCompletionInput: &conversationForApproval,
},
MCPTools: &approvedTools,
}
// Create new request with approved tools
approvedRequest := &schemas.BifrostRequest{
Provider: s.config.Provider,
Model: s.config.Model,
Input: schemas.RequestInput{
ChatCompletionInput: &conversationForApproval,
},
MCPTools: approvedTools,
}
🧰 Tools
🪛 golangci-lint (1.64.8)

340-340: cannot use &approvedTools (value of type *[]schemas.Tool) as []schemas.Tool value in struct literal

(typecheck)

🤖 Prompt for AI Agents
In plugins/mcp/chat/chatbot.go around lines 333 to 341, the MCPTools field is
incorrectly assigned a pointer to the approvedTools slice using &approvedTools,
but it expects a slice value directly. Remove the ampersand and assign
approvedTools directly to MCPTools to fix the type mismatch.

Comment thread plugins/mcp/main.go
Comment on lines +603 to +607
func (p *MCPPlugin) PreHook(ctx *context.Context, req *schemas.BifrostRequest) (*schemas.BifrostRequest, *schemas.BifrostResponse, error) {
// Check if this is an approved tool execution request
if req.MCPTools != nil && len(*req.MCPTools) > 0 && req.Input.ChatCompletionInput != nil {
return p.handleApprovedTools(ctx, req)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct slice vs pointer misuse for MCPTools and PendingMCPTools.

All three spots treat slice fields as *[], causing type-check failures.

- if req.MCPTools != nil && len(*req.MCPTools) > 0 {
+ if len(req.MCPTools) > 0 {
     return p.handleApprovedTools(ctx, req)
 }

 ...

- approvedTools := make(map[string]schemas.Tool)
- for _, tool := range *req.MCPTools {
+ approvedTools := make(map[string]schemas.Tool)
+ for _, tool := range req.MCPTools {
     approvedTools[tool.Function.Name] = tool
 }

 ...

- res.ExtraFields.PendingMCPTools = &pendingTools
+ res.ExtraFields.PendingMCPTools = pendingTools

These changes resolve the invalid operation: cannot indirect compile errors reported by golangci-lint.

Also applies to: 811-816, 954-957

🤖 Prompt for AI Agents
In plugins/mcp/main.go around lines 603 to 607, the code incorrectly treats the
MCPTools slice as a pointer to a slice (*[]), causing compile errors. To fix
this, change all occurrences of MCPTools and PendingMCPTools from pointer types
to direct slice types ([]), and update the code to access them accordingly
without dereferencing pointers. Apply the same correction to the code sections
at lines 811-816 and 954-957 to ensure consistent and correct slice usage.

Comment thread plugins/mcp/main.go
Comment on lines +292 to +306
// Mark server as running
p.serverRunning = true

// Initialize the client connection to the server
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

if _, ok := p.clientMap[BifrostClientKey]; !ok {
return fmt.Errorf("bifrost client not found")
}

_, err := p.clientMap[BifrostClientKey].Conn.Initialize(ctx)
if err != nil {
p.serverRunning = false
return fmt.Errorf("failed to initialize MCP client: %v", err)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid server goroutine leak on client-init failure.

serverRunning is set to true before client initialization; if initialization fails the HTTP server continues running. Move the flag assignment after successful init and call p.server.Shutdown(ctx) on error.

🤖 Prompt for AI Agents
In plugins/mcp/main.go around lines 292 to 306, the p.serverRunning flag is set
to true before client initialization, which can cause the server to keep running
if initialization fails. To fix this, move the assignment of p.serverRunning =
true to after the client initialization succeeds, and on initialization error,
call p.server.Shutdown(ctx) to properly stop the server and avoid goroutine
leaks.

@akshaydeo akshaydeo deleted the 06-17-feat_mcp_plugin_added branch August 31, 2025 17:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant