Skip to main content
Claude Desktop on third-party (3P) supports MCP connectors, skills, and plugins, as standard Claude Desktop does. The key difference is that administrators provision them through managed configuration and the filesystem. There are three layers, in order of precedence: Admins can disable the user layer entirely; see Controlling user extensions.

Managed MCP servers (admin)

Use the managedMcpServers configuration key to deploy MCP servers (remote HTTP/SSE or local stdio command) to every device. These appear in the user’s connector list automatically, can’t be removed by the user, and support per-tool policy locks (allow / ask / blocked). The same key also activates the servers bundled inside the app (Microsoft 365, web search, and GitHub); see Built-in connectors. The Connectors section of the in-app configuration window provides a form for each server: name, per-tool policy, headers or a headers helper script, transport, and URL.
In-app configuration window showing a managed MCP server named sentry, with fields for name, tool policy, headers, headers helper script, Streamable HTTP transport, and URL.

A managed MCP server in the Connectors section of the in-app configuration window.

In the exported configuration, each server is one entry in the managedMcpServers array:
Every entry other than a built-in server needs a transport. Claude Desktop doesn’t read type, the field name that a plugin’s .mcp.json file uses. It drops an entry that has no transport and reports a configuration error.
See the managedMcpServers schema in the configuration reference for every field, including static headers, OAuth, and the headers-helper executable for short-lived tokens. In the in-app configuration window, each server you add under Connectors has a Test this connection button that runs a live MCP initialize and tools/list against the server using the headers or OAuth settings you’ve entered, then shows the round-trip latency, the discovered tool list, or the error returned. Use it to validate reachability and credentials before exporting the configuration. Give each remote server’s final address in url. Claude Desktop doesn’t follow HTTP redirects from a managed MCP server, or from its OAuth authorization server when it fetches that server’s metadata or requests and refreshes tokens. If one of these requests is answered with a redirect, for example to the same address with a trailing slash, the server doesn’t connect, and Test this connection reports that redirects are not followed. Remote servers that organization plugins declare in .mcp.json follow the same redirect rule.

OAuth sign-in

For a remote server entry with oauth set, Claude Desktop signs each user in through the system browser and receives the authorization code on a fixed loopback address. Register this redirect URI on the OAuth client, or allow it on your authorization server if clients register themselves:
The value is the same on every device and for every delivery method (device management, a local configuration file, or a bootstrap server). On identity providers that accept any port on a loopback redirect URI (the RFC 8252 native-app pattern), a registration of http://127.0.0.1/callback also matches. With "oauth": true, Claude Desktop registers its own public client through dynamic client registration and lists this URI as the client’s only redirect URI, so the authorization server must offer a registration endpoint and accept an http loopback redirect URI. With a client you registered yourself, set oauth.clientId and add the URI to that registration. If the registration uses localhost or another port, set oauth.callbackHost or oauth.callbackPort to match; both require clientId. Set up sign-in for managed MCP servers explains how to tell which servers need a client that you register, when a client secret is also required, and where the secret goes. An http or sse entry with no oauth, no headersHelper, and no Authorization header is treated as "oauth": true when its server asks for authentication (Claude Desktop 1.24012.0 or later). This redirect URI applies to MCP server sign-in only. Gateway single sign-on and bootstrap sign-in register their own loopback redirect URI.

How OAuth sign-in works

Claude Desktop starts sign-in only when the MCP server answers an unauthenticated request with HTTP 401. A redirect to a web sign-in page does not start sign-in, so a gateway in front of the server must answer an unauthenticated MCP request with 401 rather than 302. At launch, Claude Desktop connects to every managed server without user interaction. A server with a stored token connects silently, and Claude Desktop refreshes the token first when it is near expiry. A server with no usable token appears under Customize → Connectors with a button that starts sign-in, and no browser window opens until the user selects it. When the user selects Connect or Reconnect, Claude Desktop:
  1. Starts a temporary HTTP listener on 127.0.0.1:53280 (or the host and port set in oauth.callbackHost and oauth.callbackPort). If another process holds the port, sign-in fails with a port-in-use error.
  2. Reads the 401 response. The resource_metadata URL in its WWW-Authenticate: Bearer header, or else the server’s /.well-known/oauth-protected-resource URL, leads to the protected-resource metadata. Its resource must be the server URL or a parent path on the same origin, and Claude Desktop uses the first authorization server it lists, or looks for authorization-server metadata on the MCP server’s own origin when the server publishes no protected-resource metadata. Claude Desktop then fetches the authorization server’s metadata from /.well-known/oauth-authorization-server or /.well-known/openid-configuration, and the issuer in the metadata must match the authorization server URL, or sign-in stops.
  3. Uses the client from oauth.clientId, or registers one at the authorization server’s registration endpoint and stores it for later sign-ins.
  4. Opens the authorization URL in the system browser with a PKCE (S256) challenge and a state value. The authorization endpoint must use https. When the server publishes protected-resource metadata, Claude Desktop sends the metadata’s resource value as the OAuth resource parameter on this request. With oauth.omitResource set to true, Claude Desktop sends no resource parameter for that server.
  5. Waits up to 120 seconds for the browser to return to the redirect URI, exchanges the code at the token endpoint, and connects to the server with the resulting access token. The listener closes when sign-in completes or fails.
