Skip to content

Commit 02f32e4

Browse files
committed
feat: add mcp_request_timeout option for long running tasks
1 parent 9d4887f commit 02f32e4

File tree

4 files changed

+51
-13
lines changed

4 files changed

+51
-13
lines changed

doc/configuration.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ All options are optional with sensible defaults. See below for each option in de
2020
port = 37373, -- The port `mcp-hub` server listens to
2121
shutdown_delay = 60 * 10 * 000, -- Delay in ms before shutting down the server when last instance closes (default: 10 minutes)
2222
use_bundled_binary = false, -- Use local `mcp-hub` binary (set this to true when using build = "bundled_build.lua")
23+
mcp_request_timeout = 60000, --Max time allowed for a MCP tool or resource to execute in milliseconds, set longer for long running tasks
2324

2425
---Chat-plugin related options-----------------
2526
auto_approve = false, -- Auto approve mcp tool calls
@@ -107,6 +108,13 @@ Default: `false`
107108

108109
Uses local `mcp-hub` binary. Enable this when using `build = "bundled_build.lua"` in your plugin configuration.
109110

111+
112+
### mcp_request_timeout
113+
114+
Default: 60000 (1 minute)
115+
116+
Maximum time allowed for a MCP tool or resource or prompt to execute in milliseconds. If exceeded, an McpError with code `RequestTimeout` will be raised. Set longer if you have longer running tools.
117+
110118
### cmd, cmdArgs
111119

112120
Default: `nil`

lua/mcphub/config.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ local defaults = {
88
server_url = nil, -- In cases where mcp-hub is hosted somewhere, set this to the server URL e.g `http://mydomain.com:customport` or `https://url_without_need_for_port.com`
99
config = vim.fn.expand("~/.config/mcphub/servers.json"), -- Default config location
1010
shutdown_delay = SHUTDOWN_DELAY, -- Delay before shutting down the mcp-hub
11+
mcp_request_timeout = 60000, --Timeout for MCP requests in milliseconds, useful for long running tasks
1112
---@type table<string, NativeServerDef>
1213
native_servers = {},
1314
auto_approve = false,

lua/mcphub/hub.lua

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ local utils = require("mcphub.utils")
1212
local validation = require("mcphub.utils.validation")
1313

1414
-- Default timeouts
15-
local QUICK_TIMEOUT = 1000 -- 1s for quick operations like health checks
16-
local TOOL_TIMEOUT = 30000 -- 30s for tool calls
17-
local RESOURCE_TIMEOUT = 30000 -- 30s for resource access
15+
local CONNECT_TIMEOUT = 1000 -- 1s for curl to connect to localhost
16+
local TOOL_TIMEOUT = 60000 -- 60s for tool calls
17+
local RESOURCE_TIMEOUT = 60000 -- 60s for resource access
18+
local PROMPT_TIMEOUT = 60000 -- 60s for tool calls
19+
local MCP_REQUEST_TIMEOUT = 60000 -- 60s for MCP requests
1820

1921
--- @class MCPHub.Hub
2022
--- @field port number The port number for the MCP Hub server
@@ -28,6 +30,7 @@ local RESOURCE_TIMEOUT = 30000 -- 30s for resource access
2830
--- @field server_job Job|nil The server process job if we started it
2931
--- @field is_owner boolean Whether this instance started the server
3032
--- @field is_shutting_down boolean Whether we're in the process of shutting down
33+
--- @field mcp_request_timeout number --Max time allowed for a MCP tool or resource to execute in milliseconds, set longer for long running tasks
3134
--- @field on_ready fun(hub)
3235
--- @field on_error fun(error:string)
3336
local MCPHub = {}
@@ -49,6 +52,7 @@ function MCPHub:new(opts)
4952
server_job = nil,
5053
is_owner = false,
5154
is_shutting_down = false,
55+
mcp_request_timeout = opts.mcp_request_timeout or MCP_REQUEST_TIMEOUT,
5256
on_ready = opts.on_ready or function() end,
5357
on_error = opts.on_error or function() end,
5458
}, MCPHub)
@@ -164,7 +168,7 @@ function MCPHub:check_server(callback)
164168
end
165169
-- Quick health check
166170
local opts = {
167-
timeout = QUICK_TIMEOUT,
171+
timeout = 3000,
168172
skip_ready_check = true,
169173
}
170174

@@ -336,7 +340,7 @@ end
336340
--- @param server_name string
337341
--- @param prompt_name string
338342
--- @param args table
339-
--- @param opts? { parse_response?: boolean, callback?: function, timeout?: number } Optional callback(response: table|nil, error?: string) and timeout in ms (default 30s)
343+
--- @param opts? {parse_response?: boolean, callback?: fun(res: MCPResponseOutput? ,err: string?), request_options?: MCPRequestOptions, timeout?: number } Optional callback(response: table|nil, error?: string) and timeout in ms (default 60s)
340344
--- @return {messages : {role:"user"| "assistant"|"system", output: MCPResponseOutput}[]}|nil, string|nil If no callback is provided, returns response and error
341345
function MCPHub:get_prompt(server_name, prompt_name, args, opts)
342346
opts = opts or {}
@@ -358,6 +362,8 @@ function MCPHub:get_prompt(server_name, prompt_name, args, opts)
358362
end
359363
end
360364

