Cover image
Try Now
2025-04-14

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]

  1. 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. Because ctx.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.
  2. 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, and port will be parsed according to endpoint_url automatically, so setting them in http_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.

相关推荐

  • av
  • Ejecute sin esfuerzo LLM Backends, API, frontends y servicios con un solo comando.

  • 1Panel-dev
  • 🔥 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
  • ⛓️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.

  • WangRongsheng
  • 🧑‍🚀 全世界最好的 llM 资料总结(数据处理、模型训练、模型部署、 O1 模型、 MCP 、小语言模型、视觉语言模型) | Resumen de los mejores recursos del mundo.

  • hkr04
  • SDK liviano C ++ MCP (Protocolo de contexto del modelo)

  • sigoden
  • Cree fácilmente herramientas y agentes de LLM utilizando funciones Plain Bash/JavaScript/Python.

  • RockChinQ
  • 😎简单易用、🧩丰富生态 - 大模型原生即时通信机器人平台 | 适配 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

  • modelscope
  • Iniciar aplicaciones de múltiples agentes empoderadas con Building LLM de manera más fácil.

  • dmayboroda
  • Trapo conversacional local con contenedores configurables

  • paulwing
  • Un repositorio de pruebas creado con el servicio MCP

    Reviews

    3.5 (8)
    Avatar
    user_YejZZKeK
    2025-04-24

    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!

    Avatar
    user_OqdU51Py
    2025-04-24

    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!

    Avatar
    user_rExoAtGY
    2025-04-24

    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.

    Avatar
    user_bVI6kGFJ
    2025-04-24

    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!

    Avatar
    user_hJlGEepf
    2025-04-24

    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!

    Avatar
    user_onZxAiuq
    2025-04-24

    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!

    Avatar
    user_f0LYKefb
    2025-04-24

    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!

    Avatar
    user_159M1QgA
    2025-04-24

    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!