@terreno/mcp 57.21.0 → 58.0.0-beta.1

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 (244) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +57 -3
  3. package/dist/bootstrap.d.ts.map +1 -1
  4. package/dist/bootstrap.js +17 -16
  5. package/dist/bootstrap.js.map +1 -1
  6. package/dist/docs/guidelines/admin-frontend/guidelines/core.md +6 -5
  7. package/dist/docs/guidelines/ui/guidelines/core.md +1 -1
  8. package/dist/docs/prompts/debug.md +6 -4
  9. package/dist/docs/prompts/screen.md +11 -17
  10. package/dist/docs/resources/overview.md +5 -3
  11. package/dist/docs/resources/patterns.md +157 -238
  12. package/dist/docs/resources/syncdb.md +44 -16
  13. package/dist/docs/templates/bootstrap/CLAUDE.template.md +2 -2
  14. package/dist/docs/templates/bootstrap/cursorrules.template.md +7 -5
  15. package/dist/docs/templates/bootstrap/frontend/app/(tabs)/profile.template.tsx +8 -12
  16. package/dist/docs/templates/bootstrap/frontend/app/_layout.template.tsx +75 -52
  17. package/dist/docs/templates/bootstrap/frontend/app/login.template.tsx +46 -108
  18. package/dist/docs/templates/bootstrap/frontend/app/signup.template.tsx +53 -0
  19. package/dist/docs/templates/bootstrap/frontend/lib/betterAuth.template.md +18 -0
  20. package/dist/docs/templates/bootstrap/frontend/store/apiClient.template.md +33 -0
  21. package/dist/docs/templates/bootstrap/frontend/store/apiSdk.template.md +76 -0
  22. package/dist/docs/templates/bootstrap/frontend/store/syncdb.template.md +56 -0
  23. package/dist/docs/templates/bootstrap/frontend/utils/index.template.md +0 -4
  24. package/dist/docs/upgrades/58.0.0-beta.1.md +504 -0
  25. package/dist/docs/upgrades/58.0.0.md +495 -4
  26. package/dist/docs/versioned/next/README.md +2 -2
  27. package/dist/docs/versioned/next/approval-rubric.md +2 -2
  28. package/dist/docs/versioned/next/explanation/admin-interface.md +6 -5
  29. package/dist/docs/versioned/next/explanation/admin-plugin-frontend.md +1 -1
  30. package/dist/docs/versioned/next/explanation/ai-workflows.md +5 -3
  31. package/dist/docs/versioned/next/explanation/announcements/rtk-deprecation.md +16 -11
  32. package/dist/docs/versioned/next/explanation/authentication.md +62 -43
  33. package/dist/docs/versioned/next/explanation/dependency-management.md +1 -1
  34. package/dist/docs/versioned/next/explanation/deployment-baseline.md +1 -1
  35. package/dist/docs/versioned/next/explanation/example-coverage.md +2 -2
  36. package/dist/docs/versioned/next/explanation/explicit-any-policy.md +5 -6
  37. package/dist/docs/versioned/next/explanation/gcp-hosting-architecture.md +1 -1
  38. package/dist/docs/versioned/next/explanation/local-first-data.md +11 -9
  39. package/dist/docs/versioned/next/explanation/model-router-actions.md +1 -1
  40. package/dist/docs/versioned/next/explanation/modular-api-design.md +2 -2
  41. package/dist/docs/versioned/next/explanation/no-barrel-imports.md +5 -5
  42. package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +4 -3
  43. package/dist/docs/versioned/next/explanation/source-rules.md +2 -2
  44. package/dist/docs/versioned/next/explanation/typescript-configuration.md +2 -4
  45. package/dist/docs/versioned/next/explanation/versioning-policy.md +3 -3
  46. package/dist/docs/versioned/next/how-to/README.md +3 -2
  47. package/dist/docs/versioned/next/how-to/add-feature-flags.md +50 -25
  48. package/dist/docs/versioned/next/how-to/add-organizations.md +2 -1
  49. package/dist/docs/versioned/next/how-to/admin-custom-home.md +8 -2
  50. package/dist/docs/versioned/next/how-to/admin-custom-widget.md +0 -1
  51. package/dist/docs/versioned/next/how-to/agent-ui-asks.md +6 -6
  52. package/dist/docs/versioned/next/how-to/build-admin-screens.md +10 -8
  53. package/dist/docs/versioned/next/how-to/build-for-web.md +1 -1
  54. package/dist/docs/versioned/next/how-to/call-external-apis.md +2 -2
  55. package/dist/docs/versioned/next/how-to/circleci.md +12 -6
  56. package/dist/docs/versioned/next/how-to/configure-better-auth.md +114 -60
  57. package/dist/docs/versioned/next/how-to/connect-mcp-service-token.md +10 -4
  58. package/dist/docs/versioned/next/how-to/create-a-terreno-app.md +14 -2
  59. package/dist/docs/versioned/next/how-to/debug-with-mcp.md +8 -13
  60. package/dist/docs/versioned/next/how-to/expose-mcp-tools.md +15 -4
  61. package/dist/docs/versioned/next/how-to/generate-the-client-sdk.md +33 -0
  62. package/dist/docs/versioned/next/how-to/github-actions-ci.md +2 -2
  63. package/dist/docs/versioned/next/how-to/in-app-notifications.md +1 -1
  64. package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +318 -447
  65. package/dist/docs/versioned/next/how-to/password-reset.md +1 -1
  66. package/dist/docs/versioned/next/how-to/product-announcements.md +6 -3
  67. package/dist/docs/versioned/next/how-to/rate-limiting.md +38 -16
  68. package/dist/docs/versioned/next/how-to/run-mongodb-migrations.md +19 -0
  69. package/dist/docs/versioned/next/how-to/run-tests-locally.md +2 -3
  70. package/dist/docs/versioned/next/how-to/set-up-social-login.md +1 -1
  71. package/dist/docs/versioned/next/how-to/set-up-terreno-mcp.md +2 -2
  72. package/dist/docs/versioned/next/how-to/track-upstream-expo.md +1 -1
  73. package/dist/docs/versioned/next/how-to/upgrade-banner.md +5 -3
  74. package/dist/docs/versioned/next/how-to/upgrade-terreno.md +5 -5
  75. package/dist/docs/versioned/next/how-to/use-syncdb-correctly.md +1 -1
  76. package/dist/docs/versioned/next/how-to/websocket-integration.md +113 -402
  77. package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +1 -1
  78. package/dist/docs/versioned/next/implementationPlans/migrate-admin-to-syncdb.md +5 -0
  79. package/dist/docs/versioned/next/implementationPlans/native-module-baseline.md +29 -5
  80. package/dist/docs/versioned/next/implementationPlans/remove-legacy-realtime.md +1 -1
  81. package/dist/docs/versioned/next/implementationPlans/remove-rtk.md +185 -0
  82. package/dist/docs/versioned/next/reference/README.md +1 -1
  83. package/dist/docs/versioned/next/reference/admin-frontend.md +85 -70
  84. package/dist/docs/versioned/next/reference/admin-spa.md +10 -4
  85. package/dist/docs/versioned/next/reference/ai.md +36 -4
  86. package/dist/docs/versioned/next/reference/announcements.md +3 -3
  87. package/dist/docs/versioned/next/reference/api.md +39 -24
  88. package/dist/docs/versioned/next/reference/cli.md +3 -4
  89. package/dist/docs/versioned/next/reference/environment-variables.md +16 -16
  90. package/dist/docs/versioned/next/reference/feature-flags.md +48 -7
  91. package/dist/docs/versioned/next/reference/jobs.md +2 -2
  92. package/dist/docs/versioned/next/reference/legacy/rtk.md +25 -600
  93. package/dist/docs/versioned/next/reference/mcp-server.md +441 -1158
  94. package/dist/docs/versioned/next/reference/syncdb.md +426 -16
  95. package/dist/docs/versioned/next/reference/test.md +28 -3
  96. package/dist/docs/versioned/next/reference/ui.md +132 -19
  97. package/dist/docs/versioned/next/tasks/native-module-baseline.md +8 -4
  98. package/dist/docs/versioned/next/tasks/rate-limiting.md +9 -2
  99. package/dist/docs/versioned/next/tasks/remove-legacy-realtime.md +5 -5
  100. package/dist/docs/versioned/next/tasks/remove-rtk.md +155 -0
  101. package/dist/local/localTools.d.ts.map +1 -1
  102. package/dist/local/localTools.js +3 -54
  103. package/dist/local/localTools.js.map +1 -1
  104. package/dist/local/tools/runtime.d.ts +0 -21
  105. package/dist/local/tools/runtime.d.ts.map +1 -1
  106. package/dist/local/tools/runtime.js +0 -176
  107. package/dist/local/tools/runtime.js.map +1 -1
  108. package/dist/local/tools/syncDb.d.ts +0 -11
  109. package/dist/local/tools/syncDb.d.ts.map +1 -1
  110. package/dist/local/tools/syncDb.js +1 -5
  111. package/dist/local/tools/syncDb.js.map +1 -1
  112. package/dist/packageGuidelines.d.ts +3 -2
  113. package/dist/packageGuidelines.d.ts.map +1 -1
  114. package/dist/packageGuidelines.js +4 -11
  115. package/dist/packageGuidelines.js.map +1 -1
  116. package/dist/prompts.d.ts.map +1 -1
  117. package/dist/prompts.js +17 -18
  118. package/dist/prompts.js.map +1 -1
  119. package/dist/resources.d.ts.map +1 -1
  120. package/dist/resources.js +1 -8
  121. package/dist/resources.js.map +1 -1
  122. package/dist/search/chunker.d.ts +1 -1
  123. package/dist/search/chunker.d.ts.map +1 -1
  124. package/dist/search/inferPackages.d.ts +1 -1
  125. package/dist/search/inferPackages.js +5 -5
  126. package/dist/search/inferPackages.js.map +1 -1
  127. package/dist/tools.d.ts +6 -0
  128. package/dist/tools.d.ts.map +1 -1
  129. package/dist/tools.js +126 -119
  130. package/dist/tools.js.map +1 -1
  131. package/package.json +6 -6
  132. package/src/docs/guidelines/admin-frontend/guidelines/core.md +6 -5
  133. package/src/docs/guidelines/ui/guidelines/core.md +1 -1
  134. package/src/docs/prompts/debug.md +6 -4
  135. package/src/docs/prompts/screen.md +11 -17
  136. package/src/docs/resources/overview.md +5 -3
  137. package/src/docs/resources/patterns.md +157 -238
  138. package/src/docs/resources/syncdb.md +44 -16
  139. package/src/docs/templates/bootstrap/CLAUDE.template.md +2 -2
  140. package/src/docs/templates/bootstrap/cursorrules.template.md +7 -5
  141. package/src/docs/templates/bootstrap/frontend/app/(tabs)/profile.template.tsx +8 -12
  142. package/src/docs/templates/bootstrap/frontend/app/_layout.template.tsx +75 -52
  143. package/src/docs/templates/bootstrap/frontend/app/login.template.tsx +46 -108
  144. package/src/docs/templates/bootstrap/frontend/app/signup.template.tsx +53 -0
  145. package/src/docs/templates/bootstrap/frontend/lib/betterAuth.template.md +18 -0
  146. package/src/docs/templates/bootstrap/frontend/store/apiClient.template.md +33 -0
  147. package/src/docs/templates/bootstrap/frontend/store/apiSdk.template.md +76 -0
  148. package/src/docs/templates/bootstrap/frontend/store/syncdb.template.md +56 -0
  149. package/src/docs/templates/bootstrap/frontend/utils/index.template.md +0 -4
  150. package/src/docs/upgrades/58.0.0-beta.1.md +504 -0
  151. package/src/docs/upgrades/58.0.0.md +495 -4
  152. package/src/docs/versioned/next/README.md +2 -2
  153. package/src/docs/versioned/next/approval-rubric.md +2 -2
  154. package/src/docs/versioned/next/explanation/admin-interface.md +6 -5
  155. package/src/docs/versioned/next/explanation/admin-plugin-frontend.md +1 -1
  156. package/src/docs/versioned/next/explanation/ai-workflows.md +5 -3
  157. package/src/docs/versioned/next/explanation/announcements/rtk-deprecation.md +16 -11
  158. package/src/docs/versioned/next/explanation/authentication.md +62 -43
  159. package/src/docs/versioned/next/explanation/dependency-management.md +1 -1
  160. package/src/docs/versioned/next/explanation/deployment-baseline.md +1 -1
  161. package/src/docs/versioned/next/explanation/example-coverage.md +2 -2
  162. package/src/docs/versioned/next/explanation/explicit-any-policy.md +5 -6
  163. package/src/docs/versioned/next/explanation/gcp-hosting-architecture.md +1 -1
  164. package/src/docs/versioned/next/explanation/local-first-data.md +11 -9
  165. package/src/docs/versioned/next/explanation/model-router-actions.md +1 -1
  166. package/src/docs/versioned/next/explanation/modular-api-design.md +2 -2
  167. package/src/docs/versioned/next/explanation/no-barrel-imports.md +5 -5
  168. package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +4 -3
  169. package/src/docs/versioned/next/explanation/source-rules.md +2 -2
  170. package/src/docs/versioned/next/explanation/typescript-configuration.md +2 -4
  171. package/src/docs/versioned/next/explanation/versioning-policy.md +3 -3
  172. package/src/docs/versioned/next/how-to/README.md +3 -2
  173. package/src/docs/versioned/next/how-to/add-feature-flags.md +50 -25
  174. package/src/docs/versioned/next/how-to/add-organizations.md +2 -1
  175. package/src/docs/versioned/next/how-to/admin-custom-home.md +8 -2
  176. package/src/docs/versioned/next/how-to/admin-custom-widget.md +0 -1
  177. package/src/docs/versioned/next/how-to/agent-ui-asks.md +6 -6
  178. package/src/docs/versioned/next/how-to/build-admin-screens.md +10 -8
  179. package/src/docs/versioned/next/how-to/build-for-web.md +1 -1
  180. package/src/docs/versioned/next/how-to/call-external-apis.md +2 -2
  181. package/src/docs/versioned/next/how-to/circleci.md +12 -6
  182. package/src/docs/versioned/next/how-to/configure-better-auth.md +114 -60
  183. package/src/docs/versioned/next/how-to/connect-mcp-service-token.md +10 -4
  184. package/src/docs/versioned/next/how-to/create-a-terreno-app.md +14 -2
  185. package/src/docs/versioned/next/how-to/debug-with-mcp.md +8 -13
  186. package/src/docs/versioned/next/how-to/expose-mcp-tools.md +15 -4
  187. package/src/docs/versioned/next/how-to/generate-the-client-sdk.md +33 -0
  188. package/src/docs/versioned/next/how-to/github-actions-ci.md +2 -2
  189. package/src/docs/versioned/next/how-to/in-app-notifications.md +1 -1
  190. package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +318 -447
  191. package/src/docs/versioned/next/how-to/password-reset.md +1 -1
  192. package/src/docs/versioned/next/how-to/product-announcements.md +6 -3
  193. package/src/docs/versioned/next/how-to/rate-limiting.md +38 -16
  194. package/src/docs/versioned/next/how-to/run-mongodb-migrations.md +19 -0
  195. package/src/docs/versioned/next/how-to/run-tests-locally.md +2 -3
  196. package/src/docs/versioned/next/how-to/set-up-social-login.md +1 -1
  197. package/src/docs/versioned/next/how-to/set-up-terreno-mcp.md +2 -2
  198. package/src/docs/versioned/next/how-to/track-upstream-expo.md +1 -1
  199. package/src/docs/versioned/next/how-to/upgrade-banner.md +5 -3
  200. package/src/docs/versioned/next/how-to/upgrade-terreno.md +5 -5
  201. package/src/docs/versioned/next/how-to/use-syncdb-correctly.md +1 -1
  202. package/src/docs/versioned/next/how-to/websocket-integration.md +113 -402
  203. package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +1 -1
  204. package/src/docs/versioned/next/implementationPlans/migrate-admin-to-syncdb.md +5 -0
  205. package/src/docs/versioned/next/implementationPlans/native-module-baseline.md +29 -5
  206. package/src/docs/versioned/next/implementationPlans/remove-legacy-realtime.md +1 -1
  207. package/src/docs/versioned/next/implementationPlans/remove-rtk.md +185 -0
  208. package/src/docs/versioned/next/reference/README.md +1 -1
  209. package/src/docs/versioned/next/reference/admin-frontend.md +85 -70
  210. package/src/docs/versioned/next/reference/admin-spa.md +10 -4
  211. package/src/docs/versioned/next/reference/ai.md +36 -4
  212. package/src/docs/versioned/next/reference/announcements.md +3 -3
  213. package/src/docs/versioned/next/reference/api.md +39 -24
  214. package/src/docs/versioned/next/reference/cli.md +3 -4
  215. package/src/docs/versioned/next/reference/environment-variables.md +16 -16
  216. package/src/docs/versioned/next/reference/feature-flags.md +48 -7
  217. package/src/docs/versioned/next/reference/jobs.md +2 -2
  218. package/src/docs/versioned/next/reference/legacy/rtk.md +25 -600
  219. package/src/docs/versioned/next/reference/mcp-server.md +441 -1158
  220. package/src/docs/versioned/next/reference/syncdb.md +426 -16
  221. package/src/docs/versioned/next/reference/test.md +28 -3
  222. package/src/docs/versioned/next/reference/ui.md +132 -19
  223. package/src/docs/versioned/next/tasks/native-module-baseline.md +8 -4
  224. package/src/docs/versioned/next/tasks/rate-limiting.md +9 -2
  225. package/src/docs/versioned/next/tasks/remove-legacy-realtime.md +5 -5
  226. package/src/docs/versioned/next/tasks/remove-rtk.md +155 -0
  227. package/dist/docs/guidelines/rtk/guidelines/core.md +0 -26
  228. package/dist/docs/resources/rtk.md +0 -181
  229. package/dist/docs/templates/bootstrap/frontend/openapi-config.template.md +0 -16
  230. package/dist/docs/templates/bootstrap/frontend/scripts/generate-sdk.template.md +0 -49
  231. package/dist/docs/templates/bootstrap/frontend/store/appState.template.md +0 -45
  232. package/dist/docs/templates/bootstrap/frontend/store/errors.template.md +0 -51
  233. package/dist/docs/templates/bootstrap/frontend/store/index.template.md +0 -86
  234. package/dist/docs/templates/bootstrap/frontend/store/openApiSdk.template.md +0 -49
  235. package/dist/docs/templates/bootstrap/frontend/store/sdk.template.md +0 -80
  236. package/src/docs/guidelines/rtk/guidelines/core.md +0 -26
  237. package/src/docs/resources/rtk.md +0 -181
  238. package/src/docs/templates/bootstrap/frontend/openapi-config.template.md +0 -16
  239. package/src/docs/templates/bootstrap/frontend/scripts/generate-sdk.template.md +0 -49
  240. package/src/docs/templates/bootstrap/frontend/store/appState.template.md +0 -45
  241. package/src/docs/templates/bootstrap/frontend/store/errors.template.md +0 -51
  242. package/src/docs/templates/bootstrap/frontend/store/index.template.md +0 -86
  243. package/src/docs/templates/bootstrap/frontend/store/openApiSdk.template.md +0 -49
  244. package/src/docs/templates/bootstrap/frontend/store/sdk.template.md +0 -80
