# OfficeCLI Core Plugins Extensibility: 6 Mechanisms for Extending the CLI

> Discover 6 OfficeCLI Core Plugins extensibility mechanisms. Extend the CLI with custom verbs, file extensions, and session modes via a language-agnostic architecture.

- Repository: [OfficeAI/OfficeCLI](https://github.com/iofficeai/OfficeCLI)
- Tags: architecture
- Published: 2026-07-14

---

**OfficeCLI Core Plugins extensibility is achieved through a language-agnostic, process-isolated architecture that enables external executables to register new command verbs, handle file extensions via manifest declarations, and execute in both short-lived and persistent session modes.**

The iOfficeAI/OfficeCLI repository implements a modular plugin framework in the **Core/Plugins** directory that allows developers to extend CLI functionality without modifying the core codebase. This system supports automatic discovery, manifest-based capability validation, and subprocess isolation for handling diverse document operations and data transformations.

## Plugin Discovery and Registration

The extensibility system automatically locates plugins using a hierarchical directory structure scanned by [`PluginRegistry.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/PluginRegistry.cs) in [`src/officecli/Core/Plugins/PluginRegistry.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/Plugins/PluginRegistry.cs). This class resolves plugins by **kind** (command verb) and **file extension**, caching `ResolvedPlugin` objects for performance.

Plugins reside in either user-wide (`~/.officecli/plugins`) or application-wide (`$APPDIR/plugins`) directories. The registry expects a specific layout:

```

<root>/<kind>/<ext>/plugin(.exe)

```

When scanning, the registry executes each plugin with the `--info` flag to retrieve its manifest, validates the protocol version, and registers the executable. This discovery mechanism enables **zero-configuration installation**—simply placing a plugin in the correct directory makes it available to the CLI.

## Manifest-Driven Capability Declaration

Each plugin declares its capabilities through a JSON manifest processed by [`PluginManifest.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/PluginManifest.cs) in [`src/officecli/Core/Plugins/PluginManifest.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/Plugins/PluginManifest.cs). The deserializer validates required fields including `protocol`, `kinds`, `vocabulary`, and `targetExtension`.

The manifest serves as the contract between the CLI and the plugin:

- **kinds**: Array of verbs the plugin implements (e.g., `get`, `set`, `save`)
- **targetExtension**: Native format for format-handler plugins (e.g., `.docx`, `.pdf`)
- **vocabulary**: Optional metadata enabling richer UI/help output
- **protocol**: Version identifier ensuring compatibility with the CLI

The main process rejects manifests with mismatched protocol versions or missing required fields, emitting warnings during discovery to prevent runtime failures.

## Process Isolation Architecture

OfficeCLI employs two distinct execution models to balance safety and performance, both implemented in the Core/Plugins layer to protect the host process from plugin crashes.

### Short-Lived Plugin Execution

For transient operations, [`PluginProcess.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/PluginProcess.cs) in [`src/officecli/Core/Plugins/PluginProcess.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/Plugins/PluginProcess.cs) spawns the plugin executable in a subprocess with configurable idle timeouts. The class enforces the `plugin_idle_timeout` setting (overridable via the `OFFICECLI_PLUGIN_IDLE_TIMEOUT` environment variable), killing unresponsive processes to protect the CLI from hangs or crashes.

### Persistent Format Handler Sessions

Complex document operations require stateful interaction. The `FormatHandlerSession` class in [`src/officecli/Core/Plugins/FormatHandlerSession.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/Plugins/FormatHandlerSession.cs) maintains a persistent pipe to format-handler plugins. When opening a document, the CLI spawns the plugin, sends an *open* handshake, then streams commands like `get` and `set` over the pipe until the session closes.

## Command Verb Routing

The CLI delegates operations to plugins through a resolution system defined in [`DocumentHandlerFactory.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/DocumentHandlerFactory.cs) in [`src/officecli/Handlers/DocumentHandlerFactory.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Handlers/DocumentHandlerFactory.cs). When processing a command like `officecli myfile.myext get`, the factory calls `PluginRegistry.ResolvePlugin(kind, ext)`.

If a matching plugin exists, the CLI forwards the verb to the plugin executable; otherwise, it falls back to built-in handlers or reports "unsupported". This routing mechanism allows developers to **add new verbs** by simply listing them in the manifest's `kinds` array and implementing the corresponding logic in the plugin executable.

## Runtime Configuration Options

Plugins expose custom runtime settings through the manifest or environment variables. The `plugin-runner` concept (utilized by [`McpInstaller.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/McpInstaller.cs)) allows specification of execution bridges like `mcpBridge` for alternative execution contexts.

