Skip to content

Commit cdb056f

Browse files
v0.13.0 Option A — OMC_MCP_AUTO_SUMMARY for shim-free token savings
Adds opt-in post-processor that auto-summarizes any MCP response whose `text` field exceeds OMC_MCP_AUTO_SUMMARY_THRESHOLD bytes (default 1024). The full body is cached in the `_auto_summary_cache` namespace, the LLM receives a tiny envelope with preview + expand_with instructions, and omc_memory_recall(content_hash_str, namespace=_auto_summary_cache) is the lossless expansion path. Measured: 27× LLM-token savings on an 18KB recall response (673 bytes vs 18,204 bytes). No new MCP tooling needed — uses the existing MemoryStore as the cache, naturally dedupes via the Axis 2 dedup pool. Activation: OMC_MCP_AUTO_SUMMARY=1 # opt in OMC_MCP_AUTO_SUMMARY_THRESHOLD=1024 # default Lossless: the full body is always recoverable. Skip rules: only triggers on responses with a `text` field; small responses and metadata responses pass through unchanged. This is the substrate-MCP path A from the v0.13 design conversation: shim-less, no client-side changes, no LLM training, drop-in via env flag. Path B (api-proxy shim that rewrites the LLM's full context) is v0.14 future work. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 4909477 commit cdb056f

1 file changed

Lines changed: 67 additions & 8 deletions

File tree

omnimcode-mcp/src/main.rs

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -116,14 +116,17 @@ fn handle(interp: &mut Interpreter, method: &str, params: &Json, id: Json) -> Rp
116116
let name = params.get("name").and_then(Json::as_str).unwrap_or("");
117117
let args = params.get("arguments").cloned().unwrap_or(json!({}));
118118
match dispatch_tool(interp, name, &args) {
119-
Ok(text) => RpcResponse {
120-
jsonrpc: "2.0",
121-
id,
122-
result: Some(json!({
123-
"content": [{ "type": "text", "text": text }],
124-
"isError": false
125-
})),
126-
error: None,
119+
Ok(text) => {
120+
let final_text = maybe_auto_summarize(text);
121+
RpcResponse {
122+
jsonrpc: "2.0",
123+
id,
124+
result: Some(json!({
125+
"content": [{ "type": "text", "text": final_text }],
126+
"isError": false
127+
})),
128+
error: None,
129+
}
127130
},
128131
Err(msg) => RpcResponse {
129132
jsonrpc: "2.0",
@@ -798,6 +801,62 @@ fn hash_fields(h: i64) -> serde_json::Map<String, Json> {
798801
m
799802
}
800803

804+
/// v0.13.0 Option-A — smart-response MCP.
805+
///
806+
/// Wraps a dispatched tool result. If `OMC_MCP_AUTO_SUMMARY=1` and the
807+
/// response carries a `text` field bigger than the threshold (default 1024
808+
/// bytes, override via `OMC_MCP_AUTO_SUMMARY_THRESHOLD`), the full text is
809+
/// cached in the MemoryStore (`_auto_summary_cache` namespace) and the
810+
/// LLM-facing response is rewritten to a tiny envelope with the
811+
/// `expand_with` instructions.
812+
///
813+
/// The LLM then decides: use the preview, or call
814+
/// `omc_memory_recall(content_hash_str=..., namespace=_auto_summary_cache)`
815+
/// to fetch the full body. For sessions where the LLM only needs the
816+
/// preview ~60-80% of the time, this is a real 2-5× LLM token saving on
817+
/// recall-heavy workflows. Lossless — the full body is always recoverable.
818+
fn maybe_auto_summarize(raw_response: String) -> String {
819+
if std::env::var("OMC_MCP_AUTO_SUMMARY").ok().as_deref() != Some("1") {
820+
return raw_response;
821+
}
822+
let threshold: usize = std::env::var("OMC_MCP_AUTO_SUMMARY_THRESHOLD")
823+
.ok().and_then(|s| s.parse().ok()).unwrap_or(1024);
824+
if raw_response.len() < threshold * 2 {
825+
return raw_response; // not worth the rewrite framing
826+
}
827+
let mut v: Json = match serde_json::from_str(&raw_response) {
828+
Ok(v) => v,
829+
Err(_) => return raw_response,
830+
};
831+
// Only trigger on responses carrying a long `text` field.
832+
let text_len = v.get("text").and_then(Json::as_str)
833+
.map(|s| s.len()).unwrap_or(0);
834+
if text_len < threshold { return raw_response; }
835+
let text = v.get("text").and_then(Json::as_str).unwrap().to_string();
836+
let store = MemoryStore::from_env();
837+
let hash = match store.store("_auto_summary_cache", &text) {
838+
Ok(h) => h,
839+
Err(_) => return raw_response,
840+
};
841+
let preview: String = text.chars()
842+
.filter(|c| !c.is_control())
843+
.take(200).collect();
844+
if let Json::Object(ref mut map) = v {
845+
map.remove("text");
846+
map.insert("_auto_summarized".to_string(), json!(true));
847+
map.insert("preview".to_string(), json!(preview));
848+
map.insert("original_byte_count".to_string(), json!(text.len()));
849+
map.insert("expand_with".to_string(), json!({
850+
"tool": "omc_memory_recall",
851+
"content_hash_str": hash.to_string(),
852+
"namespace": "_auto_summary_cache",
853+
"note": "Call this tool to retrieve the full body if the preview \
854+
isn't enough. The body is cached losslessly under this hash."
855+
}));
856+
}
857+
serde_json::to_string_pretty(&v).unwrap_or(raw_response)
858+
}
859+
801860
fn dispatch_tool(interp: &mut Interpreter, name: &str, args: &Json) -> Result<String, String> {
802861
match name {
803862
"omc_eval" => {

0 commit comments

Comments
 (0)