AdministrationSetup and accessManaged MCP servers

Managed MCP servers

MCP servers give the agent new tools: a database client, an issue tracker, an internal API. A local server is a program that runs on the developer’s machine; a remote one is a URL the CLI connects to. Both can reach data and systems you care about, so most organizations want a say in which ones are used.

Syntic Code gives administrators three controls:

ControlKeyWhere it can be set
Provide servers to everyonemcpServersManaged policy (also user and project settings for their own servers)
Block specific serversdeniedMcpServersAny settings file; it can only tighten
Allow only approved serversallowedMcpServersManaged policy only

How to write and connect servers in general is covered in MCP servers. Policy files themselves are covered in Managed settings.

Choose an approach

You wantUse
Everyone gets the same internal servers, and may add their ownmcpServers in policy
People may use anything except a few known-bad serversdeniedMcpServers
People may only use servers you have reviewedallowedMcpServers (plus mcpServers for the ones you provide)
No MCP servers at all"allowedMcpServers": []

Provide servers to everyone

Servers in the policy’s mcpServers block are configured for every user on the machine, the same way a user’s own servers are:

{
  "mcpServers": {
    "tickets": {
      "type": "http",
      "url": "https://mcp.example.com/tickets"
    },
    "docs-search": {
      "command": "/opt/corp/bin/docs-mcp",
      "args": ["--read-only"]
    }
  }
}

Each person still signs in to a remote server with their own account where the server asks for it (syntic mcp login <name>), so you do not need to put shared tokens in the policy.

Block servers

{
  "deniedMcpServers": [
    { "serverName": "scratch" },
    { "serverUrl": "https://*.untrusted.example/*" }
  ]
}

A deny entry from any settings file is honored: managed policy, user, project or local. Deny lists from every file are joined. Because a deny can only make things stricter, a project may add one but can never remove yours.

Allow only approved servers

{
  "allowedMcpServers": [
    { "serverName": "tickets" },
    { "serverCommand": ["/opt/corp/bin/docs-mcp", "--read-only"] },
    { "serverUrl": "https://mcp.example.com/*" }
  ]
}

The allow list is read from managed policy only. A user or project file that contains allowedMcpServers has no effect, so nobody can widen or narrow your list from a repository. If several managed files set it, their entries are joined.

State of the allow listResult
Not setEvery server may connect (deny entries still apply).
Set to []No server may connect.
Has entriesA server must match one of them.

How entries match

Each entry sets exactly one of these fields:

FieldMatchesExample
serverNameThe name the server is configured under. Works for local and remote servers.{ "serverName": "tickets" }
serverCommandA local server whose command and arguments are exactly this list, in order.{ "serverCommand": ["npx", "-y", "@corp/mcp"] }
serverUrlA remote server whose URL matches the pattern. * matches any run of characters, and the whole URL must match.{ "serverUrl": "https://mcp.example.com/*" }

${VAR} and $VAR in serverCommand and serverUrl are filled in from the environment when the policy is read. A variable that is not set becomes empty.

An entry that sets none or more than one field, or has an empty value, is skipped with a warning in the debug log; the rest of the list still applies. A list that is not an array is ignored entirely and does not switch the allow list on.

The order of checks

For each server, at the moment it is about to connect:

  1. If it matches any deny entry, it is blocked.
  2. If there is no allow list, it is allowed.
  3. If the allow list is empty, it is blocked.
  4. If a serverName entry matches, it is allowed.
  5. A local server is allowed only if a serverCommand entry matches. A remote server is allowed only if a serverUrl entry matches.
  6. Otherwise it is blocked.

A name match is the loosest kind, since anyone can name a server tickets. For local servers, prefer serverCommand, which pins the exact program. For remote servers, prefer serverUrl.

Where the check applies

The check runs wherever a server connects, whatever its source:

  • servers from any settings file or .mcp.json
  • servers added by plugins
  • servers passed with --mcp-config, including with --strict-mcp-config
  • reconnects during a session
  • the connection checks syntic mcp list runs

There is no way around it from the command line.

What people see

A blocked server does not connect, and the reason names the rule:

MCP server 'scratch' is blocked by policy (listed in deniedMcpServers)
MCP server 'personal-db' is blocked by policy (not in the managed allowedMcpServers list)
MCP server 'tickets' is blocked by policy (the managed allowedMcpServers list is empty)

Other servers keep working. The session itself starts normally.

Servers that come with a repository

A project can declare servers in .mcp.json or its settings. Connecting one runs whatever command the repository names, so these get extra checks on top of your policy:

  • Nothing project-scoped loads at all until the person has trusted the folder.
  • Even in a trusted folder, each project server must be approved by the person before it first connects. The answer is stored in their own ~/.syntic/mcp_approvals.json, never in the project.
  • An approval is tied to the server’s definition. If the repository later changes the command, the person is asked again.
  • In a non-interactive run (-p) in a trusted folder, project servers are approved automatically. In an untrusted folder they are never loaded.

syntic mcp reset-project-choices forgets a project’s approvals so the next session asks again.

Channels

Some MCP servers can push messages into a running session (channels). These have their own switches in policy:

KeyEffect
"channelsEnabled": falseNo server may push messages.
allowedChannelServersOnly these servers may push, even if a person trusts more with --channels.

Project settings can never open a channel.

Block single tools

To keep a server but stop one of its tools, use a permission rule. The tool name is mcp__<server>__<tool>; a rule naming just mcp__<server> covers the whole server:

{ "permissions": { "deny": ["mcp__tickets__delete_ticket"] } }

See Permission rules.

A complete example

{
  "mcpServers": {
    "tickets": { "type": "http", "url": "https://mcp.example.com/tickets" }
  },
  "allowedMcpServers": [
    { "serverUrl": "https://mcp.example.com/*" },
    { "serverCommand": ["/opt/corp/bin/docs-mcp", "--read-only"] }
  ],
  "deniedMcpServers": [
    { "serverUrl": "https://mcp.example.com/admin/*" }
  ],
  "channelsEnabled": false
}

This provides the ticket server to everyone, allows only servers on your own host plus one approved local program, keeps the admin endpoints off even though they match the allow pattern (deny always wins), and turns channels off.

Check your setup

  1. Run syntic doctor to confirm the policy file is read.
  2. Run syntic mcp list as a regular user. A blocked remote server shows the reason next to its name. Local servers are only started by the check when you ask for it (--check), since that runs their commands.
  3. Try adding a server that should be blocked with syntic mcp add. It is saved, but it will not connect.