How 5ire Manages and Controls Concurrent Tool Executions in MCP Environments
5ire prevents race conditions and resource exhaustion by combining single-threaded async-await per connection, explicit parallel-tool-call flags, abort-controller cancellation, and mutex-protected database access.
The open-source application 5ire (nanbingxyz/5ire) executes Model Context Protocol (MCP) tools on remote servers. To maintain stability when handling multiple tool requests, the codebase implements a deliberate concurrency control strategy that avoids uncontrolled parallelism. This article examines the specific mechanisms used to serialize tool executions, cancel stale requests, and protect shared resources.
Single-Threaded Execution per MCP Connection
At the core of 5ire's concurrency model is the MCPToolsManager.call() method in src/main/services/mcp-tools-manager.ts. When a tool is invoked, the manager parses the tool URI, looks up the appropriate connection, and executes the call with a single await.
// src/main/services/mcp-tools-manager.ts (line 223)
async call(options: MCPToolsManager.CallOptions) {
const toolURI = this.#parseToolURI(options.uri);
// …validation omitted for brevity…
const result = await connection.client.callTool({
name: toolURI.name,
arguments: options.input,
});
// result handling …
}
Because callTool is awaited, the JavaScript event loop processes each request sequentially for that connection. No additional queue or worker pool is created, so the runtime naturally serializes concurrent calls targeting the same MCP server. This single-threaded tool call per request pattern ensures that a busy server cannot be overwhelmed by parallel invocations from a single client instance.
Disabling Parallel Tool Calls at the Model Level
Even when a large language model generates multiple tool calls in a single response, 5ire forces sequential execution through a model-level parallel-tool flag. The chat payload type in src/intellichat/types.ts declares parallel_tool_calls?: boolean, which downstream services honor.
Individual model adapters explicitly disable automatic parallelism. In src/intellichat/services/AnthropicChatService.ts, the adapter sets disable_parallel_tool_use: true:
// src/intellichat/services/AnthropicChatService.ts
const request = {
model: model.name,
messages: [{ role: "user", content: prompt }],
// The Anthropic API does not support parallel tool usage
disable_parallel_tool_use: true,
};
This flag propagates through the chat service stack, ensuring that the MCP layer receives tool calls one-by-one even when the LLM suggests parallel execution.
Abort-Controlled Fetching for Pagination
When loading tool lists, 5ire prevents runaway parallel pagination requests using AbortController cancellation. The MCPToolsManager.#fetchTools() method creates a new controller for each fetch operation and aborts any pending previous request for the same connection.
// src/main/services/mcp-tools-manager.ts
const abortController = new AbortController();
collection.abortController?.abort(); // Cancel previous fetch
const result = await connection.client.listTools(
{ cursor },
{ signal: abortController.signal } // New request bound to controller
);
The same pattern appears in resource and prompt managers. By binding the fetch signal to a controller and explicitly aborting stale requests, the system guarantees that only the latest pagination fetch proceeds, eliminating race conditions in list operations.
Mutex-Protected Database Access
Certain background services must update the SQLite database while other asynchronous work runs concurrently. The DocumentEmbedder class in src/main/services/document-embedder.ts uses a lightweight Mutex from src/main/internal/mutex.ts to serialize critical sections.
// src/main/services/document-embedder.ts
await this.#mutex.acquire(); // Wait for exclusive lock
try {
await this.#database.run(/* update pending document status */);
} finally {
this.#mutex.release(); // Release for next waiter
}
The Mutex.create() factory provides an async lock that prevents multiple embedder loops from writing to the database simultaneously. This mechanism protects shared resources without blocking the main event loop.
Pagination Limits to Bound Concurrency
To prevent a single request from spawning an unbounded cascade of parallel fetches, 5ire enforces strict pagination caps. The codebase defines constants such as MAX_TOOLS_PAGE = 10 and MAX_RESOURCES_PAGE = 20 that limit how many pages can be fetched in a single list operation.
By bounding the pagination loop in MCPToolsManager, the system guarantees that resource exhaustion cannot occur through deep pagination chains, even when multiple servers are being queried concurrently.
Summary
- Single-await pattern:
MCPToolsManager.call()usesawait connection.client.callTool()to serialize per-connection tool executions naturally through the JavaScript event loop. - Explicit disable flags: Model adapters like
AnthropicChatServicesetdisable_parallel_tool_use: trueto prevent LLM-generated parallel tool calls. - Request cancellation:
AbortControllerinstances in#fetchTools()abort stale pagination requests before starting new ones. - Database safety: The
Mutexclass serializes SQLite updates in services likeDocumentEmbedderto prevent race conditions. - Bounded workloads: Pagination constants (
MAX_TOOLS_PAGE,MAX_RESOURCES_PAGE) limit the total number of concurrent fetch operations per request.
Frequently Asked Questions
Does 5ire support true parallel tool execution across different MCP servers?
Yes. While 5ire serializes calls targeting the same MCP connection using await, independent connections to different servers operate on separate async flows. The concurrency controls described here prevent per-server overload, not cross-server parallelism.
What happens if a tool call hangs or times out?
The AbortController pattern used in pagination extends to tool invocations through the MCP client transport layer. When a connection is closed or a new request supersedes a pending one, the in-flight promise is rejected via the abort signal, allowing the event loop to reclaim resources.
Why does 5ire disable parallel tool calls for Anthropic models?
According to the source code comments in AnthropicChatService.ts, the Anthropic API does not support parallel tool usage. By setting disable_parallel_tool_use: true, 5ire ensures compatibility and prevents the LLM client from generating requests that the remote server cannot handle concurrently.
Is the Mutex implementation based on Node.js primitives?
The Mutex class in src/main/internal/mutex.ts is a lightweight custom implementation using standard JavaScript Promise and queue mechanisms, not Node.js-specific primitives like Atomics or worker_threads. This keeps the codebase portable across Electron main process environments while still providing exclusive access guarantees.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →