Skip to main content

Workspace

Struct Workspace 

Source
pub struct Workspace { /* private fields */ }
Expand description

Workspace runtime state. Shared across MCP request clones via Arc.

Implementations§

Source§

impl Workspace

Source

pub fn open( workspace_dir: PathBuf, stale_after_days: u32, post_activate: Option<PostActivateHook>, ) -> Result<Self>

Open a github-flavoured workspace (clone + track flow).

Source

pub fn open_local( root: PathBuf, post_activate: Option<PostActivateHook>, ) -> Result<Self>

Open a local-directory workspace.

Binds root as the active source root immediately and fires the post-activate hook (subject to last-built-sha gating). inventory.json is kept under <root>/.mcp-workspace/ so the local mode mirrors the same gating / fingerprinting infra without polluting the user’s tree with a repos/ directory.

Source

pub fn open_local_unanchored( post_activate: Option<PostActivateHook>, ) -> Result<Self>

Open a local-directory workspace with no active root.

The unanchored sibling of open_local, for the case where the root is expected to arrive later from an MCP client (workspace.adopt_client_roots, see adopt_client_root). Nothing is bound, nothing is created on disk, and no hook fires: until something activates, active_repo_path is None and the source tools behave exactly as they do with no root — which is the designed outcome when no root ever arrives.

The inventory directory is deliberately not created here: with no root there is nowhere to put it. The first activation that commits picks the directory (<root>/.mcp-workspace/) and it is fixed from then on, mirroring the anchored mode’s rule that the inventory survives later root swaps. An activation that fails its build picks nothing and creates nothing — a client-proposed root that never activated must not end up owning the inventory, nor gain a directory it did not have.

root_ownership starts at RootOwnership::Unowned — the only constructor that does.

Source

pub fn with_adopt_client_roots(self) -> Self

Allow this workspace to adopt a client-advertised MCP root as a fallback when the operator configured none (manifest key workspace.adopt_client_roots).

Off by default. With it off no roots/list request is ever issued, which is what keeps clients that do not advertise roots — and deployments that never opt in — bit-for-bit unaffected.

Setting it on a workspace that already has a root is harmless and intentional: ownership is already RootOwnership::Operator, so adoption is refused. Call before the workspace is cloned into crate::server::ServerOptions, like the other builders.

MCP roots is deprecated as of protocol revision 2026-07-28 (SEP-2577) and is eligible for removal in the first revision released on or after 2027-07-28. New deployments should prefer the spec’s own migration path — pass directories via tool parameters, resource URIs, or server configuration (workspace.root).

Source

pub fn adopts_client_roots(&self) -> bool

Is client-root adoption enabled (workspace.adopt_client_roots)?

Source

pub fn root_ownership(&self) -> RootOwnership

Who chose the active root — see RootOwnership.

Never blocks on an in-flight activation: the flag has a lock of its own, distinct from the swap ordering lock that a root swap holds across its (arbitrarily long) build.

Source

pub fn with_sandbox_root(self, boundary: &Path) -> Result<Self>

Bound runtime root swaps to a containment boundary (local mode).

With a boundary attached, set_root_dir refuses any target whose canonical path is not inside boundary.. traversals and symlinks out of the tree are therefore rejected too, and the rejection happens before any state is touched. Without it (the default) set_root_dir stays unbounded, which is the historical behaviour.

Call immediately after open_local, before the workspace is cloned into crate::server::ServerOptions — like with_activation_summary it mutates the still-unique inner Arc. Unlike the hook builders, a late call is an error rather than a warning: a containment boundary that silently failed to attach is worse than no boundary at all.

Errors when the boundary does not exist, when the workspace is not local-flavoured, or when the already-active root lies outside the boundary — a config that contradicts itself must die at boot, not at the first swap.

Source

pub fn with_activation_summary(self, hook: ActivationSummaryHook) -> Self

Attach an ActivationSummaryHook. Call immediately after open/open_local (before the workspace is cloned into ServerOptions): it mutates the still-unique inner Arc. Calling it after the workspace has been cloned is a no-op with a warning — the summary simply won’t be attached.

Source

pub fn with_post_activate_revs(self, hook: PostActivateRevsHook) -> Self

Attach a PostActivateRevsHook. Call immediately after open/open_local (before the workspace is cloned into ServerOptions): it mutates the still-unique inner Arc, exactly like with_activation_summary. Calling it after the workspace has been cloned is a no-op with a warning. Additive — consumers that don’t set it keep the plain single-rev activation behaviour.

Source

pub fn with_activation_transaction( self, hook: ActivationTransactionHook, ) -> Self

Attach the request-scoped activation prepare/commit contract.

When set, this hook owns plain builds, revision-set builds, cheap-skip summaries, and atomic product publication. It replaces the legacy post_activate, post_activate_revs, and activation_summary callbacks for activation calls. Configure it before cloning the workspace into crate::server::ServerOptions.

Source

pub fn kind(&self) -> WorkspaceKind

Source

pub fn workspace_dir(&self) -> &Path

The directory this workspace keeps its bookkeeping under.

After an unanchored local boot (open_local_unanchored) this is the empty path until the first activation anchors it; every other constructor knows it up front.

Source

pub fn repos_dir(&self) -> PathBuf

Source

pub fn active_repo_name(&self) -> Option<String>

Active repo’s full org/repo name, or None if nothing is active.

Source

pub fn active_repo_path(&self) -> Option<PathBuf>

Active repo’s filesystem path, or None.

Source

pub fn default_github_repo(&self) -> Option<String>

Default org/repo for the GitHub tools when the caller passes none.

Github mode: the active repo — there the inventory key is the org/repo. Local mode: the active root’s origin remote parsed to org/repo, or None when there’s no GitHub remote. Crucially it is never the local/<dir> inventory key (see active_repo_name), which is a filesystem-derived key, not a valid repo slug.

Source

pub fn last_built_sha(&self, name: &str) -> Option<String>

Read the SHA recorded after the last successful post-activate hook for the named repo. None if the repo was never built (or the hook last failed). Useful for downstream consumers gating “is the active graph up to date with the repo HEAD?” checks.

Source

pub fn last_built_revs(&self, name: &str) -> Option<RevsRequest>

Read the revisions request last successfully built for the named repo — Some when the last build was multi-rev (revs=), None for a plain / HEAD-only build or a never-built repo. Drives the rev-set-aware skip gate and the update-preserves-rev-set path.

Source

pub fn repo_management( &self, name: Option<&str>, delete: bool, update: bool, force_rebuild: bool, revs: Option<&RevsRequest>, ) -> String

Public entry for the repo_management MCP tool.

  • name: org/repo to activate (None = list / refresh mode).
  • delete: remove the named repo + inventory entry. Github only.
  • update: refresh the active repo (auto-rebuild gated).
  • force_rebuild: with update=true (or initial activation), re-run the post-activate hook even when the HEAD SHA matches last_built_sha. Useful after the builder itself has been upgraded.

Local mode behaviour: name and delete are rejected; pass update=true (or no args after the initial activation) to re-fingerprint the root and rebuild if anything changed.

Source

pub fn set_root_dir( &self, new_root: &Path, revs: Option<&RevsRequest>, ) -> String

Swap the active root (local mode only). Re-fires the post-activate hook against the new root. Errors if the workspace is github-flavoured.

revs (optional): resolve revisions against the new root (which must be a git repo) and fire the revs-aware hook — see activate / RevsRequest.

Concurrency: two set_root_dir calls may overlap — the newer activation supersedes the older one. A client-root adoption may not overlap either of them: it is exclusive with every operator swap for its whole check-swap-publish sequence, which is what makes “the operator always wins” true rather than merely likely. Must not be called from inside an activation hook (that has always been true — the legacy hook path already serializes on its own lock).

Source

pub fn adopt_client_root(&self, new_root: &Path) -> Result<String, String>

Adopt a root proposed by the MCP client (roots/list).

Routes through the same validation and containment path as set_root_dir — one code path, so a workspace.sandbox_root boundary applies identically to an operator swap and to a path proposed by an external party. The MCP spec is explicit that roots are “informational guidance rather than an access-control mechanism”, so the boundary, not the client, is what bounds this.

Refuses (without touching any state) when ownership is already RootOwnership::Operator — an operator-chosen root always wins. On success ownership becomes RootOwnership::Adopted, leaving a later roots/list_changed free to replace it.

Err is a human-readable reason for the log; adoption failure is never fatal to the server.

Trait Implementations§

Source§

impl Clone for Workspace

Source§

fn clone(&self) -> Workspace

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more