Skip to content

Extensibility: MCP, Skills, Plugins ​

Kairox extends in three layers. MCP servers plug in external tools and resources over a standard protocol. Skills are repo-local prompt, tool, and workflow capabilities discovered from the filesystem. Plugins are manifest-driven bundles that ship skills, tools, hooks, and MCP servers together. The three surfaces are deliberate: each one exists where it does because the trade-offs are different.

This page covers all three.

MCP — Model Context Protocol ​

MCP is an open protocol for exposing tools, prompts, and resources to LLMs over a transport-agnostic JSON-RPC channel. Kairox's agent-mcp crate is the client side of that protocol.

Architecture ​

PieceRole
McpClientOne client per server. Handles handshake, capability discovery, and JSON-RPC request/response.
TransportTrait abstracting how messages cross the wire. Shipped: StdioTransport, SseTransport, StreamableHttpTransport.
ServerLifecycleTracks Starting → Ready → Stopped / Failed and reports transitions as McpServer* events.
McpServerManagerTop-level coordinator inside agent-runtime; reads config, starts servers, registers tools.
McpToolAdapterWraps an MCP-exposed tool in the Tool trait so the runtime treats it like any built-in.
CatalogEntryMarketplace metadata for a server (name, description, runtime requirements, install hint).

Transports ​

TransportWhen to useHow to declare
stdioLocal subprocesses that follow the MCP stdio convention (most servers).type = "stdio" plus command and args.
sseRemote HTTP services that speak MCP over Server-Sent Events.type = "sse" plus url and optional headers.
streamable_httpRemote MCP endpoints using the Streamable HTTP transport.type = "streamable_http" plus url and headers.

stdio is the default because most MCP servers ship as binaries or npx/uvx scripts.

Server lifecycle and events ​

McpServerManager emits four events:

EventTrigger
McpServerStartingManager initiates the transport and handshake.
McpServerReadyHandshake succeeds; tools are registered.
McpServerStoppedUser stops the server or shutdown is in progress.
McpServerFailedHandshake or runtime error; carries diagnostic.

Lifecycle is observable from both UIs. The TUI shows server status in the trace panel; the GUI's McpStatusIndicator.vue shows a per-server pill that turns green on Ready, yellow on Starting, red on Failed.

Marketplace catalog ​

agent-mcp exposes a catalog of curated servers. Sources are pluggable:

  • Built-in — a static list compiled into agent-mcp for first-launch discoverability.
  • Remote — a CatalogSource pointing at a JSON manifest hosted elsewhere; the runtime fetches and caches it.

The GUI's marketplace view (apps/agent-gui/src/views/MarketplaceView.vue and supporting components in apps/agent-gui/src/components/marketplace/) renders the catalog, surfaces runtime requirements (Node, Python, etc.), and walks the user through install with progress reporting.

Example: declaring an MCP server in kairox.toml ​

toml
[mcp_servers.git]
type = "stdio"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-git", "--repository", "."]

[mcp_servers.github]
type = "stdio"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-github"]
env = { GITHUB_PERSONAL_ACCESS_TOKEN = "" }

[mcp_servers.search]
type = "sse"
url = "https://example.com/mcp"
headers = { Authorization = "Bearer ${SEARCH_TOKEN}" }

[mcp_servers.remote-http]
type = "streamable_http"
url = "https://example.com/mcp"
api_key_env = "MCP_API_TOKEN"

For stdio servers, an empty env value means "read the environment variable with the same name when the server starts." The full schema lives in Configuration.

Skills — native prompt, tool, and workflow capabilities ​

agent-skills is the in-process extensibility layer. A skill is a markdown file with YAML frontmatter that declares a reusable capability — a prompt, a tool wiring, a workflow recipe, or some combination.

Anatomy of a skill ​

markdown
---
name: pr-review
description: Review a pull request diff with focus on correctness and tests.
scope: workspace
keywords: [review, pr, diff]
tools: [shell, fs.read]
---

You are a thorough code reviewer. The user will share a PR. Walk through:

1. The diff, file by file.
2. Test coverage of changed lines.
3. Any new public APIs and their docs.
4. Risk: data migrations, security, performance.

Conclude with a one-paragraph verdict and a labeled list of must-fix items.

The frontmatter is parsed into SkillFrontmatter; the body is the prompt body. The skill becomes a SkillDef in the SkillRegistry.

Frontmatter fields ​

FieldTypeRequiredMeaning
namestringyesStable identifier; namespaced by source.
descriptionstringyesOne-line summary shown in pickers and the settings UI.
scopeenumyesuser / workspace / session — where the skill applies.
keywordsstring[]noDiscovery hints; the runtime can match user prompts against keywords.
toolsstring[]noTools the skill expects to call; the runtime ensures they are registered before run.
modelstringnoPin a specific model profile when this skill runs.
argumentsobjectnoDeclared inputs; UI renders a form.

Scopes ​

ScopeLoaded fromVisible in
user~/.kairox/skills/ and configured user dirsAll sessions for this user.
workspace.kairox/skills/ inside the workspaceSessions started in this workspace.
sessionAd-hoc, in-memory, attached to a sessionOnly the originating session.

The registry deduplicates by name with workspace-scoped skills overriding user-scoped skills, and session-scoped skills overriding both. The GUI's SkillsSettingsView.vue lets a user inspect, enable, and disable skills per scope without touching the filesystem.

SkillHub install ​

The marketplace integrates with SkillHub (or equivalent skill registries) to install skills into the configured user or workspace directory. Installs are file-write operations that go through the policy engine like everything else — under the default ApprovalPolicy::OnRequest + SandboxPolicy::WorkspaceWrite pair, an install prompts for fs.write whenever the target directory falls outside the sandbox's writable roots.

