Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions cmd/thv-operator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,125 @@ kubectl describe mcpserver <name>
| `permissionProfile` | Permission profile configuration | No | - |
| `tools` | Allow-list filter on the list of tools | No | - |

### Kagent Integration

The ToolHive operator supports optional integration with [kagent](https://kagent.dev), allowing kagent agents to discover and use MCP servers managed by ToolHive. When enabled, the operator automatically creates kagent resources that reference the ToolHive-managed MCP servers.

The integration supports both:
- **kagent v1alpha1**: Creates `ToolServer` resources
- **kagent v1alpha2**: Creates `RemoteMCPServer` resources (when available)

The operator automatically detects which kagent API version is available in your cluster and creates the appropriate resources.

#### Enabling Kagent Integration

To enable kagent integration, set the following Helm value when installing the operator:

```bash
helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \
--set kagentIntegration.enabled=true \
-n toolhive-system --create-namespace
```

Or add it to your values file:

```yaml
kagentIntegration:
enabled: true
```

#### Configuration Options

You can control the kagent API version preference via environment variable:

```yaml
# In your values file
kagentIntegration:
enabled: true
apiVersion: v1alpha2 # Optional: prefer v1alpha2 when available (defaults to v1alpha1)
```

This sets the `KAGENT_API_VERSION` environment variable in the operator deployment.

#### How It Works

When kagent integration is enabled:

1. The operator detects which kagent API version is available in your cluster
2. For each ToolHive MCPServer resource created, the operator automatically creates:
- A kagent `ToolServer` resource (v1alpha1), OR
- A kagent `RemoteMCPServer` resource (v1alpha2)
3. The kagent resource references the ToolHive-managed MCP server service URL
4. The resource is owned by the MCPServer, ensuring it's deleted when the MCPServer is removed
5. Kagent agents can then discover and use these resources to access the MCP servers

The kagent resources are created with:
- Name: `toolhive-<mcpserver-name>`
- Namespace: Same as the MCPServer
- Transport configuration:
- v1alpha1: Mapped to config types (sse → sse, streamable-http → streamableHttp, stdio → sse)
- v1alpha2: Mapped to protocols (sse → SSE, streamable-http → STREAMABLE_HTTP, stdio → SSE)
- Service URL: Points to the ToolHive proxy service

#### Requirements

- Kagent must be installed in your cluster (either v1alpha1 or v1alpha2)
- The operator needs permissions to manage kagent resources (automatically configured when integration is enabled)

#### Example

When you create a ToolHive MCPServer:

```yaml
apiVersion: toolhive.stacklok.dev/v1alpha1
kind: MCPServer
metadata:
name: github
namespace: toolhive-system
spec:
image: ghcr.io/github/github-mcp-server
Copy link
Collaborator

Choose a reason for hiding this comment

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

Will this work without the secret?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

it won't. Let me update the example

transport: sse
port: 8080
```

With kagent integration enabled, the operator automatically creates one of the following:

**For kagent v1alpha1:**
```yaml
apiVersion: kagent.dev/v1alpha1
kind: ToolServer
metadata:
name: toolhive-github
namespace: toolhive-system
labels:
toolhive.stacklok.dev/managed-by: toolhive-operator
toolhive.stacklok.dev/mcpserver: github
spec:
description: "ToolHive MCP Server: github"
config:
type: sse
sse:
url: http://mcp-github-proxy.toolhive-system.svc.cluster.local:8080
```

**For kagent v1alpha2:**
```yaml
apiVersion: kagent.dev/v1alpha2
kind: RemoteMCPServer
metadata:
name: toolhive-github
namespace: toolhive-system
labels:
toolhive.stacklok.dev/managed-by: toolhive-operator
toolhive.stacklok.dev/mcpserver: github
spec:
description: "ToolHive MCP Server: github"
url: http://mcp-github-proxy.toolhive-system.svc.cluster.local:8080
protocol: SSE
```

Kagent agents can then reference these resources to use the GitHub MCP server in their workflows.

### Permission Profiles

Permission profiles can be configured in two ways:
Expand Down
15 changes: 15 additions & 0 deletions cmd/thv-operator/controllers/mcpserver_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,13 @@ func (r *MCPServerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
}
}

// Ensure kagent ToolServer resource if integration is enabled
if err := r.ensureKagentToolServer(ctx, mcpServer); err != nil {
ctxLogger.Error(err, "Failed to ensure kagent ToolServer")
// Log the error but don't fail the reconciliation
// This allows the ToolHive MCPServer to work even if kagent integration fails
}

// Check if the deployment spec changed
if deploymentNeedsUpdate(deployment, mcpServer) {
// Update the deployment
Expand Down Expand Up @@ -841,6 +848,14 @@ func (r *MCPServerReconciler) finalizeMCPServer(ctx context.Context, m *mcpv1alp
} else if !errors.IsNotFound(err) {
return fmt.Errorf("failed to check Service %s: %w", serviceName, err)
}

// Step 4: Delete associated kagent ToolServer if it exists
// This should be handled by owner references, but we explicitly delete for safety
if err := r.deleteKagentToolServer(ctx, m); err != nil {
// Log the error but don't fail the finalization
ctxLogger.Error(err, "Failed to delete kagent ToolServer during finalization")
}

// The owner references will automatically delete the deployment and service
// when the MCPServer is deleted, so we don't need to do anything here.
return nil
Expand Down
Loading
Loading