Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mcp/benchmark/src/trace/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { SearchProvenanceEntry } from "../types";
export const SEARCH_TOOL_NAMES = new Set([
"stakgraph_search",
"fulltext_search",
"graph_search",
]);

export function groupEvents(events: TraceEvent[]): DisplayUnit[] {
Expand Down
114 changes: 114 additions & 0 deletions mcp/src/repo/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,120 @@ export async function get_tools(
);
}

// Conditionally register Jarvis ontology tools if JARVIS_URL is set
const jarvisUrl = process.env.JARVIS_URL;
if (jarvisUrl) {
const jarvisHeaders = {
"Content-Type": "application/json",
"X-Api-Token": process.env.STAKWORK_SECRET ?? "",
};

allTools.get_ontology = tool({
description:
"Fetch the list of all available node types in the Jarvis knowledge graph ontology. " +
"Call this before graph_search to discover valid values for the `type` parameter.",
inputSchema: z.object({}),
execute: async () => {
const url = `${jarvisUrl}/v2/schema?concise=true`;
console.log(`[get_ontology] fetching ${url}`);
try {
const resp = await fetch(url, { headers: jarvisHeaders });
if (!resp.ok) {
const text = await resp.text();
return `HTTP ${resp.status}: ${text}`;
}
const data = (await resp.json()) as any;
const schemas: any[] = data.schemas ?? [];
return JSON.stringify(
schemas
.filter((s: any) => s.type && !s.is_deleted)
.map((s: any) => s.type)
.sort()
);
} catch (err: any) {
return `get_ontology failed: ${err?.message ?? String(err)}`;
}
},
});

allTools.graph_search = tool({
description:
"Search the Jarvis knowledge graph for ontology nodes — people, topics, episodes, clips, organizations, workflows, and more. " +
"Unlike stakgraph_search (code nodes only), this queries the full Jarvis ontology. " +
"Call get_ontology first to discover valid values for the `type` parameter.",
inputSchema: z.object({
q: z.string().describe("The search query"),
type: z
.string()
.optional()
.describe(
"Comma-separated node type filter, e.g. 'Episode' or 'Person,Topic'. " +
"Call get_ontology to see all valid values."
),
limit: z
.number()
.optional()
.default(10)
.describe("Maximum number of results to return"),
domains: z
.string()
.optional()
.describe(
"Comma-separated domain filter, e.g. 'entity' or 'content,entity'. " +
"Not required — the search works without it. " +
"Valid values: entity, content, knowledgeartifact, workflow, codeartifact."
),
}),
execute: async ({
q,
type,
limit = 10,
domains,
}: {
q: string;
type?: string;
limit?: number;
domains?: string;
}) => {
const params = new URLSearchParams({ q, limit: String(limit) });
if (type) params.set("type", type);
if (domains) params.set("domains", domains);
const url = `${jarvisUrl}/v2/nodes?${params.toString()}`;
console.log(`[graph_search] q=${q} type=${type ?? "*"} domains=${domains ?? "*"} limit=${limit}`);
try {
const resp = await fetch(url, { headers: jarvisHeaders });
if (!resp.ok) {
const text = await resp.text();
return `HTTP ${resp.status}: ${text}`;
}
const data = (await resp.json()) as any;
const nodes: any[] = Array.isArray(data) ? data : (data.nodes ?? []);
return JSON.stringify(
nodes.map((n: any) => ({
ref_id: n.ref_id ?? n.properties?.ref_id,
name:
n.properties?.name ??
n.properties?.episode_title ??
n.properties?.entity,
node_type: n.node_type,
description:
n.properties?.description ??
n.properties?.summary ??
n.properties?.text ??
"",
}))
);
} catch (err: any) {
return `graph_search failed: ${err?.message ?? String(err)}`;
}
},
});

console.log("===> registered graph_search + get_ontology tools");
} else {
console.log("===> no JARVIS_URL set, skipping graph_search + get_ontology tools");
}

// Register sub-agent tools (remote agent delegation)
if (subAgents && subAgents.length > 0) {
for (const subAgent of subAgents) {
Expand Down
Loading