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 ServerStatusStopped, 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.