Skip to content

Plugin Development Lifecycle and Sandbox Contracts

Plugin Engagement Baseline Protocol

Strictly prohibit the abuse of Plugins as a replacement for Workflow execution graphs. The authorization scope and security audit overhead of Plugins are exponentially higher than generic concurrent telemetry streams. The Plugin development flow may only be initiated upon encountering the following requisites:

  • Abstracting and exposing an MCP interface tier (Tool Schema) unsupported natively
  • Bridging Out-of-Process systems or Native local binary suites
  • Dynamically registering secondary extension topologies (Domains / Workflows / Metrics)
  • Clamping the API overshoot of the security sandbox, enforcing rigid toolExecution allowlist pruning

Minimal Viable Plugin (MVP) Topology

Built upon a declarative fluent builder pattern, all capability orchestration must adhere to an inline, brace-free chaining invocation convention. Reference manifest.ts within the jshook_plugin_template repository:

ts
import { createExtension, jsonResponse } from '@jshookmcp/extension-sdk/plugin';

export default createExtension('io.github.example.my-first-plugin', '1.0.0')
  .compatibleCore('^0.1.0')
  .allowTool(['page_click', 'network_get_requests'])
  .tool(
    'my_custom_tool',
    'Execute DOM mutation and fetch side-effect traces.',
    { selector: { type: 'string', description: 'CSS selector to click' } },
    async (args, ctx) => {
      const clickRes = await ctx.invokeTool('page_click', { selector: String(args.selector) });
      return jsonResponse({ success: true, result: clickRes });
    },
  )
  .onLoad((ctx) => {
    ctx.setRuntimeData('init_stamp', Date.now());
  });

Outward-facing plugin metadata now lives in meta.yaml, while manifest.ts stays focused on runtime declarations:

yaml
name: My First Plugin
description: Execute DOM mutation and fetch traces.
author: your-team
source_repo: https://github.com/your-org/my-first-plugin

Contract Breakdown:

  • createExtension(id, version): Distinct identity root for the plugin isolated within the core registry.
  • .allowTool(...): Mandatory built-in tool allowlist; illicit boundary traversing via invokeTool will trigger an exception.
  • .tool(...): Projects new MCP tools into the user-facing RPC gateway.
  • invokeTool: Invokes internal atomic actions strictly within the verified ctx sandbox context.
  • onLoad: The initial bootstrap hook, reserved for dependency allocation and logging prior to state mutation.

Standard Development Iteration Bus

1. Mount the Environment Topology

  • Source Template: https://github.com/vmoranv/jshook_plugin_template
  • Mount Main Process Pointer: export MCP_PLUGIN_ROOTS=<path-to-cloned-jshook_plugin_template>
  • PowerShell: $env:MCP_PLUGIN_ROOTS = "<path-to-cloned-jshook_plugin_template>"

2. Pre-compilation Constraint Verification

bash
pnpm install
pnpm run build
pnpm run check

Engineering Protocol: The local environment utilizes a TS-first verification strategy, hard-locking the source to manifest.ts. The main engine probes dist/manifest.js conforming to timestamp validations to trigger AST load optimization.

3. Namespace Isolation Identifier Replacement

Prior to mounting, you must supersede the globally conflicting references originating from the template:

  • PLUGIN_ID (Strictly requires the x.y.z reverse-domain format, e.g., io.github.example.my-plugin)
  • meta.yaml fields: name / description / author / source_repo

4. Privilege Sandbox Allowlist Clamping

The Plugin engine relies on an allowlist mechanism to validate side-effect capabilities. The lifecycle must be confined to the declaration stack:

  • toolExecution.allowTools: Restricts the scope of penetrating invocations executed via ctx.invokeTool().
  • network.allowHosts: Governs the whitelist headers for target sockets.
  • process.allowCommands: Blocks extraneous derivation of external sub-processes.
  • filesystem.readRoots / filesystem.writeRoots: Enforces mandatory I/O caging.

Disciplinary Requirements:

  • Adhere to the principle of least privilege immediately at the initial lifecycle setup.
  • The use of generalized wildcards (*) is strictly forbidden during pre-production validation tiers.

API Resolution: ExtensionBuilder Lifecycle

Runtime Context Hooks

The engine attributes structured sequential callbacks:

onLoad(ctx)

Reserved for minimal bootstrap sequences:

  • Load .env from local mounted directory.
  • Register static caches and handler signatures.
  • Inject short-lived runtime data.

onValidate(ctx)

Execute boundary condition interceptors:

  • Verify essential configuration parity.
  • Probe dependencies for health availability.
  • Audit baseUrl / Loopback endpoint legitimacy.

onActivate(ctx) / onDeactivate(ctx) / onUnload(ctx)

Takeover controls for memory and network bounds:

  • Procure hardware resource allocations on activation.
  • Terminate active IPC connections during deactivation.
  • Execute complete teardown phase on unload.

Capabilities Provided by PluginLifecycleContext

ctx.invokeTool(name, args?)

The foremost runtime capability governed by rigid isolation guards:

  • May solely invoke built-in tools.
  • Must undergo pre-verification through permissions.toolExecution.allowTools.
  • Required to be present within the currently active profile tier.

An allowlist verification is a requirement, not an exception; mismatches in Profile capability vectors result in denial.

ctx.getConfig(path, fallback)

Extract read-only environment variables mapped explicitly to the designated Plugin instance, prohibiting overall configuration exposure.

ctx.setRuntimeData(key, value) / ctx.getRuntimeData(key)

Handle fleeting state bits confined functionally to the Plugin closure context:

  • Boot synchronization timestamps.
  • Long-haul initialization caching sets.
  • Network interception states.

ctx.hasPermission(capability)

Asserts compliance queries concerning capability declarations mapped against the manifest constraints.

Helper Implementation Vectors

  • jsonResponse(payload) / errorResponse(tool, error, extra?)
  • checkExternalCommand(...) / runProcess(...)
  • resolveOutputDirectory(...) / requestJson(...)

Context Reentry Affirmation

Assert the lifecycle integrity via the service terminal sequence:

  1. Reinitialize context via reload_extensions.
  2. Inspect the loaded plugin/tool state via list_extensions.
  3. Call the extension tool directly, or use search_tools / describe_tool to inspect it.
  4. Run list_extension_workflows (If Workflow topologies are concurrently injected).

Rerun standard TS-to-JS transpilation chains via pnpm run build prior to invoking the subsystem reload probe.

reload_extensions now registers extension tools directly into the MCP tool list when they are visible in the current profile. You only need activate_tools later for tools outside the current profile, or call_tool when a client failed to refresh its cached tool list.

The tools array returned by list_extensions / reload_extensions now exposes:

  • profiles: declared profile visibility
  • visibleInCurrentProfile: whether the tool is visible in the current session profile
  • active: whether the tool is currently registered in the tool list
  • activationSource: how it became active (reload / activate_tools / activate_domain)

Conventional Transgressions

  • Operating a plugin as an unmanaged proxy mapping layer toward generic internal OS elements.
  • Omission of explicit declarations surrounding toolExecution.allowTools.
  • Ignoring boundaries of the active capability Profile assuming prior validation holds precedence.
  • Misallocating parallel procedural pipelines as Plugins rather than proper syntax orchestration Workflows.
  • Distributing .js intermediary compilation artifacts into production code mirrors.

Released under AGPL-3.0-only