Skip to main content
Glama

Recurring pitfalls log

pitfalls_log

Track recurring issues and their resolutions. Store a pitfall with title and description, list open or resolved entries, or mark a pitfall as resolved. Builds a searchable history of project-specific problems and workarounds.

Instructions

Multi-action log of recurring problems and their resolutions. Pick one via action: • store — record a new pitfall (title + body required). Writes a row. • list — list open pitfalls (set include_resolved=true for all). Read-only. • resolve — mark a pitfall resolved (pitfall_id required). Use when something keeps biting and you want a queryable problem→resolution log. For one-off design choices, use decisions_log.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
actionYesWhich sub-operation to perform: `store`, `list`, or `resolve`.
project_pathYesAbsolute project path the pitfall belongs to (or to scope `list`). Required.
titleNoShort symptom title (required for `store`). E.g. `"esbuild fails on M1 when using esm + node-gyp"`.
bodyNoMarkdown body (required for `store`). Should describe the problem and the resolution / workaround.
importanceNoImportance score in [0, 1] for `store`. Default 0.6.
limitNoMaximum pitfalls to return for `list` (1-50). Default 10.
include_resolvedNoFor `list`: if true, also include resolved pitfalls. Default false (open only).
pitfall_idNoRequired for `action="resolve"` — the id of the pitfall to mark resolved.

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
messageYesFor `store`: `Pitfall logged/updated with ID: <id>`. For `list`: markdown list with `[RESOLVED]` or `[xN]` (occurrence count) prefixes, or `No pitfalls found.` For `resolve`: `Pitfall <id> marked as resolved.` or `Pitfall <id> not found.`

