diff --git a/src/index.ts b/src/index.ts index da65de2..d256f06 100644 --- a/src/index.ts +++ b/src/index.ts @@ -443,7 +443,7 @@ Drop in as REDIS_URL with any Redis client (ioredis, node-redis, go-redis, etc.) Without INSTANODE_TOKEN: anonymous tier — 5 MB, 24h TTL. The response carries 'note' + 'upgrade' (claim URL) — surface both verbatim. With INSTANODE_TOKEN (paid): hobby 50 MB / hobby_plus 50 MB / pro 512 MB / -growth 1024 MB / team unlimited (per api/plans.yaml), permanent. +growth 1024 MB / team 1536 MB (per api/plans.yaml), permanent. Cleanup: anonymous resources auto-expire after 24h — there is no on-demand delete for anonymous tokens, by design. On a paid tier, call @@ -487,7 +487,8 @@ mongodb driver (mongoose, pymongo, etc.). Without INSTANODE_TOKEN: anonymous tier — 5 MB, 2 connections, 24h TTL. 'note' + 'upgrade' fields in the response surface the claim URL. -With INSTANODE_TOKEN (paid): hobby 100 MB / pro 2 GB / team unlimited, permanent. +With INSTANODE_TOKEN (paid): hobby 100 MB / hobby_plus 1 GB / pro 5 GB / +growth 20 GB / team 40 GB (per api/plans.yaml), permanent. Cleanup: anonymous resources auto-expire after 24h — there is no on-demand delete for anonymous tokens, by design. On a paid tier, call @@ -966,7 +967,7 @@ agent can route the user to the dashboard instead of guessing.`, server.tool( "create_deploy", - `Create a new deploy — OR set \`redeploy: true\` to update an existing deployment with the same name (preserves app_id + URL). Optionally set \`private: true\` + \`allowed_ips: ['1.2.3.4', '10.0.0.0/8']\` to restrict access to specific IPs. Requires Pro tier or higher. Useful when an agent is asked to deploy a CRM, internal dashboard, or staging app that should only be reachable by the user. + `Create a new deploy — OR set \`redeploy: true\` to update an existing deployment with the same name (preserves app_id + URL). Optionally set \`private: true\` + \`allowed_ips: ['1.2.3.4', '10.0.0.0/8']\` to restrict access to specific IPs. Deploying requires a paid plan: Hobby tier or higher (Hobby = 1 app, Hobby Plus = 2, Pro = 10, Growth = 50, Team = 100 — per api/plans.yaml deployments_apps); anonymous and free tiers cannot deploy and get HTTP 402. The PRIVATE-deploy option (private: true + allowed_ips) additionally requires Pro tier or higher. Useful when an agent is asked to deploy a CRM, internal dashboard, or staging app that should only be reachable by the user. Deploys a containerized application on instanode.dev (POST /deploy/new). diff --git a/test/integration.test.ts b/test/integration.test.ts index f0c3c35..5aa6351 100644 --- a/test/integration.test.ts +++ b/test/integration.test.ts @@ -1505,6 +1505,74 @@ describe("instanode-mcp integration suite", () => { await close(); } }); + + // BUGHUNT-iter2: the strict-≥80%-margin tier redesign (2026-06-05) retired + // every "unlimited" (-1) limit. Team's redis cap is now a finite 1536 MB + // (api/plans.yaml). The description used to say "team unlimited" — a stale + // claim an agent would relay to a user as a false promise. Guard the + // honest finite number AND assert the word "unlimited" never reappears. + it("create_cache description shows team's finite 1536 MB cap, never 'unlimited'", async () => { + const { client, close } = await connectClient(mock.url, "none"); + try { + const { tools } = await client.listTools(); + const cache = tools.find((t) => t.name === "create_cache")!; + const desc = cache.description ?? ""; + assert.match(desc, /team 1536 MB/i, "expected the finite team cap 'team 1536 MB' in create_cache description"); + assert.doesNotMatch(desc, /unlimited/i, "create_cache description must not advertise an 'unlimited' tier (strict-80 redesign retired all -1 limits)"); + } finally { + await close(); + } + }); + }); + + // ── BUGHUNT-iter2: nosql + deploy description honesty ─────────────────────── + + describe("BUGHUNT-iter2 — create_nosql description honesty", () => { + // Pre-fix the description said "hobby 100 MB / pro 2 GB / team unlimited" — + // pro was wrong (plans.yaml mongodb_storage_mb pro = 5120 MB = 5 GB, not 2 GB) + // and team is no longer unlimited (strict-80: 40960 MB = 40 GB). + it("create_nosql description quotes the live plans.yaml mongodb numbers (5 GB pro, finite team)", async () => { + const { client, close } = await connectClient(mock.url, "none"); + try { + const { tools } = await client.listTools(); + const nosql = tools.find((t) => t.name === "create_nosql")!; + const desc = nosql.description ?? ""; + assert.match(desc, /hobby 100 MB/i, "expected hobby 100 MB in create_nosql description"); + assert.match(desc, /pro 5 GB/i, "expected pro 5 GB (5120 MB) in create_nosql description — pre-fix wrongly said 2 GB"); + assert.match(desc, /team 40 GB/i, "expected the finite team cap 'team 40 GB' in create_nosql description"); + assert.doesNotMatch(desc, /unlimited/i, "create_nosql description must not advertise an 'unlimited' tier (strict-80 redesign retired all -1 limits)"); + } finally { + await close(); + } + }); + }); + + describe("BUGHUNT-iter2 — create_deploy tier-gate honesty", () => { + // Pre-fix the opening line said "Requires Pro tier or higher", conflating + // the base-deploy gate with the PRIVATE-deploy gate. Hobby CAN deploy + // (plans.yaml deployments_apps: hobby=1). An agent reading the old copy + // would wrongly refuse a legitimate Hobby deployment. The fix states the + // base gate is Hobby+ while keeping the Pro requirement for private deploys. + it("create_deploy description says Hobby can deploy and reserves Pro for private deploys", async () => { + const { client, close } = await connectClient(mock.url, "none"); + try { + const { tools } = await client.listTools(); + const deploy = tools.find((t) => t.name === "create_deploy")!; + const desc = deploy.description ?? ""; + // Base deploy gate is Hobby+, not Pro+. + assert.match(desc, /Hobby tier or higher/i, "create_deploy must state the base deploy gate is Hobby tier or higher"); + // The Pro mention must survive (private deploys genuinely need Pro+). + assert.match(desc, /pro tier/i, "create_deploy must still mention the Pro gate (for private deploys)"); + // It must NOT claim Pro is required to deploy at all. + assert.doesNotMatch( + desc, + /restrict access to specific IPs\. Requires Pro tier or higher\./i, + "create_deploy must not claim a blanket 'Requires Pro tier or higher' base gate — Hobby can deploy", + ); + } finally { + await close(); + } + }); }); });