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.

The implementation spans three architectural layers: protocol registration, URL classification, and parsing with routing.

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.

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

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://.

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.

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.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →