An MCP server implementation for Programmatic Tool Calling (PTC) that runs JavaScript.
The concept of Programmatic Tool Use was pioneered by the Anthropic Claude API, allowing models to write code that orchestrates multiple tools in a single turn. This project provides an open-source, model-agnostic alternative for environments where native PTC is unavailable, bringing the same powerful orchestration capabilities to any LLM (OpenAI, Gemini, local models) via the standard MCP protocol.
This server allows Large Language Models (LLMs) to execute complex, multi-step tool orchestrations within a secure, isolated QuickJS WASM sandbox, shifting the execution logic from the LLM reasoning loop to deterministic local execution.
Traditional LLM tool usage relies on sequential round-trips (Reason -> Call Tool A -> Wait -> Reason -> Call Tool B). PTC compresses these multi-step dependencies into a single programmatic execution script, providing:
- Reduced Latency: Eliminates network and inference overhead by executing orchestration locally.
- Concurrency: Enables true parallel execution of multiple MCP tools using
Promise.all. - Control Flow: Handles loops, conditionals, and data aggregation directly in the sandbox without relying on LLM context.
- Context Efficiency: Returns only the final, distilled data structure back to the LLM context window.
PTC MCP supports two distinct architectures depending on your deployment topology.
Operates as a transparent proxy using STDIO. It dynamically spawns local child MCP servers based on a configuration and executes the JS micro-loop internally.
-
Configure Sub-Servers: Create a
sub-mcp-servers.jsonin your project root to define underlying tools:{ "chrome": { "command": "npx", "args": ["-y", "chrome-devtools-mcp@latest"] }, "fs": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "."] } } -
Add to your Client:
- Gemini CLI:
gemini mcp add ptc npx -y js-ptc-mcp gateway
- Claude Code:
claude mcp add ptc npx -y js-ptc-mcp gateway
- Manual (Cursor / Claude Desktop):
Add the following to your
mcpServersconfiguration:{ "mcpServers": { "js-ptc-mcp": { "command": "npx", "args": ["-y", "js-ptc-mcp", "gateway"] } } }
- Gemini CLI:
💡 Best Practice: Direct Tool Exposure To prevent port collisions and resource exhaustion, do NOT register tools like
chrome-devtools-mcpdirectly in your client config if they are managed by the PTC Gateway. The Gateway automatically exposes all child tools (e.g.,chrome.navigate_page) to the LLM.
Operates as an orchestrator using SSE (Server-Sent Events). It executes no tools itself, instead relying on Inversion of Control (IoC) to suspend execution and request your secure backend to perform the operations.
- Set Up: Copy
.env.exampleto.envand configure yourPTC_API_KEY. - Run Server:
npx -y js-ptc-mcp remote --port 3000
In Remote Mode, your backend must implement a simple loop to handle tool requests from the sandbox. When the sandbox needs a tool, it returns a need_client_tool status.
Workflow:
- Call
run_js_code. - If status is
need_client_tool:- Execute the requested tools in your secure environment.
- Call
resume_js_codewith the results.
- Repeat until status is
success.
Example: See
client-sample/remote/backend-service.jsfor a full implementation of the execution loop using the MCP SDK.
The QuickJS WASM environment is strictly sandboxed, focusing on state-machine orchestration, data manipulation, and general-purpose logic. It can be used for both complex tool chaining and simple pure JavaScript calculations or data transformations.
- Available:
call_client_tool("alias.tool_name", args),print(data),async/await,Promise.all, and standard ES2022 primitives (Math, Date, Array, String, etc.). - Constraints: NO
fetch, NO timers (setTimeout), and NO Node.js/Browser APIs. All external interactions must be routed throughcall_client_tool.
Scenario A: Pure JavaScript Calculation
const fib = (n) => n <= 1 ? n : fib(n - 1) + fib(n - 2);
return { result: fib(10) };Scenario B: Tool Orchestration
print("1. Starting parallel data fetching...");
// Execute multiple underlying MCP tools concurrently
const [user, config] = await Promise.all([
call_client_tool("db.get_user", { id: 123 }),
call_client_tool("fs.read_file", { path: "config.json" })
]);
// Handle conditional logic within the sandbox
if (user.isAdmin) {
print("2. Admin detected, applying special config...");
await call_client_tool("db.update", { id: 123, status: "active" });
}
// Return the distilled result to the LLM context
return {
username: user.name,
settings: config.theme
};Understanding the Two Modes
When integrating PTC, developers face a critical divergence: Where do the underlying tools live, and who executes them?
- Gateway Mode (STDIO): Standard clients (e.g., Cursor) are stateless schedulers unable to handle suspend/resume loops. Gateway Mode resolves this by keeping the micro-loop internal. It acts as a black box router to local child processes, making it ideal for local development environments.
- Remote Mode (SSE): Custom Cloud Agents (e.g., SaaS platforms) utilize proprietary backend APIs. Executing these from a remote sandbox introduces security risks. Remote Mode utilizes SSE to stream "suspend/interrupt" signals to your backend, allowing your secure environment to execute the tools and inject the results back. Ideal for distributed web architectures.
Both modes share 100% of the underlying QuickJS deterministic state-machine, ensuring memory safety and concurrency regardless of the deployment strategy.
src/core/: The heart of the engine (Sandbox, Logic, State Machine).src/modes/: Implementations of the Gateway (STDIO) and Remote (SSE) servers.client-sample/: Example Node.js clients demonstrating each mode.
MIT