The Agent Client Protocol for MoonBit: editors talking to agents, over ASGI or stdio.
moon add moonbitstack/moonacpimpl @moonacp.Agent for Notes with fn initialize(self, request) {
ignore(self)
ignore(request)
{
..@moonacp.Initialized::new(),
capabilities: {
..@moonacp.AgentCapabilities::none(),
prompt: { image: true, audio: false, embedded_context: true, meta: None },
},
agent_info: Some(@moonacp.Implementation::new("notes", "0.1.0")),
}
}
impl @moonacp.Agent for Notes with fn new_session(self, request) {
self.sessions = self.sessions + 1
{ session_id: "notes-\{self.sessions}", modes: None, config_options: None, meta: None }
}
impl @moonacp.Agent for Notes with fn prompt(self, request, session) {
let text = request.prompt.map(block => match block {
@moonacp.ContentBlock::Text(content) => content.text
_ => ""
}).join(" ")
session.update(@moonacp.SessionUpdate::AgentMessageChunk({
content: @moonacp.ContentBlock::Text({ text: "You said: " + text, annotations: None, meta: None }),
message_id: None,
meta: None,
}))
@moonacp.StopReason::EndTurn
}async fn main {
@stdio.serve_stdio(Notes::new())
}let child = @stdio.spawn(editor, ["/usr/local/bin/notes-agent"])
let conn = child.conn()
@async.with_task_group(async fn(group) {
group.spawn_bg(async fn() { conn.run() })
ignore(conn.initialize({
protocol_version: @moonacp.protocol_version,
client_capabilities: @moonacp.ClientCapabilities::none(),
client_info: None,
meta: None,
}))
let session = conn.new_session({
cwd: "/work",
additional_directories: None,
mcp_servers: [],
meta: None,
})
let answer = conn.prompt({
session_id: session.session_id,
prompt: [@moonacp.ContentBlock::Text({ text: "hello", annotations: None, meta: None })],
meta: None,
})
println(answer.stop_reason.to_string())
})| Transport | Entry point | Shape |
|---|---|---|
| stdio | @stdio.serve_stdio, @stdio.spawn | One JSON-RPC message per line on the process's own streams. The transport an editor uses when it launches an agent. |
| ASGI | @asgi.to_asgi | One endpoint: POST for client-to-agent messages, long-lived GET streams for the answers, DELETE to end a connection, and a WebSocket upgrade carrying the same messages on one channel. |
// The agent's side
pub(open) trait Agent {
async fn initialize(Self, Initialize) -> Initialized raise Refused
async fn new_session(Self, NewSession) -> SessionStarted raise Refused
async fn prompt(Self, Prompt, Session) -> StopReason raise Refused
// authenticate, logout, session/load, session/resume, session/list, session/delete,
// session/close, session/set_mode, session/set_config_option, cancel: optional
}
// What a prompt uses to talk back
pub async fn Session::update(Self, SessionUpdate) -> Unit raise Refused
pub async fn Session::request_permission(Self, Permission) -> Outcome raise Refused
pub async fn Session::read_text_file(Self, String, line? : Int64, limit? : Int64) -> String raise Refused
pub async fn Session::write_text_file(Self, String, String) -> Unit raise Refused
pub async fn Session::terminal(Self, String, ..) -> Terminal raise Refused
pub async fn Session::elicit(Self, Elicitation) -> Answer raise Refused
// The editor's side
pub(open) trait Client {
async fn update(Self, String, SessionUpdate) -> Unit
async fn request_permission(Self, Permission) -> Outcome raise Refused
// read_text_file, write_text_file, create_terminal, terminal_output, wait_for_terminal_exit,
// kill_terminal, release_terminal, elicit: optional
}
pub fn serve(&Agent, &@peer.Frames) -> @peer.Peer
pub fn connect(&Client, &@peer.Frames) -> Conn
pub async fn Conn::initialize(Self, Initialize) -> Initialized raise Refused
pub async fn Conn::prompt(Self, Prompt) -> PromptResult raise Refused
pub async fn Conn::cancel(Self, String) -> Unit raise Refused
pub async fn Conn::run(Self) -> Unitpub(open) trait Agent {
async fn initialize(Self, Initialize) -> Initialized raise Refused
async fn authenticate(Self, Authenticate) -> Unit raise Refused = _
async fn logout(Self) -> Unit raise Refused = _
async fn new_session(Self, NewSession) -> SessionStarted raise Refused
async fn load_session(Self, LoadSession, Session) -> SessionStarted raise Refused = _
async fn resume_session(Self, ResumeSession, Session) -> SessionStarted raise Refused = _
async fn list_sessions(Self, ListSessions) -> SessionList raise Refused = _
async fn delete_session(Self, DeleteSession) -> Unit raise Refused = _
async fn close_session(Self, CloseSession) -> Unit raise Refused = _
async fn set_mode(Self, SetMode) -> Unit raise Refused = _
async fn set_config_option(Self, SetConfigOption) -> Array[ConfigOption] raise Refused = _
async fn prompt(Self, Prompt, Session) -> StopReason raise Refused
async fn cancel(Self, id : String) -> Unit = _
}pub(open) trait Client {
async fn update(Self, String, SessionUpdate) -> Unit
async fn request_permission(Self, Permission) -> Outcome raise Refused
async fn read_text_file(Self, ReadTextFile) -> String raise Refused = _
async fn write_text_file(Self, WriteTextFile) -> Unit raise Refused = _
async fn create_terminal(Self, TerminalCommand) -> String raise Refused = _
async fn terminal_output(Self, TerminalRequest) -> TerminalOutput raise Refused = _
async fn wait_for_terminal_exit(Self, TerminalRequest) -> ExitStatus raise Refused = _
async fn kill_terminal(Self, TerminalRequest) -> Unit raise Refused = _
async fn release_terminal(Self, TerminalRequest) -> Unit raise Refused = _
async fn elicit(Self, Elicitation) -> Answer raise Refused = _
}impl FromJson for AgentAuthCapabilitiesimpl ToJson for AgentAuthCapabilitiesfn AgentAuthCapabilities::from_json(json : Json, path : JsonPath) -> AgentAuthCapabilities raise JsonDecodeErrorpub(all) struct AgentCapabilities {
load_session : Bool
prompt : PromptCapabilities
mcp : McpCapabilities
session : SessionCapabilities
auth : AgentAuthCapabilities?
meta : Json?
} derive(Eq)impl FromJson for AgentCapabilitiesimpl ToJson for AgentCapabilitiesfn AgentCapabilities::from_json(json : Json, path : JsonPath) -> AgentCapabilities raise JsonDecodeErrorimpl FromJson for Annotationsimpl ToJson for Annotationspub(all) struct AudioContent {
data : String
mime_type : String
annotations : Annotations?
meta : Json?
} derive(Eq)impl FromJson for AudioContentimpl ToJson for AudioContentimpl FromJson for AuthMethodimpl ToJson for AuthMethodimpl FromJson for AuthMethodAgentimpl ToJson for AuthMethodAgentfn AuthMethodAgent::from_json(json : Json, path : JsonPath) -> AuthMethodAgent raise JsonDecodeErrorimpl FromJson for AuthMethodTerminalimpl ToJson for AuthMethodTerminalfn AuthMethodTerminal::from_json(json : Json, path : JsonPath) -> AuthMethodTerminal raise JsonDecodeErrorimpl FromJson for Authenticateimpl ToJson for Authenticatepub(all) struct AvailableCommand {
name : String
description : String
input : CommandInput?
meta : Json?
} derive(Eq)impl FromJson for AvailableCommandimpl ToJson for AvailableCommandfn AvailableCommand::from_json(json : Json, path : JsonPath) -> AvailableCommand raise JsonDecodeErrorpub(all) struct AvailableCommands {
available_commands : Array[AvailableCommand]
meta : Json?
} derive(Eq)impl FromJson for AvailableCommandsimpl ToJson for AvailableCommandsfn AvailableCommands::from_json(json : Json, path : JsonPath) -> AvailableCommands raise JsonDecodeErrorimpl FromJson for BlobResourceimpl ToJson for BlobResourceimpl FromJson for BooleanFieldimpl ToJson for BooleanFieldimpl FromJson for ClientAuthCapabilitiesimpl ToJson for ClientAuthCapabilitiesfn ClientAuthCapabilities::from_json(json : Json, path : JsonPath) -> ClientAuthCapabilities raise JsonDecodeErrorfn ClientAuthCapabilities::not_equal(x : ClientAuthCapabilities, y : ClientAuthCapabilities) -> Boolpub(all) struct ClientCapabilities {
fs : FileSystemCapabilities
terminal : Bool
session : ClientSessionCapabilities?
auth : ClientAuthCapabilities?
elicitation : ElicitationCapabilities?
meta : Json?
} derive(Eq)impl FromJson for ClientCapabilitiesimpl ToJson for ClientCapabilitiesfn ClientCapabilities::from_json(json : Json, path : JsonPath) -> ClientCapabilities raise JsonDecodeErrorimpl FromJson for ClientSessionCapabilitiesimpl ToJson for ClientSessionCapabilitiesfn ClientSessionCapabilities::from_json(json : Json, path : JsonPath) -> ClientSessionCapabilities raise JsonDecodeErrorfn ClientSessionCapabilities::not_equal(x : ClientSessionCapabilities, y : ClientSessionCapabilities) -> Boolimpl FromJson for CloseSessionimpl ToJson for CloseSessionimpl FromJson for CommandInputimpl ToJson for CommandInputpub(all) struct Compaction {
compaction_id : String
status : CompactionStatus
summary : Array[ContentBlock]?
error : String?
meta : Json?
} derive(Eq)impl FromJson for Compactionimpl ToJson for Compactionimpl FromJson for CompactionStatusimpl ToJson for CompactionStatusfn CompactionStatus::from_json(json : Json, path : JsonPath) -> CompactionStatus raise JsonDecodeErrorpub(all) struct CompactionSummary {
compaction_id : String
content : ContentBlock
meta : Json?
} derive(Eq)impl FromJson for CompactionSummaryimpl ToJson for CompactionSummaryfn CompactionSummary::from_json(json : Json, path : JsonPath) -> CompactionSummary raise JsonDecodeErrorimpl FromJson for ConfigCategoryimpl ToJson for ConfigCategoryimpl FromJson for ConfigChoiceimpl ToJson for ConfigChoiceimpl FromJson for ConfigChoicesimpl ToJson for ConfigChoicespub(all) struct ConfigGroup {
group : String
name : String
options : Array[ConfigChoice]
meta : Json?
} derive(Eq)impl FromJson for ConfigGroupimpl ToJson for ConfigGrouppub(all) struct ConfigOption {
id : String
name : String
description : String?
category : ConfigCategory?
value : ConfigValue
meta : Json?
} derive(Eq)impl FromJson for ConfigOptionimpl ToJson for ConfigOptionimpl ToJson for ConfigValueasync fn Conn::set_config_option(self : Conn, request : SetConfigOption) -> Array[ConfigOption] raise Refusedpub(all) enum ContentBlock {
Text(TextContent)
Image(ImageContent)
Audio(AudioContent)
ResourceLink(ResourceLink)
Resource(EmbeddedResource)
} derive(Eq)impl FromJson for ContentBlockimpl ToJson for ContentBlockimpl FromJson for ContentChunkimpl ToJson for ContentChunkimpl FromJson for DeleteSessionimpl ToJson for DeleteSessionimpl FromJson for ElicitScopeimpl ToJson for ElicitScopeimpl FromJson for Elicitationimpl ToJson for Elicitationimpl FromJson for ElicitationCapabilitiesimpl ToJson for ElicitationCapabilitiesfn ElicitationCapabilities::from_json(json : Json, path : JsonPath) -> ElicitationCapabilities raise JsonDecodeErrorfn ElicitationCapabilities::not_equal(x : ElicitationCapabilities, y : ElicitationCapabilities) -> Boolimpl FromJson for ElicitationSchemaimpl ToJson for ElicitationSchemafn ElicitationSchema::from_json(json : Json, path : JsonPath) -> ElicitationSchema raise JsonDecodeErrorpub(all) struct EmbeddedResource {
resource : Resource
annotations : Annotations?
meta : Json?
} derive(Eq)impl FromJson for EmbeddedResourceimpl ToJson for EmbeddedResourcefn EmbeddedResource::from_json(json : Json, path : JsonPath) -> EmbeddedResource raise JsonDecodeErrorimpl FromJson for EnumOptionimpl ToJson for EnumOptionimpl FromJson for EnvVariableimpl ToJson for EnvVariablepub(all) enum Field {
Text(TextField)
Number(NumberField)
Integer(IntegerField)
Boolean(BooleanField)
MultiSelect(MultiSelectField)
Other(Json)
} derive(Eq)impl FromJson for FileSystemCapabilitiesimpl ToJson for FileSystemCapabilitiesfn FileSystemCapabilities::from_json(json : Json, path : JsonPath) -> FileSystemCapabilities raise JsonDecodeErrorfn FileSystemCapabilities::not_equal(x : FileSystemCapabilities, y : FileSystemCapabilities) -> Boolpub(all) struct FormRequest {
message : String
schema : ElicitationSchema
scope : ElicitScope
meta : Json?
} derive(Eq)impl FromJson for FormRequestimpl ToJson for FormRequestimpl FromJson for HttpHeaderimpl ToJson for HttpHeaderpub(all) struct ImageContent {
data : String
mime_type : String
uri : String?
annotations : Annotations?
meta : Json?
} derive(Eq)impl FromJson for ImageContentimpl ToJson for ImageContentimpl FromJson for Implementationimpl ToJson for Implementationpub(all) struct Initialize {
protocol_version : Int
client_capabilities : ClientCapabilities
client_info : Implementation?
meta : Json?
} derive(Eq)impl FromJson for Initializeimpl ToJson for Initializepub(all) struct Initialized {
protocol_version : Int
capabilities : AgentCapabilities
auth_methods : Array[AuthMethod]
agent_info : Implementation?
meta : Json?
} derive(Eq)impl FromJson for Initializedimpl ToJson for Initializedimpl FromJson for IntegerFieldimpl ToJson for IntegerFieldimpl FromJson for ListSessionsimpl ToJson for ListSessionsimpl FromJson for LoadSessionimpl ToJson for LoadSessionimpl FromJson for McpCapabilitiesimpl ToJson for McpCapabilitiesfn McpCapabilities::from_json(json : Json, path : JsonPath) -> McpCapabilities raise JsonDecodeErrorpub(all) struct McpHttp {
name : String
url : String
headers : Array[HttpHeader]
meta : Json?
} derive(Eq)pub(all) struct McpSse {
name : String
url : String
headers : Array[HttpHeader]
meta : Json?
} derive(Eq)pub(all) struct McpStdio {
name : String
command : String
args : Array[String]
env : Array[EnvVariable]
meta : Json?
} derive(Eq)impl FromJson for ModeChangedimpl ToJson for ModeChangedpub(all) struct MultiSelectField {
title : String?
description : String?
min_items : Int64?
max_items : Int64?
items : ChoiceItems
default : Array[String]?
meta : Json?
} derive(Eq)impl FromJson for MultiSelectFieldimpl ToJson for MultiSelectFieldfn MultiSelectField::from_json(json : Json, path : JsonPath) -> MultiSelectField raise JsonDecodeErrorimpl FromJson for NewSessionimpl ToJson for NewSessionimpl FromJson for NumberFieldimpl ToJson for NumberFieldpub(all) struct Permission {
tool_call : ToolCallUpdate
options : Array[PermissionOption]
meta : Json?
} derive(Eq)impl FromJson for Permissionimpl ToJson for Permissionpub(all) struct PermissionOption {
option_id : String
name : String
kind : PermissionOptionKind
meta : Json?
} derive(Eq)impl FromJson for PermissionOptionimpl ToJson for PermissionOptionfn PermissionOption::from_json(json : Json, path : JsonPath) -> PermissionOption raise JsonDecodeErrorimpl FromJson for PermissionOptionKindimpl ToJson for PermissionOptionKindfn PermissionOptionKind::from_json(json : Json, path : JsonPath) -> PermissionOptionKind raise JsonDecodeErrorpub(all) struct PlanEntry {
content : String
priority : PlanEntryPriority
status : PlanEntryStatus
meta : Json?
} derive(Eq)impl FromJson for PlanEntryPriorityimpl ToJson for PlanEntryPriorityfn PlanEntryPriority::from_json(json : Json, path : JsonPath) -> PlanEntryPriority raise JsonDecodeErrorimpl FromJson for PlanEntryStatusimpl ToJson for PlanEntryStatusfn PlanEntryStatus::from_json(json : Json, path : JsonPath) -> PlanEntryStatus raise JsonDecodeErrorimpl FromJson for PromptCapabilitiesimpl ToJson for PromptCapabilitiesfn PromptCapabilities::from_json(json : Json, path : JsonPath) -> PromptCapabilities raise JsonDecodeErrorimpl FromJson for PromptResultimpl ToJson for PromptResultimpl FromJson for ReadTextFileimpl ToJson for ReadTextFilepub(all) struct ResourceLink {
uri : String
name : String
title : String?
description : String?
mime_type : String?
size : Int64?
annotations : Annotations?
meta : Json?
} derive(Eq)impl FromJson for ResourceLinkimpl ToJson for ResourceLinkimpl FromJson for ResumeSessionimpl ToJson for ResumeSessionimpl FromJson for SessionCapabilitiesimpl ToJson for SessionCapabilitiesfn SessionCapabilities::from_json(json : Json, path : JsonPath) -> SessionCapabilities raise JsonDecodeErrorimpl FromJson for SessionInfoimpl ToJson for SessionInfoimpl FromJson for SessionInfoChangedimpl ToJson for SessionInfoChangedfn SessionInfoChanged::from_json(json : Json, path : JsonPath) -> SessionInfoChanged raise JsonDecodeErrorpub(all) struct SessionList {
sessions : Array[SessionInfo]
next_cursor : String?
meta : Json?
} derive(Eq)impl FromJson for SessionListimpl ToJson for SessionListimpl FromJson for SessionModeimpl ToJson for SessionModepub(all) struct SessionModes {
current_mode_id : String
available_modes : Array[SessionMode]
meta : Json?
} derive(Eq)impl FromJson for SessionModesimpl ToJson for SessionModespub(all) struct SessionNotification {
session_id : String
update : SessionUpdate
meta : Json?
} derive(Eq)impl FromJson for SessionNotificationimpl ToJson for SessionNotificationfn SessionNotification::from_json(json : Json, path : JsonPath) -> SessionNotification raise JsonDecodeErrorpub(all) struct SessionStarted {
session_id : String
modes : SessionModes?
config_options : Array[ConfigOption]?
meta : Json?
} derive(Eq)impl FromJson for SessionStartedimpl ToJson for SessionStartedpub(all) enum SessionUpdate {
UserMessageChunk(ContentChunk)
AgentMessageChunk(ContentChunk)
AgentThoughtChunk(ContentChunk)
ToolCall(ToolCall)
ToolCallUpdate(ToolCallUpdate)
Plan(Plan)
AvailableCommands(AvailableCommands)
ModeChanged(ModeChanged)
ConfigOptionChanged(Array[ConfigOption])
SessionInfoChanged(SessionInfoChanged)
Usage(Usage)
Notice(Notice)
Compaction(Compaction)
CompactionSummary(CompactionSummary)
} derive(Eq)impl FromJson for SessionUpdateimpl ToJson for SessionUpdatepub(all) struct SetConfigOption {
session_id : String
config_id : String
value : ConfigValueId
meta : Json?
} derive(Eq)impl FromJson for SetConfigOptionimpl ToJson for SetConfigOptionfn SetConfigOption::from_json(json : Json, path : JsonPath) -> SetConfigOption raise JsonDecodeErrorimpl FromJson for SetConfigOptionsimpl ToJson for SetConfigOptionsfn SetConfigOptions::from_json(json : Json, path : JsonPath) -> SetConfigOptions raise JsonDecodeErrorimpl FromJson for StopReasonimpl ToJson for StopReasonimpl FromJson for StringFormatimpl ToJson for StringFormatpub(all) struct TerminalCommand {
session_id : String
command : String
args : Array[String]?
env : Array[EnvVariable]?
cwd : String?
output_byte_limit : Int64?
meta : Json?
} derive(Eq)impl FromJson for TerminalCommandimpl ToJson for TerminalCommandfn TerminalCommand::from_json(json : Json, path : JsonPath) -> TerminalCommand raise JsonDecodeErrorimpl FromJson for TerminalOpenedimpl ToJson for TerminalOpenedpub(all) struct TerminalOutput {
output : String
truncated : Bool
exit_status : ExitStatus?
meta : Json?
} derive(Eq)impl FromJson for TerminalOutputimpl ToJson for TerminalOutputimpl FromJson for TerminalRefimpl ToJson for TerminalRefimpl FromJson for TerminalRequestimpl ToJson for TerminalRequestfn TerminalRequest::from_json(json : Json, path : JsonPath) -> TerminalRequest raise JsonDecodeErrorimpl FromJson for TextContentimpl ToJson for TextContentpub(all) struct TextField {
title : String?
description : String?
min_length : Int64?
max_length : Int64?
pattern : String?
format : StringFormat?
default : String?
choices : Array[String]?
titled_choices : Array[EnumOption]?
meta : Json?
} derive(Eq)impl FromJson for TextResourceimpl ToJson for TextResourcepub(all) struct ToolCall {
tool_call_id : String
title : String
name : String?
kind : ToolKind?
status : ToolCallStatus?
content : Array[ToolCallContent]?
locations : Array[ToolCallLocation]?
raw_input : Json?
raw_output : Json?
meta : Json?
} derive(Eq)impl FromJson for ToolCallContentimpl ToJson for ToolCallContentfn ToolCallContent::from_json(json : Json, path : JsonPath) -> ToolCallContent raise JsonDecodeErrorimpl FromJson for ToolCallLocationimpl ToJson for ToolCallLocationfn ToolCallLocation::from_json(json : Json, path : JsonPath) -> ToolCallLocation raise JsonDecodeErrorimpl FromJson for ToolCallStatusimpl ToJson for ToolCallStatuspub(all) struct ToolCallUpdate {
tool_call_id : String
kind : ToolKind?
status : ToolCallStatus?
title : String?
name : String?
content : Array[ToolCallContent]?
locations : Array[ToolCallLocation]?
raw_input : Json?
raw_output : Json?
meta : Json?
} derive(Eq)impl FromJson for ToolCallUpdateimpl ToJson for ToolCallUpdatepub(all) enum ToolKind {
Read
Edit
Delete
Move
Search
Execute
Think
Fetch
SwitchMode
Other
} derive(Eq)pub(all) struct UrlRequest {
message : String
elicitation_id : String
url : String
scope : ElicitScope
meta : Json?
} derive(Eq)impl FromJson for WriteTextFileimpl ToJson for WriteTextFilelet authentication_required : Intlet protocol_version : Intlet resource_not_found : IntInstall
Download zipThe Agent Client Protocol for MoonBit: editors talking to agents, over ASGI or stdio.