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
51 changes: 51 additions & 0 deletions features/pilot-tools/claire/repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { apiUrl, authHeaders, fetchWithRetry } from "../shared";

// All Claire calls go through max-agent's /api/v1/claire/* proxy. That layer
// holds the X-Service-Secret + X-On-Behalf-Of pair, so the MCP server only
// needs the user's standard bearer token — same auth model as Apollo,
// Explorium, etc. Routes are synchronous: max-agent submits + polls Claire
// internally and returns the final result.

export async function claireSearch(
token: string,
body: Record<string, unknown>,
): Promise<Response> {
return fetchWithRetry(apiUrl(`/api/v1/claire/search`), {
method: "POST",
headers: authHeaders(token),
body: JSON.stringify(body),
});
}

export async function claireDeepResearch(
token: string,
body: Record<string, unknown>,
): Promise<Response> {
return fetchWithRetry(apiUrl(`/api/v1/claire/deep-research`), {
method: "POST",
headers: authHeaders(token),
body: JSON.stringify(body),
});
}

export async function claireMarketWatch(
token: string,
body: Record<string, unknown>,
): Promise<Response> {
return fetchWithRetry(apiUrl(`/api/v1/claire/market-watch`), {
method: "POST",
headers: authHeaders(token),
body: JSON.stringify(body),
});
}

export async function claireCompetitorFinder(
token: string,
body: Record<string, unknown>,
): Promise<Response> {
return fetchWithRetry(apiUrl(`/api/v1/claire/competitor-finder`), {
method: "POST",
headers: authHeaders(token),
body: JSON.stringify(body),
});
}
57 changes: 57 additions & 0 deletions features/pilot-tools/claire/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { z } from "zod";
import { withToken } from "../shared";

const modeSchema = z
.enum(["lite", "full"])
.optional()
.describe(
"Research depth. 'lite' (default) returns a fast first-pass; 'full' does deeper multi-source research and takes longer.",
);

export const claireSearchSchema = z.object({
...withToken,
query: z
.string()
.min(1)
.describe(
"Free-text research query (e.g. 'recent Series B fundraises in fintech 2026').",
),
mode: modeSchema,
});

export const claireDeepResearchSchema = z.object({
...withToken,
name: z
.string()
.min(1)
.describe(
"Name of the person or company to research (e.g. 'Stripe' or 'Patrick Collison').",
),
entity_type: z
.enum(["person", "company"])
.describe("Whether `name` refers to a person or a company."),
mode: modeSchema,
});

export const claireMarketWatchSchema = z.object({
...withToken,
url: z
.string()
.url()
.describe("Company / market URL to monitor for changes."),
criteria: z
.string()
.optional()
.describe(
"Optional filter criteria — what to watch for (e.g. 'pricing changes', 'new hires').",
),
mode: modeSchema,
});

export const claireCompetitorFinderSchema = z.object({
...withToken,
url: z
.string()
.url()
.describe("Company website URL to find direct competitors for."),
});
68 changes: 68 additions & 0 deletions features/pilot-tools/claire/tools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { callApi, strip, type McpServer } from "../shared";
import * as repo from "./repository";
import * as S from "./schema";

// Claire is Max's research-and-intelligence layer. These tools give the chat
// agent the same Claire access onboarding already has — via max-agent's
// /api/v1/claire/* proxy, which forwards to claire-api with the
// X-Service-Secret + X-On-Behalf-Of pair. No per-user Claire token needed;
// Max vouches for the user. Calls are synchronous from the tool's POV —
// max-agent handles the Claire job submit + poll internally.

export function registerClaireTools(server: McpServer): void {
server.registerTool(
"claire_search",
{
title: "Run a Claire research search",
description:
"Free-text research query against Claire's hub. Use for quick lookups like industry trends, funding news, or any topic where you'd otherwise google. Synchronous — waits for Claire to finish and returns the result. Use mode='lite' for fast first-pass (default), 'full' for deeper multi-source.",
inputSchema: S.claireSearchSchema,
},
async (input) =>
callApi(input.bearer_token, (t) =>
repo.claireSearch(t, strip(input, "bearer_token")),
),
);

server.registerTool(
"claire_deep_research",
{
title: "Deep research on a person or company",
description:
"Multi-source background research on a named person or company. Returns recent activity, news, role context, company highlights, and proof points. Synchronous (may take 30-90s). Call this BEFORE crafting personalized outreach so the message reflects who the prospect actually is.",
inputSchema: S.claireDeepResearchSchema,
},
async (input) =>
callApi(input.bearer_token, (t) =>
repo.claireDeepResearch(t, strip(input, "bearer_token")),
),
);

server.registerTool(
"claire_market_watch",
{
title: "Monitor a URL for market signals",
description:
"Run a market-watch pass on a URL, optionally filtered by criteria (e.g. 'pricing', 'hiring', 'funding'). Returns a snapshot of detected signals. Use to inform outreach timing (e.g. just-funded companies) or to spot competitive moves.",
inputSchema: S.claireMarketWatchSchema,
},
async (input) =>
callApi(input.bearer_token, (t) =>
repo.claireMarketWatch(t, strip(input, "bearer_token")),
),
);

server.registerTool(
"claire_find_competitors",
{
title: "Find competitors via Claire",
description:
"Identify direct competitors of a company by URL. Returns a list of competing companies with descriptions and sources. Synchronous (typically 1-3 min). Use to expand a prospect list with similar companies or to ground competitive positioning in outreach.",
inputSchema: S.claireCompetitorFinderSchema,
},
async (input) =>
callApi(input.bearer_token, (t) =>
repo.claireCompetitorFinder(t, strip(input, "bearer_token")),
),
);
}
2 changes: 2 additions & 0 deletions features/pilot-tools/mcp/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { registerUniboxTools } from "../unibox/tools";
import { registerAiAgentTools } from "../ai-agent/tools";
import { registerApolloTools } from "../apollo/tools";
import { registerExploriumTools } from "../explorium/tools";
import { registerClaireTools } from "../claire/tools";
import {
registerLinkedinTools,
registerLinkedinToolsGrouped,
Expand Down Expand Up @@ -39,6 +40,7 @@ export function registerPilotMcpTools(server: McpServer): void {
registerAiAgentTools(server);
registerApolloTools(server);
registerExploriumTools(server);
registerClaireTools(server);

if (useGrouped) {
registerLinkedinToolsGrouped(server);
Expand Down
Loading