Adding Resources
Resources provide data through URIs in two ways:
- Static Resources - Fixed URIs
- Resource Templates - Dynamic URIs with parameters
Type Definitions
Static Resources
lua
---@class MCPResource
---@field name? string Resource identifier
---@field description? string|fun():string Resource description
---@field mimeType? string Resource MIME type (e.g., "text/plain")
---@field uri string Static URI (e.g., "system://info")
---@field handler fun(req: ResourceRequest, res: ResourceResponse) Implementation
Resource Templates
lua
---@class MCPResourceTemplate
---@field name? string Template identifier
---@field description? string|fun():string Template description
---@field mimeType? string Default MIME type
---@field uriTemplate string URI with parameters (e.g., "buffer://{bufnr}/lines")
---@field handler fun(req: ResourceRequest, res: ResourceResponse) Implementation
Request Context
Resource handlers receive:
lua
---@class ResourceRequest
---@field params table<string,string> Template parameters from URI
---@field uri string Complete requested URI
---@field uriTemplate string|nil Original template if from template
---@field resource MCPResource|MCPResourceTemplate Complete definition
---@field server NativeServer Server instance
---@field caller table Additional context from caller
---@field editor_info EditorInfo Current editor state
Response Builder
Resource handlers use:
lua
---@class ResourceResponse
---@field text fun(text: string, mime?: string): ResourceResponse Add text
---@field blob fun(data: string, mime?: string): ResourceResponse Add binary
---@field image fun(data: string, mime?: string): ResourceResponse Add image
---@field audio fun(data: string, mime?: string): ResourceResponse Add audio
---@field error fun(message: string, details?: table): table Send error
---@field send fun(result?: table): table Send final response
Examples
Basic Examples
Static Resource
lua
mcphub.add_resource("system", {
name = "System Info",
description = "Get system information",
uri = "system://info",
mimeType = "text/plain",
handler = function(req, res)
local info = {
os = vim.loop.os_uname(),
pid = vim.fn.getpid(),
vimdir = vim.fn.stdpath("config")
}
return res:text(vim.inspect(info)):send()
end
})
Resource Template
lua
mcphub.add_resource_template("files", {
name = "File Lines",
description = "Get specific lines from a file",
uriTemplate = "files://{path}/{start}-{end}",
handler = function(req, res)
-- Get parameters
local path = req.params.path
local start_line = tonumber(req.params.start)
local end_line = tonumber(req.params.end)
-- Validate file
if not vim.loop.fs_stat(path) then
return res:error("File not found: " .. path)
end
-- Read lines
local lines = {}
local current = 0
for line in io.lines(path) do
current = current + 1
if current >= start_line then
table.insert(lines, string.format(
"%4d │ %s", current, line
))
end
if current >= end_line then
break
end
end
return res:text(table.concat(lines, "\n")):send()
end
})
Real Examples from Neovim Server
LSP Diagnostics Resource
lua
mcphub.add_resource("neovim", {
name = "Diagnostics: Current File",
description = "Get diagnostics for the current file",
uri = "neovim://diagnostics/current",
mimeType = "text/plain",
handler = function(req, res)
-- Get active buffer
local buf_info = req.editor_info.last_active
if not buf_info then
return res:error("No active buffer")
end
-- Get diagnostics
local diagnostics = vim.diagnostic.get(buf_info.bufnr)
-- Format header
local text = string.format(
"Diagnostics for: %s\n%s\n",
buf_info.filename,
string.rep("-", 40)
)
-- Format diagnostics
for _, diag in ipairs(diagnostics) do
local severity = vim.diagnostic.severity[diag.severity]
text = text .. string.format(
"\n%s: %s\nLine %d: %s\n",
severity,
diag.source or "unknown",
diag.lnum + 1,
diag.message
)
end
return res:text(text):send()
end
})
Buffer Lines Template
lua
mcphub.add_resource_template("neovim", {
name = "Buffer Lines",
description = "Get specific lines from a buffer",
uriTemplate = "neovim://buffer/{bufnr}/lines/{start}-{end}",
handler = function(req, res)
-- Get parameters
local bufnr = tonumber(req.params.bufnr)
local start_line = tonumber(req.params.start)
local end_line = tonumber(req.params.end)
-- Validate buffer
if not vim.api.nvim_buf_is_valid(bufnr) then
return res:error("Invalid buffer: " .. req.params.bufnr)
end
-- Get lines
local lines = vim.api.nvim_buf_get_lines(
bufnr,
start_line - 1, -- 0-based index
end_line, -- Exclusive end
false -- Strict indexing
)
-- Format with line numbers
local result = {}
for i, line in ipairs(lines) do
table.insert(result, string.format(
"%4d │ %s",
start_line + i - 1,
line
))
end
return res:text(table.concat(result, "\n")):send()
end
})
Advanced Features
Dynamic MIME Types
Change MIME type based on content:
lua
mcphub.add_resource_template("files", {
name = "File Content",
uriTemplate = "files://{path}",
handler = function(req, res)
local path = req.params.path
local ext = vim.fn.fnamemodify(path, ":e")
-- Get MIME type based on extension
local mime_types = {
json = "application/json",
yaml = "application/yaml",
md = "text/markdown",
txt = "text/plain"
}
local mime = mime_types[ext] or "text/plain"
return res:text(vim.fn.readfile(path), mime):send()
end
})
Binary Data
Handle binary files:
lua
mcphub.add_resource_template("files", {
name = "File Download",
uriTemplate = "files://download/{path}",
handler = function(req, res)
local path = req.params.path
local ext = vim.fn.fnamemodify(path, ":e")
-- Binary file types
local binary_types = {
png = "image/png",
jpg = "image/jpeg",
pdf = "application/pdf"
}
if binary_types[ext] then
-- Read as binary
local data = vim.fn.readfile(path, "b")
return res:blob(data, binary_types[ext]):send()
else
-- Read as text
return res:text(vim.fn.readfile(path)):send()
end
end
})
Resource Validation
URI parameter validation:
lua
mcphub.add_resource_template("git", {
name = "Commit Info",
uriTemplate = "git://commit/{hash}",
handler = function(req, res)
local hash = req.params.hash
-- Validate hash format
if not hash:match("^[0-9a-f]+$") then
return res:error("Invalid commit hash", {
hash = hash,
expected = "hexadecimal string"
})
end
-- Validate hash exists
local exists = vim.fn.system(
"git rev-parse --quiet --verify " .. hash
)
if vim.v.shell_error ~= 0 then
return res:error("Commit not found", {
hash = hash,
suggestion = "Use 'git log' to list commits"
})
end
-- Get commit info
local info = vim.fn.system(
"git show --no-patch --format='%h %s' " .. hash
)
return res:text(info):send()
end
})
Next, learn about Adding Prompts to create interactive conversations.