open_idp_sspr_portal
open_idp_sspr_portal
Opens the IDP's self-service password-reset URL in the user's default browser. Supports Okta (requires a tenant slug), Microsoft Entra, and Google Workspace. The user completes the reset in the IDP's own UI — the agent does NOT see the password. Use dryRun:true to preview the URL without opening a browser window.
Metadata
Name
open_idp_sspr_portal
Updated
2 months ago
Source
Risk
Low
Requires consent
false
Affected scope
user
Code
/**
* mcp/skills/openIdpSsprPortal.ts — open_idp_sspr_portal
*
* Deep-links the user's default browser to the IDP's self-service
* password-reset URL. The user completes the reset in the IDP's own UI
* (with whatever MFA / recovery factors the IDP enforces) — the agent
* never sees the password.
*
* After this tool returns, the parent skill emits a wait_for_user_ack
* plan step so the run blocks until the user confirms "done".
*
* Platform strategy
* -----------------
* darwin `open <url>`
* win32 `cmd /c start "" "<url>"` (empty title is required for start)
*
* Dry-run returns the URL that would be opened without actually opening
* it, so G4's dry-run preview shows the user exactly where they'll land.
*/
import { z } from "zod";
import { isDarwin, isWin32, execAsync } from "./_shared/platform";
import { buildSsprUrl, idpDisplayName, type Idp } from "./_shared/idp";
// -- Meta ---------------------------------------------------------------------
export const meta = {
name: "open_idp_sspr_portal",
description:
"Opens the IDP's self-service password-reset URL in the user's default " +
"browser. Supports Okta (requires a tenant slug), Microsoft Entra, and " +
"Google Workspace. The user completes the reset in the IDP's own UI — " +
"the agent does NOT see the password. Use dryRun:true to preview the " +
"URL without opening a browser window.",
riskLevel: "low",
destructive: false,
requiresConsent: false,
supportsDryRun: true,
affectedScope: ["user"],
auditRequired: true,
schema: {
idp: z
.enum(["okta", "entra", "google", "unknown"])
.describe("IDP identifier from detect_identity_provider."),
tenant: z
.string()
.optional()
.describe("Okta tenant slug (ignored for Entra/Google)."),
dryRun: z
.boolean()
.optional()
.describe("When true, return the resolved URL without opening a browser."),
},
} as const;
// -- Types --------------------------------------------------------------------
export interface OpenSsprResult {
idp: Idp;
tenant: string | null;
url: string | null;
/** "opened" | "dry-run" | "not-applicable" */
status: "opened" | "dry-run" | "not-applicable";
idpLabel: string;
message: string;
}
// -- Implementation -----------------------------------------------------------
async function openUrl(url: string): Promise<void> {
// URL is validated before we get here (see buildSsprUrl + the validation
// pass below), but do a final sanity check so a forged URL cannot reach
// the shell.
if (!/^https:\/\/[a-zA-Z0-9._/\-]+(\?[^ \n"'`]*)?$/.test(url)) {
throw new Error(`Refusing to open URL with suspicious characters: ${url}`);
}
if (isDarwin()) {
await execAsync(`open "${url}"`, { timeout: 5_000 });
return;
}
if (isWin32()) {
// `start` requires a blank title ("") as first arg when the URL has spaces.
await execAsync(`cmd /c start "" "${url}"`, { timeout: 5_000 });
return;
}
throw new Error("Unsupported platform — cannot open browser.");
}
// Exported for unit tests.
export const __testing = { openUrl };
// -- Exported run function ----------------------------------------------------
export async function run({
idp,
tenant,
dryRun = false,
}: {
idp: Idp;
tenant?: string;
dryRun?: boolean;
}): Promise<OpenSsprResult> {
const idpLabel = idpDisplayName(idp);
if (idp === "unknown") {
return {
idp, tenant: tenant ?? null, url: null,
status: "not-applicable",
idpLabel,
message: "IDP is unknown — cannot open a self-service portal. Escalate to helpdesk.",
};
}
const url = buildSsprUrl(idp, tenant);
if (!url) {
return {
idp, tenant: tenant ?? null, url: null,
status: "not-applicable",
idpLabel,
message: `No self-service portal URL available for ${idpLabel}.`,
};
}
if (dryRun) {
return {
idp, tenant: tenant ?? null, url,
status: "dry-run",
idpLabel,
message: `Would open ${idpLabel}'s password-reset page at ${url}.`,
};
}
await openUrl(url);
return {
idp, tenant: tenant ?? null, url,
status: "opened",
idpLabel,
message: `Opened ${idpLabel}'s password-reset page in your default browser.`,
};
}
// -- CLI smoke test -----------------------------------------------------------
if (false) {
run({ idp: "okta", tenant: "acme", dryRun: true })
.then((r) => console.log(JSON.stringify(r, null, 2)))
.catch((err: Error) => { console.error(err.message); process.exit(1); });
}