Skip to content

feat(mcp): enforce least-privilege read-only tool access - #29

Open
OllieinCanada wants to merge 1 commit into
render-oss:mainfrom
OllieinCanada:feat/mcp-read-only-mode-5
Open

feat(mcp): enforce least-privilege read-only tool access#29
OllieinCanada wants to merge 1 commit into
render-oss:mainfrom
OllieinCanada:feat/mcp-read-only-mode-5

Conversation

@OllieinCanada

Copy link
Copy Markdown

Summary

  • implement the maintainer-suggested tools/list filtering for X-MCP-Readonly: true
  • enforce the same audited annotation policy at call time, before tool and workspace-scoping handlers, so direct-name calls cannot bypass discovery filtering
  • reject malformed or ambiguous header values and bind the effective access mode to the initialized HTTP MCP session
  • keep absent/false HTTP requests and stdio full-access, while preventing read-only workspace listing from changing compatibility session state
  • validate Postgres queries as a single read-shaped statement before connecting, with the existing PostgreSQL READ ONLY transaction retained as a backstop
  • document API-key and OAuth usage, guarantees, invalid-header behavior, and boundary limitations

The tool annotation audit is the single policy source for both listing and invocation. Registration tests assert the exact named read-only and mutating sets, and unknown tools fail closed.

Mode listed read-only listed mutating read-only call mutating direct call
header absent or false yes yes allowed allowed
header true yes no allowed permission denied before handler

Validation

  • focused access-policy, real streamable HTTP/session, Postgres SQL, workspace, and registration tests
  • go build -v ./...
  • all 44 non-e2e packages with go test
  • go vet ./...
  • golangci-lint 2.13.2 with --new-from-rev upstream/main: 0 issues.
  • go generate ./... completed; its unrelated pre-existing fake-output drift was excluded from this PR
  • git diff --check
  • no go.mod, go.sum, or generated-fake changes

The Windows host has no cgo C compiler, so Go's local -race mode could not start. The repository's Linux CI runs the exact required race-enabled non-e2e suite.

Fixes #5

@demolerbi-cmyk

Copy link
Copy Markdown

Summary

  • implement the maintainer-suggested tools/list filtering for X-MCP-Readonly: true
  • enforce the same audited annotation policy at call time, before tool and workspace-scoping handlers, so direct-name calls cannot bypass discovery filtering
  • reject malformed or ambiguous header values and bind the effective access mode to the initialized HTTP MCP session
  • keep absent/false HTTP requests and stdio full-access, while preventing read-only workspace listing from changing compatibility session state
  • validate Postgres queries as a single read-shaped statement before connecting, with the existing PostgreSQL READ ONLY transaction retained as a backstop
  • document API-key and OAuth usage, guarantees, invalid-header behavior, and boundary limitations

The tool annotation audit is the single policy source for both listing and invocation. Registration tests assert the exact named read-only and mutating sets, and unknown tools fail closed.

Mode listed read-only listed mutating read-only call mutating direct call
header absent or false yes yes allowed allowed
header true yes no allowed permission denied before handler

Validation

  • focused access-policy, real streamable HTTP/session, Postgres SQL, workspace, and registration tests
  • go build -v ./...
  • all 44 non-e2e packages with go test
  • go vet ./...
  • golangci-lint 2.13.2 with --new-from-rev upstream/main: 0 issues.
  • go generate ./... completed; its unrelated pre-existing fake-output drift was excluded from this PR
  • git diff --check
  • no go.mod, go.sum, or generated-fake changes

The Windows host has no cgo C compiler, so Go's local -race mode could not start. The repository's Linux CI runs the exact required race-enabled non-e2e suite.

Fixes #5

1 similar comment
@demolerbi-cmyk

Copy link
Copy Markdown

Summary

  • implement the maintainer-suggested tools/list filtering for X-MCP-Readonly: true
  • enforce the same audited annotation policy at call time, before tool and workspace-scoping handlers, so direct-name calls cannot bypass discovery filtering
  • reject malformed or ambiguous header values and bind the effective access mode to the initialized HTTP MCP session
  • keep absent/false HTTP requests and stdio full-access, while preventing read-only workspace listing from changing compatibility session state
  • validate Postgres queries as a single read-shaped statement before connecting, with the existing PostgreSQL READ ONLY transaction retained as a backstop
  • document API-key and OAuth usage, guarantees, invalid-header behavior, and boundary limitations

The tool annotation audit is the single policy source for both listing and invocation. Registration tests assert the exact named read-only and mutating sets, and unknown tools fail closed.

Mode listed read-only listed mutating read-only call mutating direct call
header absent or false yes yes allowed allowed
header true yes no allowed permission denied before handler

Validation

  • focused access-policy, real streamable HTTP/session, Postgres SQL, workspace, and registration tests
  • go build -v ./...
  • all 44 non-e2e packages with go test
  • go vet ./...
  • golangci-lint 2.13.2 with --new-from-rev upstream/main: 0 issues.
  • go generate ./... completed; its unrelated pre-existing fake-output drift was excluded from this PR
  • git diff --check
  • no go.mod, go.sum, or generated-fake changes

The Windows host has no cgo C compiler, so Go's local -race mode could not start. The repository's Linux CI runs the exact required race-enabled non-e2e suite.

Fixes #5

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.

💡 [FEATURE REQUEST] Add to MCP configuration to pull in READ-ONLY toolsets

2 participants