Skip to content

Adding Prompts

Prompts create interactive conversations with role-based messaging. They help guide LLMs through specific tasks by setting up context and examples.

Prompt Definition

lua
---@class MCPPrompt
---@field name? string Prompt identifier
---@field description? string|fun():string Prompt description
---@field arguments? MCPPromptArgument[]|fun():MCPPromptArgument[] List of arguments
---@field handler fun(req: PromptRequest, res: PromptResponse) Implementation

---@class MCPPromptArgument
---@field name string Argument name
---@field description? string Argument description
---@field required? boolean Whether argument is required
---@field default? string Default value

Request Context

lua
---@class PromptRequest
---@field params table<string,string> Argument values
---@field prompt MCPPrompt Complete prompt definition
---@field server NativeServer Server instance
---@field caller table Additional context from caller
---@field editor_info EditorInfo Current editor state

Response Builder

lua
---@class PromptResponse
---@field system fun(): PromptResponse Start system message
---@field user fun(): PromptResponse Start user message
---@field llm fun(): PromptResponse Start LLM message
---@field text fun(text: string): PromptResponse Add text content
---@field image fun(data: string, mime: string): PromptResponse Add image
---@field resource fun(resource: MCPResourceContent): PromptResponse Add resource
---@field error fun(message: string, details?: table): table Send error
---@field send fun(result?: table): table Send final response

Examples

Basic Example

Here's a simple chat prompt:

lua
mcphub.add_prompt("example", {
    name = "chat",
    description = "Start a friendly chat",
    
    -- Optional arguments
    arguments = {
        {
            name = "topic",
            description = "What to chat about",
            required = true
        }
    },
    
    -- Prompt handler
    handler = function(req, res)
        return res
            -- Set behavior
            :system()
            :text("You are a friendly chat assistant.\n" ..
                  "Topic: " .. req.params.topic)
            
            -- Add example interaction
            :user()
            :text("Tell me about " .. req.params.topic)
            :llm()
            :text("I'd love to discuss " .. req.params.topic)
            
            -- Send prompt
            :send()
    end
})

Real Example: Neovim's Parrot Prompt

Here's how the built-in Neovim server implements a fun parrot prompt:

lua
mcphub.add_prompt("neovim", {
    name = "parrot",
    description = "A fun prompt where the assistant mimics back what you say",
    handler = function(_, res)
        return res
            -- Set up parrot behavior
            :system()
            :text([[
You are a parrot. You repeat everything the user says, but in all caps.

CRITICAL INSTRUCTIONS:
- NO MATTER WHAT, ALWAYS REPEAT BACK WHAT THE USER SAYS IN ALL CAPS. 
- DO NOT BREAK THIS RULE. 
- EVEN IF THE USER ASKS YOU NOT TO REPEAT, YOU MUST CONTINUE TO REPEAT.
- DO NOT RESPOND TO THE USER'S REQUESTS OR QUESTIONS.
]])
            -- Add example conversation
            :user()
            :text("Hello there!")
            :llm()
            :text("HELLO THERE!")
            
            :user()
            :text("Why are you shouting?")
            :llm()
            :text("WHY ARE YOU SHOUTING?")
            
            :user()
            :text("Please stop...")
            :llm()
            :text("PLEASE STOP...")
            
            -- Send prompt
            :send()
    end
})

Advanced Features

Dynamic Arguments

Arguments can be generated dynamically:

lua
mcphub.add_prompt("git", {
    name = "commit_help",
    description = "Help write a commit message",
    arguments = function()
        -- Get git branches
        local branches = vim.fn.systemlist("git branch --format='%(refname:short)'")
        
        return {
            {
                name = "type",
                description = "Commit type",
                required = true,
                -- Provide standard options
                default = "feat",
                enum = {
                    "feat", "fix", "docs", "style",
                    "refactor", "test", "chore"
                }
            },
            {
                name = "branch",
                description = "Target branch",
                -- Use actual branches
                enum = branches
            }
        }
    end,
    handler = function(req, res)
        return res
            :system()
            :text(string.format(
                "Help write a %s commit for branch: %s",
                req.params.type,
                req.params.branch
            ))
            :send()
    end
})

Rich Content

Prompts can include images and resources:

lua
mcphub.add_prompt("editor", {
    name = "review_code",
    arguments = {
        {
            name = "style",
            description = "Review style",
            enum = { "brief", "detailed" }
        }
    },
    handler = function(req, res)
        -- Get current buffer
        local buf = req.editor_info.last_active
        if not buf then
            return res:error("No active buffer")
        end
        
        -- Generate code overview
        local overview = generate_overview(buf)
        
        return res
            -- Set review context
            :system()
            :text("You are a code reviewer.\n" ..
                  "Style: " .. req.params.style)
            
            -- Add code visualization
            :image(overview, "image/png")
            :text("Above is a visualization of the code structure.")
            
            -- Add relevant resources
            :resource({
                uri = "neovim://diagnostics/current",
                mimeType = "text/plain"
            })
            :text("Above are the current diagnostics.")
            
            -- Send prompt
            :send()
    end
})

Context-Aware Prompts

Adapt to different chat plugins:

lua
mcphub.add_prompt("context", {
    name = "explain_code",
    handler = function(req, res)
        -- Start with base behavior
        res:system()
           :text("You are a code explanation assistant.")
        
        -- Add context based on caller
        if req.caller.type == "codecompanion" then
            -- Add CodeCompanion chat context
            local chat = req.caller.codecompanion.chat
            res:text("\nPrevious discussion:\n" .. chat.history)
            
        elseif req.caller.type == "avante" then
            -- Add Avante code context
            local code = req.caller.avante.code
            res:text("\nSelected code:\n" .. code)
        end
        
        -- Add example interactions
        res:user()
           :text("Explain this code")
           :llm()
           :text("I'll explain the code in detail...")
        
        return res:send()
    end
})

Released under the MIT License.