Skip to main content
Glama
reasoning.py11.7 kB
"""Reasoning endpoints for MARM MCP Server.""" from fastapi import HTTPException, APIRouter, Query import sqlite3 import numpy as np from datetime import datetime, timezone from typing import List, Dict, Optional # Import core components from core.models import ContextBridgeRequest from core.memory import memory from core.events import events from core.response_limiter import MCPResponseLimiter # Create router for reasoning endpoints router = APIRouter(prefix="", tags=["Reasoning"]) @router.get("/marm_summary", operation_id="marm_summary") async def marm_summary( session_name: str = Query(..., description="The name of the session to summarize."), limit: int = Query(50, description="Maximum number of entries to include (default: 50)", ge=1, le=200) ): """ 📊 Generate paste-ready context block for new chats Equivalent to /summary: [session name] command Uses intelligent truncation to stay within MCP 1MB limits. """ try: with memory.get_connection() as conn: # Get total count first cursor = conn.execute(''' SELECT COUNT(*) FROM log_entries WHERE session_name = ? ''', (session_name,)) total_entries = cursor.fetchone()[0] # Get limited entries for summary cursor = conn.execute(''' SELECT entry_date, topic, summary, full_entry FROM log_entries WHERE session_name = ? ORDER BY entry_date DESC LIMIT ? ''', (session_name, limit)) entries = cursor.fetchall() if not entries: return { "status": "empty", "message": f"No entries found in session '{session_name}'" } # Build base response metadata base_response = { "status": "success", "session_name": session_name, "entry_count": len(entries), "total_entries": total_entries } # Build summary with size monitoring summary_lines = [f"# MARM Session Summary: {session_name}"] summary_lines.append(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M UTC')}") summary_lines.append("") if total_entries > len(entries): summary_lines.append(f"*Showing {len(entries)} most recent entries out of {total_entries} total*") summary_lines.append("") # Add entries with progressive truncation if needed included_entries = [] current_summary_lines = summary_lines.copy() for entry in entries: # Truncate long summaries to prevent size explosion entry_summary = entry[2] if len(entry_summary) > 200: entry_summary = entry_summary[:197] + "..." entry_line = f"**{entry[0]}** [{entry[1]}]: {entry_summary}" test_lines = current_summary_lines + [entry_line] # Test response size with this entry added test_summary = "\n".join(test_lines) test_response = base_response.copy() test_response["summary"] = test_summary response_size = MCPResponseLimiter.estimate_response_size(test_response) if response_size > MCPResponseLimiter.CONTENT_LIMIT: # Can't fit this entry, stop here break # Entry fits, add it current_summary_lines.append(entry_line) included_entries.append(entry) summary_text = "\n".join(current_summary_lines) # Final response with truncation notice if needed final_response = { "status": "success", "session_name": session_name, "summary": summary_text, "entry_count": len(included_entries), "total_entries": total_entries } # Add truncation notice if we couldn't fit all entries if len(included_entries) < len(entries): final_response["_mcp_truncated"] = True final_response["_truncation_reason"] = "Summary limited to 1MB for MCP compliance" final_response["_entries_shown"] = len(included_entries) final_response["_entries_available"] = len(entries) return final_response except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to generate summary: {str(e)}") @router.post("/marm_context_bridge", operation_id="marm_context_bridge") async def marm_context_bridge(request: ContextBridgeRequest): """ 🌉 Intelligent context bridging for smooth workflow transitions Equivalent to /context_bridge: [new topic] command """ try: # Use semantic search for intelligent context bridging if memory.encoder: # Semantic search across memories for better context matching related_memories = await memory.recall_similar( query=request.new_topic, session=None, # Search across all sessions limit=8 ) # Also search log entries with basic text matching as backup with memory.get_connection() as conn: cursor = conn.execute(''' SELECT session_name, topic, summary, full_entry FROM log_entries WHERE topic LIKE ? OR summary LIKE ? ORDER BY entry_date DESC LIMIT 3 ''', (f"%{request.new_topic}%", f"%{request.new_topic}%")) log_matches = cursor.fetchall() # Combine semantic and text matches related_content = [] for memory_item in related_memories[:5]: related_content.append({ 'type': 'memory', 'session': memory_item['session_name'], 'content': memory_item['content'], 'similarity': memory_item['similarity'], 'context_type': memory_item['context_type'] }) for log_item in log_matches: related_content.append({ 'type': 'log', 'session': log_item[0], 'topic': log_item[1], 'summary': log_item[2], 'similarity': 0.7 # Default for text matches }) else: # Fallback to basic text search if no semantic search with memory.get_connection() as conn: cursor = conn.execute(''' SELECT session_name, topic, summary, full_entry FROM log_entries WHERE topic LIKE ? OR summary LIKE ? ORDER BY entry_date DESC LIMIT 5 ''', (f"%{request.new_topic}%", f"%{request.new_topic}%")) log_matches = cursor.fetchall() related_content = [] for log_item in log_matches: related_content.append({ 'type': 'log', 'session': log_item[0], 'topic': log_item[1], 'summary': log_item[2], 'similarity': 0.7 }) # Prepare base response metadata base_response = { "status": "success", "new_topic": request.new_topic, "session_name": request.session_name, } # Apply MCP size limiting to related content first limited_content, was_truncated = MCPResponseLimiter.limit_context_bridge_response( related_content, base_response ) # Build bridge text with limited content bridge_lines = [f"# Context Bridge: {request.new_topic}"] bridge_lines.append(f"Session: {request.session_name}") bridge_lines.append("") if limited_content: bridge_lines.append("## Related Context:") # Sort by similarity for better relevance sorted_content = sorted(limited_content, key=lambda x: x.get('similarity', 0), reverse=True) for item in sorted_content: similarity_pct = int(item.get('similarity', 0.7) * 100) session_badge = f"[{item['session']}]" if item.get('type') == 'memory': context_badge = f"[{item['context_type'].upper()}]" content_preview = item['content'][:100] + "..." if len(item['content']) > 100 else item['content'] truncation_indicator = " [TRUNCATED]" if item.get('_truncated', False) else "" bridge_lines.append(f"- {session_badge} {context_badge} ({similarity_pct}%): {content_preview}{truncation_indicator}") else: # log entry bridge_lines.append(f"- {session_badge} [LOG] ({similarity_pct}%): {item['topic']} - {item['summary']}") bridge_lines.append("") # Add truncation notice if content was limited if was_truncated: bridge_lines.append(f"*Note: Results limited for size compliance. {len(related_content)} total matches found, showing {len(limited_content)}.*") bridge_lines.append("") # Smart recommendations based on limited content found if limited_content: bridge_lines.append("## Recommended Approach:") context_types = [item.get('context_type', 'general') for item in limited_content if item.get('type') == 'memory'] if 'code' in context_types: bridge_lines.append("- Review related code patterns and implementations above") bridge_lines.append("- Consider lessons learned from similar technical work") elif 'project' in context_types: bridge_lines.append("- Build on successful project patterns identified above") bridge_lines.append("- Apply lessons learned from previous project phases") else: bridge_lines.append("- Leverage insights from related work shown above") bridge_lines.append("- Build on established patterns and approaches") else: bridge_lines.append("## Starting Fresh:") bridge_lines.append("- No directly related context found - starting with clean slate") bridge_lines.append("- Consider documenting key decisions as you progress") bridge_lines.append("") bridge_lines.append("---") bridge_lines.append("*Ready to proceed with focused work*") bridge_text = "\n".join(bridge_lines) final_response = { "status": "success", "new_topic": request.new_topic, "session_name": request.session_name, "bridge_text": bridge_text, "related_count": len(limited_content), "total_available": len(related_content) } # Add truncation notice if needed if was_truncated: final_response['_mcp_truncated'] = True final_response['_truncation_reason'] = "Content limited to 1MB for MCP compliance" return final_response except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to create context bridge: {str(e)}")

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Lyellr88/marm-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server