Skip to content

Commit 5c1d964

Browse files
its-mashMohammod Al Amin Ashik
authored andcommitted
feat(P2): auto-map API-key clients + binding_type in the TS API
register_api_key_client now best-effort auto-creates a clientId-keyed id mapping to the (locked or default) Space's Starter, so a new client routes sensibly out of the box and the mapping is visible + editable in the Mapping tab. Also exposes binding_type on the WorkspaceBinding TS type + create/update input. Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com>
1 parent c9ddadc commit 5c1d964

2 files changed

Lines changed: 53 additions & 3 deletions

File tree

apps/desktop/src-tauri/src/commands/oauth.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use tracing::{debug, error, info, warn};
3737
use url::Url;
3838

3939
use super::gateway::GatewayAppState;
40+
use crate::state::AppState;
4041

4142
// ============================================================================
4243
// Deep Link Handling
@@ -979,6 +980,7 @@ fn generate_api_key() -> (String, String, String) {
979980
#[tauri::command]
980981
pub async fn register_api_key_client(
981982
gateway_state: State<'_, Arc<RwLock<GatewayAppState>>>,
983+
app: State<'_, AppState>,
982984
name: String,
983985
locked_space_id: Option<String>,
984986
) -> Result<RegisteredApiKeyClient, String> {
@@ -1042,6 +1044,18 @@ pub async fn register_api_key_client(
10421044
trimmed, client_id
10431045
);
10441046

1047+
// Best-effort: auto-create a clientId-keyed mapping → the (locked or
1048+
// default) Space's Starter, so the client routes sensibly out of the box
1049+
// and the mapping is visible + editable in the Mapping tab. A failure here
1050+
// must not undo the registration — without an explicit mapping the resolver
1051+
// still falls back to the default Starter.
1052+
if let Err(e) = auto_map_api_key_client(&app, &client_id, locked_space_id.as_deref()).await {
1053+
warn!(
1054+
"[OAuth] auto-map for {} failed (non-fatal): {}",
1055+
client_id, e
1056+
);
1057+
}
1058+
10451059
Ok(RegisteredApiKeyClient {
10461060
client_id,
10471061
client_name: trimmed.to_string(),
@@ -1051,6 +1065,40 @@ pub async fn register_api_key_client(
10511065
})
10521066
}
10531067

1068+
/// Auto-create a clientId-keyed `id` mapping pointing at the (locked or
1069+
/// default) Space's Starter FeatureSet, so a freshly-registered API-key client
1070+
/// routes somewhere sensible by default and the operator can retarget it from
1071+
/// the Mapping tab.
1072+
async fn auto_map_api_key_client(
1073+
app: &AppState,
1074+
client_id: &str,
1075+
locked_space_id: Option<&str>,
1076+
) -> Result<(), String> {
1077+
let space_id = match locked_space_id {
1078+
Some(s) => uuid::Uuid::parse_str(s).map_err(|e| e.to_string())?,
1079+
None => {
1080+
app.space_service
1081+
.get_default()
1082+
.await
1083+
.map_err(|e| e.to_string())?
1084+
.ok_or("no default Space configured")?
1085+
.id
1086+
}
1087+
};
1088+
let starter = app
1089+
.feature_set_repository
1090+
.get_starter_for_space(&space_id.to_string())
1091+
.await
1092+
.map_err(|e| e.to_string())?
1093+
.ok_or("Space has no Starter FeatureSet")?;
1094+
let binding =
1095+
mcpmux_core::WorkspaceBinding::new_id(client_id.to_string(), space_id, vec![starter.id]);
1096+
app.workspace_binding_repository
1097+
.create(&binding)
1098+
.await
1099+
.map_err(|e| e.to_string())
1100+
}
1101+
10541102
/// Issue an additional API key for an existing client (rotation). Returns the
10551103
/// new key plaintext once.
10561104
#[tauri::command]

apps/desktop/src/lib/api/workspaceBindings.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { invoke } from '@tauri-apps/api/core';
1010
export interface WorkspaceBinding {
1111
id: string;
1212
workspace_root: string;
13+
/** `path` (a normalized folder) or `id` (an arbitrary exact-match key). */
14+
binding_type: 'path' | 'id';
1315
space_id: string;
1416
/**
1517
* Non-empty by construction. Order is the operator-chosen rendering
@@ -26,6 +28,8 @@ export interface WorkspaceBindingInput {
2628
workspace_root: string;
2729
space_id: string;
2830
feature_set_ids: string[];
31+
/** `path` (default — folder, normalized) or `id` (verbatim exact-match key). */
32+
binding_type?: 'path' | 'id';
2933
}
3034

3135
/** List every binding (sorted by workspace_root). */
@@ -69,9 +73,7 @@ export async function validateWorkspaceRoot(path: string): Promise<string> {
6973
}
7074

7175
/** List bindings whose target Space is the given one. */
72-
export async function listWorkspaceBindingsForSpace(
73-
spaceId: string
74-
): Promise<WorkspaceBinding[]> {
76+
export async function listWorkspaceBindingsForSpace(spaceId: string): Promise<WorkspaceBinding[]> {
7577
return invoke('list_workspace_bindings_for_space', { spaceId });
7678
}
7779

0 commit comments

Comments
 (0)