@agent-native/skills 0.2.10 → 0.2.12
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.
- package/dist/connect.d.ts +5 -6
- package/dist/connect.d.ts.map +1 -1
- package/dist/connect.js +35 -8
- package/dist/connect.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +109 -25
- package/dist/index.js.map +1 -1
- package/dist/mcp-config-writers.d.ts +32 -21
- package/dist/mcp-config-writers.d.ts.map +1 -1
- package/dist/mcp-config-writers.js +166 -23
- package/dist/mcp-config-writers.js.map +1 -1
- package/package.json +3 -3
package/dist/connect.d.ts
CHANGED
|
@@ -9,14 +9,13 @@
|
|
|
9
9
|
* SAME device-code/OAuth protocol as core.
|
|
10
10
|
*
|
|
11
11
|
* Two client families, exactly as in core:
|
|
12
|
-
* - OAuth-capable (
|
|
13
|
-
* entry (no bearer headers). The user authenticates
|
|
14
|
-
* remote MCP OAuth
|
|
12
|
+
* - OAuth-capable (Claude Code, Cursor, OpenCode, GitHub Copilot / VS Code):
|
|
13
|
+
* get a URL-only HTTP MCP entry (no bearer headers). The user authenticates
|
|
14
|
+
* in-host via standard remote MCP OAuth.
|
|
15
15
|
* - Device-code (codex, cowork): run the browser device-code flow against the
|
|
16
16
|
* descriptor's hosted URL, then write the entry WITH the minted bearer token
|
|
17
|
-
* + headers. Non-interactive (or no TTY) skips
|
|
18
|
-
*
|
|
19
|
-
* fallback command.
|
|
17
|
+
* + headers. Non-interactive (or no TTY) skips writing device-code configs,
|
|
18
|
+
* surfacing the exact `agent-native connect <url>` fallback command.
|
|
20
19
|
*
|
|
21
20
|
* Server contract (identical paths + JSON field names to core):
|
|
22
21
|
* POST <hostedUrl>/_agent-native/mcp/connect/device/start (no auth)
|
package/dist/connect.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAAE,QAAQ,EAA2B,MAAM,yBAAyB,CAAC;AA6B5E;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,aAAa,CAAC;IAC1B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,IAAI,CAAC,EAAE;QACL,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;QACzB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KACvC,CAAC;CACH;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE;QAAE,MAAM,EAAE,QAAQ,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9C,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AA0CD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAEhE;AAmWD;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,iBAAiB,CAAC,CAyH5B"}
|
package/dist/connect.js
CHANGED
|
@@ -9,14 +9,13 @@
|
|
|
9
9
|
* SAME device-code/OAuth protocol as core.
|
|
10
10
|
*
|
|
11
11
|
* Two client families, exactly as in core:
|
|
12
|
-
* - OAuth-capable (
|
|
13
|
-
* entry (no bearer headers). The user authenticates
|
|
14
|
-
* remote MCP OAuth
|
|
12
|
+
* - OAuth-capable (Claude Code, Cursor, OpenCode, GitHub Copilot / VS Code):
|
|
13
|
+
* get a URL-only HTTP MCP entry (no bearer headers). The user authenticates
|
|
14
|
+
* in-host via standard remote MCP OAuth.
|
|
15
15
|
* - Device-code (codex, cowork): run the browser device-code flow against the
|
|
16
16
|
* descriptor's hosted URL, then write the entry WITH the minted bearer token
|
|
17
|
-
* + headers. Non-interactive (or no TTY) skips
|
|
18
|
-
*
|
|
19
|
-
* fallback command.
|
|
17
|
+
* + headers. Non-interactive (or no TTY) skips writing device-code configs,
|
|
18
|
+
* surfacing the exact `agent-native connect <url>` fallback command.
|
|
20
19
|
*
|
|
21
20
|
* Server contract (identical paths + JSON field names to core):
|
|
22
21
|
* POST <hostedUrl>/_agent-native/mcp/connect/device/start (no auth)
|
|
@@ -40,7 +39,19 @@ const MCP_PATH = "/_agent-native/mcp";
|
|
|
40
39
|
const REMOTE_MCP_OAUTH_CLIENTS = new Set([
|
|
41
40
|
"claude-code",
|
|
42
41
|
"claude-code-cli",
|
|
42
|
+
"cursor",
|
|
43
|
+
"opencode",
|
|
44
|
+
"github-copilot",
|
|
43
45
|
]);
|
|
46
|
+
const CLIENT_LABELS = {
|
|
47
|
+
"claude-code": "Claude Code",
|
|
48
|
+
"claude-code-cli": "Claude Code CLI",
|
|
49
|
+
codex: "Codex",
|
|
50
|
+
cowork: "Claude Cowork",
|
|
51
|
+
cursor: "Cursor",
|
|
52
|
+
opencode: "OpenCode",
|
|
53
|
+
"github-copilot": "GitHub Copilot / VS Code",
|
|
54
|
+
};
|
|
44
55
|
// ---------------------------------------------------------------------------
|
|
45
56
|
// Helpers
|
|
46
57
|
// ---------------------------------------------------------------------------
|
|
@@ -153,6 +164,22 @@ function manualConnectGuidance(baseUrl, clients, scope) {
|
|
|
153
164
|
return (`To authenticate ${describeClients(clients)}, run: ` +
|
|
154
165
|
fallbackConnectCommand(baseUrl, clients, scope));
|
|
155
166
|
}
|
|
167
|
+
function oauthNextStepsForClients(clients, serverName) {
|
|
168
|
+
const lines = [];
|
|
169
|
+
if (clients.includes("claude-code") || clients.includes("claude-code-cli")) {
|
|
170
|
+
lines.push("Claude Code: restart Claude Code, run /mcp, and choose Authenticate.");
|
|
171
|
+
}
|
|
172
|
+
if (clients.includes("cursor")) {
|
|
173
|
+
lines.push("Cursor: restart or reload Cursor, then authenticate the MCP server from Cursor MCP settings if prompted.");
|
|
174
|
+
}
|
|
175
|
+
if (clients.includes("opencode")) {
|
|
176
|
+
lines.push(`OpenCode: run opencode mcp auth ${serverName ?? "<server-name>"} or authenticate on first use.`);
|
|
177
|
+
}
|
|
178
|
+
if (clients.includes("github-copilot")) {
|
|
179
|
+
lines.push("GitHub Copilot / VS Code: reload VS Code, open the MCP config, and use the Auth action above the server if prompted.");
|
|
180
|
+
}
|
|
181
|
+
return lines;
|
|
182
|
+
}
|
|
156
183
|
/** Write a token+headers entry for every config key, collecting files. */
|
|
157
184
|
function writeAuthedEntries(clients, keys, mcpUrl, token, headers, scope, baseDir, written, errors) {
|
|
158
185
|
for (const client of clients) {
|
|
@@ -308,7 +335,7 @@ export async function registerMcpServer(opts) {
|
|
|
308
335
|
// OAuth clients always get URL-only entries (in-host OAuth, no local bearer).
|
|
309
336
|
if (oauthClients.length > 0) {
|
|
310
337
|
writeUrlOnlyEntries(oauthClients, keys, mcpUrl, scope, baseDir, written, errors);
|
|
311
|
-
guidance.push(`${describeClients(oauthClients)}: wrote URL-only MCP config (no bearer headers).`,
|
|
338
|
+
guidance.push(`${describeClients(oauthClients)}: wrote URL-only MCP config (no bearer headers).`, ...oauthNextStepsForClients(oauthClients, descriptor.serverName));
|
|
312
339
|
}
|
|
313
340
|
// Device-code clients.
|
|
314
341
|
if (deviceClients.length > 0) {
|
|
@@ -348,7 +375,7 @@ export async function registerMcpServer(opts) {
|
|
|
348
375
|
return { written, authenticated, guidance: [...guidance, ...errors] };
|
|
349
376
|
}
|
|
350
377
|
function describeClients(clients) {
|
|
351
|
-
return clients.join(", ");
|
|
378
|
+
return clients.map((client) => CLIENT_LABELS[client]).join(", ");
|
|
352
379
|
}
|
|
353
380
|
/** Derive the `app` slug the device-start endpoint expects. */
|
|
354
381
|
function appSlugFor(descriptor, baseUrl) {
|
package/dist/connect.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect.js","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * MCP-server registration + authentication for `@agent-native/skills`.\n *\n * This is a dependency-free port of the MCP-config-writing + device-code/OAuth\n * flow that lives in `@agent-native/core`'s `cli/connect.ts`. The skills package\n * ships standalone (no `@agent-native/core` dependency), so this module\n * re-implements just the registration surface against the shared on-disk\n * writers in `./mcp-config-writers.js`. It writes the SAME config and speaks the\n * SAME device-code/OAuth protocol as core.\n *\n * Two client families, exactly as in core:\n * - OAuth-capable (claude-code, claude-code-cli): get a URL-only HTTP MCP\n * entry (no bearer headers). The user authenticates in-host via standard\n * remote MCP OAuth: restart Claude Code, run /mcp, choose Authenticate.\n * - Device-code (codex, cowork): run the browser device-code flow against the\n * descriptor's hosted URL, then write the entry WITH the minted bearer token\n * + headers. Non-interactive (or no TTY) skips the flow and writes a\n * URL-only entry, surfacing the exact `agent-native connect <url> --token`\n * fallback command.\n *\n * Server contract (identical paths + JSON field names to core):\n * POST <hostedUrl>/_agent-native/mcp/connect/device/start (no auth)\n * body { client?, app? }\n * → { device_code, user_code, verification_uri,\n * verification_uri_complete, interval, expires_in }\n * POST <hostedUrl>/_agent-native/mcp/connect/device/poll (no auth)\n * body { device_code }\n * → { status: \"pending\" }\n * | { status: \"approved\", token, mcpUrl, serverName, mcpServerEntry }\n * | { status: \"expired\" } | { status: \"consumed\" }\n * | { status: \"error\" | \"not_found\", message? }\n *\n * Node-only. Node built-ins + global fetch only; no npm deps.\n */\n\nimport { ClientId, writeHttpEntryForClient } from \"./mcp-config-writers.js\";\n\nconst DEVICE_START_PATH = \"/_agent-native/mcp/connect/device/start\";\nconst DEVICE_POLL_PATH = \"/_agent-native/mcp/connect/device/poll\";\nconst MCP_PATH = \"/_agent-native/mcp\";\n\n/** OAuth-capable clients (in-host remote MCP OAuth, never a local bearer). */\nconst REMOTE_MCP_OAUTH_CLIENTS = new Set<ClientId>([\n \"claude-code\",\n \"claude-code-cli\",\n]);\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Describes one MCP server to register. `serverName` is the canonical config\n * key; `aliases` are additional config keys that point at the same MCP URL\n * (e.g. `plan` + `agent-native-plans`). `hostedUrl` is the deployed app origin\n * the device-code flow authenticates against; `mcpUrl` is the resolved MCP\n * endpoint written into the config (defaults to `<hostedUrl>/_agent-native/mcp`\n * when only `hostedUrl` is supplied).\n */\nexport interface McpDescriptor {\n serverName: string;\n mcpUrl: string;\n aliases?: string[];\n authMode?: \"oauth\" | \"device\" | \"none\";\n hostedUrl?: string;\n}\n\nexport interface RegisterMcpOptions {\n descriptor: McpDescriptor;\n clients: ClientId[];\n scope: \"user\" | \"project\";\n baseDir: string;\n interactive: boolean;\n log?: (m: string) => void;\n deviceFlowTimeoutMs?: number;\n deps?: {\n fetchImpl?: typeof fetch;\n now?: () => number;\n sleep?: (ms: number) => Promise<void>;\n };\n}\n\nexport interface RegisterMcpResult {\n written: { client: ClientId; file: string }[];\n authenticated: boolean;\n guidance: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Device-code protocol shapes (field names match core EXACTLY).\n// ---------------------------------------------------------------------------\n\ninterface DeviceStartResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n verification_uri_complete: string;\n interval?: number;\n expires_in?: number;\n}\n\ninterface DevicePollResponse {\n status:\n | \"pending\"\n | \"approved\"\n | \"expired\"\n | \"consumed\"\n | \"error\"\n | \"not_found\";\n token?: string;\n mcpUrl?: string;\n serverName?: string;\n mcpServerEntry?: Record<string, unknown>;\n message?: string;\n error?: string;\n}\n\ninterface DeviceGrant {\n token?: string;\n mcpUrl: string;\n serverName: string;\n headers?: Record<string, string>;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nexport function supportsRemoteMcpOAuth(client: ClientId): boolean {\n return REMOTE_MCP_OAUTH_CLIENTS.has(client);\n}\n\nconst DEFAULT_DEVICE_FLOW_TIMEOUT_MS = 90_000;\n\nfunction realSleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Trailing-slash-stripped origin+path for a hosted app URL. */\nfunction stripTrailingSlash(url: string): string {\n return url.replace(/\\/+$/, \"\");\n}\n\n/**\n * Resolve the MCP endpoint URL for a descriptor. Prefers an explicit `mcpUrl`,\n * otherwise derives `<hostedUrl>/_agent-native/mcp` (mirrors core's\n * `mcpUrlForBaseUrl`). Returns `undefined` when neither is usable.\n */\nfunction resolveMcpUrl(descriptor: McpDescriptor): string | undefined {\n if (descriptor.mcpUrl && descriptor.mcpUrl.trim()) {\n return descriptor.mcpUrl.trim();\n }\n if (descriptor.hostedUrl && descriptor.hostedUrl.trim()) {\n const base = stripTrailingSlash(descriptor.hostedUrl.trim());\n return `${base}${MCP_PATH}`;\n }\n return undefined;\n}\n\n/** Base (origin) URL of the deployed app the device flow runs against. */\nfunction resolveBaseUrl(descriptor: McpDescriptor): string | undefined {\n if (descriptor.hostedUrl && descriptor.hostedUrl.trim()) {\n return stripTrailingSlash(descriptor.hostedUrl.trim());\n }\n // Fall back to stripping the MCP path off an explicit mcpUrl.\n if (descriptor.mcpUrl && descriptor.mcpUrl.trim()) {\n const trimmed = stripTrailingSlash(descriptor.mcpUrl.trim());\n if (trimmed.endsWith(MCP_PATH)) {\n return trimmed.slice(0, -MCP_PATH.length);\n }\n }\n return undefined;\n}\n\n/** All config keys to register: the canonical name plus any aliases (deduped). */\nfunction configKeys(descriptor: McpDescriptor): string[] {\n const keys: string[] = [descriptor.serverName];\n for (const alias of descriptor.aliases ?? []) {\n if (alias && alias !== descriptor.serverName && !keys.includes(alias)) {\n keys.push(alias);\n }\n }\n return keys;\n}\n\nfunction responseMessage(json: any, fallback: string): string {\n const message =\n typeof json?.message === \"string\"\n ? json.message\n : typeof json?.error === \"string\"\n ? json.error\n : \"\";\n return message.trim() || fallback;\n}\n\nasync function postJson(\n fetchImpl: typeof fetch,\n url: string,\n body: unknown,\n): Promise<{ status: number; json: any }> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 30_000);\n try {\n const response = await fetchImpl(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body ?? {}),\n signal: controller.signal,\n });\n let json: any = null;\n try {\n json = await response.json();\n } catch {\n json = null;\n }\n return { status: response.status, json };\n } finally {\n clearTimeout(timeout);\n }\n}\n\n/**\n * The exact no-browser fallback command core prints. Surfaced as guidance when\n * a device-code client is asked to register non-interactively.\n */\nfunction clientArgForClients(clients: ClientId[]): string {\n return clients.length === 1 ? clients[0] : clients.join(\",\");\n}\n\nfunction fallbackConnectCommand(\n baseUrl: string,\n clients: ClientId[],\n scope: string,\n): string {\n return `npx @agent-native/core@latest connect ${baseUrl} --client ${clientArgForClients(\n clients,\n )} --scope ${scope}`;\n}\n\n/** Write a URL-only entry (no bearer) for every config key, collecting files. */\nfunction writeUrlOnlyEntries(\n clients: ClientId[],\n keys: string[],\n mcpUrl: string,\n scope: string,\n baseDir: string,\n written: { client: ClientId; file: string }[],\n errors: string[],\n): void {\n for (const client of clients) {\n for (const key of keys) {\n try {\n const file = writeHttpEntryForClient(\n client,\n key,\n mcpUrl,\n undefined,\n baseDir,\n scope,\n undefined,\n );\n written.push({ client, file });\n } catch (err: any) {\n errors.push(\n `Could not write ${key} for ${client}: ${err?.message ?? err}`,\n );\n }\n }\n }\n}\n\nfunction manualConnectGuidance(\n baseUrl: string,\n clients: ClientId[],\n scope: string,\n): string {\n return (\n `To authenticate ${describeClients(clients)}, run: ` +\n fallbackConnectCommand(baseUrl, clients, scope)\n );\n}\n\n/** Write a token+headers entry for every config key, collecting files. */\nfunction writeAuthedEntries(\n clients: ClientId[],\n keys: string[],\n mcpUrl: string,\n token: string | undefined,\n headers: Record<string, string> | undefined,\n scope: string,\n baseDir: string,\n written: { client: ClientId; file: string }[],\n errors: string[],\n): void {\n for (const client of clients) {\n for (const key of keys) {\n try {\n const file = writeHttpEntryForClient(\n client,\n key,\n mcpUrl,\n token,\n baseDir,\n scope,\n headers,\n );\n written.push({ client, file });\n } catch (err: any) {\n errors.push(\n `Could not write ${key} for ${client}: ${err?.message ?? err}`,\n );\n }\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Device-code flow (dependency-free port of core's runDeviceFlow)\n// ---------------------------------------------------------------------------\n\n/**\n * Run the device-code flow against `baseUrl` and return the approved grant, or\n * `null` (after logging a clear message) on expiry/consumed/error/timeout. Same\n * state machine and field handling as core; the spinner/browser-open are\n * dropped since this runs inside a non-interactive installer context.\n */\nasync function runDeviceFlow(\n baseUrl: string,\n appSlug: string,\n clientArg: string,\n log: (m: string) => void,\n timeoutMs: number | undefined,\n deps: RegisterMcpOptions[\"deps\"] = {},\n): Promise<DeviceGrant | null> {\n const fetchImpl = deps.fetchImpl ?? fetch;\n const sleep = deps.sleep ?? realSleep;\n const now = deps.now ?? (() => Date.now());\n\n let start: DeviceStartResponse;\n try {\n const { status, json } = await postJson(\n fetchImpl,\n `${baseUrl}${DEVICE_START_PATH}`,\n { client: clientArg, app: appSlug },\n );\n if (status < 200 || status >= 300 || !json?.device_code) {\n log(\n ` Could not start the connect flow on ${baseUrl} (HTTP ${status}). ` +\n `Is this an agent-native app, and is it deployed with the connect ` +\n `endpoint enabled?`,\n );\n return null;\n }\n start = json as DeviceStartResponse;\n } catch (err: any) {\n log(\n ` Could not reach ${baseUrl} (${err?.message ?? err}). ` +\n `Check the URL and your network.`,\n );\n return null;\n }\n\n const interval = Math.max(1, Number(start.interval) || 5);\n const expiresIn = Math.max(interval, Number(start.expires_in) || 600);\n const serverDeadlineMs = expiresIn * 1000;\n const installerDeadlineMs = Math.max(\n 0,\n timeoutMs ?? DEFAULT_DEVICE_FLOW_TIMEOUT_MS,\n );\n const waitMs = Math.min(serverDeadlineMs, installerDeadlineMs);\n const waitWasCapped = waitMs < serverDeadlineMs;\n const deadline = now() + waitMs;\n\n log(\"\");\n log(` Connecting to ${baseUrl}`);\n log(\"\");\n log(` Your code: ${start.user_code}`);\n log(` Open: ${start.verification_uri_complete}`);\n log(\"\");\n log(\" Approve in the browser to finish.\");\n\n while (now() < deadline) {\n let poll: DevicePollResponse;\n try {\n const { status, json } = await postJson(\n fetchImpl,\n `${baseUrl}${DEVICE_POLL_PATH}`,\n { device_code: start.device_code },\n );\n if (status < 200 || status >= 300) {\n log(\n ` Connect polling failed (HTTP ${status}): ` +\n responseMessage(json, \"server returned an error.\"),\n );\n return null;\n }\n poll = (json ?? { status: \"pending\" }) as DevicePollResponse;\n } catch {\n // Transient network error — keep polling until the deadline.\n poll = { status: \"pending\" };\n }\n\n if (poll.status === \"approved\") {\n const token = poll.token ?? \"\";\n const mcpUrl = poll.mcpUrl ?? `${baseUrl}${MCP_PATH}`;\n const serverName = poll.serverName ?? appSlug;\n const headers =\n poll.mcpServerEntry &&\n typeof poll.mcpServerEntry === \"object\" &&\n poll.mcpServerEntry.headers &&\n typeof poll.mcpServerEntry.headers === \"object\"\n ? (poll.mcpServerEntry.headers as Record<string, string>)\n : undefined;\n log(\" Approved.\");\n return { token: token || undefined, mcpUrl, serverName, headers };\n }\n if (poll.status === \"expired\") {\n log(\" The connect request expired before it was approved.\");\n log(\" Run the command again to retry.\");\n return null;\n }\n if (poll.status === \"consumed\") {\n log(\" This connect code was already used. Run the command again.\");\n return null;\n }\n if (poll.status === \"error\" || poll.status === \"not_found\") {\n log(\n ` Connect polling failed: ${responseMessage(\n poll,\n poll.status === \"not_found\"\n ? \"device code was not found.\"\n : \"server returned an error.\",\n )}`,\n );\n return null;\n }\n\n const remainingMs = deadline - now();\n if (remainingMs <= 0) break;\n await sleep(Math.min(interval * 1000, remainingMs));\n }\n\n if (waitWasCapped) {\n log(\n ` Stopped waiting after ${Math.round(waitMs / 1000)}s so installation can finish.`,\n );\n log(\" You can authenticate later with the command below.\");\n } else {\n log(\" Timed out waiting for approval. Run the command again to retry.\");\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Public entry point\n// ---------------------------------------------------------------------------\n\n/**\n * Register an MCP server (plus aliases) into the requested client configs,\n * authenticating device-code clients via the browser flow when interactive.\n *\n * Idempotent: re-running replaces the same named entries. Never throws on a\n * single client/key failing — failures are collected into `guidance`.\n */\nexport async function registerMcpServer(\n opts: RegisterMcpOptions,\n): Promise<RegisterMcpResult> {\n const { descriptor, scope, baseDir, interactive } = opts;\n const log = opts.log ?? (() => {});\n const deps = opts.deps ?? {};\n\n const written: { client: ClientId; file: string }[] = [];\n const guidance: string[] = [];\n const errors: string[] = [];\n let authenticated = false;\n\n const keys = configKeys(descriptor);\n const mcpUrl = resolveMcpUrl(descriptor);\n if (!mcpUrl) {\n return {\n written,\n authenticated,\n guidance: [\n `Cannot register \"${descriptor.serverName}\": no mcpUrl or hostedUrl supplied.`,\n ],\n };\n }\n\n const authMode = descriptor.authMode ?? \"device\";\n\n // authMode \"none\" (e.g. context-xray): URL-only for ALL clients, no auth.\n if (authMode === \"none\") {\n writeUrlOnlyEntries(\n opts.clients,\n keys,\n mcpUrl,\n scope,\n baseDir,\n written,\n errors,\n );\n return { written, authenticated, guidance: [...guidance, ...errors] };\n }\n\n // Split into OAuth-capable and device-code clients, exactly like core.\n const oauthClients = opts.clients.filter((c) => supportsRemoteMcpOAuth(c));\n const deviceClients = opts.clients.filter((c) => !supportsRemoteMcpOAuth(c));\n\n // OAuth clients always get URL-only entries (in-host OAuth, no local bearer).\n if (oauthClients.length > 0) {\n writeUrlOnlyEntries(\n oauthClients,\n keys,\n mcpUrl,\n scope,\n baseDir,\n written,\n errors,\n );\n guidance.push(\n `${describeClients(oauthClients)}: wrote URL-only MCP config (no bearer headers).`,\n \"Next: restart Claude Code, run /mcp, and choose Authenticate.\",\n );\n }\n\n // Device-code clients.\n if (deviceClients.length > 0) {\n const baseUrl = resolveBaseUrl(descriptor);\n\n // We only reach here for authMode \"oauth\"/\"device\" (authMode \"none\" returned\n // earlier). Run the flow only when interactive AND we have a hosted URL to\n // authenticate against. Device-code clients such as Codex cannot use a\n // URL-only hosted entry: writing one would overwrite a working bearer token\n // and leave new sessions unauthenticated. If auth cannot complete, keep the\n // existing config untouched and surface the explicit connect command.\n const canRunFlow = interactive && !!baseUrl;\n\n if (!canRunFlow) {\n if (baseUrl) {\n guidance.push(\n `${describeClients(deviceClients)}: skipped MCP config because this client needs a bearer token.`,\n manualConnectGuidance(baseUrl, deviceClients, scope),\n );\n } else {\n guidance.push(\n `${describeClients(deviceClients)}: skipped MCP config because no hosted URL was available for authentication.`,\n );\n }\n } else {\n const appSlug = appSlugFor(descriptor, baseUrl!);\n const clientArg = deviceClients.length === 1 ? deviceClients[0] : \"all\";\n const grant = await runDeviceFlow(\n baseUrl!,\n appSlug,\n clientArg,\n log,\n opts.deviceFlowTimeoutMs,\n deps,\n );\n\n if (grant && grant.token) {\n // Write authed entries; honour the server's resolved mcpUrl when given.\n const resolvedUrl = grant.mcpUrl || mcpUrl;\n writeAuthedEntries(\n deviceClients,\n keys,\n resolvedUrl,\n grant.token,\n grant.headers,\n scope,\n baseDir,\n written,\n errors,\n );\n authenticated = true;\n } else {\n // Flow failed (or approved with no token). Do not downgrade device-code\n // clients to a URL-only hosted entry; keep any existing bearer config.\n guidance.push(\n `${describeClients(deviceClients)}: authentication did not complete; existing MCP config was left unchanged.`,\n manualConnectGuidance(baseUrl!, deviceClients, scope),\n );\n }\n }\n }\n\n return { written, authenticated, guidance: [...guidance, ...errors] };\n}\n\nfunction describeClients(clients: ClientId[]): string {\n return clients.join(\", \");\n}\n\n/** Derive the `app` slug the device-start endpoint expects. */\nfunction appSlugFor(descriptor: McpDescriptor, baseUrl: string): string {\n // Prefer the descriptor's server name without the agent-native prefix.\n const name = descriptor.serverName.replace(/^agent-native-/, \"\");\n if (name) return name;\n try {\n const host = new URL(baseUrl).hostname;\n const first = host.split(\".\")[0];\n return first && first !== \"www\" ? first : \"app\";\n } catch {\n return \"app\";\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"connect.js","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * MCP-server registration + authentication for `@agent-native/skills`.\n *\n * This is a dependency-free port of the MCP-config-writing + device-code/OAuth\n * flow that lives in `@agent-native/core`'s `cli/connect.ts`. The skills package\n * ships standalone (no `@agent-native/core` dependency), so this module\n * re-implements just the registration surface against the shared on-disk\n * writers in `./mcp-config-writers.js`. It writes the SAME config and speaks the\n * SAME device-code/OAuth protocol as core.\n *\n * Two client families, exactly as in core:\n * - OAuth-capable (Claude Code, Cursor, OpenCode, GitHub Copilot / VS Code):\n * get a URL-only HTTP MCP entry (no bearer headers). The user authenticates\n * in-host via standard remote MCP OAuth.\n * - Device-code (codex, cowork): run the browser device-code flow against the\n * descriptor's hosted URL, then write the entry WITH the minted bearer token\n * + headers. Non-interactive (or no TTY) skips writing device-code configs,\n * surfacing the exact `agent-native connect <url>` fallback command.\n *\n * Server contract (identical paths + JSON field names to core):\n * POST <hostedUrl>/_agent-native/mcp/connect/device/start (no auth)\n * body { client?, app? }\n * → { device_code, user_code, verification_uri,\n * verification_uri_complete, interval, expires_in }\n * POST <hostedUrl>/_agent-native/mcp/connect/device/poll (no auth)\n * body { device_code }\n * → { status: \"pending\" }\n * | { status: \"approved\", token, mcpUrl, serverName, mcpServerEntry }\n * | { status: \"expired\" } | { status: \"consumed\" }\n * | { status: \"error\" | \"not_found\", message? }\n *\n * Node-only. Node built-ins + global fetch only; no npm deps.\n */\n\nimport { ClientId, writeHttpEntryForClient } from \"./mcp-config-writers.js\";\n\nconst DEVICE_START_PATH = \"/_agent-native/mcp/connect/device/start\";\nconst DEVICE_POLL_PATH = \"/_agent-native/mcp/connect/device/poll\";\nconst MCP_PATH = \"/_agent-native/mcp\";\n\n/** OAuth-capable clients (in-host remote MCP OAuth, never a local bearer). */\nconst REMOTE_MCP_OAUTH_CLIENTS = new Set<ClientId>([\n \"claude-code\",\n \"claude-code-cli\",\n \"cursor\",\n \"opencode\",\n \"github-copilot\",\n]);\n\nconst CLIENT_LABELS: Record<ClientId, string> = {\n \"claude-code\": \"Claude Code\",\n \"claude-code-cli\": \"Claude Code CLI\",\n codex: \"Codex\",\n cowork: \"Claude Cowork\",\n cursor: \"Cursor\",\n opencode: \"OpenCode\",\n \"github-copilot\": \"GitHub Copilot / VS Code\",\n};\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Describes one MCP server to register. `serverName` is the canonical config\n * key; `aliases` are additional config keys that point at the same MCP URL\n * (e.g. `plan` + `agent-native-plans`). `hostedUrl` is the deployed app origin\n * the device-code flow authenticates against; `mcpUrl` is the resolved MCP\n * endpoint written into the config (defaults to `<hostedUrl>/_agent-native/mcp`\n * when only `hostedUrl` is supplied).\n */\nexport interface McpDescriptor {\n serverName: string;\n mcpUrl: string;\n aliases?: string[];\n authMode?: \"oauth\" | \"device\" | \"none\";\n hostedUrl?: string;\n}\n\nexport interface RegisterMcpOptions {\n descriptor: McpDescriptor;\n clients: ClientId[];\n scope: \"user\" | \"project\";\n baseDir: string;\n interactive: boolean;\n log?: (m: string) => void;\n deviceFlowTimeoutMs?: number;\n deps?: {\n fetchImpl?: typeof fetch;\n now?: () => number;\n sleep?: (ms: number) => Promise<void>;\n };\n}\n\nexport interface RegisterMcpResult {\n written: { client: ClientId; file: string }[];\n authenticated: boolean;\n guidance: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Device-code protocol shapes (field names match core EXACTLY).\n// ---------------------------------------------------------------------------\n\ninterface DeviceStartResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n verification_uri_complete: string;\n interval?: number;\n expires_in?: number;\n}\n\ninterface DevicePollResponse {\n status:\n | \"pending\"\n | \"approved\"\n | \"expired\"\n | \"consumed\"\n | \"error\"\n | \"not_found\";\n token?: string;\n mcpUrl?: string;\n serverName?: string;\n mcpServerEntry?: Record<string, unknown>;\n message?: string;\n error?: string;\n}\n\ninterface DeviceGrant {\n token?: string;\n mcpUrl: string;\n serverName: string;\n headers?: Record<string, string>;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nexport function supportsRemoteMcpOAuth(client: ClientId): boolean {\n return REMOTE_MCP_OAUTH_CLIENTS.has(client);\n}\n\nconst DEFAULT_DEVICE_FLOW_TIMEOUT_MS = 90_000;\n\nfunction realSleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Trailing-slash-stripped origin+path for a hosted app URL. */\nfunction stripTrailingSlash(url: string): string {\n return url.replace(/\\/+$/, \"\");\n}\n\n/**\n * Resolve the MCP endpoint URL for a descriptor. Prefers an explicit `mcpUrl`,\n * otherwise derives `<hostedUrl>/_agent-native/mcp` (mirrors core's\n * `mcpUrlForBaseUrl`). Returns `undefined` when neither is usable.\n */\nfunction resolveMcpUrl(descriptor: McpDescriptor): string | undefined {\n if (descriptor.mcpUrl && descriptor.mcpUrl.trim()) {\n return descriptor.mcpUrl.trim();\n }\n if (descriptor.hostedUrl && descriptor.hostedUrl.trim()) {\n const base = stripTrailingSlash(descriptor.hostedUrl.trim());\n return `${base}${MCP_PATH}`;\n }\n return undefined;\n}\n\n/** Base (origin) URL of the deployed app the device flow runs against. */\nfunction resolveBaseUrl(descriptor: McpDescriptor): string | undefined {\n if (descriptor.hostedUrl && descriptor.hostedUrl.trim()) {\n return stripTrailingSlash(descriptor.hostedUrl.trim());\n }\n // Fall back to stripping the MCP path off an explicit mcpUrl.\n if (descriptor.mcpUrl && descriptor.mcpUrl.trim()) {\n const trimmed = stripTrailingSlash(descriptor.mcpUrl.trim());\n if (trimmed.endsWith(MCP_PATH)) {\n return trimmed.slice(0, -MCP_PATH.length);\n }\n }\n return undefined;\n}\n\n/** All config keys to register: the canonical name plus any aliases (deduped). */\nfunction configKeys(descriptor: McpDescriptor): string[] {\n const keys: string[] = [descriptor.serverName];\n for (const alias of descriptor.aliases ?? []) {\n if (alias && alias !== descriptor.serverName && !keys.includes(alias)) {\n keys.push(alias);\n }\n }\n return keys;\n}\n\nfunction responseMessage(json: any, fallback: string): string {\n const message =\n typeof json?.message === \"string\"\n ? json.message\n : typeof json?.error === \"string\"\n ? json.error\n : \"\";\n return message.trim() || fallback;\n}\n\nasync function postJson(\n fetchImpl: typeof fetch,\n url: string,\n body: unknown,\n): Promise<{ status: number; json: any }> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 30_000);\n try {\n const response = await fetchImpl(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body ?? {}),\n signal: controller.signal,\n });\n let json: any = null;\n try {\n json = await response.json();\n } catch {\n json = null;\n }\n return { status: response.status, json };\n } finally {\n clearTimeout(timeout);\n }\n}\n\n/**\n * The exact no-browser fallback command core prints. Surfaced as guidance when\n * a device-code client is asked to register non-interactively.\n */\nfunction clientArgForClients(clients: ClientId[]): string {\n return clients.length === 1 ? clients[0] : clients.join(\",\");\n}\n\nfunction fallbackConnectCommand(\n baseUrl: string,\n clients: ClientId[],\n scope: string,\n): string {\n return `npx @agent-native/core@latest connect ${baseUrl} --client ${clientArgForClients(\n clients,\n )} --scope ${scope}`;\n}\n\n/** Write a URL-only entry (no bearer) for every config key, collecting files. */\nfunction writeUrlOnlyEntries(\n clients: ClientId[],\n keys: string[],\n mcpUrl: string,\n scope: string,\n baseDir: string,\n written: { client: ClientId; file: string }[],\n errors: string[],\n): void {\n for (const client of clients) {\n for (const key of keys) {\n try {\n const file = writeHttpEntryForClient(\n client,\n key,\n mcpUrl,\n undefined,\n baseDir,\n scope,\n undefined,\n );\n written.push({ client, file });\n } catch (err: any) {\n errors.push(\n `Could not write ${key} for ${client}: ${err?.message ?? err}`,\n );\n }\n }\n }\n}\n\nfunction manualConnectGuidance(\n baseUrl: string,\n clients: ClientId[],\n scope: string,\n): string {\n return (\n `To authenticate ${describeClients(clients)}, run: ` +\n fallbackConnectCommand(baseUrl, clients, scope)\n );\n}\n\nfunction oauthNextStepsForClients(\n clients: ClientId[],\n serverName?: string,\n): string[] {\n const lines: string[] = [];\n if (clients.includes(\"claude-code\") || clients.includes(\"claude-code-cli\")) {\n lines.push(\n \"Claude Code: restart Claude Code, run /mcp, and choose Authenticate.\",\n );\n }\n if (clients.includes(\"cursor\")) {\n lines.push(\n \"Cursor: restart or reload Cursor, then authenticate the MCP server from Cursor MCP settings if prompted.\",\n );\n }\n if (clients.includes(\"opencode\")) {\n lines.push(\n `OpenCode: run opencode mcp auth ${serverName ?? \"<server-name>\"} or authenticate on first use.`,\n );\n }\n if (clients.includes(\"github-copilot\")) {\n lines.push(\n \"GitHub Copilot / VS Code: reload VS Code, open the MCP config, and use the Auth action above the server if prompted.\",\n );\n }\n return lines;\n}\n\n/** Write a token+headers entry for every config key, collecting files. */\nfunction writeAuthedEntries(\n clients: ClientId[],\n keys: string[],\n mcpUrl: string,\n token: string | undefined,\n headers: Record<string, string> | undefined,\n scope: string,\n baseDir: string,\n written: { client: ClientId; file: string }[],\n errors: string[],\n): void {\n for (const client of clients) {\n for (const key of keys) {\n try {\n const file = writeHttpEntryForClient(\n client,\n key,\n mcpUrl,\n token,\n baseDir,\n scope,\n headers,\n );\n written.push({ client, file });\n } catch (err: any) {\n errors.push(\n `Could not write ${key} for ${client}: ${err?.message ?? err}`,\n );\n }\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Device-code flow (dependency-free port of core's runDeviceFlow)\n// ---------------------------------------------------------------------------\n\n/**\n * Run the device-code flow against `baseUrl` and return the approved grant, or\n * `null` (after logging a clear message) on expiry/consumed/error/timeout. Same\n * state machine and field handling as core; the spinner/browser-open are\n * dropped since this runs inside a non-interactive installer context.\n */\nasync function runDeviceFlow(\n baseUrl: string,\n appSlug: string,\n clientArg: string,\n log: (m: string) => void,\n timeoutMs: number | undefined,\n deps: RegisterMcpOptions[\"deps\"] = {},\n): Promise<DeviceGrant | null> {\n const fetchImpl = deps.fetchImpl ?? fetch;\n const sleep = deps.sleep ?? realSleep;\n const now = deps.now ?? (() => Date.now());\n\n let start: DeviceStartResponse;\n try {\n const { status, json } = await postJson(\n fetchImpl,\n `${baseUrl}${DEVICE_START_PATH}`,\n { client: clientArg, app: appSlug },\n );\n if (status < 200 || status >= 300 || !json?.device_code) {\n log(\n ` Could not start the connect flow on ${baseUrl} (HTTP ${status}). ` +\n `Is this an agent-native app, and is it deployed with the connect ` +\n `endpoint enabled?`,\n );\n return null;\n }\n start = json as DeviceStartResponse;\n } catch (err: any) {\n log(\n ` Could not reach ${baseUrl} (${err?.message ?? err}). ` +\n `Check the URL and your network.`,\n );\n return null;\n }\n\n const interval = Math.max(1, Number(start.interval) || 5);\n const expiresIn = Math.max(interval, Number(start.expires_in) || 600);\n const serverDeadlineMs = expiresIn * 1000;\n const installerDeadlineMs = Math.max(\n 0,\n timeoutMs ?? DEFAULT_DEVICE_FLOW_TIMEOUT_MS,\n );\n const waitMs = Math.min(serverDeadlineMs, installerDeadlineMs);\n const waitWasCapped = waitMs < serverDeadlineMs;\n const deadline = now() + waitMs;\n\n log(\"\");\n log(` Connecting to ${baseUrl}`);\n log(\"\");\n log(` Your code: ${start.user_code}`);\n log(` Open: ${start.verification_uri_complete}`);\n log(\"\");\n log(\" Approve in the browser to finish.\");\n\n while (now() < deadline) {\n let poll: DevicePollResponse;\n try {\n const { status, json } = await postJson(\n fetchImpl,\n `${baseUrl}${DEVICE_POLL_PATH}`,\n { device_code: start.device_code },\n );\n if (status < 200 || status >= 300) {\n log(\n ` Connect polling failed (HTTP ${status}): ` +\n responseMessage(json, \"server returned an error.\"),\n );\n return null;\n }\n poll = (json ?? { status: \"pending\" }) as DevicePollResponse;\n } catch {\n // Transient network error — keep polling until the deadline.\n poll = { status: \"pending\" };\n }\n\n if (poll.status === \"approved\") {\n const token = poll.token ?? \"\";\n const mcpUrl = poll.mcpUrl ?? `${baseUrl}${MCP_PATH}`;\n const serverName = poll.serverName ?? appSlug;\n const headers =\n poll.mcpServerEntry &&\n typeof poll.mcpServerEntry === \"object\" &&\n poll.mcpServerEntry.headers &&\n typeof poll.mcpServerEntry.headers === \"object\"\n ? (poll.mcpServerEntry.headers as Record<string, string>)\n : undefined;\n log(\" Approved.\");\n return { token: token || undefined, mcpUrl, serverName, headers };\n }\n if (poll.status === \"expired\") {\n log(\" The connect request expired before it was approved.\");\n log(\" Run the command again to retry.\");\n return null;\n }\n if (poll.status === \"consumed\") {\n log(\" This connect code was already used. Run the command again.\");\n return null;\n }\n if (poll.status === \"error\" || poll.status === \"not_found\") {\n log(\n ` Connect polling failed: ${responseMessage(\n poll,\n poll.status === \"not_found\"\n ? \"device code was not found.\"\n : \"server returned an error.\",\n )}`,\n );\n return null;\n }\n\n const remainingMs = deadline - now();\n if (remainingMs <= 0) break;\n await sleep(Math.min(interval * 1000, remainingMs));\n }\n\n if (waitWasCapped) {\n log(\n ` Stopped waiting after ${Math.round(waitMs / 1000)}s so installation can finish.`,\n );\n log(\" You can authenticate later with the command below.\");\n } else {\n log(\" Timed out waiting for approval. Run the command again to retry.\");\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Public entry point\n// ---------------------------------------------------------------------------\n\n/**\n * Register an MCP server (plus aliases) into the requested client configs,\n * authenticating device-code clients via the browser flow when interactive.\n *\n * Idempotent: re-running replaces the same named entries. Never throws on a\n * single client/key failing — failures are collected into `guidance`.\n */\nexport async function registerMcpServer(\n opts: RegisterMcpOptions,\n): Promise<RegisterMcpResult> {\n const { descriptor, scope, baseDir, interactive } = opts;\n const log = opts.log ?? (() => {});\n const deps = opts.deps ?? {};\n\n const written: { client: ClientId; file: string }[] = [];\n const guidance: string[] = [];\n const errors: string[] = [];\n let authenticated = false;\n\n const keys = configKeys(descriptor);\n const mcpUrl = resolveMcpUrl(descriptor);\n if (!mcpUrl) {\n return {\n written,\n authenticated,\n guidance: [\n `Cannot register \"${descriptor.serverName}\": no mcpUrl or hostedUrl supplied.`,\n ],\n };\n }\n\n const authMode = descriptor.authMode ?? \"device\";\n\n // authMode \"none\" (e.g. context-xray): URL-only for ALL clients, no auth.\n if (authMode === \"none\") {\n writeUrlOnlyEntries(\n opts.clients,\n keys,\n mcpUrl,\n scope,\n baseDir,\n written,\n errors,\n );\n return { written, authenticated, guidance: [...guidance, ...errors] };\n }\n\n // Split into OAuth-capable and device-code clients, exactly like core.\n const oauthClients = opts.clients.filter((c) => supportsRemoteMcpOAuth(c));\n const deviceClients = opts.clients.filter((c) => !supportsRemoteMcpOAuth(c));\n\n // OAuth clients always get URL-only entries (in-host OAuth, no local bearer).\n if (oauthClients.length > 0) {\n writeUrlOnlyEntries(\n oauthClients,\n keys,\n mcpUrl,\n scope,\n baseDir,\n written,\n errors,\n );\n guidance.push(\n `${describeClients(oauthClients)}: wrote URL-only MCP config (no bearer headers).`,\n ...oauthNextStepsForClients(oauthClients, descriptor.serverName),\n );\n }\n\n // Device-code clients.\n if (deviceClients.length > 0) {\n const baseUrl = resolveBaseUrl(descriptor);\n\n // We only reach here for authMode \"oauth\"/\"device\" (authMode \"none\" returned\n // earlier). Run the flow only when interactive AND we have a hosted URL to\n // authenticate against. Device-code clients such as Codex cannot use a\n // URL-only hosted entry: writing one would overwrite a working bearer token\n // and leave new sessions unauthenticated. If auth cannot complete, keep the\n // existing config untouched and surface the explicit connect command.\n const canRunFlow = interactive && !!baseUrl;\n\n if (!canRunFlow) {\n if (baseUrl) {\n guidance.push(\n `${describeClients(deviceClients)}: skipped MCP config because this client needs a bearer token.`,\n manualConnectGuidance(baseUrl, deviceClients, scope),\n );\n } else {\n guidance.push(\n `${describeClients(deviceClients)}: skipped MCP config because no hosted URL was available for authentication.`,\n );\n }\n } else {\n const appSlug = appSlugFor(descriptor, baseUrl!);\n const clientArg = deviceClients.length === 1 ? deviceClients[0] : \"all\";\n const grant = await runDeviceFlow(\n baseUrl!,\n appSlug,\n clientArg,\n log,\n opts.deviceFlowTimeoutMs,\n deps,\n );\n\n if (grant && grant.token) {\n // Write authed entries; honour the server's resolved mcpUrl when given.\n const resolvedUrl = grant.mcpUrl || mcpUrl;\n writeAuthedEntries(\n deviceClients,\n keys,\n resolvedUrl,\n grant.token,\n grant.headers,\n scope,\n baseDir,\n written,\n errors,\n );\n authenticated = true;\n } else {\n // Flow failed (or approved with no token). Do not downgrade device-code\n // clients to a URL-only hosted entry; keep any existing bearer config.\n guidance.push(\n `${describeClients(deviceClients)}: authentication did not complete; existing MCP config was left unchanged.`,\n manualConnectGuidance(baseUrl!, deviceClients, scope),\n );\n }\n }\n }\n\n return { written, authenticated, guidance: [...guidance, ...errors] };\n}\n\nfunction describeClients(clients: ClientId[]): string {\n return clients.map((client) => CLIENT_LABELS[client]).join(\", \");\n}\n\n/** Derive the `app` slug the device-start endpoint expects. */\nfunction appSlugFor(descriptor: McpDescriptor, baseUrl: string): string {\n // Prefer the descriptor's server name without the agent-native prefix.\n const name = descriptor.serverName.replace(/^agent-native-/, \"\");\n if (name) return name;\n try {\n const host = new URL(baseUrl).hostname;\n const first = host.split(\".\")[0];\n return first && first !== \"www\" ? first : \"app\";\n } catch {\n return \"app\";\n }\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type CliTelemetry } from "./telemetry.js";
|
|
2
|
-
export type SkillClient = "codex" | "claude-code";
|
|
2
|
+
export type SkillClient = "codex" | "claude-code" | "pi" | "cursor" | "opencode" | "github-copilot";
|
|
3
3
|
export type SkillScope = "project" | "user";
|
|
4
4
|
export type PlanMode = "hosted" | "local-files" | "self-hosted";
|
|
5
5
|
export interface SkillEntry {
|
|
@@ -82,6 +82,7 @@ interface ParsedArgs {
|
|
|
82
82
|
mcp: boolean;
|
|
83
83
|
planMode?: PlanMode;
|
|
84
84
|
mcpUrl?: string;
|
|
85
|
+
quiet: boolean;
|
|
85
86
|
}
|
|
86
87
|
interface PromptOption<T extends string> {
|
|
87
88
|
value: T;
|
|
@@ -101,6 +102,8 @@ export interface ScopePromptContext {
|
|
|
101
102
|
}
|
|
102
103
|
export interface GithubActionPromptContext {
|
|
103
104
|
workflowPath: string;
|
|
105
|
+
setupCommand: string;
|
|
106
|
+
docsUrl: string;
|
|
104
107
|
}
|
|
105
108
|
export interface PlanModePromptContext {
|
|
106
109
|
initialMode: PlanMode;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEvE,MAAM,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,8 @@ Usage:
|
|
|
15
15
|
|
|
16
16
|
Options:
|
|
17
17
|
--skill <name> Install only this skill (repeatable)
|
|
18
|
-
--client, -a <client> codex, claude-code,
|
|
18
|
+
--client, -a <client> codex, claude-code, pi, cursor, opencode, github-copilot, or all
|
|
19
|
+
(default: all; repeatable or comma-separated)
|
|
19
20
|
--scope <user|project> Install globally or into the current project (default: user)
|
|
20
21
|
-g, --global Alias for --scope user
|
|
21
22
|
--project Alias for --scope project
|
|
@@ -32,9 +33,9 @@ Options:
|
|
|
32
33
|
--dry-run Print intended writes without changing files
|
|
33
34
|
--json Print the result as JSON
|
|
34
35
|
|
|
35
|
-
App-backed skills (visual-plan, visual-recap
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
App-backed skills (visual-plan, visual-recap) register their hosted MCP server
|
|
37
|
+
in your agent config by default so the agent can actually use them. Use
|
|
38
|
+
--no-mcp to skip that and copy the files only.
|
|
38
39
|
For visual-plan/visual-recap, choose --mode local-files for no sharing and all
|
|
39
40
|
local files, or --mode self-hosted --mcp-url <url> for your own Plan app.
|
|
40
41
|
|
|
@@ -43,7 +44,14 @@ Examples:
|
|
|
43
44
|
npx @agent-native/skills@latest add --skill quick-recap
|
|
44
45
|
npx @agent-native/skills@latest add --skill visual-recap --with-github-action
|
|
45
46
|
`;
|
|
46
|
-
const CLIENTS = [
|
|
47
|
+
const CLIENTS = [
|
|
48
|
+
"codex",
|
|
49
|
+
"claude-code",
|
|
50
|
+
"pi",
|
|
51
|
+
"cursor",
|
|
52
|
+
"opencode",
|
|
53
|
+
"github-copilot",
|
|
54
|
+
];
|
|
47
55
|
const DEFAULT_SKILLS_SOURCE = "BuilderIO/skills";
|
|
48
56
|
const MANAGED_INSTRUCTIONS_START = "<!-- BEGIN @agent-native/skills -->";
|
|
49
57
|
const MANAGED_INSTRUCTIONS_END = "<!-- END @agent-native/skills -->";
|
|
@@ -110,6 +118,8 @@ export function parseSkillsCliArgs(argv) {
|
|
|
110
118
|
out.dryRun = true;
|
|
111
119
|
else if (arg === "--json")
|
|
112
120
|
out.printJson = true;
|
|
121
|
+
else if (arg === "--quiet")
|
|
122
|
+
out.quiet = true;
|
|
113
123
|
else if (arg === "--update-instructions")
|
|
114
124
|
out.updateInstructions = true;
|
|
115
125
|
else if (arg === "--no-update-instructions")
|
|
@@ -227,6 +237,11 @@ function shouldLoadPublicCatalog(parsed) {
|
|
|
227
237
|
return true;
|
|
228
238
|
return parsed.skillNames.some((name) => !resolveAppForSkill(name));
|
|
229
239
|
}
|
|
240
|
+
const HIDDEN_STANDALONE_BUILT_INS = [
|
|
241
|
+
"assets",
|
|
242
|
+
"design-exploration",
|
|
243
|
+
"context-xray",
|
|
244
|
+
];
|
|
230
245
|
export async function runSkillsCli(argv, options = {}) {
|
|
231
246
|
const parsed = parseSkillsCliArgs(argv);
|
|
232
247
|
// `@agent-native/skills` normally uses the exact same core flow as
|
|
@@ -255,6 +270,7 @@ export async function runSkillsCli(argv, options = {}) {
|
|
|
255
270
|
isInteractive: options.isInteractive,
|
|
256
271
|
baseDir: parsed.baseDir ?? options.baseDir,
|
|
257
272
|
catalogMode: "all",
|
|
273
|
+
hiddenBuiltInSkillTargets: HIDDEN_STANDALONE_BUILT_INS,
|
|
258
274
|
publicSkillSource: loadedSource?.root ?? parsed.source ?? DEFAULT_SKILLS_SOURCE,
|
|
259
275
|
publicSkillEntries,
|
|
260
276
|
promptSkills: options.promptSkills
|
|
@@ -268,6 +284,8 @@ export async function runSkillsCli(argv, options = {}) {
|
|
|
268
284
|
promptGithubAction: options.promptGithubAction
|
|
269
285
|
? async (context) => options.promptGithubAction?.({
|
|
270
286
|
workflowPath: context.workflowPath,
|
|
287
|
+
setupCommand: context.setupCommand,
|
|
288
|
+
docsUrl: context.docsUrl,
|
|
271
289
|
}) ?? null
|
|
272
290
|
: undefined,
|
|
273
291
|
promptPlanMode: options.promptPlanMode,
|
|
@@ -333,8 +351,8 @@ export async function runSkillsCli(argv, options = {}) {
|
|
|
333
351
|
withGithubAction: parsed.withGithubAction,
|
|
334
352
|
force: parsed.force,
|
|
335
353
|
connect: parsed.connect,
|
|
336
|
-
quiet: parsed.printJson,
|
|
337
|
-
log: parsed.printJson ? undefined : stdoutLog,
|
|
354
|
+
quiet: parsed.printJson || parsed.quiet,
|
|
355
|
+
log: parsed.printJson || parsed.quiet ? undefined : stdoutLog,
|
|
338
356
|
isInteractive: options.isInteractive,
|
|
339
357
|
promptSkills: options.promptSkills,
|
|
340
358
|
promptClients: options.promptClients,
|
|
@@ -359,6 +377,8 @@ export async function runSkillsCli(argv, options = {}) {
|
|
|
359
377
|
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
360
378
|
return;
|
|
361
379
|
}
|
|
380
|
+
if (parsed.quiet)
|
|
381
|
+
return;
|
|
362
382
|
await printInstallResult(result, {
|
|
363
383
|
baseDir: parsed.baseDir ?? options.baseDir ?? process.cwd(),
|
|
364
384
|
dryRun: parsed.dryRun,
|
|
@@ -426,8 +446,7 @@ export async function installSkills(options) {
|
|
|
426
446
|
options.telemetry?.track("skills_cli scope selected", { scope });
|
|
427
447
|
const instructionBlocks = managedInstructionBlocksForSkills(skillNames);
|
|
428
448
|
const shouldUpdateInstructions = await shouldUpdateManagedInstructions(instructionBlocks, options);
|
|
429
|
-
const shouldWriteGithubAction = selected.some((skill) => skill.name === "visual-recap")
|
|
430
|
-
shouldWritePrVisualRecapWorkflow(options, baseDir);
|
|
449
|
+
const shouldWriteGithubAction = await shouldWritePrVisualRecapWorkflow(selected.some((skill) => skill.name === "visual-recap"), options, baseDir);
|
|
431
450
|
const mcpApps = options.mcp === false || planMode === "local-files"
|
|
432
451
|
? []
|
|
433
452
|
: mcpAppsForSkills(skillNames, planMcpOverride);
|
|
@@ -441,8 +460,7 @@ export async function installSkills(options) {
|
|
|
441
460
|
const mcpServers = [];
|
|
442
461
|
try {
|
|
443
462
|
progress?.start("Installing skill files...");
|
|
444
|
-
for (const
|
|
445
|
-
const root = installRootForClient(client, scope, baseDir);
|
|
463
|
+
for (const root of unique(clients.map((client) => installRootForClient(client, scope, baseDir)))) {
|
|
446
464
|
for (const skill of selected) {
|
|
447
465
|
const destination = path.join(root, skill.name);
|
|
448
466
|
written.push(destination);
|
|
@@ -480,12 +498,17 @@ export async function installSkills(options) {
|
|
|
480
498
|
}
|
|
481
499
|
}
|
|
482
500
|
// Register the hosted MCP server for app-backed skills (visual-plan /
|
|
483
|
-
// visual-recap → Agent-Native Plan
|
|
484
|
-
//
|
|
485
|
-
//
|
|
486
|
-
//
|
|
501
|
+
// visual-recap → Agent-Native Plan) so the agent can actually call them,
|
|
502
|
+
// not just read the SKILL.md. On by default; `--no-mcp` installs the
|
|
503
|
+
// skill files only. One registration per app, so visual-plan +
|
|
504
|
+
// visual-recap share a single "plan" server.
|
|
487
505
|
if (mcpApps.length > 0) {
|
|
488
|
-
const mcpClients = clients
|
|
506
|
+
const mcpClients = clients
|
|
507
|
+
.map(skillClientToMcpClient)
|
|
508
|
+
.filter((client) => Boolean(client));
|
|
509
|
+
if (mcpClients.length === 0) {
|
|
510
|
+
throw new Error("MCP setup supports Claude Code, Codex, Cursor, OpenCode, GitHub Copilot / VS Code, or Claude Cowork. Use --mode local-files or --no-mcp for Pi.");
|
|
511
|
+
}
|
|
489
512
|
for (const app of mcpApps) {
|
|
490
513
|
progress?.message(`Registering ${app.displayName} MCP server...`);
|
|
491
514
|
if (!options.dryRun) {
|
|
@@ -574,6 +597,7 @@ function defaultArgs(command) {
|
|
|
574
597
|
force: false,
|
|
575
598
|
connect: true,
|
|
576
599
|
mcp: true,
|
|
600
|
+
quiet: false,
|
|
577
601
|
};
|
|
578
602
|
}
|
|
579
603
|
function selectedPlanApp(skillNames) {
|
|
@@ -651,14 +675,34 @@ function normalizeClients(value) {
|
|
|
651
675
|
return CLIENTS;
|
|
652
676
|
if (client === "codex")
|
|
653
677
|
return ["codex"];
|
|
678
|
+
if (client === "pi")
|
|
679
|
+
return ["pi"];
|
|
680
|
+
if (client === "cursor")
|
|
681
|
+
return ["cursor"];
|
|
682
|
+
if (client === "opencode" || client === "open-code") {
|
|
683
|
+
return ["opencode"];
|
|
684
|
+
}
|
|
685
|
+
if (client === "github-copilot" ||
|
|
686
|
+
client === "copilot" ||
|
|
687
|
+
client === "vscode" ||
|
|
688
|
+
client === "vs-code") {
|
|
689
|
+
return ["github-copilot"];
|
|
690
|
+
}
|
|
654
691
|
if (client === "claude" ||
|
|
655
692
|
client === "claude-code" ||
|
|
656
693
|
client === "claude-code-cli") {
|
|
657
694
|
return ["claude-code"];
|
|
658
695
|
}
|
|
659
|
-
throw new Error(`Unsupported client "${raw}". Use codex, claude-code, or all.`);
|
|
696
|
+
throw new Error(`Unsupported client "${raw}". Use codex, claude-code, pi, cursor, opencode, github-copilot, or all.`);
|
|
660
697
|
});
|
|
661
698
|
}
|
|
699
|
+
function skillClientToMcpClient(client) {
|
|
700
|
+
if (client === "pi")
|
|
701
|
+
return null;
|
|
702
|
+
if (client === "claude-code")
|
|
703
|
+
return "claude-code";
|
|
704
|
+
return client;
|
|
705
|
+
}
|
|
662
706
|
function normalizeSkillName(value) {
|
|
663
707
|
const normalized = value.trim().toLowerCase();
|
|
664
708
|
if (!/^[a-z0-9][a-z0-9._-]*$/.test(normalized)) {
|
|
@@ -797,7 +841,7 @@ async function printInstallResult(result, options) {
|
|
|
797
841
|
clack.note([
|
|
798
842
|
"No sharing, all local.",
|
|
799
843
|
"Run: npx @agent-native/core@latest plan blocks --out plan-blocks.md",
|
|
800
|
-
"Preview: npx @agent-native/core@latest plan local preview --dir plans/<slug>",
|
|
844
|
+
"Preview: npx @agent-native/core@latest plan local preview --dir plans/<slug> --open",
|
|
801
845
|
].join("\n"), "Local Plan files");
|
|
802
846
|
}
|
|
803
847
|
if (!options.dryRun) {
|
|
@@ -840,15 +884,24 @@ function isInteractive(options) {
|
|
|
840
884
|
function installRootForClient(client, scope, baseDir) {
|
|
841
885
|
const home = process.env.HOME || os.homedir();
|
|
842
886
|
if (scope === "project") {
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
887
|
+
if (client !== "claude-code") {
|
|
888
|
+
return path.join(baseDir, ".agents", "skills");
|
|
889
|
+
}
|
|
890
|
+
return path.join(baseDir, ".claude", "skills");
|
|
846
891
|
}
|
|
847
892
|
if (client === "codex") {
|
|
848
893
|
return process.env.CODEX_HOME
|
|
849
894
|
? path.join(process.env.CODEX_HOME, "skills")
|
|
850
895
|
: path.join(home, ".codex", "skills");
|
|
851
896
|
}
|
|
897
|
+
if (client === "pi") {
|
|
898
|
+
return path.join(home, ".agents", "skills");
|
|
899
|
+
}
|
|
900
|
+
if (client === "cursor" ||
|
|
901
|
+
client === "opencode" ||
|
|
902
|
+
client === "github-copilot") {
|
|
903
|
+
return path.join(home, ".agents", "skills");
|
|
904
|
+
}
|
|
852
905
|
return path.join(home, ".claude", "skills");
|
|
853
906
|
}
|
|
854
907
|
function discoverSkills(root) {
|
|
@@ -1083,13 +1136,44 @@ function upsertManagedBlock(file, block) {
|
|
|
1083
1136
|
function escapeRegExp(value) {
|
|
1084
1137
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1085
1138
|
}
|
|
1086
|
-
|
|
1139
|
+
const PR_VISUAL_RECAP_DOCS_URL = "https://www.agent-native.com/docs/pr-visual-recap";
|
|
1140
|
+
function prVisualRecapWorkflowPath(baseDir) {
|
|
1141
|
+
return path.join(baseDir, ".github", "workflows", "pr-visual-recap.yml");
|
|
1142
|
+
}
|
|
1143
|
+
function prVisualRecapSetupCommand() {
|
|
1144
|
+
return "npx @agent-native/core@latest recap setup";
|
|
1145
|
+
}
|
|
1146
|
+
async function promptForGithubAction(context) {
|
|
1147
|
+
const clack = await import("@clack/prompts");
|
|
1148
|
+
const result = await clack.confirm({
|
|
1149
|
+
message: "Optional: add automatic PR Visual Recaps? (GitHub Action)\n" +
|
|
1150
|
+
" Posts a human-friendly recap on every pull request.\n" +
|
|
1151
|
+
` Learn more: ${context.docsUrl}\n` +
|
|
1152
|
+
` Writes ${context.workflowPath}; ${context.setupCommand} finishes the GitHub secrets.`,
|
|
1153
|
+
initialValue: false,
|
|
1154
|
+
});
|
|
1155
|
+
if (clack.isCancel(result)) {
|
|
1156
|
+
clack.cancel("Skipped PR Visual Recap workflow.");
|
|
1157
|
+
return null;
|
|
1158
|
+
}
|
|
1159
|
+
return Boolean(result);
|
|
1160
|
+
}
|
|
1161
|
+
async function shouldWritePrVisualRecapWorkflow(hasVisualRecap, options, baseDir) {
|
|
1162
|
+
if (!hasVisualRecap)
|
|
1163
|
+
return false;
|
|
1087
1164
|
if (options.withGithubAction)
|
|
1088
1165
|
return true;
|
|
1089
|
-
if (fs.existsSync(
|
|
1166
|
+
if (fs.existsSync(prVisualRecapWorkflowPath(baseDir)))
|
|
1090
1167
|
return false;
|
|
1091
|
-
|
|
1092
|
-
|
|
1168
|
+
if (options.yes || !isInteractive(options))
|
|
1169
|
+
return false;
|
|
1170
|
+
const prompt = options.promptGithubAction ?? promptForGithubAction;
|
|
1171
|
+
const choice = await prompt({
|
|
1172
|
+
workflowPath: path.join(".github", "workflows", "pr-visual-recap.yml"),
|
|
1173
|
+
setupCommand: prVisualRecapSetupCommand(),
|
|
1174
|
+
docsUrl: PR_VISUAL_RECAP_DOCS_URL,
|
|
1175
|
+
});
|
|
1176
|
+
return choice === true;
|
|
1093
1177
|
}
|
|
1094
1178
|
const PR_VISUAL_RECAP_REUSABLE_WORKFLOW = `name: PR Visual Recap
|
|
1095
1179
|
|