diff --git a/.changeset/24404-cloud-connection-this-system.md b/.changeset/24404-cloud-connection-this-system.md new file mode 100644 index 00000000000..aa9ee9d888b --- /dev/null +++ b/.changeset/24404-cloud-connection-this-system.md @@ -0,0 +1,10 @@ +--- +'@objectstack/cloud-connection': patch +--- + +Clause-②: no + +fix(cloud-connection): the sign-in refusals and the unmanaged-install copy no longer call the running system "this environment" + +- **What changed.** Inside the running system, user-facing copy names the system the user is in as "this system", or drops the noun. The seven `401 UNAUTHENTICATED` answers on `/api/v1/cloud-connection/*` now read "Sign in to connect a cloud account." (`bind/start`), "Sign in to install apps." (`install`) and "Sign in." (`bind/poll`, `unbind`, `installation`, `installed`, `org-packages`), where each used to say "Sign in to this environment …". On `/api/v1/marketplace/install-local`, the `409 RESOURCE_CONFLICT` that refuses a managed install over an unmanaged copy now names the holder as "the writable base 'BASE' — an unmanaged install of this package, or a base authored in this system — whose items are this system's own metadata", and the unmanaged install's success `note` reads "The package's metadata is now this system's own, in the base 'BASE' …". +- **Unchanged.** Every HTTP status, error code (`UNAUTHENTICATED`, `RESOURCE_CONFLICT`), `details` object and response shape. Server log lines are not touched. diff --git a/.changeset/24404-mcp-this-system.md b/.changeset/24404-mcp-this-system.md new file mode 100644 index 00000000000..cb7d9498dff --- /dev/null +++ b/.changeset/24404-mcp-this-system.md @@ -0,0 +1,10 @@ +--- +'@objectstack/mcp': patch +--- + +Clause-②: no + +fix(mcp): the known-partial listing warning and the Connect-an-Agent bundle description say "this system" + +- **What changed.** Inside the running system, user-facing copy names the system the caller is in as "this system". The warning an MCP client receives with a known-partial object listing (the `objectstack://objects` resource and the `list_objects` tool) now says "this system declares at least that many objects, possibly more", and the Connect-an-Agent UI bundle's description ends "for connecting MCP clients to this system". +- **Unchanged.** The `SERVICE_UNAVAILABLE` code that marks a partial listing, every payload shape, the portable `SKILL.md` (which names the deployment an outside agent connects to), and server log lines. diff --git a/content/docs/deployment/cloud-connection.mdx b/content/docs/deployment/cloud-connection.mdx index fdf03357b78..949ea4094d9 100644 --- a/content/docs/deployment/cloud-connection.mdx +++ b/content/docs/deployment/cloud-connection.mdx @@ -125,7 +125,7 @@ button: | *The request expired before it was approved. Start again.* | The code's lifetime ran out, or the cloud answered `expired_token`. | | *The connection request was denied. Start again.* | The cloud answered `access_denied`. | | *Device authorization failed: …* | The cloud ended the request with another error, named after the colon. | -| *Sign in to this environment to connect a cloud account.* (or *Sign in to this environment.* while waiting) | Your session on the server has ended. | +| *Sign in to connect a cloud account.* (or *Sign in.* while waiting) | Your session on the server has ended. | | *No cloud control plane configured.* | The plugin is mounted without a control-plane URL. | | a network error such as *fetch failed* | The server could not reach the cloud. | diff --git a/packages/cloud-connection/src/cloud-connection-plugin.ts b/packages/cloud-connection/src/cloud-connection-plugin.ts index 24574139577..233061fffcc 100644 --- a/packages/cloud-connection/src/cloud-connection-plugin.ts +++ b/packages/cloud-connection/src/cloud-connection-plugin.ts @@ -594,7 +594,7 @@ export class CloudConnectionPlugin implements Plugin { } const session = await resolveSession(environmentId ?? '', c.req.raw); if (session?.authGate) return refuseHeldSession(c, session.authGate); - if (!session?.userId) return c.json({ success: false, error: { code: 'UNAUTHENTICATED', message: 'Sign in to this environment to connect a cloud account.' } }, 401); + if (!session?.userId) return c.json({ success: false, error: { code: 'UNAUTHENTICATED', message: 'Sign in to connect a cloud account.' } }, 401); // [#24148] Refused before any work, so a caller without the // capability cannot learn whether a control plane is configured. if (!(await holdsSetupAccess(c.req.raw, session))) return refuseWithoutSetupAccess(c); @@ -656,7 +656,7 @@ export class CloudConnectionPlugin implements Plugin { } const session = await resolveSession(environmentId ?? '', c.req.raw); if (session?.authGate) return refuseHeldSession(c, session.authGate); - if (!session?.userId) return c.json({ success: false, error: { code: 'UNAUTHENTICATED', message: 'Sign in to this environment.' } }, 401); + if (!session?.userId) return c.json({ success: false, error: { code: 'UNAUTHENTICATED', message: 'Sign in.' } }, 401); // [#24148] This is the door that persists the binding. if (!(await holdsSetupAccess(c.req.raw, session))) return refuseWithoutSetupAccess(c); if (!cloudUrl) return c.json({ success: false, error: { code: 'CLOUD_UNCONFIGURED', message: 'No cloud control plane configured.' } }, 503); @@ -758,7 +758,7 @@ export class CloudConnectionPlugin implements Plugin { } const session = await resolveSession(environmentId ?? '', c.req.raw); if (session?.authGate) return refuseHeldSession(c, session.authGate); - if (!session?.userId) return c.json({ success: false, error: { code: 'UNAUTHENTICATED', message: 'Sign in to this environment.' } }, 401); + if (!session?.userId) return c.json({ success: false, error: { code: 'UNAUTHENTICATED', message: 'Sign in.' } }, 401); // [#24148] Refused before the revoke and before the local clear. if (!(await holdsSetupAccess(c.req.raw, session))) return refuseWithoutSetupAccess(c); @@ -810,7 +810,7 @@ export class CloudConnectionPlugin implements Plugin { const session = await resolveSession(environmentId, c.req.raw); if (session?.authGate) return refuseHeldSession(c, session.authGate); if (!session?.userId) { - return c.json({ success: false, error: { code: 'UNAUTHENTICATED', message: 'Sign in to this environment to install apps.' } }, 401); + return c.json({ success: false, error: { code: 'UNAUTHENTICATED', message: 'Sign in to install apps.' } }, 401); } if (!cloudUrl || !credential()) { @@ -864,7 +864,7 @@ export class CloudConnectionPlugin implements Plugin { const session = await resolveSession(environmentId, c.req.raw); if (session?.authGate) return refuseHeldSession(c, session.authGate); if (!session?.userId) { - return c.json({ success: false, error: { code: 'UNAUTHENTICATED', message: 'Sign in to this environment.' } }, 401); + return c.json({ success: false, error: { code: 'UNAUTHENTICATED', message: 'Sign in.' } }, 401); } let packageId = ''; @@ -927,7 +927,7 @@ export class CloudConnectionPlugin implements Plugin { const session = await resolveSession(environmentId, c.req.raw); if (session?.authGate) return refuseHeldSession(c, session.authGate); if (!session?.userId) { - return c.json({ success: false, error: { code: 'UNAUTHENTICATED', message: 'Sign in to this environment.' } }, 401); + return c.json({ success: false, error: { code: 'UNAUTHENTICATED', message: 'Sign in.' } }, 401); } if (!cloudUrl || !credential()) { @@ -965,7 +965,7 @@ export class CloudConnectionPlugin implements Plugin { const session = await resolveSession(environmentId ?? '', c.req.raw); if (session?.authGate) return refuseHeldSession(c, session.authGate); if (!session?.userId) { - return c.json({ success: false, error: { code: 'UNAUTHENTICATED', message: 'Sign in to this environment.' } }, 401); + return c.json({ success: false, error: { code: 'UNAUTHENTICATED', message: 'Sign in.' } }, 401); } if (!cloudUrl || !credential()) { diff --git a/packages/cloud-connection/src/marketplace-install-local-install-modes.test.ts b/packages/cloud-connection/src/marketplace-install-local-install-modes.test.ts index 5946a8e5ab3..5b1c617a079 100644 --- a/packages/cloud-connection/src/marketplace-install-local-install-modes.test.ts +++ b/packages/cloud-connection/src/marketplace-install-local-install-modes.test.ts @@ -406,7 +406,7 @@ describe('install-local — a managed install never takes over an unmanaged copy expect(res.status).toBe(409); expect(res.payload.error.code).toBe('RESOURCE_CONFLICT'); expect(res.payload.error.details).toEqual({ namespace: 'crm', base: 'com.example.crm-copy' }); - expect(res.payload.error.message).toContain("held by the environment base 'com.example.crm-copy'"); + expect(res.payload.error.message).toContain("'com.example.crm-copy'"); expect(h.register).not.toHaveBeenCalled(); expect(h.syncSchemas).not.toHaveBeenCalled(); expect(h.ledgerHas('com.example.crm')).toBe(false); diff --git a/packages/cloud-connection/src/marketplace-install-local-plugin.ts b/packages/cloud-connection/src/marketplace-install-local-plugin.ts index 078c71b76c5..ec1b9115e0e 100644 --- a/packages/cloud-connection/src/marketplace-install-local-plugin.ts +++ b/packages/cloud-connection/src/marketplace-install-local-plugin.ts @@ -1494,8 +1494,8 @@ export class MarketplaceInstallLocalPlugin implements Plugin { error: { code: 'RESOURCE_CONFLICT', message: `Package '${manifestId}' cannot be installed managed: its namespace '${copyBase.namespace}' is held by ` - + `the environment base '${copyBase.base}' — an unmanaged install of this package, or a base authored ` - + 'in this environment — whose items are this environment\'s own metadata. A managed ' + + `the writable base '${copyBase.base}' — an unmanaged install of this package, or a base authored ` + + 'in this system — whose items are this system\'s own metadata. A managed ' + 'install would hand that namespace to the package and stop serving them. Keep the copy (it follows ' + `no upgrade), or uninstall the base '${copyBase.base}' first; its items go with it.`, details: { namespace: copyBase.namespace, base: copyBase.base }, @@ -2559,7 +2559,7 @@ export class MarketplaceInstallLocalPlugin implements Plugin { }, } : {}), - note: `The package's metadata is now this environment's own, in the base '${result.targetPackageId}', ` + note: `The package's metadata is now this system's own, in the base '${result.targetPackageId}', ` + 'and it follows no upgrade. Refresh the console to see it.', }, }, 200); diff --git a/packages/mcp/src/connect-ui.ts b/packages/mcp/src/connect-ui.ts index e866af19230..400cf11586d 100644 --- a/packages/mcp/src/connect-ui.ts +++ b/packages/mcp/src/connect-ui.ts @@ -81,7 +81,7 @@ export const CONNECT_AGENT_UI_BUNDLE = { type: 'plugin', scope: 'system', name: 'Connect an Agent UI', - description: 'Connect-an-Agent page + Setup and Account navigation for connecting MCP clients to this environment.', + description: 'Connect-an-Agent page + Setup and Account navigation for connecting MCP clients to this system.', pages: [CONNECT_AGENT_PAGE], // Both entries point at the one `connect_agent` page and share the item id. // That is legal and deliberate, read off the fold rather than assumed: the diff --git a/packages/mcp/src/metadata-completeness.ts b/packages/mcp/src/metadata-completeness.ts index d07c2cb1fb1..3374268c8f8 100644 --- a/packages/mcp/src/metadata-completeness.ts +++ b/packages/mcp/src/metadata-completeness.ts @@ -44,7 +44,7 @@ export function metadataPartialListingSentence(plural: string, served: number): return ( `The metadata service could not be fully read, so this listing of ${plural} is known to be INCOMPLETE. ` + `${served} ${served === 1 ? 'is' : 'are'} being served and the total is withheld — ` - + `this environment declares at least that many ${plural}, possibly more. ` + + `this system declares at least that many ${plural}, possibly more. ` + 'Retry once the metadata service is reachable.' ); }