Configuration options include:

- **idleTimeout**: Per-plugin timeout overrides for long-running operations
- **runner**: Alternative execution contexts (e.g., for MCP integration)

The installer writes a [`manifest.json`](https://github.com/iOfficeAI/OfficeCLI/blob/main/manifest.json) and optional bridge configuration, enabling the main process to invoke the plugin via the specified runner.

## Implementation Examples

The following snippets demonstrate how to interact with the plugin system using the Core/Plugins classes:

```csharp
// Resolve a plugin for a given kind and file extension
var registry = new PluginRegistry();
ResolvedPlugin? plugin = registry.ResolvePlugin(kind: "format-handler", ext: ".docx");
if (plugin == null) {
    Console.WriteLine("No plugin found for .docx");
    return;
}

```

```csharp
// Start a short-lived plugin process with timeout protection
var proc = new PluginProcess(plugin.ExecutablePath);
var result = await proc.RunAsync(new[] { "dump-reader", "--info" });
Console.WriteLine(result.StdOut);

```

```csharp
// Open a persistent session with a format-handler plugin
using var session = new FormatHandlerSession(
    filePath: "report.docx",
    plugin: plugin
);
await session.SendAsync(new { verb = "open", args = new {} });
var getResult = await session.SendAsync(new { verb = "get", args = new { path = "body/text" }});
Console.WriteLine(getResult);

```

## Summary

- **Plugin Discovery**: Automatic registration via [`PluginRegistry.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/PluginRegistry.cs) scanning `~/.officecli/plugins` and `$APPDIR/plugins` with layout `<root>/<kind>/<ext>/plugin(.exe)`
- **Manifest Validation**: JSON manifests declare capabilities through [`PluginManifest.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/PluginManifest.cs), requiring `protocol`, `kinds`, and `targetExtension` fields
- **Process Isolation**: Short-lived plugins run via [`PluginProcess.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/PluginProcess.cs) with configurable timeouts; long-running operations use [`FormatHandlerSession.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/FormatHandlerSession.cs) for persistent pipes
- **Verb Extension**: New commands are added by implementing verbs in the manifest's `kinds` array, routed through [`DocumentHandlerFactory.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/DocumentHandlerFactory.cs)
- **Configuration**: Runtime behavior is controlled via manifest fields or the `OFFICECLI_PLUGIN_IDLE_TIMEOUT` environment variable
- **Language Agnostic**: External executables communicate via stdin/stdout streams, making the system compatible with any programming language

## Frequently Asked Questions

### How do I add support for a new file type in OfficeCLI?

Place your plugin executable in the directory structure `<root>/format-handler/<ext>/plugin.exe` (where `<root>` is either `~/.officecli/plugins` or `$APPDIR/plugins`). Declare the `targetExtension` in your manifest's JSON output when invoked with `--info`. The `PluginRegistry` will automatically discover and register the handler for that extension.

### What is the difference between short-lived and long-running plugins?

Short-lived plugins are executed via [`PluginProcess.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/PluginProcess.cs) for single operations like metadata extraction, with automatic termination after `plugin_idle_timeout`. Long-running plugins maintain persistent connections through [`FormatHandlerSession.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/FormatHandlerSession.cs), enabling stateful document editing via streaming commands over pipes.

### How can I implement custom command verbs beyond get, set, and save?

The manifest's `kinds` array accepts any verb string. When you run `officecli <file> <verb>`, the `DocumentHandlerFactory` resolves the plugin by matching the verb against registered `kinds` and the file extension. Implement the verb logic in your plugin executable to handle the specific command.

### How does the system prevent plugin crashes from affecting the core CLI?

Each plugin runs in its own subprocess managed by [`PluginProcess.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/PluginProcess.cs) or [`FormatHandlerSession.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/FormatHandlerSession.cs). The CLI monitors these processes and enforces idle timeouts. If a plugin crashes or hangs, the parent process kills the subprocess and returns an error, protecting the CLI's stability.