How to Implement IDE Integration via the ACP Protocol in Kimi Code: A Complete Guide
Kimi Code exposes an Agent Client Protocol (ACP) JSON-RPC surface in the packages/acp-adapter package that enables IDEs to drive Kimi sessions, execute slash commands, and receive real-time updates via stdio or TCP transport.
The MoonshotAI/kimi-code repository provides a dedicated protocol layer that bridges the core engine with external development environments. By implementing IDE integration via the ACP protocol, you can build editor plugins that leverage the full capabilities of the @moonshot-ai/kimi-code-sdk while maintaining clean separation between the AI engine and transport mechanisms.
Understanding the ACP Architecture
The ACP stack in packages/acp-adapter consists of three distinct architectural layers that handle protocol negotiation, session state, and command translation:
| Layer | Responsibility | Main Source |
|---|---|---|
| Server | Creates an AcpServer (an Agent) that listens on stdio/TCP, negotiates protocol version, and routes incoming RPC calls to a per-session AcpSession. |
[src/server.ts](https://github.com/MoonshotAI/kimi-code/blob/main/packages/acp-adapter/src/server.ts) |
| Session | Wraps a @moonshot-ai/kimi-code-sdk Session and translates SDK events into ACP notifications (session/update, config_option_update, …). Handles model/think/mode changes, slash-command routing, and history replay. |
[src/session.ts](https://github.com/MoonshotAI/kimi-code/blob/main/packages/acp-adapter/src/session.ts) |
| Helpers | Parse slash commands, map ACP enums, build config options, compress images, etc. | [src/slash.ts](https://github.com/MoonshotAI/kimi-code/blob/main/packages/acp-adapter/src/slash.ts) – [src/types.ts](https://github.com/MoonshotAI/kimi-code/blob/main/packages/acp-adapter/src/types.ts) – [src/config-options.ts](https://github.com/MoonshotAI/kimi-code/blob/main/packages/acp-adapter/src/config-options.ts) |
Starting the ACP Server
To enable IDE integration via the ACP protocol, your entry point must initialize the Kimi harness and start the JSON-RPC server. The server implementation in src/server.ts handles protocol version negotiation (defined in src/version.ts) and manages the lifecycle of individual sessions.
// Example entry point (run with node)
import { runAcpServer } from '@moonshot-ai/acp-adapter';
import { createHarness } from '@moonshot-ai/kimi-code-sdk';
async function main() {
const harness = await createHarness(); // core engine
await runAcpServer({ // stdio JSON-RPC (ideal for IDE plugins)
harness,
stdio: true, // reads/writes from process.stdin/out
// optional: specify a custom port for TCP mode
});
}
main().catch(console.error);
When a client connects, the server creates a fresh AcpSession for every session/new request, isolating conversation state and configuration between different IDE instances.
Managing Sessions and RPC Communication
Session Creation
The IDE client initiates communication by sending a session/new request with an optional working directory. The AcpServer.newSession method constructs an AcpSession instance that holds the underlying SDK Session and maintains a reference to the AgentSideConnection.
The session automatically registers approval and question handlers in src/session.ts, ensuring that permission prompts and clarification requests are forwarded over the ACP connection rather than blocking on the server console.
Sending Prompts
Clients invoke the session/prompt method with an array of ContentBlock objects containing either plain text or base64-encoded images. The AcpSession.prompt implementation validates input and routes messages to the SDK while managing conversation flow.
import { AgentSideConnection, initialize, RequestError } from '@agentclientprotocol/sdk';
async function connect() {
const conn = new AgentSideConnection({ stdio: true }); // stdio or TCP socket
await initialize(conn, { clientName: 'MyIDE', protocolVersion: 2 });
// Create a new session
const { sessionId } = await conn.sessionNew({ cwd: process.cwd() });
// Send a prompt (plain text)
const resp = await conn.sessionPrompt({
sessionId,
blocks: [{ type: 'text', text: 'Explain the difference between HTTP and HTTPS.' }],
});
console.log('Stop reason:', resp.stopReason);
}
connect().catch(err => {
if (err instanceof RequestError) console.error('ACP error:', err);
else console.error(err);
});
Executing Slash Commands and Configuration Changes
Slash Command Routing
When processing prompts, AcpSession.prompt checks for leading slash commands via detectLeadingSlashIntent in src/slash.ts. Recognized built-in commands (defined in src/builtin-commands.ts) such as /compact, /status, or /usage are handled locally through runBuiltInCommand. Unknown commands return helpful error messages to the client without crashing the session.
// In the IDE plugin, send the slash command directly:
await conn.sessionPrompt({
sessionId,
blocks: [{ type: 'text', text: '/compact' }],
});
The server routes this to AcpSession.runBuiltInCommand('compact'), which triggers conversation compaction and returns stopReason: 'end_turn' after emitting a status update.
Model and Thinking Configuration
Clients adjust behavior by calling session/set_model, session/set_mode, or session/set_config_option. The adapter splits legacy model identifiers containing ",thinking" into separate model and thinking config options within the setModel logic.
await conn.sessionSetConfigOption({
sessionId,
configId: 'model',
value: 'kimi-k2', // base model id (no ",thinking")
});
await conn.sessionSetConfigOption({
sessionId,
configId: 'thinking',
value: 'high', // one of the model's supported effort levels
});
After updating the underlying SDK session via Session.setModel, Session.setThinking, or Session.setPlanMode, the adapter emits a config_option_update notification through emitConfigOptionUpdate, allowing the IDE UI to refresh pickers instantly.
Streaming Real-Time Updates
The SDK emits granular events (assistant.delta, tool.call.started, tool.call.delta, turn.ended) that AcpSession.runTurnBody captures via session.onEvent. The mapping logic in src/events-map.ts converts these into standardized ACP session/update notifications pushed through conn.sessionUpdate.
Tool-call streams are lazily initialized to ensure clients never receive a tool_call_update before the corresponding tool_call CREATE message, maintaining protocol consistency. Type definitions for status enums (stopReason, toolCallStatus, toolKind) reside in src/types.ts.
Implementing History Replay and Reconnection
When an IDE client reconnects after a disconnection, it can call session/load to restore state. The AcpSession.replayHistory method walks the persisted Session.getResumeState() snapshot, synthesizing session/update notifications that reconstruct the prior conversation exactly as if the client had observed it live.
This mechanism ensures seamless recovery from network interruptions without losing context or requiring the user to reprompt.
Summary
- The ACP protocol provides a JSON-RPC interface through three layers: Server (
src/server.ts), Session (src/session.ts), and Helpers (src/slash.ts,src/types.ts,src/config-options.ts). - Transport flexibility allows the server to run over stdio (ideal for IDE extensions) or TCP sockets depending on the
stdioparameter inrunAcpServer. - Slash commands are parsed via
detectLeadingSlashIntentand executed throughrunBuiltInCommand, with built-ins defined insrc/builtin-commands.ts. - Real-time synchronization happens through event mapping in
src/events-map.ts, which translates SDK events into ACP notifications while maintaining proper message ordering for tool calls. - Session resilience is achieved through
replayHistory, which reconstructs conversation state fromSession.getResumeState()when clients reconnect.
Frequently Asked Questions
What transport protocols does the Kimi Code ACP implementation support?
The AcpServer implementation in src/server.ts supports both stdio and TCP transports. Use stdio: true for IDE extensions that spawn the server as a subprocess, or specify a custom port for TCP mode when running the adapter as a standalone service.
How does the ACP protocol handle model configuration changes?
When a client calls session/set_config_option for the model or thinking config IDs, the AcpSession forwards the request to the SDK methods Session.setModel or Session.setThinking. The adapter automatically splits combined model strings (e.g., "kimi-k2,thinking") into separate configuration options before emitting a config_option_update notification.
Can the ACP server handle multiple concurrent IDE sessions?
Yes. Each session/new request creates an isolated AcpSession instance with its own underlying SDK Session. The server maintains a registry of active sessions, routing RPC calls to the appropriate session instance based on the sessionId parameter, enabling multiple IDE windows or users to share a single server process.
What is the difference between built-in slash commands and custom skills?
Built-in commands like /compact and /status are defined in src/builtin-commands.ts and handled locally by AcpSession.runBuiltInCommand. Custom skills invoked via /skill:<name> are resolved through the slash command parser in src/slash.ts but executed through the SDK's skill system, allowing the IDE to trigger specialized capabilities without hardcoding logic in the adapter layer.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →