Skip to content

Commit a3240ba

Browse files
authored
Merge pull request #129 from kernel/rgarcia/managed-auth-mcp-app
Add secure Managed Auth MCP App
2 parents af256a6 + f3f70ed commit a3240ba

45 files changed

Lines changed: 5357 additions & 85 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,17 @@ NEXT_PUBLIC_CLERK_DOMAIN=<x>.clerk.accounts.dev
1313
# API Configuration - Only needed to set the Kernel SDK base url
1414
API_BASE_URL=<x>
1515

16+
# Public origin of this MCP server. The Managed Auth MCP App connects only to
17+
# the narrowly scoped same-origin relay. Local development: http://localhost:3002
18+
MANAGED_AUTH_APP_ORIGIN=https://mcp.onkernel.com
19+
1620
# Mintlify API Configuration - Only needed for the search_docs tool call
1721
MINTLIFY_ASSISTANT_API_TOKEN=mint_dsc_<x>
1822
MINTLIFY_DOMAIN=<x>
1923

2024
# Optional MCP toolset gating. Comma-separated values.
2125
# Example: api_keys hides manage_api_keys so deployments can opt out of key management.
22-
# Supported: apps, api_keys, browser_pools, browsers, computer, docs, extensions, playwright, profiles, projects, proxies, replays, shell
26+
# Supported: apps, api_keys, auth_connections, browser_pools, browsers, computer, docs, extensions, playwright, profiles, projects, proxies, replays, shell
2327
# KERNEL_MCP_DISABLED_TOOLSETS=api_keys
2428

2529
# Redis Configuration

.github/workflows/ci.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,20 @@ jobs:
1111
steps:
1212
- uses: actions/checkout@v4
1313

14+
# Pin Bun: the managed-auth App bundle check is byte-exact and Bun's
15+
# minifier output can change between releases. Regenerate the bundle
16+
# with this exact version when bumping it.
1417
- uses: oven-sh/setup-bun@v2
18+
with:
19+
bun-version: "1.3.3"
1520

1621
- run: bun install --frozen-lockfile
1722

23+
- name: Check managed-auth App bundle
24+
run: bun run check:managed-auth-app
25+
1826
- name: Type check
19-
run: bunx tsc --noEmit
27+
run: bunx tsc --noEmit --incremental false
2028

2129
- name: Test
2230
run: bun test

.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
src/lib/mcp/apps/generated/managed-auth-app.ts

README.md

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,11 @@ Many other MCP-capable tools accept:
255255

256256
Configure these values wherever the tool expects MCP server settings.
257257

258-
## Tools (16 total)
258+
## Tools (17 model-facing, plus 1 app-only helper)
259259

260-
Each Kernel feature has a single `manage_*` tool with an `action` parameter, keeping the tool set small and consistent. Five standalone tools handle high-frequency workflows.
260+
Each Kernel feature has a single `manage_*` tool with an `action` parameter, keeping the tool set small and consistent. Standalone tools handle high-frequency and interactive workflows.
261+
262+
One additional Managed Auth helper (`begin_auth_login`) is marked app-only (`_meta.ui.visibility: ["app"]`); it refuses to execute on hosts that do not declare MCP Apps support. The App forwards the server-issued signed flow checkpoint to the shared `manage_auth_connections` `wait` action, so flow identity and terminal-state decisions stay on the server.
261263

262264
Self-hosted deployments can hide sensitive tool families by setting `KERNEL_MCP_DISABLED_TOOLSETS` to a comma-separated list. For example, `KERNEL_MCP_DISABLED_TOOLSETS=api_keys` prevents `manage_api_keys` from being registered.
263265

