How to set permissions for a third-party agent plugin

The package arrives sealed, with the connector exposed. Its manifest has ten fields, and none declares portable permissions.
An electrical installation offers a useful comparison: you buy the appliance, not the circuit breaker. The limit is added afterward, at the wall, by the installer. The same logic applies to an agent plugin, and the decision remains with the installer, not the file that arrived.
On August 6, 2026, the Agent Plugins 1.0.0 specification standardized the package of skills and MCP servers, with a ten-field manifest and no portable permissions. Setting agent plugin permissions requires deciding before installation which components to accept, which credential to use and which scopes to approve.
Key Facts / Decision Matrix: Sealed module, exposed connector: none of the ten fields carries portable permissions [3][4]. Only
$schemaandnameare required;versionis optional [4].allowed-toolsis optional, experimental and has variable support [10]. If the challenge containsscope, it is authoritative; without it, the recommendation is to request all scopes inscopes_supported, if defined [12].
Prerequisites
Before the first step, the team needs administrative access to the client, a list of approved plugins, a credential vault outside the package and a destination for the inventory. Without access to the client, there is no way to check what loaded. Without an inventory, there is no way to prove the decision.
Each Agent Plugins client controls installation and permissions [2]. The list of compatible clients included twelve, compared with five in the announcement [1][8]. That does not measure maturity. Before installing a third-party plugin, the team must identify the client in use and check which components it actually loads. The manifest does not provide a portable permission: the review covers skills, MCP servers and the credentials each server receives [3][4]. In a literal header, the format prohibits secrets, but does not prevent their inclusion in the package [16]. The vault stays outside it. For a remote server, approval also requires examining the issuer and comparing the requested scope with policy [12][13]. The subsequent record distinguishes the acceptance decision from the connection result, since a failure can leave only part of the package active [5]. Discovery and versioning of servers at scale is a separate job.
Six steps, in the order in which the decision happens:
- Check
plugin.jsonas identity. - Separate skills from MCP servers.
- Decide whether to accept each component.
- Decide which credential the server receives.
- Read the scope in the authorization challenge.
- Install and record the active inventory.
Step 1: the manifest is identity, not a permissions contract
plugin.json accepts ten top-level fields, with only $schema and name required. None defines portable permissions. An unknown field and an extensions value that is not an object are reported and ignored, and loading continues; other schema violations are fatal, and the client rejects the plugin without discovering or executing its components [3][4].
The remaining plugin.json manifest fields are version, description, author, homepage, repository, license, keywords and extensions; an absent version does not prevent installation [4]. Name and version identify the package, while extensions can contain client-specific data. The manifest does not define a portable permission. "permissions": [...] at the top level of the plugin manifest is a schema violation: a conforming client reports and ignores the field without assigning it semantics [3][4]. The label binds nobody. The difference is in the outcome: the package continues with an unknown field, but a name of the wrong type causes rejection before discovery. The specification recommends that the client identify the invalid required field [4]. If extensions is not an object, the field is reported and ignored without interrupting loading [3]. Within an implemented namespace, validation follows the client's rules; unimplemented namespaces are ignored [4].
The manifest identifies what was downloaded. It does not answer what the plugin can do.
Step 2: separate the two component types
Version 1 standardizes skills, instructions loaded into context, and MCP servers, local processes or remote connections declared in the plugin's mcp.json. That file defines the server transport and configuration. Commands, hooks and agents are left to each client, outside the portable core [1][3][9]. Acceptance begins with separation.
A plugin skill is not a tool call: it is an instruction loaded into context when its descriptor matches the task [11], discovered in a fixed directory and validated individually [7]. The allowed-tools field is optional, experimental and has variable support [10]. It is not a guaranteed limit. Accepting a third-party skill means accepting untrusted content that can influence the model using tools already granted to the agent. Its effective ceiling is the set of tools exposed by the host: reviewing the skill requires examining instructions and injection risk, not just the schema [7][10][11]. At the top level of mcp.json, only $schema and mcpServers are accepted. Stdio requires type and command, with args, env and cwd optional; remote requires type and url, with optional literal headers. sse is deprecated, with optional support [16]. The command and arguments come from the author.
Hooks can live in the client's extension directory [1][3]. According to VS Code, hooks and MCP servers can execute code on the machine [14].
Step 3: decide agent plugin permissions by component
Agent plugin permissions require a decision per component: an invalid server entry disables only that entry, an invalid skill does not bring down the other types, and an authentication failure is a connection failure [5][6]. There is one exception: an invalid top-level mcp.json disables all of the plugin's MCP [16].
An agent plugin combines surfaces with different limits. For a skill, allowed-tools depends on the client and does not guarantee containment of instructions [10]. For a stdio MCP server, command, args, env and cwd configure process startup, but path containment governs package files: it neither isolates the subprocess nor restricts execution paths [3][9]. For a remote MCP server, the challenge scope guides authorization; if absent, there is fallback to scopes_supported when defined, and the grant can differ from the request [12][13]. The author declares components and configurations; their application depends on the client [2]. An invalid server entry is ignored without bringing down the other components, while an invalid top-level mcp.json disables all of the plugin's MCP [5][6][16]. Selective refusal of a valid entry depends on client options, not a guarantee of the specification. The table separates the limits:
| Surface | Where the declaration lives | What it limits | What it lets through |
|---|---|---|---|
| Skill | SKILL.md, with optional, experimental allowed-tools | Tool list, when applied by the client | Untrusted instructions and tools already exposed by the host |
| Stdio MCP server | mcp.json, with command, args, env and cwd | Initial process configuration | Access after startup; execution paths |
| Remote MCP server | Protected resource metadata and, when present, scope in WWW-Authenticate | Scope requested for authorization | Fallback to scopes_supported; a different grant from the authorization server |
The last column decides. allowed-tools depends on the client [10]. Path containment governs package files, but neither isolates the subprocess nor restricts execution paths [3][9]. On a remote server, the scope may be absent and the grant can differ from the request [12][13]. The author declares components and configurations; their application depends on the client [2].
Least privilege for an agent in production: ephemeral token covers the task; here the unit is the component.
Step 4: decide which credential the server receives
For stdio, the specification recommends obtaining credentials from the environment and not following the MCP authorization flow [12]. An entry whose env declares PLUGIN_ROOT or PLUGIN_DATA is invalid and rejected; in the launched process environment, the client sets those variables last [5][16]. The other variables are worth comparing before acceptance.
Whoever configures the base environment decides the identity only for variables not declared by the package. A collision can change the credential used. The env names and the base environment need to be compared before acceptance. In a valid stdio MCP server, the client applies the package's env over the base environment and sets PLUGIN_ROOT and PLUGIN_DATA last in the launched environment [5][16]. The rule does not make an entry valid if its env declares those reserved names: the client must reject the entry and continue loading the other components [3][5]. A header configured in mcp.json is literal, visible package data and must not contain a credential or secret [16]. The specification prohibits it, but does not technically block it. A token pasted there travels with the package. Authorization for a remote MCP server is optional; when used, OAuth 2.1 with metadata discovery applies, and the client discovers the authorization server through protected resource metadata. The credential is managed on the client side [5][12].
Agent credentials in production: the sprawl problem covers credential inventory.
Step 5: read the scope in the challenge before approval
The server should include scope in WWW-Authenticate, but may omit it: it is a recommendation, not a guarantee. If present, the challenged scope is authoritative for the operation. Without it, the client is advised to request all scopes in scopes_supported, when defined, during initial scope selection [12]. Before the scope, check the issuer.
In remote MCP server authorization, the challenge's scope is authoritative for the operation when present. No relationship is presumed between the challenge scopes and scopes_supported: the challenge can contain scopes outside the list [12]. If neither is available, the guidance is to omit scope from the request [12]. The grant can be smaller [13]. The approver must compare the request and grant with policy, including during fallback. The resource_metadata URL in the challenge and the authorization_servers URLs in the metadata can be controlled by the third-party server [13]. Validating the origin and expected issuer reduces the risk of consenting to someone else's authorization server; the URLs also require protection against SSRF. The client must send resource, with the canonical URI of the intended MCP server, in authorization and token requests, even if the authorization server does not support it [12]. The MCP server must validate the audience and not accept a token issued for another destination.
Token passthrough is prohibited by the MCP security best practices [13]. Forwarding can make auditing harder; compliance with the prohibition depends on the accepted server.
Step 6: install and record the inventory that became active
If a server fails to start, connect, authenticate or complete the handshake, the client continues loading the other servers and components [5], without treating the failure as invalid configuration. Installation can appear complete with a server offline. Recording the result reveals which subset became active.
An agent plugin inventory needs to distinguish configuration, connection and execution. For each component, the record keeps type, origin, credential, connection result, date and client version. An invalid MCP server entry is ignored individually; an authentication failure, in turn, is treated as a connection failure [5][6]. These are distinct outcomes. If the top-level configuration of mcp.json is invalid, all of the plugin's MCP is disabled, although the client continues loading other component types [16]. The specification does not define fallback after the first connection failure [16]. The declared transport and the result of the attempt therefore need to appear in the inventory, rather than being assumed from initial acceptance. The list does not isolate the subprocess [3][9]. After loading, comparing accepted components with those actually active shows what became available to task policy.
The per-operation MCP rate limit is another decision, after the tool loads.
How to verify that the permission in use is the one decided
Three checks close the verification after installation: list loaded components, compare the granted scope with the one decided and record each call with subject, tool and result. The first shows what was left out; the second finds grants larger than policy allowed.
Verifying agent plugin permissions requires inventory, scope and log, in that order. The inventory records type, origin, credential, connection, date and client version; comparison with acceptance reveals components that did not load [5][16]. For a remote MCP server, the challenge scope is authoritative for the operation when present, but the grant can be smaller than the request [12][13]. Checking both sets avoids treating the request as proof of effective permission. The record of each call associates subject, tool and result with the observed activity. None of these checks turns file containment into a sandbox: the specification neither isolates the subprocess nor restricts its execution paths [3][9]. The least-privilege matrix and audit trail are in AI agent permissions and access: define and trace. Task policy is a decision separate from component acceptance.
The package remains valid.
Recurring mistakes
Four mistakes recur: confusing validation with a sandbox, approving the package as a single decision, extending marketplace trust to the plugin and assuming an authentication failure uninstalls the rest. None usually appears in the log as an error, which is why it persists. If you look only for execution failures, all four pass unnoticed.
For an agent plugin, schema and containment describe the package; they do not impose a sandbox [3]. File path validation does not restrict a subprocess's execution paths after it starts [3][9]. A single acceptance also hides the active subset [5][16]. An MCP server connection failure does not roll back the package: the client continues loading other servers and components [5]. An invalid entry and an invalid top-level mcp.json also produce different outcomes, respectively rejection of the entry and disabling the plugin's MCP [5][16]. Trust in the marketplace does not automatically extend to the plugin: VS Code asks for publisher review and trust in the first plugin from each marketplace [14]. Nor does the manifest replace the permission decision: none of the ten top-level fields defines portable authorization [3][4]. For a remote server, approval needs to include issuer and scope, even when the challenge does not provide scope [12][13].
Agent plugin permissions require decisions beyond the label. Model control for AI agents in production covers another layer.
Frequently asked questions
An agent plugin manifest does not offer portable permissions. Data in extensions depends on the client; rejection of an invalid server does not guarantee selective refusal of a valid entry. In remote authorization, the challenge guides the request. Package validation and execution isolation are separate decisions [3][4][5][12].
In the Agent Plugins 1.0.0 specification, extensions can carry data specific to a namespace implemented by the client, but does not establish a portable permission [4]. An invalid MCP server entry is ignored while the remaining components continue loading; the option to selectively refuse a valid entry depends on the client [5][6]. In remote MCP authorization, scope in the challenge is authoritative for the operation. Without scope, the guidance is to request all scopes in scopes_supported, when defined, or omit the parameter if neither is available [12]. The grant can be smaller [13]. A literal mcp.json header must not contain credentials or secrets, although the format does not prevent their inclusion [16]. Path validation protects the reading of package files, but provides no sandbox and does not limit paths accessible to the running subprocess [3][9].
Can the extensions field carry a permission?
It can carry specific permission semantics, but not a portable permission. In extensions, the namespace owner defines the content and its validation; a client implementing that namespace can apply it [4]. The installed client's documentation defines that semantics; namespaces it does not implement are ignored.
Can the skills alone be accepted and the MCP server refused?
Yes, if the client offers selective refusal of a valid server; the specification does not guarantee that option. In a different case, an invalid entry is ignored and the client continues loading the other components, including supported skills [5][6]. But an invalid top-level mcp.json disables MCP for the entire plugin [16].
Who approves the scope when the MCP server belongs to a third party?
The installer, according to the organization's policy. If the challenge contains scope, it is authoritative for the operation, with no presumed relationship to scopes_supported [12]. If it does not, the client is advised to request all advertised scopes when available. The request and issuer need review before approval.
Can mcp.json carry a credential in a header?
It must not. A header configured in mcp.json is visible to the installer and cannot contain a credential or secret [16]. The prohibition is normative, not technical: nothing in the format prevents an author from pasting a token there, and the token travels with the package.
Does package validation isolate what the plugin executes?
No. Validation provides a description of the package, not a boundary for the process [3], and an execution sandbox is the client's choice, which the format does not impose: once the server starts, what it can reach does not depend on the schema.
References and further reading
The primary sources separate plugin format, client behavior and MCP authorization. The Agent Plugins 1.0.0 specification defines the manifest and loading; the MCP specification defines scope selection for remote authorization. Client documentation records specific behaviors that are not portable [3][4][12][14].
The Agent Plugins 1.0.0 specification describes the closed manifest, the two component types and failure boundaries during loading [3]. The manifest reference details extensions and the accepted fields [4]; the MCP servers documentation covers mcp.json, transports and configuration [16]. The Agent Skills specification describes allowed-tools, while the plugin skills page covers discovery [7][10]. MCP authorization establishes priority between the challenge's scope and scopes_supported [12]. MCP security practices address audience, issuer and token passthrough [13]. The VS Code documentation describes publisher review and trust in the first plugin from each marketplace, a rule of that client [14]. The list of compatible clients and the announcement provide the two cited counts without measuring maturity [1][8]. The material also distinguishes connection failures from invalid configurations [5][6].
Sources consulted on November 13, 2026.
- Vercel, Introducing Agent Plugins, published August 6, 2026.
- Agent Plugins, specification homepage.
- Agent Plugins, Specification 1.0.0.
- Agent Plugins, Plugin manifest.
- Agent Plugins, MCP runtime.
- Agent Plugins, Client conformance checklist.
- Agent Plugins, Skills.
- Agent Plugins, Compatible Clients.
- Specification repository, agent-plugins-spec.
- Agent Skills, Specification.
- Agent Skills, homepage.
- Model Context Protocol, Specification 2026-07-28, Authorization.
- Model Context Protocol, Security Best Practices.
- Visual Studio Code Docs, Agent plugins in VS Code.
- GitHub Docs, Plugins for GitHub Copilot.
- Agent Plugins, MCP servers.
What to do with the first accepted package
With the inventory in hand, each accepted tool needs a task policy. Component, credential and scope decide what enters; policy decides what the tool can do afterward. These are separate decisions, with records for later comparison.
Nexforce Agents includes Nexforce Work for nontechnical teams, with approvals, sandbox, skill manager and MCP connectors, and Nexforce Code for terminal, IDE and CI. In both, Nexforce Router is the inference layer. No public material consulted documents per-plugin scope or credential issuance by component in these products. The Agent Plugins 1.0.0 specification likewise does not define portable permissions in the manifest [3][4]. The team must check which components loaded in the installed client and compare the result with the acceptance inventory. For a remote MCP server, the requested and actually granted scopes need to be compared with policy, because the grant can be smaller [12][13]. The component list does not replace process isolation: package containment rules are not a sandbox [3][9]. After acceptance, task policy and the record of subject, tool and result address tool use.
The package arrives with an identity. Permission remains yours.

Deploy Work and Code Agentswith zero software licensing costs
Automate operational tasks and code writing autonomously with dedicated agents integrated into your systems
Free TrialRelated articles

How to schedule AI agents safely with runtime controls
An operating contract connects the approved schedule to the expected occurrence, its business deadline, and an owner's decision.
Read more
Vercel's agent-desktop: who is responsible for the session?
The agent-desktop update raises a governance question: who controls an agent's access and actions in a graphical session?
Read more
AI agent isolation: a network claim is not enough
A review of cybersecurity evaluations found real-world access where the test environment was described as isolated. The distinction matters for teams setting permissions for production agents.
Read more