
Lua-Resty-MCP
Protocolo de contexto modelo SDK implementado en Lua para OpenResty
3 years
Works with Finder
4
Github Watches
2
Github Forks
4
Github Stars
lua-resty-mcp
Model Context Protocol (MCP) SDK implemented in Lua for OpenResty.
Status
In development.
Table of Contents
Features
- Transports
- stdio
- Streamable HTTP
- Protocols
- Lifecycle
- Prompts
- Resources
- Tools
- Roots
- Sampling
- Utilities
- Pagination
- Ping
- Logging
- Progress
- Completion
- Cancellation
Quickstart
A simple server demonstrating prompts, resources, tools:
-- Import lua-resty-mcp module
local mcp = require("resty.mcp")
-- Create an MCP server session with stdio transport
local server, err = mcp.server(mcp.transport.stdio, {})
if not server then
error(err)
end
-- Register a prompt
local ok, err = server:register(mcp.prompt("echo", function(args)
return "Please process this message: "..args.message
end, "Create an echo prompt", {message = {required = true}}))
if not ok then
error(err)
end
-- Register a resource template
local ok, err = server:register(mcp.resource_template("echo://{message}", "echo", function(uri, vars)
return true, "Resource echo: "..ngx.unescape_uri(vars.message)
end, "Echo a message as a resource", "text/plain"))
if not ok then
error(err)
end
-- Register a tool
local ok, err = server:register(mcp.tool("echo", function(args)
return "Tool echo: "..args.message
end, "Echo a message as a tool", {
type = "object",
properties = {
message = {type = "string"}
},
required = {"message"}
}))
if not ok then
error(err)
end
-- Launch the server session
server:run()
Server APIs
mcp.server
syntax: server, err = mcp.server(transport[, options])
Create an MCP server session with the specific transport (currently only mcp.transport.stdio
is available here) and options.
A successful call returns an MCP server session. Otherwise, it returns nil
and a string describing the error.
Available options:
{
-- Common options
name = "lua-resty-mcp", -- Name of this session (optional)
version = "1.0", -- Version of this session (optional)
-- You can also put your other options here and access them via `options` field of the session instance
}
mcp.transport.streamable_http.endpoint
syntax: mcp.transport.streamable_http.endpoint(custom_fn[, options])
Create an MCP Streamable HTTP endpoint.
[!NOTE] This method should only be called from
content_by_lua*
directives.
The 1st argument of this method custom_fn
, will be called when the clients initiate the initialization phase; the reference to resty.mcp
module and the instance of the server session will be passed into this callback. You should configure the contexts for the server and launch the server session in this callback. It could be defined as follows:
function custom_fn(mcp, server)
local ok, err = server:register(mcp.tool("echo", function(args)
return "Tool echo: "..args.message
end, "Echo a message as a tool", {
type = "object",
properties = {
message = {type = "string"}
},
required = {"message"}
}))
if not ok then
error(err)
end
server:run()
end
The optional 2nd argument of this method options
, should be a dict-like Lua table that contains the configuration options of the endpoint and server session. It includes the following optional fields:
{
-- Configure the message bus of this endpoint
message_bus = {
-- Type of the message bus, currently only "builtin" is available
-- It's implemented using the shared memory zone of OpenResty
type = "builtin",
-- Options for "builtin" message bus
shm_zone = "mcp_message_bus", -- name of the shared memory zone
mark_ttl = 10, -- TTL of the session mark (seconds)
cache_ttl = 90, -- TTL of the cached events (seconds)
-- Options for spin waiting
step = 0.001,
ratio = 2,
max_step = 0.5
},
-- Whether to enable the resumability and redelivery mechanism
enable_resumability = false,
-- Other options are the same as `mcp.server` API
...
}
[!TIP] It is recommended to use different shared memory zones for different endpoints.
A simple echo demo server configuration:
worker_processes auto;
events {
}
http {
lua_shared_dict mcp_message_bus 64m;
server {
listen 80;
location = /mcp {
content_by_lua_block {
require("resty.mcp").transport.streamable_http.endpoint(function(mcp, server)
local ok, err = server:register(mcp.tool("echo", function(args)
return "Tool echo: "..args.message
end, "Echo a message as a tool", {
type = "object",
properties = {
message = {type = "string"}
},
required = {"message"}
}))
if not ok then
error(err)
end
server:run()
end)
}
}
}
}
[!IMPORTANT] Endpoint callbacks use a different request context than outside the callback, so DO NOT access variables outside the callback through upvalues of the closure, which will result in undefined behavior.
server:run
syntax: server:run([options])
Launch the server session.
Available options:
{
-- Configure server capabilities, which are enabled by default
-- Explicitly set the field to `false` to disable the corresponding capability
-- (optional)
capabilities = {
prompts = {
listChanged = true
},
resources = {
subscribe = true,
listChanged = true
},
tools = {
listChanged = true
}
},
-- Configure the page size of the corresponding list, 0 disables pagination
-- The default value for these fields is 0
-- (optional)
pagination = {
prompts = 0,
resources = 0,
tools = 0
},
-- Instructions describing how to use the server and its features (optional)
instructions = "Hello, MCP!",
-- Configure the event handlers of the server session (optional)
event_handlers = {
initialized = function(params, ctx)
-- Will be called after `initialized` notification (optional)
local current_session = ctx.session
-- Interact with the current session or other services
end,
["roots/list_changed"] = function(params, ctx)
-- Will be called after `roots/list_changed` notification (optional)
local current_session = ctx.session
-- Interact with the current session or other services
end
}
}
[!NOTE] Before launching a server session, maybe you should register some context components.
server:register
syntax: ok, err = server:register(component)
Register a context component to the server session.
A successful call returns true
. Otherwise, it returns nil
and a string describing the error.
Available context components:
mcp.prompt
`syntax: component = mcp.prompt(name, callback[, desc[, args]])
Create a prompt or prompt template.
callback
will be called when a client requests to get this prompt, and it could be defined as follows:
function callback(args, ctx)
local meta_field = ctx._meta -- `_meta` field of current request
local current_session = ctx.session
-- Interact with the current session or other services
if error_occurred then
return nil, "an error occured"
end
return "content of this prompt" or {
{role = "user", content = {type = "text", text = "text content"}},
{role = "assistant", content = {type = "image", data = "...", mimeType = "image/jpeg"}},
...
}
end
The 4th argument of this method args
, is used to declare the expected arguments that will be passed into callback
. It should be a table and could be defined as follows:
{
arg_name = {
description = "What is this argument.",
required = true
},
...
}
mcp.resource
syntax: component = mcp.resource(uri, name, callback[, desc[, mime[, annos[, size]]]])
Create a resource.
callback
will be called when a client requests to read this resource, and it could be defined as follows:
function callback(uri, ctx)
local meta_field = ctx._meta -- `_meta` field of current request
local current_session = ctx.session
-- Interact with the current session or other services
if error_occurred then
return nil, "an error occured"
end
return "content of this resource" or {
{text = "content of "..uri},
{uri = uri.."/bin", blob = "SGVsbG8sIHdvcmxkIQ==", mimeType = "application/octet-stream"},
...
}
end
The 6th argument of this method annos
, is optional annotations for the client. It should be a table and could be defined as follows:
{
-- Describes who the intended customer of this object or data is
audience = {"user", "assistant"},
-- Describes how important this data is for operating the server
-- The value will be clipped to the range [0, 1]
priority = 0.42
}
mcp.resource_template
syntax: component = mcp.resource_template(pattern, name, callback[, desc[, mime[, annos]]])
Create a resource template.
callback
will be called when a client requests to read a resource that matches this template, and it could be defined as follows:
function callback(uri, vars, ctx)
-- `vars` is a table that holds variables extracted from the URI according to the template pattern
local meta_field = ctx._meta -- `_meta` field of current request
local current_session = ctx.session
-- Interact with the current session or other services
if resource_not_found then
return false
end
if error_occurred then
return true, nil, "an error occured"
end
return true, "content of this resource" or {
{text = string.format("content of %s, foo=%s", uri, vars.foo)},
{uri = uri.."/bin", blob = "SGVsbG8sIHdvcmxkIQ==", mimeType = "application/octet-stream"},
...
}
end
The argument annos
is the same as in mcp.resource
.
mcp.tool
syntax: component = mcp.tool(name, callback[, desc[, input_schema[, annos]]])
Create a tool.
callback
will be called when a client requests to call this tool, and it could be defined as follows:
function callback(args, ctx)
local meta_field = ctx._meta -- `_meta` field of current request
local current_session = ctx.session
-- Interact with the current session or other services
if error_occurred then
return nil, "an error occured" or {
-- multi-content error information
{type = "text", text = "an error occured"},
{type = "audio", data = "...", mimeType = "audio/mpeg"},
...
}
end
return "result of this tool calling" or {
{type = "text", text = "result of this tool calling"},
{type = "image", data = "...", mimeType = "image/jpeg"},
...
}
end
The 4th argument input_schema
, is a JSON Schema object defining the expected arguments for the tool.
The 5th argument annos
, is optional additional tool information. It should be a table and could be defined as follows:
{
-- A human-readable title for the tool
title = "Foobar",
-- If true, the tool does not modify its environment
-- Default: false
readOnlyHint = false,
-- If true, the tool may perform destructive updates to its environment
-- If false, the tool performs only additive updates
-- This property is meaningful only when `readOnlyHint == false`
-- Default: true
destructiveHint = true,
-- If true, calling the tool repeatedly with the same arguments will have no additional effect on the its environment
-- This property is meaningful only when `readOnlyHint == false`
-- Default: false
idempotentHint = false,
-- If true, this tool may interact with an "open world" of external entities
-- If false, the tool's domain of interaction is closed
-- Default: true
openWorldHint = true
}
[!NOTE] All of the above properties are hints. They are not guaranteed to provide a faithful description of tool behavior (including descriptive properties like
title
).
[!IMPORTANT]
- When you need to call server methods from callbacks (event handlers and context component callbacks), you MUST call them via the
ctx.session
field instead of via the server instance outside of the callback using closure upvalues. Becausectx.session
is a wrapper of the server instance that contains the context required by the backend components, calling the server methods via the server instance outside of the callback will result in undefined behavior.- The fields in
ctx
argument of callbacks (event handlers and context component callbacks) are ONLY available before the callback returns; accessing them after the callback returns results in undefined behavior.
server:unregister_*
syntax: ok, err = unregister_prompt(name)
syntax: ok, err = unregister_resource(uri)
syntax: ok, err = unregister_resource_template(pattern)
syntax: ok, err = unregister_tool(name)
Unregister the corresponding component.
A successful call returns true
. Otherwise, it returns nil
and a string describing the error.
server:resource_updated
syntax: ok, err = server:resource_updated(uri)
Trigger the resource updated event.
A successful call returns true
. Otherwise, it returns nil
and a string describing the error.
server:list_roots
syntax: roots, err = server:list_roots([timeout])
Request a list of root URIs from the client.
A successful call returns an array-like Lua table that contains the roots. Otherwise, it returns nil
and a string describing the error.
The returned roots
may have the following structure:
{
{uri = "file:///path/to/project", name = "Project"},
{uri = "file:///path/to/foobar"},
...
}
server:create_message
syntax: res, err = server:create_message(messages, max_tokens[, options[, timeout]])
Request to sample an LLM via the client.
A successful call returns a dict-like Lua table that contains the sampled message from the client. Otherwise, it returns nil
and a string describing the error.
The 1st argument of this method messages
, should be an array-like Lua table that contains a list of structured conversation messages. It could be defined as follows:
{
{
role = "user" or "assistant",
content = {type = "text", text = "text message"}
},
{
role = "user" or "assistant",
content = {type = "image" or "audio", data = "...", mimeType = "..."}
},
...
}
The 3rd argument of this method options
, should be a dict-like Lua table that contains the additional sampling options. It includes the following optional fields:
{
-- Preferences for which model to select
modelPreferences = {
-- Hints to use for model selection
hints = {
{name = "gemma"},
{name = "llama"},
...
},
-- How much to prioritize cost when selecting a model
-- 0 means cost, 1 means cost is the most important factor
costPriority = 0.5,
-- How much to prioritize sampling speed (latency) when selecting a model
-- 0 means speed is not important, 1 means speed is the most important factor
speedPriority = 0.5,
-- How much to prioritize intelligence and capabilities when selecting a model
-- 0 means intelligence is not important, 1 means intelligence is the most important factor
intelligencePriority = 0.5
},
-- System prompt you wants to use for sampling
systemPrompt = "You are a helpful assistant."
-- A request to include context from one or more MCP servers (including the caller), to be attached to the prompt
includeContext = "none" or "thisServer" or "allServers",
temperature = 0.4,
stopSequences = {"foo", "bar", ...},
metadata = {...}
}
[!NOTE] All of the above properties are hints.
The returned message is similar to the list elements passed in the messages
argument, but has an additional model
field containing the name of the model that generated the message, and an optional stopReason
field containing the reason why sampling stopped, if known.
server:shutdown
syntax: server:shutdown()
Shutdown the server session.
Writing MCP Clients
The following code demonstrates how to use the high-level client APIs to interact with an MCP server:
-- Import lua-resty-mcp module
local mcp = require("resty.mcp")
-- Create an MCP client session with stdio transport
local client, err = mcp.client(mcp.transport.stdio, {
command = {"npx", "-y", "@modelcontextprotocol/server-everything"}
})
-- Initialize this session with roots (optional) and sampling callback (optional)
local ok, err = client:initialize({
{path = "/path/to/project", name = "Project"} -- Expose a directory named `Project` to server
}, function(params)
-- Sampling callback
return "Mock sampling text."
end)
-- Discover available prompts
local prompts, err = client:list_prompts()
-- Get a specific prompt
local res, err = client:get_prompt("complex_prompt", {temperature = "0.4", style = "json"})
-- Discover available resources
local resources, err = client:list_resources()
-- Read a specific resource
local res, err = client:read_resource("test://static/resource/1")
-- Subscribe to a specific resource
local ok, err = client:subscribe_resource("test://static/resource/42", function(uri)
-- Resource updated callback
end)
-- Unsubscribe from a specific resource
local ok, err = client:unsubscribe_resource("test://static/resource/42")
-- Discover available tools
local tools, err = client:list_tools()
-- Call a specific tool
local res, err = client:call_tool("echo", {message = "Hello, world!"})
-- Shutdown this client session
client:shutdown()
Client APIs
mcp.client
syntax: client, err = mcp.client(transport, options)
Create an MCP client session with the specific transport (mcp.transport.stdio
or mcp.transport.streamable_http
) and options.
A successful call returns an MCP client session. Otherwise, it returns nil
and a string describing the error.
Available options:
{
-- Common options
name = "lua-resty-mcp", -- Name of this session (optional)
version = "1.0", -- Version of this session (optional)
-- Options for stdio transport
-- Command and arguments for starting server (required)
command = "ngx -y @modelcontextprotocol/server-everything" or {"npx", "-y", "@modelcontextprotocol/server-everything"},
-- Options for pipe connected to server (optional)
pipe_opts = {
-- Buffer size used by reading operations, in bytes
-- Default: 4096
buffer_size = 4096,
-- Environment variables for server
environ = {"PATH=/tmp/bin", "CWD=/tmp/work"},
-- Write timeout threshold, in milliseconds
-- Default: 10000, 0 for never timeout
write_timeout = 10000,
-- STDOUT read timeout threshold, in milliseconds
-- Default: 10000, 0 for never timeout
stdout_read_timeout = 10000,
-- Wait timeout threshold, in milliseconds
-- Default: 10000, 0 for never timeout
wait_timeout = 10000
},
-- Options for Streamable HTTP transport
-- URL of the MCP endpoint (required)
endpoint_url = "http://127.0.0.1/mcp",
-- Content of the HTTP Authorization header (optional)
endpoint_auth = "Bearer TOKEN",
-- Read timeout threshold, in seconds
-- Default: 10
read_timeout = 10,
-- Whether to enable the standalone GET SSE stream
-- Default: false
enable_get_sse = false,
-- Options for HTTP connections
http_opts = {
...
},
-- Options for spin waiting
spin_opts = {
step = 0.001,
ratio = 2,
max_step = 0.5
},
-- You can also put your other options here and access them via `options` field of the session instance
}
[!NOTE] Available options for HTTP connections can be viewed here. Note that
scheme
,host
, andport
will be parsed according toendpoint_url
automatically, so setting them inhttp_opts
will be ignored.
client:initialize
syntax: ok, err = client:initialize([roots[, sampling_callback[, timeout]]])
Initialize the client session.
A successful call returns true
. Otherwise, it returns nil
and a string describing the error.
roots
is a list of directories that will be exposed to the server. Its structure is as follows:
{
{path = "/path/to/first", name = "First Directory"},
{path = "/path/to/second"},
...
}
sampling_callback
will be called when the MCP server requests sampling LLM, and it could be defined as follows:
function sampling_callback(params)
if error_occurred then
return nil, -1, "an error occured", opt_extra_err_info
end
return "Mock sampling text." or {
role = "assistant",
content = {
type = "text",
text = "Mock sampling text."
},
model = "gemma3-4b",
stopReason = "endTurn"
}
end
The callback's argument params
may have the following structure:
{
messages = {
{
role = "user",
content = {type = "text", text = "What is the capital of France?"}
},
...
},
modelPreferences = {
hints = {
{name = "claude-3-sonnet"},
...
},
intelligencePriority = 0.8,
speedPriority = 0.5
},
systemPrompt = "You are a helpful assistant.",
maxTokens = 100
}
client:shutdown
syntax: client:shutdown()
Shutdown the client session.
client:expose_roots
syntax: ok, err = client:expose_roots(roots)
Expose a new set of directories to the server.
A successful call returns true
. Otherwise, it returns nil
and a string describing the error.
The argument roots
is the same as in client:initialize
, pass nil
or {}
to expose no directories to the server.
[!NOTE] Calling this method will trigger
notifications/roots/list_changed
.
client:list_prompts
syntax: prompts, err = client:list_prompts([timeout])
List the available prompts of the MCP server.
A successful call returns an array-like Lua table that contains the prompts. Otherwise, it returns nil
and a string describing the error.
The returned prompts
may have the following structure:
{
{
name = "code_review",
description = "Asks the LLM to analyze code quality and suggest improvements",
arguments = {
{
name = "code",
description = "The code to review",
required = true
},
...
}
},
...
}
client:get_prompt
syntax: res, err = client:get_prompt(name, args[, timeout])
Get a specific prompt from the MCP server.
A successful call returns a dict-like Lua table that contains the content of the prompt. Otherwise, it returns nil
and a string describing the error.
The content of the prompt may have the following structure:
{
description = "Code review prompt",
messages = {
{
role = "user",
content = {
type = "text",
text = "Please review this Python code:\ndef hello():\n print('world')"
}
},
...
}
}
client:list_resources
syntax: resources, err = client:list_resources([timeout])
List the available resources of the MCP server.
A successful call returns an array-like Lua table that contains the resources. Otherwise, it returns nil
and a string describing the error.
The returned resources
may have the following structure:
{
{
uri = "file:///project/src/main.rs",
name = "main.rs",
description = "Primary application entry point",
mimeType = "text/x-rust"
},
...
}
client:list_resource_templates
syntax: templates, err = client:list_resource_templates([timeout])
List the available resource templates of the MCP server.
A successful call returns an array-like Lua table that contains the resource templates. Otherwise, it returns nil
and a string describing the error.
The returned templates
may have the following structure:
{
{
uriTemplate = "file://{+path}",
name = "Project Files",
description = "Access files in the project directory",
mimeType = "application/octet-stream"
},
...
}
client:read_resource
syntax: res, err = client:read_resource(uri[, timeout])
Read a specific resource from the MCP server.
A successful call returns a dict-like Lua table that contains the content of the resource. Otherwise, it returns nil
and a string describing the error.
The content of the resource may have the following structure:
{
contents = {
{
uri = "file:///project/src/main.rs",
mimeType = "text/x-rust",
text = "fn main() {\n println!(\"Hello world!\");\n}"
},
...
}
}
client:subscribe_resource
syntax: ok, err = client:subscribe_resource(uri, callback[, timeout])
Subscribe to a specific resource of the MCP server.
A successful call returns true
. Otherwise, it returns nil
and a string describing the error.
callback
will be called when the MCP server triggers notifications/resources/updated
, and it could be defined as follows:
function updated_callback(uri)
-- Read this resource from the MCP server to get the latest content
end
client:unsubscribe_resource
syntax: ok, err = client:unsubscribe_resource(uri[, timeout])
Unsubscribe from a subscribed resource of the MCP server.
A successful call returns true
. Otherwise, it returns nil
and a string describing the error.
client:list_tools
syntax: tools, err = client:list_tools([timeout])
List the available tools of the MCP server.
A successful call returns an array-like Lua table that contains the tools. Otherwise, it returns nil
and a string describing the error.
The returned tools
may have the following structure:
{
{
name = "get_weather",
description = "Get current weather information for a location",
inputSchema = {
type = "object",
properties = {
location = {
type = "string",
description = "City name or zip code"
}
},
required = {"location"}
}
},
...
}
client:call_tool
syntax: res, err = client:call_tool(name, args[, timeout])
Call a specific tool in the MCP server.
A successful call returns a dict-like Lua table that contains the result of the tool call. Otherwise, it returns nil
and a string describing the error.
The result of the tool calling may have the following structure:
{
jsonrpc = "2.0",
id = 2,
result = {
content = {
{
type = "text",
text = "Current weather in New York:\nTemperature: 72°F\nConditions: Partly cloudy"
},
...
},
isError = false
}
}
License
BSD-3-Clause license. See LICENSE for details.
相关推荐
🔥 1Panel proporciona una interfaz web intuitiva y un servidor MCP para administrar sitios web, archivos, contenedores, bases de datos y LLM en un servidor de Linux.
⛓️Rulego es un marco de motor de regla de orquestación de componentes de alta generación de alto rendimiento, de alto rendimiento y de alto rendimiento para GO.
🧑🚀 全世界最好的 llM 资料总结(数据处理、模型训练、模型部署、 O1 模型、 MCP 、小语言模型、视觉语言模型) | Resumen de los mejores recursos del mundo.
Cree fácilmente herramientas y agentes de LLM utilizando funciones Plain Bash/JavaScript/Python.
😎简单易用、🧩丰富生态 - 大模型原生即时通信机器人平台 | 适配 Qq / 微信(企业微信、个人微信) / 飞书 / 钉钉 / Discord / Telegram / Slack 等平台 | 支持 Chatgpt 、 Deepseek 、 DiFy 、 Claude 、 Gemini 、 Xai 、 PPIO 、 Ollama 、 LM Studio 、阿里云百炼、火山方舟、 Siliconflow 、 Qwen 、 Moonshot 、 Chatglm 、 SillyTraven 、 MCP 等 LLM 的机器人 / Agente | Plataforma de bots de mensajería instantánea basada en LLM, admite Discord, Telegram, WeChat, Lark, Dingtalk, QQ, Slack
Iniciar aplicaciones de múltiples agentes empoderadas con Building LLM de manera más fácil.
Reviews

user_YejZZKeK
Recently, I discovered lua-resty-mcp by ufownl and it has significantly improved my development workflow. This powerful module offers seamless integration with Nginx, making it an essential tool for anyone working with Lua and Nginx. The detailed documentation and straightforward setup are truly commendable. Highly recommended for developers looking to enhance their performance and efficiency!

user_OqdU51Py
As a loyal user of lua-resty-mcp, I highly recommend this product for anyone working within the Lua environment. Created by ufownl, it's an incredibly efficient and reliable memcached client that has significantly optimized our caching processes. It integrates seamlessly, offers robust performance, and enhances development efficiency. Whether you're a novice or an expert, lua-resty-mcp is a fantastic tool!

user_rExoAtGY
As a dedicated user of lua-resty-mcp, I highly recommend this tool created by ufownl. It offers robust performance for managing Memcached proxies and seamlessly integrates with Nginx. The ease of use and comprehensive documentation make it an essential choice for developers working with Lua.

user_bVI6kGFJ
As a dedicated user of lua-resty-mcp, I must say this library by ufownl is a game-changer. It provides seamless integration and easy manipulation of memcached protocol in OpenResty applications. The robust and efficient design immensely boosts performance and scalability, making it an essential tool for anyone working with Lua and Nginx. I highly recommend it!

user_hJlGEepf
As a dedicated user of lua-resty-mcp, I find it to be an outstanding library developed by ufownl. It significantly simplifies the interaction with memcached servers within OpenResty. The efficiency and ease of integration have enhanced our application's performance. Highly recommended for anyone looking to optimize their caching strategy in a Lua-based environment!

user_onZxAiuq
As a dedicated user of lua-resty-mcp by ufownl, I am thoroughly impressed by this product's capabilities. It offers a robust and seamless solution for working with Lua. Highly recommend it to anyone interested in maximizing their productivity with Lua. Great job, ufownl!

user_f0LYKefb
As an avid user of lua-resty-mcp, I must say that this application is truly remarkable! Created by ufownl, it offers outstanding performance for managing memcached pools in OpenResty. The ease of integration and the efficiency it brings to our projects make it a must-have for any developer working with Lua and OpenResty. Highly recommended!

user_159M1QgA
I've been using lua-resty-mcp for a while now, and it's truly a fantastic tool by ufownl. It integrates seamlessly and provides tremendous value for managing Memcached in an efficient way. The setup was straightforward, and the performance boost is noticeable. Highly recommend this for anyone needing robust Memcached solutions with Lua!