-
Notifications
You must be signed in to change notification settings - Fork 363
Decouple KMCP integration #822
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Decouple KMCP integration #822
Conversation
| --set providers.anthropic.apiKey=$(ANTHROPIC_API_KEY) \ | ||
| --set providers.default=$(KAGENT_DEFAULT_MODEL_PROVIDER) \ | ||
| --set kmcp.enabled=$(KMCP_ENABLED) \ | ||
| --set kmcp.image.tag=$(KMCP_VERSION) \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only required due to kagent-dev/kmcp#74
| # ============================================================================== | ||
|
|
||
| kmcp: | ||
| enabled: true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Related discussion here. Have used kmcp.enabled here instead of something like integrations.kmcp.enabled, but happy to rework things if desired.
| }). | ||
| For(&v1alpha1.MCPServer{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). | ||
| Named("toolserver"). | ||
| Named("mcpserver"). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change wasn't possible previously (see #757) because the code was already running a controller named mcpserver. I.e. this code was running 2 separate controllers reconciling the same resources. 😧
|
Just realised that I haven't even considered the UI implications of this change, and I'm going to go ahead and guess that there's stuff that needs to be handled there as well. Will try to take a look ASA,P but feel free to point me in the right direction if you like 🙏 |
8b203cb to
4c41c95
Compare
| } | ||
|
|
||
| // HandleListToolServerTypes handles GET /api/toolservertypes requests | ||
| func (h *ToolServerTypesHandler) HandleListToolServerTypes(w ErrorResponseWriter, r *http.Request) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not 100% sure about this approach. I.e., adding an API endpoint and then calling that via the UI. But, it seemed to fit best with the current usage patterns between UI<->API vs. e.g., having the UI query the cluster directly. It may also prove a useful extension point if other "tool server" types/integrations are added later on. 🤷
More than happy to switch approach if anyone has any thoughts.
| URL | ||
| </TabsTrigger> | ||
| {renderTabTrigger( | ||
| "command", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left the tabs as "url" and "command" for now although I can't help but feel like this dialog/the tabs need a bit of an overhaul given that ultimately, url = RemoteMCPServer and command = MCPServer (which incidentally now needs to support a far broader range of cmd than just npx and uvx).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. As a start, it might make sense to rename them RemoteMCPServer and MCPServer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you want to do that as part of this PR or as a separate one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
up to you
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's deal with it separately since it will be needed regardless of what happens with this PR.
In the UI, we'll have to block the creation of tools that use MCPServer resource (the stdio ones, the sse use the RemoteMCPServer crds). |
|
Had a bit of a discussion with @EItanya on Discord regarding this PR, and I was made aware that there is some history as to why things were implemented the way they have been. We will discuss further at the next contributors meeting. In the meantime, I will open for review and try to address any comments/feedback that are received between now and then. That should also hopefully provide time for the comments here and here to be addressed. |
4c41c95 to
7d7f79a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR decouples the KMCP (Kubernetes MCP) integration from kagent by making it an optional, standalone component. Instead of the hard-coupled approach where kagent-controller directly runs the kmcp controller, KMCP is now deployed as a separate pod that can be managed independently.
Key changes include:
- Making KMCP an optional Helm dependency with configurable enablement
- Adding dynamic tool server type detection based on available CRDs
- Removing direct KMCP controller integration from kagent's main application
Reviewed Changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
ui/src/components/AddServerDialog.tsx |
Updates UI to conditionally show MCPServer tab based on KMCP availability |
ui/src/app/servers/page.tsx |
Fetches supported tool server types to pass to the dialog |
ui/src/app/actions/servers.ts |
Adds API call to retrieve available tool server types |
helm/kagent/values.yaml |
Adds KMCP configuration section with enabled flag |
helm/kagent/templates/NOTES.txt |
Conditionally displays KMCP controller in deployment notes |
helm/kagent/Chart-template.yaml |
Adds KMCP as conditional Helm dependency |
helm/kagent-crds/values.yaml |
Adds KMCP enabled configuration |
helm/kagent-crds/Chart-template.yaml |
Makes KMCP CRDs conditional |
go/pkg/app/app.go |
Removes direct KMCP controller initialization |
go/internal/httpserver/server.go |
Adds route for tool server types endpoint |
go/internal/httpserver/handlers/toolservertypes_test.go |
Tests for tool server types handler |
go/internal/httpserver/handlers/toolservertypes.go |
Implements handler for listing supported tool server types |
go/internal/httpserver/handlers/toolservers.go |
Updates to use shared tool server types and validation |
go/internal/httpserver/handlers/handlers.go |
Registers tool server types handler |
go/internal/controller/mcp_server_controller.go |
Adds CRD detection to conditionally enable controller |
go/internal/controller/agent_controller.go |
Makes MCPServer watching conditional on CRD availability |
Makefile |
Adds KMCP configuration variables and build parameters |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| export function AddServerDialog({ open, onOpenChange, onAddServer, onError }: AddServerDialogProps) { | ||
| const [activeTab, setActiveTab] = useState<"command" | "url">("command"); | ||
| export function AddServerDialog({ open, supportedToolServerTypes, onOpenChange, onAddServer, onError }: AddServerDialogProps) { | ||
| const [activeTab, setActiveTab] = useState<"command" | "url">("url"); |
Copilot
AI
Aug 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default tab has been changed from 'command' to 'url', but this change appears to be unrelated to the KMCP decoupling purpose of this PR. Consider whether this change should be in a separate commit or if it's intentional for this feature.
| const [activeTab, setActiveTab] = useState<"command" | "url">("url"); | |
| const [activeTab, setActiveTab] = useState<"command" | "url">("command"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was intentionally changed to ensure that the default tab is always enabled.
950561e to
913633e
Compare
031aea7 to
d6a925b
Compare
Signed-off-by: Brian Fox <878612+onematchfox@users.noreply.github.com>
Signed-off-by: Brian Fox <878612+onematchfox@users.noreply.github.com>
Signed-off-by: Brian Fox <878612+onematchfox@users.noreply.github.com>
Signed-off-by: Brian Fox <878612+onematchfox@users.noreply.github.com>
Signed-off-by: Brian Fox <878612+onematchfox@users.noreply.github.com>
Signed-off-by: Brian Fox <878612+onematchfox@users.noreply.github.com>
Signed-off-by: Brian Fox <878612+onematchfox@users.noreply.github.com>
…not supported Signed-off-by: Brian Fox <878612+onematchfox@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Brian Fox <878612+onematchfox@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Brian Fox <878612+onematchfox@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Brian Fox <878612+onematchfox@users.noreply.github.com>
Signed-off-by: Brian Fox <878612+onematchfox@users.noreply.github.com>
d6a925b to
fadb976
Compare
|
Superseded by #939 |
Signed-off-by: Eitan Yarmush <eitan.yarmush@solo.io>
Signed-off-by: Eitan Yarmush <eitan.yarmush@solo.io>
Signed-off-by: Eitan Yarmush <eitan.yarmush@solo.io>
Signed-off-by: Eitan Yarmush <eitan.yarmush@solo.io>
Signed-off-by: Eitan Yarmush <eitan.yarmush@solo.io>
Signed-off-by: Eitan Yarmush <eitan.yarmush@solo.io>
Signed-off-by: Eitan Yarmush <eitan.yarmush@solo.io>
Signed-off-by: Eitan Yarmush <eitan.yarmush@solo.io>
Signed-off-by: Eitan Yarmush <eitan.yarmush@solo.io>
This PR decouples the kmcp integration, ensuring that kmcp is a separate, optional component that can be deployed alongside, and independently of, kagent. The kmcp integration remains fully in place and, by default, is enabled as is the case in the current version. The main difference being that, instead of the hard-coupling that is in place today, where the `kagent-controller` [runs the kmcp controller](https://github.com/kagent-dev/kagent/blob/bdbde1109e7fd214a2536940043a6afef8e07c92/go/pkg/app/app.go#L323-L329), the kmcp controller is deployed as a separate, standalone component/pod. This is important for numerous reasons... not least of which is the fact that these are separate projects with separate lifecycles and one should be able to manage them independently if desired. Notes: - The `KMCP_ENABLED` flag (and associated Helm setting) merely determines whether the kmcp controller and associated CRDs are installed as part of the `kagent` and `kagent-crds`. Users can set this to false and install kmcp independently of the `kagent` Helm chart, and the controller will still work with any additional configuration. The controller simply enables/disables reconciliation of `MCPServer` resources based on whether or not the CRD is present in the cluster. If desired, in the future, a flag could be added to the controller to disable/enable the integration manually, but for now, I think that's overkill - the (automatic) integration is great 💌 - The default setup will currently break if `KMCP_ENABLED` is set to `false`. This is due to the use of the `MCPServer` for the [`grafana` MCP](https://github.com/kagent-dev/kagent/blob/bdbde1109e7fd214a2536940043a6afef8e07c92/helm/agents/observability/templates/mcp-server.yaml#L12-L31) as part of the `observability` agent. This is addressed as part of kagent-dev#812, which switches to using `RemoteMCPServer`, ensuring that all built-in components/agents/tools work regardless of whether KMCP is enabled. --------- Signed-off-by: Brian Fox <878612+onematchfox@users.noreply.github.com> Signed-off-by: Eitan Yarmush <eitan.yarmush@solo.io> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Eitan Yarmush <eitan.yarmush@solo.io> Signed-off-by: killjoycircuit <rutujdhawale@gmail.com>

This PR decouples the kmcp integration, ensuring that kmcp is a separate, optional component that can be deployed alongside, and independently of, kagent.
The kmcp integration remains fully in place and, by default, is enabled as is the case in the current version. The main difference being that, instead of the hard-coupling that is in place today, where the
kagent-controllerruns the kmcp controller, the kmcp controller is deployed as a separate, standalone component/pod. This is important for numerous reasons... not least of which is the fact that these are separate projects with separate lifecycles and one should be able to manage them independently if desired.Notes:
KMCP_ENABLEDflag (and associated Helm setting) merely determines whether the kmcp controller and associated CRDs are installed as part of thekagentandkagent-crds. Users can set this to false and install kmcp independently of thekagentHelm chart, and the controller will still work with any additional configuration. The controller simply enables/disables reconciliation ofMCPServerresources based on whether or not the CRD is present in the cluster. If desired, in the future, a flag could be added to the controller to disable/enable the integration manually, but for now, I think that's overkill - the (automatic) integration is great 💌KMCP_ENABLEDis set tofalse. This is due to the use of theMCPServerfor thegrafanaMCP as part of theobservabilityagent. This is addressed as part of feat(helm): extract Helm chart for Grafana MCP #812, which switches to usingRemoteMCPServer, ensuring that all built-in components/agents/tools work regardless of whether KMCP is enabled.