@@ -272,7 +274,7 @@ Self-hosted deployments can hide sensitive tool families by setting `KERNEL_MCP_
272274
- `manage_replays` - Start, stop, and list MP4 video replay recordings for a browser session. Session-scoped: start once, run your automation, then stop. Requires a paid Kernel plan.
273275
- `manage_extensions` - List and delete uploaded browser extensions.
274276
- `manage_apps` - List/search apps, invoke actions, get/list/delete deployments, and get invocation results.
275-
- `manage_auth_connections` - Create, list, get, delete managed auth connections; start login flows (returns a hosted URL and live view); submit MFA codes or SSO selections.
277+
- `manage_auth_connections` - Create, list, get, delete, login, submit, and wait for managed-auth connections in every client. Use domain-filtered `list` for discovery. App-capable clients additionally receive `open_auth_login`; the programmatic actions remain available there too.
276278
- `manage_credentials` - Create, list, get, update, and delete stored credentials; fetch a current TOTP code for credentials with a configured totp_secret.
277279
- `manage_credential_providers` - Create, list, get, update, and delete external credential providers (e.g. 1Password); list available items and test the provider connection.
278280

@@ -283,6 +285,7 @@ Self-hosted deployments can hide sensitive tool families by setting `KERNEL_MCP_
283285
- `execute_playwright_code` - Execute Playwright/TypeScript code against an existing browser session. Does not create or delete browsers - use `manage_browsers` for session lifecycle.
284286
- `exec_command` - Run shell commands inside a browser VM. Returns decoded stdout/stderr.
285287
- `search_docs` - Search Kernel platform documentation and guides.
288+
- `open_auth_login` - Open a secure interactive Managed Auth MCP App after user consent. Registered only for clients that declare MCP Apps support; credentials and MFA never enter MCP/model traffic.
286289

287290
## Resources
288291

@@ -327,6 +330,19 @@ Assistant: I'll create a browser session, then execute Playwright code against i
327330
Returns: { success: true, result: "Example Domain" }
328331
```
329332

333+
### Use managed authentication for a protected site
334+
335+
1. Call `manage_auth_connections` with `action: "list"` and the exact `domain_filter`.
336+
2. Fetch all pages. Reuse an authenticated connection; ask only when multiple relevant accounts match.
337+
3. A direct request to log in is consent. If authentication is discovered incidentally, ask before opening the App.
338+
4. For a new connection, choose a concise service-derived profile name unless the user supplied one; do not ask solely for a profile name.
339+
5. Call `open_auth_login`, then immediately follow its `next_action` and repeat the read-only wait while it reports `pending`.
340+
6. The user enters credentials/MFA only in the secure App. Once the wait reports `authenticated`, resume the original task with the verified `profile_name`.
341+
342+
Example: “Log me into my Hacker News account and update my profile to add a random emoji at the bottom.” The agent should discover `news.ycombinator.com`, open the App when needed, wait for authentication, then continue the profile edit without asking for credentials or a profile name in chat.
343+
344+
The secure App defaults `record_session` and `browser_telemetry.enabled` to `true`, recording replay video plus the operational telemetry categories (`control`, `connection`, `system`, and `captcha`) for managed-auth browser sessions. Callers can explicitly disable either setting. The programmatic `manage_auth_connections` create/login actions preserve the API’s opt-in and inheritance behavior when these parameters are omitted.
345+
330346
### Set up browser profiles for authentication
331347

332348
```

bun.lock