To name the authorization server yourself instead of discovering it, set oauth.clientId together with one of oauth.tenantId plus oauth.scope (Microsoft Entra ID), a single oauth.authorizationServer entry (the issuer URL exactly as that server’s metadata states it), or oauth.authorizationUrl and oauth.tokenUrl for a provider that serves no discovery document. With several oauth.authorizationServer entries, discovery runs as in step 2 and the discovered authorization server must match one of them. Tokens are stored on the device, encrypted with the operating system’s secure storage (see Credentials), and refreshed in the background before they expire. When the authorization request carries a scope and the authorization server’s metadata lists offline_access, Claude Desktop adds offline_access so that a refresh token is issued; see oauth.scope and oauth.appendOfflineAccess in the configuration reference to change the requested scopes. If the authorization server rejects the refresh token or the client, for example with invalid_grant or invalid_client, the MCP server’s row under Customize → Connectors shows a Reconnect button. The row also shows Reconnect when a sign-in through the system browser returned no refresh token and the access token expires. The user selects Reconnect to sign in again. For a refresh that the authorization server refuses with another error while the server is connected, see Troubleshoot sign-in. A stored sign-in belongs to the server’s url, so after you change the url, each user signs in again. If sign-in fails (the callback does not arrive within 120 seconds, the authorization server rejects the registration, or the identity provider completes sign-in from a host other than the authorization endpoint’s), the user sees a connection error, the server keeps its Connect or Reconnect button, and main.log in the logs directory records the reason. When the authorization server rejects the redirect URI at its authorization endpoint, main.log records the callback timeout. For an identity provider that completes sign-in from a different host than its authorization endpoint, list that host in oauth.additionalRedirectReferrerHosts. The log names the rejected host. For a server whose OAuth sign-in goes to Microsoft Entra ID, you can run that sign-in through the OS identity broker instead of the system browser by setting authFlow to broker inside the entry’s oauth object, alongside tenantId, clientId, and scope. On a device where the broker is unavailable, sign-in for that server falls back to the system browser, so keep the loopback redirect URI registered as well if any devices lack the broker.

Short-lived credentials with a headers helper

For short-lived header credentials, configure the helper per server: The helper follows the inferenceCredentialHelper execution model, with these differences: a 30-second time limit, no CLAUDE_HELPER_CONTEXT, and no prompting for input. The helper applies only to servers provisioned through managed configuration and never replaces the Authorization header on oauth entries. While the connection is open, the TTL schedule triggers renewal, and a request that the server rejects with HTTP 401 or 403 also re-runs the helper and, when it returns new headers, is retried once with them (Claude Desktop 1.46388.1 or later). A failed helper run does not interrupt the connection; Claude Desktop keeps the current headers and retries on its schedule. A failure while the server is connecting shows the server as needing authentication.
Mid-session renewal requires Claude Desktop 1.21459.0 or later. Earlier versions run the helper only when the server connects.

Supported MCP servers

Any MCP server reachable from the user’s device over HTTPS works with Claude Desktop on 3P, including public servers from third parties and internal servers you build and host (including on internal gateways).
Claude Desktop does not present a TLS client certificate when connecting to MCP servers, so a server that requires mutual TLS (mTLS) client-certificate authentication fails to connect. Terminate the client-certificate requirement before the MCP endpoint (for example, at a gateway or reverse proxy), and authenticate the connection with headers or OAuth instead.
The Claude connector directory is the canonical catalog of vetted servers. Every connector in the directory that is not labeled “Made by Anthropic” is accessible in Claude Desktop on 3P and can be deployed via managedMcpServers or installed by users. Connectors labeled “Made by Anthropic” are hosted on Anthropic infrastructure and are available only in standard Claude Desktop.
Some connectors return MCP Apps, interactive widgets that Claude Desktop renders in place of a plain-text tool result. Each widget loads in a sandboxed iframe on *.claudemcpcontent.com, and setting disableNonessentialServices to true blocks that origin, so the conversation shows an error in place of the widget. The same key also blocks artifact previews and connector favicons. To keep MCP Apps rendering, leave disableNonessentialServices unset or false, and allow the widget hosts listed under Required egress paths at your perimeter firewall.

Productivity suites

Google Workspace and Microsoft 365 each have a dedicated setup path:

Google Workspace

Gmail, Calendar, Drive, Docs, and more via Google’s own Workspace MCP servers. See Google’s setup guide to get started.

Microsoft 365

Outlook, OneDrive, SharePoint, and Teams. Requires registering an app in your Entra tenant. The remote connector also needs an Anthropic allowlist step.

Plugin marketplaces (admin)

A plugin marketplace is a catalog file (marketplace.json) that lists one or more Claude plugins. You host it either as a git repository or as a plain file over HTTPS. Claude Desktop fetches it on each device, lists its plugins under Customize → Plugins → Discover → Organization in both Cowork and Code, and keeps them in sync with the revision you pin. You control which plugins are available, which install automatically, and which are required. This is the recommended way to distribute organization plugins. Claude Desktop clones a git-hosted marketplace with the git already installed on each device, so include git in your device baseline: Git for Windows on Windows, the Xcode Command Line Tools on macOS, and the git package on Linux. Devices without git can use a marketplace hosted over HTTPS instead. On macOS and Windows, use the system-wide directory path when end-user devices cannot reach a git server or an HTTPS file host.

Create the marketplace repository

A marketplace repository contains a .claude-plugin/marketplace.json file at its root that lists each plugin and its location. The format is shared with Claude Code; see Create and distribute a plugin marketplace for the full schema and walkthrough.
.claude-plugin/marketplace.json
Put plugin content directly in the marketplace repository with a relative source path. When Claude Desktop syncs the marketplace, it doesn’t fetch or auto-install a plugin whose source points at a different repository, and that plugin doesn’t appear in the Organization tab. Git submodules aren’t fetched either, so a plugin folder added as a submodule is empty on users’ devices. The marketplace name must match ^[A-Za-z0-9]([A-Za-z0-9._-]{0,126}[A-Za-z0-9_-])?$ and must not be one of the reserved values unknown, org, or org-provisioned.

