fix: show oauth manual URL by default

This commit is contained in:
Peter Steinberger 2026-05-04 05:44:54 +01:00
parent 7691ba812d
commit 45dcb6561e
No known key found for this signature in database
3 changed files with 32 additions and 1 deletions

View File

@ -10,6 +10,7 @@
- Restore `mcporter call --key value` / `--key=value` tool arguments, including JSON array/object coercion, `--json -` stdin payloads, schema-aware bare string-to-array wrapping, and kebab-case to camelCase field mapping. (Issues #119 and #126)
- Quote generated `emit-ts` members for tool names that are not valid TypeScript identifiers. (PR #149 / issue #30, thanks @solomonneas)
- Resolve relative stdio args in generated CLI bundles against the generated script location instead of the caller's current directory. (PR #148 / issue #56, thanks @solomonneas)
- Print OAuth manual-completion URLs at the default warning log level so headless users can copy them. (PR #143 / issue #139, thanks @stainlu)
### Config

View File

@ -256,7 +256,7 @@ class PersistentOAuthClientProvider implements OAuthClientProvider {
this.logger.info(`Authorization required for ${this.definition.name}. Opening browser...`);
this.ensureAuthorizationDeferred();
__oauthInternals.openExternal(authorizationUrl.toString());
this.logger.info(`If the browser did not open, visit ${authorizationUrl.toString()} manually.`);
this.logger.warn(`If the browser did not open, visit ${authorizationUrl.toString()} manually.`);
}
async saveCodeVerifier(codeVerifier: string): Promise<void> {

View File

@ -213,4 +213,34 @@ describe('FileOAuthClientProvider session lifecycle', () => {
await expect(waitPromise).resolves.toBe('stable-deferred-code');
await session.close();
});
it('logs the manual OAuth URL at warn level for headless terminals (#139)', async () => {
const tokenCacheDir = await fs.mkdtemp(path.join(os.tmpdir(), 'mcporter-oauth-test-'));
tempDirs.push(tokenCacheDir);
const definition: ServerDefinition = {
name: 'test-oauth-headless-url',
description: 'Test OAuth server',
command: { kind: 'http', url: new URL('https://example.com/mcp') },
auth: 'oauth',
tokenCacheDir,
};
const logger = {
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
};
const session = await createOAuthSession(definition, logger);
const provider = session.provider as StatefulProvider;
vi.spyOn(__oauthInternals, 'openExternal').mockImplementation(() => {});
const authorizationUrl = new URL('https://example.com/auth?code=xyz');
const waitPromise = session.waitForAuthorizationCode().catch(() => undefined);
await provider.redirectToAuthorization(authorizationUrl);
expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining(`visit ${authorizationUrl.toString()} manually`));
await session.close();
await waitPromise;
});
});