1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
//! Request / response shapes for the machine-registry coordination MCP tools.
//!
//! The registry itself persists msgpack structs ([`WorkspaceRecord`](crate::registry::WorkspaceRecord)
//! et al.) that derive serde but NOT `JsonSchema`. To keep the MCP schema surface honest and stable,
//! this module defines MCP-facing DTOs and maps the registry rows into them at the helper boundary —
//! the raw persistence structs never leak into the tool schema.
#![cfg(all(feature = "comms", any(unix, windows)))]
use serde::{Deserialize, Serialize};
use crate::registry::{BranchRecord, WorkspaceKind, WorkspaceRecord, WorktreeRecord};
/// Params for `workspaces`: list every registered workspace in the machine registry.
#[derive(Debug, Clone, Default, Deserialize, Serialize, schemars::JsonSchema)]
pub struct WorkspacesParams {
/// Optional sub-identity to act as; defaults to the server's own agent. Lets one orchestrator
/// drive many named subagents.
#[serde(default)]
pub as_agent: Option<String>,
}
/// One workspace row in a `workspaces` response.
#[derive(Debug, Serialize)]
pub(super) struct WorkspaceDto {
/// Stable workspace key (blake3 of the canonical root); also the cache-dir identity.
pub key: String,
/// `"git"` or `"plain"`.
pub kind: String,
/// Canonical workspace root.
pub root: String,
/// Owning repo id, absent for a plain workspace.
#[serde(skip_serializing_if = "Option::is_none")]
pub repo_id: Option<String>,
/// Main-worktree root of the owning clone, absent for a plain workspace.
#[serde(skip_serializing_if = "Option::is_none")]
pub main_worktree: Option<String>,
/// Unix micros of the last register/refresh.
pub last_seen: i64,
}
impl From<&WorkspaceRecord> for WorkspaceDto {
fn from(record: &WorkspaceRecord) -> Self {
Self {
key: record.key.clone(),
kind: match record.kind {
WorkspaceKind::Git => "git".to_string(),
WorkspaceKind::Plain => "plain".to_string(),
},
root: record.root.display().to_string(),
repo_id: record.repo_id.clone(),
main_worktree: record.main_worktree.as_ref().map(|p| p.display().to_string()),
last_seen: record.last_seen,
}
}
}
/// Response for `workspaces`.
#[derive(Debug, Serialize)]
pub(super) struct WorkspacesResponse {
/// Number of workspaces returned.
pub total: usize,
/// The workspace rows, sorted by key.
pub workspaces: Vec<WorkspaceDto>,
}
/// Params for `worktrees`: list the worktrees of a registered repo.
#[derive(Debug, Clone, Deserialize, Serialize, schemars::JsonSchema)]
pub struct WorktreesParams {
/// The repo id (normalized remote URL or `path:<root>`) whose worktrees to list.
pub repo_id: String,
/// Optional sub-identity to act as; defaults to the server's own agent.
#[serde(default)]
pub as_agent: Option<String>,
}
/// One worktree row in a `worktrees` response.
#[derive(Debug, Serialize)]
pub(super) struct WorktreeDto {
/// Owning repo id.
pub repo_id: String,
/// `"(main)"` or the linked-worktree directory name.
pub name: String,
/// Absolute, canonical checkout root.
pub path: String,
/// Head commit sha, absent on an unborn HEAD.
#[serde(skip_serializing_if = "Option::is_none")]
pub head_sha: Option<String>,
/// Checked-out branch, absent when detached or unresolvable.
#[serde(skip_serializing_if = "Option::is_none")]
pub branch: Option<String>,
/// True when HEAD is detached.
pub detached: bool,
/// Advisory claimant currently holding this worktree, if any.
#[serde(skip_serializing_if = "Option::is_none")]
pub claimed_by: Option<String>,
/// Unix micros of the last refresh.
pub last_seen: i64,
}
impl From<&WorktreeRecord> for WorktreeDto {
fn from(record: &WorktreeRecord) -> Self {
Self {
repo_id: record.repo_id.clone(),
name: record.name.clone(),
path: record.path.display().to_string(),
head_sha: record.head_sha.clone(),
branch: record.branch.clone(),
detached: record.detached,
claimed_by: record.claimed_by.clone(),
last_seen: record.last_seen,
}
}
}
/// Response for `worktrees`.
#[derive(Debug, Serialize)]
pub(super) struct WorktreesResponse {
/// The repo id queried.
pub repo_id: String,
/// Number of worktrees returned.
pub total: usize,
/// The worktree rows, sorted by name.
pub worktrees: Vec<WorktreeDto>,
}
/// Params for `branches`: list the local branches of a registered repo.
#[derive(Debug, Clone, Deserialize, Serialize, schemars::JsonSchema)]
pub struct BranchesParams {
/// The repo id whose branches to list.
pub repo_id: String,
/// Optional sub-identity to act as; defaults to the server's own agent.
#[serde(default)]
pub as_agent: Option<String>,
}
/// One branch row in a `branches` response.
#[derive(Debug, Serialize)]
pub(super) struct BranchDto {
/// Owning repo id.
pub repo_id: String,
/// Short branch name (`refs/heads/` stripped).
pub name: String,
/// 40-hex head commit sha.
pub head_sha: String,
/// Unix micros of the last refresh.
pub last_seen: i64,
}
impl From<&BranchRecord> for BranchDto {
fn from(record: &BranchRecord) -> Self {
Self {
repo_id: record.repo_id.clone(),
name: record.name.clone(),
head_sha: record.head_sha.clone(),
last_seen: record.last_seen,
}
}
}
/// Response for `branches`.
#[derive(Debug, Serialize)]
pub(super) struct BranchesResponse {
/// The repo id queried.
pub repo_id: String,
/// Number of branches returned.
pub total: usize,
/// The branch rows, sorted by name.
pub branches: Vec<BranchDto>,
}
/// Params for `worktree_claim`: advisory-claim a worktree.
#[derive(Debug, Clone, Deserialize, Serialize, schemars::JsonSchema)]
pub struct WorktreeClaimParams {
/// The owning repo id.
pub repo_id: String,
/// The worktree name (`"(main)"` or the linked-worktree directory name).
pub name: String,
/// Optional claimant sub-identity; defaults to the server's own agent id.
#[serde(default)]
pub as_agent: Option<String>,
}
/// Params for `worktree_release`: release an advisory worktree claim.
#[derive(Debug, Clone, Deserialize, Serialize, schemars::JsonSchema)]
pub struct WorktreeReleaseParams {
/// The owning repo id.
pub repo_id: String,
/// The worktree name whose claim to release.
pub name: String,
/// Optional claimant sub-identity; defaults to the server's own agent id.
#[serde(default)]
pub as_agent: Option<String>,
}
/// Response for `worktree_claim` / `worktree_release`.
#[derive(Debug, Serialize)]
pub(super) struct WorktreeClaimResponse {
/// The owning repo id.
pub repo_id: String,
/// The worktree name acted on.
pub name: String,
/// The claimant identity the claim/release ran as.
pub claimant: String,
/// For a claim: `true` when the claim is now held by the claimant. For a release: `true` when a
/// claim by the claimant was cleared. `false` otherwise (unknown worktree, or held by another).
pub held: bool,
}