@@ -1,1354 +1,642 @@
1
1
  # @terreno/mcp
2
2
 
3
- Published npm package for the Terreno Model Context Protocol (MCP) servers. The monorepo directory
4
- is `mcp-server/`. The package ships two servers with separate tool sets:
3
+ Published npm package for the Terreno Model Context Protocol (MCP) server. The monorepo directory is still `mcp-server/`. The package exposes:
5
4
 
6
- | Server | npm bin | Entry | Transport | Registers |
7
- |--------|---------|-------|-----------|-----------|
8
- | Hosted `terreno-mcp` | `terreno-mcp` → `dist/index.js` | `src/index.ts`, `src/httpApp.ts` | Streamable HTTP, stateless | 14 tools, 9 prompts, documentation resources |
9
- | Local `terreno-mcp-local` | `terreno-mcp-local` → `dist/local/index.js` | `src/local/index.ts`, `src/local/localServer.ts` | stdio | 13 tools (no prompts, no resources) |
5
+ - **`terreno-mcp`** — HTTP server used in Cloud Run and local debugging (`src/index.ts`)
6
+ - **`terreno-mcp-local`** — stdio server for project runtime tools (`src/local/index.ts`): app/database/log/browser diagnostics and SyncDB state/action/snapshot tools (the Redux `get_rtk_state` tool was removed in Terreno 58). `evaluate`, `navigate`, and SyncDB state changes are gated by `TERRENO_MCP_EVAL`. Metro URL defaults from `frontend/package.json` `--port` or `TERRENO_METRO_URL`. `application_info` reads bootstrap `backend/` + `frontend/`, or this monorepo's `example-backend/` + `example-frontend/` (bootstrap names win when both exist).
10
7
 
11
- The hosted server gives an agent documentation search, upgrade notes, and code generators. It never
12
- sees your project. The local server runs inside your project and reads its packages, database, logs,
13
- Metro, and running app.
8
+ It provides AI coding assistants with documentation access, code generation tools, and workflow prompts.
9
+ For editor configuration and runtime prerequisites, see
10
+ [Set up Terreno MCP](../how-to/set-up-terreno-mcp.md). For the end-to-end diagnostic workflow, see
11
+ [Debug a Terreno app with MCP](../how-to/debug-with-mcp.md).
14
12
 
15
- For editor configuration, see [Set up Terreno MCP](../how-to/set-up-terreno-mcp.md). For the
16
- end-to-end diagnostic workflow, see [Debug a Terreno app with MCP](../how-to/debug-with-mcp.md).
17
-
18
- Both HTTP MCP surfaces (`@terreno/mcp` and the `modelRouter` endpoint in `@terreno/api`) use the MCP
19
- TypeScript SDK v2 and speak the stateless `2026-07-28` protocol revision. The HTTP handlers retain
20
- the SDK's stateless legacy fallback for 2025-era clients. `terreno-mcp-local` uses v2 `serveStdio`
21
- with `legacy: "serve"`, which negotiates the connection era and pins one server instance for that
13
+ Both HTTP MCP surfaces (`@terreno/mcp` and the `modelRouter` endpoint in
14
+ `@terreno/api`) use the MCP TypeScript SDK v2 and speak the stateless
15
+ `2026-07-28` protocol revision. The HTTP handlers retain the SDK's stateless
16
+ legacy fallback for 2025-era clients. `terreno-mcp-local` uses v2 `serveStdio`,
17
+ which negotiates the connection era and pins one server instance for that
22
18
  connection.
23
19
 
24
20
  ## Table of Contents
25
21
 