Plugins — manifests that ship bundles ​

A plugin packages skills, tools, hooks, and MCP server declarations together. agent-plugins parses the manifest and feeds the inventory to the relevant crates.

Manifest ​

Kairox resolves plugin manifests in this order: .kairox-plugin/plugin.json, .codex-plugin/plugin.json, then .claude-plugin/plugin.json. MCP server inventory can be declared through the manifest's mcpServers field or through a sibling .mcp.json file.

json
{
  "name": "my-plugin",
  "version": "0.2.0",
  "description": "Project workflow helpers.",
  "homepage": "https://github.com/example/kairox-my-plugin",
  "skills": "./skills/",
  "mcpServers": {
    "issue-tracker": {
      "command": "node",
      "args": ["./mcp/issue-tracker.js"]
    }
  },
  "hooks": [
    {
      "event": "pre_turn",
      "script": "./hooks/inject-context.js"
    }
  ],
  "permissions": {
    "approvalPolicy": "on_request",
    "sandboxPolicy": "workspace_write",
    "tools": ["shell.exec", "fs.read"]
  },
  "compatibility": {
    "kairoxVersion": ">=0.43.0 <0.44.0",
    "platforms": ["macos", "linux"],
    "requires": ["node >=20", "git"]
  },
  "publisher": "Example Labs",
  "trust": "community"
}

Codex-compatible plugins often keep MCP declarations in .mcp.json instead:

json
{
  "mcpServers": {
    "issue-tracker": {
      "command": "node",
      "args": ["./mcp/issue-tracker.js"]
    }
  }
}

Inventory ​

PluginManifestView exposes a flat inventory plus permission, compatibility, and trust metadata for settings and marketplace display. Each kind of contribution is routed to its owning crate:

ContributionRouted to
skillSkillRegistry (with the plugin name as namespace)
toolToolRegistry
MCP serverMcpServerManager (via agent-config merge)
hookruntime hook registry

Skills shipped via plugins are namespaced as <plugin>:<name>. The convention prevents two plugins from colliding on the same skill name and gives users a clear path back to the source.

Settings ​

Plugins have first-class GUI settings: enable / disable as a whole, enable / disable individual contributions, override paths. Disabled contributions do not load, even if their files exist.

Plugins vs MCP servers ​

Plugins can include MCP servers. The distinction:

  • An MCP server declared in kairox.toml is a user-level configuration choice; the user maintains the install.
  • An MCP server bundled in a plugin ships with the plugin; the user installs the plugin and the server comes along.

If you ship a workflow tool, prefer a plugin so the user gets one install instead of three. If you maintain a long-lived MCP server that many people use independently, ship it standalone and let users wire it up.

LSP & DAP — code intelligence and debugging ​

The agent-lsp crate provides Language Server Protocol (LSP) and Debug Adapter Protocol (DAP) clients. Unlike MCP servers — which expose new capabilities — LSP and DAP servers give the agent access to existing developer tooling: go-to-definition, find-references, hover docs, breakpoints, and variable inspection.

Architecture ​

TypeKey structPurpose
LSP clientLspClientJSON-RPC client that speaks the LSP protocol over a stdio transport
DAP clientDapClientJSON-RPC client that speaks the DAP protocol over a stdio transport
LifecycleLspServerLifecycle / DapServerLifecycleOwns the child process, tracks ServerStatus, handles start / stop / restart
TransportLspStdioTransportSpawns the server process, wires stdin/stdout, drains stderr to tracing
Tool providersLspToolProvider / DapToolProvider (in agent-tools)Wrap the clients as dynamic Tool instances so the agent can call them

Server lifecycle ​

Each LSP/DAP server is defined in configuration and managed by a lifecycle struct. The lifecycle:

  1. Spawns the server process via stdio transport.
  2. Sends the initialize request with project root URI and client capabilities.
  3. Tracks ServerStatus — Stopped, Starting, Running, or Failed.
  4. On shutdown, sends shutdown + exit notifications and kills the child process.

Dynamic tool injection ​

When an LSP server starts, the runtime registers an LspToolProvider that exposes LSP operations — textDocument/definition, textDocument/references, textDocument/hover, etc. — as tools the agent can call during a session. DAP servers work the same way via DapToolProvider, exposing debug operations like launch, setBreakpoints, and variables.

These tools appear alongside MCP tools and built-in tools in the tool registry. The agent picks the right tool based on the task — a search.ripgrep for text search, an LSP textDocument/definition for precise navigation.

Example: declaring an LSP server in kairox.toml ​

toml
[lsp_servers.rust-analyzer]
command = "rust-analyzer"
args = []
languages = ["rust"]
file_patterns = ["*.rs"]
toml
[dap_servers.codelldb]
command = "codelldb"
args = ["--port", "0"]
languages = ["rust", "c", "cpp"]

Choosing the right surface ​

NeedUse
Reusable prompt for the current project, edited inline.Workspace skill
Personal prompt library that travels with the user.User skill
External capability spoken to over a process / network boundary.MCP server
Code intelligence (go-to-definition, references, hover).LSP server
Interactive debugging (breakpoints, stepping, variables).DAP server
Bundle of related skills + tools + hooks + MCP for a workflow.Plugin
One-off scratch prompt for a single session.Session skill
Behavior change that should apply to every session in the repo.Instructions config (see Configuration)

What this page does not cover ​

This page describes how external capabilities reach the runtime. It does not cover the configuration schema for any of these surfaces — that lives in Configuration. It does not cover the runtime's per-turn behavior — that is in Runtime & Sessions.

Released under the Apache-2.0 License.