Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions packages/mcp/src/__tests__/oauth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ describe('McpClientManager OAuth E2E', () => {
assert.ok(authorizationUrl.searchParams.get('code_challenge'));
// Dynamic registration ran before the redirect.
assert.ok(fixture.registrations.length >= 1);
const registration = fixture.registrations[0];
assert.ok(registration && typeof registration === 'object');
const body = registration as Record<string, unknown>;
assert.equal(body.client_uri, 'https://maka.apache.org/en/');
assert.equal(body.software_id, 'maka');
assert.equal(body.client_name, 'maka');
// Consent disclosure material: the resolved issuer, the scope the round
// requests, and the round's state travel back to the caller so a UI can
// show what is being granted before a browser opens.
Expand Down Expand Up @@ -1064,6 +1070,28 @@ describe('McpClientManager OAuth E2E', () => {
assert.equal(status.state, 'connected');
});

test('OAuth client metadata identifies the Apache project and the running client', () => {
for (const clientName of ['maka', 'maka-tui', 'maka-desktop'] as const) {
const provider = new McpOAuthProvider({
serverId: 'remote',
serverUrl: 'https://mcp.example/mcp',
storage: createMemoryMcpOAuthStorage(),
clientName,
clientVersion: '0.2.0',
});
assert.equal(provider.clientMetadata.client_uri, 'https://maka.apache.org/en/');
assert.equal(provider.clientMetadata.software_id, clientName);
assert.equal(provider.clientMetadata.client_name, clientName);
assert.equal(provider.clientMetadata.software_version, '0.2.0');
assert.deepEqual(provider.clientMetadata.redirect_uris, []);
assert.deepEqual(provider.clientMetadata.grant_types, [
'authorization_code',
'refresh_token',
]);
assert.doesNotMatch(JSON.stringify(provider.clientMetadata), /maka-agent\/maka-agent/u);
}
});

test('a discovery that moves to another authorization server drops the registered client', async () => {
const storage = createMemoryMcpOAuthStorage();
await storage.set('remote', {
Expand Down
7 changes: 5 additions & 2 deletions packages/mcp/src/oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ export interface McpOAuthProviderOptions {
* token request. */
const BACKGROUND_REDIRECT_URL = 'http://127.0.0.1/maka-mcp-oauth-noninteractive';

/** Product homepage sent in OAuth dynamic client registration. */
const MCP_OAUTH_CLIENT_URI = 'https://maka.apache.org/en/';

export class McpOAuthProvider implements OAuthClientProvider {
/** Present only when an interactive state was supplied — the SDK treats
* a defined method as "client uses state". */
Expand All @@ -177,8 +180,8 @@ export class McpOAuthProvider implements OAuthClientProvider {
get clientMetadata(): OAuthClientMetadata {
return {
client_name: this.options.clientName,
client_uri: 'https://github.com/maka-agent/maka-agent',
software_id: 'maka-desktop',
client_uri: MCP_OAUTH_CLIENT_URI,
software_id: this.options.clientName,
software_version: this.options.clientVersion,
redirect_uris: this.options.interactive ? [this.options.interactive.redirectUrl] : [],
grant_types: ['authorization_code', 'refresh_token'],
Expand Down