26
- - [Hosted server (`terreno-mcp`)](#hosted-server-terreno-mcp)
27
- - [Endpoint](#endpoint)
28
- - [Hosted tools](#hosted-tools)
29
- - [Prompts](#prompts)
30
- - [Resources](#resources)
31
- - [Rate limiting](#rate-limiting)
32
- - [Local server (`terreno-mcp-local`)](#local-server-terreno-mcp-local)
33
- - [Running the local server](#running-the-local-server)
34
- - [Local tools](#local-tools)
35
- - [`TERRENO_MCP_EVAL` gate](#terreno_mcp_eval-gate)
36
- - [Metro and CDP connection](#metro-and-cdp-connection)
37
- - [React Native DevTools and the `app` source](#react-native-devtools-and-the-app-source)
38
- - [What the local server can and cannot see](#what-the-local-server-can-and-cannot-see)
39
- - [Environment variables](#environment-variables)
22
+ - [Overview](#overview)
23
+ - [Installation](#installation)
24
+ - [Resources](#resources)
25
+ - [Tools](#tools)
26
+ - [Prompts](#prompts)
27
+ - [Environment Variables](#environment-variables)
40
28
  - [Development](#development)
41
- - [Architecture](#architecture)
42
- - [Deployment](#deployment)
43
29
 
44
- ## Hosted server (`terreno-mcp`)
30
+ ## Overview
31
+
32
+ The MCP server exposes Terreno's documentation and code generation capabilities through the Model Context Protocol, enabling AI assistants (like Claude in Cursor or Claude Desktop) to:
33
+
34
+ - Access up-to-date documentation for all Terreno packages
35
+ - Generate boilerplate code following Terreno conventions
36
+ - Provide multi-step workflows for common development tasks
37
+
38
+ **Key concepts:**
45
39
 
46
- ### Endpoint
40
+ - **Resources**: Read-only documentation from `docs/` directory
41
+ - **Tools**: Code generators that return text (AI writes files), documentation search (`terreno_search_docs`, `terreno_get_component_docs`), upgrade notes (`terreno_get_upgrade_guide`), plus local-only tools when using `terreno-mcp-local`
42
+ - **Prompts**: Pre-built multi-step instructions for complex workflows
47
43
 
48
- | Item | Value |
49
- |------|-------|
50
- | URL | `https://mcp.terreno.app/mcp` |
51
- | JSON-RPC routes | `ALL /mcp` and `POST /` (same handler, same rate-limit bucket) |
52
- | Health check | `GET /` → `{"mcpEndpoint":"/mcp","service":"terreno-mcp","status":"ok"}` (never rate limited) |
53
- | Sessions | None. Each request is independent; no `initialize` round trip is required. |
54
- | Request headers | `Accept: application/json, text/event-stream` is required; without both types the server returns JSON-RPC error `-32000` `Not Acceptable: Client must accept both application/json and text/event-stream` |
55
- | Response | An SSE stream of `event: message` frames (`responseMode: "auto"`) |
56
- | Logging | Every `tools/call` and `prompts/get` is logged with its name and arguments (`logger.info`), and the server is wrapped with `Sentry.wrapMcpServerWithSentry`. Do not put secrets or PHI in tool arguments. |
44
+ ## Installation
57
45
 
58
- `mcp.terreno.flourish.health` is the legacy hostname for the same service. Use
59
- `https://mcp.terreno.flourish.health/mcp` while `mcp.terreno.app` does not resolve
60
- (`getaddrinfo ENOTFOUND mcp.terreno.app`).
46
+ ### With Claude Desktop
61
47
 
62
- Editor entry (the Terreno servers `create-terreno-app` writes to `.cursor/mcp.json` and
63
- `.mcp.json`; the full files are in [Set up Terreno MCP](../how-to/set-up-terreno-mcp.md)):
48
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
64
49
 
65
50
  ``````json
66
51
  {
67
52
  "mcpServers": {
68
- "terreno": {"type": "http", "url": "https://mcp.terreno.app/mcp"},
69
- "terreno-local": {"command": "bunx", "args": ["-p", "@terreno/mcp", "terreno-mcp-local"]}
53
+ "terreno": {
54
+ "command": "bun",
55
+ "args": ["/absolute/path/to/terreno/mcp-server/dist/index.js"]
56
+ }
70
57
  }
71
58
  }
72
59
  ``````
73
60
 
74
- Raw HTTP call against a local build (`PORT=8799 bun dist/index.js`):
75
-
76
- ``````bash
77
- curl -s -X POST http://127.0.0.1:8799/mcp \
78
- -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
79
- -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"terreno_get_upgrade_guide","arguments":{"fromVersion":"57.14.0","toVersion":"57.14.0"}}}'
80
- ``````
81
-
82
- ``````text
83
- event: message
84
- data: {"result":{"content":[{"type":"text","text":"Recorded notes in 57.14.0 → 57.14.0: 57.14.0\n\n---\n\n# Upgrade to 57.14.0…"}]},"jsonrpc":"2.0","id":1}
85
- ``````
86
-
87
- JSON-RPC methods: `tools/list`, `tools/call`, `prompts/list`, `prompts/get`, `resources/list`,
88
- `resources/read`.
89
-
90
- ### Hosted tools
91
-
92
- Code generators return text. They do not write files: the agent receives the code and a target path
93
- and writes it. Scaffold a new app with [`create-terreno-app`](create-terreno-app.md)
94
- (`bunx create-terreno-app`), not an MCP tool. Every tool returns one text content block.
95
-
96
- | Tool | Purpose |
97
- |------|---------|
98
- | [`terreno_bootstrap_ai_rules`](#terreno_bootstrap_ai_rules) | Generate AI assistant rule files for an app |
99
- | [`terreno_search_docs`](#terreno_search_docs) | Keyword search over bundled Terreno docs |
100
- | [`terreno_get_component_docs`](#terreno_get_component_docs) | Props table for one `@terreno/ui` component |
101
- | [`terreno_get_upgrade_guide`](#terreno_get_upgrade_guide) | Bundled upgrade notes between two versions |
102
- | [`terreno_search_update_notes`](#terreno_search_update_notes) | Search upgrade notes and help-API announcements |
103
- | [`terreno_get_update_note`](#terreno_get_update_note) | Full text of one update note |
104
- | [`terreno_ask_update_help`](#terreno_ask_update_help) | Ranked update-note excerpts for a question |
105
- | [`terreno_generate_model`](#terreno_generate_model) | Mongoose model source |
106
- | [`terreno_generate_route`](#terreno_generate_route) | `modelRouter` route source |
107
- | [`terreno_generate_screen`](#terreno_generate_screen) | React Native screen source |
108
- | [`terreno_generate_form_fields`](#terreno_generate_form_fields) | `@terreno/ui` form field source |
109
- | [`terreno_validate_model_schema`](#terreno_validate_model_schema) | Check schema source against Terreno conventions |
110
- | [`terreno_validate_ui_blocks`](#terreno_validate_ui_blocks) | Validate a whole-reply UI block document |
111
- | [`terreno_install_admin`](#terreno_install_admin) | Admin panel files and setup steps |
112
-
113
- Example outputs below are real tool output, shortened with `…`.
114
-
115
- #### terreno_bootstrap_ai_rules
116
-
117
- Generate AI coding assistant rules (`AGENTS.md`, `CLAUDE.md`, `.rulesync/` sources, and
118
- `rulesync.jsonc`). Guideline bodies come from each package's `.ai/guidelines/core.md`, copied into
119
- this package at build time (`bun run sync-package-guidelines`). Defined in `src/bootstrap.ts`.
120
-
121
- | Parameter | Type | Required | Description |
122
- |-----------|------|----------|-------------|
123
- | `appName` | string | yes | App name in kebab-case |
124
- | `appDisplayName` | string | yes | Human-readable name |
125
- | `description` | string | no | One-line app description |
126
- | `packages` | string[] | no | Guideline packages to merge: `api`, `ui`, `rtk`, `syncdb`, `admin-backend`, `admin-frontend`, or the `@terreno/*` name. Omitted or no valid id means all six. |
127
-
128
- ``````json
129
- {"name":"terreno_bootstrap_ai_rules","arguments":{"appName":"my-app","appDisplayName":"My App","packages":["api"]}}
130
- ``````
131
-
132
- ``````markdown
133
- # Bootstrap AI Rules for My App
134
-
135
- ## Files to Create
136
-
137
- The following AI coding assistant configuration files need to be created:
138
-
139
- - `.rulesync/rules/00-root.md`
140
- - `backend/AGENTS.md`
141
- - `backend/CLAUDE.md`
142
- - `frontend/AGENTS.md`
143
- - `frontend/CLAUDE.md`
144
- - `rulesync.jsonc`
145
-
146
- ## Instructions
147
-
148
- 1. **Create all the files listed above.** Each file's content is provided below.
149
- …
150
- 3. **Before generating, move hand-written rules into `.rulesync/rules/`.** `rulesync generate` overwrites the root `CLAUDE.md`, `AGENTS.md`, and `.github/copilot-instructions.md` and deletes every file under `.claude/rules/`, `.cursor/rules/`, `.github/instructions/`, and `.agents/memories/` that it did not generate. …
151
-
152
- 4. **Delete the stale `.cursorrules`** that `create-terreno-app` wrote. …
153
-
154
- 5. **Generate the editor files with rulesync:**
155
- ```bash
156
- bunx rulesync@9 generate
157
- ```
158
- …
159
- ``````
160
-
161
- `rulesync.jsonc` targets `cursor`, `claudecode`, `copilot`, and `agentsmd`. The one
162
- `root: true` rule in `00-root.md` covers all four. See
163
- [Agent guidelines](../explanation/agent-guidelines.md#terreno_bootstrap_ai_rules) for the
164
- generated editor files and how to add project rules.
165
-
166
- #### terreno_search_docs
167
-
168
- BM25-style keyword search over markdown bundled with the server: `docs/resources/*.md`, the Diátaxis
169
- docs synced into `docs/versioned/`, and per-component excerpts from `ui-types-documentation.json`.
170
- Call it before guessing a Terreno API.
171
-
172
- | Parameter | Type | Required | Description |
173
- |-----------|------|----------|-------------|
174
- | `queries` | string[] | yes | One or more search phrases |
175
- | `packages` | string[] | no | Filter by package id or scope, e.g. `["api", "@terreno/ui"]` |
176
- | `tokenLimit` | number | no | Approximate markdown budget. Default 3000, clamped to 8000. |
177
- | `version` | string | no | `@terreno/*` lockstep version (e.g. `57.2.0`). Omit for current `next` docs. |
61
+ ### With Claude Code CLI
178
62
 
179
- An unmatched `version` falls back to the nearest retained snapshot (`website/versioned_docs/`,
180
- copied into `docs/versioned/` at build), and the response says so. Range prefixes such as
181
- `^57.2.0` are stripped. A non-array `queries` returns a usage message instead of results.
63
+ Add to your project's `.claude/settings.json`:
182
64
 
183
65
  ``````json
184
- {"name":"terreno_search_docs","arguments":{"queries":["instanceActions"],"packages":["api"],"tokenLimit":300}}
66
+ {
67
+ "mcpServers": {
68
+ "terreno": {
69
+ "command": "bun",
70
+ "args": ["./mcp-server/dist/index.js"]
71
+ }
72
+ }
73
+ }
185
74
  ``````
186
75
 
187
- ``````markdown
188
- # Terreno documentation search results
76
+ ### Building from Source
189
77
 
190
- Queries: "instanceActions"
191
- Docs version: next
192
- Package filter: api
78
+ ``````bash
79
+ # From monorepo root
80
+ bun install
81
+ bun run mcp:build
193
82
 
194
- ## modelRouter actions
195
- _Source: `versioned/next/reference/api.md` · Packages: api_
196
- …
197
- _…Truncated to approximate token budget (~300 tokens)._
83
+ # Or from mcp-server directory
84
+ cd mcp-server
85
+ bun run build
198
86
  ``````
199
87
 
200
- #### terreno_get_component_docs
88
+ ## Resources
201
89
 
202
- Return the props table for one `@terreno/ui` component from `ui-types-documentation.json`, plus
203
- related markdown excerpts.
90
+ Documentation resources accessible via `terreno://` protocol:
204
91
 
205
- | Parameter | Type | Required | Description |
206
- |-----------|------|----------|-------------|
207
- | `component` | string | yes | Exported component name, e.g. `Button` |
208
- | `version` | string | no | Lockstep version. A retained snapshot returns that version's generated component page (MDX chrome stripped); otherwise current props are used and the fallback is noted. |
92
+ | URI | Description |
93
+ |-----|-------------|
94
+ | `terreno://docs/overview` | Monorepo overview and architecture |
95
+ | `terreno://docs/api` | @terreno/api reference documentation |
96
+ | `terreno://docs/ui` | @terreno/ui reference documentation |
97
+ | `terreno://docs/syncdb` | @terreno/syncdb: synced collections, request client + generated API hooks, auth session, config, flags |
98
+ | `terreno://docs/patterns` | Common patterns and best practices |
209
99
 
210
- ``````json
211
- {"name":"terreno_get_component_docs","arguments":{"component":"Badge"}}
212
- ``````
100
+ **How it works:**
213
101
 
214
- ``````markdown
215
- # Badge
102
+ - Resources are loaded from markdown files in `docs/` directory
103
+ - Content is transformed and served via MCP protocol
104
+ - AI assistants cache resources for fast lookup
105
+ - Updates require MCP server restart
216
106
 
217
- **Interface:** `BadgeProps`
107
+ **Custom docs directory:**
218
108
 
219
- ## Props
109
+ Set `TERRENO_MCP_DOCS_DIR` environment variable to override default path.
220
110
 
221
- | Name | Type | Required | Description |
222
- |------|------|----------|-------------|
223
- | `color` | `"bold" | "subtle"` | No | For the "status" variant, whether the dot uses the bold (saturated) or …
224
- …
225
- ``````
111
+ ## Tools
226
112
 
227
- A build without `ui-types-documentation.json` returns:
228
- `Component documentation is unavailable (ui-types-documentation.json not found). Run `bun run sync-ui-docs` in mcp-server before build.`
113
+ Code generation tools return TypeScript/JavaScript code as text. **Code generation tools do not
114
+ write files** — the AI assistant receives the code and writes it to appropriate locations. Scaffold
115
+ a new app with [`create-terreno-app`](create-terreno-app.md) (`bunx create-terreno-app`), not an MCP
116
+ tool.
229
117
 
230
- #### terreno_get_upgrade_guide
118
+ The local `browser` tool is different: its `screenshot` action writes the requested image path so an
119
+ agent can attach the rendered result as verification evidence. It keeps one headless session alive
120
+ for the MCP connection. Call `open` first, perform interactions, call `snapshot` and `screenshot` to
121
+ prove the final state, then call `close`.
231
122
 
232
- Return bundled lockstep upgrade notes between two versions. The response always lists which versions
233
- in the range have notes and names the gaps, because an empty concatenation would read as "nothing
234
- changed". Note format: `mcp-server/src/docs/upgrades/README.md`.
235
-
236
- | Parameter | Type | Required | Description |
237
- |-----------|------|----------|-------------|
238
- | `fromVersion` | string | yes | Installed `@terreno/*` semver. Must be ≤ `toVersion`. |
239
- | `toVersion` | string | yes | Target semver |
123
+ `open` accepts only `http:` and `https:` URLs. Snapshots redact password input values; use them for
124
+ page structure and visible state, not credential inspection.
240
125
 
241
126
  ``````json
242
- {"name":"terreno_get_upgrade_guide","arguments":{"fromVersion":"57.13.0","toVersion":"57.14.0"}}
127
+ {"name":"browser","arguments":{"action":"open","url":"http://localhost:8082","width":1280,"height":720}}
128
+ {"name":"browser","arguments":{"action":"wait","timeout":1000}}
129
+ {"name":"browser","arguments":{"action":"click","selector":"button[type=submit]"}}
130
+ {"name":"browser","arguments":{"action":"snapshot"}}
131
+ {"name":"browser","arguments":{"action":"screenshot","output":"/opt/cursor/artifacts/app.png"}}
132
+ {"name":"browser","arguments":{"action":"close"}}
243
133
  ``````
244
134
 
245
- ``````markdown
246
- Recorded notes in 57.13.0 → 57.14.0: 57.14.0
135
+ `browser` requires Bun 1.4 or newer. macOS uses system WebKit; Linux and Windows use an installed
136
+ Chrome, Chromium, or Edge through the Chrome DevTools Protocol. Browser storage is ephemeral unless
137
+ `open` receives `dataDir`. Set `TERRENO_MCP_EVAL=1` to enable its arbitrary-JavaScript `evaluate`
138
+ action. Screenshot paths must stay under the project root or `/opt/cursor/artifacts`; persistent
139
+ browser data must stay under the project root. Calls are serialized around the one session owned by
140
+ the local MCP process.
247
141
 
248
- ---
142
+ ### SyncDB runtime tools
249
143
 
250
- # Upgrade to 57.14.0
144
+ Create the app client with `debug: true` so it registers by its configured
145
+ `name`. The local MCP uses the in-process registry when available and Hermes CDP
146
+ for a separately running app.
251
147
 
252
- # Upgrading to 57.14.0
148
+ | Tool | Purpose |
149
+ | --- | --- |
150
+ | `get_syncdb_state` | Read status, entities/tombstones, outbox, conflicts, cursors, streams, repair markers, values, and debugger events |
151
+ | `syncdb_snapshot` | Capture, list, get, compare, or delete snapshots held by this MCP process |
152
+ | `syncdb_action` | Mutate or directly edit local state, flush, reconcile/resync, resolve/retry, toggle offline, clear debug events, or merge a snapshot |
253
153
 
254
- **Action required:** No
255
- **Affected packages:** @terreno/syncdb
256
- …
257
- ``````
154
+ State and snapshot responses redact fields containing `password`, `token`,
155
+ `secret`, `authorization`, or `cookie`. Snapshot merge payloads are retained
156
+ privately and are not returned. Start the local MCP with
157
+ `TERRENO_MCP_EVAL=1` before calling `syncdb_action`.
258
158
 
259
- #### terreno_search_update_notes
159
+ See [Debug a Terreno app with MCP](../how-to/debug-with-mcp.md) for examples and
160
+ safety guidance.
260
161
 
261
- Search bundled upgrade notes and, when `TERRENO_HELP_API_URL` is set, product announcements from a
262
- Terreno backend help API. Returns JSON.
162
+ ### terreno_search_docs
263
163
 
264
- | Parameter | Type | Required | Description |
265
- |-----------|------|----------|-------------|
266
- | `queries` | string[] | no | Keyword phrases |
267
- | `question` | string | no | Natural-language question, used as an extra phrase |
268
- | `source` | `auto` \| `bundled` \| `announcement` | no | Default `auto` |
269
- | `limit` | number | no | Default 10 |
270
- | `includeArchived` | boolean | no | Include archived announcements |
164
+ BM25-style keyword search over markdown bundled with the MCP server: `docs/resources/*.md`, synced Diátaxis docs under `docs/versioned/`, and per-component excerpts derived from `ui-types-documentation.json`. **Prefer this tool before guessing** Terreno APIs (same posture as Laravel Boost `search-docs`).
271
165
 
272
- ``````json
273
- {"name":"terreno_search_update_notes","arguments":{"queries":["rate limit"],"limit":2}}
274
- ``````
166
+ **Parameters:**
275
167
 
276
- ``````json
168
+ ``````typescript
277
169
  {
278
- "hits": [
279
- {
280
- "excerpt": "# Upgrading to 57.3.0 **Action required:** Yes **Affected packages:** @terreno/api, …",
281
- "id": "upgrade:57.3.0",
282
- "kind": "upgrade",
283
- "title": "Terreno 57.3.0 upgrade notes",
284
- "version": "57.3.0"
285
- },
286
- …
287
- ],
288
- "sources": [
289
- "bundled-upgrades"
290
- ]
170
+ queries: string[]; // Required — one or more search phrases
171
+ packages?: string[]; // Optional — filter by package id or scope, e.g. ["api", "@terreno/ui"]
172
+ tokenLimit?: number; // Approximate max tokens of markdown (default 3000)
173
+ version?: string; // Optional @terreno/* lockstep version (e.g. 57.2.0). Omit for current `next` docs.
291
174
  }
292
175
  ``````
293
176
 
294
- #### terreno_get_update_note
177
+ Unmatched versions fall back to the nearest retained snapshot (`website/versioned_docs/`, copied into `docs/versioned/` at MCP build). The response names the resolved version and includes a note when a fallback happened. Pass the consumer app's `@terreno/*` version from `application_info` or `package.json`. Range prefixes such as `^57.2.0` are stripped before matching.
295
178
 
296
- Return the full text of one update note as JSON (`body`, `id`, `kind`, `title`, `version`).
179
+ ### terreno_get_component_docs
297
180
 
298
- | Parameter | Type | Required | Description |
299
- |-----------|------|----------|-------------|
300
- | `id` | string | yes | `upgrade:<semver>`, `announcement:<mongoId>`, or a bare upgrade semver |
301
- | `includeArchived` | boolean | no | Allow archived announcements from the help API |
181
+ Returns the full props table for a single `@terreno/ui` component from `ui-types-documentation.json`, plus short related markdown excerpts when the search index finds matches.
302
182
 
303
- ``````json
304
- {"name":"terreno_get_update_note","arguments":{"id":"upgrade:57.14.0"}}
305
- ``````
183
+ **Parameters:**
306
184
 
307
- ``````json
185
+ ``````typescript
308
186
  {
309
- "body": "Recorded notes in 57.14.0 → 57.14.0: 57.14.0\n\n---\n\n# Upgrade to 57.14.0\n…",
310
- "id": "upgrade:57.14.0",
311
- "kind": "upgrade",
312
- "title": "Terreno 57.14.0 upgrade notes",
313
- "version": "57.14.0"
187
+ component: string; // e.g. "Button", "TextField"
188
+ version?: string; // Optional @terreno/* lockstep version. Omit for current TypeDoc props.
314
189
  }
315
190
  ``````
316
191
 
317
- #### terreno_ask_update_help
318
-
319
- Answer a question with ranked update-note excerpts and the ids to fetch with
320
- `terreno_get_update_note`. Returns text.
321
-
322
- | Parameter | Type | Required | Description |
323
- |-----------|------|----------|-------------|
324
- | `question` | string | yes | Question about what changed |
325
- | `limit` | number | no | Default 5 |
326
- | `includeArchived` | boolean | no | Include archived announcements |
327
-
328
- ``````json
329
- {"name":"terreno_ask_update_help","arguments":{"question":"How do I enable rate limiting?","limit":2}}
330
- ``````
331
-
332
- ``````text
333
- Question: How do I enable rate limiting?
334
- Sources: bundled-upgrades
192
+ When `version` matches a retained docs snapshot, the tool returns that version's generated component page (MDX chrome stripped). Snapshot filenames may be hyphenated (`text-field.mdx`) or concatenated (`userinactivity.mdx`); lookup tries both. Otherwise it uses current `ui-types-documentation.json` and notes the fallback.
335
193
 
336
- Top matches (call terreno_get_update_note with `id` for the full note):
337
- - [upgrade] Terreno 57.3.0 upgrade notes (upgrade:57.3.0)
338
- # Upgrading to 57.3.0 **Action required:** Yes …
339
- - [upgrade] Terreno 58.0.0 upgrade notes (upgrade:58.0.0)
340
- …
341
- ``````
342
-
343
- #### terreno_generate_model
194
+ ### terreno_get_upgrade_guide
344
195
 
345
- Generate a Mongoose model with `strict: "throw"`, virtuals in `toJSON`/`toObject`, and
346
- `addDefaultPlugins`. Target path: `backend/src/models/<name>.ts`.
196
+ Return bundled Terreno lockstep upgrade notes between two semver versions (markdown). Use before major bumps to `@terreno/*` packages. The response always lists which versions in the range **have** notes. If some or all versions have none, it names those gaps — an empty concatenation would look like “nothing changed,” which is usually false. `fromVersion` must be less than or equal to `toVersion`. Note format: `mcp-server/src/docs/upgrades/README.md`.
347
197
 
348
- | Parameter | Type | Required | Description |
349
- |-----------|------|----------|-------------|
350
- | `name` | string | yes | Model name, PascalCase |
351
- | `fields` | object[] | yes | Each `{name*, type*, required?, unique?, ref?, default?}`. `type`: `String`, `Number`, `Boolean`, `Date`, `ObjectId`, `Array`, `Mixed`. `default` is a string. |
352
- | `hasOwner` | boolean | no | Add a required `ownerId` referencing `User` |
353
- | `softDelete` | boolean | no | Enable soft delete |
198
+ **Parameters:**
354
199
 
355
- ``````json
356
- {"name":"terreno_generate_model","arguments":{"name":"Product","fields":[{"name":"title","type":"String","required":true}],"hasOwner":true}}
200
+ ``````typescript
201
+ {
202
+ fromVersion: string; // Installed @terreno/* semver (e.g. 0.19.0)
203
+ toVersion: string; // Target semver to upgrade to (e.g. 0.20.0)
204
+ }
357
205
  ``````
358
206
 
359
- ``````text
360
- **IMPORTANT: Write this code to a file**
361
-
362
- File path: `backend/src/models/product.ts`
207
+ ### terreno_generate_model
363
208
 
364
- After writing the file:
365
- 1. Export from `backend/src/models/index.ts`: `export * from "./product";`
209
+ Generate a Mongoose model with Terreno conventions (timestamps, soft delete, owner tracking, type definitions).
366
210
 
367
- ---
211
+ **Parameters:**
368
212
 
369
- import mongoose from "mongoose";
370
- import { addDefaultPlugins } from "@terreno/api";
371
- …
372
- const productSchema = new mongoose.Schema<ProductDocument, ProductModel>(
373
- {
374
- title: { type: String, required: true, trim: true },
375
- ownerId: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
376
- },
377
- …
213
+ ``````typescript
214
+ {
215
+ name: string; // Model name (PascalCase)
216
+ fields: Array<{
217
+ name: string; // Field name (camelCase)
218
+ type: string; // "String" | "Number" | "Boolean" | "Date" | "ObjectId"
219
+ required?: boolean;
220
+ default?: string; // Default value as string
221
+ ref?: string; // Referenced model name (for ObjectId)
222
+ description?: string; // Field description (recommended)
223
+ }>;
224
+ hasOwner?: boolean; // Add ownerId field (default: false)
225
+ softDelete?: boolean; // Add deleted field (default: false)
226
+ timestamps?: boolean; // Add created/updated fields (default: true)
227
+ }
378
228
  ``````
379
229
 
380
- #### terreno_generate_route
381
-
382
- Generate a `modelRouter` route. Target path: `backend/src/api/<model>.ts`.
383
-
384
- | Parameter | Type | Required | Description |
385
- |-----------|------|----------|-------------|
386
- | `modelName` | string | yes | Model name, PascalCase |
387
- | `routePath` | string | yes | API path, e.g. `/products` |
388
- | `permissions` | object | no | `{create, list, read, update, delete}`, each `admin`, `any`, `authenticated`, `authOrReadOnly`, or `owner`. A missing or unknown value becomes `Permissions.IsAuthenticated`. |
389
- | `queryFields` | string[] | no | Allowed query fields |
390
- | `sort` | string | no | Default sort, e.g. `-created` |
391
- | `ownerFiltered` | boolean | no | Add `OwnerQueryFilter` and an `ownerId`-setting `preCreate` |
230
+ **Example:**
392
231
 
393
232
  ``````json
394
- {"name":"terreno_generate_route","arguments":{"modelName":"Product","routePath":"/products","permissions":{"list":"authenticated"}}}
395
- ``````
396
-
397
- ``````text
398
- **IMPORTANT: Write this code to a file**
399
-
400
- File path: `backend/src/api/product.ts`
401
-
402
- After writing the file:
403
- 1. Export from `backend/src/api/index.ts`: `export * from "./product";`
404
- 2. Register in server setup: `addProductRoutes(router, options);`
405
- 3. Regenerate frontend SDK: `bun run sdk` in the frontend directory
406
- …
233
+ {
234
+ "name": "Product",
235
+ "fields": [
236
+ {"name": "title", "type": "String", "required": true, "description": "Product title"},
237
+ {"name": "price", "type": "Number", "required": true, "description": "Price in cents"},
238
+ {"name": "active", "type": "Boolean", "default": "true", "description": "Is product active"}
239
+ ],
240
+ "hasOwner": true,
241
+ "softDelete": true
242
+ }
407
243
  ``````
408
244
 
409
- #### terreno_generate_screen
245
+ **Returns:**
410
246
 
411
- Generate a React Native screen with `@terreno/ui` components and generated RTK Query hooks. Target
412
- path: `frontend/src/screens/<name>Screen.tsx`.
247
+ - Model schema code with proper type definitions
248
+ - Methods and statics structure
249
+ - Plugin configuration
250
+ - Export statements
413
251
 
414
- | Parameter | Type | Required | Description |
415
- |-----------|------|----------|-------------|
416
- | `name` | string | yes | Screen name, PascalCase |
417
- | `type` | `list` \| `detail` \| `form` \| `empty` | yes | Screen kind |
418
- | `modelName` | string | no | Model for data fetching |
419
- | `fields` | string[] | no | Fields to display or edit |
252
+ ### terreno_generate_route
420
253
 
421
- ``````json
422
- {"name":"terreno_generate_screen","arguments":{"name":"ProductList","type":"list","modelName":"Product","fields":["title"]}}
423
- ``````
254
+ Generate modelRouter configuration with permissions and query options.
424
255
 
425
- ``````text
426
- **IMPORTANT: Write this code to a file**
256
+ **Parameters:**
427
257
 
428
- File path: `frontend/src/screens/ProductListScreen.tsx`
429
- …
430
- import { useGetProductsQuery } from "@/store/openApiSdk";
431
-
432
- const ProductListScreen: React.FC = () => {
433
- const { data: products, isLoading, error, refetch } = useGetProductsQuery({});
434
- …
258
+ ``````typescript
259
+ {
260
+ modelName: string; // Model name (PascalCase)
261
+ routePath: string; // API path (e.g., "/products")
262
+ permissions?: {
263
+ create?: "any" | "authenticated" | "admin" | "owner";
264
+ list?: "any" | "authenticated" | "admin" | "owner";
265
+ read?: "any" | "authenticated" | "admin" | "owner";
266
+ update?: "any" | "authenticated" | "admin" | "owner";
267
+ delete?: "any" | "authenticated" | "admin" | "owner";
268
+ };
269
+ queryFields?: string[]; // Allowed query parameters
270
+ ownerFiltered?: boolean; // Apply OwnerQueryFilter (default: false)
271
+ sort?: string; // Default sort order (e.g., "-created")
272
+ populate?: Array<{path: string; fields?: string[]}>;
273
+ }
435
274
  ``````
436
275
 
437
- #### terreno_generate_form_fields
438
-
439
- Generate `@terreno/ui` form fields and their state declarations. Fields use the real
440
- `@terreno/ui` props (`title`, `value`, `onChange`, `errorText`); `password` renders
441
- `TextField type="password"`, and `number` and date values are strings. A required field adds an
442
- `errors` state and `errorText={errors.<name>}`.
443
-
444
- | Parameter | Type | Required | Description |
445
- |-----------|------|----------|-------------|
446
- | `fields` | object[] | yes | Each `{name*, type*, label?, required?, options?}`. `type`: `text`, `email`, `password`, `number`, `textarea`, `select`, `boolean`, `date`, `datetime`. `options` is `[{label, value}]` for `select`. |
276
+ **Example:**
447
277
 
448
278
  ``````json
449
- {"name":"terreno_generate_form_fields","arguments":{"fields":[{"name":"title","type":"text","required":true}]}}
450
- ``````
451
-
452
- ``````text
453
- **IMPORTANT: Write this code to a file**
454
-
455
- File path: `your form component file`
456
- …
457
- // State declarations (add to your component):
458
- const [title, setTitle] = useState("");
459
- …
460
- const [errors, setErrors] = useState<Record<string, string | undefined>>({});
461
-
462
- // Form fields (add inside your component's return):
463
- …
279
+ {
280
+ "modelName": "Product",
281
+ "routePath": "/products",
282
+ "permissions": {
283
+ "create": "authenticated",
284
+ "list": "any",
285
+ "read": "any",
286
+ "update": "owner",
287
+ "delete": "admin"
288
+ },
289
+ "queryFields": ["active", "category"],
290
+ "ownerFiltered": true,
291
+ "sort": "-created"
292
+ }
464
293
  ``````
465
294
 
466
- #### terreno_validate_model_schema
467
-
468
- Check schema source text against Terreno conventions and list issues and suggestions. The
469
- model's `<Name>Document` type may be declared in the file or imported from the app's type files
470
- (`src/modelInterfaces.ts`, `src/types/models/`); "Missing TypeScript interfaces" means neither.
295
+ **Returns:**
471
296
 
472
- | Parameter | Type | Required | Description |
473
- |-----------|------|----------|-------------|
474
- | `schema` | string | yes | Schema source code |
297
+ - Router setup code with modelRouter configuration
298
+ - Permission mapping
299
+ - Lifecycle hooks structure
300
+ - Instructions for registering route
475
301
 
476
- ``````json
477
- {"name":"terreno_validate_model_schema","arguments":{"schema":"const s = new mongoose.Schema({title: {type: String}});"}}
478
- ``````
302
+ ### terreno_generate_screen
479
303
 
480
- ``````text
481
- Schema Validation Results:
304
+ Generate React Native screen component with Terreno UI components.
482
305
 
483
- Issues Found:
484
- - Missing strict: 'throw' in schema options
485
- - Missing toJSON virtuals configuration
486
- - Missing default plugins
487
- - Missing TypeScript interfaces
306
+ **Parameters:**
488
307
 
489
- Suggestions:
490
- …
308
+ ``````typescript
309
+ {
310
+ name: string; // Screen name (PascalCase)
311
+ type: "list" | "detail" | "form" | "empty";
312
+ modelName?: string; // Model name for CRUD screens
313
+ fields?: string[]; // Fields to display/edit
314
+ collection?: string; // Synced collection (default: kebab-case plural of modelName)
315
+ }
491
316
  ``````
492
317
 
493
- #### terreno_validate_ui_blocks
494
-
495
- Validate one whole-reply YAML or JSON block document. The text is the report
496
- `terreno-blocks validate` prints: one line per problem, `path CODE message — fix`. A valid document
497
- with no warnings returns an empty string.
498
-
499
- | Parameter | Type | Required | Description |
500
- |-----------|------|----------|-------------|
501
- | `document` | string | yes | The YAML or JSON document |
318
+ **Example:**
502
319
 
503
320
  ``````json
504
- {"name":"terreno_validate_ui_blocks","arguments":{"document":"v: 1\nblocks:\n - type: bogus\n"}}
505
- ``````
506
-
507
- ``````text
508
- blocks[0].type.type INVALID_ENUM blocks[0].type is not a supported block. — Set type to heading, text, metric, …, list, or html.
509
- ``````
510
-
511
- #### terreno_install_admin
512
-
513
- Generate admin panel screen files and backend/frontend setup steps for `@terreno/admin-backend` and
514
- `@terreno/admin-frontend`.
515
-
516
- | Parameter | Type | Required | Description |
517
- |-----------|------|----------|-------------|
518
- | `models` | object[] | yes | Each `{modelName*, routePath*, displayName*, listFields*}`; `listFields` is string[] |
519
-
520
- ``````json
521
- {"name":"terreno_install_admin","arguments":{"models":[{"modelName":"Todo","routePath":"/todos","displayName":"Todos","listFields":["title"]}]}}
321
+ {
322
+ "name": "ProductList",
323
+ "type": "list",
324
+ "modelName": "Product",
325
+ "fields": ["title", "price", "active"]
326
+ }
522
327
  ``````
523
328
 
524
- ``````markdown
525
- # Install Admin Panel
329
+ **Returns:**
526
330
 
527
- ## Files to Create
331
+ - React Native functional component
332
+ - `@terreno/syncdb/react` hooks against the synced collection: `useQuery` + `useSyncStatus` (list), `useEntity` (detail), `useMutate().create` (form). For a model that is not synced, swap them for the generated `@/store/apiSdk` hooks
333
+ - @terreno/ui components (Box, Text, Button, Page, etc.)
334
+ - Empty and not-found states
528
335
 
529
- - `frontend/app/(tabs)/admin/index.tsx`
530
- - `frontend/app/(tabs)/admin/[model].tsx`
531
- - `frontend/app/(tabs)/admin/[model]/create.tsx`
532
- - `frontend/app/(tabs)/admin/[model]/[id].tsx`
336
+ ### terreno_generate_form_fields
533
337
 
534
- ## Setup Instructions
338
+ Generate form field components for a model.
535
339
 
536
- ### Step 1: Add `@terreno/admin-backend` to backend
537
- …
538
- ``````
340
+ **Parameters:**
539
341
 
540
- ### Prompts
541
-
542
- Multi-step workflow prompts, served by `prompts/list` and `prompts/get`. `terreno_bootstrap` is
543
- defined in `src/bootstrap.ts`; the rest in `src/prompts.ts`. `*` marks a required argument. All
544
- arguments are strings.
545
-
546
- | Prompt | Arguments | Purpose |
547
- |--------|-----------|---------|
548
- | `terreno_bootstrap` | `appName*`, `appDisplayName*` | Run `bunx create-terreno-app`, then `terreno_bootstrap_ai_rules` |
549
- | `terreno_create_crud_feature` | `name*`, `fields*` (`title:string,price:number`), `hasOwner` (`yes`/`no`) | Model, routes, and frontend screens |
550
- | `terreno_create_api_endpoint` | `path*`, `method*`, `description*` | Custom endpoint with OpenAPI docs |
551
- | `terreno_create_ui_component` | `name*`, `type*` (`display`, `interactive`, `form`, `layout`) | Reusable `@terreno/ui`-style component |
552
- | `terreno_create_form_screen` | `name*`, `fields*` (`name:text,email:email`), `endpoint*` | Form screen with validation |
553
- | `terreno_add_authentication` | `features` (`email,social,passwordReset`) | Login/signup screens and auth state |
554
- | `terreno_style_guide` | none | Terreno code style guide |
555
- | `terreno_migrate_to_terreno_app` | `serverFile` | Move from `setupServer` to `TerrenoApp` |
556
- | `terreno_upgrade` | `targetVersion` (omit for latest stable) | Lockstep upgrade: read notes, bump `@terreno/*`, test, delegate Expo SDK steps to the upgrading-expo skill |
557
-
558
- ### Resources
559
-
560
- Read-only markdown, loaded once at startup from `<docs root>/resources/` (see
561
- `TERRENO_MCP_DOCS_DIR`). Restart the server to pick up changes.
562
-
563
- | URI | Name | Source |
564
- |-----|------|--------|
565
- | `terreno://docs/overview` | Terreno Overview | `overview.md` |
566
- | `terreno://docs/api` | @terreno/api Documentation | `api.md` |
567
- | `terreno://docs/ui` | @terreno/ui Documentation | `ui.md` |
568
- | `terreno://docs/syncdb` | @terreno/syncdb Documentation | `syncdb.md` |
569
- | `terreno://docs/rtk` | @terreno/rtk Documentation (legacy) | `rtk.md` |
570
- | `terreno://docs/patterns` | Terreno Patterns & Best Practices | `patterns.md` |
571
- | `terreno://docs/ui/components` | @terreno/ui Component Reference | Generated from `ui-types-documentation.json` |
572
- | `terreno://docs/ui/components/<slug>` | `<Component> Component` | One per component |
573
-
574
- The two component rows exist only when `ui-types-documentation.json` is bundled.
575
-
576
- ### Rate limiting
577
-
578
- The hosted server limits its JSON-RPC routes per client IP: `/mcp` (all methods) and `POST /` share
579
- one bucket. The `GET /` health check is never limited. The local stdio server has no limit.
580
-
581
- - **Default:** 120 requests per 60 seconds per client IP, a fixed window. Search, codegen, and docs
582
- calls are cheap per call (the search index is built once at startup), and an agent session makes
583
- a few dozen calls a minute, so the default leaves room for several agents behind one NAT while
584
- capping one abusive client at about 2 requests per second.
585
- - **Configure:** `TERRENO_MCP_RATE_LIMIT_MAX` (`0` turns the limit off, for example in local
586
- development) and `TERRENO_MCP_RATE_LIMIT_WINDOW_MS`. An invalid value stops the server at startup
587
- rather than running unlimited.
588
- - **Per instance:** counters live in memory on each Cloud Run instance. With `N` instances, one
589
- client can make up to `N` times the limit in a window if its requests land on different
590
- instances.
591
- - **Client IP:** on Cloud Run the server trusts one proxy hop, so the client IP is the address the
592
- Google Front End appended to `X-Forwarded-For`. Entries a client writes into that header itself
593
- are ignored, so a forged header cannot switch buckets. Off Cloud Run no proxy is trusted and the
594
- socket address is used. Set `TERRENO_MCP_TRUST_PROXY_HOPS` when you add a proxy layer (for
595
- example `2` behind an external HTTPS load balancer).
596
-
597
- A request over the limit gets HTTP `429` with `Retry-After`, `RateLimit`, and `RateLimit-Policy`
598
- headers and a JSON-RPC error body. `id` echoes the request id when the body is a single request
599
- with a string or number id; otherwise it is `null`.
600
-
601
- ``````json
342
+ ``````typescript
602
343
  {
603
- "jsonrpc": "2.0",
604
- "error": {
605
- "code": -32029,
606
- "message": "Rate limit exceeded: 120 requests per 60 seconds per client IP. Retry after 42s.",
607
- "data": {"limit": 120, "windowMs": 60000, "retryAfterSeconds": 42}
608
- },
609
- "id": 7
344
+ modelName: string; // Model name (PascalCase)
345
+ fields: Array<{
346
+ name: string;
347
+ type: "text" | "number" | "boolean" | "date" | "select";
348
+ required?: boolean;
349
+ options?: string[]; // For select fields
350
+ }>;
610
351
  }
611
352
  ``````
612
353
 
613
- `-32029` is in the JSON-RPC range reserved for implementation-defined server errors. Clients
614
- should wait `retryAfterSeconds` (or `Retry-After`) before retrying.
615
-
616
- ## Local server (`terreno-mcp-local`)
617
-
618
- ### Running the local server
619
-
620
- Run it from the app repository root with `bunx -p @terreno/mcp terreno-mcp-local`. The bin and the
621
- package have different names, so `bunx` needs `-p` unless `@terreno/mcp` is already installed in
622
- the project (as in this monorepo). The bin is
623
- `#!/usr/bin/env bun`, so it needs Bun; the `browser` tool needs Bun 1.4 or newer. It registers tools
624
- only.
625
-
626
- Project root discovery (`src/local/projectRoot.ts`): `TERRENO_PROJECT_ROOT` when set. Otherwise it
627
- walks up to 20 directories from the working directory to the first directory with a `package.json`
628
- and both a backend workspace (`backend/` or `example-backend/`) and a frontend workspace
629
- (`frontend/` or `example-frontend/`), or one of them plus a non-empty `workspaces` array. With no
630
- match it uses the working directory. Bootstrap names win when both exist.
631
-
632
- Errors: a refused or invalid call usually returns explanatory text. A thrown error (for example from
633
- `browser`, or a database exception) becomes a JSON-RPC error with code `-32603` and the error
634
- message. An unknown tool name returns the text `Unknown tool: <name>`.
635
-
636
- ### Local tools
637
-
638
- All schemas set `additionalProperties: false`. Every tool returns one text block, JSON for most
639
- tools. The server does not validate arguments against the schema: an unknown argument, such as
640
- `projection` on `database_query`, is ignored, not rejected. Check a result against the parameter
641
- table when it looks unfiltered.
642
-
643
- | Tool | Purpose | Gated by `TERRENO_MCP_EVAL` |
644
- |------|---------|------|
645
- | [`application_info`](#application_info) | Workspace packages and key dependency versions | no |
646
- | [`database_schema`](#database_schema) | Mongo collections, indexes, counts, model excerpts | no |
647
- | [`database_query`](#database_query) | Read-only Mongo queries | no |
648
- | [`read_logs`](#read_logs) | Merged backend, browser, Metro, and app log tail | no |
649
- | [`last_error`](#last_error) | Newest error-level log entry | no |
650
- | [`get_client_state`](#get_client_state) | RTK Query and SyncDB state of the running app | no |
651
- | [`get_rtk_state`](#get_rtk_state) | Deprecated alias of `get_client_state` | no |
652
- | [`get_syncdb_state`](#get_syncdb_state) | SyncDB client state | no |
653
- | [`syncdb_snapshot`](#syncdb_snapshot) | Capture and compare SyncDB snapshots | no |
654
- | [`syncdb_action`](#syncdb_action) | Change SyncDB client state | yes, every action |
655
- | [`evaluate`](#evaluate) | Run JavaScript in the app runtime | yes |
656
- | [`navigate`](#navigate) | Navigate the app with expo-router | yes |
657
- | [`browser`](#browser) | Drive a headless WebView | only the `evaluate` action |
658
-
659
- Example outputs below are real tool output, shortened with `…`. Where an output needs a running
660
- Hermes app, which the capture environment did not have, the section says so and shows only the
661
- call or the failure output.
662
-
663
- #### application_info
664
-
665
- Read the root `package.json` and the first backend (`backend/`, then `example-backend/`) and frontend
666
- (`frontend/`, then `example-frontend/`) `package.json`. It reports `@terreno/*` dependencies and
667
- `expo`, `react`, `react-native`, `mongoose`, `bun`, and `@types/bun` as declared (ranges such as
668
- `^57.2.0`, `catalog:`, or `workspace:*`, not resolved versions). It checks that `bun.lock` exists but
669
- does not parse it. Call it at the start of a session. No parameters.
354
+ **Example:**
670
355
 
671
356
  ``````json
672
- {"name":"application_info","arguments":{}}
357
+ {
358
+ "modelName": "Product",
359
+ "fields": [
360
+ {"name": "title", "type": "text", "required": true},
361
+ {"name": "price", "type": "number", "required": true},
362
+ {"name": "category", "type": "select", "options": ["electronics", "books", "clothing"]}
363
+ ]
364
+ }
673
365
  ``````
674
366
 
675
- ``````markdown
676
- # Application info
677
-
678
- - **projectRoot**: `…`
679
- - **root package**: @terreno/root @ 1.0.0
680
- - **bun.lock present**: yes
367
+ **Returns:**
681
368
 
682
- ## Backend workspace (example-backend/)
683
- - **name**: @terreno/example-backend
684
- - **version**: 1.0.0
685
- - @terreno/admin-backend: workspace:*
686
- …
687
- - **mongoose**: catalog:
688
-
689
- ## Frontend workspace (example-frontend/)
690
- …
691
- - **react-native**: catalog:
692
-
693
- ## Lockfile
694
- Workspace package versions are recorded under `workspaces` in `bun.lock`. Prefer installed semver over `latest` in generated code.
695
- ``````
369
+ - TextField, NumberField, SelectField components
370
+ - Validation logic structure
371
+ - Form state management pattern
696
372
 
697
- #### database_schema
373
+ ### terreno_validate_model_schema
698
374
 
699
- List live Mongo collections with `estimatedDocumentCount` and indexes, plus the first 80 lines of
700
- each `backend/src/models/*.ts` file (except `index.ts`), read as text. Only `backend/` is scanned,
701
- not `example-backend/`. Each call connects with Mongoose and disconnects at the end.
375
+ Validate a Mongoose schema against Terreno conventions.
702
376
 
703
- Mongo URI: `MONGO_URI` from the environment, else `MONGO_URI`, else `MONGODB_URI` parsed from
704
- `<project root>/backend/.env`. `example-backend/.env` is not read.
377
+ **Parameters:**
705
378
 
706
- | Parameter | Type | Required | Description |
707
- |-----------|------|----------|-------------|
708
- | `collectionFilter` | string | no | Case-insensitive substring filter on collection names |
709
- | `summary` | boolean | no | Count model files instead of printing excerpts |
710
-
711
- ``````json
712
- {"name":"database_schema","arguments":{"summary":true}}
379
+ ``````typescript
380
+ {
381
+ schemaCode: string; // Full schema code to validate
382
+ }
713
383
  ``````
714
384
 
715
- Output with no URI configured:
385
+ **Returns:**
716
386
 
717
- ``````markdown
718
- # Database schema
387
+ - List of convention violations
388
+ - Recommendations for fixes
389
+ - Severity levels (error, warning, info)
719
390
 
720
- ## Declared models (static scan of `backend/src/models/*.ts`)
391
+ ### terreno_validate_ui_blocks
721
392
 
722
- _(No model files found.)_
393
+ Check one whole-reply YAML or JSON document. The text is the same report `terreno-blocks validate` prints: each problem is `path CODE message — fix`. A valid document with no warnings returns an empty string.
723
394
 
724
- ## Live MongoDB
395
+ **Parameters:**
725
396
 
726
- _Unavailable: set `MONGO_URI` in `backend/.env` or export `MONGO_URI` to include live collections._
397
+ ``````typescript
398
+ {
399
+ document: string; // Required — the YAML or JSON document
400
+ }
727
401
  ``````
728
402
 
729
- #### database_query
403
+ **Returns:**
730
404
 
731
- Run one read-only query. Each call opens its own connection and closes it afterwards; queries time
732
- out after 10 seconds (`maxTimeMS`), and `aggregate` runs with `allowDiskUse: false`. Same URI
733
- resolution as `database_schema`. Results are returned unredacted, so avoid collections that hold
734
- credentials (Better Auth keeps password hashes in `account`).
405
+ - The CLI report, including a trailing newline when there is at least one line
406
+ - An empty string when the document is valid and has no warnings
735
407
 
736
- | Parameter | Type | Required | Description |
737
- |-----------|------|----------|-------------|
738
- | `collection` | string | yes | Collection name |
739
- | `operation` | `find` \| `aggregate` \| `countDocuments` \| `distinct` | yes | Operation |
740
- | `filter` | object | no | Query for `find`, `countDocuments`, `distinct`. Rejected anywhere it contains `$where`, `$function`, or `$accumulator`. |
741
- | `pipeline` | object[] | no | Stages for `aggregate`. Rejected anywhere it contains `$out`, `$merge`, or `$function`. |
742
- | `field` | string | for `distinct` | Field to collect |
743
- | `limit` | number | no | Rows or distinct values returned. Default 50, clamped 1–200. |
408
+ ### terreno_bootstrap_ai_rules
744
409
 
745
- Results: `find` and `aggregate` return `{collection, count, rows}`; `countDocuments` returns
746
- `{collection, count}`; `distinct` returns `{collection, field, truncated, values}`.
410
+ Scaffold AI coding assistant rules (AGENTS.md, Cursor/Windsurf rules, Copilot instructions, rulesync config).
747
411
 
748
- ``````json
749
- {"name":"database_query","arguments":{"collection":"todos","operation":"aggregate","pipeline":[{"$group":{"_id":"$completed","count":{"$sum":1}}},{"$sort":{"_id":1}}]}}
750
- ``````
412
+ **Parameters:**
751
413
 
752
- ``````json
414
+ ``````typescript
753
415
  {
754
- "collection": "todos",
755
- "count": 1,
756
- "rows": [
757
- {
758
- "_id": false,
759
- "count": 2
760
- }
761
- ]
416
+ appName: string;
417
+ appDisplayName: string;
418
+ description?: string;
419
+ /** Optional — which @terreno/* packages to merge into rules. Use ids like `api`, `ui`, `syncdb`, `admin-backend`, `admin-frontend`, or `@terreno/api`. Omit or pass only what the app uses so admin guidelines stay out of projects without the admin panel. */
420
+ packages?: string[];
762
421
  }
763
422
  ``````
764
423
 
765
- ``````json
766
- {"name":"database_query","arguments":{"collection":"todos","operation":"aggregate","pipeline":[{"$out":"x"}]}}
767
- ``````
424
+ Guideline bodies are composed from per-package `.ai/guidelines/core.md` files, copied into this package at build time (`bun run sync-package-guidelines` in `mcp-server/`).
768
425
 
769
- ``````text
770
- Pipeline rejected: `$out`, `$merge`, and `$function` are not allowed.
771
- ``````
426
+ **Returns:** Rules files and instructions for installing/syncing with rulesync.
772
427
 
773
- ``````json
774
- {"name":"database_query","arguments":{"collection":"todos","operation":"find","filter":{"$where":"true"}}}
775
- ``````
428
+ ### terreno_install_admin
776
429
 
777
- ``````text
778
- Filter rejected: `$where`, `$function`, and `$accumulator` are not allowed.
779
- ``````
430
+ Generate admin panel integration for `@terreno/admin-backend` and `@terreno/admin-frontend`.
780
431
 
781
- #### read_logs
432
+ **Parameters:** Model configurations with `modelName`, `routePath`, `displayName`, `listFields`, etc.
782
433
 
783
- Merge log entries from up to four sources, sort them by timestamp, and return the newest.
434
+ **Returns:** Frontend screen files and backend/frontend setup snippets. The generated
435
+ `admin/_layout.tsx` wraps the routes in `AdminProvider` with `apiOrigin={baseUrl}` (from
436
+ `@terreno/syncdb/config`) and `getAuthHeaders` reading `authProvider.getToken()` from the
437
+ scaffold's `frontend/store/syncdb.ts`. No `@terreno/rtk` import.
784
438
 
785
- | Source | Reads | Written by | Notes |
786
- |--------|-------|-----------|-------|
787
- | `backend` | `app.log` in each existing `<root>/backend/.terreno/logs`, `<root>/example-backend/.terreno/logs`, `<root>/.terreno/logs` | `@terreno/api` logger JSONL transport (see `TERRENO_LOG_FILE`) at `<backend cwd>/.terreno/logs/app.log` | No `stack` field: `logger.error` stacks are inside `message` |
788
- | `browser` | `browser.log` in the same directories | [`POST /__terreno/browser-logs`](api.md#development-browser-log-ingestion), fed by `installTerrenoDevConsoleLogger` from `@terreno/syncdb` | `console.error` entries carry no stack; uncaught errors carry `stack`. A render error caught by an error boundary is a `console.error` entry: no `stack`, unformatted `%o`/`%s` placeholders, and the component name in `message` |
789
- | `metro` | In-memory ring (2000) from Metro's `ws://<metro>/events` | Metro dev server | Always empty with Expo CLI 57 (see [Metro and CDP connection](#metro-and-cdp-connection)) |
790
- | `app` | In-memory ring (2000) of Hermes `Runtime.consoleAPICalled` over CDP | The running Hermes app | Needs a Hermes target; `stack` is a JSON string of the first 8 call frames; timestamps are receipt time |
439
+ ## Prompts
791
440
 
792
- The `metro` and `app` rings start filling on the first call that requests those sources and are
793
- held for the life of the MCP process. Earlier output is not captured.
441
+ Multi-step workflow prompts that guide AI assistants through complex tasks.
794
442
 
795
- | Parameter | Type | Required | Description |
796
- |-----------|------|----------|-------------|
797
- | `sources` | string[] | no | Any of `backend`, `browser`, `metro`, `app`. Default all four. |
798
- | `entries` | number | no | Default 200, clamped 1–2000. Limits lines read per file and the merged result. |
799
- | `level` | string | no | Exact, case-insensitive level match, e.g. `error` |
800
- | `since` | string | no | ISO-8601 instant. Drops entries before it; entries without a parseable timestamp are kept. |
443
+ ### terreno_bootstrap
801
444
 
802
- | Response field | Content |
803
- |----------------|---------|
804
- | `connection` | Always the CDP and Metro events status (see [Metro and CDP connection](#metro-and-cdp-connection)), even when `metro` and `app` were not requested |
805
- | `entries` | Each `{level, message, raw, source, timestamp}`; `raw` is the line as written |
806
- | `metroHint` | Connection details for the `metro` and `app` sources requested; absent when neither was requested |
807
- | `projectRoot` | Resolved project root |
808
-
809
- ``````json
810
- {"name":"read_logs","arguments":{"level":"error","entries":3}}
811
- ``````
812
-
813
- ``````json
814
- {
815
- "connection": "CDP: Error: No debuggable Hermes/React Native target with webSocketDebuggerUrl (is the app running?); Metro events: connected",
816
- "entries": [
817
- …,
818
- {
819
- "level": "error",
820
- "message": "Uncaught TypeError: Cannot read properties of undefined (reading 'profile')",
821
- "raw": "{\"level\":\"error\",\"message\":\"Uncaught TypeError: Cannot read properties of undefined (reading 'profile')\",\"source\":\"browser\",\"stack\":\"TypeError: Cannot read properties of undefined (reading 'profile')\\n at brokenHandler (eval at evaluate (:303:30), <anonymous>:4:19)\\n at eval (eval at evaluate (:303:30), <anonymous>:6:22)\",\"timestamp\":\"2026-10-12T00:24:58.867+00:00\"}",
822
- "source": "browser",
823
- "timestamp": "2026-10-12T00:24:58.867+00:00"
824
- },
825
- {
826
- "level": "error",
827
- "message": "Fallthrough error: SyntaxError: JSON Parse error: Unexpected identifier \"broken\"\nSyntaxError: JSON Parse error: Unexpected identifier \"broken\"\n at <parse> (:0)\n…",
828
- "raw": "{\"level\":\"error\",\"message\":\"Fallthrough error: SyntaxError: …\",\"requestId\":\"2edcea24-66a8-40b6-821c-a62f57af91fc\",\"terrenoRequestLog\":{\"requestId\":\"2edcea24-66a8-40b6-821c-a62f57af91fc\",\"userId\":null},\"timestamp\":\"2026-10-12T00:25:13.380Z\"}",
829
- "source": "backend",
830
- "timestamp": "2026-10-12T00:25:13.380Z"
831
- }
832
- ],
833
- "metroHint": "connected | Error: No debuggable Hermes/React Native target with webSocketDebuggerUrl (is the app running?)",
834
- "projectRoot": "/workspace"
835
- }
836
- ``````
445
+ Workflow prompt for scaffolding a new Terreno app. Instructs the assistant to run `bunx create-terreno-app`, then `terreno_bootstrap_ai_rules`.
837
446
 
838
- `app` source output, captured against a test device that speaks Metro's device protocol (not a real
839
- Hermes runtime):
447
+ **Arguments:**
840
448
 
841
- ``````json
842
- {
843
- "connection": "CDP: connected; Metro events: not connected",
844
- "entries": [
845
- …,
846
- {"level": "info", "message": "fake app tick #94", "source": "app", "timestamp": "2026-10-12T00:47:43.238Z", "raw": "…"},
847
- {"level": "error", "message": "fake app error #95", "source": "app", "timestamp": "2026-10-12T00:47:44.239Z", "raw": "{\"level\":\"error\",\"message\":\"fake app error #95\",\"source\":\"app\",\"stack\":\"[{\\\"columnNumber\\\":10,\\\"functionName\\\":\\\"tick\\\",\\\"lineNumber\\\":42,\\\"scriptId\\\":\\\"1\\\",\\\"url\\\":\\\"http://localhost:8082/index.bundle\\\"}]\",\"timestamp\":\"2026-10-12T00:47:44.239Z\"}"},
848
- …
849
- ],
850
- "metroHint": "connected",
851
- "projectRoot": "/workspace"
852
- }
853
- ``````
449
+ - `appName` (string) — Application name in kebab-case
450
+ - `appDisplayName` (string) — Human-readable display name
854
451
 
855
- #### last_error
452
+ ### terreno_upgrade
856
453
 
857
- Return the newest `error`-level entry across the selected sources. It reads the last 500 lines of
858
- each file plus the `metro` and `app` rings. On a hit it returns the entry's `raw` line only: backend
859
- lines have no `source` field, and no connection status is appended.
454
+ Lockstep Terreno upgrade workflow: read bundled upgrade notes, bump `@terreno/*` packages, run tests, and delegate Expo SDK steps to the official upgrading-expo skill.
860
455
 
861
- | Parameter | Type | Required | Description |
862
- |-----------|------|----------|-------------|
863
- | `sources` | string[] | no | Any of `backend`, `browser`, `metro`, `app`. Default all four. |
456
+ **Arguments:** `targetVersion` (optional) — target `@terreno/*` semver; omit to mean latest stable.
864
457
 
865
- ``````json
866
- {"name":"last_error","arguments":{"sources":["browser"]}}
867
- ``````
458
+ ### terreno_create_crud_feature
868
459
 
869
- ``````text
870
- {"level":"error","message":"Uncaught TypeError: Cannot read properties of undefined (reading 'profile')","source":"browser","stack":"TypeError: Cannot read properties of undefined (reading 'profile')\n at brokenHandler (eval at evaluate (:303:30), <anonymous>:4:19)\n at eval (eval at evaluate (:303:30), <anonymous>:6:22)","timestamp":"2026-10-12T00:24:58.867+00:00"}
871
- ``````
460
+ Generate complete CRUD feature: backend model + routes + frontend screens.
872
461
 
873
- With no error-level entry:
462
+ **Arguments:**
874
463
 
875
- ``````text
876
- No recent error-level entries in selected sources (app). CDP: Error: No debuggable Hermes/React Native target with webSocketDebuggerUrl (is the app running?); Metro events: connected
877
- ``````
464
+ - `name` (string) — Feature name (e.g., "Product")
465
+ - `fields` (string) — Comma-separated fields: `title:string,price:number,active:boolean`
466
+ - `hasOwner` (string) — "yes" or "no" (default: "no")
878
467
 
879
- #### get_client_state
468
+ **Workflow:**
880
469
 
881
- Detect and read the running app's client data layer:
470
+ 1. Generate Mongoose model with type definitions
471
+ 2. Generate API routes with permissions
472
+ 3. Generate list screen with DataTable
473
+ 4. Generate detail screen
474
+ 5. Generate form screen with validation
475
+ 6. Provide instructions for:
476
+ - Registering routes in `server.ts`
477
+ - Regenerating SDK: `bun run sdk`
478
+ - Adding navigation
882
479
 
883
- - RTK Query: the Redux store registered by `registerTerrenoDevStore(store)` from `@terreno/rtk`
884
- (`__DEV__` only) as `globalThis.__TERRENO_STORE__`.
885
- - SyncDB: clients created with `createSyncDb({debug: true})`, registered on
886
- `globalThis.__TERRENO_SYNCDB__`.
480
+ ### terreno_create_api_endpoint
887
481
 
888
- It reads the in-process registries when the MCP process shares the app's heap; otherwise it runs a
889
- fixed read-only expression in the app through CDP `Runtime.evaluate` (not gated, because the caller
890
- cannot change the code). Keys matching `password`, `token`, `secret`, `authorization`, or `cookie`
891
- (case-insensitive) are returned as `[REDACTED]`.
482
+ Generate custom (non-CRUD) API endpoint with OpenAPI documentation.
892
483
 
893
- | Parameter | Type | Required | Description |
894
- |-----------|------|----------|-------------|
895
- | `slice` | string | no | RTK: `auth` (Better Auth slice, else legacy `auth`), `betterAuth`, `rtk` or `terreno-rtk`, or any top-level state key. Omit for auth plus the RTK Query cache summary. |
896
- | `query` | string | no | RTK: keep cache rows whose endpoint name or serialized args contain this substring (case-insensitive) |
897
- | `name` | string | no | SyncDB client name. Default the first registered client. |
898
- | `collection` | string | no | SyncDB collection filter |
899
- | `entityId` | string | no | SyncDB entity id filter |
900
- | `includeDebugEvents` | boolean | no | SyncDB: `false` returns debug stats without event bodies |
901
- | `limit` | number | no | SyncDB entities per collection. Default 500, max 2000. |
484
+ **Arguments:**
902
485
 
903
- Response: `{layer, rtk?, syncdb?}`. `layer` is `rtk`, `syncdb`, `syncdb+rtk` (both payloads
904
- returned), or `none`. `rtk` holds `{auth, terrenoRtk: {queries, mutations}}` (rows are
905
- `{args, endpoint, status}`) or the requested slice. `syncdb` holds the SyncDB inspect output. With
906
- `layer: "none"` the response has `connection`, `hint`, and per-layer `reasons` instead.
486
+ - `path` (string) — Endpoint path (e.g., "/stats/summary")
487
+ - `method` (string) — HTTP method ("get", "post", "patch", "delete")
488
+ - `description` (string) — What the endpoint does
907
489
 
908
- ``````json
909
- {"name":"get_client_state","arguments":{}}
910
- ``````
490
+ **Workflow:**
911
491
 
912
- Output with Metro running and no Hermes app attached (a populated response needs a running Hermes
913
- app and is not shown):
492
+ 1. Generate route handler with asyncHandler
493
+ 2. Generate OpenAPI builder configuration
494
+ 3. Generate response types
495
+ 4. Provide authentication setup instructions
496
+ 5. Provide SDK regeneration instructions
914
497
 
915
- ``````json
916
- {
917
- "connection": "CDP: Error: No debuggable Hermes/React Native target with webSocketDebuggerUrl (is the app running?); Metro events: not connected",
918
- "hint": "No client data layer found. For RTK Query, call `registerTerrenoDevStore(store)` from @terreno/rtk in __DEV__. For SyncDB, create the client with `createSyncDb({debug: true})`. Keep the app running; when the MCP process cannot see the app heap, start Metro so CDP can reach Hermes (see `connection`).",
919
- "layer": "none",
920
- "reasons": {
921
- "rtk": "Error: No debuggable Hermes/React Native target with webSocketDebuggerUrl (is the app running?)",
922
- "syncdb": "Error: Error: No debuggable Hermes/React Native target with webSocketDebuggerUrl (is the app running?)\nCDP: …"
923
- }
924
- }
925
- ``````
498
+ ### terreno_create_ui_component
926
499
 
927
- #### get_rtk_state
500
+ Generate reusable UI component following @terreno/ui patterns.
928
501
 
929
- Deprecated alias of `get_client_state`, kept for one release. Same schema, same handler, same
930
- `{layer, rtk?, syncdb?}` response.
502
+ **Arguments:**
931
503
 
932
- #### get_syncdb_state
504
+ - `name` (string) — Component name (PascalCase)
505
+ - `type` (string) — "display" | "interactive" | "form" | "layout"
506
+ - `description` (string) — Component purpose
933
507
 
934
- Read a SyncDB client registered by `createSyncDb({debug: true})`: status, entities (including
935
- tombstones and pending metadata), decoded outbox, conflicts, cursors, streams, repair markers,
936
- values, and debugger events. Same access path and redaction as `get_client_state`. Not gated.
508
+ **Workflow:**
937
509
 
938
- | Parameter | Type | Required | Description |
939
- |-----------|------|----------|-------------|
940
- | `name` | string | no | Client name. Default the first registered client. |
941
- | `collection` | string | no | Collection filter |
942
- | `entityId` | string | no | Entity id filter |
943
- | `includeDebugEvents` | boolean | no | `false` returns debug stats without event bodies |
944
- | `limit` | number | no | Entities per collection. Default 500, max 2000. |
510
+ 1. Generate component structure with TypeScript types
511
+ 2. Include @terreno/ui imports (Box, Text, Button, etc.)
512
+ 3. Add prop definitions with JSDoc
513
+ 4. Include usage example
514
+ 5. Provide testing setup
945
515
 
946
- ``````json
947
- {"name":"get_syncdb_state","arguments":{"collection":"todos"}}
948
- ``````
516
+ ### terreno_create_form_screen
949
517
 
950
- On success the text is the redacted inspect output as JSON. With no reachable client it is the
951
- reason (shown without a Hermes app; a populated response is not shown):
518
+ Generate form screen with validation and error handling.
952
519
 
953
- ``````text
954
- Error: Error: No debuggable Hermes/React Native target with webSocketDebuggerUrl (is the app running?)
955
- CDP: Error: No debuggable Hermes/React Native target with webSocketDebuggerUrl (is the app running?); Metro events: not connected
956
- ``````
520
+ **Arguments:**
957
521
 
958
- #### syncdb_snapshot
522
+ - `name` (string) — Screen name (e.g., "CreateProduct")
523
+ - `modelName` (string) — Model being created/edited
524
+ - `fields` (string) — Comma-separated: `title:text,price:number,active:boolean`
959
525
 
960
- Capture, list, read, compare, or delete SyncDB snapshots. Snapshots live in the MCP process memory
961
- and are lost when it exits. A capture keeps the full mergeable TinyBase content privately for a later
962
- `syncdb_action` `mergeSnapshot`; returned state is redacted. Not gated.
526
+ **Workflow:**
963
527
 
964
- | Parameter | Type | Required | Description |
965
- |-----------|------|----------|-------------|
966
- | `action` | `capture` \| `list` \| `get` \| `compare` \| `delete` | yes | Operation |
967
- | `name`, `collection`, `entityId` | string | no | Capture filters, as in `get_syncdb_state` |
968
- | `includeDebugEvents` | boolean | no | Capture option |
969
- | `limit` | number | no | Capture option |
970
- | `snapshotId` | string | for `get`, `compare`, `delete` | Snapshot id from `capture` or `list` |
971
- | `otherSnapshotId` | string | for `compare` | Second snapshot. `compare` returns up to 200 path diffs. |
528
+ 1. Generate screen component with Page layout
529
+ 2. Add form fields from @terreno/ui
530
+ 3. Include validation logic
531
+ 4. Add generated `store/apiSdk.ts` mutation hooks (or syncdb `useMutate` for synced collections)
532
+ 5. Add loading/error/success states
533
+ 6. Provide navigation setup
972
534
 
973
- ``````json
974
- {"name":"syncdb_snapshot","arguments":{"action":"list"}}
975
- ``````
535
+ ### terreno_add_authentication
976
536
 
977
- ``````json
978
- []
979
- ``````
537
+ Generate authentication setup for new projects.
980
538
 
981
- #### syncdb_action
982
-
983
- Change a live SyncDB client, then return `{action, client, result, state}` with the redacted state
984
- after the change. Every action requires `TERRENO_MCP_EVAL`.
985
-
986
- | Parameter | Type | Required | Description |
987
- |-----------|------|----------|-------------|
988
- | `action` | enum | yes | `mutate`, `setLocalEntity`, `deleteLocalEntity`, `flush`, `reconcile`, `forceResync`, `resolveConflict`, `retryFailed`, `goOffline`, `goOnline`, `clearDebug`, `mergeSnapshot` |
989
- | `name` | string | no | Client name |
990
- | `collection`, `id` | string | no | Target row for `mutate`, `setLocalEntity`, `deleteLocalEntity` |
991
- | `operation` | `create` \| `update` \| `delete` | no | For `mutate` |
992
- | `data` | object | no | Row data for `mutate`, `setLocalEntity` |
993
- | `maxAttempts` | number | no | For `mutate` |
994
- | `deleted` | boolean | no | For `setLocalEntity` |
995
- | `seq` | number | no | For `setLocalEntity` |
996
- | `stream` | string | no | For `setLocalEntity` |
997
- | `mutationId` | string | no | For `resolveConflict` |
998
- | `strategy` | `useServer` \| `keepMine` | no | For `resolveConflict` |
999
- | `entityId` | string | no | For `retryFailed` |
1000
- | `snapshotId` | string | no | For `mergeSnapshot`; must belong to the same client |
539
+ **Arguments:**
1001
540
 
1002
- ``````json
1003
- {"name":"syncdb_action","arguments":{"action":"flush"}}
1004
- ``````
541
+ - `strategies` (string) — Comma-separated: "email", "github", "google"
542
+ - `includeRefreshToken` (string) — "yes" or "no" (default: "yes")
1005
543
 
1006
- Without the opt-in:
544
+ **Workflow:**
1007
545
 
1008
- ``````text
1009
- Refused: set `TERRENO_MCP_EVAL=1` to opt in to SyncDB state changes.
1010
- ``````
546
+ 1. Configure User model with passport-local-mongoose
547
+ 2. Set up auth routes in backend
548
+ 3. Wire `SessionProvider` / `useSession` from `@terreno/syncdb/auth` in the frontend
549
+ 4. Generate login screen
550
+ 5. Generate signup screen
551
+ 6. Set up token storage
552
+ 7. Provide environment variable list
1011
553
 
1012
- #### evaluate
554
+ ### terreno_migrate_to_terreno_app
1013
555
 
1014
- Run a JavaScript expression in the app's Hermes runtime with CDP `Runtime.evaluate`
1015
- (`awaitPromise`, `returnByValue`). Returns `{result, status}` or `{error, status}`, where `status` is
1016
- the connection status. Requires `TERRENO_MCP_EVAL`.
556
+ Guide for migrating from `setupServer` to the `TerrenoApp` fluent API pattern.
1017
557
 
1018
- | Parameter | Type | Required | Description |
1019
- |-----------|------|----------|-------------|
1020
- | `code` | string | yes | Expression to evaluate |
558
+ **Arguments:**
1021
559
 
1022
- ``````json
1023
- {"name":"evaluate","arguments":{"code":"1 + 1"}}
1024
- ``````
560
+ - `serverFile` (string, optional) — Path to server file to migrate (e.g., `src/server.ts`)
1025
561
 
1026
- Without the opt-in (a successful call needs a Hermes app and is not shown):
562
+ ### terreno_style_guide
1027
563
 
1028
- ``````text
1029
- Refused: set `TERRENO_MCP_EVAL=1` to opt in to `Runtime.evaluate` (arbitrary JS in the app runtime).
1030
- ``````
564
+ Returns comprehensive code style guide from project documentation.
1031
565
 
1032
- #### navigate
566
+ **No arguments required.**
1033
567
 
1034
- Run a fixed snippet in the app that calls `require("expo-router").router.navigate(path)`, or
1035
- `push(path)` when `navigate` is missing. Returns `{result, status}` with `result` like
1036
- `{ok, method}`, or `{error, status}`. Requires `TERRENO_MCP_EVAL`.
568
+ **Returns:**
1037
569
 
1038
- | Parameter | Type | Required | Description |
1039
- |-----------|------|----------|-------------|
1040
- | `path` | string | yes | expo-router path |
570
+ - TypeScript conventions
571
+ - React/React Native patterns
572
+ - Backend API conventions
573
+ - Testing practices
574
+ - Logging guidelines
1041
575
 
1042
- ``````json
1043
- {"name":"navigate","arguments":{"path":"/settings"}}
1044
- ``````
576
+ ## Environment Variables
1045
577
 
1046
- Without the opt-in (a successful call needs a Hermes app and is not shown):
578
+ ### Server Configuration
1047
579
 
1048
- ``````text
1049
- Refused: set `TERRENO_MCP_EVAL=1` to opt in to CDP-driven navigation (runs a fixed expo-router snippet in the app).
1050
- ``````
580
+ | Variable | Default | Description |
581
+ |----------|---------|-------------|
582
+ | `PORT` | `8080` | HTTP server port |
583
+ | `MCP_HOST` or `HOST` | `0.0.0.0` | Server host address |
584
+ | `TERRENO_MCP_DOCS_DIR` | `../docs` | Path to documentation directory (relative to dist/) |
585
+ | `TERRENO_PROJECT_ROOT` | nearest Terreno project root | Override local package, database, and log discovery |
586
+ | `TERRENO_METRO_URL` | Frontend script port or `http://localhost:8082` | Metro origin used for `/events` and CDP discovery |
587
+ | `TERRENO_MCP_EVAL` | Disabled | Set to `1` to enable local MCP `evaluate`, `navigate`, and SyncDB state-changing actions |
588
+ | `TERRENO_BROWSER_LOGS` | Enabled only when `NODE_ENV=development` | Set to `true` to opt in in another non-production environment or `false` to disable; production rejects the route, and non-loopback clients must authenticate |
1051
589
 
1052
- #### browser
1053
-
1054
- Drive a headless page with Bun's built-in `Bun.WebView`: system WebKit on macOS; on Linux and
1055
- Windows, Chrome, Chromium, or Edge over the Chrome DevTools Protocol (set `BUN_CHROME_PATH` when Bun
1056
- cannot find one). One session per MCP process; calls are serialized. Use it to exercise a changed web
1057
- flow, then `snapshot` and `screenshot` as proof, then `close`.
1058
-
1059
- | Action | Uses | Notes |
1060
- |--------|------|-------|
1061
- | `open` | `url` (required), `width` (1280), `height` (720), `dataDir` | `http:`/`https:` only. Closes any open session. Storage is ephemeral unless `dataDir`, which must be under the project root. |
1062
- | `click` | `selector` (required), `timeout` | `timeout` is the selector wait in ms |
1063
- | `type` | `text`, `selector`, `timeout` | Clicks `selector` first when given |
1064
- | `press` | `key` (required), `modifiers` | `modifiers`: `Shift`, `Control`, `Alt`, `Meta` |
1065
- | `scroll` | `selector` or `x`/`y` | Scrolls to `selector`, else by the pixel delta |
1066
- | `evaluate` | `code` (required) | Gated by `TERRENO_MCP_EVAL`; without it the call fails with JSON-RPC error `-32603` ``Refused: set `TERRENO_MCP_EVAL=1` to enable arbitrary JavaScript in the WebView.`` |
1067
- | `snapshot` | none | Up to 200 visible interactive elements with a usable `selector`, plus page text (20 000 chars). Password input values are `[REDACTED]`. |
1068
- | `screenshot` | `output` (required), `quality` | `.png`, `.jpg`/`.jpeg`, or `.webp` (other extensions write PNG). Path must be under the project root or `/opt/cursor/artifacts`. |
1069
- | `back`, `forward`, `reload` | none | History navigation |
1070
- | `wait` | `timeout` | Sleeps `timeout` ms (default 1000) |
1071
- | `close` | none | Ends the session |
1072
-
1073
- Results are JSON with `action`, `ok`, and the page `title` and `url`, plus action-specific fields.
1074
- Calling any page action before `open` fails with `No browser session. Run the open action first.`
590
+ **Example:**
1075
591
 
1076
- ``````json
1077
- {"name":"browser","arguments":{"action":"open","url":"http://localhost:8082","width":1280,"height":720}}
1078
- {"name":"browser","arguments":{"action":"snapshot"}}
1079
- {"name":"browser","arguments":{"action":"screenshot","output":"artifacts/app.png"}}
1080
- {"name":"browser","arguments":{"action":"close"}}
592
+ ``````bash
593
+ PORT=3001 HOST=localhost bun run start
1081
594
  ``````
1082
595
 
1083
- ``````json
1084
- {
1085
- "action": "open",
1086
- "ok": true,
1087
- "title": "",
1088
- "url": "http://localhost:8082/"
1089
- }
1090
- ``````
596
+ ## Development
1091
597
 
1092
- ``````json
1093
- {
1094
- "action": "snapshot",
1095
- "ok": true,
1096
- "snapshot": {
1097
- "elements": [
1098
- …,
1099
- {
1100
- "ariaLabel": "Text input field",
1101
- "disabled": false,
1102
- "name": null,
1103
- "placeholder": "Password",
1104
- "role": "input",
1105
- "selector": "#login-screen-password-input",
1106
- "text": "[REDACTED]",
1107
- "type": "password"
1108
- },
1109
- …,
1110
- {
1111
- "ariaLabel": "Log In",
1112
- "disabled": true,
1113
- "name": null,
1114
- "placeholder": null,
1115
- "role": "button",
1116
- "selector": "[data-testid=\"login-screen-submit-button\"]",
1117
- "text": "Log In",
1118
- "type": "button"
1119
- },
1120
- …
1121
- ],
1122
- "text": "Welcome Back\nEmail\nPassword\n\nLog In\nForgot password?\n…",
1123
- "title": "",
1124
- "url": "http://localhost:8082/login"
1125
- }
1126
- }
1127
- ``````
598
+ ``````bash
599
+ # Install dependencies
600
+ bun install
1128
601
 
1129
- ``````json
1130
- {
1131
- "action": "screenshot",
1132
- "ok": true,
1133
- "output": "…/artifacts/app.png",
1134
- "title": "",
1135
- "url": "http://localhost:8082/"
1136
- }
1137
- ``````
602
+ # Build
603
+ bun run build
1138
604
 
1139
- ### `TERRENO_MCP_EVAL` gate
1140
-
1141
- `evaluate`, `navigate`, `syncdb_action`, and the `browser` `evaluate` action run only when the local
1142
- server's environment has `TERRENO_MCP_EVAL=1` or `TERRENO_MCP_EVAL=true`. The variable is read on
1143
- every call; set it in the editor's server entry (`"env": {"TERRENO_MCP_EVAL": "1"}`).
1144
-
1145
- | Gated | What it allows |
1146
- |-------|----------------|
1147
- | `evaluate` | Any JavaScript in the app's Hermes runtime |
1148
- | `navigate` | A fixed expo-router snippet in the app runtime |
1149
- | `syncdb_action` (all actions) | Writes to the app's local SyncDB store, outbox, and sync state |
1150
- | `browser` `evaluate` | Any JavaScript in the WebView page |
1151
-
1152
- The gate exists because these calls run code with the app's full privileges: its session tokens and
1153
- secure storage, its network access as the signed-in user, and its local data. An agent can be steered
1154
- by text it reads, including log lines and database rows, so code execution in the app is a per-process
1155
- opt-in rather than a default.
1156
-
1157
- Not gated: `get_client_state`, `get_rtk_state`, `get_syncdb_state`, and `syncdb_snapshot` also use
1158
- `Runtime.evaluate` when the app is in another process, but only with fixed read expressions built by
1159
- the server (caller values are JSON-encoded), and their output is redacted. `database_query` is
1160
- read-only by operation allowlist and operator checks. The other `browser` actions only interact with
1161
- the page as a user would.
1162
-
1163
- ### Metro and CDP connection
1164
-
1165
- `read_logs` (`metro`, `app`), `last_error`, `get_client_state`, `get_syncdb_state`,
1166
- `syncdb_snapshot`, `syncdb_action`, `evaluate`, and `navigate` share one module-level session
1167
- (`src/local/metro/metroDevSession.ts`):
1168
-
1169
- - **Metro URL:** `TERRENO_METRO_URL`, else `--port N` from the frontend workspace's `scripts.start`
1170
- (or `scripts.web`), else `http://localhost:8082`.
1171
- - **Target:** `GET <metro>/json/list` (4 s timeout); the first target whose title contains `hermes`,
1172
- else whose description contains `react native`, else the first with a `webSocketDebuggerUrl`. A
1173
- web browser tab is not a target, so the `app` source and the CDP paths need a Hermes app (dev build
1174
- or Expo Go) attached to Metro.
1175
- - **Origin:** the debugger socket sends `Origin: <scheme>//127.0.0.1:<Metro port>`.
1176
- `@react-native/dev-middleware` rejects a socket without an Origin, and Expo CLI closes one whose
1177
- host is not `127.0.0.1:<Metro port>`. A base URL that reaches Metro through a port-remapping tunnel,
1178
- or a Metro whose server URL is overridden by `REACT_NATIVE_PACKAGER_HOSTNAME` or
1179
- `EXPO_PACKAGER_PROXY_URL`, still fails Expo's check.
1180
- - **Lifetime:** connect lazily on first use, send `Runtime.enable`, then keep the socket for the life
1181
- of the MCP process. Each CDP call times out after 8 s. After a close, the next call that needs the
1182
- app reconnects, with no backoff.
1183
-
1184
- The status appears as `CDP: <cdp status>; Metro events: <events status>` in `read_logs`
1185
- `connection`, in `last_error` misses, and in `get_client_state` `layer: "none"` responses.
1186
-
1187
- | CDP status | Meaning |
1188
- |------------|---------|
1189
- | `not connected` | No call has needed the app yet |
1190
- | `connected to <title>` | This call opened a new socket |
1191
- | `connected` | This call reused the open socket |
1192
- | `Error: No debuggable Hermes/React Native target with webSocketDebuggerUrl (is the app running?)` | Metro answered, but no Hermes app is attached |
1193
- | `TypeError: Unable to connect. Is the computer able to access the url?` | Metro is not reachable (Bun's fetch error; the Metro URL is not included) |
1194
- | `Error: CDP websocket error: …` | The debugger socket upgrade failed |
1195
- | `CDP disconnected (code <code>: <reason>)` | Metro closed the socket; a `[NEW_DEBUGGER_OPENED]` reason adds a hint (see below) |
1196
- | `CDP socket not open after connect` | The socket closed during connect |
1197
-
1198
- | Metro events status | Meaning |
1199
- |---------------------|---------|
1200
- | `not connected` | No call has requested the `metro` source |
1201
- | `connected ws://<host>/events` / `connected` | New / reused socket |
1202
- | `Error: Metro /events unreachable at ws://<host>/events` | Metro is not reachable |
1203
-
1204
- The events status is not updated when the socket later closes, so a call that skips `metro` can
1205
- print a stale `connected`.
1206
-
1207
- **`metro` source under Expo CLI 57:** the `/events` socket opens and reports `connected`, but
1208
- receives nothing. Expo CLI 57.0.24 (`createEventsSocket`) registers its connection handler on one
1209
- WebSocket server and serves `/events` from a second one with no handler, so no client is ever counted
1210
- and no event is broadcast. The `metro` source works only with a Metro whose `/events` broadcasts,
1211
- such as the React Native community CLI. Read bundler errors from the Metro terminal instead.
1212
-
1213
- ### React Native DevTools and the `app` source
1214
-
1215
- React Native 0.86 (Terreno 57) reports `supportsMultipleDebuggers` for each app page. On a normal
1216
- `expo start`, React Native DevTools and `terreno-mcp-local` attach to the app at the same time, and
1217
- opening DevTools does not affect the `app` source.
1218
-
1219
- When Metro allows one debugger per app (older React Native, or Expo running headless with
1220
- `EXPO_UNSTABLE_HEADLESS=1` or in a webcontainer), opening DevTools (`j` in Metro) disconnects the
1221
- MCP. A call that does not read the app then shows, verbatim:
1222
-
1223
- ``````text
1224
- CDP: CDP disconnected (code 1000: [NEW_DEBUGGER_OPENED] New debugger opened for the same app instance); another debugger (such as React Native DevTools) took the session, and the next call that reads the app reconnects and disconnects it
1225
- ``````
605
+ # (Build runs sync-ui-docs, sync-versioned-docs, and sync-package-guidelines before tsc.)
606
+ # To refresh only bundled package AI guidelines from the monorepo:
607
+ # bun run sync-package-guidelines
1226
608
 
1227
- The next call that reads the app (`read_logs` or `last_error` with `app`, or any CDP tool)
1228
- reconnects automatically without an error, reports `CDP: connected to <app> (<device>)`, and
1229
- disconnects DevTools, which shows `[NEW_DEBUGGER_OPENED] New debugger opened for the same app
1230
- instance`. App console output logged while DevTools held the connection is not in `read_logs`; the
1231
- first call after recovery may return only entries from before the gap.
1232
-
1233
- Workaround: use one or the other. Close DevTools, then call `read_logs` with `sources: ["app"]`
1234
- twice: the first call reconnects, the second shows new entries. To keep DevTools open, stop calling
1235
- tools that read the app, or each call takes the connection back.
1236
-
1237
- ## What the local server can and cannot see
1238
-
1239
- The local server inspects the project from the outside. It never imports or runs consumer code in its
1240
- own process: model files are read as text, and the app's runtime is reached only over Metro and CDP.
1241
-
1242
- **Can see:**
1243
-
1244
- | Surface | How |
1245
- |---------|-----|
1246
- | Package manifests | Root and backend/frontend workspace `package.json` files (declared ranges); `bun.lock` presence only |
1247
- | MongoDB | `MONGO_URI` from the environment or `backend/.env`; collections, indexes, counts, and read-only queries |
1248
- | Model source | First 80 lines of each `backend/src/models/*.ts`, as text |
1249
- | Backend and browser logs | `app.log` and `browser.log` JSONL under `.terreno/logs/` in `backend/`, `example-backend/`, or the root |
1250
- | Metro | `/json/list` targets and the `/events` socket |
1251
- | Running Hermes app | Console output, client state, SyncDB state over CDP; code execution and navigation with `TERRENO_MCP_EVAL` |
1252
- | Web app | Rendered page through the `browser` tool's own headless WebView |
1253
-
1254
- **Cannot see:**
1255
-
1256
- - Production or deployed environments: no remote logs, databases, or apps. Production backends do
1257
- not write the JSONL log or accept browser logs.
1258
- - The web app's JavaScript heap or console through CDP. A browser tab is not a Metro debug target; use
1259
- `browser`, `browser.log`, and the backend log instead.
1260
- - Native crash reports, device logs, or a device that is not attached to the dev server's Metro.
1261
- - App console output from before the first `app` read, or while another debugger held a
1262
- single-debugger session.
1263
- - Metro bundler events under Expo CLI 57 (the `metro` source is empty).
1264
- - The resolved dependency tree: `application_info` reports declared ranges, not installed versions.
1265
- - `example-backend/.env` and `example-backend/src/models/` (database discovery reads `backend/` only).
1266
-
1267
- ## Environment variables
1268
-
1269
- ### Hosted server variables
1270
-
1271
- | Variable | Default | Effect |
1272
- |----------|---------|--------|
1273
- | `PORT` | `8080` | HTTP listen port. Parsed with `parseInt`, so leading digits win (`80abc` is port 80); a value with no leading digits stops startup with `Invalid PORT value`. |
1274
- | `MCP_HOST`, then `HOST` | `0.0.0.0` | Listen host |
1275
- | `NODE_ENV` | unset | `production` disables console colors and warns at startup when `SENTRY_DSN` is unset |
1276
- | `SENTRY_DSN` | unset (Sentry disabled) | Sentry DSN |
1277
- | `APP_ENV` | `development` | Sentry `environment` |
1278
- | `SENTRY_TRACES_SAMPLE_RATE` | `0.1` | Sentry trace sample rate (float) |
1279
- | `TERRENO_MCP_DOCS_DIR` | `<module dir>/docs` (`dist/docs`), else `docs/` next to the executable | Root for bundled resources, prompts' markdown, the search index, and upgrade notes |
1280
- | `TERRENO_HELP_API_URL` | unset | Terreno backend help API for announcements in the three update-note tools. Unset means bundled upgrade notes only. |
1281
- | `TERRENO_HELP_API_TOKEN` | unset | Bearer token for `TERRENO_HELP_API_URL` |
1282
- | `TERRENO_MCP_RATE_LIMIT_MAX` | `120` | JSON-RPC requests per client IP per window; `0` disables. Invalid values stop startup. See [Rate limiting](#rate-limiting). |
1283
- | `TERRENO_MCP_RATE_LIMIT_WINDOW_MS` | `60000` | Rate-limit window in milliseconds (minimum 1) |
1284
- | `TERRENO_MCP_TRUST_PROXY_HOPS` | `1` when `K_SERVICE` is set, else `0` | Reverse-proxy hops trusted for the client IP in `X-Forwarded-For` |
1285
- | `K_SERVICE` | set by Cloud Run | Read only to choose the `TERRENO_MCP_TRUST_PROXY_HOPS` default |
1286
-
1287
- ### Local server variables
1288
-
1289
- | Variable | Default | Effect |
1290
- |----------|---------|--------|
1291
- | `TERRENO_PROJECT_ROOT` | Discovered root (see [Running the local server](#running-the-local-server)) | Root for packages, database, logs, Metro URL, and browser paths |
1292
- | `TERRENO_METRO_URL` | `--port` from the frontend `start`/`web` script, else `http://localhost:8082` | Metro HTTP origin for `/json/list`, `/events`, and the debugger Origin's port |
1293
- | `TERRENO_MCP_EVAL` | disabled | `1` or `true` enables `evaluate`, `navigate`, `syncdb_action`, and `browser` `evaluate`. See [`TERRENO_MCP_EVAL` gate](#terreno_mcp_eval-gate). |
1294
- | `MONGO_URI` | `MONGO_URI`, then `MONGODB_URI`, from `<root>/backend/.env` | MongoDB for `database_schema` and `database_query` |
1295
- | `BUN_CHROME_PATH` | Bun auto-discovery | Chrome, Chromium, or Edge binary for `browser` on Linux and Windows (read by Bun, not Terreno) |
1296
-
1297
- ### App-side variables that feed the local server
1298
-
1299
- | Variable | Read by | Default | Effect |
1300
- |----------|---------|---------|--------|
1301
- | `TERRENO_LOG_FILE` | `@terreno/api` logger | on when `NODE_ENV !== "production"` | Backend JSONL transport to `<cwd>/.terreno/logs/app.log` (5 MB × 3 files, level `debug`). `false`/`0` forces off, `true`/`1` forces on. A toggle, not a path. |
1302
- | `TERRENO_BROWSER_LOGS` | `@terreno/api` `TerrenoApp` | on only when `NODE_ENV === "development"` | Registers `POST /__terreno/browser-logs`, which appends to `<cwd>/.terreno/logs/browser.log`. `true`/`1` opts in outside development, `false`/`0` disables; production never registers it. See [Development browser-log ingestion](api.md#development-browser-log-ingestion). |
1303
- | `NODE_ENV` | `@terreno/api` | unset | Drives both defaults above |
1304
- | `TERRENO_DEV_CONSOLE_LOGGER_TEST` | `@terreno/syncdb` `installTerrenoDevConsoleLogger` | unset | Test-only: `true` lets the console logger install outside `__DEV__` |
1305
-
1306
- The full cross-package list is in [Environment variables](environment-variables.md).
609
+ # Watch mode (rebuilds on changes)
610
+ bun run dev
1307
611
 
1308
- ## Development
612
+ # Start server
613
+ bun run start
1309
614
 
1310
- ``````bash
1311
- # From the monorepo root
1312
- bun install
1313
- bun run mcp:build # sync docs + tsc + copy docs into dist/
1314
-
1315
- # From mcp-server/
1316
- bun run compile # type-check only
1317
- bun run build # full artifact (sync-ui-docs, sync-versioned-docs, sync-package-guidelines, tsc, copy docs)
1318
- bun run dev # hosted server with watch
1319
- bun run start # hosted server from dist/ (run build first)
1320
- bun run test # bun test src/
615
+ # Lint
1321
616
  bun run lint
1322
- ``````
1323
617
 
1324
- `bun run sync-package-guidelines` refreshes only the bundled package AI guidelines.
618
+ # Fix lint issues
619
+ bun run lint:fix
620
+ ``````
1325
621
 
1326
622
  ### Docker
1327
623
 
1328
- Build from the repository root, as `scripts/ci/gcp-deploy.sh mcp-prod` does. The image needs the
1329
- monorepo (`api/`, `blocks/`, `ui/`, `docs/`, `website/versioned_docs/`):
1330
-
1331
624
  ``````bash
1332
- docker build --file mcp-server/Dockerfile -t terreno-mcp-server .
625
+ # Build image
626
+ docker build -t terreno-mcp-server ./mcp-server
627
+
628
+ # Run container
1333
629
  docker run --rm -p 8080:8080 terreno-mcp-server
1334
630
  ``````
1335
631
 
1336
- `mcp-server/src/docs/ui-types-documentation.json` and `mcp-server/src/docs/versioned/` are
1337
- git-ignored, so the builder stage generates them. `bun run --filter "@terreno/ui" types` writes the
1338
- TypeDoc JSON that `terreno_get_component_docs` reads. `sync-versioned-docs` and
1339
- `sync-package-guidelines` fill the `terreno_search_docs` corpus. Without that step the image served
1340
- the one-component test fixture (`No component named "Badge". Examples: Button`) and searched only
1341
- `resources/*.md`.
1342
-
1343
632
  ### Testing with MCP Inspector
1344
633
 
1345
634
  ``````bash
1346
- # Local server over stdio
1347
- bunx @modelcontextprotocol/inspector bun ./mcp-server/dist/local/index.js
635
+ # Start server
636
+ bun run start
1348
637
 
1349
- # Hosted server: start it, then connect the Inspector with Streamable HTTP to http://localhost:8080/mcp
1350
- TERRENO_MCP_RATE_LIMIT_MAX=0 bun ./mcp-server/dist/index.js
1351
- bunx @modelcontextprotocol/inspector
638
+ # In another terminal, use MCP inspector
639
+ npx @modelcontextprotocol/inspector bun run ./mcp-server/dist/index.js
1352
640
  ``````
1353
641
 
1354
642
  ## Architecture
@@ -1356,34 +644,29 @@ bunx @modelcontextprotocol/inspector
1356
644
  ``````
1357
645
  mcp-server/
1358
646
  ├── src/
1359
- │ ├── index.ts # Hosted entry: reads env, starts the HTTP server
1360
- │ ├── httpApp.ts # Express app: JSON-RPC handlers, routes, health check
1361
- │ ├── rateLimit.ts # Per-IP JSON-RPC rate limit and trust-proxy config
1362
- │ ├── instrument.ts # Sentry init
1363
- │ ├── tools.ts # Hosted tools and dispatch
1364
- │ ├── bootstrap.ts # terreno_bootstrap_ai_rules tool and terreno_bootstrap prompt
1365
- │ ├── prompts.ts # Workflow prompts
1366
- │ ├── resources.ts # Documentation resources
1367
- │ ├── docsRoot.ts # Bundled docs root (TERRENO_MCP_DOCS_DIR)
1368
- │ ├── search/ # Docs search index
1369
- │ ├── help/ # Update-note search and help API client
1370
- │ ├── docs/ # Bundled markdown (resources, upgrades, guidelines)
1371
- │ └── local/
1372
- │ ├── index.ts # Local entry (stdio)
1373
- │ ├── localServer.ts # Server and tools/list, tools/call handlers
1374
- │ ├── localTools.ts # Local tool schemas and dispatch
1375
- │ ├── projectRoot.ts # Project root discovery
1376
- │ ├── logPaths.ts # .terreno/logs discovery
1377
- │ ├── metro/ # Metro /events and CDP session
1378
- │ └── tools/ # Tool implementations
1379
- ├── dist/ # Compiled output
1380
- └── Dockerfile # Hosted server image
647
+ │ ├── index.ts # Express server + JSON-RPC handlers
648
+ │ ├── resources.ts # Documentation resource loader
649
+ │ ├── tools.ts # Code generation tools
650
+ │ ├── prompts.ts # Workflow prompts
651
+ │ └── docs/ # Inline documentation content
652
+ ├── dist/ # Compiled output
653
+ └── Dockerfile # Container image
1381
654
  ``````
1382
655
 
656
+ **JSON-RPC 2.0 handlers:**
657
+
658
+ - `resources/list` — List available documentation
659
+ - `resources/read` — Read documentation content
660
+ - `tools/list` — List available tools
661
+ - `tools/call` — Execute a tool
662
+ - `prompts/list` — List available prompts
663
+ - `prompts/get` — Get prompt details
664
+
1383
665
  ## Deployment
1384
666
 
1385
- The hosted server runs as the `terreno-mcp` Cloud Run service. CircleCI's continuous-delivery
1386
- pipeline (`.circleci/config.yml`, triggered by changes under `mcp-server/`) builds and rolls the
1387
- image; Terraform owns the service's structure, and its hostname cutover is in
1388
- [infra/flourish/README.md](../../infra/flourish/README.md#hosted-mcp-hostname).
667
+ See [mcp-server/README.md](../../mcp-server/README.md#cicd) for:
668
+
669
+ - GitHub Actions workflows
670
+ - Google Cloud Run deployment
671
+ - Required secrets configuration
1389
672
  - Workload Identity Federation setup