Custom Agent Integration

For developers building custom agents that connect directly to SkillzDrive via MCP.

Connecting your own agent gives you real-time access to the full skill library — dynamic discovery, on-demand execution, and programmatic drive management. This guide covers skill awareness, UI feedback, async execution, and caching patterns to make your integration production-ready.

Not building a custom agent?

If you use Claude Code, Cursor, Claude.ai, ChatGPT, or another client with built-in MCP support, you don't need this page — follow the Quickstart instead.

A. Making Your Agent Aware of Skills

Baseline: install the guide skill

Connecting MCP and expecting the AI to use it is the #1 integration mistake. Most LLMs will NOT call MCP tools unless explicitly told to.

Install the skillzdrive-mcp-guide skill locally alongside your agent. Once installed, the MCP server's briefing (plus skills_getStarted) teaches your agent all 29 tools, execution workflows, error handling patterns, and fallback chains — no system prompt engineering required.

With the baseline covered, the two patterns below address production concerns the briefing can't — architectural choices that depend on how your agent is structured.

Pattern 1: Pinned Skills for Sub-Agents

In orchestrator/sub-agent architectures, the orchestrator discovers skills dynamically (via searchSkills or listSkills), then pins a specific skill or script to a sub-agent for efficiency. The sub-agent skips discovery entirely and goes straight to execution.

typescript
// Orchestrator: discover dynamically, then delegate
const { results } = await mcpClient.callTool("skills_searchSkills", {
  query: userTask, // e.g. "merge these PDFs"
});
const skill = results[0]; // best match

// Pin the skill slug to a sub-agent's context
const subAgentPrompt = `You are a ${skill.name} specialist.
Use skill slug: ${skill.slug}
Go directly to skills_listScripts → skills_runScript → skills_readFile → skills_closeSession.
Do NOT call searchSkills or listSkills — your skill is already assigned.`;

// Sub-agent now runs with zero discovery overhead
await spawnSubAgent(subAgentPrompt);

Tip

This is the most efficient pattern for production systems. The orchestrator handles discovery once, and each sub-agent operates with minimal tool calls. You can even pin down to a specific script name if the orchestrator already knows which script to run.

Pattern 2: Caching Strategy

Don't re-fetch skill lists on every message. Skills and scripts don't change mid-conversation.

What to CacheCache DurationWhen to Refresh
listSkills resultsEntire sessionAgent restart
listScripts per slugEntire sessionAgent restart
runScript resultsNever cacheEvery execution is unique

B. UI Feedback for Tool Calls

The #2 mistake

Running skills silently. Users see nothing for 5-30 seconds and think the app is broken. Always show what's happening.

What to show at each stage

StageWhat to ShowExample
searchSkillsSearching for skills...Spinner + message
listScriptsFound merge.py — preparing...Status update
runScriptProcessing your PDF...Progress indicator
readFileReading results...Brief flash
SuccessFormatted resultCode block or rendered output
ErrorError + suggestionsAlert with recovery action

Implementation pattern

typescript
// Hook into MCP tool call events for real-time UI feedback
agent.onToolCall((toolName, args) => {
  switch (toolName) {
    case "skills_searchSkills":
      showStatus("Searching for skills...");
      break;
    case "skills_listScripts":
      showStatus(`Loading scripts for ${args.skillSlug}...`);
      break;
    case "skills_runScript":
      showStatus(`Running ${args.scriptName}...`);
      break;
    case "skills_readFile":
      showStatus("Reading results...");
      break;
    case "skills_closeSession":
      // No UI needed — cleanup is silent
      break;
  }
});

agent.onToolResult((toolName, result) => {
  if (toolName === "skills_runScript") {
    if (result.exitCode === 0) {
      showStatus("Script completed successfully", "success");
    } else {
      showStatus(result.error || "Script failed", "error");
    }
  }
});

Displaying errors well

SkillzDrive errors are structured for great UX. Use all the fields:

typescript
// Error response structure
{
  "error": "skill_not_found",           // Machine-readable code
  "message": "No skill with slug 'pdff'", // Show this to users
  "suggestions": [                       // Show as recovery options
    "Did you mean 'pdf'?",
    "Use skills_searchSkills to find skills"
  ],
  "_workflow": {                         // Use for automatic recovery
    "nextSteps": [
      { "tool": "skills_searchSkills", "example": { "query": "pdf" } }
    ]
  }
}

// Display pattern:
// 1. Show `message` as the error text
// 2. Show `suggestions[0]` as a clickable recovery action
// 3. Use `_workflow.nextSteps` to auto-recover if possible
// 4. NEVER show raw JSON-RPC errors to users

C. Async Script Execution

Warning

Scripts can take 5-60 seconds. Don't make users wait silently. Acknowledge immediately, show progress, and notify when done.

Recommended async pattern

#StepWhat Happens
1Start the scriptrunScript with reuseSession: true
2Acknowledge immediately"I'm processing your request..."
3Continue conversationLet users ask other questions while script runs
4Await completionScript finishes, read output
5Notify with results"Your PDF merge is complete!"
typescript
async function executeSkillAsync(
  mcpClient: McpClient,
  skillSlug: string,
  scriptName: string,
  args?: string[]
) {
  // 1. Show immediate feedback
  notify("Starting skill execution...", "loading");

  // 2. Run the script
  const runResult = await mcpClient.callTool("skills_runScript", {
    skillSlug,
    scriptName,
    args,
    reuseSession: true,
  });

  // 3. Handle failure
  if (runResult.exitCode !== 0) {
    notify(`Error: ${runResult.error}`, "error");
    return null;
  }

  // 4. Read the output
  const output = await mcpClient.callTool("skills_readFile", {
    sessionId: runResult.sessionId,
    filePath: "/tmp/last_run.out",
  });

  // 5. Show results
  notify("Skill completed!", "success");
  displayOutput(output.content);

  // 6. Clean up
  await mcpClient.callTool("skills_closeSession", {
    sessionId: runResult.sessionId,
  });

  return output.content;
}

Handling large output

If output.hasMore is true, paginate with startLine and limit parameters. The default returns the first 200 lines.

D. Integration Checklist

Run through this before shipping your integration. Every checked box is a better user experience.

Connection
Skill Awareness
Execution
User Experience

Next Steps