Host the marketplace over HTTPS instead of git

When end-user devices do not have git available, or when you already run an internal web server, artifact repository, or object store, you can serve the marketplace as static files over HTTPS instead. Claude Desktop downloads the manifest and each plugin archive itself, so the endpoint has no git dependency, and the fetch goes through the same proxy and TLS path as the rest of the app. Serve a marketplace.json file at any HTTPS path and package each plugin as a zip archive on the same origin as the manifest:
marketplace.json
Then add a "source": "url" entry to allowedPluginMarketplaces whose url points at this manifest (see the field table below). Claude Desktop verifies each archive’s sha256 before unpacking it. The zip must contain the plugin at its root (a single wrapping folder is tolerated), including .claude-plugin/plugin.json. Archive URLs must share the manifest’s origin. That origin is the only host you need to allow through your perimeter firewall, and the only host the marketplace credential is sent to. When Claude Desktop syncs the marketplace, it doesn’t fetch a plugin with a source kind other than archive, or with an archive on a different origin. That plugin doesn’t appear in the Organization tab. If any archive in a fetch fails to download or fails its digest check, Claude Desktop installs nothing from that fetch and retries on the next sync.

Configure the marketplace

If your organization is managed from the Enterprise Admin Console, add marketplaces on the Plugins page under Desktop 3P in the console. Otherwise, you can add marketplaces directly in the in-app configuration window: in the Plugins section, click Add marketplace and choose Blank, GitHub repo, Git URL, or Marketplace URL. The form validates the entry and exports the encoded JSON for you.
In-app configuration window Plugins section showing the plugin marketplaces card with an open Add marketplace menu offering Blank, GitHub repo, and Git URL, above the organization plugins folder path with two loaded plugins.

The Plugins section of the in-app configuration window, with the Add marketplace menu and the organization plugins folder.

To write the configuration yourself instead, add the repository to the allowedPluginMarketplaces configuration key. In an MDM profile the value is a JSON array encoded as a string (see Value types). In a local configuration file or a bootstrap server response, the value is a native JSON array.
.mobileconfig (macOS)
On Windows, write the same string to the allowedPluginMarketplaces value in the registry policy key your deployment already uses (HKLM\SOFTWARE\Policies\Claude for machine policy). Keep the value in the same hive as the rest of your configuration: when machine policy is present, the app ignores user policy entirely; see Deploy the configuration for the exact rule. For GitLab, Bitbucket, or a self-hosted git server, use "source": "git" with a full HTTPS url instead of repo. For a marketplace hosted over HTTPS without git, use "source": "url" with url pointing at the marketplace.json file:
You can configure multiple marketplaces, and each appears as its own sub-tab under Organization in the Directory. If an admin-configured marketplace has the same repo, url, or manifest name as one the user added themselves, the admin entry replaces the user’s.

Marketplace installation preferences

"auto_install" and "required" marketplaces must carry an admin-side content pin so the exact plugin content deployed to every device is deterministic and auditable. Git sources must set ref to a full 40-character commit SHA; Claude Desktop refuses to auto-install from a branch or tag name. url sources must set manifestSha256 to the SHA-256 of the exact marketplace.json bytes and give every archive a sha256; Claude Desktop refuses a served manifest with a different digest and skips unpinned archives.

Per-plugin auto-install from a trusted origin

A url marketplace served from your deployment’s own inference gateway origin (inferenceGatewayBaseUrl) or bootstrap server origin (bootstrapUrl) can mark individual plugins for automatic installation inside marketplace.json itself, without a manifestSha256 pin in configuration. Leave the entry’s installationPreference at "available" and set installationPreference on each plugin you want installed automatically:
marketplace.json
Each plugin marked this way still needs a sha256 on its archive. Claude Desktop re-fetches the manifest periodically and picks up a newly published version without a configuration change or an app relaunch. A plugin the user removes stays removed. Claude Desktop honors these per-plugin marks only when the manifest is served from your inference gateway’s or bootstrap server’s own origin, because those hosts already carry your deployment’s configuration and credentials. On any other origin the marks are ignored, and the marketplace behaves as "available". Entry-level "auto_install" and "required" continue to require the admin-side content pin on every origin.

Marketplace credentials

Claude Desktop fetches marketplaces on the host operating system, outside the Cowork VM. The credential is used only for this fetch and is never passed into the VM or exposed to the model. Because the fetch happens on the host, the marketplace host does not need to be on the coworkEgressAllowedHosts allowlist. It does need to be reachable from end-user devices.

Roll out marketplace updates

For a git marketplace, commit the change to the repository, update the ref in allowedPluginMarketplaces to the new commit SHA, and distribute the updated managed configuration. For a url marketplace with a manifestSha256 pin, publish the new archive, update its url and sha256 in marketplace.json, and update manifestSha256 in configuration to the new file’s digest. For a url marketplace using per-plugin auto-install from a trusted origin, publish the new marketplace.json and no configuration change is needed. Devices sync to the new revision on the next app launch or periodic re-fetch. To remove a marketplace, delete its entry; Claude Desktop unregisters it and uninstalls its plugins on the next sync.

Move a marketplace to a new source

Claude Desktop identifies a marketplace entry by its repo or url and its path. If you change one of them, for example to move from a git repository to a hosted marketplace.json, Claude Desktop treats the change as removing one marketplace and adding another, and uninstalls the plugins users installed from the old one. Changing ref is an update, not a move. To keep users’ installed plugins, first make sure that every device is running Claude Desktop 3.6556.1 or later. Earlier versions uninstall them when they sync the move. Then make the move:
1

