@awesomate/hosting-mcp 0.11.0 → 0.12.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.
package/dist/index.js CHANGED
@@ -40114,9 +40114,9 @@ readTool(
40114
40114
  var PLAN_LADDER_FALLBACK = {
40115
40115
  note: "Live per-account limits come from awesomate_get_limits \u2014 this is the plan ladder reference (static fallback).",
40116
40116
  plans: [
40117
- { name: "Essentials", purchasable: false, monthlyPriceUsd: 75, maxSites: 1, maxCustomDomains: 0, maxApps: 0, cpanelAccess: true, shellAccess: false, appBuilder: false },
40118
- { name: "Support Plus", purchasable: true, monthlyPriceUsd: 175, maxSites: 2, maxCustomDomains: 3, maxApps: 5, cpanelAccess: true, shellAccess: true, appBuilder: true },
40119
- { name: "Pro", purchasable: true, monthlyPriceUsd: 375, maxSites: 10, maxCustomDomains: 10, maxApps: 20, cpanelAccess: true, shellAccess: true, appBuilder: true },
40117
+ { name: "Essentials", purchasable: false, monthlyPriceUsd: 75, maxSites: 1, maxCustomDomains: 1, maxApps: 0, cpanelAccess: true, shellAccess: false, appBuilder: false },
40118
+ { name: "Support Plus", purchasable: true, monthlyPriceUsd: 175, maxSites: 2, maxCustomDomains: 5, maxApps: 5, cpanelAccess: true, shellAccess: true, appBuilder: true },
40119
+ { name: "Pro", purchasable: true, monthlyPriceUsd: 375, maxSites: 10, maxCustomDomains: 20, maxApps: 20, cpanelAccess: true, shellAccess: true, appBuilder: true },
40120
40120
  { name: "Embedded", purchasable: false, monthlyPriceUsd: null, maxSites: 100, maxCustomDomains: 100, maxApps: 100, cpanelAccess: true, shellAccess: true, appBuilder: true, note: "Not generally available yet" }
40121
40121
  ],
40122
40122
  upgradePath: "Essentials \u2192 Support Plus \u2192 Pro \u2192 Embedded; upgrades at hub.awesomate.ai/billing (only Support Plus and Pro are purchasable)"
@@ -40147,7 +40147,7 @@ server.registerTool(
40147
40147
  {
40148
40148
  description: "Snapshot a WordPress site (files + database) BEFORE any risky change \u2014 an AI edit, a deploy, a plugin/theme/core update. Returns a snapshotId you can roll back to. ALWAYS snapshot before mutating a live site. Requires shell access (Support Plus+).",
40149
40149
  inputSchema: {
40150
- domain: external_exports.string().describe("The site domain (as shown by awesomate_list_sites), e.g. mysite.awesomate.io"),
40150
+ domain: external_exports.string().describe("The site domain (as shown by awesomate_list_sites), e.g. mysite.awesomate.site"),
40151
40151
  reason: external_exports.string().optional().describe('Why (stored in the snapshot list), e.g. "before deploy"')
40152
40152
  }
40153
40153
  },
@@ -40194,6 +40194,60 @@ server.registerTool(
40194
40194
  }
40195
40195
  }
40196
40196
  );
40197
+ server.registerTool(
40198
+ "awesomate_site_staging_create",
40199
+ {
40200
+ description: "Create a staging copy of a WordPress site on the client's private awesomate.dev address (files + database cloned, URLs rewritten). Use this BEFORE making user-facing changes so the user can review at the staging URL first \u2014 post to dev, review, then awesomate_site_staging_promote. The staging site is hidden from search engines and AI crawlers by policy; anyone with the link can view it. One staging copy per site \u2014 a 409 with code 'staging_exists' means promote or discard the existing one first. Cloning can take a few minutes on large sites. Requires shell access (Support Plus+).",
40201
+ inputSchema: {
40202
+ domain: external_exports.string().describe("The LIVE site domain (as shown by awesomate_list_sites), e.g. mysite.awesomate.site")
40203
+ }
40204
+ },
40205
+ async ({ domain }) => {
40206
+ try {
40207
+ return textResult(
40208
+ await hubPost(requireConfig(), `/api/hosting-access/sites/${encodeURIComponent(domain)}/staging`, {})
40209
+ );
40210
+ } catch (err) {
40211
+ return errorResult(err);
40212
+ }
40213
+ }
40214
+ );
40215
+ server.registerTool(
40216
+ "awesomate_site_staging_promote",
40217
+ {
40218
+ description: "Publish the staging copy to the LIVE site (overwrites live files + database with staging). The live site is auto-snapshotted first \u2014 the result includes preSnapshotId, which awesomate_rollback_site can restore if anything looks wrong. **Confirm with the user before promoting \u2014 it replaces the live site.** Requires shell access (Support Plus+).",
40219
+ inputSchema: {
40220
+ domain: external_exports.string().describe("The LIVE site domain whose staging copy should go live")
40221
+ }
40222
+ },
40223
+ async ({ domain }) => {
40224
+ try {
40225
+ return textResult(
40226
+ await hubPost(requireConfig(), `/api/hosting-access/sites/${encodeURIComponent(domain)}/staging/promote`, {})
40227
+ );
40228
+ } catch (err) {
40229
+ return errorResult(err);
40230
+ }
40231
+ }
40232
+ );
40233
+ server.registerTool(
40234
+ "awesomate_site_staging_discard",
40235
+ {
40236
+ description: "Delete the staging copy of a site (staging WP install + its awesomate.dev address; the live site is untouched). **Confirm with the user before discarding \u2014 unpromoted staging changes are lost.** Requires shell access (Support Plus+).",
40237
+ inputSchema: {
40238
+ domain: external_exports.string().describe("The LIVE site domain whose staging copy should be discarded")
40239
+ }
40240
+ },
40241
+ async ({ domain }) => {
40242
+ try {
40243
+ return textResult(
40244
+ await hubDelete(requireConfig(), `/api/hosting-access/sites/${encodeURIComponent(domain)}/staging`)
40245
+ );
40246
+ } catch (err) {
40247
+ return errorResult(err);
40248
+ }
40249
+ }
40250
+ );
40197
40251
  readTool(
40198
40252
  "awesomate_app_context",
40199
40253
  "Call FIRST before any app-builder work. Returns the connected account's plan, whether the app builder is unlocked (capability.appBuilder \u2014 Support Plus+), whether they have a cPanel hosting account yet, their existing apps, firstRun, and capability-aware next-step suggestions to offer the user. If appBuilder is false, relay the upgrade suggestion rather than attempting to build.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awesomate/hosting-mcp",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "Awesomate MCP server — lets Claude manage your Awesomate WordPress hosting, plan, limits, n8n automations, and build Node/static apps + databases",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -87,12 +87,22 @@ target; never touch prod without an explicit ask.**
87
87
  [references/node-recipes.md](references/node-recipes.md) has the recipes
88
88
  (add a table/endpoint, env vars, connection strings, promote/rollback).
89
89
  Handle **secrets** only via the **awesomate-credentials** skill
90
- (encrypted + injected, never committed/echoed). Wire **version control**
91
- from the first commit via **awesomate-github**. If it needs email/lookup/AI,
92
- attach n8n (§"when to use n8n").
93
- 6. **Testing** deploy to **dev** (`git push` to `dev`), then
94
- `awesomate_app_health` to confirm it came up (Node: `/api/ready`). Show the
95
- user the live dev URL and let them try it. Iterate on dev until they're happy.
90
+ (encrypted + injected, never committed/echoed). **Version control is not
91
+ optional**: as soon as the scaffold exists, run the awesomate-github setup
92
+ (repo + first commit + push-to-deploy) ask only the project name and
93
+ private-y/n, in plain words ("I'll back your work up as we go"). From then
94
+ on, **auto-checkpoint**: commit after every working change with a message
95
+ you write, and run the github skill's `vc-healthcheck.mjs` at session start
96
+ and before every deploy. If it needs email/lookup/AI, attach n8n (§"when to
97
+ use n8n").
98
+ 6. **Testing — always ask "live or dev?" before posting anything.** When the
99
+ user asks to publish/change something user-facing, ask: *"Want this on your
100
+ live site, or on your dev site (awesomate.dev) to review and test first?"*
101
+ Recommend dev. Deploy to **dev** (`git push` to `dev` — the dev site lives
102
+ at `{app}.{slug}.awesomate.dev`, hidden from search engines and AI crawlers
103
+ by policy), then `awesomate_app_health` to confirm it came up (Node:
104
+ `/api/ready`). Show the user the live dev URL and let them try it. Iterate
105
+ on dev until they're happy.
96
106
  7. **Wrap-up / promote** — only when the user approves, promote **dev → staging
97
107
  → main** (merge + push per branch). `awesomate_app_deploy` reports the
98
108
  branch→env map + last-deploy state. Prod is `main`. **Before merging to
@@ -113,7 +123,11 @@ files directly (no build step).
113
123
  - Non-technical user: explain in plain words, one or two short questions at a
114
124
  time, no unexplained jargon. Recommend; don't quiz them on architecture.
115
125
  - Confirm the stack + name before `awesomate_app_create`. Default the target
116
- to **dev**; never push to prod without an explicit ask.
126
+ to **dev**; never push to prod without an explicit ask — and when they ask
127
+ to "post"/"publish" something, ask **"live or dev?"** first.
128
+ - You are the user's version-control expert (they don't know what a repo is):
129
+ set up GitHub right after the first build, auto-commit as you go, and run
130
+ the github skill's vc-healthcheck at session start + before deploys.
117
131
  - A `403` on a write = plan gate (Support Plus+). Offer an upgrade, don't retry.
118
132
  - A `409` with code `app_limit` = the plan's app allowance is full (Support
119
133
  Plus 5, Pro 20 — `awesomate_app_context.limits` has the live numbers).
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: awesomate-github
3
- description: Set up GitHub version control for the user's app with zero Git knowledge required — connect their GitHub once, then create the repo, .gitignore, first commit and push-to-deploy for them. Use when the user says "connect GitHub", "back up my app", "save my work", "set up version control", "put this on GitHub", or after building an app when it should be version-controlled. Companion to awesomate-app-builder.
3
+ description: Set up GitHub version control for the user's app with zero Git knowledge required — connect their GitHub once, then create the repo, .gitignore, first commit and push-to-deploy for them. Use when the user says "connect GitHub", "back up my app", "save my work", "set up version control", "put this on GitHub", or after building an app when it should be version-controlled — and proactively: run its vc-healthcheck at session start and before deploys to keep commits/pushes/deploys healthy for users who do not know Git. Companion to awesomate-app-builder.
4
4
  ---
5
5
 
6
6
  # Awesomate GitHub — version control for people who don't know Git
@@ -62,6 +62,32 @@ Explain it as *"save your work = it goes live"* — don't lecture on branches.
62
62
  - "Put it live" → push to the right branch (dev/staging/main) and tell them the URL.
63
63
  - Always write clear commit messages **for** them; don't ask them to.
64
64
 
65
+ ## 4. Be the version-control guardian (proactive, not reactive)
66
+
67
+ The user is ~99% likely to have no idea what a repo is. YOU own their version
68
+ control — they should never have to think about it:
69
+
70
+ - **Health check at session start.** Before touching an app folder, run:
71
+ ```
72
+ node ~/.claude/skills/awesomate-github/scripts/vc-healthcheck.mjs <project-dir>
73
+ ```
74
+ It prints JSON (`problems[]`: `no_repo`, `no_remote`, `uncommitted_changes`,
75
+ `unpushed_commits`, `env_file_tracked`, `last_deploy_failed`, …). Fix what
76
+ you can silently (commit + push loose work with a message you write); for
77
+ `no_repo`/`no_remote`, offer setup (§1) in plain words: *"your last changes
78
+ aren't backed up anywhere — want me to set that up? Takes a minute."*
79
+ `env_file_tracked` is urgent: untrack it, .gitignore it, and route the
80
+ secrets through awesomate-credentials.
81
+ - **Auto-checkpoint.** After every meaningful working change (feature works,
82
+ bug fixed, content updated), commit with a plain-English message — don't
83
+ wait for "save my work", and never ask them to write a message. Push at
84
+ natural pauses so their backup is never behind.
85
+ - **Health check before every deploy/publish.** Re-run the script; never push
86
+ to a deploy branch with a failing prior run you haven't explained.
87
+ - **Report in their language.** "Your work is backed up" / "your last 3
88
+ changes were never saved — I've saved them now" — never "you're 2 commits
89
+ ahead of origin/main".
90
+
65
91
  ## Hard rules
66
92
  - Never commit a secret or a `.env`. The script's secret guard is a backstop,
67
93
  not a licence to skip checking. If you spot a secret, route it through the
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Version-control health check for an Awesomate app folder.
4
+ *
5
+ * Run from (or pass) the project directory. Prints a JSON report Claude uses
6
+ * to keep the user's version control healthy WITHOUT them knowing Git:
7
+ * - repo/remote wiring, uncommitted + unpushed work
8
+ * - branch → deploy-env mapping (dev → awesomate.dev, main → live)
9
+ * - last GitHub Actions deploy run (when `gh` is authed)
10
+ *
11
+ * Exit code 0 always (reporting tool, not a gate) — Claude decides what to
12
+ * fix based on the JSON.
13
+ */
14
+ import { execSync } from 'node:child_process';
15
+ import { existsSync } from 'node:fs';
16
+ import { resolve } from 'node:path';
17
+
18
+ const dir = resolve(process.argv[2] ?? '.');
19
+ const report = {
20
+ dir,
21
+ gitInstalled: false,
22
+ repoInitialized: false,
23
+ remote: null,
24
+ branch: null,
25
+ uncommittedFiles: 0,
26
+ unpushedCommits: null, // null = unknown (no upstream)
27
+ hasUpstream: false,
28
+ envTracked: false,
29
+ ghAuthed: false,
30
+ lastDeployRun: null,
31
+ problems: [],
32
+ };
33
+
34
+ function sh(cmd) {
35
+ return execSync(cmd, { cwd: dir, stdio: ['ignore', 'pipe', 'pipe'], timeout: 15_000 })
36
+ .toString()
37
+ .trim();
38
+ }
39
+ function trySh(cmd) {
40
+ try {
41
+ return sh(cmd);
42
+ } catch {
43
+ return null;
44
+ }
45
+ }
46
+
47
+ if (trySh('git --version') === null) {
48
+ report.problems.push('git_not_installed');
49
+ } else {
50
+ report.gitInstalled = true;
51
+ report.repoInitialized = trySh('git rev-parse --is-inside-work-tree') === 'true';
52
+ if (!report.repoInitialized) {
53
+ report.problems.push('no_repo');
54
+ } else {
55
+ report.branch = trySh('git branch --show-current') || '(detached)';
56
+ report.remote = trySh('git remote get-url origin');
57
+ if (!report.remote) report.problems.push('no_remote');
58
+
59
+ const status = trySh('git status --porcelain') ?? '';
60
+ report.uncommittedFiles = status ? status.split('\n').length : 0;
61
+ if (report.uncommittedFiles > 0) report.problems.push('uncommitted_changes');
62
+
63
+ // .env must never be tracked — the single most dangerous drift.
64
+ const tracked = trySh('git ls-files .env */.env') ?? '';
65
+ report.envTracked = tracked.length > 0;
66
+ if (report.envTracked) report.problems.push('env_file_tracked');
67
+
68
+ const upstream = trySh('git rev-parse --abbrev-ref @{upstream}');
69
+ report.hasUpstream = !!upstream;
70
+ if (upstream) {
71
+ const ahead = trySh('git rev-list --count @{upstream}..HEAD');
72
+ report.unpushedCommits = ahead === null ? null : Number(ahead);
73
+ if (report.unpushedCommits > 0) report.problems.push('unpushed_commits');
74
+ } else if (report.remote) {
75
+ report.problems.push('branch_not_pushed');
76
+ }
77
+
78
+ if (report.remote && trySh('gh auth status') !== null) {
79
+ report.ghAuthed = true;
80
+ const runs = trySh(
81
+ 'gh run list --limit 1 --json displayTitle,conclusion,headBranch,updatedAt',
82
+ );
83
+ try {
84
+ const parsed = JSON.parse(runs ?? '[]');
85
+ if (parsed[0]) {
86
+ report.lastDeployRun = parsed[0];
87
+ if (parsed[0].conclusion && parsed[0].conclusion !== 'success') {
88
+ report.problems.push('last_deploy_failed');
89
+ }
90
+ }
91
+ } catch {
92
+ /* no runs / not parseable — fine */
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ if (!existsSync(resolve(dir, '.gitignore')) && report.repoInitialized) {
99
+ report.problems.push('no_gitignore');
100
+ }
101
+
102
+ console.log(JSON.stringify(report, null, 2));
@@ -3,7 +3,7 @@ name: awesomate-hosting
3
3
  description: >
4
4
  Manage, build, and grow your Awesomate WordPress hosting from Claude. Use when
5
5
  the user mentions their Awesomate site or hosting, cPanel, a site on
6
- *.awesomate.io or a custom domain hosted with Awesomate, WordPress admin,
6
+ *.awesomate.site (or legacy *.site.awesomate.io) or a custom domain hosted with Awesomate, WordPress admin,
7
7
  installing a plugin/theme, running WP-CLI, "my site is slow / out of space /
8
8
  broke", snapshotting or rolling back a site, deploying from a local WordPress
9
9
  Studio site to live, checking hosting stats or how close they are to a plan
@@ -11,7 +11,9 @@ description: >
11
11
  plan. Trigger phrases: "my awesomate site", "connect my hosting", "spin up a
12
12
  wordpress site", "install wordpress", "add my domain", "why is my site slow",
13
13
  "am I near my limit", "snapshot my site", "roll back my site", "deploy to
14
- live", "run wp-cli", "upgrade my plan", "how many sites can I have".
14
+ live", "run wp-cli", "upgrade my plan", "how many sites can I have",
15
+ "create a staging site", "test changes before going live", "publish staging",
16
+ "post this to my dev site".
15
17
  ---
16
18
 
17
19
  # Awesomate Hosting
@@ -170,25 +172,34 @@ Allowed: `awesomate_get_context`, `_get_hosting_status`, `_get_hosting_account`,
170
172
  `_get_hosting_stats`, `_get_grafana_url`, `_get_credit_balance`, the billing
171
173
  preview/deep-link tools, and 1-click WP-admin / cPanel SSO links.
172
174
 
173
- Never attempt SSH, WP-CLI, snapshot/rollback, deploy, multi-site, or custom
174
- domains — those tools will 403. Instead, surface the specific limit and what
175
+ Never attempt SSH, WP-CLI, snapshot/rollback, staging, deploy, multi-site
176
+ those tools will 403. Instead, surface the specific limit and what
175
177
  Support Plus/Pro unlocks (see §4), and offer to preview the upgrade.
176
178
 
177
179
  ### Support Plus and above (shell) — full workflow
178
180
 
179
181
  You additionally have: jailed SSH to the client's own cPanel account, WP-CLI
180
182
  (`awesomate_run_wp_cli` or raw SSH via `scripts/ssh-connect.sh`),
181
- snapshot/rollback, and the local-first deploy flow 5). Golden rule, always:
183
+ snapshot/rollback, **WP staging on awesomate.dev**5a), and the local-first
184
+ deploy flow (§5). Golden rule, always:
182
185
 
183
186
  > **Local first, live never — and snapshot before you touch live.**
184
187
 
188
+ **Ask "live or dev?" before posting anything user-facing.** When the user asks
189
+ you to change/publish something on their WordPress site, ask: *"Want me to put
190
+ this on your live site, or on your private dev copy (awesomate.dev) so you can
191
+ review it first?"* Default suggestion: dev first, publish after they've looked
192
+ at it. Their dev copy is invisible to Google and AI crawlers by policy.
193
+
185
194
  ## 3. Safety rules (non-negotiable)
186
195
 
187
196
  - **Before ANY change to a live site** — plugin/theme/core update, `search-replace`,
188
197
  bulk edit, deploy — call **`awesomate_snapshot_site`** first and tell the user the
189
198
  `snapshotId`. If it goes wrong, `awesomate_rollback_site` restores files + DB.
190
199
  - **Confirm before destructive actions**: `awesomate_uninstall_site`,
191
- `awesomate_rollback_site`, dropping tables, `wp db reset`, deleting content.
200
+ `awesomate_rollback_site`, `awesomate_site_staging_promote` (replaces live),
201
+ `awesomate_site_staging_discard` (loses unpublished staging work), dropping
202
+ tables, `wp db reset`, deleting content.
192
203
  State exactly what will be lost and wait for an explicit "yes".
193
204
  - Prefer building/testing in **WordPress Studio locally**, then deploy (§5).
194
205
  - Dry-run risky WP-CLI where the command supports it before the real run.
@@ -214,8 +225,8 @@ one command — `scripts/deploy.sh`, which **snapshots live first** (so a bad
214
225
  deploy is one rollback away), pushes files, then optionally the DB:
215
226
 
216
227
  ```
217
- scripts/deploy.sh --from ~/Studio/mysite --domain mysite.awesomate.io # files only
218
- scripts/deploy.sh --from ~/Studio/mysite --domain mysite.awesomate.io --with-db # + database
228
+ scripts/deploy.sh --from ~/Studio/mysite --domain mysite.awesomate.site # files only
229
+ scripts/deploy.sh --from ~/Studio/mysite --domain mysite.awesomate.site --with-db # + database
219
230
  ```
220
231
 
221
232
  - **Always files-first is the safe default.** `--with-db` also exports the
@@ -230,6 +241,26 @@ scripts/deploy.sh --from ~/Studio/mysite --domain mysite.awesomate.io --with-db
230
241
  Never deploy without confirming with the user first, and always report the
231
242
  snapshot id and the verified live status.
232
243
 
244
+ ## 5a. Staging on awesomate.dev (Support Plus+)
245
+
246
+ The middle tier between "edit live" and "local Studio": a private full copy of
247
+ the site at `{name}.{slug}.awesomate.dev`, noindexed at the edge (no Google, no
248
+ AI crawlers — anyone with the link can view, perfect for client review).
249
+
250
+ - `awesomate_site_staging_create <live domain>` — clones files + DB, rewrites
251
+ URLs. One staging copy per site (409 `staging_exists` → promote or discard
252
+ first). Takes a few minutes on large sites.
253
+ - Make the changes ON the staging domain (WP-CLI / wp-admin / deploy.sh with
254
+ `--domain <staging domain>`), send the user the staging URL to review.
255
+ - `awesomate_site_staging_promote <live domain>` — publishes staging over live.
256
+ Live is auto-snapshotted first; report the returned `preSnapshotId` as the
257
+ undo handle (`awesomate_rollback_site`). **Confirm before promoting.**
258
+ - `awesomate_site_staging_discard <live domain>` — deletes the staging copy;
259
+ live untouched. **Confirm — unpublished staging changes are lost.**
260
+
261
+ Prefer this flow over editing live whenever the change is user-visible
262
+ (theme/layout/content restructures, plugin experiments, redesigns).
263
+
233
264
  ## 6. Version control with GitHub (optional)
234
265
 
235
266
  Awesomate's snapshots are your safety net, so Git isn't required — but for real
@@ -4,7 +4,7 @@
4
4
  # Awesomate site. SNAPSHOTS LIVE FIRST via the hub (so a bad deploy is one
5
5
  # rollback away), then pushes files, then optionally the database.
6
6
  #
7
- # deploy.sh --from ~/Studio/mysite --domain mysite.awesomate.io [options]
7
+ # deploy.sh --from ~/Studio/mysite --domain mysite.awesomate.site [options]
8
8
  #
9
9
  # Options:
10
10
  # --docroot PATH Live docroot (default: /home/<cpanel-user>/public_html)
@@ -4,7 +4,7 @@
4
4
  # from an existing live site). Read-only against live — it never changes the
5
5
  # live site. Copies files + exports the live DB.
6
6
  #
7
- # pull-live.sh --domain mysite.awesomate.io --to ~/Studio/mysite [--docroot PATH]
7
+ # pull-live.sh --domain mysite.awesomate.site --to ~/Studio/mysite [--docroot PATH]
8
8
  #
9
9
  # Then import the files into a new WordPress Studio site and load db.sql.
10
10
  # Client-side deps: bash, ssh, rsync, scp.