@lanes-sh/link 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +144 -0
  3. package/bin/lanes +42 -0
  4. package/instructions/agents/lanes-link-scout.md +73 -0
  5. package/instructions/skills/lanes-link/SKILL.md +187 -0
  6. package/package.json +95 -0
  7. package/src/audit/chain.ts +278 -0
  8. package/src/audit/conformance.ts +374 -0
  9. package/src/audit/fanout.ts +97 -0
  10. package/src/audit/index.ts +218 -0
  11. package/src/audit/stdout.ts +60 -0
  12. package/src/auth/index.ts +220 -0
  13. package/src/auth/oauth/metadata.ts +75 -0
  14. package/src/auth/oauth/server.ts +385 -0
  15. package/src/auth/oauth/store.ts +215 -0
  16. package/src/auth/oidc.ts +206 -0
  17. package/src/auth/remote.ts +72 -0
  18. package/src/cli/argv.ts +94 -0
  19. package/src/cli/callback-page.ts +256 -0
  20. package/src/cli/commands/connect/accounts.ts +94 -0
  21. package/src/cli/commands/connect/authorise.ts +298 -0
  22. package/src/cli/commands/connect/client.ts +284 -0
  23. package/src/cli/commands/connect/index.ts +398 -0
  24. package/src/cli/commands/connect/outcome.ts +119 -0
  25. package/src/cli/commands/connect/requirements.ts +103 -0
  26. package/src/cli/commands/connect/scopes-gate.ts +146 -0
  27. package/src/cli/commands/connect/settle.ts +136 -0
  28. package/src/cli/commands/connect/setup.ts +276 -0
  29. package/src/cli/commands/mcp/assets.ts +189 -0
  30. package/src/cli/commands/mcp/harnesses.ts +143 -0
  31. package/src/cli/commands/mcp/list.ts +70 -0
  32. package/src/cli/commands/mcp/register.ts +199 -0
  33. package/src/cli/commands/mcp/stdio.ts +57 -0
  34. package/src/cli/commands/mcp.ts +22 -0
  35. package/src/cli/commands/operate/attach.ts +121 -0
  36. package/src/cli/commands/operate/audit.ts +119 -0
  37. package/src/cli/commands/operate/inspect.ts +355 -0
  38. package/src/cli/commands/operate/outputs.ts +206 -0
  39. package/src/cli/commands/operate/policy.ts +80 -0
  40. package/src/cli/commands/operate/serve.ts +55 -0
  41. package/src/cli/commands/operate/status.ts +133 -0
  42. package/src/cli/commands/operate/token.ts +70 -0
  43. package/src/cli/commands/operate.ts +27 -0
  44. package/src/cli/commands/owner/memory.ts +110 -0
  45. package/src/cli/commands/owner/shared.ts +125 -0
  46. package/src/cli/commands/owner/skills.ts +92 -0
  47. package/src/cli/commands/owner/vault.ts +140 -0
  48. package/src/cli/commands/owner.ts +46 -0
  49. package/src/cli/commands/profile/removal.ts +278 -0
  50. package/src/cli/commands/profile/remove.ts +315 -0
  51. package/src/cli/commands/profile.ts +156 -0
  52. package/src/cli/commands/secrets.ts +176 -0
  53. package/src/cli/commands/setup.ts +150 -0
  54. package/src/cli/commands/target.ts +310 -0
  55. package/src/cli/config-edit.ts +397 -0
  56. package/src/cli/endpoint-url.ts +77 -0
  57. package/src/cli/identity.ts +109 -0
  58. package/src/cli/lanes.ts +78 -0
  59. package/src/cli/main.ts +333 -0
  60. package/src/cli/oauth-error.ts +13 -0
  61. package/src/cli/oauth-exchange.ts +146 -0
  62. package/src/cli/oauth.ts +354 -0
  63. package/src/cli/output.ts +184 -0
  64. package/src/cli/prompt.ts +180 -0
  65. package/src/cli/publish.ts +185 -0
  66. package/src/cli/runtime/discovery.ts +123 -0
  67. package/src/cli/runtime/open.ts +342 -0
  68. package/src/cli/runtime/registry.ts +185 -0
  69. package/src/cli/runtime/select.ts +124 -0
  70. package/src/cli/runtime.ts +34 -0
  71. package/src/cli/scopes.ts +63 -0
  72. package/src/cli/usage.ts +111 -0
  73. package/src/cli/version.ts +25 -0
  74. package/src/connectivity/auth/README.md +36 -0
  75. package/src/connectivity/auth/api-key/index.ts +43 -0
  76. package/src/connectivity/auth/authorize.ts +49 -0
  77. package/src/connectivity/auth/basic/index.ts +68 -0
  78. package/src/connectivity/auth/bearer/index.ts +13 -0
  79. package/src/connectivity/auth/credential.ts +19 -0
  80. package/src/connectivity/auth/header/index.ts +18 -0
  81. package/src/connectivity/auth/index.ts +35 -0
  82. package/src/connectivity/auth/none/index.ts +12 -0
  83. package/src/connectivity/auth/oauth-authcode/broker.ts +261 -0
  84. package/src/connectivity/auth/oauth-authcode/index.ts +64 -0
  85. package/src/connectivity/auth/oauth-authcode/provider.ts +279 -0
  86. package/src/connectivity/auth/oauth-authcode/refresh.ts +118 -0
  87. package/src/connectivity/auth/resolve.ts +61 -0
  88. package/src/connectivity/auth/strategy/index.ts +14 -0
  89. package/src/connectivity/capability.ts +164 -0
  90. package/src/connectivity/connector.ts +175 -0
  91. package/src/connectivity/context.ts +77 -0
  92. package/src/connectivity/index.ts +94 -0
  93. package/src/connectivity/mail/attachments.ts +368 -0
  94. package/src/connectivity/mail/compose.ts +73 -0
  95. package/src/connectivity/mail/index.ts +40 -0
  96. package/src/connectivity/mail/message.ts +82 -0
  97. package/src/connectivity/mail/nodemailer.d.ts +83 -0
  98. package/src/connectivity/mail/staging.ts +143 -0
  99. package/src/connectivity/mail/url.ts +301 -0
  100. package/src/connectivity/manifest/auth.ts +135 -0
  101. package/src/connectivity/manifest/bundles.ts +23 -0
  102. package/src/connectivity/manifest/connector.ts +168 -0
  103. package/src/connectivity/manifest/credential-ref.ts +73 -0
  104. package/src/connectivity/manifest/identity.ts +43 -0
  105. package/src/connectivity/manifest/index.ts +46 -0
  106. package/src/connectivity/manifest/primitives.ts +20 -0
  107. package/src/connectivity/manifest/provider.ts +208 -0
  108. package/src/connectivity/manifest/requirements.ts +146 -0
  109. package/src/connectivity/manifest/setup.ts +55 -0
  110. package/src/connectivity/provider.ts +163 -0
  111. package/src/connectivity/transports/README.md +33 -0
  112. package/src/connectivity/transports/composite/index.ts +68 -0
  113. package/src/connectivity/transports/dav/calendar.ts +217 -0
  114. package/src/connectivity/transports/dav/capabilities.ts +151 -0
  115. package/src/connectivity/transports/dav/client.ts +226 -0
  116. package/src/connectivity/transports/dav/contacts.ts +75 -0
  117. package/src/connectivity/transports/dav/ical.ts +412 -0
  118. package/src/connectivity/transports/dav/index.ts +143 -0
  119. package/src/connectivity/transports/dav/operations.ts +43 -0
  120. package/src/connectivity/transports/dav/request.ts +161 -0
  121. package/src/connectivity/transports/dav/xml.ts +123 -0
  122. package/src/connectivity/transports/factory.ts +181 -0
  123. package/src/connectivity/transports/fs/capabilities.ts +88 -0
  124. package/src/connectivity/transports/fs/commands.ts +258 -0
  125. package/src/connectivity/transports/fs/index.ts +121 -0
  126. package/src/connectivity/transports/fs/operations.ts +45 -0
  127. package/src/connectivity/transports/fs/paths.ts +120 -0
  128. package/src/connectivity/transports/fs/result.ts +12 -0
  129. package/src/connectivity/transports/http/index.ts +255 -0
  130. package/src/connectivity/transports/imap/attachment.ts +166 -0
  131. package/src/connectivity/transports/imap/capabilities.ts +158 -0
  132. package/src/connectivity/transports/imap/client.ts +398 -0
  133. package/src/connectivity/transports/imap/commands.ts +385 -0
  134. package/src/connectivity/transports/imap/index.ts +152 -0
  135. package/src/connectivity/transports/imap/operations.ts +64 -0
  136. package/src/connectivity/transports/imap/parse.ts +130 -0
  137. package/src/connectivity/transports/imap/parser.ts +272 -0
  138. package/src/connectivity/transports/imap/result.ts +15 -0
  139. package/src/connectivity/transports/imap/send.ts +92 -0
  140. package/src/connectivity/transports/imap/socket.ts +111 -0
  141. package/src/connectivity/transports/imap/utf7.ts +136 -0
  142. package/src/connectivity/transports/index.ts +20 -0
  143. package/src/connectivity/transports/local/index.ts +173 -0
  144. package/src/connectivity/transports/mcp/index.ts +215 -0
  145. package/src/deployments/README.md +63 -0
  146. package/src/deployments/adapters/audit-blob.ts +203 -0
  147. package/src/deployments/adapters/filesystem.ts +184 -0
  148. package/src/deployments/adapters/gcp-secret-manager.ts +492 -0
  149. package/src/deployments/adapters/gcs.ts +191 -0
  150. package/src/deployments/adapters/otlp.ts +128 -0
  151. package/src/deployments/adapters/s3.ts +195 -0
  152. package/src/deployments/azure/README.md +21 -0
  153. package/src/deployments/bootstrap.ts +177 -0
  154. package/src/deployments/deploy.ts +290 -0
  155. package/src/deployments/driver.ts +157 -0
  156. package/src/deployments/drivers.ts +35 -0
  157. package/src/deployments/gcp/Dockerfile +70 -0
  158. package/src/deployments/gcp/cloudbuild.yaml +31 -0
  159. package/src/deployments/gcp/driver.ts +175 -0
  160. package/src/deployments/gcp/gcloud.ts +178 -0
  161. package/src/deployments/gcp/provision.ts +290 -0
  162. package/src/deployments/gcp/survey.ts +319 -0
  163. package/src/deployments/local/README.md +12 -0
  164. package/src/deployments/prepare.ts +257 -0
  165. package/src/deployments/steps.ts +137 -0
  166. package/src/deployments/target.ts +295 -0
  167. package/src/deployments/upload.ts +207 -0
  168. package/src/dispatch/context.ts +195 -0
  169. package/src/dispatch/dispatch.ts +350 -0
  170. package/src/dispatch/index.ts +32 -0
  171. package/src/dispatch/staging.ts +102 -0
  172. package/src/policy/index.ts +179 -0
  173. package/src/policy/limits.ts +77 -0
  174. package/src/profile/authorization.ts +81 -0
  175. package/src/profile/files.ts +71 -0
  176. package/src/profile/index.ts +76 -0
  177. package/src/profile/layout.ts +123 -0
  178. package/src/profile/load.ts +199 -0
  179. package/src/profile/primitives.ts +45 -0
  180. package/src/profile/schema.ts +347 -0
  181. package/src/profile/secret-detection.ts +162 -0
  182. package/src/profile/targets.ts +152 -0
  183. package/src/profile/workspace.ts +262 -0
  184. package/src/providers/custom/index.ts +21 -0
  185. package/src/providers/custom/load.ts +115 -0
  186. package/src/providers/custom/template.ts +156 -0
  187. package/src/providers/example/provider.ts +207 -0
  188. package/src/providers/google/calendar/index.ts +66 -0
  189. package/src/providers/google/calendar/redact.ts +40 -0
  190. package/src/providers/google/contacts/index.ts +50 -0
  191. package/src/providers/google/contacts/redact.ts +21 -0
  192. package/src/providers/google/docs/index.ts +45 -0
  193. package/src/providers/google/drive/hints.ts +28 -0
  194. package/src/providers/google/drive/index.ts +34 -0
  195. package/src/providers/google/drive/redact.ts +39 -0
  196. package/src/providers/google/drive-mcp/index.ts +21 -0
  197. package/src/providers/google/gmail/api.ts +42 -0
  198. package/src/providers/google/gmail/attachment.ts +142 -0
  199. package/src/providers/google/gmail/hints.ts +55 -0
  200. package/src/providers/google/gmail/index.ts +112 -0
  201. package/src/providers/google/gmail/redact.ts +56 -0
  202. package/src/providers/google/gmail/send.ts +365 -0
  203. package/src/providers/google/gmail-mcp/index.ts +35 -0
  204. package/src/providers/google/index.ts +10 -0
  205. package/src/providers/google/shared/oauth.ts +122 -0
  206. package/src/providers/google/shared/scopes.ts +99 -0
  207. package/src/providers/google/shared/setup.ts +80 -0
  208. package/src/providers/google/sheets/hints.ts +45 -0
  209. package/src/providers/google/sheets/index.ts +70 -0
  210. package/src/providers/google/sheets/redact.ts +45 -0
  211. package/src/providers/google/specs/calendar.v3.json +1829 -0
  212. package/src/providers/google/specs/docs.v1.json +381 -0
  213. package/src/providers/google/specs/drive.v3.json +2208 -0
  214. package/src/providers/google/specs/gmail.v1.json +2578 -0
  215. package/src/providers/google/specs/people.v1.json +506 -0
  216. package/src/providers/google/specs/sheets.v4.json +1269 -0
  217. package/src/providers/google/specs/tasks.v1.json +840 -0
  218. package/src/providers/google/specs/vendor.ts +661 -0
  219. package/src/providers/google/tasks/index.ts +53 -0
  220. package/src/providers/google/tasks/redact.ts +34 -0
  221. package/src/providers/harness.ts +95 -0
  222. package/src/providers/icloud/calendar/index.ts +27 -0
  223. package/src/providers/icloud/contacts/index.ts +17 -0
  224. package/src/providers/icloud/drive/index.ts +47 -0
  225. package/src/providers/icloud/index.ts +8 -0
  226. package/src/providers/icloud/mail/index.ts +37 -0
  227. package/src/providers/icloud/shared/setup.ts +66 -0
  228. package/src/providers/index.ts +93 -0
  229. package/src/providers/linear/index.ts +11 -0
  230. package/src/providers/linear/scopes.ts +7 -0
  231. package/src/providers/memory/provider.ts +429 -0
  232. package/src/providers/notion/index.ts +19 -0
  233. package/src/providers/owner.ts +49 -0
  234. package/src/providers/scopes.ts +26 -0
  235. package/src/providers/setup/plan.ts +141 -0
  236. package/src/providers/setup/provider.ts +323 -0
  237. package/src/providers/shared/frontmatter.ts +119 -0
  238. package/src/providers/skills/provider.ts +283 -0
  239. package/src/providers/skills/store.ts +252 -0
  240. package/src/providers/vault/provider.ts +194 -0
  241. package/src/registry/index.ts +36 -0
  242. package/src/registry/policy-bridge.ts +32 -0
  243. package/src/registry/reconcile.ts +313 -0
  244. package/src/registry/registry.ts +240 -0
  245. package/src/secrets/document.ts +293 -0
  246. package/src/secrets/index.ts +154 -0
  247. package/src/secrets/system.ts +173 -0
  248. package/src/secrets/vault.ts +336 -0
  249. package/src/server/attachments.ts +197 -0
  250. package/src/server/container.ts +96 -0
  251. package/src/server/edge.ts +53 -0
  252. package/src/server/endpoint.ts +352 -0
  253. package/src/server/generations.ts +362 -0
  254. package/src/server/harness.ts +400 -0
  255. package/src/server/index.ts +331 -0
  256. package/src/server/logging.ts +41 -0
  257. package/src/server/mcp/build.ts +68 -0
  258. package/src/server/mcp/icon.ts +145 -0
  259. package/src/server/mcp/index.ts +32 -0
  260. package/src/server/mcp/instructions.ts +245 -0
  261. package/src/server/mcp/naming.ts +39 -0
  262. package/src/server/mcp/prompts.ts +78 -0
  263. package/src/server/mcp/resources.ts +106 -0
  264. package/src/server/mcp/routing.ts +117 -0
  265. package/src/server/mcp/schema.ts +78 -0
  266. package/src/server/mcp/tools.ts +186 -0
  267. package/src/server/mcp/visibility.ts +132 -0
  268. package/src/server/oauth.ts +222 -0
  269. package/src/server/rebinding.ts +53 -0
  270. package/src/server/stdio.ts +192 -0
  271. package/src/stores/blobs/conformance.ts +223 -0
  272. package/src/stores/blobs/index.ts +125 -0
  273. package/src/stores/blobs/testing.ts +49 -0
  274. package/src/stores/state/index.ts +247 -0
  275. package/src/stores/state/keys.ts +68 -0
  276. package/src/stores/state/testing.ts +41 -0