Keep the marketplace and plugin names

In the new source’s marketplace.json, keep the marketplace name and the plugin names the same as in the old source. Point each plugin’s source at content in the new marketplace’s own repository or archives. Claude Desktop doesn’t carry over any other plugin, such as one whose code comes from another repository.
2

Swap the entries in one configuration change

In allowedPluginMarketplaces, delete the old entry and add the new entry with expectedName set to that name. The value must match exactly, including letter case. If you delete the old entry in an earlier change, devices that sync in between uninstall the plugins.
3

Leave expectedName set on the new entry

Without expectedName, a device that hasn’t yet loaded the new source removes the old marketplace and uninstalls its plugins.
On the next sync, the plugins users installed from the marketplace’s own repository or archives stay installed and load from the new source. Under "available", each one stays on or off as the user left it. The new entry’s installationPreference applies as it does for a new marketplace. Move a marketplace this way when the new source holds the same marketplace. At their next session, users’ installed plugins run what the new source lists under those names.
  • If a device can’t fetch the new source or rejects it: the plugins from that marketplace stop loading on that device, and the Directory shows the reason in a banner on the Organization tab. That device also uninstalls a plugin that a Claude Desktop version before 1.44121.0 installed outside the marketplace’s folder on the device. Once the device loads the new source, the plugins that stayed installed load again without a reinstall, and the user can install the uninstalled plugin again if the Organization tab lists it.
  • If Claude Desktop drops an entry in allowedPluginMarketplaces as invalid: the move waits, and the plugins already installed from the old source keep loading, until you fix the entry.

Organization plugins (admin)

For most deployments, distribute organization plugins via a plugin marketplace instead. Marketplaces let you manage plugin content in git or on any HTTPS file host and roll out updates by changing a single configuration value, rather than pushing files to every device. Use the directory path below when end-user devices cannot reach a git server or an HTTPS file host.
Plugins bundle MCP connectors, skills, slash commands, hooks, and sub-agents into a single directory. On this path, admins distribute plugins by placing them in a system-wide directory on each device, typically via the same MDM or software-distribution channel used for the app itself. Plugins distributed this way are available in Cowork sessions and Chat conversations. Code sessions do not load their skills, commands, sub-agents, or hooks, so a plugin that must reach Code sessions has to be distributed through a plugin marketplace instead.

Plugin directory location

On Windows, the directory is under Program Files (not ProgramData) so that only administrators can create or modify it. Claude Desktop treats the presence of this directory as an admin-provisioned source. On Linux, Claude Desktop reads no organization plugins directory, so use a plugin marketplace there.

Plugin structure

Each subdirectory of org-plugins/ is one plugin. The directory name is the plugin’s canonical name.
Each entry in org-plugins/ must carry a valid manifest: a .claude-plugin/plugin.json, or a top-level SKILL.md whose frontmatter declares agents or mcpServers (a skill folder that also acts as a plugin). A plain skill folder does not qualify on its own. To distribute a single skill, place it under skills/<name>/SKILL.md in a plugin that has a plugin.json. A directory with no valid manifest is not loaded and never appears in the user’s plugin browser. The diagnostic report’s plugin section shows the rejected entry and why. To distribute an MCP connector, declare it in a plugin’s .mcp.json or use managedMcpServers. Claude Desktop doesn’t start a desktop extension (.mcpb) that a plugin in org-plugins/ names in its manifest.
See the plugins reference for the full file format of each component, including the hooks schema.
Symlinks inside a plugin are followed as long as the target resolves to a path inside the plugin directory. Symlinks that point outside the plugin (for example, skills/foo/SKILL.md → /etc/hosts) are skipped. A symlinked top-level plugin directory (for example, org-plugins/my-plugin → /opt/shared/my-plugin) is also followed.
MCP servers declared in a plugin’s .mcp.json don’t carry a toolPolicy field in the plugin file itself. To lock tools on a plugin-delivered server, set orgPluginSettings in managed configuration, keyed on the server’s name, or add a managedMcpServers entry with "transport": "policy-only", the server’s name, and a toolPolicy. A policy-only entry connects to nothing. It only applies the permissions, and it takes precedence over orgPluginSettings for that server. A regular managedMcpServers entry that matches a plugin’s server by URL or name governs that server the same way.

Auto-installing organization plugins

By default, organization plugins appear in the user’s plugin browser as available to install, and each user opts in. To install a plugin automatically for every user, set installationPreference in the plugin’s .claude-plugin/plugin.json:
This mirrors the installation preference behavior of remote-managed plugins on claude.ai. Changing a plugin’s installationPreference takes effect at each user’s next sync.

Updating organization plugins

To roll out a new version of a plugin:
  1. Update the plugin contents in org-plugins/<name>/ via your software-distribution tool
  2. Bump the version string in version.json
  3. Users pick up the change on their next app launch
To withdraw a plugin, remove its folder from org-plugins/. On Claude Desktop 1.46388.1 or later, each user’s installed copy is unregistered the next time the app syncs organization plugins (at launch or when a session starts); earlier versions leave the copy installed.

Remote MCP servers in plugins

