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
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ pkill -HUP kubernetes-mcp-server

### MCP Prompts

The server supports MCP prompts for workflow templates. Define custom prompts in `config.toml`:
1. The server supports MCP prompts for workflow templates. Define custom prompts in `config.toml`:

```toml
[[prompts]]
Expand All @@ -311,6 +311,8 @@ role = "user"
content = "Help me with {{resource_name}}"
```

2. Toolset prompts implemented by toolset developers

See docs/PROMPTS.md for detailed documentation.

## 🛠️ Tools and Functionalities <a id="tools-and-functionalities"></a>
Expand Down
52 changes: 51 additions & 1 deletion docs/PROMPTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,54 @@ Use `{{argument_name}}` placeholders in message content. The template engine rep

## Configuration File Location

Place your prompts in the `config.toml` file used by the MCP server. Specify the config file path using the `--config` flag when starting the server.
Place your prompts in the `config.toml` file used by the MCP server. Specify the config file path using the `--config` flag when starting the server.

## Toolset Prompts

Toolsets can provide built-in prompts by implementing the `GetPrompts()` method. This allows toolset developers to ship workflow templates alongside their tools.

### Implementing Toolset Prompts

```go
func (t *MyToolset) GetPrompts() []api.ServerPrompt {
return []api.ServerPrompt{
{
Prompt: api.Prompt{
Name: "my-workflow",
Description: "Custom workflow for my toolset",
Arguments: []api.PromptArgument{
{
Name: "namespace",
Description: "Target namespace",
Required: true,
},
},
},
Handler: func(params api.PromptHandlerParams) (*api.PromptCallResult, error) {
args := params.GetArguments()
namespace := args["namespace"]

// Build messages dynamically based on arguments
messages := []api.PromptMessage{
{
Role: "user",
Content: api.PromptContent{
Type: "text",
Text: fmt.Sprintf("Help me with namespace: %s", namespace),
},
},
}

return api.NewPromptCallResult("Workflow description", messages, nil), nil
},
},
}
}
```

### Prompt Merging

When both toolset and config prompts exist:
- Config-defined prompts **override** toolset prompts with the same name
- This allows administrators to customize built-in workflows
- Prompts with unique names from both sources are available
3 changes: 3 additions & 0 deletions pkg/api/toolsets.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ type Toolset interface {
// Will be used to generate documentation and help text.
GetDescription() string
GetTools(o Openshift) []ServerTool
// GetPrompts returns the prompts provided by this toolset.
// Returns nil if the toolset doesn't provide any prompts.
GetPrompts() []ServerPrompt
}

type ToolCallRequest interface {
Expand Down
25 changes: 22 additions & 3 deletions pkg/mcp/mcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,22 @@ func (s *Server) reloadToolsets() error {
// Track previously enabled prompts
previousPrompts := s.enabledPrompts

// Build and register prompts from all toolsets
applicablePrompts := make([]api.ServerPrompt, 0)
s.enabledPrompts = make([]string, 0)

// Load embedded toolset prompts
for _, toolset := range s.configuration.Toolsets() {
prompts := toolset.GetPrompts()
if prompts == nil {
continue
}
for _, prompt := range prompts {
applicablePrompts = append(applicablePrompts, prompt)
s.enabledPrompts = append(s.enabledPrompts, prompt.Prompt.Name)
}
}

// Load config prompts into registry
prompts.Clear()
if s.configuration.HasPrompts() {
Expand All @@ -180,9 +196,12 @@ func (s *Server) reloadToolsets() error {
// Get prompts from registry
configPrompts := prompts.ConfigPrompts()

// Merge: config prompts override embedded prompts with same name
applicablePrompts = mergePrompts(applicablePrompts, configPrompts)

// Update enabled prompts list
s.enabledPrompts = make([]string, 0)
for _, prompt := range configPrompts {
for _, prompt := range applicablePrompts {
s.enabledPrompts = append(s.enabledPrompts, prompt.Prompt.Name)
}

Expand All @@ -195,8 +214,8 @@ func (s *Server) reloadToolsets() error {
}
s.server.RemovePrompts(promptsToRemove...)

// Register all config prompts
for _, prompt := range configPrompts {
// Register all applicable prompts
for _, prompt := range applicablePrompts {
mcpPrompt, promptHandler, err := ServerPromptToGoSdkPrompt(s, prompt)
if err != nil {
return fmt.Errorf("failed to convert prompt %s: %v", prompt.Prompt.Name, err)
Expand Down
Loading