naro-mcp 0.18.0 → 0.18.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 (3) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +139 -74
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -60,10 +60,10 @@ Headless/CI agents can skip `naro login` and set a token instead:
60
60
  | `whoami`, `list_teams` | Account / team context |
61
61
  | `list_projects`, `get_project`, `link_project` | Project discovery + `.naro/project.json` linking |
62
62
  | `get_team_usage` | Team usage for a UTC month (`month`, the current one by default): requests, builds and custom domains per day and per project, the plan's `limits`, the enforcement mode, and `summary` (each metric against its limit, and `status` paused/over/near). `team` defaults to the team linked in `directory`, then your only team; otherwise `needsTeam` with the list. Owner, billing and enterprise_viewer only (403 `FORBIDDEN` otherwise). Read-only |
63
- | `deploy` | Local Next.js build → upload → live URL (preview, `production: true`, or a named environment with `environment: "qa"` — not together with `production`; `production` and `preview` are not environment names). The result's `target` is `production`, `preview` or the environment's slug, and `environment` is the slug or null. Local .env files are never uploaded; unset keys come back as `missingEnvKeys` |
63
+ | `deploy` | Local Next.js build → upload → live URL (preview, `production: true`, or a named environment with `environment: "qa"` — not together with `production`: both are refused with `VALIDATION_FAILED` `environment and production cannot be used together.` before any value is read, asked for or sent and before the deployment is created; `production` and `preview` are not environment names). The result's `target` is `production`, `preview` or the environment's slug, and `environment` is the slug or null. Local .env files are never uploaded; unset keys come back as `missingEnvKeys` |
64
64
  | `list_deployments`, `get_deployment` | Deployment status / history |
65
65
  | `get_build_logs`, `get_runtime_logs` | Debugging failed builds and live workers |
66
- | `list_environment_variables`, `set_environment_variable`, `remove_environment_variable` | Env management (secret values never returned). `target` is `production`, `preview`, `all` or an environment's slug (`all` by default when setting); without `target`, `remove_environment_variable` removes the `production`, `preview` and `all` rows and leaves an environment's value alone. A name that cannot be a slug is refused as `Invalid target "<value>" — use production, preview, all or an environment name.`; a slug that is not an environment answers `404 ENVIRONMENT_NOT_FOUND` |
66
+ | `list_environment_variables`, `set_environment_variable`, `remove_environment_variable` | Env management (secret values never returned). `target` is `production`, `preview`, `all` or an environment's slug (`all` by default when setting); without `target`, `remove_environment_variable` removes the `production`, `preview` and `all` rows and leaves an environment's value alone. A name that cannot be a slug is refused as `Invalid target "<value>" — use production, preview, all or an environment name.`. For `set_environment_variable`, a slug that is not an environment answers `404 ENVIRONMENT_NOT_FOUND`; `remove_environment_variable` reads the list and answers `ENV_NOT_FOUND` (`No KEY variable found for the <slug> environment.`). Without `target`, a key only environments hold answers `ENV_NOT_FOUND` `No KEY variable found outside named environments. Pass the environment to remove it: qa.` with `key` and `environments`, and nothing is removed |
67
67
  | `list_domains`, `add_domain`, `verify_domain`, `remove_domain` | Custom domains: the one CNAME to create (naro connects it within ~5 minutes), plus a re-check that says which of ownership / certificate / routing is still missing. An apex is attached as an apex+www pair by default (`withWww: false` to opt out) |
68
68
  | `promote_deployment`, `rollback_deployment` | Production traffic control |
69
69
  | `cancel_deployment` | Stops a build that is still running (QUEUED–BUILDING). Does not undo a deployment that already went live — use `rollback_deployment` for that |
package/dist/index.js CHANGED
@@ -15595,6 +15595,8 @@ var DeploymentsApi = class {
15595
15595
  }
15596
15596
  }
15597
15597
  };
15598
+ var STATIC_ASSETS_BINDING = "ASSETS";
15599
+ var MANAGED_DATABASE_BINDING = "DB";
15598
15600
  var EnvApi = class {
15599
15601
  constructor(client) {
15600
15602
  this.client = client;
@@ -15624,6 +15626,37 @@ var EnvApi = class {
15624
15626
  }
15625
15627
  );
15626
15628
  }