A plugin can declare remote MCP servers in its .mcp.json file, and Claude Desktop shows each one as a connector. For a plugin from one of your marketplaces, or a plugin that a user added, Claude can use these servers in the Cowork sessions, Chat conversations, and Code sessions that load the plugin. If a server requires OAuth sign-in, Claude can use it in Cowork sessions, Chat conversations, and local Code sessions after the user signs in. The user signs in once for Code and once for the rest of Claude Desktop. In a marketplace plugin or a user-added plugin, a remote server’s entry in .mcp.json needs no oauth field, even if the server requires OAuth sign-in. If you set oauth, make it an object, as MCP in Claude Code shows, and not true as in the org-plugins/ directory. Claude Code skips a server whose entry says "oauth": true. If users sign in outside Code, leave clientId out of the object, as described under Requirements for plugin server sign-in outside Code. Claude Code reads this .mcp.json file and skips a server whose entry it can’t read. Claude Desktop still shows a skipped server as a connector, but Claude can’t use it. To check a plugin’s entries before you publish it, run claude plugin validate <plugin-folder> in your terminal, on the plugin’s own folder and not the marketplace’s folder. The command checks the entries against the version of Claude Code that runs it, so it can pass an entry that the version in your users’ Claude Desktop skips.
For the servers of plugins in the org-plugins/ directory, see Organization plugins.

Sign in to a plugin server

Users sign in to a server of a marketplace plugin or a user-added plugin separately for Code and for the rest of Claude Desktop:
  • Outside Code: anywhere in Claude Desktop other than Code, the user goes to Customize > Connectors, selects the server’s row, clicks Connect in the panel that opens, and completes the sign-in in the browser. One sign-in covers the user’s Cowork sessions, Chat conversations, and scheduled Cowork tasks on the device, and it works where Code is turned off. A scheduled task’s run asks for approval of the server’s tools as it does for other MCP tools that have no toolPolicy entry, as described under Tool approvals on scheduled tasks. This sign-in has requirements.
  • In Code: with Code open, the user goes to Customize > Plugins, selects the plugin, selects the server on the plugin’s Connectors tab, clicks Connect, and completes the sign-in in the browser. The sign-in covers local Code sessions. While no local Code session that loads the plugin is running, the Connect button in Code is unavailable, and its tooltip reads “Open a Code session that loads this connector, then try again”.
Outside Code, the server’s row reads “Check status” until Claude Desktop knows whether the user is signed in, and then “Connected” or “Not connected”. Claude Desktop checks the sign-in when the user selects the row and the panel opens. Outside Code, the server’s rows under Settings > Connectors, under Connectors in the message box’s + menu, and on the plugin’s Connectors tab under Customize > Plugins open the same panel. In a Cowork session or Chat conversation that needs the sign-in, a banner that reads “Claude can’t access <server-name>.” can also offer Connect. To end a sign-in made outside Code, the user goes to Customize > Connectors, selects the server’s row, and clicks Disconnect. Claude Desktop removes the sign-in from the device and revokes it at the authorization server, if that server supports revocation.

Requirements for plugin server sign-in outside Code

A sign-in outside Code has these main requirements:
  • Claude Desktop opens claude:// links: the browser returns the user to Claude Desktop through a claude:// link. Leave disableDeepLinkRegistration unset or false, and let users’ browsers open claude:// links.
  • The plugin doesn’t name its own OAuth client: the server’s entry in .mcp.json doesn’t set oauth.clientId
  • The server’s entry uses no placeholder or headers helper: its url and headers hold no ${…} placeholder, and it doesn’t set headersHelper
  • The authorization server supports dynamic client registration: it offers a registration endpoint and accepts the redirect URI claude://claude.ai/mcp-auth-callback/sdk. The registration request names the client Claude Code (plugin:<plugin-name>:<server-name>), where <plugin-name> is the name in the plugin’s .claude-plugin/plugin.json file and <server-name> is the server’s key in .mcp.json. If the authorization server registers only the client names that you list, add this name for each plugin server. Each user’s first sign-in on a device registers a new client, and so does a sign-in after the user clicks Disconnect, so allow for any limit that the authorization server sets on registrations, for example per network address and hour.
When a requirement isn’t met, users see one of the following:
  • No Connect button: if disableDeepLinkRegistration is true, the operating system doesn’t open claude:// links with Claude Desktop, the plugin names its own OAuth client, or Claude Code skipped the server’s entry, the server’s panel reads “Claude can’t sign you in to <server-name> through this plugin. Ask your admin to set it up as a managed connector.” A managed connector is a managedMcpServers entry that replaces the plugin server. If either list of plugin servers is set, a skipped entry’s panel reads the message for a blocked server instead.
  • No Connect button, because of a placeholder or headers helper: the panel reads “This connector is provided by a plugin, and you cannot sign in to it here. If it needs no sign-in, it connects by itself. If Claude cannot use it, ask your admin to set it up as a managed connector.”
  • A message after Connect: if the authorization server offers no registration endpoint, or refuses the redirect URI when Claude Desktop registers, the panel offers Connect. After the click, the panel reads “Claude couldn’t sign you in to <server-name> through this plugin. Ask your admin to set it up as a managed connector.”
  • A time-out: if the claude:// link doesn’t reach Claude Desktop within five minutes, a message reads “Sign-in for <server-name> timed out. Select Connect to try again.”
A sign-in times out when something keeps the claude:// link from reaching Claude Desktop, for example:
  • The browser doesn’t open the link: a browser policy blocks claude:// links, or the user doesn’t let the browser open Claude Desktop. In Microsoft Edge, for example, a URLBlocklist entry such as claude://* blocks the link, and an AutoLaunchProtocolsFromOrigins entry with the protocol claude and the authorization server’s origin opens it without asking the user.
  • The consent page’s Content Security Policy blocks the link: the consent page is the authorization server’s page where the user approves the sign-in. If it carries a Content-Security-Policy header whose form-action directive doesn’t allow claude:, Google Chrome, for example, blocks the link and shows no error on the page. Check any such header that a proxy or web application firewall in front of the authorization server sets.

