Shared memory and context tools for agentic work.
Code Rooms
// === crates/m1nd-mcp/src/protocol.rs ===
use serde::{Deserialize, Serialize};
// ---------------------------------------------------------------------------
// JSON-RPC transport types
#[derive(Clone, Debug, Deserialize)]
pub struct JsonRpcRequest {
pub jsonrpc: String,
pub id: serde_json::Value,
pub method: String,
#[serde(default = "default_params")]
pub params: serde_json::Value,
}
#[derive(Clone, Debug, Serialize)]
pub struct JsonRpcResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<serde_json::Value>,
pub error: Option<JsonRpcError>,
pub struct JsonRpcError {
pub code: i32,
pub message: String,
pub data: Option<serde_json::Value>,
// MCP tool input types (03-MCP Section 2 tool schemas)
/// Input for activate (03-MCP Section 2.1).
pub struct ActivateInput {
pub query: String,
pub agent_id: String,
#[serde(default = "default_top_k")]
pub top_k: usize,
#[serde(default = "default_dimensions")]
pub dimensions: Vec<String>,
#[serde(default = "default_true")]
pub xlr: bool,
pub include_ghost_edges: bool,
#[serde(default)]
pub include_structural_holes: bool,
/// Optional approximate context-token budget. When set, the `activated`
/// node list is kept in graph-importance rank order until the running token
/// ESTIMATE (chars/4) would exceed this budget; lower-ranked nodes are
/// dropped and a `budget` block reports what was kept vs dropped. None =
/// unbudgeted (return the full top_k set, behavior unchanged).
pub token_budget: Option<usize>,
/// Input for impact (03-MCP Section 2.2).
pub struct ImpactInput {
pub node_id: String,
#[serde(default = "default_forward")]
pub direction: String,
pub include_causal_chains: bool,
/// Maximum blast-radius nodes to return. Default: 150.
/// Set higher (or None/omit) to get the full set.
pub max_nodes: Option<usize>,
/// Input for m1nd.missing (03-MCP Section 2.3).
pub struct MissingInput {
#[serde(default = "default_min_sibling")]
pub min_sibling_activation: f32,
/// Input for m1nd.why (03-MCP Section 2.4).
pub struct WhyInput {
pub source: String,
pub target: String,
#[serde(default = "default_max_hops")]
pub max_hops: u8,
/// Input for m1nd.warmup (03-MCP Section 2.5).
pub struct WarmupInput {
pub task_description: String,
#[serde(default = "default_boost")]
pub boost_strength: f32,
/// Input for m1nd.counterfactual (03-MCP Section 2.6).
pub struct CounterfactualInput {
pub node_ids: Vec<String>,
pub include_cascade: bool,
/// Input for m1nd.predict (03-MCP Section 2.7).
pub struct PredictInput {
pub changed_node: String,
#[serde(default = "default_top_k_10")]
pub include_velocity: bool,
/// Minimum co-change observation count to include git-derived co-change
/// entries from the orchestrator matrix. Default: 2. Set to 1 to include
/// all entries including singletons.
pub min_co_change_count: Option<u32>,
/// Input for m1nd.fingerprint (03-MCP Section 2.8).
pub struct FingerprintInput {
pub target_node: Option<String>,
#[serde(default = "default_similarity")]
pub similarity_threshold: f32,
pub probe_queries: Option<Vec<String>>,
/// Input for m1nd.drift (03-MCP Section 2.9).
pub struct DriftInput {
#[serde(default = "default_last_session")]
pub since: String,
pub include_weight_drift: bool,
/// Input for m1nd.learn (03-MCP Section 2.10).
pub struct LearnInput {
pub feedback: String, // "correct", "wrong", or "partial"
#[serde(default = "default_feedback_strength")]
pub strength: f32,
/// Input for m1nd.ingest (03-MCP Section 2.11).
pub struct IngestInput {
pub path: String,
pub incremental: bool,
/// Adapter to use: "code" (default), "json", "memory", or future adapters.
#[serde(default = "default_adapter")]
pub adapter: String,
/// Whether the ingest replaces the active graph or merges into it.
#[serde(default = "default_ingest_mode")]
pub mode: String,
/// Optional namespace tag for non-code adapters.
pub namespace: Option<String>,
/// Include selected dotfiles and hidden config directories during ingest.
pub include_dotfiles: bool,
/// Prefix-style patterns (for example `.codex/**`) that are allowed when
/// include_dotfiles=true.
pub dotfile_patterns: Vec<String>,
/// Two-Tier Brain (interim variant): when set, this `ingest` is a per-project
/// BOOTSTRAP handled entirely by the served owner's HTTP dispatch layer — it
/// creates (or warm-resolves) an owner-hosted per-project brain rooted at this
/// path, ingests the repo into it, binds the calling wire session to it, and
/// returns that new brain's orientation, all in ONE call. Thereafter the
/// caller's `caller_root` routes to that brain silently. `None` = classic
/// ingest against whichever graph the call already resolved to. HTTP/attach
/// only — on the stdio path this field is inert (the bound graph serves
/// stdio exactly as before).
pub project_root: Option<String>,
// NOTE: the bootstrap escape hatch `allow_overlap` is intentionally NOT a field
// here. It is a routing directive consumed at the served-owner layer
// (`project_brains::bootstrap` reads it from the raw arguments and strips it,
// exactly like `project_root`), never by the ingest adapter — so it never
// deserializes into this struct. See the `ingest` tool schema in `server.rs`.
/// Input for m1nd.resonate (resonance analysis).
pub struct ResonateInput {
pub query: Option<String>,
pub node_id: Option<String>,
/// Input for m1nd.health (03-MCP Section 2.12).
pub struct HealthInput {
/// Input for m1nd.session_handshake.
#[derive(Clone, Debug, Default, Deserialize)]
pub struct SessionHandshakeInput {
pub observed_tool_count: Option<u64>,
pub available_tools: Vec<String>,
pub missing_tools: Vec<String>,
pub scope: Option<String>,
// --- ORGANISM-INSIDE P1 — optional presence DECLARATION (askGOD verdict
// 2026-07-13). The session's self-declared control-room enrichment, all
// optional and honest-absent. Measured facts (brain / caller_root /
// task_ref) are NEVER taken from here — only what the agent chooses to
// declare about itself. ---
/// orchestrator | executor | pool-hand | runner | oracle | human-ui.
pub kind: Option<String>,
/// One free-text line ("reader slice 1", "F12 curation lane").
pub theme: Option<String>,
/// `read` | `mutate` — the DECLARED mutation level (verdict c).
pub intent: Option<String>,
/// Worktree/branch display string.
pub worktree: Option<String>,
/// Declared working set: repo-relative paths and/or `sb_` block ids.
pub working_set: Vec<String>,
/// Input for m1nd.trust_selftest.
pub struct TrustSelftestInput {
pub observed_tool: Option<String>,
pub observed_proof_state: Option<String>,
pub observed_candidates: Option<u64>,
pub error_text: Option<String>,
/// Input for m1nd.doctor.
pub struct DoctorInput {
/// Input for m1nd.recovery_playbook.
pub struct RecoveryPlaybookInput {
pub trust_mode: Option<String>,
// Default value helpers
fn default_top_k() -> usize {
20
fn default_top_k_10() -> usize {
10
fn default_dimensions() -> Vec<String> {
vec![
"structural".into(),
"semantic".into(),
"temporal".into(),
"causal".into(),
]
fn default_true() -> bool {
true
fn default_forward() -> String {
"forward".into()
fn default_min_sibling() -> f32 {
0.3
fn default_max_hops() -> u8 {
6
fn default_boost() -> f32 {
0.15
fn default_similarity() -> f32 {
0.85
fn default_last_session() -> String {
"last_session".into()
pub(crate) fn default_feedback_strength() -> f32 {
0.2
fn default_adapter() -> String {
"code".into()
fn default_ingest_mode() -> String {
"replace".into()
fn default_params() -> serde_json::Value {
serde_json::Value::Object(serde_json::Map::new())
// MCP tool output types (03-MCP Section 2 output schemas)
// All output types are Serialize for JSON-RPC response.
/// Output for activate.
pub struct ActivateOutput {
pub seeds: Vec<SeedOutput>,
pub activated: Vec<ActivatedNodeOutput>,
pub ghost_edges: Vec<GhostEdgeOutput>,
pub structural_holes: Vec<StructuralHoleOutput>,
pub plasticity: PlasticityOutput,
pub elapsed_ms: f64,
pub proof_state: String,
pub next_suggested_tool: Option<String>,
pub next_suggested_target: Option<String>,
pub next_step_hint: Option<String>,
pub confidence: Option<f32>,
pub why_this_next_step: Option<String>,
pub what_is_missing: Option<String>,
pub graph_state: Option<serde_json::Value>,
pub recovery: Option<serde_json::Value>,
pub agent_runtime_contract: Option<serde_json::Value>,
/// Present only when `token_budget` was requested: an honest accounting of
/// the context-budget packing (requested/used estimate, kept, dropped).
pub budget: Option<serde_json::Value>,
pub struct SeedOutput {
pub label: String,
pub relevance: f32,
pub struct ActivatedNodeOutput {
#[serde(rename = "type")]
pub node_type: String,
pub activation: f32,
pub dimensions: DimensionsOutput,
pub pagerank: f32,
pub tags: Vec<String>,
pub provenance: Option<ProvenanceOutput>,
pub struct ProvenanceOutput {
pub source_path: Option<String>,
pub line_start: Option<u32>,
pub line_end: Option<u32>,
pub excerpt: Option<String>,
pub canonical: bool,
pub struct DimensionsOutput {
pub structural: f32,
pub semantic: f32,
pub temporal: f32,
pub causal: f32,
pub struct GhostEdgeOutput {
pub shared_dimensions: Vec<String>,
pub struct StructuralHoleOutput {
pub reason: String,
pub struct PlasticityOutput {
pub edges_strengthened: u32,
pub edges_decayed: u32,
pub ltp_events: u32,
pub priming_nodes: u32,
/// Output for impact.
pub struct ImpactOutput {
pub source_label: String,
pub blast_radius: Vec<BlastRadiusEntry>,
pub total_energy: f32,
pub max_hops_reached: u8,
pub causal_chains: Vec<CausalChainOutput>,
/// Total blast-radius nodes before any cap was applied.
pub total_blast_nodes: usize,
/// True when blast_radius was capped by max_nodes.
pub truncated: bool,
pub struct BlastRadiusEntry {
pub signal_strength: f32,
pub hop_distance: u8,
/// True when this node is a light:evidenced_by citation marker reached via a
/// `grounded_in` edge from the impact source (or is itself such a marker).
/// Tells the agent that this entry appeared because it is a memorized claim
/// that cites a file in the blast radius.
pub is_knowledge_citation: Option<bool>,
/// The full label text of the evidence marker node (the memorized claim text).
/// Present only when `is_knowledge_citation` is `true`.
pub claim: Option<String>,
pub struct CausalChainOutput {
pub path: Vec<String>,
pub relations: Vec<String>,
pub cumulative_strength: f32,
/// Output for m1nd.health.
pub struct HealthOutput {
pub status: String,
pub node_count: u32,
pub edge_count: u64,
pub queries_processed: u64,
pub uptime_seconds: f64,
pub memory_usage_bytes: u64,
pub plasticity_state: String,
pub last_persist_time: Option<String>,
pub active_sessions: Vec<serde_json::Value>,
pub git: serde_json::Value,
pub binding_fingerprint: serde_json::Value,
pub tool_surface_contract: serde_json::Value,
pub host_binding_alignment: serde_json::Value,
/// First-Contact Reception block (TWO-TIER-BRAIN-PRD §9.5.5): present only on
/// a caller_root mismatch; absent on match / unknown caller (serde-skipped).
pub reception: Option<serde_json::Value>,
#[cfg(test)]
mod tests {
use super::JsonRpcRequest;
#[test]
fn request_defaults_missing_params_to_empty_object() {
let request: JsonRpcRequest =
serde_json::from_str(r#"{"jsonrpc":"2.0","id":1,"method":"tools/list"}"#)
.expect("request without params should parse");
assert_eq!(request.method, "tools/list");
assert_eq!(request.params, serde_json::json!({}));