Skip to content

Conversation

@nader-ziada
Copy link
Collaborator

@nader-ziada nader-ziada commented Dec 8, 2025

This PR replaces: #510

Fixes: #226

Enable administrators to define custom MCP prompts in config.toml using template substitution with {{argument}} syntax.

Features:

  • PromptLoader for loading prompts from TOML config
  • Core prompt types (ServerPrompt, Prompt, PromptArgument, PromptMessage)
  • Template argument substitution with {{variable}} syntax
  • Required argument validation
  • Integration with MCP server to register and serve config prompts

@nader-ziada nader-ziada changed the title add configurable prompts support feat: add configurable prompts support Dec 8, 2025
@nader-ziada nader-ziada force-pushed the 1-configurable-prompts branch 2 times, most recently from aaac007 to 7994bf0 Compare December 9, 2025 19:49
@nader-ziada
Copy link
Collaborator Author

Spec Proposed:

type Prompt struct {
    Messages    []PromptMessage  // Static templates
    GetMessages func(arguments map[string]string) []PromptMessage  // Dynamic function
}

What Was Actually Implemented:

type Prompt struct {
    Templates   []PromptTemplate  // Static templates for TOML parsing
}

type ServerPrompt struct {
    Prompt  Prompt
    Handler PromptHandlerFunc  // Dynamic handler function
}

why:
The handler pattern provides separation of concerns by keeping Prompt as just data (serializable to/from TOML) while ServerPrompt adds behaviour through the handler function. This eliminates serialization issues that would require json:"-" tags on function fields or special handling during TOML encoding. Both config and toolset prompts use the same unified PromptHandlerFunc interface, and handlers receive context through PromptHandlerParams

Config prompts: PromptLoader.createHandler() generates a handler that performs {{argument}} substitution on the Templates field at runtime

(For Later): Toolset prompts: Implementers provide a custom handler function directly in ServerPrompt.Handler

@manusa manusa self-assigned this Dec 10, 2025
@manusa
Copy link
Member

manusa commented Dec 10, 2025

Thanks for taking care of this.

I still find that the API is getting far too complicated and complex for something that should be simpler and more intuitive.
This is also revealing a nasty coupling between the config and kubernetes module.

Let me create a PR to your branch with some proposed changes so that we can better discuss tradeoffs.

Enable administrators to define custom MCP prompts in config.toml
using template substitution with {{argument}} syntax.

- PromptLoader for loading prompts from TOML config
- Core prompt types (ServerPrompt, Prompt, PromptArgument, PromptMessage)
- Template argument substitution with {{variable}} syntax
- Required argument validation
- Integration with MCP server to register and serve config prompts

Signed-off-by: Nader Ziada <[email protected]>
@nader-ziada nader-ziada force-pushed the 1-configurable-prompts branch from 99858f9 to 7dcf0a7 Compare December 10, 2025 20:22
@nader-ziada
Copy link
Collaborator Author

@manusa ready for review

Comment on lines +211 to +232
// mergePrompts merges two slices of prompts, with prompts in override taking precedence
// over prompts in base when they have the same name
func mergePrompts(base, override []api.ServerPrompt) []api.ServerPrompt {
// Create a map of override prompts by name for quick lookup
overrideMap := make(map[string]api.ServerPrompt)
for _, prompt := range override {
overrideMap[prompt.Prompt.Name] = prompt
}

// Build result: start with base prompts, skipping any that are overridden
result := make([]api.ServerPrompt, 0, len(base)+len(override))
for _, prompt := range base {
if _, exists := overrideMap[prompt.Prompt.Name]; !exists {
result = append(result, prompt)
}
}

// Add all override prompts
result = append(result, override...)

return result
}
Copy link
Member

Choose a reason for hiding this comment

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

I believe this is something you'll be using in the follow-up for #556

Comment on lines +31 to +35
Name string `yaml:"name" json:"name" toml:"name"`
Title string `yaml:"title,omitempty" json:"title,omitempty" toml:"title,omitempty"`
Description string `yaml:"description,omitempty" json:"description,omitempty" toml:"description,omitempty"`
Arguments []PromptArgument `yaml:"arguments,omitempty" json:"arguments,omitempty" toml:"arguments,omitempty"`
Templates []PromptTemplate `yaml:"messages,omitempty" json:"messages,omitempty" toml:"messages,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure we need the YAML tags or the yaml serialization tests.

Maybe this is a misunderstanding because the issue (#226) explicitly mentions yaml.
I believe I included this because the former PR had the yaml tags.

From my perspective there's no need for yaml since the prompts will be provided by the toml configuration and I don't believe they'll be marshalled to yaml from the MCP layer.

If you don't see any reason to include those, maybe we can simply remove the yaml layer.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

i can remove this in a follow up PR

Comment on lines +77 to +82
// Prompt configuration
// Raw TOML primitive for prompt definitions, parsed later
// Note: Uses toml:"-" because Primitive can't be encoded, only decoded
Prompts toml.Primitive `toml:"-"`
promptsDefined bool // Internal: tracks if prompts were defined in config
promptsMetadata toml.MetaData // Internal: metadata for prompts decoding
Copy link
Member

Choose a reason for hiding this comment

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

I don't quite grasp the limitation here.

I think it would be much clearer if StaticConfig only defined a Prompts []api.Prompt field.

I'll look into this because I'm sure I might be missing something, but the end goal would be that config-wise we didn't have to iterate over the prompts.

Copy link
Member

Choose a reason for hiding this comment

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

OK; I see we're still having problems with the cyclic dependencies and the kubernetes package. I'll try to properly address this, creating an issue now for tracking.

Copy link
Member

Choose a reason for hiding this comment

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

@manusa manusa changed the title feat: add configurable prompts support feat(config): add configurable prompts support Dec 11, 2025
@manusa manusa added this to the 0.1.0 milestone Dec 11, 2025
Copy link
Member

@manusa manusa left a comment

Choose a reason for hiding this comment

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

LGTM, thx!

Thx for your patience too 😅

@manusa manusa merged commit 5623684 into containers:main Dec 11, 2025
6 checks passed
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.

[CONFIG] Configurable prompt templates

2 participants