Replace a plugin server with a managed MCP server

If a managedMcpServers entry has the same url or the same name as a plugin server, Claude Desktop uses your entry in place of the plugin server. Give the entry a transport, the plugin server’s url, and oauth, as described under OAuth sign-in. Each user clicks Connect next to your entry’s name under Customize > Connectors and signs in once for Cowork sessions, Chat conversations, and Code sessions.

Limit plugin servers and end sign-ins

To choose which plugin servers sessions connect to, set allowedPluginMcpServers or deniedPluginMcpServers. Users can’t sign in to a server that these lists keep out. Outside Code, its row keeps reading “Check status”, and its panel reads “Your organization may have blocked <server-name>. Ask your admin to allow it.” While either list is set, the panel reads the same for a server whose entry Claude Code skipped, so also check the entry as described under Remote MCP servers in plugins. The lists and the sign-ins have these limits:
  • OAuth hosts aren’t checked: both lists match the server’s own url. They don’t check the hosts that sign-in, registration, and token requests go to. The server’s OAuth metadata or the plugin’s .mcp.json file can point those requests at other hosts.
  • Blocking doesn’t revoke: after you block a server or remove its plugin, new sessions don’t connect to it. Sign-ins that users already made stay on their devices and aren’t revoked. To end them, revoke them at the server’s authorization server.
  • Storage: on Windows and Linux, sign-ins to plugin servers are stored in a file that isn’t encrypted. For where sign-ins to plugin servers and to managed MCP servers are stored, see Credentials.

Plugin hooks

Hooks bundled in a plugin, under hooks/ or declared in its manifest, run wherever the plugin itself loads:
  • Cowork sessions run hooks from marketplace plugins, from plugins in the org-plugins/ directory, and from plugins users add themselves.
  • Code sessions run hooks from marketplace plugins (Claude Desktop 1.32352.0 or later), from plugins users upload in the app, and from plugins the user installed for Claude Code. Hooks from plugins in the org-plugins/ directory do not run in Code sessions. In remote SSH sessions, hooks from the plugins Claude Desktop copies to the host do not run.
  • Chat conversations run hooks from the same plugins as Cowork sessions, on Claude Desktop 1.52386.0 or later.
With Unified Claude on, a Cowork session started from the home screen runs the same hooks as any other Cowork session. A UserPromptSubmit hook that blocks a prompt stops that turn and shows the hook’s reason to the user. When a conversation or session is created, Claude Desktop also sends its first message to your inference provider in a separate request, without tools, to generate the title shown in the sidebar. That request does not pass through plugin hooks, so a first message that a hook blocks still reaches your provider for titling. Claude Code managed settings deployed on the device govern these hooks as they do in the Claude Code CLI: disableAllHooks turns them off, and allowManagedHooksOnly keeps only the hooks those managed settings define.
To stop users’ own mods from loading, see Claude Code mods.

Hooks on Windows devices without Git for Windows

On a Windows device without Git for Windows, Claude Code runs a hook in PowerShell when the hook sets neither shell nor args. Hooks in Cowork sessions run the same way, on the device itself and not in Cowork’s virtual machine. PowerShell rejects some commands that Bash accepts, such as a quoted program path followed by arguments. When a hook blocks tool calls on such a device, check for each of these causes:
  • The hook fails or times out: a PreToolUse or PermissionRequest command hook that has no args and no onFailure field blocks the tool call when it fails or times out, whatever shell it sets. A hook written for Bash that was given "shell": "powershell" can block every tool call that it matches. Under Windows PowerShell 5.1, which Claude Code uses when PowerShell 7 isn’t installed, a program that writes to stderr under a redirect in the command, such as 2>&1, can make the hook count as failed although the program exits with 0. The hook error that Claude receives includes blocking because Git Bash wasn't found.
  • The command is only a quoted path: a PreToolUse or PermissionRequest hook that sets none of shell, args, and "onFailure": "continue" blocks the tool call when its command is only a quoted path, as in "command": "\"C:\\path\\guard.exe\"". Bash runs that program, but PowerShell only prints the path. Adding "onFailure": "continue" or "shell": "powershell" stops the blocking, but the program still doesn’t start, so the hook checks nothing.
  • The hook ends with exit code 2: a PreToolUse hook that ends with exit code 2 blocks the tool call whatever onFailure says, and the error that Claude then receives is the hook’s own error output. In PowerShell a PreToolUse hook ends with 2 when its command ends with a program that exits with 2. That reaches a hook written for Bash such as python $CLAUDE_PROJECT_DIR/check.py, even with "onFailure": "continue". A bare $CLAUDE_PROJECT_DIR is empty in PowerShell, so Python can’t open its script and exits with 2.
If your Windows devices have hooks, in a plugin or in Claude Code managed settings, make one of these changes:
  • Keep a hook written for Bash: install Git for Windows in its default location (C:\Program Files\Git) on each device, remove "shell": "powershell" from the hook if it has it, then quit and reopen Claude Desktop
  • Run the hook without a shell: write the hook in exec form, with an executable such as an .exe (not a .cmd or .bat file) in command and its arguments in args ("args": [] if it has none). Write the path without quotes of its own, even when it has spaces, as in "command": "C:\\Program Files\\Guard\\guard.exe". Write the project folder as ${CLAUDE_PROJECT_DIR}, braces included, since no shell expands $CLAUDE_PROJECT_DIR there. A Bash script can’t run this way without Git.
  • Keep a hook written for PowerShell: set "shell": "powershell" and "onFailure": "block" on it, and start its program with the call operator, as in "command": "& \"C:\\path\\guard.exe\"". Make the program the last thing that the command runs, and in a PreToolUse hook have it exit with 0 to let the tool call go on and with 2 to block it. A .ps1 script does so through its own exit 0 or exit 2. A script whose last line runs a program ends with 0 even when that program exits with 2, so start the program from the command and not from inside a script. Don’t redirect the program’s stderr in the command, and don’t end the command with exit $LASTEXITCODE. If the program can’t start, exit $LASTEXITCODE exits with 0 and the tool call goes ahead, even with "onFailure": "block".
