Skip to main content
Glama

marm-mcp

reasoning.pyβ€’11.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)}")

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