get_summary
Retrieve the project memory summary to avoid re-reading source files. Call this before answering any project question to ensure accurate and efficient responses.
Instructions
Read the project memory summary.
MANDATORY: call this BEFORE answering ANY question about the project.
Do NOT answer from conversation history alone.
Do NOT re-scan source files (package.json, README, src/) to understand
the project — `summary.md` is the distilled authoritative source and
costs ~500 tokens versus ~5,000 to re-derive.
Your prior assumptions about this project may be stale. Call this
cheaply at session start (and again before ending) to verify your
work is recorded.Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- src/projectmem/mcp_server.py:192-210 (handler)The get_summary MCP tool handler. Reads and returns the .projectmem/summary.md file contents, or returns a fallback message if the file doesn't exist. Decorated with @mcp.tool() for registration and @safe_tool for exception+stdout safety.
@mcp.tool() @safe_tool def get_summary() -> str: """Read the project memory summary. MANDATORY: call this BEFORE answering ANY question about the project. Do NOT answer from conversation history alone. Do NOT re-scan source files (package.json, README, src/) to understand the project — `summary.md` is the distilled authoritative source and costs ~500 tokens versus ~5,000 to re-derive. Your prior assumptions about this project may be stale. Call this cheaply at session start (and again before ending) to verify your work is recorded.""" path = summary_path() if path.exists(): return path.read_text(encoding="utf-8") return "No summary found. Run `pjm init` first." - src/projectmem/mcp_server.py:192-192 (registration)The @mcp.tool() decorator registers get_summary as an MCP tool on the FastMCP server instance.
@mcp.tool() - src/projectmem/storage.py:68-69 (helper)Helper that resolves the path to .projectmem/summary.md by calling require_mem_dir(). The SUMMARY_FILE constant is 'summary.md'.
def summary_path(root: Path | None = None) -> Path: return require_mem_dir(root) / SUMMARY_FILE - src/projectmem/storage.py:39-61 (helper)Helper that locates the .projectmem directory from an explicit root, CWD, or by walking up the directory tree. Raises ProjectMemError if not found.
def require_mem_dir(root: Path | None = None) -> Path: # If an explicit root was given, honor only that root (back-compat). if root is not None: path = mem_path(root) if path.exists(): return path raise ProjectMemError( f"No .projectmem directory found in {root}. Run `projectmem init`." ) # No explicit root: try CWD first, then walk up the directory tree. cwd_path = mem_path(None) if cwd_path.exists(): return cwd_path found = discover_mem_dir(None) if found is not None: return found raise ProjectMemError( f"No .projectmem directory found in {Path.cwd()} or any parent. " f"If running over MCP, set the project root via the `cwd` field in your " f"MCP client config or via the PROJECTMEM_ROOT environment variable. " f"Otherwise run `projectmem init` to create one." ) - src/projectmem/mcp_server.py:107-122 (helper)Decorator that wraps tool functions to suppress stdout (to prevent JSON-RPC pollution) and catch exceptions (to prevent connection crashes). Applied to get_summary.
def safe_tool(fn: Callable) -> Callable: """Wrap a tool so exceptions become text the AI can read and recover from. Every tool body also runs inside ``_suppress_stdout``. Combined, these two layers guarantee: - JSON-RPC stdio is never polluted (L-009) - A single tool failure never tears down the session (L-010) """ @functools.wraps(fn) def wrapper(*args, **kwargs): try: with _suppress_stdout(): return fn(*args, **kwargs) except Exception as exc: # pragma: no cover - defensive return f"projectmem tool error: {type(exc).__name__}: {exc}" return wrapper