After either of the first two changes, a hook that fails or times out lets the tool call go ahead. To stop the tool call instead, add "onFailure": "block" to the hook.

User extensions

Unless restricted by an admin, end users can add their own extensions through the in-app UI:
  • Plugins: install plugins (which can bundle skills, hooks, slash commands, and sub-agents) from the Plugins settings page
  • Skills: create and upload their own skills, including by asking Claude to save one in a conversation
  • Local MCP servers: add local MCP server processes from Settings → Developer
End users cannot add remote MCP servers from the app’s settings or install desktop extension files (.mcpb) themselves. Remote servers come from admin-provisioned managedMcpServers, from organization plugins, and from other plugins that bundle them, including plugins users install (see Remote MCP servers in plugins). A Code session can also load the servers a user defines in Claude Code’s own configuration (~/.claude.json, a project’s .mcp.json, or claude mcp add). To limit plugin-bundled servers, see Controlling user extensions. To limit the servers a Code session loads, see Applied as managed policy. User-added extensions are stored in the user’s local data directory and apply only to that device. A plugin that a user uploads loads in Cowork sessions, Chat conversations, scheduled tasks, and Code sessions, whichever part of the app they upload it from. It doesn’t load where a plugin of the same name from your organization does. One uploaded with an earlier version of Claude Desktop stays where it was until they upload it again. A marketplace that a user adds, and the plugins they install from it, stay in the part of the app where they added it: Code or the rest of Claude Desktop. In the app, when a user installs, uploads, or turns on a plugin that your organization didn’t provide, Claude Desktop first asks whether to continue, because the plugin may run code on the device. The user clicks Continue to go ahead, or Cancel to leave the plugin as it was. Claude Desktop doesn’t save the answer, so it asks at each install, upload, and turn-on, in Cowork and in Code. It doesn’t ask when a session starts, when a plugin updates, or about a plugin that is already on. Plugins from your marketplaces and from the org-plugins/ directory install and turn on without the question.
If a user adds a marketplace themselves and the Add marketplace dialog shows “This marketplace could not be accessed. Private repositories are only supported using the GitHub owner/repo format.”, the marketplace’s host refused access. To give users the plugins of a private marketplace, add it to allowedPluginMarketplaces with a marketplace credential.
Claude Desktop keeps its own record of the skills each user creates or uploads, and it removes a skill folder that the record doesn’t list. To deliver skills to users, use organization plugins or a plugin marketplace.

Controlling user extensions

Admins can restrict or disable each user-extension surface independently via managed configuration: Setting isLocalDevMcpEnabled to false and leaving isDesktopExtensionEnabled at false restricts MCP servers and connectors to those delivered through managedMcpServers and org-plugins/, plus the remote servers that installed plugins bundle, whether from your marketplaces or added by users. In Code sessions, remote servers that users define in Claude Code’s own configuration can also connect; see Applied as managed policy for the keys that stop them. On macOS and Windows, to keep a plugin’s local server running while isLocalDevMcpEnabled is false, deliver the plugin through the organization plugins directory. To limit plugin-bundled servers to ones you name, or to none, set allowedPluginMcpServers to a list of URL patterns. An empty list admits no plugin-bundled server. Setting skillCreationEnabled to false turns off skill creation and upload in the app. Skills already on the device keep working, as do skills from organization plugins. Users can still install plugins from the marketplaces you provision regardless of these settings. Setting userPluginUploadsEnabled to false removes only users’ options for adding plugins themselves. Plugins a user uploaded earlier stay in place. See the Locked down profile for a complete example. Setting userPluginMarketplacesEnabled to false also hides every marketplace already on the device that your organization didn’t provision, whether a user added it in the app earlier or Claude Code registered it from a terminal on the same device. By default, Claude Code adds Anthropic’s official marketplace the first time a user starts an interactive terminal session, so that marketplace is hidden too unless you provision it. The app doesn’t install or update plugins from a hidden marketplace. Plugins already installed from a hidden marketplace stay on the device, but they don’t appear in the app or load in its sessions until you remove the key or set it back to true. userPluginMarketplacesEnabled doesn’t govern Claude Code in a terminal. To limit the marketplaces Claude Code can use in a terminal, set marketplace restrictions in its managed settings. If you deploy Claude Code managed settings to the device, restrict marketplaces there too, because in Code sessions those settings can take precedence over the app’s restrictions.

Skill scanning

Skill scanning is in beta. It’s off until an administrator turns it on.
Skill scanning checks the skills that users create or upload in the app. It looks for signs that a skill is malicious, such as hidden code execution, credential exposure, or data exfiltration. With scanning on, a scan starts each time a user creates, uploads, or edits a skill in the app. The user can’t use the skill until the scan finishes, and the app blocks a skill that fails the scan. Claude Desktop doesn’t scan plugins, or the skills inside them. To stop users from adding their own plugins, see Controlling user extensions. When a skill passes a scan or gets a caution, Claude Desktop changes some characters in the skill’s files, and a skill that depends on those characters can stop working. Before you turn on scanning, see Changes to skill files after a scan.

Turn on skill scanning

