Skip to content

Add scratchpad feature with read, write, and edit operations#27

Merged
marcelsamyn merged 1 commit intomainfrom
claude/add-scratchpad-memory-6lusi
Mar 4, 2026
Merged

Add scratchpad feature with read, write, and edit operations#27
marcelsamyn merged 1 commit intomainfrom
claude/add-scratchpad-memory-6lusi

Conversation

@marcelsamyn
Copy link
Owner

Summary

This PR introduces a new scratchpad feature that allows users to store and manage persistent text content. The scratchpad supports read, write (with overwrite/append modes), and edit operations with built-in safeguards.

Key Changes

  • Database Schema: Added scratchpads table with user-specific storage (one scratchpad per user via unique constraint on user_id)
  • Core Library: Implemented src/lib/scratchpad.ts with three main operations:
    • readScratchpad: Retrieves existing scratchpad content or returns empty state
    • writeScratchpad: Overwrites or appends content to scratchpad
    • editScratchpad: Safely replaces text with validation to prevent ambiguous replacements
  • API Routes: Added three POST endpoints under /scratchpad/:
    • read.post.ts: Fetch scratchpad content
    • write.post.ts: Create or update scratchpad
    • edit.post.ts: Modify specific text within scratchpad
  • MCP Integration: Registered three new tools in the MCP server for AI assistant access to scratchpad functionality
  • Schemas: Created Zod validation schemas for all scratchpad request/response types
  • Type System: Added scratchpad to the TypeID system for consistent ID generation

Implementation Details

  • The edit operation includes safeguards: it validates that the text to replace exists exactly once, preventing accidental bulk replacements
  • Scratchpad content defaults to empty string and is automatically created on first write
  • All operations track updatedAt timestamp for audit purposes
  • Uses database upsert pattern to handle both creation and updates efficiently

https://claude.ai/code/session_01BNiwDytpczv5sEB3dsxPgQ

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request delivers a foundational scratchpad feature, providing users with a private, persistent space to store and manipulate text. It establishes the necessary database infrastructure, implements robust backend logic for reading, writing, and safely editing content, and exposes these capabilities through new API endpoints. Crucially, it integrates this new functionality into the AI assistant's toolset, enhancing its ability to manage user-specific information.

Highlights

  • New Scratchpad Feature: Introduced a persistent text scratchpad for users, enabling storage and management of personal notes or content.
  • Database Integration: Added a new scratchpads table to the database schema, ensuring user-specific storage with unique constraints and proper indexing.
  • Core Operations: Implemented readScratchpad, writeScratchpad (with overwrite/append modes), and editScratchpad functions, including safeguards for the edit operation to prevent ambiguous replacements.
  • API Endpoints: Created dedicated POST API routes (/scratchpad/read, /scratchpad/write, /scratchpad/edit) for interacting with the scratchpad functionality.
  • AI Assistant Integration: Registered three new tools within the MCP server, allowing AI assistants to access and utilize the scratchpad features.
  • Schema and Type System Updates: Developed Zod validation schemas for all scratchpad requests and responses, and integrated 'scratchpad' into the TypeID system for consistent ID generation.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • .gitignore
    • Updated to include package-lock.json within the minio-data/ exclusion.
  • drizzle/0007_illegal_lila_cheney.sql
    • Added SQL migration to create the scratchpads table with id, user_id, content, updated_at, and created_at columns.
    • Established a unique constraint on user_id for the scratchpads table.
    • Created a foreign key relationship linking scratchpads.user_id to users.id.
    • Added an index on scratchpads.user_id.
  • drizzle/meta/0007_snapshot.json
    • Updated the Drizzle ORM snapshot to reflect the new scratchpads table and its associated schema details.
  • drizzle/meta/_journal.json
    • Recorded the new Drizzle migration 0007_illegal_lila_cheney in the journal.
  • src/db/schema.ts
    • Defined the scratchpads table schema using Drizzle ORM, including typeId for id, text for userId and content, and timestamp for updatedAt and createdAt.
    • Configured a unique constraint and an index on userId for the scratchpads table.
    • Established a one-to-one relationship between scratchpads and users.
  • src/lib/mcp/mcp-server.ts
    • Imported scratchpad-related schemas and functions.
    • Registered a 'read scratchpad' tool, allowing AI to fetch scratchpad content.
    • Registered a 'write scratchpad' tool, enabling AI to overwrite or append to the scratchpad.
    • Registered an 'edit scratchpad' tool, providing AI with a safeguarded text replacement mechanism for the scratchpad.
  • src/lib/schemas/scratchpad.ts
    • Created Zod schemas for scratchpadReadRequest, scratchpadWriteRequest, and scratchpadEditRequest.
    • Defined Zod schemas for scratchpadResponse and scratchpadEditResponse.
    • Exported corresponding TypeScript types for all scratchpad schemas.
  • src/lib/scratchpad.ts
    • Implemented readScratchpad to retrieve user-specific scratchpad content, returning an empty state if none exists.
    • Implemented writeScratchpad to handle both overwriting and appending content to a user's scratchpad.
    • Implemented editScratchpad with logic to validate that the oldText exists exactly once before replacement, returning an error message if not.
    • Created an internal upsertScratchpad function to efficiently create or update scratchpad entries in the database.
  • src/routes/scratchpad/edit.post.ts
    • Added a new API route for handling POST requests to edit scratchpad content, utilizing the editScratchpad function and Zod schemas for validation.
  • src/routes/scratchpad/read.post.ts
    • Added a new API route for handling POST requests to read scratchpad content, utilizing the readScratchpad function and Zod schemas for validation.
  • src/routes/scratchpad/write.post.ts
    • Added a new API route for handling POST requests to write scratchpad content, utilizing the writeScratchpad function and Zod schemas for validation.
  • src/types/typeid.ts
    • Added 'scratchpad' to the list of ID_TYPE_NAMES.
    • Assigned the prefix 'spad' to the 'scratchpad' ID type.
