Add skill context to Ask Hexclave

This commit is contained in:
mantrakp04 2026-06-16 12:44:48 -07:00
parent c7ef9e9461
commit 29b6554cb2
3 changed files with 83 additions and 0 deletions

View File

@ -3,6 +3,7 @@ import { selectModel } from "@/lib/ai/models";
import { getFullSystemPrompt } from "@/lib/ai/prompts";
import { reviewMcpCall } from "@/lib/ai/qa-reviewer";
import { requestBodySchema } from "@/lib/ai/schema";
import { getMcpSkillContextPrompt } from "@/lib/ai/mcp-skill-context";
import { getTools } from "@/lib/ai/tools";
import { getVerifiedQaContext } from "@/lib/ai/verified-qa";
import { listManagedProjectIds } from "@/lib/projects";
@ -61,6 +62,7 @@ export const POST = createSmartRouteHandler({
if (isDocsOrSearch) {
systemPrompt += await getVerifiedQaContext();
}
systemPrompt += await getMcpSkillContextPrompt(body.mcpCallMetadata?.toolName);
const tools = await getTools(toolNames, { auth: fullReq.auth, targetProjectId: projectId });
const toolsArg = Object.keys(tools).length > 0 ? tools : undefined;
const isCreateDashboard = systemPromptId === "create-dashboard";

View File

@ -0,0 +1,51 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { getMcpSkillContextPrompt } from "./mcp-skill-context";
describe("getMcpSkillContextPrompt", () => {
afterEach(() => {
vi.restoreAllMocks();
});
it("does not fetch skill context for non-ask_hexclave requests", async () => {
const fetchSpy = vi.spyOn(globalThis, "fetch");
await expect(getMcpSkillContextPrompt("other_tool")).resolves.toMatchInlineSnapshot(`""`);
expect(fetchSpy).not.toHaveBeenCalled();
});
it("fetches and embeds the canonical skill for ask_hexclave requests", async () => {
const fetchSpy = vi.spyOn(globalThis, "fetch").mockResolvedValue(
new Response("# Hexclave Skill\n\nUse Hexclave docs."),
);
await expect(getMcpSkillContextPrompt("ask_hexclave")).resolves.toMatchInlineSnapshot(`
"
## MCP-Provided Hexclave Skill Context
The current request came through the public Hexclave MCP server's ask_hexclave tool.
The backend fetched the canonical Hexclave agent skill from https://skill.hexclave.com
immediately before spawning this assistant. Treat this skill content as baseline context
for answering the user's question, while still using documentation tools for specific
facts and citations:
# Hexclave Skill
Use Hexclave docs.
"
`);
expect(fetchSpy).toHaveBeenCalledWith("https://skill.hexclave.com", {
headers: { Accept: "text/markdown" },
});
});
it("fails loudly when the canonical skill cannot be fetched", async () => {
vi.spyOn(globalThis, "fetch").mockResolvedValue(
new Response("missing", { status: 503, statusText: "Service Unavailable" }),
);
await expect(getMcpSkillContextPrompt("ask_hexclave")).rejects.toThrowErrorMatchingInlineSnapshot(
`[Error: Failed to fetch skill from https://skill.hexclave.com: 503 Service Unavailable]`,
);
});
});

View File

@ -0,0 +1,30 @@
const hexclaveSkillResourceUri = "https://skill.hexclave.com";
export async function getMcpSkillContextPrompt(toolName: string | null | undefined): Promise<string> {
if (toolName !== "ask_hexclave") {
return "";
}
const response = await fetch(hexclaveSkillResourceUri, {
headers: { Accept: "text/markdown" },
});
if (!response.ok) {
throw new Error(
`Failed to fetch skill from ${hexclaveSkillResourceUri}: ${response.status} ${response.statusText}`,
);
}
const skillContext = await response.text();
return `
## MCP-Provided Hexclave Skill Context
The current request came through the public Hexclave MCP server's ask_hexclave tool.
The backend fetched the canonical Hexclave agent skill from https://skill.hexclave.com
immediately before spawning this assistant. Treat this skill content as baseline context
for answering the user's question, while still using documentation tools for specific
facts and citations:
${skillContext}
`;
}