@@ -0,0 +1,80 @@
1
+ import { GOOGLE_APP } from './oauth.ts';
2
+
3
+ /**
4
+ * The walkthrough every Google provider prints before asking for anything.
5
+ *
6
+ * Google requires a pre-registered OAuth client — even for Google's own MCP
7
+ * servers. That is their policy and no architecture avoids it, but it does not
8
+ * follow that *you* have to be the one to register it: since ADR-028 the client
9
+ * Lanes operates is the default, and this walkthrough is what `--own-client`
10
+ * opts into. It stays complete, because the people who need it — an
11
+ * organisation that forbids third-party clients, a Workspace "Internal" app —
12
+ * need all of it.
13
+ *
14
+ * Gmail and Drive share the `google` app, which is why `oauth_apps` exists:
15
+ * every connection of a vendor authorises against the same registered client,
16
+ * and declaring that entry is also how a profile says it has one of its own.
17
+ */
18
+ export const googleSetup = (
19
+ product: string,
20
+ scopes: readonly string[],
21
+ options: { preview?: boolean; apis?: readonly string[] } = {},
22
+ ) => {
23
+ // Which APIs to enable is per product, not global: telling someone connecting
24
+ // Gmail to enable Sheets is advice to grant more than they asked for. Sheets
25
+ // and Docs name Drive as well, because their `identity` block reads
26
+ // `drive/v3/about` — with the Drive API disabled, connecting succeeds and then
27
+ // fails to label the connection.
28
+ const apis =
29
+ options.apis ??
30
+ (options.preview
31
+ ? [
32
+ 'gmail.googleapis.com',
33
+ 'gmailmcp.googleapis.com',
34
+ 'drive.googleapis.com',
35
+ 'drivemcp.googleapis.com',
36
+ ]
37
+ : ['gmail.googleapis.com', 'drive.googleapis.com']);
38
+
39
+ return {
40
+ summary: `${product} signs in with Google. By default it authorises against the OAuth client Lanes operates, so there is nothing to register and no client secret on this machine — the code is exchanged for a token by the Lanes API, which holds that secret. Pass --own-client to register a client of your own instead; the steps below are that path, asked once per profile and then covering every Google account you connect.`,
41
+ docs: 'docs/detailed/setup/google.md',
42
+ docs_url: 'https://console.cloud.google.com/auth',
43
+ steps: [
44
+ ...(options.preview
45
+ ? [
46
+ 'ENROL IN THE WORKSPACE DEVELOPER PREVIEW FIRST — https://developers.google.com/workspace/preview\n This provider proxies Google\'s MCP server, which is gated behind it. Without enrolment everything below still succeeds — consent is granted, the tool list is returned — and then every single call answers "The caller does not have permission". Enrolment needs a Google Workspace account; a personal @gmail.com cannot enrol, and should use the plain "' +
47
+ product.toLowerCase() +
48
+ '" provider instead, which talks to the REST API and has no gate.',
49
+ ]
50
+ : []),
51
+ 'Create or pick a project at https://console.cloud.google.com',
52
+ `Enable the APIs:\n gcloud services enable ${apis.join(' ')} --project=YOUR_PROJECT${
53
+ options.preview
54
+ ? '\n Note there are TWO per product for the MCP path: the service itself and its separate MCP API. Enabling only one produces a 403 whose explanation is buried in the response body.'
55
+ : ''
56
+ }\n Without gcloud: APIs & Services → Library, and search for each by name.`,
57
+ 'The rest is under Google Auth Platform — https://console.cloud.google.com/auth — in this order:',
58
+ ' BRANDING — app name and a support email. Seen by nobody but you.',
59
+ ' AUDIENCE — User type: EXTERNAL (even with a Workspace domain: "Internal" admits only that one domain, so a mix of personal and Workspace accounts needs External). Add every account you will connect under "Test users". LEAVE the status as "Testing".',
60
+ ` DATA ACCESS — where scopes live now. Add:\n ${scopes.join('\n ')}\n Note drive.file is filed under "sensitive" rather than "restricted", so it appears in a different section of that page.`,
61
+ ' CLIENTS — Create OAuth client → type: DESKTOP APP. Google\'s docs say "Web application" with a redirect URI for Claude or Antigravity, because they assume the agent host runs the OAuth. Here the CLI does, on a loopback port — which is also why this stays Desktop even when the server runs on Cloud Run.',
62
+ 'Copy the client ID and secret — you are asked for them next.',
63
+ 'Not publishing is deliberate: these are restricted scopes, and publishing them means Google verification with a CASA assessment taking months. Testing needs none. The cost is that refresh tokens expire after 7 days, so expect to re-run "lanes link connect" weekly — "lanes link doctor" says which are stale.',
64
+ ],
65
+ prompts: [
66
+ {
67
+ key: 'client_id',
68
+ label: 'Google OAuth client ID',
69
+ secret: false,
70
+ credential_ref: `${GOOGLE_APP}/client_id`,
71
+ },
72
+ {
73
+ key: 'client_secret',
74
+ label: 'Google OAuth client secret',
75
+ secret: true,
76
+ credential_ref: `${GOOGLE_APP}/client_secret`,
77
+ },
78
+ ],
79
+ };
80
+ };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Where the Sheets surface stops, and what continues it.
3
+ *
4
+ * Two gaps, both of which read as missing features rather than as boundaries.
5
+ *
6
+ * There is no `spreadsheets.create` here and there will not be: its request
7
+ * body is a whole `Spreadsheet`, which generates a 1,133 KB input schema
8
+ * against a 64 KB budget — the argument is in `specs/vendor.ts`. Creating the
9
+ * file belongs to Drive. Nothing in a tool list says so, and "sheet has no
10
+ * create" is the reasonable conclusion from reading one.
11
+ *
12
+ * And `batchUpdate` is the whole structural-edit surface behind a single
13
+ * `requests` argument that has to stay opaque, because inlining Google's
14
+ * `Request` union costs 2,469 KB. Opaque means the tool cannot enumerate what
15
+ * it does, so the common cases are named here instead.
16
+ */
17
+ export const SHEETS_HINTS: Record<string, string> = {
18
+ 'spreadsheets.batchUpdate': [
19
+ 'This is the structural-edit surface: adding, renaming, reordering, and deleting tabs,',
20
+ 'plus formatting, frozen rows, filters, and charts.',
21
+ 'The `requests` array takes Google\'s `Request` objects — `addSheet` to create a tab,',
22
+ '`updateSheetProperties` to rename one, `deleteSheet` to remove it,',
23
+ '`repeatCell` to format a range. Its structure is omitted from this schema because',
24
+ 'inlining the full union costs megabytes; the shapes are at',
25
+ 'https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request.',
26
+ 'To change cell *values* rather than structure, use the `values_*` tools instead.',
27
+ 'To create a whole new spreadsheet file, use `drive_files_create` with',
28
+ '`mimeType: "application/vnd.google-apps.spreadsheet"` — there is no create tool here.',
29
+ ].join(' '),
30
+
31
+ // On the read too, because "how do I make one" is asked while looking at the
32
+ // tool that reads one, and this is the only other tool named `spreadsheets.*`
33
+ // that an agent scanning the list is likely to open.
34
+ 'spreadsheets.get': [
35
+ 'Reads an existing spreadsheet. To create one, use `drive_files_create` with',
36
+ '`mimeType: "application/vnd.google-apps.spreadsheet"`, then write into it with the',
37
+ '`values_*` tools — this provider edits spreadsheets but does not create the file.',
38
+ ].join(' '),
39
+
40
+ 'spreadsheets.sheets.copyTo': [
41
+ 'Copies one tab into a *different* spreadsheet. To duplicate a tab within the same',
42
+ 'file use `batchUpdate` with a `duplicateSheet` request, and to copy the whole file',
43
+ 'use `drive_files_copy`.',
44
+ ].join(' '),
45
+ };
@@ -0,0 +1,70 @@
1
+ import { defineProvider } from '#connectivity';
2
+ import { DRIVE_IDENTITY, GOOGLE_APP, GOOGLE_OAUTH, specPath } from '../shared/oauth.ts';
3
+ import { googleSetup } from '../shared/setup.ts';
4
+ import { SHEETS_HINTS } from './hints.ts';
5
+ import { SHEETS_REDACT } from './redact.ts';
6
+
7
+ /**
8
+ * Sheets, because Drive cannot edit a spreadsheet.
9
+ *
10
+ * Drive holds the file and exports a rendering of it; nothing in Drive writes a
11
+ * cell. The nearest thing would be `files.update` with media, which replaces the
12
+ * whole file through import conversion and discards formulas, formatting, tabs,
13
+ * and comments in the process. Cell-level editing exists in exactly one place.
14
+ *
15
+ * Note the `base_url`: Sheets carries its version in the path
16
+ * (`/v4/spreadsheets/...`), where Drive carries it in the host. Copying Drive's
17
+ * shape here yields `/v4/v4/...` and a 404 on every call.
18
+ */
19
+
20
+ /**
21
+ * Editing a file someone already has, and why that costs a broad scope.
22
+ *
23
+ * `drive.readonly` and `drive.file` are held already and between them nearly
24
+ * cover this: reads of any spreadsheet accept `drive.readonly`, and every Sheets
25
+ * write accepts `drive.file`. But `drive.file` means *files this app created* —
26
+ * its other half, files the user picks, arrives through the Google Picker, and
27
+ * there is no picker on an MCP endpoint. So under those two scopes an agent can
28
+ * build a spreadsheet and maintain it forever, and cannot touch the one someone
29
+ * made in the browser last week. That second case is the request.
30
+ *
31
+ * `spreadsheets` is the only scope that reaches it, and it is the `gmail.modify`
32
+ * argument again, arriving at the same answer for the same reason: there is no
33
+ * narrower grant that does the job, so the choice is this or the feature. It is
34
+ * marked broad in `../shared/scopes.ts` and should stay marked. What bounds it is
35
+ * what bounds Gmail — the token is wide, the tool surface is eight operations,
36
+ * and `lanes link policy deny` narrows that further.
37
+ *
38
+ * `drive.readonly` stays alongside it. `spreadsheets` alone cannot answer
39
+ * `drive/v3/about`, which is how the connection gets labelled with an address.
40
+ */
41
+ const SHEETS_SCOPES = [
42
+ 'https://www.googleapis.com/auth/drive.readonly',
43
+ 'https://www.googleapis.com/auth/drive.file',
44
+ 'https://www.googleapis.com/auth/spreadsheets',
45
+ ];
46
+
47
+ export const sheets = defineProvider({
48
+ id: 'sheets',
49
+ name: 'Google Sheets',
50
+ description:
51
+ 'Read and edit spreadsheet cells — ranges, appends, and structural changes like tabs, formatting, and frozen rows — via the Sheets REST API.',
52
+ connector: {
53
+ kind: 'http',
54
+ base_url: 'https://sheets.googleapis.com',
55
+ openapi: specPath('sheets.v4.json'),
56
+ },
57
+ auth: {
58
+ kind: 'oauth',
59
+ registration: 'manual',
60
+ app: GOOGLE_APP,
61
+ scopes: SHEETS_SCOPES,
62
+ ...GOOGLE_OAUTH,
63
+ },
64
+ identity: DRIVE_IDENTITY,
65
+ setup: googleSetup('Sheets', SHEETS_SCOPES, {
66
+ apis: ['sheets.googleapis.com', 'drive.googleapis.com'],
67
+ }),
68
+ redact: SHEETS_REDACT,
69
+ hints: SHEETS_HINTS,
70
+ });
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Identifiers and shape, never contents.
3
+ *
4
+ * The split is cleaner here than it was for mail. `values` and `data` hold the
5
+ * cells themselves, and `requests` holds the text of a structural edit — that is
6
+ * the spreadsheet, and withholding it is the whole point of the default. What is
7
+ * kept is where and how: which file, which range, and which write mode, without
8
+ * which a write log records that a spreadsheet changed and not where.
9
+ *
10
+ * Reads are listed too, unlike Gmail's block. Gmail withholds its reads because
11
+ * `q` is a search query and the question is the sensitive part; a Sheets read
12
+ * carries no query, only a file id and an A1 range, so there is nothing there to
13
+ * protect. A range does embed a tab name, which is the user's word — that is the
14
+ * one judgement call, made in favour of an audit log that can distinguish
15
+ * reading a cell from reading the sheet.
16
+ *
17
+ * The two writes take `pathRange`, not `range`. A `ValueRange` body carries a
18
+ * `range` of its own, so the generator prefixes both with where they came from,
19
+ * and the plain name matches neither. This block said `range` until the
20
+ * argument-name check in `cli/tools.test.ts` was written — which meant the one
21
+ * thing these entries exist to record, *where* the sheet was written, was the
22
+ * one thing being withheld. The reads keep the plain name because they have no
23
+ * body to collide with.
24
+ */
25
+ export const SHEETS_REDACT: Record<string, string[]> = {
26
+ 'spreadsheets.get': ['spreadsheetId', 'ranges', 'includeGridData'],
27
+ 'spreadsheets.values.get': ['spreadsheetId', 'range', 'majorDimension'],
28
+ 'spreadsheets.values.batchGet': ['spreadsheetId', 'ranges', 'majorDimension'],
29
+ 'spreadsheets.values.update': ['spreadsheetId', 'pathRange', 'valueInputOption'],
30
+ 'spreadsheets.values.batchUpdate': ['spreadsheetId'],
31
+ 'spreadsheets.values.append': [
32
+ 'spreadsheetId',
33
+ 'pathRange',
34
+ 'valueInputOption',
35
+ 'insertDataOption',
36
+ ],
37
+ 'spreadsheets.values.clear': ['spreadsheetId', 'range'],
38
+ 'spreadsheets.values.batchClear': ['spreadsheetId', 'ranges'],
39
+ // Every argument, because every argument is an identifier: this operation
40
+ // carries no cell contents at all, and the destination file is the one fact
41
+ // an audit log must not lose — a tab copied into the wrong workbook is the
42
+ // failure worth being able to trace.
43
+ 'spreadsheets.sheets.copyTo': ['spreadsheetId', 'sheetId', 'destinationSpreadsheetId'],
44
+ 'spreadsheets.batchUpdate': ['spreadsheetId'],
45
+ };