Activity
  • No specific activity (comments, reviews, progress updates) has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new scratchpad feature, enabling users to store, read, write, and edit persistent text content. The changes include a new scratchpads table in the database schema, core library functions (readScratchpad, writeScratchpad, editScratchpad), corresponding API routes, and integration with the MCP server. Zod schemas are used for robust request and response validation, and the TypeID system has been extended for scratchpad IDs. The implementation includes safeguards for the edit operation to prevent ambiguous replacements. However, critical security vulnerabilities, primarily related to Insecure Direct Object Reference (IDOR), were identified. The API endpoints and MCP tools accept a userId directly from the request without proper authorization checks, which could allow any user to access or modify the scratchpad of any other user. It is crucial to implement robust authentication and authorization checks to ensure users can only interact with their own data.

Comment on lines +8 to +9
const params = scratchpadReadRequestSchema.parse(await readBody(event));
return scratchpadResponseSchema.parse(await readScratchpad(params));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The read.post.ts endpoint accepts a userId directly from the request body and uses it to fetch the scratchpad content without verifying if the authenticated user has permission to access that userId's data. This allows any user to read the scratchpad of any other user if they know their userId.

Comment on lines +8 to +9
const params = scratchpadWriteRequestSchema.parse(await readBody(event));
return scratchpadResponseSchema.parse(await writeScratchpad(params));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The write.post.ts endpoint accepts a userId directly from the request body and uses it to create or update a scratchpad without verifying if the authenticated user has permission to modify that userId's data. This allows any user to overwrite or append to the scratchpad of any other user.

Comment on lines +8 to +9
const params = scratchpadEditRequestSchema.parse(await readBody(event));
return scratchpadEditResponseSchema.parse(await editScratchpad(params));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The edit.post.ts endpoint accepts a userId directly from the request body and uses it to modify a scratchpad without verifying if the authenticated user has permission to edit that userId's data. This allows any user to perform targeted edits on the scratchpad of any other user.

Comment on lines +107 to +152
server.tool(
"read scratchpad",
scratchpadReadRequestSchema,
async ({ userId }) => {
const result = await readScratchpad({ userId });
return {
content: [
{ type: "text", text: result.content || "(empty scratchpad)" },
],
};
},
);

// Write scratchpad (overwrite or append)
server.tool(
"write scratchpad",
scratchpadWriteRequestSchema,
async (params) => {
const result = await writeScratchpad(params);
return {
content: [
{ type: "text", text: `Scratchpad updated.\n\n${result.content}` },
],
};
},
);

// Edit scratchpad (replace text with safeguards)
server.tool(
"edit scratchpad",
scratchpadEditRequestSchema,
async (params) => {
const result = await editScratchpad(params);
if (!result.applied) {
return {
content: [{ type: "text", text: `Edit failed: ${result.message}` }],
isError: true,
};
}
return {
content: [
{ type: "text", text: `Edit applied.\n\n${result.content}` },
],
};
},
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The MCP tools for reading, writing, and editing scratchpads accept a userId as a parameter and pass it directly to the underlying library functions without any authorization checks. If these tools are exposed to untrusted users (e.g., through an AI assistant), they could be used to access or modify other users' scratchpads.

.gitignore Outdated

postgres_data/
minio-data/ No newline at end of file
minio-data/package-lock.json
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The .gitignore entry was changed from minio-data/ to minio-data/package-lock.json. The original entry ignored the entire minio-data directory and its contents. The new entry only ignores package-lock.json within that directory. This means other files in minio-data/ will now be tracked by Git. Please confirm if this change in scope is intentional. If the entire minio-data directory should still be ignored, the entry should remain minio-data/.

minio-data/

});

if (!existing) {
return { content: "", updatedAt: new Date() };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When a scratchpad does not exist, the readScratchpad function returns a synthetic updatedAt timestamp (new Date()). This can be semantically misleading as a non-existent entity doesn't truly have an updatedAt timestamp from the database. Consider making updatedAt optional in ScratchpadResponse and its Zod schema (scratchpadResponseSchema) to accurately reflect when a scratchpad has not yet been persisted.

@marcelsamyn marcelsamyn force-pushed the claude/add-scratchpad-memory-6lusi branch from 8fb9a8e to 7c8bbe1 Compare March 4, 2026 12:40
Add a scratchpad/notepad that LLM assistants can read, write, and edit
directly, giving them explicit control over what they remember alongside
the automatic background ingestion pipeline.

- New `scratchpads` table (one per user, dedicated table following
  userProfiles pattern)
- Scratchpad service with read, write (overwrite/append), and edit
  (targeted text replacement with safeguards)
- Edit safeguards: existence check, uniqueness check (text must appear
  exactly once), exact string matching (no regex), returns current
  content on failure for retry
- MCP tools: "read scratchpad", "write scratchpad", "edit scratchpad"
- HTTP routes: POST /scratchpad/read, /scratchpad/write, /scratchpad/edit
- Drizzle migration for the new table
- Add package-lock.json to .gitignore

https://claude.ai/code/session_01BNiwDytpczv5sEB3dsxPgQ
@marcelsamyn marcelsamyn force-pushed the claude/add-scratchpad-memory-6lusi branch from 7c8bbe1 to 8fd6268 Compare March 4, 2026 12:42
@marcelsamyn marcelsamyn merged commit 4d36f21 into main Mar 4, 2026
1 check passed
@marcelsamyn marcelsamyn deleted the claude/add-scratchpad-memory-6lusi branch March 4, 2026 12:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants