Last reviewed: 2026-08-27
Direct answer
Replace MCP Roots with a run-scoped workspace contract that is explicit at every tool boundary. Put the directories, files, or resource references a task may use into tool arguments, resource identifiers, or server configuration; then enforce that contract in the MCP client and server before a model call is allowed to do work. Treat CometAPI as the model-routing boundary, not as a filesystem permission system. Carry a non-secret scope identifier alongside the application request and make the tool service re-check the scope on every call.
The reason to make this change now is normative. The current MCP Roots specification marks Roots deprecated in protocol version 2026-07-28. It also says that Roots are informational guidance rather than an access-control mechanism, and asks clients and servers to validate URIs, obtain consent, apply access controls, and respect boundaries. The MCP deprecation registry gives the feature a migration path of passing directories or files through tool parameters, resource URIs, or server configuration. Roots remain available during the deprecation window, but a new integration should not make them the only enforcement point.
Use this migration sequence:
- Inventory each client, MCP server, transport, and protocol era. Record whether a connection uses modern per-request metadata or the older initialize-based handshake.
- Define a small scope object for each run. Include a scope ID, canonical workspace-relative paths or resource references, read/write mode, permitted tool names, approval rules, and an expiry. Keep the scope separate from the natural-language prompt.
- Validate the scope before calling CometAPI. Resolve paths against an approved base, reject traversal and unresolved links, verify the resource scheme, and check operating-system permissions. A model should never be the component that decides whether a path is inside the workspace.
- Expose only the tools needed for that scope. Make read-only inspection the default; require an explicit human or programmatic approval for writes, moves, deletes, or other destructive actions.
- Run a canary with modern and legacy clients. A modern connection should use the current per-request version contract. A legacy connection may continue with its initialize flow and an explicit server-side directory configuration while it is being migrated.
- Record a sanitized decision for every call. Correlate the model request, scope decision, tool result, and any retry without recording prompts, file contents, full paths, or authentication material.
This design keeps the security property local to the component that can actually read or change files. It also makes a CometAPI request reviewable: an operator can see which scope was intended, which tool was exposed, and why the call was allowed or denied, without depending on an implicit Roots exchange.
Who this is for
This guide is for engineers who run coding agents against repositories, worktrees, documentation trees, or build artifacts through MCP and a model gateway such as CometAPI. It is especially useful when a team has several IDE clients, a mixture of old and new MCP servers, or a filesystem server that currently receives its allowed directories through Roots. It is not a replacement for operating-system sandboxing, container isolation, network policy, or a code-review process. Those controls still matter; this article shows how to make the workspace contract explicit inside the agent workflow.
Key takeaways
- A Root tells a server what the client considers relevant; it is not, by itself, a filesystem access-control list.
- New code should pass a bounded directory, file, or resource reference explicitly and validate it before a tool call.
- Resource URIs are useful for selecting context, but the service still has to check permissions and sanitize file paths.
- Keep a compatibility path for legacy initialize-based clients, and make the fallback scope at least as narrow as the modern scope.
- Filter the tool list and classify actions so read operations can be automatic while destructive operations require approval.
- Put only sanitized correlation fields in logs. A scope hash and tool decision are useful; prompts, contents, credentials, and unrestricted paths are not.
- Fail closed on an empty, expired, malformed, or version-incompatible scope. Do not widen access just to make a retry succeed.
Sources checked
The MCP Roots specification supplies the boundary warning, capability shape, URI requirement, and implementation guidance for consent, path validation, and root-boundary checks. The MCP deprecated-features registry records SEP-2577, the 2026-07-28 deprecation date, the earliest removal window on or after 2027-07-28, and the recommended migration choices.
The MCP Resources specification documents URI-addressed context, application-driven selection, per-request authorization variance, resource subscriptions, and the requirement to validate resource URIs and sanitize paths for file-scheme resources. The MCP Versioning and Compatibility guide distinguishes modern per-request metadata from legacy initialize-based versions and describes probing and fallback for dual-era implementations.
For implementation patterns, the OpenAI Agents SDK MCP guide documents Streamable HTTP, SSE, and stdio transports, least-privilege credentials, authorization headers, static and dynamic tool filtering, and per-tool approval policies. The official filesystem MCP server README documents command-line allowed directories, dynamic Roots behavior, fallback behavior for clients without Roots, and the fact that filesystem operations are restricted to configured directories. These sources are independently useful: one is normative protocol guidance, one is the lifecycle registry, one describes resources, one covers compatibility, and the final two show client and server implementation behavior.
Contract details to verify
Before changing a production client, write down the contract it will send to the tool service. An application-level scope can be as small as this:
{
"scope_id": "scope-20260827-demo",
"allowed_paths": ["workspace/src", "workspace/tests"],
"resource_refs": ["resource:repo-readme"],
"access_mode": "read",
"tool_allowlist": ["list_directory", "read_text_file"],
"approval": "required_for_write",
"expires_after_minutes": 30
}
The names are application choices, not a claim that MCP has one universal scope schema. What matters is that the service receives enough information to make a deterministic decision. allowed_paths should be canonicalized before storage; the displayed labels should not be used as path authorities. resource_refs should be resolved by a server that knows the caller’s permissions. Keep access_mode and the tool allowlist in the same policy record so a later model turn cannot silently add a write-capable tool.
For each call, verify five contracts. First, confirm that the requested path or resource is inside the approved scope after normalization, including checks for traversal, symlink escape, and a changed mount. Second, confirm the operation class: reading a file is different from editing, moving, overwriting, or deleting one. Third, confirm that the server has the required capability and that the client is speaking a mutually supported protocol version. Fourth, confirm that the CometAPI application boundary can correlate the call with scope_id and the run ID; if metadata cannot be preserved, keep the correlation in a sidecar record and deny calls that cannot be attributed. Fifth, confirm expiry and revocation immediately before execution, not only when the agent session starts.
The version check deserves its own test. The current versioning guide says modern requests carry protocol version and capabilities as per-request metadata, while legacy implementations use initialize. A dual-era client can probe and cache the server era, retrying with a mutually supported version when the server returns an explicit unsupported-version error. It should not interpret every error as permission to fall back. A permission denial, malformed scope, or path-outside-scope error must remain a denial.
The tool policy should be equally concrete. The Agents SDK guide shows allowlists and per-tool approval maps. Apply that idea at the gateway-facing agent layer: expose only list_directory and read_text_file for a discovery run; add write tools only after a reviewer approves a new scope; and make approval state part of the call record. Classify each allowed operation in your own policy and do not treat descriptive metadata from an untrusted server as proof that a call is safe.
Failure modes
Roots are mistaken for a hard sandbox. A client may advertise a directory while a server still has another route to the host filesystem. The Roots specification explicitly says the protocol does not enforce that servers stay within Roots. Fix this by enforcing canonical paths and operating-system permissions in the server, then log the decision independently of the Root exchange.
A current client meets a legacy server. The client may send modern metadata to a server that only understands initialize, or a legacy client may reach a modern-only endpoint. Follow the versioning guide’s probe and fallback rules, pin the fallback to an explicit directory configuration, and surface an actionable error when no mutually supported version exists. Never broaden the workspace during negotiation.
A filesystem server receives an empty scope. An empty scope gives the server no valid application-level boundary to enforce. Treat it as a configuration error rather than substituting a broader directory. For a legacy client, supply a deliberately narrow command-line directory configuration and require the operator to update that configuration when the approved workspace changes.
A resource reference escapes its intended tree. URI decoding, .. segments, alternate separators, symlinks, or a changed mount can turn a harmless-looking reference into a different file. The Resources specification requires URI validation and path sanitization for file-scheme resources. Normalize, resolve, compare against the approved base, and deny on ambiguity before reading anything.
The model sees more tools than the task needs. A broad tool list turns a read-only request into an accidental write path. Use a positive allowlist, then add explicit approvals for edits, moves, overwrites, and deletes. Classify read-only and destructive operations in application policy so retry and approval behavior remains enforceable even when server metadata is incomplete.
A retry repeats a destructive action. A timeout at CometAPI or an MCP transport does not prove that a write failed. Treat edits, moves, overwrites, and deletes as non-idempotent unless the tool contract proves otherwise. Do not automatically retry those calls. Require an operation ID, inspect the first result if possible, and ask for approval before any replay.
Gateway metadata disappears. If the model request reaches CometAPI but scope_id is absent from the tool-service record, an operator cannot prove which workspace was authorized. Make correlation a preflight check. Reject an un-attributed call, or route it to a read-only diagnostic path that cannot access repository contents.
Logs leak the very data the boundary protects. Full paths, prompts, file contents, and authentication headers can reveal sensitive material even when the access decision was correct. Keep logs to identifiers and outcomes. A sanitized record can look like this:
{
"run_id": "run-20260827-001",
"scope_hash": "sha256:short",
"protocol_era": "modern",
"protocol_version": "2026-07-28",
"server_origin": "mcp.example.invalid",
"tool": "read_text_file",
"action_class": "read",
"decision": "allow",
"approval": "not_required",
"error_code": null,
"retry_count": 0,
"latency_ms": 184
}
For a denial, keep the same fields and set decision to deny, use a stable internal error class such as PATH_OUTSIDE_SCOPE, and replace any path detail with [REDACTED]. Never log the prompt or the returned file content. This record is enough to answer who asked, which policy was selected, what tool class was attempted, and why execution stopped.
FAQ
Are MCP Roots already removed? No. The current registry marks Roots deprecated, not removed. The feature remains in the specification during a deprecation window of at least twelve months, and is eligible for removal in a revision released on or after 2027-07-28. That window is for migration, not a reason to make Roots the only control in new code.
Do explicit paths automatically make a workflow secure? No. Explicit inputs make the decision inspectable, but the client and server still need canonicalization, permission checks, boundary enforcement, expiry, and a fail-closed response. A string supplied as a tool argument is data, not an authorization grant.
Can a team support old and new clients at the same time? Yes. The versioning guide describes dual-era implementations and transport-specific probing. Keep the legacy initialize path narrow, test both paths, and distinguish an unsupported version from an ordinary tool or permission error.
Should every repository file become a resource? No. Use a resource reference when the server already has a stable, permission-aware resource catalog. Use a tool parameter or server configuration when a short-lived checkout or disposable worktree is the safer boundary. In either case, validate the identifier and enforce access at read time.
Where does CometAPI fit? Use CometAPI to route the model request and keep the application-level scope ID available for correlation. Enforce filesystem policy in the MCP client and server, because the supplied protocol sources do not make a model gateway an access-control mechanism. If the gateway cannot preserve the correlation, stop the call rather than guessing its scope.
What if an existing server only documents Roots? Keep a compatibility adapter with a fixed, least-privilege directory configuration, test empty and changed scopes, and schedule migration to explicit arguments, resource references, or server configuration. The filesystem README is useful for understanding current behavior, while the current protocol registry is the source for the deprecation timeline.
Reader next step
Start with one read-only coding-agent task. Create a scope containing only the checkout directories that task needs, allow two read tools, set a short expiry, and run one modern and one legacy compatibility test. Verify that the happy path produces a complete result and a sanitized allow record. Then submit a traversal attempt and an un-attributed gateway call; both should be denied without a wider retry. Once that canary is boring, add an approval gate for one write tool and repeat the same tests.
For the next implementation pass, review the MCP tool approval gates guide and the permission and secret boundaries guide . When you are ready to route the validated model workflow, Start with CometAPI .