How to Use the craftagents:// URL Scheme for Deep Linking in Craft Agents
The craftagents:// URL scheme enables external applications and web pages to launch the Craft Agents desktop app and navigate directly to specific workspaces, sessions, or actions by registering a custom protocol handler in Electron that parses URLs into typed navigation targets.
The Craft Agents open-source application (craft-ai-agents/craft-agents-oss) implements a custom protocol handler that transforms craftagents:// URLs into internal navigation commands. This deep linking system allows users to open specific views, trigger actions with parameters, or target particular workspaces directly from external sources such as web browsers, other desktop applications, or system shortcuts.
How the Deep Link System Works
The implementation spans three architectural layers: protocol registration, URL classification, and parsing with routing.
- Protocol registration occurs in
apps/electron/src/main/index.ts, where the Electron app registers itself as the default handler forcraftagents://URIs usingapp.setAsDefaultProtocolClient. - URL classification in
packages/shared/src/utils/url-safety.tsdistinguishes internal deep links from external URLs to prevent security vulnerabilities. - Parsing and routing handled by
apps/electron/src/main/deep-link.tsconverts valid URLs into structuredDeepLinkTargetobjects, whilepackages/server-core/src/handlers/rpc/system.tsdispatches navigation requests via theRPC_CHANNELS.shell.OPEN_URLhandler.
Registering the craftagents:// Protocol
The Electron main process registers the custom protocol during application startup. The scheme defaults to craftagents but can be customized via the CRAFT_DEEPLINK_SCHEME environment variable for development environments.
// apps/electron/src/main/index.ts
const DEEPLINK_SCHEME = process.env.CRAFT_DEEPLINK_SCHEME || 'craftagents';
app.setAsDefaultProtocolClient(`${DEEPLINK_SCHEME}://`);
This registration tells the operating system to launch the Craft Agents executable whenever a user clicks a craftagents:// link. During development, you can run multiple instances by setting distinct schemes such as CRAFT_DEEPLINK_SCHEME=craftagents1 or craftagents2.
Security and URL Classification
Before routing, the system validates URLs to ensure internal deep links do not open in external browsers. The classifyExternalUrl function in packages/shared/src/utils/url-safety.ts identifies craftagents: schemes as internal resources.
// packages/shared/src/utils/url-safety.ts
const INTERNAL_DEEPLINK_SCHEME = 'craftagents:';
export function classifyExternalUrl(url: string) {
// Returns { kind: 'internal-deeplink' } for craftagents://…
}
When the system RPC handler receives an OPEN_URL request, it checks this classification. If the URL is an internal deep link, the handler intercepts it and routes through the internal parser rather than invoking shell.openExternal.
Parsing Deep Links into Navigation Targets
The parseDeepLink function in apps/electron/src/main/deep-link.ts extracts structured data from craftagents:// URLs. It supports compound routes, workspace-specific paths, and action-based URLs with query parameters.
// apps/electron/src/main/deep-link.ts
export function parseDeepLink(url: string): DeepLinkTarget | null {
const parsed = new URL(url);
if (parsed.protocol !== 'craftagents:') return null;
const host = parsed.hostname;
const pathParts = parsed.pathname.split('/').filter(Boolean);
const windowMode = parseWindowMode(parsed);
const rightSidebar = parseRightSidebar(parsed);
// Compound routes (e.g., craftagents://allSessions/session/abc)
if (COMPOUND_ROUTE_PREFIXES.includes(host)) {
return {
view: host + (pathParts.length ? '/' + pathParts.join('/') : ''),
windowMode,
rightSidebar
};
}
// Workspace-specific routes
if (host === 'workspace') {
const workspaceId = pathParts[0];
// ... workspace handling logic
}
// Action routes (e.g., craftagents://action/new-chat)
if (host === 'action') {
const result: DeepLinkTarget = {
action: pathParts[0],
actionParams: {},
windowMode,
rightSidebar,
};
// Copy search params into actionParams
return result;
}
return null;
}
The parser extracts the window query parameter to determine display mode (e.g., focused or full) and returns a DeepLinkTarget containing the view path, workspace ID, action name, and parameters.
RPC Handling and Navigation Dispatch
The packages/server-core/src/handlers/rpc/system.ts file implements the bridge between the main process and renderer. When a deep link arrives, the RPC_CHANNELS.shell.OPEN_URL handler validates and forwards the navigation.
// packages/server-core/src/handlers/rpc/system.ts
server.handle(RPC_CHANNELS.shell.OPEN_URL, async (_ctx, rawUrl: string) => {
const classification = classifyExternalUrl(rawUrl);
if (classification.kind === 'internal-deeplink') {
const target = parseDeepLink(rawUrl);
if (!target) return { success: false, error: 'Invalid deep link' };
const navigation: DeepLinkNavigation = {
view: target.view,
action: target.action,
actionParams: target.actionParams,
};
await requestClientOpenExternal(server, ctx.clientId, navigation);
return { success: true };
}
// Fallback to external browser
await requestClientOpenExternal(server, ctx.clientId, rawUrl);
return { success: true };
});
This handler ensures that craftagents:// URLs trigger internal navigation within the active window, while standard HTTP URLs open in the system default browser.
Practical craftagents:// URL Examples
Opening a Specific Session
Navigate directly to a session within the All Sessions view:
const url = 'craftagents://allSessions/session/abc123';
window.open(url);
This opens the Craft Agents app and displays the session with ID abc123.
Accessing Settings Pages
Deep link to specific settings subsections:
const url = 'craftagents://settings/shortcuts';
window.open(url);
The renderer receives view: 'settings/shortcuts' and navigates to the shortcuts configuration panel.
Triggering Actions with Parameters
Start a new chat with pre-filled input and automatic sending:
const url = 'craftagents://action/new-chat?input=Hello%20world&send=true';
window.open(url);
The parser produces:
{
"action": "new-chat",
"actionParams": { "input": "Hello world", "send": "true" }
}
Targeting Specific Workspaces
Execute actions within a particular workspace context:
const url = 'craftagents://workspace/ws42/action/delete-session/12345';
window.open(url);
This switches to workspace ws42 and executes the delete-session action on session 12345.
Opening Views in New Windows
Use the window=focused parameter to spawn a new Electron window:
const url = 'craftagents://allSessions?window=focused';
window.open(url);
Instead of navigating the current window, this creates a new focused window displaying the All Sessions view.
Summary
- The
craftagents://URL scheme is registered inapps/electron/src/main/index.tsusingapp.setAsDefaultProtocolClient. - Security validation occurs in
packages/shared/src/utils/url-safety.tsto prevent internal links from opening in external browsers. - The parser in
apps/electron/src/main/deep-link.tsconverts URLs into structuredDeepLinkTargetobjects supporting views, workspaces, and actions. - Navigation dispatch happens through
RPC_CHANNELS.shell.OPEN_URLinpackages/server-core/src/handlers/rpc/system.ts, which routes internally or falls back to the OS browser. - You can customize the scheme via the
CRAFT_DEEPLINK_SCHEMEenvironment variable for development scenarios.
Frequently Asked Questions
Can I customize the URL scheme to something other than craftagents://?
Yes. According to the source code in apps/electron/src/main/index.ts, you can override the default scheme by setting the CRAFT_DEEPLINK_SCHEME environment variable before launching the application. This is particularly useful for running multiple development instances simultaneously with schemes like craftagents1:// or craftagents2://.
What happens if Craft Agents is not installed when a craftagents:// link is clicked?
If the application is not installed, the operating system cannot resolve the protocol handler and will typically display an error dialog or prompt the user to find an application capable of opening the link. The deep link only functions when the Craft Agents Electron app has registered itself as the protocol client.
How do I open a specific workspace using the deep link scheme?
Use the workspace host segment followed by the workspace ID and optional action path. For example, craftagents://workspace/ws42 opens workspace ws42, while craftagents://workspace/ws42/action/delete-session/12345 opens that workspace and executes a specific action. The parser in deep-link.ts handles these compound routes by extracting the workspace ID from the path segments.
Are craftagents:// links safe to use in web browsers?
Yes, but with caveats. The packages/shared/src/utils/url-safety.ts module explicitly classifies these URLs as internal deeplinks, and the system RPC handler intercepts them before they reach shell.openExternal. However, if Craft Agents is not installed, clicking the link will result in a protocol error. For web applications, consider providing fallback instructions or checking protocol support via navigator.registerProtocolHandler where appropriate.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →