Skip to content
1,079 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

API and MCP server in Rust; a public index at the API root1//! The OpenAPI document, generated from the same list the routes are.
Merge branch 'worktree-agent-ab2e39e11a6493412'2//!
3//! The docs site builds its API reference from a copy of this document,
4//! `apps/docs/src/data/openapi.json`. A test keeps the copy current: run
5//! `G1T_WRITE_OPENAPI=1 cargo test -p g1t-api openapi` to rewrite it.
API and MCP server in Rust; a public index at the API root6
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step7use g1t_contracts::scopes::scope_for;
API and MCP server in Rust; a public index at the API root8use serde_json::{Map, Value, json};
9
10use crate::operations::Op;
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar11use crate::security::SecurityOp;
API and MCP server in Rust; a public index at the API root12use crate::rest::{ROUTES, Route};
13
Merge branch 'worktree-agent-ab2e39e11a6493412'14/// The sections of the API reference: a name, what it covers, and its
15/// operations in the order a reader meets them.
16const SECTIONS: &[(&str, &str, &[Op])] = &[
17 (
18 "Accounts",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look19 "Signing in from a tool, who a token acts as, and your email addresses.",
20 &[Op::Whoami, Op::ListEmails, Op::AddEmail, Op::RemoveEmail, Op::UpdateEmailSettings],
Merge branch 'worktree-agent-ab2e39e11a6493412'21 ),
22 (
API: notifications over REST and MCP, with notifications scopes23 "Notifications",
24 "Your inbox: a thread for each thing you were told about (an issue, a pull request, a workflow on a branch, a deployment), why you were told, and what you subscribe to and watch. Your own: personal tokens and sessions only.",
25 &[
26 Op::ListNotifications,
27 Op::MarkNotificationsRead,
28 Op::GetNotificationThread,
29 Op::MarkThreadRead,
30 Op::MarkThreadDone,
31 Op::SaveThread,
32 Op::SnoozeThread,
33 Op::GetThreadSubscription,
34 Op::SetThreadSubscription,
35 Op::DeleteThreadSubscription,
36 Op::GetRepoSubscription,
37 Op::SetRepoSubscription,
38 Op::DeleteRepoSubscription,
39 Op::ListWatchedRepos,
40 ],
41 ),
42 (
API: pinned projects over REST and MCP43 "Pinned projects",
44 "The projects you keep at the top of a workspace's sidebar, in your order, up to eight a workspace. Your own: personal tokens and sessions only.",
45 &[Op::ListPinnedProjects, Op::PinProject, Op::UnpinProject, Op::ReorderPinnedProjects],
46 ),
47 (
Merge branch 'worktree-agent-ab2e39e11a6493412'48 "Workspaces",
49 "A workspace owns repositories and is the first part of their address. People and agents work in workspaces.",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily50 &[Op::CreateWorkspace, Op::UpdateWorkspace, Op::DeleteWorkspace],
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look51 ),
52 (
53 "Invites",
54 "While g1t is invite-only, every new account needs an invite. Your invites, and inviting people into a workspace by email.",
55 &[
56 Op::ListInvites,
57 Op::CreateInvite,
58 Op::RevokeInvite,
59 Op::ListWorkspaceInvites,
60 Op::InviteMember,
61 Op::RevokeWorkspaceInvite,
62 ],
Merge branch 'worktree-agent-ab2e39e11a6493412'63 ),
64 (
65 "Repositories",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look66 "A repository, how it handles pull requests, and its timeline: renaming, archiving, moving and deleting it.",
Merge branch 'worktree-agent-ab2e39e11a6493412'67 &[
68 Op::ListRepos,
69 Op::CreateRepo,
70 Op::GetRepo,
71 Op::UpdateRepo,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look72 Op::RenameRepo,
73 Op::RenameBranch,
74 Op::SetRepoVisibility,
75 Op::ArchiveRepo,
76 Op::UnarchiveRepo,
77 Op::TransferRepo,
78 Op::DeleteRepo,
79 Op::ListDeletedRepos,
80 Op::RestoreRepo,
81 Op::PurgeRepo,
Merge branch 'worktree-agent-ab2e39e11a6493412'82 Op::GetRepoSettings,
83 Op::UpdateRepoSettings,
Fast pages, required checks on the branch, self-hosted runners, honest incidents84 Op::ListCheckNames,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar85 Op::GetCodeownersErrors,
Merge branch 'worktree-agent-ab2e39e11a6493412'86 Op::ListEvents,
87 ],
88 ),
89 (
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look90 "Access",
91 "Who can do what in a repository: repository roles, people given a role on one repository (outside collaborators when they are not members), invitations, and a workspace's base permission.",
92 &[
93 Op::ListCollaborators,
94 Op::AddCollaborator,
95 Op::UpdateCollaborator,
96 Op::RemoveCollaborator,
97 Op::GetCollaboratorPermission,
98 Op::ListRepoInvitations,
99 Op::RevokeRepoInvitation,
100 Op::ListMyRepoInvitations,
101 Op::AcceptRepoInvitation,
102 Op::DeclineRepoInvitation,
103 Op::SetBasePermission,
104 Op::ListOutsideCollaborators,
105 ],
106 ),
107 (
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar108 "Teams",
109 "Groups of a workspace's members: given a role on repositories together, mentioned together as @workspace/team, and asked to review together. Any member may create a team; the workspace's owners and the team's maintainers manage it.",
110 &[
111 Op::ListTeams,
112 Op::CreateTeam,
113 Op::GetTeam,
114 Op::UpdateTeam,
115 Op::DeleteTeam,
116 Op::ListTeamMembers,
117 Op::SetTeamMember,
118 Op::RemoveTeamMember,
119 Op::ListChildTeams,
120 Op::ListTeamRepos,
121 Op::SetTeamRepo,
122 Op::RemoveTeamRepo,
123 Op::SetTeamReviewAssignment,
124 Op::ListUserTeams,
125 ],
126 ),
127 (
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily128 "Security",
129 "Secrets found in what is pushed and in a repository's history, and dependencies with known vulnerabilities: listing the alerts, and dismissing or reopening them.",
130 &[Op::ListSecurityAlerts, Op::DismissSecurityAlert, Op::ReopenSecurityAlert],
131 ),
132 (
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar133 "Secret scanning",
134 "Secrets found in pushes and history, where each one is, pushing past push protection with a reason (and asking for approval when the workspace delegates bypasses), checking with a secret's issuer whether it still works, and custom patterns.",
135 &[
136 Op::Security(SecurityOp::ListSecretAlerts),
137 Op::Security(SecurityOp::GetSecretAlert),
138 Op::Security(SecurityOp::UpdateSecretAlert),
139 Op::Security(SecurityOp::ListSecretLocations),
140 Op::Security(SecurityOp::BypassPushProtection),
141 Op::Security(SecurityOp::CheckSecretValidity),
142 Op::Security(SecurityOp::ListBypassRequests),
143 Op::Security(SecurityOp::ReviewBypassRequest),
144 Op::Security(SecurityOp::ListCustomPatterns),
145 Op::Security(SecurityOp::CreateCustomPattern),
146 Op::Security(SecurityOp::UpdateCustomPattern),
147 Op::Security(SecurityOp::DeleteCustomPattern),
148 Op::Security(SecurityOp::DryRunCustomPattern),
149 ],
150 ),
151 (
152 "Code scanning",
153 "Results of static analysis tools, uploaded as SARIF: alerts on the default branch, the analyses that made them, uploads, and putting g1t on an alert to fix it.",
154 &[
155 Op::Security(SecurityOp::ListCodeAlerts),
156 Op::Security(SecurityOp::GetCodeAlert),
157 Op::Security(SecurityOp::UpdateCodeAlert),
158 Op::Security(SecurityOp::ListAnalyses),
159 Op::Security(SecurityOp::UploadSarif),
160 Op::Security(SecurityOp::GetSarifUpload),
161 Op::Security(SecurityOp::FixAlert),
162 ],
163 ),
164 (
165 "Supply chain",
166 "What a repository depends on: vulnerability alerts, the dependency graph, an SPDX SBOM of it, and comparing two commits' dependencies as dependency review does.",
167 &[
168 Op::Security(SecurityOp::ListVulnerabilityAlerts),
169 Op::Security(SecurityOp::GetVulnerabilityAlert),
170 Op::Security(SecurityOp::UpdateVulnerabilityAlert),
171 Op::Security(SecurityOp::GetDependencyGraph),
172 Op::Security(SecurityOp::GetSbom),
173 Op::Security(SecurityOp::CompareDependencies),
174 ],
175 ),
176 (
177 "Security settings",
178 "When pull request checks fail, dependency review's policy, delegated bypass and validity checks, and a workspace's security overview.",
179 &[
180 Op::Security(SecurityOp::GetSettings),
181 Op::Security(SecurityOp::UpdateSettings),
182 Op::Security(SecurityOp::GetWorkspaceSettings),
183 Op::Security(SecurityOp::UpdateWorkspaceSettings),
184 Op::Security(SecurityOp::GetOverview),
185 ],
186 ),
187 (
Merge branch 'worktree-agent-ab2e39e11a6493412'188 "Issues",
189 "What should change in a repository, with labels and comments. Issues and pull requests share one sequence of numbers.",
190 &[
191 Op::ListIssues,
192 Op::CreateIssue,
193 Op::GetIssue,
194 Op::UpdateIssue,
195 Op::CloseIssue,
196 Op::ReopenIssue,
197 Op::AssignIssue,
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step198 Op::Delegate,
Merge branch 'worktree-agent-ab2e39e11a6493412'199 Op::AddComment,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar200 Op::ListIssueLabels,
201 Op::AddIssueLabels,
202 Op::SetIssueLabels,
203 Op::RemoveIssueLabels,
204 ],
205 ),
206 (
207 "Labels and milestones",
208 "A repository's labels, which issues and pull requests carry by name, and its milestones, which gather them under a goal and a due date.",
209 &[
Merge branch 'worktree-agent-ab2e39e11a6493412'210 Op::ListLabels,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar211 Op::CreateLabel,
212 Op::UpdateLabel,
213 Op::DeleteLabel,
214 Op::AddDefaultLabels,
215 Op::ListMilestones,
216 Op::CreateMilestone,
217 Op::GetMilestone,
218 Op::UpdateMilestone,
219 Op::DeleteMilestone,
Merge branch 'worktree-agent-ab2e39e11a6493412'220 ],
221 ),
222 (
223 "Plans",
224 "An outcome turned into the issues that would get there, with the order they must merge in.",
225 &[Op::PlanWork, Op::GetPlan, Op::ApplyPlan],
226 ),
227 (
228 "Pull requests",
229 "A proposed change in its own fork or on a branch. Several can be made for one issue; the one merged resolves it.",
230 &[
231 Op::ListPullRequests,
232 Op::CreatePullRequest,
233 Op::GetPullRequest,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar234 Op::UpdatePullRequest,
Merge branch 'worktree-agent-ab2e39e11a6493412'235 Op::GetPullRequestChanges,
236 Op::MarkPullRequestReady,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar237 Op::RequestReviewers,
238 Op::RemoveRequestedReviewers,
Merge branch 'worktree-agent-ab2e39e11a6493412'239 Op::ReviewPullRequest,
240 Op::MergePullRequest,
241 Op::ClosePullRequest,
242 Op::GetMergeQueue,
243 Op::MessageAgent,
244 Op::AnswerMessage,
245 Op::TakeMessages,
246 ],
247 ),
248 (
249 "Sessions",
250 "The record of how a pull request was made: prompts, reasoning and the tools that ran.",
251 &[Op::ReadSession, Op::RecordSession],
252 ),
253 (
Agents and memory, checks and conflicts, profiles, slug renames, custom domains254 "Memory",
255 "What agents and people learned that the next agent should know, for one project or across a workspace. Members and g1t's agents only; never a secret.",
256 &[Op::Remember, Op::Recall],
257 ),
258 (
Search across all of g1t, Explore, and a command palette259 "Search",
260 "One search across all of g1t: repositories, code, issues, pull requests, people and workspaces. Public content for everyone, and private content in workspaces you belong to.",
261 &[Op::Search],
262 ),
263 (
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API264 "Context",
265 "A workspace's context hub: a catalog of what it builds and runs, built from its repositories, deployments and integrations, and one search across the catalog, docs, issues, pull requests and memory.",
266 &[Op::SearchContext, Op::GetEntity],
267 ),
268 (
Merge branch 'worktree-agent-ab2e39e11a6493412'269 "Actions",
270 "GitHub Actions workflows in .g1t/workflows, their runs, and their jobs' logs.",
271 &[
272 Op::ListWorkflows,
273 Op::ListWorkflowRuns,
274 Op::GetWorkflowRun,
275 Op::GetJobLogs,
276 Op::DispatchWorkflow,
277 Op::CancelWorkflowRun,
278 Op::RerunWorkflowRun,
279 Op::UpdateWorkflow,
280 ],
281 ),
282 (
283 "Secrets and variables",
284 "Values that workflows and deployments read, per repository or for a whole workspace, with a row per environment.",
285 &[
286 Op::ListActionsSecrets,
287 Op::SetActionsSecret,
288 Op::DeleteActionsSecret,
289 Op::ListActionsVariables,
290 Op::SetActionsVariable,
291 Op::DeleteActionsVariable,
292 ],
293 ),
294 (
Fast pages, required checks on the branch, self-hosted runners, honest incidents295 "Runners",
296 "Self-hosted runners: your own machines, which run your workflow jobs (and, if you choose, your agents' work) for $0 of g1t compute. They register with a short-lived token and only ever connect out.",
297 &[
298 Op::ListRunners,
299 Op::CreateRunnerRegistrationToken,
300 Op::RemoveRunner,
301 Op::ListRunnerGroups,
302 Op::CreateRunnerGroup,
303 Op::UpdateRunnerGroup,
304 Op::DeleteRunnerGroup,
305 Op::GetRunnerSettings,
306 Op::UpdateRunnerSettings,
307 ],
308 ),
309 (
Merge branch 'worktree-agent-ab2e39e11a6493412'310 "Webhooks",
311 "Signed HTTPS requests sent to your own address as things happen, for a repository or a whole workspace.",
312 &[
313 Op::ListWebhooks,
314 Op::CreateWebhook,
315 Op::UpdateWebhook,
316 Op::DeleteWebhook,
317 Op::PingWebhook,
318 Op::ListWebhookDeliveries,
319 Op::RedeliverWebhook,
320 ],
321 ),
322 (
323 "Integrations",
324 "A workspace's connections to outside systems: model providers, alert sources and issue trackers.",
325 &[
326 Op::ListIntegrations,
327 Op::ConnectIntegration,
328 Op::DisconnectIntegration,
329 Op::TestIntegration,
330 Op::GetModelRoutes,
331 Op::SetModelRoutes,
332 Op::GetContext,
333 Op::ImportIssue,
334 ],
335 ),
336];
337
API and MCP server in Rust; a public index at the API root338/// The section of the API reference an operation is listed under.
339fn tag(op: Op) -> &'static str {
Merge branch 'worktree-agent-ab2e39e11a6493412'340 SECTIONS
API and MCP server in Rust; a public index at the API root341 .iter()
Merge branch 'worktree-agent-ab2e39e11a6493412'342 .find(|(_, _, ops)| ops.contains(&op))
343 .map_or("Repositories", |(name, _, _)| name)
344}
345
346/// What an operation's page is called, as a short sentence.
347fn title(op: Op) -> &'static str {
348 match op {
349 Op::Whoami => "Get the current user",
350 Op::CreateWorkspace => "Create a workspace",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look351 Op::DeleteWorkspace => "Delete a workspace",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily352 Op::UpdateWorkspace => "Update a workspace",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look353 Op::ListEmails => "List your email addresses",
354 Op::AddEmail => "Add an email address",
355 Op::RemoveEmail => "Remove an email address",
356 Op::UpdateEmailSettings => "Change your email settings",
357 Op::ListInvites => "List your invites",
358 Op::CreateInvite => "Create an invite",
359 Op::RevokeInvite => "Revoke an invite",
360 Op::ListWorkspaceInvites => "List a workspace's invites",
361 Op::InviteMember => "Invite someone to a workspace",
362 Op::RevokeWorkspaceInvite => "Revoke a workspace's invite",
363 Op::TransferRepo => "Transfer a repository",
364 Op::RenameRepo => "Rename a repository",
365 Op::RenameBranch => "Rename a branch",
366 Op::ArchiveRepo => "Archive a repository",
367 Op::UnarchiveRepo => "Unarchive a repository",
368 Op::SetRepoVisibility => "Change a repository's visibility",
369 Op::DeleteRepo => "Delete a repository",
370 Op::ListDeletedRepos => "List recently deleted repositories",
371 Op::RestoreRepo => "Restore a deleted repository",
372 Op::PurgeRepo => "Purge a deleted repository",
Merge branch 'worktree-agent-ab2e39e11a6493412'373 Op::ListRepos => "List repositories",
374 Op::GetRepo => "Get a repository",
375 Op::CreateRepo => "Create a repository",
376 Op::UpdateRepo => "Update a repository",
377 Op::GetRepoSettings => "Get repository settings",
378 Op::UpdateRepoSettings => "Update repository settings",
Fast pages, required checks on the branch, self-hosted runners, honest incidents379 Op::ListCheckNames => "List check names",
Merge branch 'worktree-agent-ab2e39e11a6493412'380 Op::GetMergeQueue => "Get the merge queue",
381 Op::MessageAgent => "Message an agent",
382 Op::AnswerMessage => "Answer a message",
383 Op::TakeMessages => "Take new messages",
Agents and memory, checks and conflicts, profiles, slug renames, custom domains384 Op::Remember => "Remember something",
385 Op::Recall => "Recall memory",
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API386 Op::SearchContext => "Search the context hub",
387 Op::GetEntity => "Get a catalog entry",
Search across all of g1t, Explore, and a command palette388 Op::Search => "Search g1t",
Merge branch 'worktree-agent-ab2e39e11a6493412'389 Op::ListIssues => "List issues",
390 Op::GetIssue => "Get an issue",
391 Op::CreateIssue => "Create an issue",
392 Op::UpdateIssue => "Update an issue",
393 Op::CloseIssue => "Close an issue",
394 Op::ReopenIssue => "Reopen an issue",
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent395 Op::AssignIssue => "Assign an issue to g1t",
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step396 Op::Delegate => "Put an agent on it",
Merge branch 'worktree-agent-ab2e39e11a6493412'397 Op::PlanWork => "Plan work",
398 Op::GetPlan => "Get a plan",
399 Op::ApplyPlan => "Apply a plan",
400 Op::ListLabels => "List labels",
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar401 Op::CreateLabel => "Create a label",
402 Op::UpdateLabel => "Update a label",
403 Op::DeleteLabel => "Delete a label",
404 Op::AddDefaultLabels => "Add the default labels",
405 Op::ListIssueLabels => "List an issue's labels",
406 Op::AddIssueLabels => "Add labels to an issue",
407 Op::SetIssueLabels => "Set an issue's labels",
408 Op::RemoveIssueLabels => "Remove labels from an issue",
409 Op::ListMilestones => "List milestones",
410 Op::GetMilestone => "Get a milestone",
411 Op::CreateMilestone => "Create a milestone",
412 Op::UpdateMilestone => "Update a milestone",
413 Op::DeleteMilestone => "Delete a milestone",
414 Op::UpdatePullRequest => "Update a pull request",
Merge branch 'worktree-agent-ab2e39e11a6493412'415 Op::AddComment => "Add a comment",
416 Op::ReviewPullRequest => "Review a pull request",
417 Op::ListPullRequests => "List pull requests",
418 Op::GetPullRequest => "Get a pull request",
419 Op::CreatePullRequest => "Create a pull request",
420 Op::RecordSession => "Record session entries",
421 Op::ReadSession => "Read a session",
422 Op::MarkPullRequestReady => "Mark a pull request ready",
423 Op::ClosePullRequest => "Close a pull request",
424 Op::GetPullRequestChanges => "Get a pull request's changes",
425 Op::MergePullRequest => "Merge a pull request",
426 Op::ListEvents => "List repository events",
427 Op::ListIntegrations => "List integrations",
428 Op::ConnectIntegration => "Connect an integration",
429 Op::DisconnectIntegration => "Disconnect an integration",
430 Op::TestIntegration => "Test an integration",
431 Op::GetContext => "Look up a ticket",
432 Op::ImportIssue => "Import an issue",
433 Op::GetModelRoutes => "Get model routes",
434 Op::SetModelRoutes => "Set model routes",
435 Op::ListWebhooks => "List webhooks",
436 Op::CreateWebhook => "Create a webhook",
437 Op::UpdateWebhook => "Update a webhook",
438 Op::DeleteWebhook => "Delete a webhook",
439 Op::PingWebhook => "Ping a webhook",
440 Op::ListWebhookDeliveries => "List webhook deliveries",
441 Op::RedeliverWebhook => "Redeliver a webhook delivery",
442 Op::ListWorkflows => "List workflows",
443 Op::ListWorkflowRuns => "List workflow runs",
444 Op::GetWorkflowRun => "Get a workflow run",
445 Op::GetJobLogs => "Get a job's log",
446 Op::DispatchWorkflow => "Run a workflow",
447 Op::CancelWorkflowRun => "Cancel a workflow run",
448 Op::RerunWorkflowRun => "Re-run a workflow run",
449 Op::UpdateWorkflow => "Turn a workflow on or off",
450 Op::ListActionsSecrets => "List secrets",
451 Op::SetActionsSecret => "Set a secret",
452 Op::DeleteActionsSecret => "Delete a secret",
453 Op::ListActionsVariables => "List variables",
454 Op::SetActionsVariable => "Set a variable",
455 Op::DeleteActionsVariable => "Delete a variable",
Fast pages, required checks on the branch, self-hosted runners, honest incidents456 Op::ListRunners => "List self-hosted runners",
457 Op::ListRunnerGroups => "List runner groups",
458 Op::GetRunnerSettings => "Get runner settings",
459 Op::CreateRunnerRegistrationToken => "Create a runner registration token",
460 Op::RemoveRunner => "Remove a self-hosted runner",
461 Op::CreateRunnerGroup => "Create a runner group",
462 Op::UpdateRunnerGroup => "Change a runner group",
463 Op::DeleteRunnerGroup => "Delete a runner group",
464 Op::UpdateRunnerSettings => "Change runner settings",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look465 Op::ListCollaborators => "List who has access",
466 Op::AddCollaborator => "Add a collaborator",
467 Op::UpdateCollaborator => "Change a collaborator's role",
468 Op::RemoveCollaborator => "Remove a collaborator",
469 Op::GetCollaboratorPermission => "Get someone's permission",
470 Op::ListRepoInvitations => "List a repository's invitations",
471 Op::RevokeRepoInvitation => "Revoke a repository invitation",
472 Op::ListMyRepoInvitations => "List your repository invitations",
473 Op::AcceptRepoInvitation => "Accept a repository invitation",
474 Op::DeclineRepoInvitation => "Decline a repository invitation",
475 Op::SetBasePermission => "Set the base permission",
476 Op::ListOutsideCollaborators => "List outside collaborators",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily477 Op::ListSecurityAlerts => "List security alerts",
478 Op::DismissSecurityAlert => "Dismiss a security alert",
479 Op::ReopenSecurityAlert => "Reopen a security alert",
API: notifications over REST and MCP, with notifications scopes480 Op::ListNotifications => "List notifications",
481 Op::MarkNotificationsRead => "Mark notifications read",
482 Op::GetNotificationThread => "Get a thread",
483 Op::MarkThreadRead => "Mark a thread read",
484 Op::MarkThreadDone => "Mark a thread done",
485 Op::SaveThread => "Save a thread",
486 Op::SnoozeThread => "Snooze a thread",
487 Op::GetThreadSubscription => "Get a thread subscription",
488 Op::SetThreadSubscription => "Set a thread subscription",
489 Op::DeleteThreadSubscription => "Unsubscribe from a thread",
490 Op::GetRepoSubscription => "Get how you watch a repository",
491 Op::SetRepoSubscription => "Watch a repository",
492 Op::DeleteRepoSubscription => "Stop watching a repository",
493 Op::ListWatchedRepos => "List repositories you watch",
API: pinned projects over REST and MCP494 Op::ListPinnedProjects => "List your pinned projects",
495 Op::PinProject => "Pin a project",
496 Op::UnpinProject => "Unpin a project",
497 Op::ReorderPinnedProjects => "Reorder your pinned projects",
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar498 Op::ListTeams => "List teams",
499 Op::GetTeam => "Get a team",
500 Op::CreateTeam => "Create a team",
501 Op::UpdateTeam => "Update a team",
502 Op::DeleteTeam => "Delete a team",
503 Op::ListTeamMembers => "List a team's members",
504 Op::SetTeamMember => "Add or change a team member",
505 Op::RemoveTeamMember => "Remove a team member",
506 Op::ListChildTeams => "List child teams",
507 Op::ListTeamRepos => "List a team's repositories",
508 Op::SetTeamRepo => "Give a team a role on a repository",
509 Op::RemoveTeamRepo => "Remove a team from a repository",
510 Op::SetTeamReviewAssignment => "Set a team's review assignment",
511 Op::ListUserTeams => "List someone's teams",
512 Op::RequestReviewers => "Request reviewers",
513 Op::RemoveRequestedReviewers => "Remove requested reviewers",
514 Op::GetCodeownersErrors => "List CODEOWNERS errors",
515 Op::Security(op) => op.title(),
API and MCP server in Rust; a public index at the API root516 }
517}
518
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look519/// Why an operation can be refused with `402 payment_required`, if it
520/// can: the ones that start an agent, when the workspace has no credit,
521/// and the ones that make a repository private in a workspace, when a free
522/// workspace's private storage has no room for it.
523fn may_need_payment(op: Op) -> Option<&'static str> {
524 match op {
525 Op::AssignIssue | Op::PlanWork | Op::ApplyPlan => Some("The workspace has no agent credit."),
526 Op::UpdateRepo | Op::SetRepoVisibility | Op::TransferRepo => Some(
527 "A free workspace's private storage has no room for this private repository.",
528 ),
529 _ => None,
530 }
Merge branch 'worktree-agent-ab2e39e11a6493412'531}
532
533/// What the reference says beyond each operation's own description, keyed
534/// by operation id, written by hand from what the services return: `notes`
535/// (Markdown, added to the description) and example `params` (path),
536/// `query`, `request` (body) and `response`.
537const REFERENCE: &str = include_str!("reference.json");
538
539fn examples() -> Map<String, Value> {
540 match serde_json::from_str(REFERENCE) {
541 Ok(Value::Object(examples)) => examples,
542 _ => Map::new(),
API and MCP server in Rust; a public index at the API root543 }
544}
545
Agents as a team: lifecycle, merge queue, billing and a new shell546/// `/repos/:owner/:name` as OpenAPI writes it: `/repos/{owner}/{name}`.
API and MCP server in Rust; a public index at the API root547fn openapi_path(route: &Route) -> String {
548 route
549 .path
550 .split('/')
551 .map(|segment| match segment.strip_prefix(':') {
552 Some(name) => format!("{{{name}}}"),
553 None => segment.to_owned(),
554 })
555 .collect::<Vec<_>>()
556 .join("/")
557}
558
559fn error_response(description: &str) -> Value {
560 json!({
561 "description": description,
562 "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } },
563 })
564}
565
Merge branch 'worktree-agent-ab2e39e11a6493412'566/// A parameter in the path or the query, described by the operation's
567/// input schema where it has the same name.
568fn parameter(name: &str, place: &str, required: bool, schema: Option<&Value>) -> Value {
569 let mut schema = schema.cloned().unwrap_or_else(|| json!({ "type": "string" }));
570 let description = match name {
571 "owner" => Some(Value::from("The workspace that owns the repository.")),
572 "name" => Some(Value::from("The repository's name.")),
573 _ => schema.as_object_mut().and_then(|schema| schema.remove("description")),
574 };
575 let mut parameter = json!({
576 "name": name,
577 "in": place,
578 "required": required,
579 "schema": schema,
580 });
581 if let Some(description) = description {
582 parameter["description"] = description;
583 }
584 parameter
585}
586
587/// The operation id of a route. An operation reached at a workspace's
588/// address as well as a repository's is documented once for each, with its
589/// own id; GitHub's alternative addresses for one operation keep GitHub's
590/// names.
591fn operation_id(route: &Route) -> String {
592 let op = route.op;
593 let base = match (route.method, route.path.rsplit('/').next().unwrap_or_default()) {
594 ("PUT", "enable") => "enable_workflow".to_owned(),
595 ("PUT", "disable") => "disable_workflow".to_owned(),
596 ("POST", "rerun-failed-jobs") => "rerun_failed_jobs".to_owned(),
597 ("PATCH", ":setting") => "update_actions_variable".to_owned(),
598 ("GET", "runs") if route.path.contains("/workflows/:workflow/") => "list_runs_of_workflow".to_owned(),
API: notifications over REST and MCP, with notifications scopes599 // One repository's notifications, and an issue's subscription by
600 // its number rather than a thread's id.
601 (_, "notifications") if route.path.starts_with("/repos/") => match op {
602 Op::ListNotifications => "list_repo_notifications".to_owned(),
603 _ => "mark_repo_notifications_read".to_owned(),
604 },
605 (method, "subscription") if route.path.contains("/issues/:number/") => match method {
606 "GET" => "get_issue_subscription".to_owned(),
607 "PUT" => "set_issue_subscription".to_owned(),
608 _ => "delete_issue_subscription".to_owned(),
609 },
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar610 // One label off an issue, by its name in the path.
611 ("DELETE", ":label") if route.path.contains("/issues/:number/") => "remove_issue_label".to_owned(),
API: notifications over REST and MCP, with notifications scopes612 ("DELETE", "saved") => "unsave_thread".to_owned(),
613 ("DELETE", "snooze") => "unsnooze_thread".to_owned(),
Merge branch 'worktree-agent-ab2e39e11a6493412'614 _ => op.name().to_owned(),
615 };
616 if route.path.starts_with("/workspaces/") && ROUTES.iter().any(|other| other.op == op && other.path.starts_with("/repos/")) {
617 format!("{base}_for_workspace")
618 } else {
619 base
620 }
621}
622
623/// The summary of a route: its operation's title, or for one of GitHub's
624/// alternative addresses, what that address does.
625fn summary(route: &Route, id: &str) -> String {
626 let base = match id.trim_end_matches("_for_workspace") {
627 "enable_workflow" => "Turn a workflow on",
628 "disable_workflow" => "Turn a workflow off",
629 "rerun_failed_jobs" => "Re-run failed jobs",
630 "update_actions_variable" => "Update a variable",
631 "list_runs_of_workflow" => "List a workflow's runs",
API: notifications over REST and MCP, with notifications scopes632 "list_repo_notifications" => "List a repository's notifications",
633 "mark_repo_notifications_read" => "Mark a repository's notifications read",
634 "get_issue_subscription" => "Get your subscription to an issue",
635 "set_issue_subscription" => "Subscribe to an issue",
636 "delete_issue_subscription" => "Unsubscribe from an issue",
637 "unsave_thread" => "Unsave a thread",
638 "unsnooze_thread" => "Bring a snoozed thread back",
Merge branch 'worktree-agent-ab2e39e11a6493412'639 _ => title(route.op),
640 };
641 if id.ends_with("_for_workspace") {
642 format!("{base} for a workspace")
643 } else {
644 base.to_owned()
645 }
646}
647
API and MCP server in Rust; a public index at the API root648fn operation(route: &Route) -> Value {
649 let op = route.op;
650 let path_params: Vec<&str> = route.params().collect();
651 // `owner` and `name` in the path stand for the operation's `repo` input.
652 let covered = |name: &str| name == "repo" || path_params.contains(&name);
Merge branch 'worktree-agent-ab2e39e11a6493412'653 let all_properties = op.properties();
654 let mut properties = all_properties.clone();
API and MCP server in Rust; a public index at the API root655 properties.retain(|name, _| !covered(name));
656 let required: Vec<String> = op
657 .required()
658 .into_iter()
659 .filter(|name| !covered(name))
660 .collect();
661
662 let mut parameters: Vec<Value> = path_params
663 .iter()
Merge branch 'worktree-agent-ab2e39e11a6493412'664 .map(|name| parameter(name, "path", true, all_properties.get(*name)))
API and MCP server in Rust; a public index at the API root665 .collect();
666 let mut body = Value::Null;
667 if route.method == "GET" {
668 for (name, key) in route.query {
Merge branch 'worktree-agent-ab2e39e11a6493412'669 parameters.push(parameter(
670 name,
671 "query",
672 required.iter().any(|required| required == key),
673 properties.get(*key),
674 ));
API and MCP server in Rust; a public index at the API root675 }
676 } else if !properties.is_empty() {
677 let mut schema = json!({ "type": "object", "properties": properties });
678 if !required.is_empty() {
679 schema["required"] = json!(required);
680 }
681 body = json!({
682 "required": !required.is_empty(),
683 "content": { "application/json": { "schema": schema } },
684 });
685 }
686
Merge branch 'worktree-agent-ab2e39e11a6493412'687 let id = operation_id(route);
688 let mut responses = Map::new();
689 responses.insert(
690 "200".into(),
691 json!({
692 "description": "Success.",
693 "content": { "application/json": { "schema": {} } },
694 }),
695 );
696 responses.insert(
697 "401".into(),
698 error_response("A token is required, or the one sent is not valid."),
699 );
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look700 if let Some(reason) = may_need_payment(op) {
701 responses.insert("402".into(), error_response(reason));
Merge branch 'worktree-agent-ab2e39e11a6493412'702 }
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step703 responses.insert(
704 "403".into(),
705 error_response("Signed in, but not allowed to do this: the role you have is not enough, or the token lacks the scope it needs, which `needed_scope` names."),
706 );
Search across all of g1t, Explore, and a command palette707 if !matches!(op, Op::Whoami | Op::ListRepos | Op::Search) {
Merge branch 'worktree-agent-ab2e39e11a6493412'708 responses.insert("404".into(), error_response("It does not exist, or you cannot see it."));
709 }
710 if route.method != "GET" {
711 responses.insert(
712 "409".into(),
713 error_response("The request conflicts with the current state."),
714 );
715 }
716 if op != Op::Whoami {
717 responses.insert("422".into(), error_response("The input is not valid."));
718 }
719 // Public data can be read without a token; everything else needs one.
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step720 let scope: Vec<&str> = scope_for(op.name()).map(|scope| scope.as_str()).into_iter().collect();
Merge branch 'worktree-agent-ab2e39e11a6493412'721 let security = if op.needs_user() {
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step722 json!([{ "token": scope }])
Webhooks: every event, to your own addresses, signed and retried723 } else {
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step724 json!([{ "token": scope }, {}])
Webhooks: every event, to your own addresses, signed and retried725 };
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step726 let (tool, action) = crate::tools::TOOLS
727 .iter()
728 .find_map(|tool| {
729 tool.actions
730 .iter()
731 .find(|action| action.op == op)
732 .map(|action| (tool.name, action.name))
733 })
734 .unwrap_or_default();
API and MCP server in Rust; a public index at the API root735 let mut described = json!({
Webhooks: every event, to your own addresses, signed and retried736 "operationId": id,
API and MCP server in Rust; a public index at the API root737 "tags": [tag(op)],
Merge branch 'worktree-agent-ab2e39e11a6493412'738 "summary": summary(route, &id),
API and MCP server in Rust; a public index at the API root739 "description": op.description(),
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step740 "x-operation": op.name(),
741 "x-mcp-tool": tool,
742 "x-mcp-action": action,
743 "x-scope": scope.first().copied(),
Merge branch 'worktree-agent-ab2e39e11a6493412'744 "security": security,
API and MCP server in Rust; a public index at the API root745 "parameters": parameters,
Merge branch 'worktree-agent-ab2e39e11a6493412'746 "responses": responses,
API and MCP server in Rust; a public index at the API root747 });
748 if !body.is_null() {
749 described["requestBody"] = body;
750 }
751 described
752}
753
754/// Entries for device sign-in, which is not an operation.
755fn onboarding() -> Map<String, Value> {
756 let paths = json!({
Agents as a team: lifecycle, merge queue, billing and a new shell757 "/device/code": {
API and MCP server in Rust; a public index at the API root758 "post": {
759 "operationId": "device_code",
760 "tags": ["Accounts"],
761 "summary": "Start signing in",
Agents as a team: lifecycle, merge queue, billing and a new shell762 "description": "Begins a device sign-in. Show the person `verification_uri_complete` and have them open it in a browser, where they sign in or register and approve the code. Then poll `/device/token`.",
API and MCP server in Rust; a public index at the API root763 "security": [],
764 "requestBody": {
765 "content": { "application/json": { "schema": {
766 "type": "object",
767 "properties": {
768 "client_name": {
769 "type": "string",
770 "description": "What is asking, shown to the person approving. For example, Claude Code.",
771 },
772 },
773 } } },
774 },
775 "responses": { "200": {
776 "description": "The codes for this sign-in.",
777 "content": { "application/json": { "schema": {
778 "type": "object",
779 "properties": {
Agents as a team: lifecycle, merge queue, billing and a new shell780 "device_code": { "type": "string", "description": "Secret. Send it to /device/token." },
API and MCP server in Rust; a public index at the API root781 "user_code": { "type": "string", "description": "Shown to the person, like WDJB-MJHT." },
782 "verification_uri": { "type": "string" },
783 "verification_uri_complete": {
784 "type": "string",
785 "description": "The link to give the person; it carries the code.",
786 },
787 "expires_in": { "type": "integer", "description": "Seconds until the codes expire." },
788 "interval": { "type": "integer", "description": "Seconds to wait between polls." },
789 },
790 } } },
791 } },
792 },
793 },
Agents as a team: lifecycle, merge queue, billing and a new shell794 "/device/token": {
API and MCP server in Rust; a public index at the API root795 "post": {
796 "operationId": "device_token",
797 "tags": ["Accounts"],
798 "summary": "Finish signing in",
799 "description": "Asks whether the person has approved. Poll no faster than the interval. The token is returned once.",
800 "security": [],
801 "requestBody": {
802 "required": true,
803 "content": { "application/json": { "schema": {
804 "type": "object",
805 "required": ["device_code"],
806 "properties": { "device_code": { "type": "string" } },
807 } } },
808 },
809 "responses": { "200": {
810 "description": "The state of the sign-in.",
811 "content": { "application/json": { "schema": {
812 "type": "object",
813 "required": ["status"],
814 "properties": {
815 "status": { "type": "string", "enum": ["pending", "approved", "denied", "expired"] },
816 "token": { "type": "string", "description": "Present when approved." },
817 "username": { "type": "string" },
818 "verified": {
819 "type": "boolean",
820 "description": "Whether the account's email is confirmed.",
821 },
822 },
823 } } },
824 } },
825 },
826 },
827 });
828 match paths {
829 Value::Object(paths) => paths,
830 _ => Map::new(),
831 }
832}
833
Merge branch 'worktree-agent-ab2e39e11a6493412'834
835/// Puts each operation's examples, where it has them, into its request
836/// and response. Path and query values go under `x-example-params` and
837/// `x-example-query`, which tools that build a request can use.
838fn attach_examples(paths: &mut Map<String, Value>) {
839 let examples = examples();
840 for methods in paths.values_mut() {
841 let Some(methods) = methods.as_object_mut() else { continue };
842 for operation in methods.values_mut() {
843 let id = operation["operationId"].as_str().unwrap_or_default().to_owned();
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step844 let name = operation["x-operation"].as_str().unwrap_or_default().to_owned();
845 let Some(example) = examples.get(&id).or_else(|| examples.get(&name)) else {
Merge branch 'worktree-agent-ab2e39e11a6493412'846 continue;
847 };
848 if let Some(notes) = example.get("notes").and_then(Value::as_str) {
849 let description = operation["description"].as_str().unwrap_or_default();
850 operation["description"] = json!(format!("{description}\n\n{notes}"));
851 }
852 if let Some(response) = example.get("response") {
853 let content = &mut operation["responses"]["200"]["content"]["application/json"];
854 if content.is_object() {
855 content["example"] = response.clone();
856 }
857 }
858 if let Some(request) = example.get("request") {
859 let content = &mut operation["requestBody"]["content"]["application/json"];
860 if content.is_object() {
861 content["example"] = request.clone();
862 }
863 }
864 for (key, extension) in [("params", "x-example-params"), ("query", "x-example-query")] {
865 if let Some(values) = example.get(key) {
866 operation[extension] = values.clone();
867 }
868 }
869 }
870 }
871}
872
API and MCP server in Rust; a public index at the API root873pub fn document() -> Value {
874 let mut paths = onboarding();
875 for route in ROUTES {
876 let entry = paths
877 .entry(openapi_path(route))
878 .or_insert_with(|| json!({}));
879 entry[route.method.to_lowercase()] = operation(route);
880 }
Merge branch 'worktree-agent-ab2e39e11a6493412'881 attach_examples(&mut paths);
882 let tags: Vec<Value> = SECTIONS
883 .iter()
884 .map(|(name, description, ops)| {
885 json!({
886 "name": name,
887 "description": description,
888 // The section's operations in reading order, by MCP tool name.
889 "x-tools": ops.iter().map(|op| op.name()).collect::<Vec<_>>(),
890 })
891 })
892 .collect();
893 let codes = ["unauthenticated", "payment_required", "forbidden", "not_found", "conflict", "invalid"];
API and MCP server in Rust; a public index at the API root894 json!({
895 "openapi": "3.1.0",
896 "info": {
897 "title": "g1t API",
898 "version": "1",
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API899 "description": "The REST API for g1t, a git forge built for agents. The same operations are available to agents as MCP tools at https://mcp.g1t.sh. Every name in a request or response body is `snake_case`; names you chose, such as a workflow's inputs or a secret's name, are returned as you wrote them.",
API and MCP server in Rust; a public index at the API root900 "license": { "name": "MIT", "identifier": "MIT" },
901 },
902 "servers": [{ "url": "https://api.g1t.sh" }],
903 "security": [{ "token": [] }, {}],
Merge branch 'worktree-agent-ab2e39e11a6493412'904 "tags": tags,
API and MCP server in Rust; a public index at the API root905 "paths": paths,
906 "components": {
907 "securitySchemes": {
908 "token": {
909 "type": "http",
910 "scheme": "bearer",
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step911 "description": "An access token, `g1t_…`. Public data needs none. Each operation names the scope a token needs for it; see https://docs.g1t.sh/guides/authentication/#scopes.",
API and MCP server in Rust; a public index at the API root912 },
913 },
914 "schemas": {
915 "Error": {
916 "type": "object",
917 "required": ["error"],
918 "properties": {
919 "error": {
920 "type": "object",
921 "required": ["code", "message"],
922 "properties": {
Merge branch 'worktree-agent-ab2e39e11a6493412'923 "code": { "type": "string", "enum": codes },
API and MCP server in Rust; a public index at the API root924 "message": { "type": "string" },
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step925 "needed_scope": {
926 "type": "string",
927 "description": "On a 403 for an access token without the scope the call needs: that scope, such as `issues:write`.",
928 },
API and MCP server in Rust; a public index at the API root929 },
930 },
931 },
932 },
933 },
934 },
935 })
936}
937
938#[cfg(test)]
939mod tests {
940 use super::*;
941
942 #[test]
943 fn every_route_is_documented_once() {
944 let document = document();
945 let mut ids = Vec::new();
946 for (_, methods) in document["paths"].as_object().unwrap() {
947 for (_, operation) in methods.as_object().unwrap() {
948 ids.push(operation["operationId"].as_str().unwrap().to_owned());
949 }
950 }
951 for op in Op::ALL {
952 assert_eq!(
953 ids.iter().filter(|id| *id == op.name()).count(),
954 1,
955 "{}",
956 op.name()
957 );
958 }
Webhooks: every event, to your own addresses, signed and retried959 let mut unique = ids.clone();
960 unique.sort();
961 unique.dedup();
962 assert_eq!(unique.len(), ids.len(), "operation ids repeat");
API and MCP server in Rust; a public index at the API root963 }
964
965 #[test]
966 fn path_and_query_inputs_are_not_repeated_in_the_body() {
967 let document = document();
Agents as a team: lifecycle, merge queue, billing and a new shell968 let merge = &document["paths"]["/repos/{owner}/{name}/pulls/{number}/merge"]["post"];
API and MCP server in Rust; a public index at the API root969 let body = &merge["requestBody"]["content"]["application/json"]["schema"]["properties"];
970 assert!(body.get("keep_issue_open").is_some());
971 assert!(body.get("repo").is_none() && body.get("number").is_none());
Agents as a team: lifecycle, merge queue, billing and a new shell972 let list = &document["paths"]["/repos"]["get"];
API and MCP server in Rust; a public index at the API root973 assert_eq!(list["parameters"][0]["name"], "q");
974 assert!(list.get("requestBody").is_none());
975 }
976
977 #[test]
Merge branch 'worktree-agent-ab2e39e11a6493412'978 fn every_operation_is_in_one_section() {
979 for op in Op::ALL {
980 let sections = SECTIONS
981 .iter()
982 .filter(|(_, _, ops)| ops.contains(&op))
983 .count();
984 assert_eq!(sections, 1, "{}", op.name());
985 }
986 }
987
988 #[test]
API and MCP server in Rust; a public index at the API root989 fn titles_read_as_sentences() {
Merge branch 'worktree-agent-ab2e39e11a6493412'990 assert_eq!(title(Op::CreateIssue), "Create an issue");
API and MCP server in Rust; a public index at the API root991 assert_eq!(title(Op::Whoami), "Get the current user");
992 }
Merge branch 'worktree-agent-ab2e39e11a6493412'993
994 #[test]
995 fn every_operation_has_an_example_response() {
996 let examples = examples();
997 assert!(!examples.is_empty(), "reference.json does not parse");
998 let document = document();
999 let mut known = Vec::new();
1000 for (path, methods) in document["paths"].as_object().unwrap() {
1001 for (method, operation) in methods.as_object().unwrap() {
1002 known.push(operation["operationId"].as_str().unwrap().to_owned());
1003 let example = &operation["responses"]["200"]["content"]["application/json"]["example"];
1004 assert!(!example.is_null(), "{method} {path} has no example response");
1005 }
1006 }
1007 for id in examples.keys() {
1008 assert!(known.contains(id), "reference.json names {id}, which is not an operation");
1009 }
1010 }
1011
1012 #[test]
1013 fn example_requests_send_only_what_the_body_takes() {
1014 let document = document();
1015 for (path, methods) in document["paths"].as_object().unwrap() {
1016 for (method, operation) in methods.as_object().unwrap() {
1017 let content = &operation["requestBody"]["content"]["application/json"];
1018 let Some(example) = content["example"].as_object() else { continue };
1019 let properties = &content["schema"]["properties"];
1020 for key in example.keys() {
1021 assert!(!properties[key].is_null(), "{method} {path}: {key} is not in the body");
1022 }
1023 }
1024 }
1025 }
1026
1027 /// The docs site's copy of the document. Run with `G1T_WRITE_OPENAPI=1`
1028 /// to rewrite it after changing an operation.
1029 #[test]
1030 fn the_docs_copy_is_current() {
1031 let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../docs/src/data/openapi.json");
1032 let current = serde_json::to_string_pretty(&document()).unwrap() + "\n";
1033 if std::env::var_os("G1T_WRITE_OPENAPI").is_some() {
1034 std::fs::write(path, &current).unwrap();
1035 return;
1036 }
1037 let copy = std::fs::read_to_string(path).unwrap_or_default().replace("\r\n", "\n");
1038 assert!(
1039 copy == current,
1040 "apps/docs/src/data/openapi.json is out of date: run G1T_WRITE_OPENAPI=1 cargo test -p g1t-api openapi"
1041 );
1042 }
API reference: no example reads as a real secret1043
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1044 /// The reference shows responses as they are sent: `snake_case`.
1045 #[test]
1046 fn example_responses_are_snake_case() {
1047 let document = document();
1048 for (path, methods) in document["paths"].as_object().unwrap() {
1049 for (method, operation) in methods.as_object().unwrap() {
1050 let example = &operation["responses"]["200"]["content"]["application/json"]["example"];
1051 let leaked = g1t_kit::wire::camel_case_keys(example);
1052 assert!(leaked.is_empty(), "{method} {path} shows {leaked:?}");
1053 }
1054 }
1055 }
1056
API reference: no example reads as a real secret1057 /// Examples never hold anything that reads as a real credential, which
1058 /// secret scanners rightly flag in a public repository: they end in `…`
1059 /// after the prefix, as `whsec_…` and `g1t_…` do.
1060 #[test]
1061 fn examples_hold_no_real_looking_secrets() {
Fast pages, required checks on the branch, self-hosted runners, honest incidents1062 let prefixes = ["whsec_", "g1t_", "g1tr_", "g1trt_", "sk_live_", "sk_test_", "ghp_", "github_pat_", "xoxb-", "AKIA"];
API reference: no example reads as a real secret1063 for (line, text) in REFERENCE.lines().enumerate() {
1064 for prefix in prefixes {
1065 let mut rest = text;
1066 while let Some(at) = rest.find(prefix) {
1067 let after = &rest[at + prefix.len()..];
1068 let run = after.chars().take_while(|c| c.is_ascii_alphanumeric()).count();
1069 assert!(
1070 run < 12,
1071 "reference.json line {}: `{prefix}` followed by {run} characters reads as a real secret; write `{prefix}…`",
1072 line + 1
1073 );
1074 rest = after;
1075 }
1076 }
1077 }
1078 }
API and MCP server in Rust; a public index at the API root1079}

This file's history is long; its oldest lines are credited to the oldest commit read.