pctx_code_execution_runtime 0.1.0

JavaScript/TypeScript execution runtime for pctx
docs.rs failed to build pctx_code_execution_runtime-0.1.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

PCTX Code Execution Runtime

A Deno extension providing:

  • MCP Client: Model Context Protocol client functionality
  • JS Local Tools: JavaScript callback-based tools with dependency support
  • Console Capturing: Automatic stdout/stderr capture
  • Network Permissions: Host-based fetch controls

Primary Use Case: TypeScript SDK with Dependencies

The main use case is running PCTX as a TypeScript SDK where users define tools with their own dependencies:

// User has Zod in their environment
import { z } from 'npm:zod';

// Define a tool that uses Zod
registerJsLocalTool({
    name: "createUser",
    description: "Creates a user with validation"
}, (args) => {
    const UserSchema = z.object({
        name: z.string().min(2),
        email: z.string().email(),
        age: z.number().min(0).max(120)
    });
    return UserSchema.parse(args); // Uses Zod for validation!
});

// Sandboxed code calls the tool (doesn't need Zod directly)
const user = await callJsLocalTool("createUser", {
    name: "Alice",
    email: "alice@example.com",
    age: 30
});

Key benefit: Dependencies (Zod, database clients, APIs, etc.) are available where tools are defined (trusted zone), but sandboxed code just calls the tools without needing dependency access.

See the complete example: typescript_sdk_with_dependencies.rs

Quick Start

use deno_core::{JsRuntime, RuntimeOptions};
use pctx_code_execution_runtime::{
    pctx_runtime_snapshot, MCPRegistry, JsCallableToolRegistry,
    AllowedHosts, RUNTIME_SNAPSHOT
};

// Create registries
let mcp_registry = MCPRegistry::new();
let js_local_tool_registry = JsCallableToolRegistry::new();
let allowed_hosts = AllowedHosts::new(Some(vec!["example.com".to_string()]));

let mut runtime = JsRuntime::new(RuntimeOptions {
    startup_snapshot: Some(RUNTIME_SNAPSHOT),
    extensions: vec![pctx_runtime_snapshot::init(
        mcp_registry,
        js_local_tool_registry,
        allowed_hosts
    )],
    ..Default::default()
});

// MCP API and JS Local Tools are now available in JavaScript
let code = r#"
    // Register an MCP server
    registerMCP({ name: "my-server", url: "http://localhost:3000" });

    // Register a local tool with a callback
    registerJsLocalTool({
        name: "calculator",
        description: "Performs arithmetic",
    }, (args) => {
        return args.a + args.b;
    });

    // Call MCP tool
    const mcpResult = await callMCPTool({
        name: "my-server",
        tool: "get_data",
        arguments: { id: 42 }
    });

    // Call local tool
    const localResult = await callJsLocalTool("calculator", { a: 10, b: 5 });

    console.log("MCP:", mcpResult, "Local:", localResult);
"#;

runtime.execute_script("<main>", code)?;

Rust API Reference

Core Types

MCPRegistry

Thread-safe registry for MCP server configurations.

let registry = MCPRegistry::new();

JsCallableToolRegistry

Thread-safe registry for local tool metadata (callbacks stored in JS).

let callable_registry = JsCallableToolRegistry::new();

// Query tools
if callable_registry.has("my-tool") {
    println!("Tool exists!");
}

let metadata = callable_registry.get_metadata("my-tool");
let all_tools = callable_registry.list();

AllowedHosts

Whitelist of hosts allowed for network access.

let allowed_hosts = AllowedHosts::new(Some(vec![
    "example.com".to_string(),
    "api.service.com".to_string(),
]));

Snapshot

RUNTIME_SNAPSHOT

Pre-compiled V8 snapshot containing the runtime.

pub static RUNTIME_SNAPSHOT: &[u8] = /* ... */;

Examples

Console Output Capture

let code = r#"
    console.log("Line 1");
    console.log("Line 2");
    console.error("Error line");

    export default {
        stdout: globalThis.__stdout,
        stderr: globalThis.__stderr
    };
"#;

let result = runtime.execute_script("<capture>", code)?;

// Extract captured output
let scope = &mut runtime.handle_scope();
let local = v8::Local::new(scope, result);
let output = serde_v8::from_v8::<serde_json::Value>(scope, local)?;

println!("Stdout: {:?}", output["stdout"]);
println!("Stderr: {:?}", output["stderr"]);

Network Permissions

// Allow only specific hosts
let allowed_hosts = AllowedHosts::new(Some(vec![
    "api.example.com".to_string(),
    "cdn.example.com".to_string(),
]));

let mut runtime = JsRuntime::new(RuntimeOptions {
    startup_snapshot: Some(RUNTIME_SNAPSHOT),
    extensions: vec![pctx_runtime_snapshot::init(
        MCPRegistry::new(),
        allowed_hosts
    )],
    ..Default::default()
});

let code = r#"
    // This will succeed
    await fetch("http://api.example.com/data");

    // This will fail - host not allowed
    try {
        await fetch("http://malicious.com/data");
    } catch (e) {
        console.error("Blocked:", e.message);
    }
"#;

runtime.execute_script("<permissions>", code)?;

Security

Network Access

  • Only whitelisted hosts can be accessed via fetch()
  • Attempts to access non-whitelisted hosts throw errors
  • Host matching is exact (no wildcards)

MCP Registry

  • Each runtime instance has its own isolated registry
  • No cross-runtime access to MCP configurations
  • Registry is not persisted between runtime sessions

Console Capture

  • Captured output is stored in runtime-local buffers
  • No disk I/O or external logging
  • Buffers cleared when runtime is dropped

Performance

  • Startup: Instant (V8 snapshot loads in <1ms)
  • Memory: ~2MB base runtime overhead
  • MCP Operations: Native Rust performance
  • Console Capture: Minimal overhead (~1% per log)

License

MIT

Contributing

Contributions welcome! Please ensure:

  • All tests pass: cargo test --package pctx_runtime
  • Code is formatted: cargo fmt
  • Documentation is updated

Features

JS Local Tools

Local tools allow you to define JavaScript callbacks that can be invoked from sandboxed code. See JS_LOCAL_TOOLS.md for comprehensive documentation.

Quick example:

// Register a tool with a callback
registerJsLocalTool({
    name: "file-reader",
    description: "Reads a file from the host system",
    inputSchema: {
        type: "object",
        properties: {
            path: { type: "string" }
        }
    }
}, async (args) => {
    const fs = require('fs').promises;
    return await fs.readFile(args.path, 'utf8');
});

// Call the tool from sandboxed code
const content = await callJsLocalTool("file-reader", { path: "./data.txt" });

Benefits:

  • Fast: No IPC overhead
  • Simple: Just JavaScript callbacks
  • Flexible: Sync or async
  • Type-safe: Full TypeScript support

MCP Client

Connect to external MCP servers for tool integration.

registerMCP({
    name: "github",
    url: "http://localhost:3000"
});

const result = await callMCPTool({
    name: "github",
    tool: "create_issue",
    arguments: { title: "Bug report" }
});

Examples

See the examples/ directory:

  • typescript_sdk_with_dependencies.rs - Primary use case: Shows how TypeScript SDK users can define tools with custom dependencies (Zod, DB clients, etc.) and use them from sandboxed code

See Also