Skip to content

Commit 2791683

Browse files
committed
feat(sdk): add URL-only mode with init() and isUrlMode
Support direct provider URLs via `urls` option, bypassing directory lookup. CLI/MCP uses client.init() for discovery and filters out search_providers in URL mode. Made-with: Cursor
1 parent 209e434 commit 2791683

File tree

5 files changed

+1303
-2158
lines changed

5 files changed

+1303
-2158
lines changed

packages/cli/src/client.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ function openBrowser(url: string): void {
2020

2121
export function createClient(config: ClientConfig = {}): AgentAuthClient {
2222
const storage = new FileStorage(config.storageDir);
23+
const urlMode = (config.urls?.length ?? 0) > 0;
2324
return new AgentAuthClient({
2425
storage,
25-
directoryUrl: config.directoryUrl ?? process.env.AGENT_AUTH_DIRECTORY_URL,
26+
urls: config.urls,
27+
directoryUrl: urlMode ? undefined : (config.directoryUrl ?? process.env.AGENT_AUTH_DIRECTORY_URL),
2628
hostName: config.hostName ?? process.env.AGENT_AUTH_HOST_NAME,
2729
providers: config.providers as any,
2830
onApprovalRequired(info) {

packages/cli/src/mcp.ts

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -87,20 +87,13 @@ function jsonSchemaToZod(params: ToolParameters): ZodRawShape | undefined {
8787
export async function startMcpServer(config: ClientConfig): Promise<void> {
8888
const client = createClient(config);
8989

90-
if (config.urls?.length) {
91-
await Promise.all(
92-
config.urls.map(async (url) => {
93-
try {
94-
await client.discoverProvider(url);
95-
} catch (err) {
96-
const msg = err instanceof Error ? err.message : String(err);
97-
console.error(`Warning: could not discover ${url}: ${msg}`);
98-
}
99-
}),
100-
);
101-
}
90+
await client.init();
10291

103-
const tools = getAgentAuthTools(client);
92+
let tools = getAgentAuthTools(client);
93+
94+
if (client.isUrlMode) {
95+
tools = filterTools(tools, { exclude: ["search_providers"] });
96+
}
10497

10598
const server = new McpServer(
10699
{ name: "auth-agent", version: "0.1.0" },

packages/sdk/src/client.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,21 @@ export class AgentAuthClient {
6565
private readonly onApprovalStatusChange: ((status: AgentStatus) => void | Promise<void>) | null;
6666
private readonly approvalTimeoutMs: number;
6767
private readonly abortController: AbortController;
68+
private readonly urls: string[] | null;
6869

6970
constructor(opts: AgentAuthClientOptions = {}) {
7071
this.storage = opts.storage ?? new MemoryStorage();
7172
this.fetchFn = opts.fetch ?? globalThis.fetch.bind(globalThis);
72-
this.directoryUrl = opts.directoryUrl ?? "https://agent-auth.directory";
73-
this.allowDirectDiscovery = opts.allowDirectDiscovery ?? !this.directoryUrl;
73+
const urlMode = (opts.urls?.length ?? 0) > 0;
74+
this.urls = urlMode ? opts.urls! : null;
75+
this.directoryUrl =
76+
opts.directoryUrl !== undefined
77+
? opts.directoryUrl
78+
: urlMode
79+
? null
80+
: "https://agent-auth.directory";
81+
this.allowDirectDiscovery =
82+
opts.allowDirectDiscovery ?? (urlMode ? true : !this.directoryUrl);
7483
this.jwtExpirySeconds = opts.jwtExpirySeconds ?? 60;
7584
this.hostName = opts.hostName ?? detectHostName();
7685
this.onApprovalRequired = opts.onApprovalRequired ?? null;
@@ -85,6 +94,32 @@ export class AgentAuthClient {
8594
}
8695
}
8796

97+
/**
98+
* Initialize the client — discovers any URLs passed via the `urls`
99+
* option. Call this once after construction when using URL-only mode.
100+
* Returns the discovered provider configs (failures are logged, not thrown).
101+
*/
102+
async init(): Promise<ProviderConfig[]> {
103+
if (!this.urls?.length) return [];
104+
const results = await Promise.allSettled(
105+
this.urls.map((url) => this.discoverProvider(url)),
106+
);
107+
const configs: ProviderConfig[] = [];
108+
for (const r of results) {
109+
if (r.status === "fulfilled") {
110+
configs.push(r.value);
111+
}
112+
}
113+
return configs;
114+
}
115+
116+
/**
117+
* Whether the client is in URL-only mode (no directory).
118+
*/
119+
get isUrlMode(): boolean {
120+
return this.urls !== null && this.urls.length > 0;
121+
}
122+
88123
/**
89124
* Cancel all in-flight polling loops (approval, async execution)
90125
* and prevent new ones from starting. Call this when tearing down

packages/sdk/src/types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,16 @@ export interface AgentAuthClientOptions {
335335
* @default true when no directoryUrl, false when directoryUrl is set
336336
*/
337337
allowDirectDiscovery?: boolean;
338+
/**
339+
* Provider URLs to discover at startup.
340+
* When set, the directory is disabled and only these providers
341+
* are available — `listProviders` returns only them,
342+
* `searchProviders` is unavailable, and `search` is cache-only.
343+
*
344+
* Callers must await {@link AgentAuthClient.init | `init()`} after
345+
* construction to complete URL discovery.
346+
*/
347+
urls?: string[];
338348
/**
339349
* Pre-configured providers. Skips discovery for these.
340350
*/

0 commit comments

Comments
 (0)