15629
+ /**
15630
+ * The names the env route refuses as a key — `409 VALIDATION_FAILED`
15631
+ * `<KEY> is already used by a storage binding.` — read the way it reads
15632
+ * them: `ASSETS` (the static files), every binding of the project's storage
15633
+ * and, on a project using the managed database (`database_provider` is
15634
+ * `d1`), `DB`, which may not be a storage row yet. For a caller that sends
15635
+ * many entries in several requests (`naro env import`) and has to refuse
15636
+ * before the first one is saved. A control plane whose project row has no
15637
+ * `database_provider` reads as having no managed database, so `DB` is only
15638
+ * left out there.
15639
+ */
15640
+ async takenBindingNames(projectId) {
15641
+ const base = `/api/projects/${encodeURIComponent(projectId)}`;
15642
+ const [storage, project] = await Promise.all([
15643
+ this.client.request(
15644
+ `${base}/storage`,
15645
+ { context: "List storage" }
15646
+ ),
15647
+ this.client.request(
15648
+ base,
15649
+ { context: "Read project" }
15650
+ )
15651
+ ]);
15652
+ const names = /* @__PURE__ */ new Set([
15653
+ STATIC_ASSETS_BINDING,
15654
+ ...storage.storage.map((row) => row.bindingName)
15655
+ ]);
15656
+ if (project.project.database_provider === "d1")
15657
+ names.add(MANAGED_DATABASE_BINDING);
15658
+ return [...names];
15659
+ }
15627
15660
  async removeById(projectId, envVarId) {
15628
15661
  await this.client.request(
15629
15662
  `/api/projects/${encodeURIComponent(projectId)}/env/${encodeURIComponent(envVarId)}`,
@@ -15634,13 +15667,28 @@ var EnvApi = class {
15634
15667
  * Removes a key's rows: the one `target` names, or, with no target, the
15635
15668
  * `production`, `preview` and `all` rows. An environment's value goes only
15636
15669
  * when its slug is the target, so removing a key everywhere else does not
15637
- * take a QA or staging value with it.
15670
+ * take a QA or staging value with it. A key only environments hold fails
15671
+ * ENV_NOT_FOUND naming them (`details.environments`), not as if there were
15672
+ * none.
15638
15673
  */
15639
15674
  async remove(projectId, key, target) {
15640
15675
  const rows = await this.list(projectId);
15641
15676
  const matches = rows.filter(
15642
15677
  (row) => row.key === key && (target === void 0 ? isReservedEnvTarget(row.target) : row.target === target)
15643
15678
  );
15679
+ if (matches.length === 0 && target === void 0) {
15680
+ const environments = [
15681
+ ...new Set(
15682
+ rows.filter((row) => row.key === key).map((row) => row.target).filter((slug) => !isReservedEnvTarget(slug))
15683
+ )
15684
+ ].toSorted();
15685
+ if (environments.length > 0)
15686
+ throw new NaroError(
15687
+ "ENV_NOT_FOUND",
15688
+ `No ${key} variable found outside named environments. Pass the environment to remove it: ${environments.join(", ")}.`,
15689
+ { details: { key, environments } }
15690
+ );
15691
+ }
15644
15692
  if (matches.length === 0) {
15645
15693
  throw new NaroError(
15646
15694
  "ENV_NOT_FOUND",
@@ -16912,6 +16960,7 @@ var MEDIA_TYPE = new RegExp(`^${TOKEN_CHARACTERS}+/${TOKEN_CHARACTERS}+$`);
16912
16960
  // ../../packages/data-core/src/storage-table.ts
16913
16961
  var STORAGE_OBJECTS = "storage.objects";
16914
16962
  var STORAGE_OBJECTS_TABLE = "_naro_storage_objects";
16963
+ var STORAGE_UNAVAILABLE_REASON = `${STORAGE_OBJECTS} is not available on this backend yet`;
16915
16964
  var STORAGE_BUCKETS_TABLE = "_naro_storage_buckets";
16916
16965
  var STORAGE_POLICY_COLUMNS = [
16917
16966
  "id",
@@ -17477,7 +17526,6 @@ var SPECIAL_WORDS = /* @__PURE__ */ new Set([
17477
17526
  "auth"
17478
17527
  ]);
17479
17528
  var TEXT_TYPES = /* @__PURE__ */ new Set(["text", "varchar", "uuid"]);
17480
- var STORAGE_SUBQUERY_PROBLEM = "A policy cannot read storage.objects in a subquery yet";
17481
17529
  var FOLDERNAME_INDEX_PROBLEM = `storage.foldername(name) is available only as (storage.foldername(name))[n] with n from 1 to ${String(MAX_FOLDERNAME_INDEX)}`;
17482
17530
  function storageHelperColumnProblem(fn) {
17483
17531
  return `storage.${fn}() takes the name column of storage.objects`;
@@ -17928,8 +17976,12 @@ function atRealtimeMessages(stream) {
17928
17976
  }
17929
17977
  function readTableName(stream) {
17930
17978
  const first = stream.peek();
17931
- if (atStorageObjects(stream))
17932
- throw new PolicySyntaxError(STORAGE_SUBQUERY_PROBLEM, first.position);
17979
+ if (atStorageObjects(stream)) {
17980
+ stream.next();
17981
+ stream.next();
17982
+ stream.next();
17983
+ return STORAGE_OBJECTS;
17984
+ }
17933
17985
  if (atRealtimeMessages(stream))
17934
17986
  throw new PolicySyntaxError(REALTIME_SUBQUERY_PROBLEM, first.position);
17935
17987
  let token = first;
@@ -18366,9 +18418,10 @@ function printValue(expr) {
18366
18418
  }
18367
18419
  function printSubquery(subquery) {
18368
18420
  const select = subquery.select === "*" ? "*" : printValue(subquery.select);
18421
+ const table = asciiLower(subquery.table) === STORAGE_OBJECTS ? STORAGE_OBJECTS : quoteIdent(subquery.table);
18369
18422
  const alias = subquery.alias === null ? "" : ` ${quoteIdent(subquery.alias)}`;
18370
18423
  const where = subquery.where === null ? "" : ` WHERE ${printExpr(subquery.where)}`;
18371
- return `SELECT ${select} FROM ${quoteIdent(subquery.table)}${alias}${where}`;
18424
+ return `SELECT ${select} FROM ${table}${alias}${where}`;
18372
18425
  }
18373
18426
  function printElement(element) {
18374
18427
  if (typeof element === "object") return printValue(element);
@@ -19326,70 +19379,9 @@ var PROJECT_BYTES = `(SELECT coalesce(sum(total_bytes), 0) FROM ${STORAGE_BUCKET
19326
19379
  var LIVE_BUCKET = `${STORAGE_BUCKET_DELETING_COLUMN.column} IS NULL`;
19327
19380
 
19328
19381
  // ../../packages/data-core/src/policy-check.ts
19329
- var CONTEXTS = [
19330
- { role: "anon" },
19331
- {
19332
- role: "authenticated",
19333
- userId: "naro-policy-check",
19334
- email: "check@naro.invalid",
19335
- emailVerified: true
19336
- }
19337
- ];
19338
19382
  var STORAGE_PROBE_NONCE = "f".repeat(STORAGE_NONCE_MAX_DIGITS);
19339
- var STORAGE_CONTEXTS = [
19340
- ...CONTEXTS,
19341
- { role: "authenticated", userId: "naro-policy-check" }
19342
- ];
19343
19383
  var REALTIME_PROBE_NONCE = "f".repeat(64);
19344
19384
 
19345
- // ../../packages/data-core/src/policy-migration.ts
19346
- var STORED_POLICIES_SQL = `SELECT table_name, name, kind, command, roles, using_expr, check_expr, owner_column, updated_at FROM ${NAMED_POLICY_TABLE} ORDER BY table_name, name`;
19347
- var SYSTEM_PREDICATE = systemTablePredicate("name");
19348
- var STORAGE_EXISTS = `EXISTS (SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${sqlStringLiteral(STORAGE_OBJECTS_TABLE)})`;
19349
-
19350
- // ../../packages/data-core/src/realtime-limits.ts
19351
- var HOBBY_REALTIME_LIMITS = Object.freeze({
19352
- connections: 200,
19353
- newConnectionsPerSecond: 10,
19354
- messagesPerSecond: 100,
19355
- joinsPerSecond: 100,
19356
- presencePerSecond: 20,
19357
- broadcastPayloadBytes: 262144,
19358
- policyChecksPerSecond: 100,
19359
- restRequestsPerSecond: 10
19360
- });
19361
- var PRO_REALTIME_LIMITS = Object.freeze({
19362
- connections: 500,
19363
- newConnectionsPerSecond: 25,
19364
- messagesPerSecond: 500,
19365
- joinsPerSecond: 500,
19366
- presencePerSecond: 50,
19367
- broadcastPayloadBytes: 3072e3,
19368
- policyChecksPerSecond: 250,
19369
- restRequestsPerSecond: 25
19370
- });
19371
- var ENTERPRISE_REALTIME_LIMITS = Object.freeze({
19372
- connections: 2e3,
19373
- newConnectionsPerSecond: 100,
19374
- messagesPerSecond: 1e3,
19375
- joinsPerSecond: 1e3,
19376
- presencePerSecond: 1e3,
19377
- broadcastPayloadBytes: 3072e3,
19378
- policyChecksPerSecond: 500,
19379
- restRequestsPerSecond: 100
19380
- });
19381
-
19382
- // ../../packages/data-core/src/storage-multipart.ts
19383
- var MiB = 1024 * 1024;
19384
- var MULTIPART_PART_SIZES = [16 * MiB, 32 * MiB, 64 * MiB];
19385
- var MAX_MULTIPART_PARTS = 1e4;
19386
- var MAX_MULTIPART_BYTES = 64 * MiB * MAX_MULTIPART_PARTS;
19387
- var MULTIPART_LOCK_MS = 60 * 60 * 1e3;
19388
- var MULTIPART_PART_EXTEND_BELOW_MS = 30 * 60 * 1e3;
19389
- var MULTIPART_COMPLETE_LOCK_MS = 15 * 60 * 1e3;
19390
- var UUID = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
19391
- var UPLOAD_ID = new RegExp(`^(${UUID})\\.(${UUID})$`);
19392
-
19393
19385
  // ../../packages/data-core/src/system-sql.ts
19394
19386
  async function convertLegacyPolicies(db, found) {
19395
19387
  const rows = rowsOf(await db.prepare(POLICY_SQL.list).all());
@@ -19420,6 +19412,9 @@ async function convertLegacyPolicies(db, found) {
19420
19412
  return statements;
19421
19413
  }
19422
19414
  var MIGRATIONS_TABLE = "CREATE TABLE IF NOT EXISTS _naro_migrations (version INTEGER PRIMARY KEY, name TEXT NOT NULL, applied_at TEXT NOT NULL DEFAULT (datetime('now')))";
19415
+ var STORAGE_SUBQUERIES_VERSION = 7;
19416
+ var STORAGE_SUBQUERIES_NAME = "storage_subquery_policies";
19417
+ var STORAGE_SUBQUERIES_SQL = `SELECT version FROM _naro_migrations WHERE version = ${String(STORAGE_SUBQUERIES_VERSION)} AND name = '${STORAGE_SUBQUERIES_NAME}'`;
19423
19418
  var SYSTEM_MIGRATIONS = [
19424
19419
  {
19425
19420
  version: 1,
@@ -19488,9 +19483,78 @@ var SYSTEM_MIGRATIONS = [
19488
19483
  name: "storage_bucket_deleting",
19489
19484
  statements: [],
19490
19485
  addColumns: [STORAGE_BUCKET_DELETING_COLUMN]
19486
+ },
19487
+ {
19488
+ // Storage 3-1j (plan correction (p)): no schema — the mark that a worker
19489
+ // which reads storage.objects in a policy's subquery has migrated this
19490
+ // database. Only the Naro worker's own `internal/migrate` applies system
19491
+ // migrations, so the row is there once such a worker has run, and stays:
19492
+ // every later worker reads them too — as long as the worker bundle does
19493
+ // not go back. Deploying web from before 3-1j makes the next upgrade
19494
+ // put an older worker back, and a run that lost its lease may upload
19495
+ // one late; such a policy then answers 500 POLICY_INVALID (closed, no
19496
+ // rows) until it is dropped or the newer worker is uploaded again
19497
+ // (3-1j 최종 리뷰). Migration 5 alone cannot tell —
19498
+ // backends migrated by 3-1a to 3-1i have it, and their worker refuses
19499
+ // such a policy ("A policy cannot read storage.objects in a subquery
19500
+ // yet") and answers every request of its table 500 POLICY_INVALID until
19501
+ // the next upgrade. The control plane takes one only where this row is
19502
+ // (`STORAGE_SUBQUERIES_SQL`, `buildPolicyBatch`).
19503
+ version: STORAGE_SUBQUERIES_VERSION,
19504
+ name: STORAGE_SUBQUERIES_NAME,
19505
+ statements: []
19491
19506
  }
19492
19507
  ];
19493
19508
 
19509
+ // ../../packages/data-core/src/policy-migration.ts
19510
+ var STORED_POLICIES_SQL = `SELECT table_name, name, kind, command, roles, using_expr, check_expr, owner_column, updated_at FROM ${NAMED_POLICY_TABLE} ORDER BY table_name, name`;
19511
+ var SYSTEM_PREDICATE = systemTablePredicate("name");
19512
+ var STORAGE_EXISTS = `EXISTS (SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ${sqlStringLiteral(STORAGE_OBJECTS_TABLE)})`;
19513
+ var STORAGE_SUBQUERIES_READY = `EXISTS (${STORAGE_SUBQUERIES_SQL})`;
19514
+
19515
+ // ../../packages/data-core/src/realtime-limits.ts
19516
+ var HOBBY_REALTIME_LIMITS = Object.freeze({
19517
+ connections: 200,
19518
+ newConnectionsPerSecond: 10,
19519
+ messagesPerSecond: 100,
19520
+ joinsPerSecond: 100,
19521
+ presencePerSecond: 20,
19522
+ broadcastPayloadBytes: 262144,
19523
+ policyChecksPerSecond: 100,
19524
+ restRequestsPerSecond: 10
19525
+ });
19526
+ var PRO_REALTIME_LIMITS = Object.freeze({
19527
+ connections: 500,
19528
+ newConnectionsPerSecond: 25,
19529
+ messagesPerSecond: 500,
19530
+ joinsPerSecond: 500,
19531
+ presencePerSecond: 50,
19532
+ broadcastPayloadBytes: 3072e3,
19533
+ policyChecksPerSecond: 250,
19534
+ restRequestsPerSecond: 25
19535
+ });
19536
+ var ENTERPRISE_REALTIME_LIMITS = Object.freeze({
19537
+ connections: 2e3,
19538
+ newConnectionsPerSecond: 100,
19539
+ messagesPerSecond: 1e3,
19540
+ joinsPerSecond: 1e3,
19541
+ presencePerSecond: 1e3,
19542
+ broadcastPayloadBytes: 3072e3,
19543
+ policyChecksPerSecond: 500,
19544
+ restRequestsPerSecond: 100
19545
+ });
19546
+
19547
+ // ../../packages/data-core/src/storage-multipart.ts
19548
+ var MiB = 1024 * 1024;
19549
+ var MULTIPART_PART_SIZES = [16 * MiB, 32 * MiB, 64 * MiB];
19550
+ var MAX_MULTIPART_PARTS = 1e4;
19551
+ var MAX_MULTIPART_BYTES = 64 * MiB * MAX_MULTIPART_PARTS;
19552
+ var MULTIPART_LOCK_MS = 60 * 60 * 1e3;
19553
+ var MULTIPART_PART_EXTEND_BELOW_MS = 30 * 60 * 1e3;
19554
+ var MULTIPART_COMPLETE_LOCK_MS = 15 * 60 * 1e3;
19555
+ var UUID = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
19556
+ var UPLOAD_ID = new RegExp(`^(${UUID})\\.(${UUID})$`);
19557
+
19494
19558
  // ../../packages/data-core/src/write-quota.ts
19495
19559
  var WRITE_QUOTA_KEY = "write_quota";
19496
19560
  var WRITE_QUOTA_SQL = {
@@ -20627,7 +20691,7 @@ var DATABASE_TOOLS = [
20627
20691
  destructiveHint: true,
20628
20692
  idempotentHint: false
20629
20693
  },
20630
- description: `${DESTRUCTIVE}applies a migration (DDL) to the D1 database and records it in the d1_migrations ledger \u2014 the same ledger wrangler uses. \`name\` is a short snake_case title; naro numbers it (NNNN_name.sql) and, when the project directory is known, writes the file to ./migrations so \`naro db push\` sees it as applied. All-or-nothing: if a statement fails, the whole migration is rolled back and not recorded. Write CREATE POLICY in the same migration as the table, as in Supabase: it is applied with the table, all or nothing. A table without a policy refuses every anon and signed-in request to the Data API. DROP POLICY [IF EXISTS], COMMENT ON POLICY p ON t IS 'naro:owner <column>' and ALTER TABLE t ENABLE ROW LEVEL SECURITY are accepted too; DISABLE ROW LEVEL SECURITY, GRANT, REVOKE and ALTER POLICY are refused before anything runs. Dropping a table drops its policies, so a SQLite table rebuild (create __new_x, copy, drop x, rename) must create the policies again after the rename: warnings lists the ones it removed, with their SQL. ALTER TABLE a RENAME TO b moves a's policies to b and rewrites the policies of other tables that read a, as in Postgres: do not create them again on b, where one of the same name is refused. ${UNTRUSTED} ${SHARED_DATABASE} ${LIMITS}`,
20694
+ description: `${DESTRUCTIVE}applies a migration (DDL) to the D1 database and records it in the d1_migrations ledger \u2014 the same ledger wrangler uses. \`name\` is a short snake_case title; naro numbers it (NNNN_name.sql) and, when the project directory is known, writes the file to ./migrations so \`naro db push\` sees it as applied. All-or-nothing: if a statement fails, the whole migration is rolled back and not recorded. Write CREATE POLICY in the same migration as the table, as in Supabase: it is applied with the table, all or nothing. A table without a policy refuses every anon and signed-in request to the Data API. DROP POLICY [IF EXISTS], COMMENT ON POLICY p ON t IS 'naro:owner <column>' and ALTER TABLE t ENABLE ROW LEVEL SECURITY are accepted too; DISABLE ROW LEVEL SECURITY, GRANT, REVOKE and ALTER POLICY are refused before anything runs. Dropping a table drops its policies, so a SQLite table rebuild (create __new_x, copy, drop x, rename) must create the policies again after the rename: warnings lists the ones it removed, with their SQL. ALTER TABLE a RENAME TO b moves a's policies to b and rewrites the policies of other tables that read a, as in Postgres: do not create them again on b, where one of the same name is refused. ALTER TABLE t RENAME COLUMN c TO d rewrites every policy that reads t.c, an owner rule's column included, to read t.d; a rename such a policy cannot follow is refused before anything runs. ${UNTRUSTED} ${SHARED_DATABASE} ${LIMITS}`,
20631
20695
  inputSchema: {
20632
20696
  type: "object",
20633
20697
  properties: {
@@ -20731,7 +20795,7 @@ var DATABASE_TOOLS = [
20731
20795
  destructiveHint: true,
20732
20796
  idempotentHint: false
20733
20797
  },
20734
- description: `${DESTRUCTIVE}changes a table of the project's D1 database without writing SQL, as one migration recorded in d1_migrations and written to ./migrations when the project directory is known. action is add_column (newColumn, shaped like a create_table column), rename_column (column, to), rename_table (to) or drop_column (column) \u2014 which deletes the column and its value in every row and needs confirm set to "<table>.<column>". SQLite cannot add a PRIMARY KEY or UNIQUE column, a NOT NULL column without a default that is not NULL, or a column defaulting to CURRENT_TIMESTAMP, and cannot drop a primary key, UNIQUE or indexed column: naro refuses those up front and says why. It also refuses to drop or rename a table or column that a trigger or view uses (read from their SQL; when the SQL leaves it open, a warning instead) \u2014 drop or recreate that trigger or view with apply_migration first. Changing a column's type is not an action \u2014 it needs a table rebuild, so write that migration with apply_migration. rename_table moves the table's Data API policies to the new name and rewrites the policies of other tables that read it by name, in the same migration, as in Postgres: warnings says which. A rename such a policy cannot follow (an alias in it already goes by the new name) is refused before anything runs. Policies an earlier table left under the new name are removed: removedPolicies lists them: statements has each as the CREATE POLICY that naro policy create takes back, except an owner rule (its ownerColumn is set), which CREATE POLICY cannot say \u2014 setCommand is the naro policy set command that sets those again. A rename or drop that takes a table or column from under a policy \u2014 this table's or, through a subquery, another table's \u2014 makes the requests that policy applies to answer 500 POLICY_INVALID: warnings names each policy the change breaks, with dryRun: true before anything is applied. Read warnings after a rename or a drop. dryRun: true returns the SQL without applying anything. ${UNTRUSTED} ${SHARED_DATABASE}`,
20798
+ description: `${DESTRUCTIVE}changes a table of the project's D1 database without writing SQL, as one migration recorded in d1_migrations and written to ./migrations when the project directory is known. action is add_column (newColumn, shaped like a create_table column), rename_column (column, to), rename_table (to) or drop_column (column) \u2014 which deletes the column and its value in every row and needs confirm set to "<table>.<column>". SQLite cannot add a PRIMARY KEY or UNIQUE column, a NOT NULL column without a default that is not NULL, or a column defaulting to CURRENT_TIMESTAMP, and cannot drop a primary key, UNIQUE or indexed column: naro refuses those up front and says why. It also refuses to drop or rename a table or column that a trigger or view uses (read from their SQL; when the SQL leaves it open, a warning instead) \u2014 drop or recreate that trigger or view with apply_migration first. Changing a column's type is not an action \u2014 it needs a table rebuild, so write that migration with apply_migration. rename_table moves the table's Data API policies to the new name and rewrites the policies of other tables that read it by name, in the same migration, as in Postgres: warnings says which. rename_column rewrites every policy that reads the column, this table's or another's through a subquery, an owner rule's column included: warnings says which. A rename such a policy cannot follow (an alias in it already goes by the new name, or a policy already reads the new column name) is refused before anything runs. Policies an earlier table left under the new name are removed: removedPolicies lists them: statements has each as the CREATE POLICY that naro policy create takes back, except an owner rule (its ownerColumn is set), which CREATE POLICY cannot say \u2014 setCommand is the naro policy set command that sets those again. A rename or drop that takes a table or column from under a policy \u2014 this table's or, through a subquery, another table's \u2014 makes the requests that policy applies to answer 500 POLICY_INVALID: warnings names each policy the change breaks, with dryRun: true before anything is applied. Read warnings after a rename or a drop. dryRun: true returns the SQL without applying anything. ${UNTRUSTED} ${SHARED_DATABASE}`,
20735
20799
  inputSchema: {
20736
20800
  type: "object",
20737
20801
  properties: {
@@ -22075,7 +22139,7 @@ var REGISTERED = [
22075
22139
  },
22076
22140
  environment: {
22077
22141
  type: "string",
22078
- description: "Slug of a named environment (list_environments, create_environment) to deploy to instead of preview, such as qa. Not together with production: true \u2014 the control plane refuses both. production and preview are not environment names: use production: true, or omit this for a preview."
22142
+ description: "Slug of a named environment (list_environments, create_environment) to deploy to instead of preview, such as qa. Not together with production: true \u2014 both are refused (VALIDATION_FAILED) before any value is read, asked for or sent and before the deployment is created. production and preview are not environment names: use production: true, or omit this for a preview."
22079
22143
  },
22080
22144
  branch: {
22081
22145
  type: "string",
@@ -22124,8 +22188,9 @@ var REGISTERED = [
22124
22188
  projectId: link?.projectId,
22125
22189
  teamSlug: team.slug,
22126
22190
  production: args.production === true,
22127
- // Sent beside production, not instead of it: the control plane
22128
- // refuses both together and that answer is the one shown.
22191
+ // Passed beside production, not instead of it: deployDirectory
22192
+ // refuses both together before any request (an older control plane
22193
+ // would deploy production), and that answer is the one shown.
22129
22194
  environment,
22130
22195
  // Never a surprise browser window: this server may be headless.
22131
22196
  allowLogin: false,
@@ -22678,7 +22743,7 @@ var REGISTERED = [
22678
22743
  destructiveHint: false,
22679
22744
  idempotentHint: true
22680
22745
  },
22681
- description: 'Connects the project to a GitHub repository so every push to the default branch deploys automatically. Use after deploy reports gitConnection.state "available" AND the user has agreed. If the response has connected:false with an installUrl, the GitHub App is not installed on that account yet \u2014 give the user the installUrl to open in their own browser, then call this tool again once they confirm. With installUrl null there is nothing to install from here: tell the user to connect from the Naro dashboard. Never open a browser yourself.',
22746
+ description: 'Connects the project to a GitHub repository so every push to the default branch deploys automatically. Use after deploy reports gitConnection.state "available" AND the user has agreed. If the response has connected:false with an installUrl, Naro cannot reach the repository yet \u2014 the installUrl either installs the GitHub App on that account or imports the GitHub access of the user for an account the team already connected. Give it to the user to open in their own browser; if the repository is not among the ones the App was granted on GitHub, they add it there first. Call this tool again once they confirm. With installUrl null there is nothing to install from here: tell the user to connect from the Naro dashboard. Never open a browser yourself.',
22682
22747
  inputSchema: {
22683
22748
  type: "object",
22684
22749
  properties: {
@@ -22719,7 +22784,7 @@ var REGISTERED = [
22719
22784
  // installing from here would not connect anything. "Open the
22720
22785
  // installUrl" sent the agent guessing one (known-issues #42); point
22721
22786
  // at the dashboard, as the CLI does.
22722
- message: result.installUrl ? `Naro cannot reach ${fullName} yet. Ask the user to open the installUrl in their browser and install the GitHub App, then call this tool again.` : `Naro cannot reach ${fullName}, and no install link was offered. Tell the user to connect the repository from the Naro dashboard \u2014 do not make up a link.`
22787
+ message: result.installUrl ? `Naro cannot reach ${fullName} yet. Ask the user to open the installUrl in their browser (and, if the repository is not among the ones the GitHub App was granted on GitHub, add it there first), then call this tool again.` : `Naro cannot reach ${fullName}, and no install link was offered. Tell the user to connect the repository from the Naro dashboard \u2014 do not make up a link.`
22723
22788
  };
22724
22789
  }
22725
22790
  return {
@@ -22786,7 +22851,7 @@ async function openClaims(client, projectId) {
22786
22851
  }
22787
22852
 
22788
22853
  // src/server.ts
22789
- var SERVER_VERSION = "0.18.0";
22854
+ var SERVER_VERSION = "0.18.1";
22790
22855
  function visibleTools(options2) {
22791
22856
  const { features, readOnly } = options2;
22792
22857
  return TOOLS.flatMap((tool) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "naro-mcp",
3
- "version": "0.18.0",
3
+ "version": "0.18.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "naro-mcp": "./dist/index.js"
@@ -18,7 +18,7 @@
18
18
  "dependencies": {
19
19
  "@modelcontextprotocol/sdk": "^1.0.0",
20
20
  "@supabase/supabase-js": "2.112.3",
21
- "naro-cli": "0.19.0"
21
+ "naro-cli": "0.19.1"
22
22
  },
23
23
  "devDependencies": {
24
24
  "tsup": "8.5.0",