@sashido/mcp-server 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +201 -2
  3. package/dist/access.d.ts +47 -0
  4. package/dist/access.d.ts.map +1 -0
  5. package/dist/access.js +156 -0
  6. package/dist/access.js.map +1 -0
  7. package/dist/advice/format.d.ts +22 -0
  8. package/dist/advice/format.d.ts.map +1 -0
  9. package/dist/advice/format.js +70 -0
  10. package/dist/advice/format.js.map +1 -0
  11. package/dist/advice/schemaChecks.d.ts +17 -0
  12. package/dist/advice/schemaChecks.d.ts.map +1 -0
  13. package/dist/advice/schemaChecks.js +64 -0
  14. package/dist/advice/schemaChecks.js.map +1 -0
  15. package/dist/audit.d.ts +32 -0
  16. package/dist/audit.d.ts.map +1 -0
  17. package/dist/audit.js +41 -0
  18. package/dist/audit.js.map +1 -0
  19. package/dist/auth.d.ts +17 -0
  20. package/dist/auth.d.ts.map +1 -0
  21. package/dist/auth.js +72 -0
  22. package/dist/auth.js.map +1 -0
  23. package/dist/bin/login.d.ts +3 -0
  24. package/dist/bin/login.d.ts.map +1 -0
  25. package/dist/bin/login.js +16 -0
  26. package/dist/bin/login.js.map +1 -0
  27. package/dist/bin/mcp-server.d.ts +3 -0
  28. package/dist/bin/mcp-server.d.ts.map +1 -0
  29. package/dist/bin/mcp-server.js +27 -0
  30. package/dist/bin/mcp-server.js.map +1 -0
  31. package/dist/client/parse.d.ts +219 -0
  32. package/dist/client/parse.d.ts.map +1 -0
  33. package/dist/client/parse.js +414 -0
  34. package/dist/client/parse.js.map +1 -0
  35. package/dist/client/sashido.d.ts +213 -0
  36. package/dist/client/sashido.d.ts.map +1 -0
  37. package/dist/client/sashido.js +289 -0
  38. package/dist/client/sashido.js.map +1 -0
  39. package/dist/config.d.ts +27 -0
  40. package/dist/config.d.ts.map +1 -0
  41. package/dist/config.js +70 -0
  42. package/dist/config.js.map +1 -0
  43. package/dist/context.d.ts +33 -0
  44. package/dist/context.d.ts.map +1 -0
  45. package/dist/context.js +132 -0
  46. package/dist/context.js.map +1 -0
  47. package/dist/index.d.ts +10 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +8 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/server.d.ts +13 -0
  52. package/dist/server.d.ts.map +1 -0
  53. package/dist/server.js +60 -0
  54. package/dist/server.js.map +1 -0
  55. package/dist/session/redis-client.d.ts +23 -0
  56. package/dist/session/redis-client.d.ts.map +1 -0
  57. package/dist/session/redis-client.js +136 -0
  58. package/dist/session/redis-client.js.map +1 -0
  59. package/dist/session/session-manager.d.ts +6 -0
  60. package/dist/session/session-manager.d.ts.map +1 -0
  61. package/dist/session/session-manager.js +68 -0
  62. package/dist/session/session-manager.js.map +1 -0
  63. package/dist/session/sse-pubsub.d.ts +23 -0
  64. package/dist/session/sse-pubsub.d.ts.map +1 -0
  65. package/dist/session/sse-pubsub.js +121 -0
  66. package/dist/session/sse-pubsub.js.map +1 -0
  67. package/dist/session/types.d.ts +43 -0
  68. package/dist/session/types.d.ts.map +1 -0
  69. package/dist/session/types.js +2 -0
  70. package/dist/session/types.js.map +1 -0
  71. package/dist/tools/advisor/index.d.ts +3 -0
  72. package/dist/tools/advisor/index.d.ts.map +1 -0
  73. package/dist/tools/advisor/index.js +149 -0
  74. package/dist/tools/advisor/index.js.map +1 -0
  75. package/dist/tools/apps/index.d.ts +3 -0
  76. package/dist/tools/apps/index.d.ts.map +1 -0
  77. package/dist/tools/apps/index.js +113 -0
  78. package/dist/tools/apps/index.js.map +1 -0
  79. package/dist/tools/audit/index.d.ts +6 -0
  80. package/dist/tools/audit/index.d.ts.map +1 -0
  81. package/dist/tools/audit/index.js +69 -0
  82. package/dist/tools/audit/index.js.map +1 -0
  83. package/dist/tools/builds/index.d.ts +3 -0
  84. package/dist/tools/builds/index.d.ts.map +1 -0
  85. package/dist/tools/builds/index.js +85 -0
  86. package/dist/tools/builds/index.js.map +1 -0
  87. package/dist/tools/classes/index.d.ts +3 -0
  88. package/dist/tools/classes/index.d.ts.map +1 -0
  89. package/dist/tools/classes/index.js +223 -0
  90. package/dist/tools/classes/index.js.map +1 -0
  91. package/dist/tools/cloud-functions/index.d.ts +3 -0
  92. package/dist/tools/cloud-functions/index.d.ts.map +1 -0
  93. package/dist/tools/cloud-functions/index.js +154 -0
  94. package/dist/tools/cloud-functions/index.js.map +1 -0
  95. package/dist/tools/deployments/index.d.ts +3 -0
  96. package/dist/tools/deployments/index.d.ts.map +1 -0
  97. package/dist/tools/deployments/index.js +124 -0
  98. package/dist/tools/deployments/index.js.map +1 -0
  99. package/dist/tools/engines/index.d.ts +3 -0
  100. package/dist/tools/engines/index.d.ts.map +1 -0
  101. package/dist/tools/engines/index.js +68 -0
  102. package/dist/tools/engines/index.js.map +1 -0
  103. package/dist/tools/env/index.d.ts +3 -0
  104. package/dist/tools/env/index.d.ts.map +1 -0
  105. package/dist/tools/env/index.js +116 -0
  106. package/dist/tools/env/index.js.map +1 -0
  107. package/dist/tools/files/index.d.ts +3 -0
  108. package/dist/tools/files/index.d.ts.map +1 -0
  109. package/dist/tools/files/index.js +132 -0
  110. package/dist/tools/files/index.js.map +1 -0
  111. package/dist/tools/health/index.d.ts +3 -0
  112. package/dist/tools/health/index.d.ts.map +1 -0
  113. package/dist/tools/health/index.js +47 -0
  114. package/dist/tools/health/index.js.map +1 -0
  115. package/dist/tools/infrastructure/deployments.d.ts +23 -0
  116. package/dist/tools/infrastructure/deployments.d.ts.map +1 -0
  117. package/dist/tools/infrastructure/deployments.js +76 -0
  118. package/dist/tools/infrastructure/deployments.js.map +1 -0
  119. package/dist/tools/infrastructure/index.d.ts +9 -0
  120. package/dist/tools/infrastructure/index.d.ts.map +1 -0
  121. package/dist/tools/infrastructure/index.js +164 -0
  122. package/dist/tools/infrastructure/index.js.map +1 -0
  123. package/dist/tools/jobs/index.d.ts +3 -0
  124. package/dist/tools/jobs/index.d.ts.map +1 -0
  125. package/dist/tools/jobs/index.js +338 -0
  126. package/dist/tools/jobs/index.js.map +1 -0
  127. package/dist/tools/logs/index.d.ts +3 -0
  128. package/dist/tools/logs/index.d.ts.map +1 -0
  129. package/dist/tools/logs/index.js +133 -0
  130. package/dist/tools/logs/index.js.map +1 -0
  131. package/dist/tools/objects/index.d.ts +3 -0
  132. package/dist/tools/objects/index.d.ts.map +1 -0
  133. package/dist/tools/objects/index.js +185 -0
  134. package/dist/tools/objects/index.js.map +1 -0
  135. package/dist/tools/push/index.d.ts +3 -0
  136. package/dist/tools/push/index.d.ts.map +1 -0
  137. package/dist/tools/push/index.js +434 -0
  138. package/dist/tools/push/index.js.map +1 -0
  139. package/dist/tools/types/index.d.ts +3 -0
  140. package/dist/tools/types/index.d.ts.map +1 -0
  141. package/dist/tools/types/index.js +45 -0
  142. package/dist/tools/types/index.js.map +1 -0
  143. package/dist/tools/webhooks/index.d.ts +3 -0
  144. package/dist/tools/webhooks/index.d.ts.map +1 -0
  145. package/dist/tools/webhooks/index.js +443 -0
  146. package/dist/tools/webhooks/index.js.map +1 -0
  147. package/dist/transports/http.d.ts +4 -0
  148. package/dist/transports/http.d.ts.map +1 -0
  149. package/dist/transports/http.js +241 -0
  150. package/dist/transports/http.js.map +1 -0
  151. package/dist/transports/stdio.d.ts +7 -0
  152. package/dist/transports/stdio.d.ts.map +1 -0
  153. package/dist/transports/stdio.js +29 -0
  154. package/dist/transports/stdio.js.map +1 -0
  155. package/dist/typegen/dart.d.ts +3 -0
  156. package/dist/typegen/dart.d.ts.map +1 -0
  157. package/dist/typegen/dart.js +84 -0
  158. package/dist/typegen/dart.js.map +1 -0
  159. package/dist/typegen/kotlin.d.ts +3 -0
  160. package/dist/typegen/kotlin.d.ts.map +1 -0
  161. package/dist/typegen/kotlin.js +84 -0
  162. package/dist/typegen/kotlin.js.map +1 -0
  163. package/dist/typegen/model.d.ts +37 -0
  164. package/dist/typegen/model.d.ts.map +1 -0
  165. package/dist/typegen/model.js +82 -0
  166. package/dist/typegen/model.js.map +1 -0
  167. package/dist/typegen/swift.d.ts +3 -0
  168. package/dist/typegen/swift.d.ts.map +1 -0
  169. package/dist/typegen/swift.js +105 -0
  170. package/dist/typegen/swift.js.map +1 -0
  171. package/dist/typegen/typescript.d.ts +3 -0
  172. package/dist/typegen/typescript.d.ts.map +1 -0
  173. package/dist/typegen/typescript.js +73 -0
  174. package/dist/typegen/typescript.js.map +1 -0
  175. package/dist/upgrade/rules.d.ts +27 -0
  176. package/dist/upgrade/rules.d.ts.map +1 -0
  177. package/dist/upgrade/rules.js +133 -0
  178. package/dist/upgrade/rules.js.map +1 -0
  179. package/dist/upgrade/scan.d.ts +18 -0
  180. package/dist/upgrade/scan.d.ts.map +1 -0
  181. package/dist/upgrade/scan.js +38 -0
  182. package/dist/upgrade/scan.js.map +1 -0
  183. package/dist/upgrade/versions.d.ts +8 -0
  184. package/dist/upgrade/versions.d.ts.map +1 -0
  185. package/dist/upgrade/versions.js +25 -0
  186. package/dist/upgrade/versions.js.map +1 -0
  187. package/dist/version.d.ts +3 -0
  188. package/dist/version.d.ts.map +1 -0
  189. package/dist/version.js +3 -0
  190. package/dist/version.js.map +1 -0
  191. package/package.json +67 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 SashiDo (https://www.sashido.io)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,202 @@
1
- # Temporary Holding Version
1
+ <p align="center">
2
+ <img src="https://dka575ofm4ao0.cloudfront.net/pages-transactional_logos/retina/27162/FDedZYhLRraWlTG8tqDP" alt="SashiDo Logo" width="200"/>
3
+ </p>
2
4
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
5
+ <h1 align="center">SashiDo MCP Server</h1>
6
+
7
+ <p align="center">
8
+ <strong>Manage your SashiDo Parse Server apps from Claude, Cursor, VS Code and any MCP client</strong>
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT"></a>
13
+ <a href="https://nodejs.org/"><img src="https://img.shields.io/badge/node-%3E%3D18.17-brightgreen.svg" alt="Node.js Version"></a>
14
+ <a href="https://modelcontextprotocol.io/"><img src="https://img.shields.io/badge/MCP-Compatible-purple.svg" alt="MCP Compatible"></a>
15
+ </p>
16
+
17
+ ---
18
+
19
+ A [Model Context Protocol](https://modelcontextprotocol.io/) server for [SashiDo](https://www.sashido.io). An AI assistant connected to it can work with your apps the way you do in the dashboard: read the schema and data, deploy, scale, read logs, send push notifications, run jobs, check an app for security problems and plan a Parse Server upgrade.
20
+
21
+ It acts with a **personal access token** that you create in the dashboard, with only the permissions you choose. Every call is recorded in your account's Activity.
22
+
23
+ ## Connect
24
+
25
+ ### 1. Create an access token
26
+
27
+ In the [SashiDo Dashboard](https://dashboard.sashido.io) open **Account › Access Tokens** and create a token. Choose:
28
+
29
+ - **Scopes**: what the token may do. *Read-only* is the safe choice to start with.
30
+ - **Apps**: all apps, or only some.
31
+ - **Expiry**: optional.
32
+
33
+ The dashboard shows ready configurations for the clients below.
34
+
35
+ ### 2. Add the server to your client
36
+
37
+ The hosted server is at **`https://mcp.sashido.io/mcp`** (Streamable HTTP), with `https://mcp.sashido.io/sse` for clients that only speak SSE. The token goes in the `Authorization` header.
38
+
39
+ **Claude Code**
40
+
41
+ ```bash
42
+ claude mcp add --transport http sashido https://mcp.sashido.io/mcp \
43
+ --header "Authorization: Bearer sashido_pat_..."
44
+ ```
45
+
46
+ **Cursor** (`~/.cursor/mcp.json`) and other clients with remote servers
47
+
48
+ ```json
49
+ {
50
+ "mcpServers": {
51
+ "sashido": {
52
+ "url": "https://mcp.sashido.io/mcp",
53
+ "headers": { "Authorization": "Bearer sashido_pat_..." }
54
+ }
55
+ }
56
+ }
57
+ ```
58
+
59
+ **Claude Desktop** and clients that run local servers (stdio)
60
+
61
+ ```json
62
+ {
63
+ "mcpServers": {
64
+ "sashido": {
65
+ "command": "npx",
66
+ "args": ["-y", "@sashido/mcp-server"],
67
+ "env": { "SASHIDO_ACCESS_TOKEN": "sashido_pat_..." }
68
+ }
69
+ }
70
+ }
71
+ ```
72
+
73
+ The local server also has the tools that read your files or fetch URLs (`upload_file_from_path`, `upload_file_from_url`). The hosted one doesn't.
74
+
75
+ ### Narrowing a connection
76
+
77
+ A connection can do less than its token allows. Over HTTP, add options to the URL. Over stdio, set the matching environment variables.
78
+
79
+ | URL option | stdio variable | Effect |
80
+ |---|---|---|
81
+ | `read_only=true` | `SASHIDO_READ_ONLY=true` | Only tools that change nothing |
82
+ | `apps=id1,id2` | `SASHIDO_APPS=id1,id2` | Only these apps |
83
+ | `features=objects,logs` | `SASHIDO_FEATURES=objects,logs` | Only these tool groups (see below) |
84
+
85
+ For example: `https://mcp.sashido.io/mcp?read_only=true&apps=AbC123`
86
+
87
+ ## Scopes
88
+
89
+ | Scope | Allows |
90
+ |---|---|
91
+ | `account:read` | List apps, read the Activity (audit log), see an app's infrastructure |
92
+ | `apps:read` | Builds, deployments, engines, health, Parse Server versions |
93
+ | `apps:write` | Create and rename apps, change engines, scale, high availability, Parse Server version |
94
+ | `deploy` | Build, deploy and cancel |
95
+ | `env:read` / `env:write` | Environment variables |
96
+ | `logs:read` | Logs |
97
+ | `data:read` | Schema, objects, cloud code, push status, jobs, webhooks, advisor |
98
+ | `data:write` | Change data and schema, call functions, send push, run jobs, webhooks |
99
+
100
+ A client only sees the tools its token and options allow. The SashiDo API checks the same scopes again on every call.
101
+
102
+ ## Tools
103
+
104
+ 71 tools in these groups (the names are what `features` takes):
105
+
106
+ | Group | Tools |
107
+ |---|---|
108
+ | `apps` | `list_apps`, `get_app`, `create_app`, `update_app_name` |
109
+ | `audit` | `get_audit_log`: who did what and through which client (dashboard, API, MCP) |
110
+ | `builds` | `list_builds`, `get_build_log`, `trigger_build`, `cancel_build` |
111
+ | `deployments` | `get_deployment_info`, `deploy_app`, `cancel_deployment` |
112
+ | `infrastructure` | `get_app_infrastructure`, `wait_for_deployment`, `list_parse_server_versions`, `change_parse_server_version` |
113
+ | `engines` | `list_engines`, `set_engine`, `scale_app`, `set_high_availability` |
114
+ | `health` | `check_app_health` |
115
+ | `env` | `list_env_vars`, `set_env_var`, `delete_env_var` |
116
+ | `classes` | `list_classes`, `get_class`, `get_class_permissions`, `create_class`, `add_field`, `update_class_permissions`, `delete_field`, `delete_class` |
117
+ | `types` | `generate_types`: typed classes from the schema for TypeScript, Swift, Kotlin and Dart |
118
+ | `advisor` | `get_app_advice`: security, data, cloud code and runtime checks, each with what to do; `plan_parse_server_upgrade`: what breaks in your cloud code on a newer version, where, and how to fix it |
119
+ | `objects` | `query_objects`, `get_object`, `create_object`, `update_object`, `delete_object` |
120
+ | `files` | `upload_file`, `upload_file_from_url`*, `upload_file_from_path`* |
121
+ | `cloud-functions` | `call_cloud_function`, `list_cloud_files`, `read_cloud_code` |
122
+ | `push` | `send_push`, `send_push_to_channels`, `get_push_status`, `list_audiences`, `create_audience`, `delete_audience`, `list_available_devices` |
123
+ | `jobs` | `list_available_jobs`, `list_scheduled_jobs`, `get_job_status`, `schedule_job`, `run_job`, `delete_job` |
124
+ | `logs` | `get_logs`, `get_error_logs` |
125
+ | `webhooks` | `list_/get_/create_/update_/delete_function_webhook`, `list_/get_/create_/update_/delete_trigger_webhook` |
126
+
127
+ \* local (stdio) only.
128
+
129
+ Tools carry MCP annotations (read-only, destructive, idempotent), so clients can ask before a change. Changes that are hard to undo, such as `change_parse_server_version`, show a preview first.
130
+
131
+ Things to ask:
132
+
133
+ - *"Check my-app for security problems and tell me what to fix."*
134
+ - *"What breaks in my cloud code if I move to Parse Server 6?"*
135
+ - *"Generate TypeScript types for my classes."*
136
+ - *"Deploy my-app and tell me when it's live."*
137
+ - *"Show the errors from the last hour."*
138
+ - *"Who changed my-app's environment variables this week?"*
139
+
140
+ ## Security
141
+
142
+ - **Tokens, not passwords.** The server never sees your password or a dashboard session. A token has only the scopes and apps you gave it. You can revoke it at any time in the dashboard.
143
+ - **Over HTTP, credentials come only from the request.** Each request brings its own token in the `Authorization` header. The server stores no tokens, and the token is never placed in a URL.
144
+ - **Audit log.** Every call is recorded in the account's Activity: the token, the client (e.g. Claude Code), the tool, the result and a request ID.
145
+ - **Hosted limits.** Browser origins must be allowed (`ALLOWED_ORIGINS`), requests are rate-limited per IP, and the tools that read local files or URLs are off.
146
+ - **Keys stay on the server side.** Calls to your app go through SashiDo's API with your token. The app's master key is never sent to the client or the model.
147
+
148
+ ## Self-hosting
149
+
150
+ ```bash
151
+ git clone https://github.com/sashido/sashido-mcp-server.git
152
+ cd sashido-mcp-server
153
+ npm ci && npm run build
154
+ npm run start:http # HTTP on 127.0.0.1:3100
155
+ npm run start:stdio # stdio, with SASHIDO_ACCESS_TOKEN set
156
+ ```
157
+
158
+ | Variable | Default | |
159
+ |---|---|---|
160
+ | `SASHIDO_API_URL` | `https://api.sashido.io` | |
161
+ | `SASHIDO_ACCESS_TOKEN` | – | stdio only; over HTTP every request brings its own |
162
+ | `SASHIDO_READ_ONLY`, `SASHIDO_APPS`, `SASHIDO_FEATURES` | – | stdio only: narrow the connection |
163
+ | `HOST` | `127.0.0.1` | `0.0.0.0` in a container |
164
+ | `PORT` / `MCP_SERVER_PORT` | `3100` | `PORT` wins |
165
+ | `ALLOWED_ORIGINS` | – | Comma-separated browser origins; clients that aren't browsers send none |
166
+ | `TRUST_PROXY` | – | `true` behind a load balancer, so rate limits see the client's IP |
167
+ | `RATE_LIMIT_PER_MINUTE` | `300` | Per client IP |
168
+ | `REDIS_URL`, `REDIS_PASSWORD`, `REDIS_DB` | – | SSE across several instances (Streamable HTTP needs no Redis) |
169
+ | `SESSION_TTL_SECONDS` | `3600` | SSE sessions in Redis |
170
+ | `LOG_LEVEL` | `info` | `debug`, `info`, `warn`, `error` |
171
+
172
+ ### HTTP endpoints
173
+
174
+ | Method | Path | |
175
+ |---|---|---|
176
+ | `POST` | `/mcp` | Streamable HTTP, stateless: any instance can answer |
177
+ | `GET` | `/sse`, `POST` `/sse/messages` | SSE for older clients; needs Redis with more than one instance |
178
+ | `GET` | `/health` | Status, version, SSE connections, Redis |
179
+
180
+ ### In a container
181
+
182
+ Any Node.js 20+ image works: `npm ci && npm run build`, then `npm start` with `HOST=0.0.0.0` (and `TRUST_PROXY=true` behind a load balancer). Put TLS in front of it, and use `/health` for the readiness and liveness probes.
183
+
184
+ ## Development
185
+
186
+ ```bash
187
+ npm run dev # stdio with reload
188
+ npm run typecheck
189
+ npm run test:run # vitest
190
+ ```
191
+
192
+ Tool calls are logged to stderr as JSON lines (tool, app, client, result, duration, request ID, a short fingerprint of the token), never with the arguments or the token itself.
193
+
194
+ ## Support
195
+
196
+ - [SashiDo documentation](https://www.sashido.io/en/docs)
197
+ - [support@sashido.io](mailto:support@sashido.io)
198
+ - [GitHub issues](https://github.com/sashido/sashido-mcp-server/issues)
199
+
200
+ ## License
201
+
202
+ MIT, see [LICENSE](LICENSE).
@@ -0,0 +1,47 @@
1
+ import type { ToolAnnotations } from "@modelcontextprotocol/sdk/types.js";
2
+ /**
3
+ * What each tool needs and does. A tool is registered only when the access
4
+ * token has its scope, it isn't a change in read-only mode, its group is
5
+ * enabled, and (for local-only tools) the server runs locally over stdio.
6
+ * The API enforces the same scopes; this keeps clients from seeing tools
7
+ * that would only fail.
8
+ */
9
+ export type Scope = "account:read" | "apps:read" | "apps:write" | "deploy" | "env:read" | "env:write" | "logs:read" | "data:read" | "data:write";
10
+ /** read: no changes; write: changes; destructive: deletes or overwrites */
11
+ export type ToolKind = "read" | "write" | "destructive";
12
+ export interface ToolPolicy {
13
+ scope: Scope;
14
+ kind: ToolKind;
15
+ /** Reads local files or fetches URLs: only on the user's own machine */
16
+ localOnly?: boolean;
17
+ /** Needs a token for all apps: one limited to some can't, e.g., add apps */
18
+ allApps?: boolean;
19
+ }
20
+ export declare const TOOL_POLICY: Record<string, ToolPolicy>;
21
+ /** Tool groups for the `features` option: the folders under src/tools */
22
+ export type ToolGroup = string;
23
+ export interface Access {
24
+ /** Scopes of the access token */
25
+ scopes: string[];
26
+ /** null: every app the token may use */
27
+ appIds: string[] | null;
28
+ /** Register only tools that change nothing */
29
+ readOnly: boolean;
30
+ /** null: every group */
31
+ groups: ToolGroup[] | null;
32
+ /** stdio: local files and URLs may be read */
33
+ local: boolean;
34
+ }
35
+ export declare function isToolAllowed(name: string, group: ToolGroup, access: Access): boolean;
36
+ /** MCP annotations, so clients can ask before changes and deletions */
37
+ export declare function annotationsFor(name: string): ToolAnnotations;
38
+ /**
39
+ * Options a client adds to the MCP URL (or env vars locally). They can only
40
+ * narrow what the token allows: ?read_only=true&apps=id1,id2&features=objects,logs
41
+ */
42
+ export declare function narrowAccess(base: Access, options: {
43
+ readOnly?: string;
44
+ apps?: string;
45
+ features?: string;
46
+ }): Access;
47
+ //# sourceMappingURL=access.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAE1E;;;;;;GAMG;AACH,MAAM,MAAM,KAAK,GACb,cAAc,GACd,WAAW,GACX,YAAY,GACZ,QAAQ,GACR,UAAU,GACV,WAAW,GACX,WAAW,GACX,WAAW,GACX,YAAY,CAAC;AAEjB,2EAA2E;AAC3E,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,aAAa,CAAC;AAExD,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,wEAAwE;IACxE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AASD,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAwFlD,CAAC;AAEF,yEAAyE;AACzE,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAE/B,MAAM,WAAW,MAAM;IACrB,iCAAiC;IACjC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,wCAAwC;IACxC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACxB,8CAA8C;IAC9C,QAAQ,EAAE,OAAO,CAAC;IAClB,wBAAwB;IACxB,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IAC3B,8CAA8C;IAC9C,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,MAAM,GACb,OAAO,CAuBT;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAW5D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/D,MAAM,CAqBR"}
package/dist/access.js ADDED
@@ -0,0 +1,156 @@
1
+ const read = (scope) => ({ scope, kind: "read" });
2
+ const write = (scope) => ({ scope, kind: "write" });
3
+ const destructive = (scope) => ({
4
+ scope,
5
+ kind: "destructive",
6
+ });
7
+ export const TOOL_POLICY = {
8
+ // apps
9
+ list_apps: read("account:read"),
10
+ // audit log: the account's activity
11
+ get_audit_log: read("account:read"),
12
+ get_app: read("account:read"),
13
+ create_app: { ...write("apps:write"), allApps: true },
14
+ update_app_name: write("apps:write"),
15
+ // builds and deployments
16
+ list_builds: read("apps:read"),
17
+ get_build_log: read("apps:read"),
18
+ trigger_build: write("deploy"),
19
+ cancel_build: write("deploy"),
20
+ deploy_app: write("deploy"),
21
+ cancel_deployment: write("deploy"),
22
+ get_deployment_info: read("apps:read"),
23
+ // infrastructure: what runs an app, versions
24
+ get_app_infrastructure: read("account:read"),
25
+ wait_for_deployment: read("apps:read"),
26
+ list_parse_server_versions: read("apps:read"),
27
+ // a new major version can break cloud code and clients
28
+ change_parse_server_version: destructive("apps:write"),
29
+ // engines, scale and health
30
+ list_engines: read("apps:read"),
31
+ set_engine: write("apps:write"),
32
+ scale_app: write("apps:write"),
33
+ set_high_availability: write("apps:write"),
34
+ check_app_health: read("apps:read"),
35
+ // environment variables
36
+ list_env_vars: read("env:read"),
37
+ set_env_var: write("env:write"),
38
+ delete_env_var: destructive("env:write"),
39
+ // classes (schema)
40
+ list_classes: read("data:read"),
41
+ generate_types: read("data:read"),
42
+ // advisor: checks and upgrade plans, read only
43
+ get_app_advice: read("data:read"),
44
+ plan_parse_server_upgrade: read("data:read"),
45
+ get_class: read("data:read"),
46
+ get_class_permissions: read("data:read"),
47
+ create_class: write("data:write"),
48
+ add_field: write("data:write"),
49
+ update_class_permissions: write("data:write"),
50
+ delete_field: destructive("data:write"),
51
+ delete_class: destructive("data:write"),
52
+ // objects
53
+ query_objects: read("data:read"),
54
+ get_object: read("data:read"),
55
+ create_object: write("data:write"),
56
+ update_object: write("data:write"),
57
+ delete_object: destructive("data:write"),
58
+ // files
59
+ upload_file: write("data:write"),
60
+ upload_file_from_url: { ...write("data:write"), localOnly: true },
61
+ upload_file_from_path: { ...write("data:write"), localOnly: true },
62
+ // cloud code
63
+ call_cloud_function: write("data:write"),
64
+ list_cloud_files: read("data:read"),
65
+ read_cloud_code: read("data:read"),
66
+ // push
67
+ send_push: write("data:write"),
68
+ send_push_to_channels: write("data:write"),
69
+ get_push_status: read("data:read"),
70
+ list_audiences: read("data:read"),
71
+ list_available_devices: read("data:read"),
72
+ create_audience: write("data:write"),
73
+ delete_audience: destructive("data:write"),
74
+ // jobs
75
+ list_available_jobs: read("data:read"),
76
+ list_scheduled_jobs: read("data:read"),
77
+ get_job_status: read("data:read"),
78
+ schedule_job: write("data:write"),
79
+ run_job: write("data:write"),
80
+ delete_job: destructive("data:write"),
81
+ // logs
82
+ get_logs: read("logs:read"),
83
+ get_error_logs: read("logs:read"),
84
+ // webhooks
85
+ list_function_webhooks: read("data:read"),
86
+ get_function_webhook: read("data:read"),
87
+ create_function_webhook: write("data:write"),
88
+ update_function_webhook: write("data:write"),
89
+ delete_function_webhook: destructive("data:write"),
90
+ list_trigger_webhooks: read("data:read"),
91
+ get_trigger_webhook: read("data:read"),
92
+ create_trigger_webhook: write("data:write"),
93
+ update_trigger_webhook: write("data:write"),
94
+ delete_trigger_webhook: destructive("data:write"),
95
+ };
96
+ export function isToolAllowed(name, group, access) {
97
+ const policy = TOOL_POLICY[name];
98
+ if (!policy) {
99
+ // every tool must be listed above; failing closed keeps a forgotten
100
+ // tool from being exposed with unknown access
101
+ return false;
102
+ }
103
+ if (access.scopes.indexOf(policy.scope) === -1) {
104
+ return false;
105
+ }
106
+ if (access.readOnly && policy.kind !== "read") {
107
+ return false;
108
+ }
109
+ if (policy.localOnly && !access.local) {
110
+ return false;
111
+ }
112
+ if (policy.allApps && access.appIds) {
113
+ return false;
114
+ }
115
+ if (access.groups && access.groups.indexOf(group) === -1) {
116
+ return false;
117
+ }
118
+ return true;
119
+ }
120
+ /** MCP annotations, so clients can ask before changes and deletions */
121
+ export function annotationsFor(name) {
122
+ const policy = TOOL_POLICY[name];
123
+ if (!policy) {
124
+ throw new Error(`Tool ${name} has no access policy`);
125
+ }
126
+ return {
127
+ readOnlyHint: policy.kind === "read",
128
+ destructiveHint: policy.kind === "destructive",
129
+ // these act on SashiDo, not on an open-ended world
130
+ openWorldHint: false,
131
+ };
132
+ }
133
+ /**
134
+ * Options a client adds to the MCP URL (or env vars locally). They can only
135
+ * narrow what the token allows: ?read_only=true&apps=id1,id2&features=objects,logs
136
+ */
137
+ export function narrowAccess(base, options) {
138
+ const list = (value) => value
139
+ ? value
140
+ .split(",")
141
+ .map((v) => v.trim())
142
+ .filter(Boolean)
143
+ : null;
144
+ const apps = list(options.apps);
145
+ let appIds = base.appIds;
146
+ if (apps) {
147
+ appIds = base.appIds ? apps.filter((id) => base.appIds.indexOf(id) !== -1) : apps;
148
+ }
149
+ return {
150
+ ...base,
151
+ readOnly: base.readOnly || options.readOnly === "true" || options.readOnly === "1",
152
+ appIds,
153
+ groups: list(options.features) || base.groups,
154
+ };
155
+ }
156
+ //# sourceMappingURL=access.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access.js","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { AppCheck } from "../client/parse.js";
2
+ import { UpgradeRule } from "../upgrade/rules.js";
3
+ import { Finding } from "../upgrade/scan.js";
4
+ /** Problems first, each with what to do; the passing ones in one line */
5
+ export declare function formatChecks(checks: Array<AppCheck & {
6
+ group?: string;
7
+ }>): string[];
8
+ export interface UpgradeReport {
9
+ app: string;
10
+ from: string;
11
+ to: string;
12
+ /** the target's imageTag, for create_app */
13
+ toTag: string;
14
+ majors: number[];
15
+ rules: UpgradeRule[];
16
+ findings: Finding[];
17
+ filesScanned: number;
18
+ filesSkipped: string[];
19
+ rollback: string | null;
20
+ }
21
+ export declare function formatUpgrade(r: UpgradeReport): string;
22
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/advice/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAY,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAU,MAAM,oBAAoB,CAAC;AAIrD,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,MAAM,EAAE,CAiBnF;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAUD,wBAAgB,aAAa,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CA6CtD"}
@@ -0,0 +1,70 @@
1
+ import { PLATFORM } from "../upgrade/rules.js";
2
+ import { byRule } from "../upgrade/scan.js";
3
+ const MARK = { fail: "❗", warning: "⚠️", error: "⏱️", ok: "✓" };
4
+ /** Problems first, each with what to do; the passing ones in one line */
5
+ export function formatChecks(checks) {
6
+ const order = ["fail", "warning", "error"];
7
+ const problems = checks
8
+ .filter((c) => c.state !== "ok")
9
+ .sort((a, b) => order.indexOf(a.state) - order.indexOf(b.state));
10
+ const lines = [];
11
+ for (const c of problems) {
12
+ lines.push(`${MARK[c.state] || "•"} ${c.title}${c.group ? ` (${c.group})` : ""}: ${c.message}`);
13
+ if (c.solution) {
14
+ lines.push(` What to do: ${c.solution}`);
15
+ }
16
+ }
17
+ const ok = checks.filter((c) => c.state === "ok");
18
+ if (ok.length) {
19
+ lines.push(`${MARK.ok} Fine: ${ok.map((c) => c.title).join(", ")}.`);
20
+ }
21
+ return lines;
22
+ }
23
+ function placesOf(places) {
24
+ const shown = places.slice(0, 8).map((p) => ` ${p.file}:${p.line} ${p.text}`);
25
+ if (places.length > 8) {
26
+ shown.push(` … and ${places.length - 8} more`);
27
+ }
28
+ return shown;
29
+ }
30
+ export function formatUpgrade(r) {
31
+ const groups = byRule(r.findings);
32
+ const breaks = groups.filter((g) => g.rule.severity === "breaks");
33
+ const checks = groups.filter((g) => g.rule.severity === "check");
34
+ const clean = r.rules.filter((rule) => !groups.some((g) => g.rule.id === rule.id));
35
+ const lines = [
36
+ `Upgrade plan for ${r.app}: Parse Server ${r.from} → ${r.to}`,
37
+ r.majors.length ? `Major versions crossed: ${r.majors.join(", ")}.` : "Same major version: no breaking changes expected.",
38
+ `Cloud code scanned: ${r.filesScanned} file${r.filesScanned === 1 ? "" : "s"}${r.filesSkipped.length ? ` (not read: ${r.filesSkipped.join(", ")})` : ""}.`,
39
+ "",
40
+ ];
41
+ if (breaks.length) {
42
+ lines.push("Fix before upgrading (breaks on the new version):");
43
+ for (const g of breaks) {
44
+ lines.push(`❗ ${g.rule.title} (Parse Server ${g.rule.major})`, ` ${g.rule.fix}`, ...placesOf(g.places), ` Source: ${g.rule.source}`);
45
+ }
46
+ lines.push("");
47
+ }
48
+ if (checks.length) {
49
+ lines.push("Check (may behave differently):");
50
+ for (const g of checks) {
51
+ lines.push(`⚠️ ${g.rule.title} (Parse Server ${g.rule.major})`, ` ${g.rule.fix}`, ...placesOf(g.places), ` Source: ${g.rule.source}`);
52
+ }
53
+ lines.push("");
54
+ }
55
+ if (!breaks.length && !checks.length) {
56
+ lines.push("No breaking changes found in the cloud code.", "");
57
+ }
58
+ if (clean.length) {
59
+ lines.push(`Not used in the cloud code: ${clean.map((rule) => rule.title).join("; ")}.`, "");
60
+ }
61
+ const platform = PLATFORM.filter((p) => r.majors.includes(p.major));
62
+ if (platform.length) {
63
+ lines.push("Handled by SashiDo or in App Settings:", ...platform.map((p) => `• Parse Server ${p.major}: ${p.note}`), "");
64
+ }
65
+ lines.push("Plan:", `1. ${breaks.length ? "Fix the code above in the app's cloud code (its GitHub repository) and deploy it." : "Nothing in the cloud code has to change first."}`, `2. Try it on a test app: create_app with parseServerVersion "${r.toTag}", deploy the same cloud code, test your clients against it.`, `3. change_parse_server_version on ${r.app} (it shows a preview first, and refuses when the app's settings need a change first, e.g. Google login client IDs), then wait_for_deployment, check_app_health and get_error_logs.`, r.rollback
66
+ ? `4. If something breaks: change_parse_server_version back to "${r.rollback}".`
67
+ : "4. The current version isn't offered anymore, so there is no switching back: test well in step 2.");
68
+ return lines.join("\n");
69
+ }
70
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/advice/format.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { ParseSchema } from "../client/parse.js";
2
+ /**
3
+ * The security checks of the app servers' GET /app_checks (Parse Server
4
+ * 6.5.11+ on SashiDo) that
5
+ * need only the schema, for apps on older versions; same wording, so the
6
+ * advice reads the same everywhere.
7
+ */
8
+ export type CheckState = "ok" | "warning" | "fail" | "error";
9
+ export interface CheckResult {
10
+ id: string;
11
+ title: string;
12
+ state: CheckState;
13
+ message: string;
14
+ solution?: string;
15
+ }
16
+ export declare function schemaChecks(classes: ParseSchema[]): CheckResult[];
17
+ //# sourceMappingURL=schemaChecks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemaChecks.d.ts","sourceRoot":"","sources":["../../src/advice/schemaChecks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD;;;;;GAKG;AAEH,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAE7D,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA2ED,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAElE"}
@@ -0,0 +1,64 @@
1
+ const CHECKED_SYSTEM_CLASSES = ["_User"];
2
+ const MAX_CLASSES = 50;
3
+ const PUBLIC_KEYS = "Your client keys are public, anyone can read them from your web app or your mobile app. ";
4
+ // a class-level permission is public when it isn't set or allows everyone
5
+ function isPublic(clp, operation) {
6
+ return !clp || !clp[operation] || clp[operation]["*"] === true;
7
+ }
8
+ function checkedClasses(classes) {
9
+ return classes.filter((c) => !c.className.startsWith("_") || CHECKED_SYSTEM_CLASSES.includes(c.className));
10
+ }
11
+ function classList(names) {
12
+ const shown = names.slice(0, MAX_CLASSES);
13
+ const more = names.length - shown.length;
14
+ return shown.join(", ") + (more > 0 ? ` and ${more} more` : "");
15
+ }
16
+ const clpOf = (c) => c.classLevelPermissions;
17
+ function userListing(classes) {
18
+ const user = classes.find((c) => c.className === "_User");
19
+ const base = { id: "public-user-find", title: "Public listing of users" };
20
+ if (!user || !isPublic(clpOf(user), "find")) {
21
+ return { ...base, state: "ok", message: "The users can't be listed by everyone." };
22
+ }
23
+ return {
24
+ ...base,
25
+ state: "warning",
26
+ message: `${PUBLIC_KEYS}So anyone can list the users of your app and read their public fields (e.g. usernames).`,
27
+ solution: "In the Data Browser open _User > Security and allow Find only for authenticated users, roles or the master key. Sign up, login and fetching the current user keep working.",
28
+ };
29
+ }
30
+ function addField(classes) {
31
+ const open = checkedClasses(classes)
32
+ .filter((c) => isPublic(clpOf(c), "addField"))
33
+ .map((c) => c.className);
34
+ const base = { id: "public-add-field", title: "Adding fields from the clients" };
35
+ if (!open.length) {
36
+ return { ...base, state: "ok", message: "The clients can't add new fields to the classes." };
37
+ }
38
+ return {
39
+ ...base,
40
+ state: "warning",
41
+ message: `${PUBLIC_KEYS}So anyone can add new fields to ${open.length} classes by saving objects with new keys: ${classList(open)}. It is a risk when your cloud code trusts fields like isAdmin.`,
42
+ solution: "In the Data Browser open the class > Security and turn off Add Field for the public. You can still add fields from the dashboard and with the master key.",
43
+ };
44
+ }
45
+ function publicWrite(classes) {
46
+ const open = checkedClasses(classes)
47
+ .filter((c) => c.className !== "_User")
48
+ .filter((c) => ["create", "update", "delete"].every((op) => isPublic(clpOf(c), op)))
49
+ .map((c) => c.className);
50
+ const base = { id: "public-write", title: "Classes writable by everyone" };
51
+ if (!open.length) {
52
+ return { ...base, state: "ok", message: "No class can be changed by everyone." };
53
+ }
54
+ return {
55
+ ...base,
56
+ state: "fail",
57
+ message: `${PUBLIC_KEYS}So anyone can create objects in ${open.length} classes and change or delete the objects saved without an ACL: ${classList(open)}.`,
58
+ solution: "In the Data Browser open the class > Security and allow Create, Update and Delete only for authenticated users, roles or the master key, or make sure every object is saved with an ACL (e.g. set it in a beforeSave trigger).",
59
+ };
60
+ }
61
+ export function schemaChecks(classes) {
62
+ return [userListing(classes), addField(classes), publicWrite(classes)];
63
+ }
64
+ //# sourceMappingURL=schemaChecks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemaChecks.js","sourceRoot":"","sources":["../../src/advice/schemaChecks.ts"],"names":[],"mappings":"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"}