feat(mcp): add permissions and hooks features to rulesync MCP server#1537
feat(mcp): add permissions and hooks features to rulesync MCP server#1537dyoshikawa merged 2 commits intomainfrom
Conversation
Extend the rulesyncTool gateway (exposed via `rulesync mcp`) to support get/put/delete operations on `.rulesync/permissions.json` and `.rulesync/hooks.json`, mirroring the existing `mcp` and `ignore` feature routes. Adds per-feature implementations, unit tests, gateway-level dispatch tests, and docs sync. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Extends the rulesync mcp server’s single rulesyncTool gateway to support managing two additional fixed JSON configuration files—.rulesync/permissions.json and .rulesync/hooks.json—via new permissions and hooks features, aligned with the existing ignore/mcp content-based operations.
Changes:
- Add
permissionsandhooksfeature routing torulesyncTool(get/put/delete) and update MCP docs to list the new capabilities. - Introduce new MCP tool modules (
src/mcp/permissions.ts,src/mcp/hooks.ts) with JSON validation and size caps. - Add unit tests for the new tools plus gateway dispatch tests.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/mcp/tools.ts | Adds permissions/hooks feature enum entries and dispatch logic in the unified MCP tool. |
| src/mcp/tools.test.ts | Adds gateway-level lifecycle tests for the new content-based features. |
| src/mcp/permissions.ts | Implements get/put/delete tool functions for .rulesync/permissions.json. |
| src/mcp/permissions.test.ts | Adds unit tests for permissions MCP tools (valid/invalid/oversize/idempotent delete). |
| src/mcp/hooks.ts | Implements get/put/delete tool functions for .rulesync/hooks.json. |
| src/mcp/hooks.test.ts | Adds unit tests for hooks MCP tools (valid/invalid/oversize/idempotent delete). |
| skills/rulesync/mcp-server.md | Updates skill docs to list supported features/operations. |
| docs/reference/mcp-server.md | Updates reference docs to list supported features/operations. |
| if (content.length > maxHooksSizeBytes) { | ||
| throw new Error( | ||
| `Hooks file size ${content.length} bytes exceeds maximum ${maxHooksSizeBytes} bytes (1MB) for ${RULESYNC_HOOKS_RELATIVE_FILE_PATH}`, |
There was a problem hiding this comment.
The size limit check uses content.length (UTF-16 code units) but the constant is expressed in bytes. This can under/over-count for non-ASCII content and incorrectly allow/reject payloads. Use Buffer.byteLength(content, "utf8") (as done in src/mcp/ignore.ts) and report the computed byte size in the error message.
| if (content.length > maxHooksSizeBytes) { | |
| throw new Error( | |
| `Hooks file size ${content.length} bytes exceeds maximum ${maxHooksSizeBytes} bytes (1MB) for ${RULESYNC_HOOKS_RELATIVE_FILE_PATH}`, | |
| const contentSizeBytes = Buffer.byteLength(content, "utf8"); | |
| if (contentSizeBytes > maxHooksSizeBytes) { | |
| throw new Error( | |
| `Hooks file size ${contentSizeBytes} bytes exceeds maximum ${maxHooksSizeBytes} bytes (1MB) for ${RULESYNC_HOOKS_RELATIVE_FILE_PATH}`, |
| const relativePathFromCwd = join( | ||
| rulesyncHooks.getRelativeDirPath(), | ||
| rulesyncHooks.getRelativeFilePath(), | ||
| ); |
There was a problem hiding this comment.
relativePathFromCwd is part of the tool response payload (non-filesystem path). Using node:path.join here will emit platform-specific separators (e.g., \ on Windows), which can break clients/tests expecting POSIX paths. Prefer returning RULESYNC_HOOKS_RELATIVE_FILE_PATH directly, or build it with path.posix.join / toPosixPath for stable forward slashes (see src/constants/rulesync-paths.ts where these constants are defined with path.posix.join).
| name: "rulesyncTool", | ||
| description: | ||
| "Manage Rulesync files through a single MCP tool. Features: rule/command/subagent/skill support list/get/put/delete; ignore/mcp support get/put/delete only; generate supports run only; import supports run only. Parameters: list requires no targetPathFromCwd (lists all items); get/delete require targetPathFromCwd; put requires targetPathFromCwd, frontmatter, and body (or content for ignore/mcp); generate/run uses generateOptions to configure generation; import/run uses importOptions to configure import.", | ||
| "Manage Rulesync files through a single MCP tool. Features: rule/command/subagent/skill support list/get/put/delete; ignore/mcp/permissions/hooks support get/put/delete only; generate supports run only; import supports run only. Parameters: list requires no targetPathFromCwd (lists all items); get/delete require targetPathFromCwd; put requires targetPathFromCwd, frontmatter, and body (or content for ignore/mcp/permissions/hooks); generate/run uses generateOptions to configure generation; import/run uses importOptions to configure import.", |
There was a problem hiding this comment.
The tool description says put requires targetPathFromCwd, but for ignore/mcp/permissions/hooks the implementation does not accept or require targetPathFromCwd (it always targets fixed .rulesync/*.json files). Update the description to avoid misleading MCP clients/tooling that rely on this text.
| "Manage Rulesync files through a single MCP tool. Features: rule/command/subagent/skill support list/get/put/delete; ignore/mcp/permissions/hooks support get/put/delete only; generate supports run only; import supports run only. Parameters: list requires no targetPathFromCwd (lists all items); get/delete require targetPathFromCwd; put requires targetPathFromCwd, frontmatter, and body (or content for ignore/mcp/permissions/hooks); generate/run uses generateOptions to configure generation; import/run uses importOptions to configure import.", | |
| "Manage Rulesync files through a single MCP tool. Features: rule/command/subagent/skill support list/get/put/delete; ignore/mcp/permissions/hooks support get/put/delete only; generate supports run only; import supports run only. Parameters: for rule/command/subagent/skill, list requires no targetPathFromCwd (lists all items), get/delete require targetPathFromCwd, and put requires targetPathFromCwd, frontmatter, and body; for ignore/mcp/permissions/hooks, get/delete require no targetPathFromCwd and put requires content only; generate/run uses generateOptions to configure generation; import/run uses importOptions to configure import.", |
| if (content.length > maxPermissionsSizeBytes) { | ||
| throw new Error( | ||
| `Permissions file size ${content.length} bytes exceeds maximum ${maxPermissionsSizeBytes} bytes (1MB) for ${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}`, |
There was a problem hiding this comment.
The size limit check uses content.length (UTF-16 code units) but the constant is expressed in bytes. This can under/over-count for non-ASCII content and incorrectly allow/reject payloads. Use Buffer.byteLength(content, "utf8") (as done in src/mcp/ignore.ts) and report the computed byte size in the error message.
| if (content.length > maxPermissionsSizeBytes) { | |
| throw new Error( | |
| `Permissions file size ${content.length} bytes exceeds maximum ${maxPermissionsSizeBytes} bytes (1MB) for ${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}`, | |
| const contentSizeBytes = Buffer.byteLength(content, "utf8"); | |
| if (contentSizeBytes > maxPermissionsSizeBytes) { | |
| throw new Error( | |
| `Permissions file size ${contentSizeBytes} bytes exceeds maximum ${maxPermissionsSizeBytes} bytes (1MB) for ${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}`, |
| const relativePathFromCwd = join( | ||
| rulesyncPermissions.getRelativeDirPath(), | ||
| rulesyncPermissions.getRelativeFilePath(), | ||
| ); |
There was a problem hiding this comment.
relativePathFromCwd is part of the tool response payload (non-filesystem path). Using node:path.join here will emit platform-specific separators (e.g., \ on Windows), which can break clients/tests expecting POSIX paths. Prefer returning RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH directly, or build it with path.posix.join / toPosixPath for stable forward slashes (see src/constants/rulesync-paths.ts where these constants are defined with path.posix.join).
Spawn `rulesync mcp` as a child process and drive it via the official MCP SDK stdio client to verify put/get/delete round-trips against the real binary for the new permissions and hooks features, plus a negative case for the missing-content guard. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@dyoshikawa Thank you! |
Summary
rulesync mcpserver (therulesyncToolgateway) to support two new features:permissionsandhooks.get/put/deleteoperations that read, upsert, and remove.rulesync/permissions.jsonand.rulesync/hooks.jsonrespectively — mirroring the existingmcpandignoreroutes (JSON validation on put, 1 MB size cap, zod schema validation viaRulesyncPermissions/RulesyncHooksfile classes).src/mcp/permissions.test.ts,src/mcp/hooks.test.ts), gateway-level dispatch tests insrc/mcp/tools.test.tsto cover the new switch-cases and thecontentrequired-guard, and regenerated skill docs.Test plan
pnpm cicheck— 5088 tests / 198 files passing, type/lint/format/cspell/secretlint/sync-skill-docs all green.rulesync mcplocally and drive the new operations from an MCP client (e.g.feature: "permissions", operation: "put", content: "{...}") to confirm end-to-end wiring.permissions/hooksfeatures.🤖 Generated with Claude Code