In the Enterprise Admin Console:
  1. In Organization settings, go to the Capabilities page under Desktop 3P.
  2. Under General restrictions, turn on the Scan user-created skills switch.
  3. Click Save changes.
The console shows the Scan user-created skills switch only while Allow user-created skills is on. A group’s permission policy has the same switch, and its setting overrides the organization’s setting for that group. You can also turn on scanning in these ways: Changes from the console, a bootstrap server, or MDM take effect at the app’s next configuration check, and users don’t have to relaunch the app.

How a scan works

The app sends the text of the skill’s files to a Claude model on your organization’s own inference provider, using the same credential that the user’s sessions use. How well a scan detects a malicious skill depends on the model that runs it. A scan runs on one model from your organization’s model list. Claude Desktop prefers an Opus model, then a Sonnet model, then a Haiku model, but only among the models it can recognize. It recognizes a model when the model ID contains “opus”, “sonnet”, or “haiku”, or when you set anthropicFamilyTier on that model in inferenceModels. If your model IDs are a gateway’s route names, Claude Desktop may not recognize them, so set anthropicFamilyTier on the model that should run scans. anthropicFamilyTier also makes that model stand in for its tier in Cowork and Code sessions. If Claude Desktop recognizes none of your models, it still runs the scan on another model from the list. Your organization pays its inference provider for scans. A scan’s tokens count toward the user’s token limit. A scan ends in one of these results:
  • Pass: the user can use the skill.
  • Caution: the scan found something that could be unsafe but couldn’t confirm that the skill is unsafe. The user can use the skill.
  • Blocked: the skill failed the scan, and the user can’t use it. The user can correct the skill and upload it again, which starts a new scan.
  • Not reviewed: the scan didn’t produce a result, and the user can’t use the skill. This happens, for example, when the provider returns an error or the user is over their token limit. Once the cause is fixed, the user can click Retry on the skill’s page.
Neither a user nor an administrator can approve a blocked skill. Turning scanning off doesn’t unblock it. But while scanning is off, a user who edits a Blocked or Not reviewed skill, or uploads it again, can use it, because the app no longer scans it. The Enterprise Admin Console doesn’t show scan results. To collect them, set otlpEndpoint. Claude Desktop then sends scan records to your own OpenTelemetry collector as log records whose body is custom3p_skill_scan_completed. It sends no scan records while otlpDesktopLogLevel is off. A scan record identifies the user and includes the skill’s name, the result, and the model’s summary of the skill when there is one. The summary can quote the skill, and Claude Desktop sends it even when content capture is off. To keep the summary out of your backend, drop the scan_summary attribute in the collector.

Changes to skill files after a scan

When a skill gets a Pass or Caution result, Claude Desktop changes some characters in the skill’s text files. The app doesn’t tell the user or the administrator about the changes, and it doesn’t keep a copy of the original files. Turning on scanning doesn’t change the skills that users already saved. Claude Desktop scans such a skill the next time the user saves it, and its files can change then. The model that runs a scan doesn’t see some characters, but a program that runs the file acts on every character. Claude Desktop removes or replaces these characters before it sends the text to the model, and then saves the same text. For the limits of these changes, see What skill scanning doesn’t cover. Claude Desktop makes these changes, and a few similar ones, to a text file: In PowerShell files, such as .ps1 files, curly quotes become straight quotes and a carriage return on its own becomes a line feed. A PowerShell file that contains non-ASCII characters can also gain a byte-order mark and a line break at its start. Claude Desktop doesn’t change file names. It also leaves PNG and JPEG images, PDFs, and dependency lock files such as package-lock.json as they are. A skill that depends on the original characters can stop working after the scan, as in these examples:
  • Windows batch files: cmd.exe can fail to find a goto or call label in a file that has Unix line endings.
  • Signed or hashed files: a changed PowerShell script doesn’t match its signature, and a changed file doesn’t match a stored hash.
  • Mentions of the skill’s own files: if a file name contains a changed character, as in What’s New.md, a mention of the file in SKILL.md changes but the name doesn’t, so the two don’t match.
You can’t exempt a skill from these changes, and turning scanning off doesn’t restore the original files. While scanning is off, a user can upload their own copy of the skill again, and Claude Desktop saves it unchanged. To give users a skill that depends on these characters, deliver it as an organization plugin, through a plugin marketplace or the system-wide directory. Claude Desktop doesn’t scan plugins or change their files, so review the skill’s files yourself first.

What skill scanning doesn’t cover

Besides plugins, the app doesn’t scan these:
  • Skills that users saved before you turned on scanning. The app scans such a skill the next time the user saves it.
  • Changes that someone makes directly to a skill’s files on disk.
  • Files inside a skill that the scan skips because of their file names, for example PNG and JPEG images, PDFs, and dependency lock files such as package-lock.json. The scan doesn’t look at what is in these files, so it doesn’t read a script that is saved as logo.png.
  • Skills that aren’t saved in the app at all, for example Claude Code skills in .claude/skills folders on the device. A Code session in Claude Desktop loads those skills too.
After these changes to a skill’s files, a program can still read a file differently from the model. For example, Windows PowerShell 5.1 and cmd.exe can interpret non-ASCII characters according to the device’s code page. A pass means that the scan found no sign that the skill is malicious. It isn’t a guarantee that the skill is safe.

Code

How extensions and managed settings reach the embedded Claude Code engine.

MCP in Claude Code

Configure MCP servers for the standalone Claude Code CLI.

Claude Code plugins

Plugin structure, marketplaces, and management for Claude Code.

Managed MCP in Claude Code

Restrict which MCP servers Claude Code users can add.