Implementation Reference

  • The `handlePitfallsLog` function implements the tool logic for `pitfalls_log`. It dispatches on `action` ('store', 'list', 'resolve') and calls the corresponding `PitfallsRepo` methods.
    export async function handlePitfallsLog(repo: PitfallsRepo, params: {
      action: string; project_path: string; title?: string; body?: string;
      importance?: number; limit?: number; include_resolved?: boolean; pitfall_id?: string;
    }): Promise<string> {
      if (params.action === "store") {
        if (!params.title || !params.body) return "title and body are required for action='store'.";
        const id = repo.store(params.project_path, params.title, params.body, params.importance);
        return `Pitfall logged/updated with ID: ${id}`;
      }
      if (params.action === "list") {
        const pitfalls = repo.list(params.project_path, params.limit, params.include_resolved);
        if (!pitfalls.length) return "No pitfalls found.";
        return pitfalls.map(p => {
          const status = p.resolved ? "RESOLVED" : `x${p.occurrence_count}`;
          return `[${status}] ${p.title}\n  ID: ${p.id}\n  ${(p.body as string).slice(0, 300)}\n  Last seen: ${p.last_seen_at}`;
        }).join("\n\n");
      }
      if (params.action === "resolve") {
        if (!params.pitfall_id) return "pitfall_id is required for action='resolve'.";
        return repo.resolve(params.pitfall_id)
          ? `Pitfall ${params.pitfall_id} marked as resolved.`
          : `Pitfall ${params.pitfall_id} not found.`;
      }
      return `Invalid action: ${params.action}. Use 'store', 'list', or 'resolve'.`;
    }
  • Type-level schema for the params object: action, project_path, title?, body?, importance?, limit?, include_resolved?, pitfall_id?
    export async function handlePitfallsLog(repo: PitfallsRepo, params: {
      action: string; project_path: string; title?: string; body?: string;
      importance?: number; limit?: number; include_resolved?: boolean; pitfall_id?: string;
    }): Promise<string> {
  • src/index.ts:475-508 (registration)
    The `pitfalls_log` tool is registered on the MCP server via `server.registerTool('pitfalls_log', ...)` with inputSchema using Zod validation and an async handler calling `handlePitfallsLog`.
    server.registerTool(
      "pitfalls_log",
      {
        title: "Recurring pitfalls log",
        description: [
          "Multi-action log of recurring problems and their resolutions. Pick one via `action`:",
          "  • `store` — record a new pitfall (`title` + `body` required). Writes a row.",
          "  • `list` — list open pitfalls (set `include_resolved=true` for all). Read-only.",
          "  • `resolve` — mark a pitfall resolved (`pitfall_id` required).",
          "Use when something keeps biting and you want a queryable problem→resolution log. For one-off design choices, use `decisions_log`.",
        ].join(" "),
        inputSchema: {
          action: z.enum(["store", "list", "resolve"]).describe("Which sub-operation to perform: `store`, `list`, or `resolve`."),
          project_path: z.string().describe("Absolute project path the pitfall belongs to (or to scope `list`). Required."),
          title: z.string().default("").describe("Short symptom title (required for `store`). E.g. `\"esbuild fails on M1 when using esm + node-gyp\"`."),
          body: z.string().default("").describe("Markdown body (required for `store`). Should describe the problem and the resolution / workaround."),
          importance: z.number().min(0).max(1).default(0.6).describe("Importance score in [0, 1] for `store`. Default 0.6."),
          limit: z.number().int().min(1).max(50).default(10).describe("Maximum pitfalls to return for `list` (1-50). Default 10."),
          include_resolved: z.boolean().default(false).describe("For `list`: if true, also include resolved pitfalls. Default false (open only)."),
          pitfall_id: z.string().default("").describe("Required for `action=\"resolve\"` — the id of the pitfall to mark resolved."),
        },
        annotations: {
          title: "Recurring pitfalls log",
          readOnlyHint: false,
          destructiveHint: false,
          idempotentHint: false,
          openWorldHint: false,
        },
        outputSchema: {
          message: z.string().describe("For `store`: `Pitfall logged/updated with ID: <id>`. For `list`: markdown list with `[RESOLVED]` or `[xN]` (occurrence count) prefixes, or `No pitfalls found.` For `resolve`: `Pitfall <id> marked as resolved.` or `Pitfall <id> not found.`"),
        },
      },
      async (params) => textResult(await handlePitfallsLog(pitRepo, params))
    );
  • Input schema for `pitfalls_log` using Zod: action (enum), project_path (string), title, body, importance, limit, include_resolved, pitfall_id — with descriptions and defaults.
    inputSchema: {
      action: z.enum(["store", "list", "resolve"]).describe("Which sub-operation to perform: `store`, `list`, or `resolve`."),
      project_path: z.string().describe("Absolute project path the pitfall belongs to (or to scope `list`). Required."),
      title: z.string().default("").describe("Short symptom title (required for `store`). E.g. `\"esbuild fails on M1 when using esm + node-gyp\"`."),
      body: z.string().default("").describe("Markdown body (required for `store`). Should describe the problem and the resolution / workaround."),
      importance: z.number().min(0).max(1).default(0.6).describe("Importance score in [0, 1] for `store`. Default 0.6."),
      limit: z.number().int().min(1).max(50).default(10).describe("Maximum pitfalls to return for `list` (1-50). Default 10."),
      include_resolved: z.boolean().default(false).describe("For `list`: if true, also include resolved pitfalls. Default false (open only)."),
      pitfall_id: z.string().default("").describe("Required for `action=\"resolve\"` — the id of the pitfall to mark resolved."),
    },
  • The `PitfallsRepo` class (store, list, listAll, resolve) provides the database operations backing the `pitfalls_log` tool — including secret scrubbing, dedup logic, and occurrence counting.
    export class PitfallsRepo {
      constructor(private db: Database.Database) {}
    
      private getOrCreateProject(rootPath: string): string {
        const row = this.db.prepare("SELECT id FROM projects WHERE root_path = ?").get(rootPath) as any;
        if (row) return row.id;
        const id = randomUUID();
        this.db.prepare("INSERT INTO projects (id, name, root_path) VALUES (?, ?, ?)").run(id, rootPath.split("/").pop() ?? rootPath, rootPath);
        return id;
      }
    
      store(projectPath: string, title: string, body: string, importance = 0.6): string {
        const projectId = this.getOrCreateProject(projectPath);
        const now = nowIso();
    
        // Issue #12: scrub secrets from title and body at write time.
        const cleanTitle = scrubSecrets(title);
        const cleanBody = scrubSecrets(body);
        if (cleanTitle !== title) {
          logger.warn(`Secret pattern detected and scrubbed in pitfall title`);
        }
        if (hasPrivate(title)) {
          logger.warn(`Warning: <private> tags detected in pitfall title — tags do not redact in titles. Move sensitive content to body.`);
        }
    
        const existing = this.db.prepare(
          "SELECT id, occurrence_count FROM pitfalls WHERE project_id = ? AND title = ? AND deleted_at IS NULL AND resolved = 0"
        ).get(projectId, cleanTitle) as any;
    
        // Issue #4: set has_private flag on store/update.
        const hasPrivateFlag = hasPrivate(cleanBody) ? 1 : 0;
        if (existing) {
          this.db.prepare("UPDATE pitfalls SET occurrence_count = occurrence_count + 1, last_seen_at = ?, body = ?, has_private = ? WHERE id = ?")
            .run(now, cleanBody, hasPrivateFlag, existing.id);
          return existing.id;
        }
    
        const id = randomUUID();
        this.db.prepare(`
          INSERT INTO pitfalls (id, project_id, title, body, importance_score, last_seen_at, has_private, created_at)
          VALUES (?, ?, ?, ?, ?, ?, ?, ?)
        `).run(id, projectId, cleanTitle, cleanBody, importance, now, hasPrivateFlag, now);
        return id;
      }
    
      list(projectPath: string, limit = 10, includeResolved = false): any[] {
        const projectId = this.getOrCreateProject(projectPath);
        const resolvedClause = includeResolved ? "" : "AND resolved = 0";
        return this.db.prepare(`
          SELECT * FROM pitfalls WHERE project_id = ? AND deleted_at IS NULL ${resolvedClause}
          ORDER BY occurrence_count DESC, importance_score DESC LIMIT ?
        `).all(projectId, limit) as any[];
      }
    
      listAll(limit = 10, includeResolved = false): any[] {
        const resolvedClause = includeResolved ? "" : "AND resolved = 0";
        return this.db.prepare(`
          SELECT * FROM pitfalls WHERE deleted_at IS NULL ${resolvedClause}
          ORDER BY occurrence_count DESC, importance_score DESC LIMIT ?
        `).all(limit) as any[];
      }
    
      resolve(pitfallId: string): boolean {
        return this.db.prepare("UPDATE pitfalls SET resolved = 1 WHERE id = ? AND deleted_at IS NULL").run(pitfallId).changes > 0;
      }
    }
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses behavioral traits beyond annotations: 'Writes a row' for store, 'Read-only' for list, and 'mark a pitfall resolved' for resolve. Annotations already indicate non-read-only, so description adds useful context without contradiction.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact, using bullet points and bold for actions. Every sentence is informative and necessary. It is front-loaded with the overall purpose and efficiently covers all three actions.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the presence of an output schema, the description need not explain return values. It covers all actions, parameter requirements, distinguishes from sibling, and provides context on markdown body and importance score. It is complete for the tool's complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 100% schema coverage, baseline is 3. The description adds value by explaining per-action parameter requirements (e.g., 'title + body required for store') and providing an example for title, going beyond the schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it is a 'Multi-action log of recurring problems and their resolutions' and enumerates three distinct actions (store, list, resolve). It explicitly distinguishes from the sibling tool 'decisions_log' by noting that one-off design choices should use that tool instead.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear when-to-use guidance for each action and differentiates from 'decisions_log' for one-off choices. It lacks explicit when-not-to-use guidance for the overall tool but is otherwise strong.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/lfrmonteiro99/memento-mcp'

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