Server-side Skills
Load skills from files and URLs on the server with loadSkills()
Beta — This feature is in alpha. APIs may change before stable release.
For file and url skill sources, or when you need server-controlled skill loading, use loadSkills() in your API route.
Basic Setup
// app/api/chat/route.ts
import path from "path";
import { loadSkills } from "@yourgpt/copilot-sdk/server";
export async function POST(req: Request) {
const { messages, __skills } = await req.json();
const { buildSystemPrompt, tools } = await loadSkills({
// Source 1: .md files from a local directory (highest precedence)
dir: path.join(process.cwd(), "skills"),
// Source 2: Remote .md URLs
remoteUrls: ["https://cdn.myapp.com/skills/support-policy.md"],
// Source 3: Inline skills forwarded from client (lowest precedence)
clientSkills: __skills ?? [],
});
return streamText({
model: anthropic("claude-sonnet-4-6"),
system: buildSystemPrompt("You are a helpful assistant for Acme Corp."),
messages,
tools: {
...tools, // includes load_skill automatically
...myOtherTools,
},
}).toDataStreamResponse();
}loadSkills Options
interface LoadSkillsOptions {
dir?: string; // Path to /skills directory (Node.js only)
remoteUrls?: string[]; // Remote .md URLs to fetch
clientSkills?: ClientInlineSkill[]; // Forwarded from useSkill() hooks
}loadSkills Result
interface LoadSkillsResult {
skills: ResolvedSkill[];
diagnostics: SkillDiagnostic[];
// Build system prompt: prepends eager content, appends auto catalog
buildSystemPrompt(basePrompt?: string): string;
// Ready-to-use load_skill tool definition
tools: {
load_skill: ToolDefinition;
};
}Forwarding Client Skills
<SkillProvider> automatically syncs inline skills to CopilotProvider, which includes them in every API request as __skills. Read them in your route:
const { messages, __skills } = await req.json();
const { buildSystemPrompt, tools } = await loadSkills({
dir: path.join(process.cwd(), "skills"),
clientSkills: __skills ?? [],
});Source Precedence & Collision Detection
When the same skill name appears in multiple sources, the higher-precedence source wins:
server-dir > remote-url > client-inlineconst { diagnostics } = await loadSkills({ ... });
// [{
// type: "collision",
// name: "code-review",
// winner: "server-dir",
// loser: "client-inline",
// }]
if (diagnostics.length) {
console.warn("Skill collisions:", diagnostics);
}This lets you safely override client-provided skills with authoritative server versions.
Full Example
Project structure
skills/
├── brand-voice.md # eager — always active
└── sql-expert.md # auto — loaded on demandAPI route
// app/api/chat/route.ts
import path from "path";
import { loadSkills } from "@yourgpt/copilot-sdk/server";
import { streamText } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
export async function POST(req: Request) {
const { messages, __skills } = await req.json();
const { buildSystemPrompt, tools } = await loadSkills({
dir: path.join(process.cwd(), "skills"),
clientSkills: __skills ?? [],
});
return streamText({
model: anthropic("claude-sonnet-4-6"),
system: buildSystemPrompt("You are a helpful assistant for Acme Corp."),
messages,
tools,
}).toDataStreamResponse();
}Type Reference
type SkillStrategy = "eager" | "auto" | "manual";
type SkillSource =
| { type: "inline"; content: string }
| { type: "url"; url: string }
| { type: "file"; path: string };
interface ResolvedSkill {
name: string;
description: string;
content: string;
strategy?: SkillStrategy;
version?: string;
}
interface SkillDiagnostic {
type: "collision";
name: string;
winner: "server-dir" | "remote-url" | "client-inline";
loser: "server-dir" | "remote-url" | "client-inline";
}