Skip to content

feat(mcp): add permissions and hooks features to rulesync MCP server#1537

Merged
dyoshikawa merged 2 commits intomainfrom
enhance-mcp
Apr 22, 2026
Merged

feat(mcp): add permissions and hooks features to rulesync MCP server#1537
dyoshikawa merged 2 commits intomainfrom
enhance-mcp

Conversation

@dyoshikawa
Copy link
Copy Markdown
Owner

Summary

  • Extend the rulesync mcp server (the rulesyncTool gateway) to support two new features: permissions and hooks.
  • Both features expose get / put / delete operations that read, upsert, and remove .rulesync/permissions.json and .rulesync/hooks.json respectively — mirroring the existing mcp and ignore routes (JSON validation on put, 1 MB size cap, zod schema validation via RulesyncPermissions / RulesyncHooks file classes).
  • Adds per-feature unit tests (src/mcp/permissions.test.ts, src/mcp/hooks.test.ts), gateway-level dispatch tests in src/mcp/tools.test.ts to cover the new switch-cases and the content required-guard, and regenerated skill docs.

Test plan

  • pnpm cicheck — 5088 tests / 198 files passing, type/lint/format/cspell/secretlint/sync-skill-docs all green.
  • Start rulesync mcp locally and drive the new operations from an MCP client (e.g. feature: "permissions", operation: "put", content: "{...}") to confirm end-to-end wiring.
  • Verify the tool description text rendered by the MCP host lists the new permissions / hooks features.

🤖 Generated with Claude Code

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>
Copilot AI review requested due to automatic review settings April 22, 2026 04:10
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 permissions and hooks feature routing to rulesyncTool (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.

Comment thread src/mcp/hooks.ts
Comment on lines +51 to +53
if (content.length > maxHooksSizeBytes) {
throw new Error(
`Hooks file size ${content.length} bytes exceeds maximum ${maxHooksSizeBytes} bytes (1MB) for ${RULESYNC_HOOKS_RELATIVE_FILE_PATH}`,
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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}`,

Copilot uses AI. Check for mistakes.
Comment thread src/mcp/hooks.ts
Comment on lines +24 to +27
const relativePathFromCwd = join(
rulesyncHooks.getRelativeDirPath(),
rulesyncHooks.getRelativeFilePath(),
);
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

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).

Copilot uses AI. Check for mistakes.
Comment thread src/mcp/tools.ts
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.",
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
"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.",

Copilot uses AI. Check for mistakes.
Comment thread src/mcp/permissions.ts
Comment on lines +51 to +53
if (content.length > maxPermissionsSizeBytes) {
throw new Error(
`Permissions file size ${content.length} bytes exceeds maximum ${maxPermissionsSizeBytes} bytes (1MB) for ${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}`,
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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}`,

Copilot uses AI. Check for mistakes.
Comment thread src/mcp/permissions.ts
Comment on lines +24 to +27
const relativePathFromCwd = join(
rulesyncPermissions.getRelativeDirPath(),
rulesyncPermissions.getRelativeFilePath(),
);
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

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).

Copilot uses AI. Check for mistakes.
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 dyoshikawa merged commit bbe7ea7 into main Apr 22, 2026
9 checks passed
@dyoshikawa dyoshikawa deleted the enhance-mcp branch April 22, 2026 05:09
@dyoshikawa
Copy link
Copy Markdown
Owner Author

@dyoshikawa Thank you!

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