Skip to content

Security Examples

Real-world security configurations for common MCP servers.

Postgres — Read-Only with SQL Filtering

yaml
servers:
  postgres:
    command: postgres-mcp
    env:
      DATABASE_URL: "$(secret.pg_url)"
    security:
      mode: custom
      allowed_tools: [query, list_tables, describe_table, list_schemas]
      blocked_tools: [execute, create_database, drop_database]
      policies:
        - name: no-mutations
          match:
            tools: [query]
            content:
              target: args.sql
              deny_pattern: "(?i)\\b(INSERT|UPDATE|DELETE|DROP|TRUNCATE|ALTER|CREATE|GRANT|REVOKE)\\b"
          action: deny
          message: "Mutation queries blocked — this connection is read-only"

        - name: no-sensitive-tables
          match:
            tools: [query]
            content:
              target: args.sql
              deny_pattern: "(?i)\\b(credentials|api_keys|sessions|password_resets)\\b"
          action: deny
          message: "Access to sensitive tables is restricted"

        - name: require-limit
          match:
            tools: [query]
            content:
              target: args.sql
              require_pattern: "(?i)\\bLIMIT\\b"
              when: "(?i)^\\s*SELECT"
          action: warn
          message: "SELECT without LIMIT — consider adding one to avoid full table scans"

Slack — Channel Restrictions

yaml
servers:
  slack:
    command: slack-mcp
    transport: http
    url: "http://localhost:3001"
    security:
      mode: custom
      allowed_tools: [search_messages, list_channels, get_thread, get_channel_info]
      blocked_tools: [delete_message, edit_message]
      policies:
        - name: no-public-channels
          match:
            tools: [send_message]
            args:
              channel:
                deny_pattern: "^#general$|^#announcements$|^#all-hands$"
          action: deny
          message: "Cannot post to public broadcast channels"

        - name: no-dm-spam
          match:
            tools: [send_message]
            args:
              channel:
                deny_pattern: "^@"
          action: warn
          message: "Sending DMs — verify this is intended"

Jira — Read-Only with Project Restrictions

yaml
servers:
  jira:
    command: jira-mcp
    transport: http
    url: "$(secret.jira_url)"
    headers:
      Authorization: "Bearer $(secret.jira_token)"
    security:
      mode: read-only
      allowed_tools: [search_issues, get_issue, list_projects, get_comments]
      policies:
        - name: restrict-projects
          match:
            tools: ["*"]
            args:
              project:
                allow_prefix: ["ENG-", "PLATFORM-", "INFRA-"]
          action: deny
          message: "Access restricted to engineering projects"

GitHub — Controlled Access

yaml
servers:
  github:
    command: github-mcp-server
    env:
      GITHUB_TOKEN: "$(secret.github_token)"
    security:
      mode: custom
      allowed_tools: [search_*, get_*, list_*]
      blocked_tools: [create_*, delete_*, merge_*, close_*]
      policies:
        - name: restrict-repos
          match:
            tools: ["*"]
            args:
              repo:
                allow_prefix: ["acme/", "acme-platform/"]
          action: deny
          message: "Access restricted to acme organization repos"

Serena — Path-Restricted Editing

yaml
servers:
  serena:
    command: serena
    args: [start-mcp-server, --context=claude-code]
    daemon: true
    security:
      mode: editing
      policies:
        - name: source-only
          match:
            tools: ["replace_*", "insert_*", "rename_*"]
            args:
              relative_path:
                allow_prefix: ["src/", "internal/", "cmd/", "pkg/", "./"]
          action: deny
          message: "Writes restricted to source directories"

        - name: no-generated
          match:
            tools: ["replace_*", "insert_*"]
            args:
              relative_path:
                deny_prefix: ["generated/", "dist/", "build/", ".git/"]
          action: deny
          message: "Cannot modify generated or build artifacts"

Filesystem — Directory Sandbox

yaml
servers:
  filesystem:
    command: npx
    args: ["-y", "@modelcontextprotocol/server-filesystem", "$(mcpx.project_root)"]
    security:
      mode: custom
      blocked_tools: [delete_file, move_file]
      policies:
        - name: no-dotfiles
          match:
            tools: ["*"]
            args:
              path:
                deny_pattern: "/\\."
          action: deny
          message: "Cannot access dotfiles or hidden directories"

        - name: no-escape
          match:
            tools: ["*"]
            args:
              path:
                deny_pattern: "\\.\\.\\/|\\.\\.\\\\\\/"
          action: deny
          message: "Cannot escape project directory"

Combined Global + Per-Server

A typical team setup with global protections and server-specific rules:

yaml
security:
  enabled: true
  global:
    audit:
      enabled: true
      log: "$(mcpx.project_root)/.mcpx/audit.jsonl"
      redact: ["$(secret.*)"]
    policies:
      - name: no-path-traversal
        match:
          args:
            "*path*":
              deny_pattern: "\\.\\.\\/|\\.\\.\\\\\\/"
        action: deny
        message: "Path traversal blocked"

servers:
  serena:
    security:
      mode: editing
  postgres:
    security:
      mode: read-only
  jira:
    security:
      mode: read-only
  slack:
    security:
      blocked_tools: [send_message, delete_message]

Released under the MIT License.