365+
local request_options =
366+
vim.tbl_deep_extend("force", { timeout = self.mcp_request_timeout }, opts.request_options or {})
361367
-- Signal prompt start
362368
utils.fire("MCPHubPromptStart", {
363369
server = server_name,
@@ -393,11 +399,12 @@ function MCPHub:get_prompt(server_name, prompt_name, args, opts)
393399
"POST",
394400
"servers/prompts",
395401
vim.tbl_extend("force", {
396-
timeout = opts.timeout or TOOL_TIMEOUT,
402+
timeout = (request_options.timeout + 5000) or PROMPT_TIMEOUT,
397403
body = {
398404
server_name = server_name,
399405
prompt = prompt_name,
400406
arguments = arguments,
407+
request_options = request_options,
401408
},
402409
}, opts)
403410
)
@@ -417,7 +424,7 @@ end
417424
--- @param server_name string
418425
--- @param tool_name string
419426
--- @param args table
420-
--- @param opts? {parse_response?: boolean, callback?: fun(res: MCPResponseOutput? ,err: string?), timeout?: number } Optional callback(response: table|nil, error?: string) and timeout in ms (default 30s)
427+
--- @param opts? {parse_response?: boolean, callback?: fun(res: MCPResponseOutput? ,err: string?), request_options?: MCPRequestOptions, timeout?: number } Optional callback(response: table|nil, error?: string) and timeout in ms (default 60s)
421428
--- @return MCPResponseOutput?, string? If no callback is provided, returns response and error
422429
function MCPHub:call_tool(server_name, tool_name, args, opts)
423430
opts = opts or {}
@@ -438,7 +445,8 @@ function MCPHub:call_tool(server_name, tool_name, args, opts)
438445
end
439446
end
440447
end
441-
448+
local request_options =
449+
vim.tbl_deep_extend("force", { timeout = self.mcp_request_timeout }, opts.request_options or {})
442450
-- Signal tool start
443451
utils.fire("MCPHubToolStart", {
444452
server = server_name,
@@ -473,11 +481,13 @@ function MCPHub:call_tool(server_name, tool_name, args, opts)
473481
"POST",
474482
"servers/tools",
475483
vim.tbl_extend("force", {
476-
timeout = opts.timeout or TOOL_TIMEOUT,
484+
---Make sure that actual curl request timeout is more than the MCP request timeout
485+
timeout = (request_options.timeout + 5000) or TOOL_TIMEOUT,
477486
body = {
478487
server_name = server_name,
479488
tool = tool_name,
480489
arguments = arguments,
490+
request_options = request_options,
481491
},
482492
}, opts)
483493
)
@@ -496,7 +506,7 @@ end
496506
--- Access a server resource
497507
--- @param server_name string
498508
--- @param uri string
499-
--- @param opts? { parse_response?: boolean, callback?: fun(res: MCPResponseOutput?,err:string?), timeout?: number } Optional callback(response: table|nil, error?: string) and timeout in ms (default 30s)
509+
--- @param opts? {parse_response?: boolean, callback?: fun(res: MCPResponseOutput? ,err: string?), request_options?: MCPRequestOptions, timeout?: number } Optional callback(response: table|nil, error?: string) and timeout in ms (default 60s)
500510
--- @return MCPResponseOutput?, string? If no callback is provided, returns response and error
501511
function MCPHub:access_resource(server_name, uri, opts)
502512
opts = opts or {}
@@ -518,6 +528,8 @@ function MCPHub:access_resource(server_name, uri, opts)
518528
end
519529
end
520530

531+
local request_options =
532+
vim.tbl_deep_extend("force", { timeout = self.mcp_request_timeout }, opts.request_options or {})
521533
-- Signal resource start
522534
utils.fire("MCPHubResourceStart", {
523535
server = server_name,
@@ -545,10 +557,12 @@ function MCPHub:access_resource(server_name, uri, opts)
545557
"POST",
546558
"servers/resources",
547559
vim.tbl_extend("force", {
548-
timeout = opts.timeout or RESOURCE_TIMEOUT,
560+
---Make sure that actual curl request timeout is more than the MCP request timeout
561+
timeout = (request_options.timeout + 5000) or RESOURCE_TIMEOUT,
549562
body = {
550563
server_name = server_name,
551564
uri = uri,
565+
request_options = request_options,
552566
},
553567
}, opts)
554568
)
@@ -587,8 +601,16 @@ function MCPHub:api_request(method, path, opts)
587601
end
588602

589603
local raw = {}
604+
vim.list_extend(raw, {
605+
"--connect-timeout",
606+
tostring(vim.fn.floor(CONNECT_TIMEOUT / 1000)),
607+
})
590608
if opts.timeout then
591-
vim.list_extend(raw, { "--connect-timeout", tostring(opts.timeout / 1000) })
609+
local timeout_seconds = tostring(vim.fn.floor((opts.timeout or TOOL_TIMEOUT) / 1000))
610+
vim.list_extend(raw, {
611+
"--max-time",
612+
timeout_seconds,
613+
})
592614
end
593615

594616
-- Prepare request options
@@ -610,6 +632,9 @@ function MCPHub:api_request(method, path, opts)
610632
callback(nil, tostring(error))
611633
end
612634
else
635+
if callback then
636+
callback(nil, tostring(error))
637+
end
613638
State:add_error(error)
614639
end
615640
end),
@@ -1268,7 +1293,6 @@ function MCPHub:get_marketplace_catalog(opts)
12681293
}, "marketplace")
12691294
-- Make request with market-specific error handling
12701295
return self:api_request("GET", "marketplace", {
1271-
timeout = opts.timeout or TOOL_TIMEOUT,
12721296
query = query,
12731297
callback = function(response, err)
12741298
if err then

lua/mcphub/types.lua

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,8 @@
7575

7676
---@class EnhancedMCPTool : MCPTool
7777
---@field server_name string
78+
79+
---@class MCPRequestOptions
80+
---@field timeout? number
81+
---@field resetTimeoutOnProgress? boolean
82+
---@field maxTotalTimeout? number

0 commit comments

Comments
 (0)