Skip to content

Process Capability Specification

Capability Identity

PropertyValue
EnumA2ECapability.PROC
String"proc"
Plugin TypeProcPlugin
Plugin Priority5
Namespaceproc/*
Message Count9

Overview

The process capability provides long-running process management. Unlike tool/call (which runs to completion), proc/* keeps a process alive so the agent can read/write its I/O incrementally. This enables interactive shell sessions, long-running server processes, and streaming command output.

Key distinction from tools: Tools are request/response and run to completion. Processes are long-lived, support incremental I/O, and require explicit lifecycle management (spawn, write, kill).

Protocol Flow

Message Types (9)

proc/spawn/req — ProcSpawnRequest

Agent → Host. Spawn a persistent process.

FieldTypeRequiredDefaultDescription
typestrYes"proc/spawn/req"Message type
idstrYesautoMessage UUID
versionstrYes"1.0"Protocol version
tsfloatYesautoTimestamp
session_idstrYes""Session from HandshakeResponse
cmdlist[str]Yes[]Command + args (must match allowlist)
cwdstrNo""Working directory (default: host CWD)
envdict[str, str]No{}Additional environment variables
stdin_modestrNo"pipe"Stdin mode: pipe or null
timeoutintNo0Auto-kill after seconds (0 = no limit)

proc/spawn/resp — ProcSpawnResponse

Host → Agent. Returns proc_id for subsequent messages.

FieldTypeRequiredDefaultDescription
typestrYes"proc/spawn/resp"Message type
idstrYesautoMessage UUID
versionstrYes"1.0"Protocol version
tsfloatYesautoTimestamp
req_idstrYes""Echoes request ID
proc_idstrYes""Process identifier (UUID)
okboolYesFalseWhether spawn succeeded
pidintNoNoneOS process ID
errorstrNo""Error message on failure

proc/write/req — ProcWriteRequest

Agent → Host. Write a chunk to the process's stdin.

FieldTypeRequiredDefaultDescription
typestrYes"proc/write/req"Message type
idstrYesautoMessage UUID
versionstrYes"1.0"Protocol version
tsfloatYesautoTimestamp
proc_idstrYes""Target process
datastrYes""UTF-8 text (use base64 for binary)
eofboolNoFalseClose stdin after this write

proc/write/resp — ProcWriteResponse

Host → Agent.

FieldTypeRequiredDefaultDescription
typestrYes"proc/write/resp"Message type
idstrYesautoMessage UUID
versionstrYes"1.0"Protocol version
tsfloatYesautoTimestamp
req_idstrYes""Echoes request ID
proc_idstrYes""Target process
okboolYesFalseWhether write succeeded
pidintNo0OS process ID
errorstrNo""Error message on failure

proc/read/event — ProcReadEvent

Host → Agent (server-initiated). Chunk of process stdout/stderr. Extends A2EEvent.

FieldTypeRequiredDefaultDescription
typestrYes"proc/read/event"Message type
idstrYesautoMessage UUID
versionstrYes"1.0"Protocol version
tsfloatYesautoTimestamp
proc_idstrYes""Source process
stream_typestrYes"stdout"Stream: stdout, stderr, killed
datastrYesJSON-encoded data (text line, or exit code)
req_idstrYes""Correlates to original spawn request

data payload shapes by stream_type:

Stream TypeData ShapeDescription
stdout{"text": str} or {"code": int}Output line or exit code 0
stderr{"text": str} or {"code": int}Error line or non-zero exit code
killed{}Process was killed

proc/kill/req — ProcKillRequest

Agent → Host. Terminate a process.

FieldTypeRequiredDefaultDescription
typestrYes"proc/kill/req"Message type
idstrYesautoMessage UUID
versionstrYes"1.0"Protocol version
tsfloatYesautoTimestamp
proc_idstrYes""Process to terminate
signalstrNo"SIGTERM"Signal: SIGTERM, SIGKILL, SIGINT

proc/kill/resp — ProcKillResponse

Host → Agent.

FieldTypeRequiredDefaultDescription
typestrYes"proc/kill/resp"Message type
idstrYesautoMessage UUID
versionstrYes"1.0"Protocol version
tsfloatYesautoTimestamp
req_idstrYes""Echoes request ID
okboolYesFalseWhether kill succeeded
statestrNo""Final ProcState

proc/status/req — ProcStatusRequest

Agent → Host. Query process status.

FieldTypeRequiredDefaultDescription
typestrYes"proc/status/req"Message type
idstrYesautoMessage UUID
versionstrYes"1.0"Protocol version
tsfloatYesautoTimestamp
proc_idstrYes""Process to query (empty = all procs in session)

proc/status/resp — ProcStatusResponse

Host → Agent.

FieldTypeRequiredDefaultDescription
typestrYes"proc/status/resp"Message type
idstrYesautoMessage UUID
versionstrYes"1.0"Protocol version
tsfloatYesautoTimestamp
req_idstrYes""Echoes request ID
proc_idstrYesProcess identifier
statusstrYesCurrent status
errorstrYesError message (if any)

Data Models

ProcState

ValueDescription
runningProcess is actively running
stoppedProcess has been stopped
crashedProcess crashed unexpectedly
timedoutProcess exceeded timeout limit

Plugin-internal statuses:

ValueDescription
runningActive and streaming output
completedExited with code 0
failedExited with non-zero code
killedTerminated by kill request
not_foundSession ID not found
errorInternal error

ProcStatus

FieldTypeDescription
proc_idstrProcess identifier
cmdstrCommand that was executed
statestrProcState value
pidintOS process ID
exit_codeint or NoneExit code (when process has terminated)
started_atfloatStart timestamp

ProcSession (internal)

FieldTypeDescription
proc_idstrProcess identifier
processsubprocess.PopenOS subprocess handle
req_idstrOriginal spawn request ID
statusstrCurrent status string
errorstr or NoneError message

Error Codes — ProcMCPError

CodeEnum ValueDescriptionRetryable
unknown_procUNKNOWN_PROCProcess ID not foundNo
proc_deadPROC_DEADProcess has already exitedNo
proc_limitPROC_LIMITSession process quota exceededNo

Command Allowlist

The ProcPlugin enforces a command allowlist. By default:

python
ALLOWED_COMMANDS = {"python3", "bash", "ls"}

Commands are validated by checking cmd[0] against the allowlist. Hosts can customize the allowlist via config:

python
self.allowed_commands = config.get("ALLOWED_COMMANDS", ALLOWED_COMMANDS)

Execution Model

  1. Spawn: subprocess.Popen with stdin=PIPE, stdout=PIPE, stderr=PIPE, text=True, shell=False
  2. Stream readers: Daemon threads read stdout/stderr line-by-line, emitting ProcReadEvent for each line
  3. Wait thread: Daemon thread calls proc.wait(), then emits final exit code event
  4. Write: Direct proc.stdin.write() + flush()
  5. Kill: proc.terminate() with status set to "killed"

Wire Examples

Spawn a Python Process

json
{"type":"proc/spawn/req","id":"p1","version":"1.0","ts":1716123456.789,"session_id":"s1","cmd":["python3","-i"],"cwd":"","env":{},"stdin_mode":"pipe","timeout":0}
json
{"type":"proc/spawn/resp","id":"p2","version":"1.0","ts":1716123456.900,"req_id":"p1","proc_id":"proc_abc123","ok":true,"pid":12345,"error":""}

Write to stdin

json
{"type":"proc/write/req","id":"p3","version":"1.0","ts":1716123457.100,"proc_id":"proc_abc123","data":"print('hello')\n","eof":false}

Read Output (server-initiated)

json
{"type":"proc/read/event","id":"p4","version":"1.0","ts":1716123457.150,"proc_id":"proc_abc123","stream_type":"stdout","data":"{\"text\":\"hello\\n\"}","req_id":"p1"}

Kill Process

json
{"type":"proc/kill/req","id":"p5","version":"1.0","ts":1716123458.100,"proc_id":"proc_abc123","signal":"SIGTERM"}
json
{"type":"proc/kill/resp","id":"p6","version":"1.0","ts":1716123458.200,"req_id":"p5","ok":true,"state":"killed"}

Security Considerations

  1. Command allowlist: Only pre-approved commands can be spawned (default: python3, bash, ls)
  2. No shell injection: shell=False in Popen prevents shell metacharacter injection
  3. Process quota: proc_limit error prevents fork bombs
  4. Timeout enforcement: Auto-kill prevents zombie processes
  5. Session isolation: Processes are scoped to sessions; cannot access other sessions' processes
  6. Stdin mode: null mode disables stdin for processes that don't need it

A2E Protocol v1.0 — Released under the MIT License.