Lines changed: 32 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
},
2323
"scripts": {
2424
"dev": "next dev -p 3002",
25-
"build": "next build",
25+
"build:managed-auth-app": "bun scripts/build-managed-auth-app.mjs",
26+
"check:managed-auth-app": "bun scripts/build-managed-auth-app.mjs --check",
27+
"build": "bun run check:managed-auth-app && next build",
2628
"start": "next start -p 3002",
2729
"lint": "next lint",
2830
"test": "bun test",
@@ -35,7 +37,8 @@
3537
"@clerk/themes": "^2.4.19",
3638
"@mcp-ui/server": "^5.10.0",
3739
"@modelcontextprotocol/sdk": "1.26.0",
38-
"@onkernel/sdk": "^0.78.0",
40+
"@onkernel/managed-auth-react": "0.4.1",
41+
"@onkernel/sdk": "^0.85.0",
3942
"@posthog/mcp": "0.10.1",
4043
"@types/jsonwebtoken": "^9.0.10",
4144
"@types/redis": "^4.0.11",
@@ -62,6 +65,7 @@
6265
"@types/node": "^20",
6366
"@types/react": "^19",
6467
"@types/react-dom": "^19",
68+
"bun": "1.3.3",
6569
"bun-types": "^1.3.14",
6670
"postcss": "^8.5.6",
6771
"tailwindcss": "^4.1.11"

scripts/build-managed-auth-app.mjs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Builds the managed-auth MCP App into a single self-contained HTML bundle.
2+
// The --check mode is byte-exact, and Bun's minifier output can change between
3+
// releases, so the bundle is only reproducible with the exact Bun dependency
4+
// pinned in package.json and matched by CI (currently 1.3.3). Production build
5+
// checks the committed artifact; regeneration remains an explicit command:
6+
// bun run build:managed-auth-app
7+
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
8+
import { tmpdir } from "node:os";
9+
import { join, resolve } from "node:path";
10+
11+
const EXPECTED_BUN_VERSION = "1.3.3";
12+
if (Bun.version !== EXPECTED_BUN_VERSION) {
13+
throw new Error(
14+
`Managed-auth App bundling requires Bun ${EXPECTED_BUN_VERSION}; got ${Bun.version}. Run bun install and use the project-local bun binary.`,
15+
);
16+
}
17+
18+
const root = resolve(import.meta.dirname, "..");
19+
const entrypoint = join(root, "src/lib/mcp/apps/managed-auth-entry.tsx");
20+
const generatedPath = join(
21+
root,
22+
"src/lib/mcp/apps/generated/managed-auth-app.ts",
23+
);
24+
const check = process.argv.includes("--check");
25+
const temp = await mkdtemp(join(tmpdir(), "kernel-managed-auth-app-"));
26+
27+
try {
28+
const build = await Bun.build({
29+
entrypoints: [entrypoint],
30+
outdir: temp,
31+
target: "browser",
32+
format: "esm",
33+
minify: true,
34+
splitting: false,
35+
sourcemap: "none",
36+
define: {
37+
"process.env.NODE_ENV": JSON.stringify("production"),
38+
},
39+
});
40+
41+
if (!build.success) {
42+
for (const log of build.logs) console.error(log);
43+
process.exitCode = 1;
44+
} else {
45+
let javascript = "";
46+
let css = "";
47+
for (const output of build.outputs) {
48+
if (output.path.endsWith(".js")) javascript += await output.text();
49+
if (output.path.endsWith(".css")) css += await output.text();
50+
}
51+
if (!javascript)
52+
throw new Error("Bun did not emit managed-auth JavaScript");
53+
54+
const escapeScript = (value) => value.replaceAll("</script", "<\\/script");
55+
const escapeStyle = (value) => value.replaceAll("</style", "<\\/style");
56+
const html = `<!DOCTYPE html>
57+
<html lang="en">
58+
<head>
59+
<meta charset="utf-8" />
60+
<meta name="viewport" content="width=device-width, initial-scale=1" />
61+
<title>Kernel Managed Authentication</title>
62+
<style>${escapeStyle(css)}
63+
html,body,#root{margin:0;min-height:100%}
64+
html,body{background:transparent}
65+
.kernel-app-status,.kernel-app-fallback{font-family:ui-sans-serif,system-ui,sans-serif;padding:16px;text-align:center}
66+
.kernel-app-actions{display:flex;flex-direction:column;align-items:center;gap:8px;padding:0 16px 16px}
67+
.kernel-app-button{appearance:none;border:0;border-radius:8px;background:#81b300;color:#fff;cursor:pointer;font:600 14px ui-sans-serif,system-ui,sans-serif;padding:10px 16px}
68+
.kernel-app-button:hover{background:#709c00}
69+
</style>
70+
</head>
71+
<body><div id="root"></div>
72+
<script type="module">${escapeScript(javascript)}</script>
73+
</body>
74+
</html>`;
75+
const generated = `// Generated by scripts/build-managed-auth-app.mjs. Do not edit.\nexport const MANAGED_AUTH_APP_HTML = ${JSON.stringify(html)};\n`;
76+
77+
if (check) {
78+
let current = "";
79+
try {
80+
current = await readFile(generatedPath, "utf8");
81+
} catch {
82+
// Report the same actionable stale-bundle error below.
83+
}
84+
if (current !== generated) {
85+
console.error(
86+
"Managed-auth App bundle is stale. Run: bun run build:managed-auth-app",
87+
);
88+
process.exitCode = 1;
89+
}
90+
} else {
91+
await mkdir(resolve(generatedPath, ".."), { recursive: true });
92+
await writeFile(generatedPath, generated);
93+
console.log(`Generated ${generatedPath}`);
94+
}
95+
}
96+
} finally {
97+
await rm(temp, { recursive: true, force: true });
98+
}

0 commit comments

Comments
 (0)