chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
+137
View File
@@ -0,0 +1,137 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundBotLogsParams, ProfoundBotLogsResponse } from './types'
export const profoundBotLogsTool: ToolConfig<ProfoundBotLogsParams, ProfoundBotLogsResponse> = {
id: 'profound_bot_logs',
name: 'Profound Bot Logs',
description: 'Get identified bot visit logs with filters for a domain in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Domain to query bot logs for (e.g. example.com)',
},
startDate: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Start date (YYYY-MM-DD or ISO 8601)',
},
endDate: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'End date (YYYY-MM-DD or ISO 8601). Defaults to now',
},
dimensions: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Comma-separated dimensions: timestamp, method, host, path, status_code, ip, user_agent, referer, bytes_sent, duration_ms, query_params, bot_name, bot_provider, bot_types',
},
filters: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'JSON array of filter objects, e.g. [{"field":"bot_name","operator":"is","value":"GPTBot"}]',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results (default 10000, max 50000)',
},
},
request: {
url: 'https://api.tryprofound.com/v1/logs/raw/bots',
method: 'POST',
headers: (params) => ({
'X-API-Key': params.apiKey,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
domain: params.domain,
start_date: params.startDate,
metrics: ['count'],
}
if (params.endDate) {
body.end_date = params.endDate
}
if (params.dimensions) {
body.dimensions = params.dimensions.split(',').map((d) => d.trim())
}
if (params.filters) {
try {
body.filters = JSON.parse(params.filters)
} catch {
throw new Error('Invalid JSON in filters parameter')
}
}
if (params.limit != null) {
body.pagination = { limit: params.limit }
}
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to get bot logs')
}
if (Array.isArray(data)) {
return {
success: true,
output: {
totalRows: data.length,
data: data.map((row: { metrics: number[]; dimensions: string[] }) => ({
metrics: row.metrics ?? [],
dimensions: row.dimensions ?? [],
})),
},
}
}
return {
success: true,
output: {
totalRows: data.info?.total_rows ?? 0,
data: (data.data ?? []).map((row: { metrics: number[]; dimensions: string[] }) => ({
metrics: row.metrics ?? [],
dimensions: row.dimensions ?? [],
})),
},
}
},
outputs: {
totalRows: {
type: 'number',
description: 'Total number of bot log entries',
},
data: {
type: 'json',
description: 'Bot log data rows with metrics and dimension values',
properties: {
metrics: { type: 'json', description: 'Array of metric values (count)' },
dimensions: {
type: 'json',
description: 'Array of dimension values matching requested dimensions order',
},
},
},
},
}
+145
View File
@@ -0,0 +1,145 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundBotsReportParams, ProfoundBotsReportResponse } from './types'
export const profoundBotsReportTool: ToolConfig<
ProfoundBotsReportParams,
ProfoundBotsReportResponse
> = {
id: 'profound_bots_report',
name: 'Profound Bots Report',
description: 'Query bot traffic report with hourly granularity for a domain in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Domain to query bot traffic for (e.g. example.com)',
},
startDate: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Start date (YYYY-MM-DD or ISO 8601)',
},
endDate: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'End date (YYYY-MM-DD or ISO 8601). Defaults to now',
},
metrics: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Comma-separated metrics: count, citations, indexing, training, last_visit',
},
dimensions: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated dimensions: date, hour, path, bot_name, bot_provider, bot_type',
},
dateInterval: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Date interval: hour, day, week, month, year',
},
filters: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'JSON array of filter objects, e.g. [{"field":"bot_name","operator":"is","value":"GPTBot"}]',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results (default 10000, max 50000)',
},
},
request: {
url: 'https://api.tryprofound.com/v2/reports/bots',
method: 'POST',
headers: (params) => ({
'X-API-Key': params.apiKey,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
domain: params.domain,
start_date: params.startDate,
metrics: params.metrics.split(',').map((m) => m.trim()),
}
if (params.endDate) {
body.end_date = params.endDate
}
if (params.dimensions) {
body.dimensions = params.dimensions.split(',').map((d) => d.trim())
}
if (params.dateInterval) {
body.date_interval = params.dateInterval
}
if (params.filters) {
try {
body.filters = JSON.parse(params.filters)
} catch {
throw new Error('Invalid JSON in filters parameter')
}
}
if (params.limit != null) {
body.pagination = { limit: params.limit }
}
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to query bots report')
}
return {
success: true,
output: {
totalRows: data.info?.total_rows ?? 0,
data: (data.data ?? []).map((row: { metrics: number[]; dimensions: string[] }) => ({
metrics: row.metrics ?? [],
dimensions: row.dimensions ?? [],
})),
},
}
},
outputs: {
totalRows: {
type: 'number',
description: 'Total number of rows in the report',
},
data: {
type: 'json',
description: 'Report data rows with metrics and dimension values',
properties: {
metrics: {
type: 'json',
description: 'Array of metric values matching requested metrics order',
},
dimensions: {
type: 'json',
description: 'Array of dimension values matching requested dimensions order',
},
},
},
},
}
@@ -0,0 +1,84 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundCategoryAssetsParams, ProfoundCategoryAssetsResponse } from './types'
export const profoundCategoryAssetsTool: ToolConfig<
ProfoundCategoryAssetsParams,
ProfoundCategoryAssetsResponse
> = {
id: 'profound_category_assets',
name: 'Profound Category Assets',
description: 'List assets (companies/brands) for a specific category in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
categoryId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Category ID (UUID)',
},
},
request: {
url: (params) =>
`https://api.tryprofound.com/v1/org/categories/${encodeURIComponent(params.categoryId)}/assets`,
method: 'GET',
headers: (params) => ({
'X-API-Key': params.apiKey,
Accept: 'application/json',
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to list category assets')
}
return {
success: true,
output: {
assets: (data ?? []).map(
(item: {
id: string
name: string
website: string
alternate_domains: string[] | null
is_owned: boolean
created_at: string
logo_url: string
}) => ({
id: item.id ?? null,
name: item.name ?? null,
website: item.website ?? null,
alternateDomains: item.alternate_domains ?? null,
isOwned: item.is_owned ?? false,
createdAt: item.created_at ?? null,
logoUrl: item.logo_url ?? null,
})
),
},
}
},
outputs: {
assets: {
type: 'json',
description: 'List of assets in the category',
properties: {
id: { type: 'string', description: 'Asset ID' },
name: { type: 'string', description: 'Asset/company name' },
website: { type: 'string', description: 'Website URL' },
alternateDomains: { type: 'json', description: 'Alternate domain names' },
isOwned: { type: 'boolean', description: 'Whether the asset is owned by the organization' },
createdAt: { type: 'string', description: 'When the asset was created' },
logoUrl: { type: 'string', description: 'URL of the asset logo' },
},
},
},
}
@@ -0,0 +1,98 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundCategoryPersonasParams, ProfoundCategoryPersonasResponse } from './types'
export const profoundCategoryPersonasTool: ToolConfig<
ProfoundCategoryPersonasParams,
ProfoundCategoryPersonasResponse
> = {
id: 'profound_category_personas',
name: 'Profound Category Personas',
description: 'List personas for a specific category in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
categoryId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Category ID (UUID)',
},
},
request: {
url: (params) =>
`https://api.tryprofound.com/v1/org/categories/${encodeURIComponent(params.categoryId)}/personas`,
method: 'GET',
headers: (params) => ({
'X-API-Key': params.apiKey,
Accept: 'application/json',
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to list category personas')
}
return {
success: true,
output: {
personas: (data.data ?? []).map(
(item: {
id: string
name: string
persona: {
behavior: { painPoints: string | null; motivations: string | null }
employment: {
industry: string[]
jobTitle: string[]
companySize: string[]
roleSeniority: string[]
}
demographics: { ageRange: string[] }
}
}) => ({
id: item.id ?? null,
name: item.name ?? null,
persona: {
behavior: {
painPoints: item.persona?.behavior?.painPoints ?? null,
motivations: item.persona?.behavior?.motivations ?? null,
},
employment: {
industry: item.persona?.employment?.industry ?? [],
jobTitle: item.persona?.employment?.jobTitle ?? [],
companySize: item.persona?.employment?.companySize ?? [],
roleSeniority: item.persona?.employment?.roleSeniority ?? [],
},
demographics: {
ageRange: item.persona?.demographics?.ageRange ?? [],
},
},
})
),
},
}
},
outputs: {
personas: {
type: 'json',
description: 'List of personas in the category',
properties: {
id: { type: 'string', description: 'Persona ID' },
name: { type: 'string', description: 'Persona name' },
persona: {
type: 'json',
description: 'Persona profile with behavior, employment, and demographics',
},
},
},
},
}
+189
View File
@@ -0,0 +1,189 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundCategoryPromptsParams, ProfoundCategoryPromptsResponse } from './types'
export const profoundCategoryPromptsTool: ToolConfig<
ProfoundCategoryPromptsParams,
ProfoundCategoryPromptsResponse
> = {
id: 'profound_category_prompts',
name: 'Profound Category Prompts',
description: 'List prompts for a specific category in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
categoryId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Category ID (UUID)',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results (default 10000, max 10000)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from previous response',
},
orderDir: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort direction: asc or desc (default desc)',
},
promptType: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated prompt types to filter: visibility, sentiment',
},
topicId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated topic IDs (UUIDs) to filter by',
},
tagId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated tag IDs (UUIDs) to filter by',
},
regionId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated region IDs (UUIDs) to filter by',
},
platformId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated platform IDs (UUIDs) to filter by',
},
},
request: {
url: (params) => {
const url = new URL(
`https://api.tryprofound.com/v1/org/categories/${encodeURIComponent(params.categoryId)}/prompts`
)
if (params.limit != null) url.searchParams.set('limit', String(params.limit))
if (params.cursor) url.searchParams.set('cursor', params.cursor)
if (params.orderDir) url.searchParams.set('order_dir', params.orderDir)
if (params.promptType) {
for (const pt of params.promptType.split(',').map((s) => s.trim())) {
url.searchParams.append('prompt_type', pt)
}
}
if (params.topicId) {
for (const tid of params.topicId.split(',').map((s) => s.trim())) {
url.searchParams.append('topic_id', tid)
}
}
if (params.tagId) {
for (const tid of params.tagId.split(',').map((s) => s.trim())) {
url.searchParams.append('tag_id', tid)
}
}
if (params.regionId) {
for (const rid of params.regionId.split(',').map((s) => s.trim())) {
url.searchParams.append('region_id', rid)
}
}
if (params.platformId) {
for (const pid of params.platformId.split(',').map((s) => s.trim())) {
url.searchParams.append('platform_id', pid)
}
}
return url.toString()
},
method: 'GET',
headers: (params) => ({
'X-API-Key': params.apiKey,
Accept: 'application/json',
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to list category prompts')
}
return {
success: true,
output: {
totalRows: data.info?.total_rows ?? 0,
nextCursor: data.info?.next_cursor ?? null,
prompts: (data.data ?? []).map(
(item: {
id: string
prompt: string
prompt_type: string
topic: { id: string; name: string }
tags: Array<{ id: string; name: string }>
regions: Array<{ id: string; name: string }>
platforms: Array<{ id: string; name: string }>
created_at: string
}) => ({
id: item.id ?? null,
prompt: item.prompt ?? null,
promptType: item.prompt_type ?? null,
topicId: item.topic?.id ?? null,
topicName: item.topic?.name ?? null,
tags: (item.tags ?? []).map((t: { id: string; name: string }) => ({
id: t.id ?? null,
name: t.name ?? null,
})),
regions: (item.regions ?? []).map((r: { id: string; name: string }) => ({
id: r.id ?? null,
name: r.name ?? null,
})),
platforms: (item.platforms ?? []).map((p: { id: string; name: string }) => ({
id: p.id ?? null,
name: p.name ?? null,
})),
createdAt: item.created_at ?? null,
})
),
},
}
},
outputs: {
totalRows: {
type: 'number',
description: 'Total number of prompts',
},
nextCursor: {
type: 'string',
description: 'Cursor for next page of results',
optional: true,
},
prompts: {
type: 'json',
description: 'List of prompts',
properties: {
id: { type: 'string', description: 'Prompt ID' },
prompt: { type: 'string', description: 'Prompt text' },
promptType: { type: 'string', description: 'Prompt type (visibility or sentiment)' },
topicId: { type: 'string', description: 'Topic ID' },
topicName: { type: 'string', description: 'Topic name' },
tags: { type: 'json', description: 'Associated tags' },
regions: { type: 'json', description: 'Associated regions' },
platforms: { type: 'json', description: 'Associated platforms' },
createdAt: { type: 'string', description: 'When the prompt was created' },
},
},
},
}
+64
View File
@@ -0,0 +1,64 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundCategoryTagsParams, ProfoundCategoryTagsResponse } from './types'
export const profoundCategoryTagsTool: ToolConfig<
ProfoundCategoryTagsParams,
ProfoundCategoryTagsResponse
> = {
id: 'profound_category_tags',
name: 'Profound Category Tags',
description: 'List tags for a specific category in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
categoryId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Category ID (UUID)',
},
},
request: {
url: (params) =>
`https://api.tryprofound.com/v1/org/categories/${encodeURIComponent(params.categoryId)}/tags`,
method: 'GET',
headers: (params) => ({
'X-API-Key': params.apiKey,
Accept: 'application/json',
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to list category tags')
}
return {
success: true,
output: {
tags: (data ?? []).map((item: { id: string; name: string }) => ({
id: item.id ?? null,
name: item.name ?? null,
})),
},
}
},
outputs: {
tags: {
type: 'json',
description: 'List of tags in the category',
properties: {
id: { type: 'string', description: 'Tag ID (UUID)' },
name: { type: 'string', description: 'Tag name' },
},
},
},
}
@@ -0,0 +1,64 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundCategoryTopicsParams, ProfoundCategoryTopicsResponse } from './types'
export const profoundCategoryTopicsTool: ToolConfig<
ProfoundCategoryTopicsParams,
ProfoundCategoryTopicsResponse
> = {
id: 'profound_category_topics',
name: 'Profound Category Topics',
description: 'List topics for a specific category in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
categoryId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Category ID (UUID)',
},
},
request: {
url: (params) =>
`https://api.tryprofound.com/v1/org/categories/${encodeURIComponent(params.categoryId)}/topics`,
method: 'GET',
headers: (params) => ({
'X-API-Key': params.apiKey,
Accept: 'application/json',
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to list category topics')
}
return {
success: true,
output: {
topics: (data ?? []).map((item: { id: string; name: string }) => ({
id: item.id ?? null,
name: item.name ?? null,
})),
},
}
},
outputs: {
topics: {
type: 'json',
description: 'List of topics in the category',
properties: {
id: { type: 'string', description: 'Topic ID (UUID)' },
name: { type: 'string', description: 'Topic name' },
},
},
},
}
@@ -0,0 +1,60 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundCitationPromptsParams, ProfoundCitationPromptsResponse } from './types'
export const profoundCitationPromptsTool: ToolConfig<
ProfoundCitationPromptsParams,
ProfoundCitationPromptsResponse
> = {
id: 'profound_citation_prompts',
name: 'Profound Citation Prompts',
description: 'Get prompts that cite a specific domain across AI platforms in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
inputDomain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Domain to look up citations for (e.g. ramp.com)',
},
},
request: {
url: (params) => {
const url = new URL('https://api.tryprofound.com/v1/prompt-volumes/citation-prompts')
url.searchParams.set('input_domain', params.inputDomain)
return url.toString()
},
method: 'GET',
headers: (params) => ({
'X-API-Key': params.apiKey,
Accept: 'application/json',
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to get citation prompts')
}
return {
success: true,
output: {
data: data ?? null,
},
}
},
outputs: {
data: {
type: 'json',
description: 'Citation prompt data for the queried domain',
},
},
}
+144
View File
@@ -0,0 +1,144 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundCitationsReportParams, ProfoundCitationsReportResponse } from './types'
export const profoundCitationsReportTool: ToolConfig<
ProfoundCitationsReportParams,
ProfoundCitationsReportResponse
> = {
id: 'profound_citations_report',
name: 'Profound Citations Report',
description: 'Query citations report for a category in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
categoryId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Category ID (UUID)',
},
startDate: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Start date (YYYY-MM-DD or ISO 8601)',
},
endDate: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'End date (YYYY-MM-DD or ISO 8601)',
},
metrics: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Comma-separated metrics: count, citation_share',
},
dimensions: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Comma-separated dimensions: hostname, path, date, region, topic, model, tag, prompt, url, root_domain, persona, citation_category',
},
dateInterval: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Date interval: hour, day, week, month, year',
},
filters: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'JSON array of filter objects, e.g. [{"field":"hostname","operator":"is","value":"example.com"}]',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results (default 10000, max 50000)',
},
},
request: {
url: 'https://api.tryprofound.com/v1/reports/citations',
method: 'POST',
headers: (params) => ({
'X-API-Key': params.apiKey,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
category_id: params.categoryId,
start_date: params.startDate,
end_date: params.endDate,
metrics: params.metrics.split(',').map((m) => m.trim()),
}
if (params.dimensions) {
body.dimensions = params.dimensions.split(',').map((d) => d.trim())
}
if (params.dateInterval) {
body.date_interval = params.dateInterval
}
if (params.filters) {
try {
body.filters = JSON.parse(params.filters)
} catch {
throw new Error('Invalid JSON in filters parameter')
}
}
if (params.limit != null) {
body.pagination = { limit: params.limit }
}
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to query citations report')
}
return {
success: true,
output: {
totalRows: data.info?.total_rows ?? 0,
data: (data.data ?? []).map((row: { metrics: number[]; dimensions: string[] }) => ({
metrics: row.metrics ?? [],
dimensions: row.dimensions ?? [],
})),
},
}
},
outputs: {
totalRows: {
type: 'number',
description: 'Total number of rows in the report',
},
data: {
type: 'json',
description: 'Report data rows with metrics and dimension values',
properties: {
metrics: {
type: 'json',
description: 'Array of metric values matching requested metrics order',
},
dimensions: {
type: 'json',
description: 'Array of dimension values matching requested dimensions order',
},
},
},
},
}
+24
View File
@@ -0,0 +1,24 @@
export { profoundBotLogsTool } from './bot_logs'
export { profoundBotsReportTool } from './bots_report'
export { profoundCategoryAssetsTool } from './category_assets'
export { profoundCategoryPersonasTool } from './category_personas'
export { profoundCategoryPromptsTool } from './category_prompts'
export { profoundCategoryTagsTool } from './category_tags'
export { profoundCategoryTopicsTool } from './category_topics'
export { profoundCitationPromptsTool } from './citation_prompts'
export { profoundCitationsReportTool } from './citations_report'
export { profoundListAssetsTool } from './list_assets'
export { profoundListCategoriesTool } from './list_categories'
export { profoundListDomainsTool } from './list_domains'
export { profoundListModelsTool } from './list_models'
export { profoundListOptimizationsTool } from './list_optimizations'
export { profoundListPersonasTool } from './list_personas'
export { profoundListRegionsTool } from './list_regions'
export { profoundOptimizationAnalysisTool } from './optimization_analysis'
export { profoundPromptAnswersTool } from './prompt_answers'
export { profoundPromptVolumeTool } from './prompt_volume'
export { profoundQueryFanoutsTool } from './query_fanouts'
export { profoundRawLogsTool } from './raw_logs'
export { profoundReferralsReportTool } from './referrals_report'
export { profoundSentimentReportTool } from './sentiment_report'
export { profoundVisibilityReportTool } from './visibility_report'
+85
View File
@@ -0,0 +1,85 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundListAssetsParams, ProfoundListAssetsResponse } from './types'
export const profoundListAssetsTool: ToolConfig<
ProfoundListAssetsParams,
ProfoundListAssetsResponse
> = {
id: 'profound_list_assets',
name: 'Profound List Assets',
description: 'List all organization assets (companies/brands) across all categories in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
},
request: {
url: 'https://api.tryprofound.com/v1/org/assets',
method: 'GET',
headers: (params) => ({
'X-API-Key': params.apiKey,
Accept: 'application/json',
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to list assets')
}
return {
success: true,
output: {
assets: (data.data ?? []).map(
(item: {
id: string
name: string
website: string
alternate_domains: string[] | null
is_owned: boolean
created_at: string
logo_url: string
category: { id: string; name: string }
}) => ({
id: item.id ?? null,
name: item.name ?? null,
website: item.website ?? null,
alternateDomains: item.alternate_domains ?? null,
isOwned: item.is_owned ?? false,
createdAt: item.created_at ?? null,
logoUrl: item.logo_url ?? null,
categoryId: item.category?.id ?? null,
categoryName: item.category?.name ?? null,
})
),
},
}
},
outputs: {
assets: {
type: 'json',
description: 'List of organization assets with category info',
properties: {
id: { type: 'string', description: 'Asset ID' },
name: { type: 'string', description: 'Asset/company name' },
website: { type: 'string', description: 'Asset website URL' },
alternateDomains: { type: 'json', description: 'Alternate domain names' },
isOwned: {
type: 'boolean',
description: 'Whether this asset is owned by the organization',
},
createdAt: { type: 'string', description: 'When the asset was created' },
logoUrl: { type: 'string', description: 'URL of the asset logo' },
categoryId: { type: 'string', description: 'Category ID the asset belongs to' },
categoryName: { type: 'string', description: 'Category name' },
},
},
},
}
@@ -0,0 +1,57 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundListCategoriesParams, ProfoundListCategoriesResponse } from './types'
export const profoundListCategoriesTool: ToolConfig<
ProfoundListCategoriesParams,
ProfoundListCategoriesResponse
> = {
id: 'profound_list_categories',
name: 'Profound List Categories',
description: 'List all organization categories in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
},
request: {
url: 'https://api.tryprofound.com/v1/org/categories',
method: 'GET',
headers: (params) => ({
'X-API-Key': params.apiKey,
Accept: 'application/json',
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to list categories')
}
return {
success: true,
output: {
categories: (data ?? []).map((item: { id: string; name: string }) => ({
id: item.id ?? null,
name: item.name ?? null,
})),
},
}
},
outputs: {
categories: {
type: 'json',
description: 'List of organization categories',
properties: {
id: { type: 'string', description: 'Category ID' },
name: { type: 'string', description: 'Category name' },
},
},
},
}
+59
View File
@@ -0,0 +1,59 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundListDomainsParams, ProfoundListDomainsResponse } from './types'
export const profoundListDomainsTool: ToolConfig<
ProfoundListDomainsParams,
ProfoundListDomainsResponse
> = {
id: 'profound_list_domains',
name: 'Profound List Domains',
description: 'List all organization domains in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
},
request: {
url: 'https://api.tryprofound.com/v1/org/domains',
method: 'GET',
headers: (params) => ({
'X-API-Key': params.apiKey,
Accept: 'application/json',
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to list domains')
}
return {
success: true,
output: {
domains: (data ?? []).map((item: { id: string; name: string; created_at: string }) => ({
id: item.id ?? null,
name: item.name ?? null,
createdAt: item.created_at ?? null,
})),
},
}
},
outputs: {
domains: {
type: 'json',
description: 'List of organization domains',
properties: {
id: { type: 'string', description: 'Domain ID (UUID)' },
name: { type: 'string', description: 'Domain name' },
createdAt: { type: 'string', description: 'When the domain was added' },
},
},
},
}
+57
View File
@@ -0,0 +1,57 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundListModelsParams, ProfoundListModelsResponse } from './types'
export const profoundListModelsTool: ToolConfig<
ProfoundListModelsParams,
ProfoundListModelsResponse
> = {
id: 'profound_list_models',
name: 'Profound List Models',
description: 'List all AI models/platforms tracked in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
},
request: {
url: 'https://api.tryprofound.com/v1/org/models',
method: 'GET',
headers: (params) => ({
'X-API-Key': params.apiKey,
Accept: 'application/json',
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to list models')
}
return {
success: true,
output: {
models: (data ?? []).map((item: { id: string; name: string }) => ({
id: item.id ?? null,
name: item.name ?? null,
})),
},
}
},
outputs: {
models: {
type: 'json',
description: 'List of AI models/platforms',
properties: {
id: { type: 'string', description: 'Model ID (UUID)' },
name: { type: 'string', description: 'Model/platform name' },
},
},
},
}
@@ -0,0 +1,104 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundListOptimizationsParams, ProfoundListOptimizationsResponse } from './types'
export const profoundListOptimizationsTool: ToolConfig<
ProfoundListOptimizationsParams,
ProfoundListOptimizationsResponse
> = {
id: 'profound_list_optimizations',
name: 'Profound List Optimizations',
description: 'List content optimization entries for an asset in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
assetId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Asset ID (UUID)',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results (default 10000, max 50000)',
},
offset: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Offset for pagination (default 0)',
},
},
request: {
url: (params) => {
const url = new URL(
`https://api.tryprofound.com/v1/content/${encodeURIComponent(params.assetId)}/optimization`
)
if (params.limit != null) url.searchParams.set('limit', String(params.limit))
if (params.offset != null) url.searchParams.set('offset', String(params.offset))
return url.toString()
},
method: 'GET',
headers: (params) => ({
'X-API-Key': params.apiKey,
Accept: 'application/json',
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to list optimizations')
}
return {
success: true,
output: {
totalRows: data.info?.total_rows ?? 0,
optimizations: (data.data ?? []).map(
(item: {
id: string
title: string
created_at: string
extracted_input: string | null
type: string
status: string
}) => ({
id: item.id ?? null,
title: item.title ?? null,
createdAt: item.created_at ?? null,
extractedInput: item.extracted_input ?? null,
type: item.type ?? null,
status: item.status ?? null,
})
),
},
}
},
outputs: {
totalRows: {
type: 'number',
description: 'Total number of optimization entries',
},
optimizations: {
type: 'json',
description: 'List of content optimization entries',
properties: {
id: { type: 'string', description: 'Optimization ID (UUID)' },
title: { type: 'string', description: 'Content title' },
createdAt: { type: 'string', description: 'When the optimization was created' },
extractedInput: { type: 'string', description: 'Extracted input text' },
type: { type: 'string', description: 'Content type: file, text, or url' },
status: { type: 'string', description: 'Optimization status' },
},
},
},
}
+96
View File
@@ -0,0 +1,96 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundListPersonasParams, ProfoundListPersonasResponse } from './types'
export const profoundListPersonasTool: ToolConfig<
ProfoundListPersonasParams,
ProfoundListPersonasResponse
> = {
id: 'profound_list_personas',
name: 'Profound List Personas',
description: 'List all organization personas across all categories in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
},
request: {
url: 'https://api.tryprofound.com/v1/org/personas',
method: 'GET',
headers: (params) => ({
'X-API-Key': params.apiKey,
Accept: 'application/json',
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to list personas')
}
return {
success: true,
output: {
personas: (data.data ?? []).map(
(item: {
id: string
name: string
category: { id: string; name: string }
persona: {
behavior: { painPoints: string | null; motivations: string | null }
employment: {
industry: string[]
jobTitle: string[]
companySize: string[]
roleSeniority: string[]
}
demographics: { ageRange: string[] }
}
}) => ({
id: item.id ?? null,
name: item.name ?? null,
categoryId: item.category?.id ?? null,
categoryName: item.category?.name ?? null,
persona: {
behavior: {
painPoints: item.persona?.behavior?.painPoints ?? null,
motivations: item.persona?.behavior?.motivations ?? null,
},
employment: {
industry: item.persona?.employment?.industry ?? [],
jobTitle: item.persona?.employment?.jobTitle ?? [],
companySize: item.persona?.employment?.companySize ?? [],
roleSeniority: item.persona?.employment?.roleSeniority ?? [],
},
demographics: {
ageRange: item.persona?.demographics?.ageRange ?? [],
},
},
})
),
},
}
},
outputs: {
personas: {
type: 'json',
description: 'List of organization personas with profile details',
properties: {
id: { type: 'string', description: 'Persona ID' },
name: { type: 'string', description: 'Persona name' },
categoryId: { type: 'string', description: 'Category ID' },
categoryName: { type: 'string', description: 'Category name' },
persona: {
type: 'json',
description: 'Persona profile with behavior, employment, and demographics',
},
},
},
},
}
+57
View File
@@ -0,0 +1,57 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundListRegionsParams, ProfoundListRegionsResponse } from './types'
export const profoundListRegionsTool: ToolConfig<
ProfoundListRegionsParams,
ProfoundListRegionsResponse
> = {
id: 'profound_list_regions',
name: 'Profound List Regions',
description: 'List all organization regions in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
},
request: {
url: 'https://api.tryprofound.com/v1/org/regions',
method: 'GET',
headers: (params) => ({
'X-API-Key': params.apiKey,
Accept: 'application/json',
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to list regions')
}
return {
success: true,
output: {
regions: (data ?? []).map((item: { id: string; name: string }) => ({
id: item.id ?? null,
name: item.name ?? null,
})),
},
}
},
outputs: {
regions: {
type: 'json',
description: 'List of organization regions',
properties: {
id: { type: 'string', description: 'Region ID (UUID)' },
name: { type: 'string', description: 'Region name' },
},
},
},
}
@@ -0,0 +1,161 @@
import type { ToolConfig } from '@/tools/types'
import type {
ProfoundOptimizationAnalysisParams,
ProfoundOptimizationAnalysisResponse,
} from './types'
export const profoundOptimizationAnalysisTool: ToolConfig<
ProfoundOptimizationAnalysisParams,
ProfoundOptimizationAnalysisResponse
> = {
id: 'profound_optimization_analysis',
name: 'Profound Optimization Analysis',
description: 'Get detailed content optimization analysis for a specific content item in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
assetId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Asset ID (UUID)',
},
contentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Content/optimization ID (UUID)',
},
},
request: {
url: (params) =>
`https://api.tryprofound.com/v1/content/${encodeURIComponent(params.assetId)}/optimization/${encodeURIComponent(params.contentId)}`,
method: 'GET',
headers: (params) => ({
'X-API-Key': params.apiKey,
Accept: 'application/json',
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to get optimization analysis')
}
const analysis = data.data
return {
success: true,
output: {
content: {
format: analysis?.content?.format ?? null,
value: analysis?.content?.value ?? null,
},
aeoContentScore: analysis?.aeo_content_score
? {
value: analysis.aeo_content_score.value ?? 0,
targetZone: {
low: analysis.aeo_content_score.target_zone?.low ?? 0,
high: analysis.aeo_content_score.target_zone?.high ?? 0,
},
}
: null,
analysis: {
breakdown: (analysis?.analysis?.breakdown ?? []).map(
(b: { title: string; weight: number; score: number }) => ({
title: b.title ?? null,
weight: b.weight ?? 0,
score: b.score ?? 0,
})
),
},
recommendations: (analysis?.recommendations ?? []).map(
(r: {
title: string
status: string
impact: { section: string; score: number } | null
suggestion: { text: string; rationale: string }
}) => ({
title: r.title ?? null,
status: r.status ?? null,
impact: r.impact
? {
section: r.impact.section ?? null,
score: r.impact.score ?? 0,
}
: null,
suggestion: {
text: r.suggestion?.text ?? null,
rationale: r.suggestion?.rationale ?? null,
},
})
),
},
}
},
outputs: {
content: {
type: 'json',
description: 'The analyzed content',
properties: {
format: { type: 'string', description: 'Content format: markdown or html' },
value: { type: 'string', description: 'Content text' },
},
},
aeoContentScore: {
type: 'json',
description: 'AEO content score with target zone',
optional: true,
properties: {
value: { type: 'number', description: 'AEO score value' },
targetZone: {
type: 'json',
description: 'Target zone range',
properties: {
low: { type: 'number', description: 'Low end of target range' },
high: { type: 'number', description: 'High end of target range' },
},
},
},
},
analysis: {
type: 'json',
description: 'Analysis breakdown by category',
properties: {
breakdown: {
type: 'json',
description: 'Array of scoring breakdowns',
properties: {
title: { type: 'string', description: 'Category title' },
weight: { type: 'number', description: 'Category weight' },
score: { type: 'number', description: 'Category score' },
},
},
},
},
recommendations: {
type: 'json',
description: 'Content optimization recommendations',
properties: {
title: { type: 'string', description: 'Recommendation title' },
status: { type: 'string', description: 'Status: done or pending' },
impact: { type: 'json', description: 'Impact details with section and score' },
suggestion: {
type: 'json',
description: 'Suggestion text and rationale',
properties: {
text: { type: 'string', description: 'Suggestion text' },
rationale: { type: 'string', description: 'Why this recommendation matters' },
},
},
},
},
},
}
+141
View File
@@ -0,0 +1,141 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundPromptAnswersParams, ProfoundPromptAnswersResponse } from './types'
export const profoundPromptAnswersTool: ToolConfig<
ProfoundPromptAnswersParams,
ProfoundPromptAnswersResponse
> = {
id: 'profound_prompt_answers',
name: 'Profound Prompt Answers',
description: 'Get raw prompt answers data for a category in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
categoryId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Category ID (UUID)',
},
startDate: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Start date (YYYY-MM-DD or ISO 8601)',
},
endDate: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'End date (YYYY-MM-DD or ISO 8601)',
},
filters: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'JSON array of filter objects, e.g. [{"field":"prompt_type","operator":"is","value":"visibility"}]',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results (default 10000, max 50000)',
},
},
request: {
url: 'https://api.tryprofound.com/v1/prompts/answers',
method: 'POST',
headers: (params) => ({
'X-API-Key': params.apiKey,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
category_id: params.categoryId,
start_date: params.startDate,
end_date: params.endDate,
}
if (params.filters) {
try {
body.filters = JSON.parse(params.filters)
} catch {
throw new Error('Invalid JSON in filters parameter')
}
}
if (params.limit != null) {
body.pagination = { limit: params.limit }
}
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to get prompt answers')
}
return {
success: true,
output: {
totalRows: data.info?.total_rows ?? 0,
data: (data.data ?? []).map(
(row: {
prompt: string | null
prompt_type: string | null
response: string | null
mentions: string[] | null
citations: string[] | null
topic: string | null
region: string | null
model: string | null
asset: string | null
created_at: string | null
}) => ({
prompt: row.prompt ?? null,
promptType: row.prompt_type ?? null,
response: row.response ?? null,
mentions: row.mentions ?? [],
citations: row.citations ?? [],
topic: row.topic ?? null,
region: row.region ?? null,
model: row.model ?? null,
asset: row.asset ?? null,
createdAt: row.created_at ?? null,
})
),
},
}
},
outputs: {
totalRows: {
type: 'number',
description: 'Total number of answer rows',
},
data: {
type: 'json',
description: 'Raw prompt answer data',
properties: {
prompt: { type: 'string', description: 'The prompt text' },
promptType: { type: 'string', description: 'Prompt type (visibility or sentiment)' },
response: { type: 'string', description: 'AI model response text' },
mentions: { type: 'json', description: 'Companies/assets mentioned in the response' },
citations: { type: 'json', description: 'URLs cited in the response' },
topic: { type: 'string', description: 'Topic name' },
region: { type: 'string', description: 'Region name' },
model: { type: 'string', description: 'AI model/platform name' },
asset: { type: 'string', description: 'Asset name' },
createdAt: { type: 'string', description: 'Timestamp when the answer was collected' },
},
},
},
}
+138
View File
@@ -0,0 +1,138 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundPromptVolumeParams, ProfoundPromptVolumeResponse } from './types'
export const profoundPromptVolumeTool: ToolConfig<
ProfoundPromptVolumeParams,
ProfoundPromptVolumeResponse
> = {
id: 'profound_prompt_volume',
name: 'Profound Prompt Volume',
description:
'Query prompt volume data to understand search demand across AI platforms in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
startDate: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Start date (YYYY-MM-DD or ISO 8601)',
},
endDate: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'End date (YYYY-MM-DD or ISO 8601)',
},
metrics: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Comma-separated metrics: volume, change',
},
dimensions: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Comma-separated dimensions: keyword, date, platform, country_code, matching_type, frequency',
},
dateInterval: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Date interval: hour, day, week, month, year',
},
filters: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'JSON array of filter objects, e.g. [{"field":"keyword","operator":"contains","value":"best"}]',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results (default 10000, max 50000)',
},
},
request: {
url: 'https://api.tryprofound.com/v1/prompt-volumes/volume',
method: 'POST',
headers: (params) => ({
'X-API-Key': params.apiKey,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
start_date: params.startDate,
end_date: params.endDate,
metrics: params.metrics.split(',').map((m) => m.trim()),
}
if (params.dimensions) {
body.dimensions = params.dimensions.split(',').map((d) => d.trim())
}
if (params.dateInterval) {
body.date_interval = params.dateInterval
}
if (params.filters) {
try {
body.filters = JSON.parse(params.filters)
} catch {
throw new Error('Invalid JSON in filters parameter')
}
}
if (params.limit != null) {
body.pagination = { limit: params.limit }
}
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to query prompt volume')
}
return {
success: true,
output: {
totalRows: data.info?.total_rows ?? 0,
data: (data.data ?? []).map((row: { metrics: number[]; dimensions: string[] }) => ({
metrics: row.metrics ?? [],
dimensions: row.dimensions ?? [],
})),
},
}
},
outputs: {
totalRows: {
type: 'number',
description: 'Total number of rows in the report',
},
data: {
type: 'json',
description: 'Volume data rows with metrics and dimension values',
properties: {
metrics: {
type: 'json',
description: 'Array of metric values matching requested metrics order',
},
dimensions: {
type: 'json',
description: 'Array of dimension values matching requested dimensions order',
},
},
},
},
}
+143
View File
@@ -0,0 +1,143 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundQueryFanoutsParams, ProfoundQueryFanoutsResponse } from './types'
export const profoundQueryFanoutsTool: ToolConfig<
ProfoundQueryFanoutsParams,
ProfoundQueryFanoutsResponse
> = {
id: 'profound_query_fanouts',
name: 'Profound Query Fanouts',
description:
'Query fanout report showing how AI models expand prompts into sub-queries in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
categoryId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Category ID (UUID)',
},
startDate: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Start date (YYYY-MM-DD or ISO 8601)',
},
endDate: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'End date (YYYY-MM-DD or ISO 8601)',
},
metrics: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Comma-separated metrics: fanouts_per_execution, total_fanouts, share',
},
dimensions: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated dimensions: prompt, query, model, region, date',
},
dateInterval: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Date interval: hour, day, week, month, year',
},
filters: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'JSON array of filter objects',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results (default 10000, max 50000)',
},
},
request: {
url: 'https://api.tryprofound.com/v1/reports/query-fanouts',
method: 'POST',
headers: (params) => ({
'X-API-Key': params.apiKey,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
category_id: params.categoryId,
start_date: params.startDate,
end_date: params.endDate,
metrics: params.metrics.split(',').map((m) => m.trim()),
}
if (params.dimensions) {
body.dimensions = params.dimensions.split(',').map((d) => d.trim())
}
if (params.dateInterval) {
body.date_interval = params.dateInterval
}
if (params.filters) {
try {
body.filters = JSON.parse(params.filters)
} catch {
throw new Error('Invalid JSON in filters parameter')
}
}
if (params.limit != null) {
body.pagination = { limit: params.limit }
}
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to query fanouts report')
}
return {
success: true,
output: {
totalRows: data.info?.total_rows ?? 0,
data: (data.data ?? []).map((row: { metrics: number[]; dimensions: string[] }) => ({
metrics: row.metrics ?? [],
dimensions: row.dimensions ?? [],
})),
},
}
},
outputs: {
totalRows: {
type: 'number',
description: 'Total number of rows in the report',
},
data: {
type: 'json',
description: 'Report data rows with metrics and dimension values',
properties: {
metrics: {
type: 'json',
description: 'Array of metric values matching requested metrics order',
},
dimensions: {
type: 'json',
description: 'Array of dimension values matching requested dimensions order',
},
},
},
},
}
+137
View File
@@ -0,0 +1,137 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundRawLogsParams, ProfoundRawLogsResponse } from './types'
export const profoundRawLogsTool: ToolConfig<ProfoundRawLogsParams, ProfoundRawLogsResponse> = {
id: 'profound_raw_logs',
name: 'Profound Raw Logs',
description: 'Get raw traffic logs with filters for a domain in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Domain to query logs for (e.g. example.com)',
},
startDate: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Start date (YYYY-MM-DD or ISO 8601)',
},
endDate: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'End date (YYYY-MM-DD or ISO 8601). Defaults to now',
},
dimensions: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Comma-separated dimensions: timestamp, method, host, path, status_code, ip, user_agent, referer, bytes_sent, duration_ms, query_params',
},
filters: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'JSON array of filter objects, e.g. [{"field":"path","operator":"contains","value":"/blog"}]',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results (default 10000, max 50000)',
},
},
request: {
url: 'https://api.tryprofound.com/v1/logs/raw',
method: 'POST',
headers: (params) => ({
'X-API-Key': params.apiKey,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
domain: params.domain,
start_date: params.startDate,
metrics: ['count'],
}
if (params.endDate) {
body.end_date = params.endDate
}
if (params.dimensions) {
body.dimensions = params.dimensions.split(',').map((d) => d.trim())
}
if (params.filters) {
try {
body.filters = JSON.parse(params.filters)
} catch {
throw new Error('Invalid JSON in filters parameter')
}
}
if (params.limit != null) {
body.pagination = { limit: params.limit }
}
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to get raw logs')
}
if (Array.isArray(data)) {
return {
success: true,
output: {
totalRows: data.length,
data: data.map((row: { metrics: number[]; dimensions: string[] }) => ({
metrics: row.metrics ?? [],
dimensions: row.dimensions ?? [],
})),
},
}
}
return {
success: true,
output: {
totalRows: data.info?.total_rows ?? 0,
data: (data.data ?? []).map((row: { metrics: number[]; dimensions: string[] }) => ({
metrics: row.metrics ?? [],
dimensions: row.dimensions ?? [],
})),
},
}
},
outputs: {
totalRows: {
type: 'number',
description: 'Total number of log entries',
},
data: {
type: 'json',
description: 'Log data rows with metrics and dimension values',
properties: {
metrics: { type: 'json', description: 'Array of metric values (count)' },
dimensions: {
type: 'json',
description: 'Array of dimension values matching requested dimensions order',
},
},
},
},
}
+146
View File
@@ -0,0 +1,146 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundReferralsReportParams, ProfoundReferralsReportResponse } from './types'
export const profoundReferralsReportTool: ToolConfig<
ProfoundReferralsReportParams,
ProfoundReferralsReportResponse
> = {
id: 'profound_referrals_report',
name: 'Profound Referrals Report',
description:
'Query human referral traffic report with hourly granularity for a domain in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Domain to query referral traffic for (e.g. example.com)',
},
startDate: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Start date (YYYY-MM-DD or ISO 8601)',
},
endDate: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'End date (YYYY-MM-DD or ISO 8601). Defaults to now',
},
metrics: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Comma-separated metrics: visits, last_visit',
},
dimensions: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated dimensions: date, hour, path, referral_source, referral_type',
},
dateInterval: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Date interval: hour, day, week, month, year',
},
filters: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'JSON array of filter objects, e.g. [{"field":"referral_source","operator":"is","value":"openai"}]',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results (default 10000, max 50000)',
},
},
request: {
url: 'https://api.tryprofound.com/v2/reports/referrals',
method: 'POST',
headers: (params) => ({
'X-API-Key': params.apiKey,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
domain: params.domain,
start_date: params.startDate,
metrics: params.metrics.split(',').map((m) => m.trim()),
}
if (params.endDate) {
body.end_date = params.endDate
}
if (params.dimensions) {
body.dimensions = params.dimensions.split(',').map((d) => d.trim())
}
if (params.dateInterval) {
body.date_interval = params.dateInterval
}
if (params.filters) {
try {
body.filters = JSON.parse(params.filters)
} catch {
throw new Error('Invalid JSON in filters parameter')
}
}
if (params.limit != null) {
body.pagination = { limit: params.limit }
}
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to query referrals report')
}
return {
success: true,
output: {
totalRows: data.info?.total_rows ?? 0,
data: (data.data ?? []).map((row: { metrics: number[]; dimensions: string[] }) => ({
metrics: row.metrics ?? [],
dimensions: row.dimensions ?? [],
})),
},
}
},
outputs: {
totalRows: {
type: 'number',
description: 'Total number of rows in the report',
},
data: {
type: 'json',
description: 'Report data rows with metrics and dimension values',
properties: {
metrics: {
type: 'json',
description: 'Array of metric values matching requested metrics order',
},
dimensions: {
type: 'json',
description: 'Array of dimension values matching requested dimensions order',
},
},
},
},
}
+144
View File
@@ -0,0 +1,144 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundSentimentReportParams, ProfoundSentimentReportResponse } from './types'
export const profoundSentimentReportTool: ToolConfig<
ProfoundSentimentReportParams,
ProfoundSentimentReportResponse
> = {
id: 'profound_sentiment_report',
name: 'Profound Sentiment Report',
description: 'Query sentiment report for a category in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
categoryId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Category ID (UUID)',
},
startDate: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Start date (YYYY-MM-DD or ISO 8601)',
},
endDate: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'End date (YYYY-MM-DD or ISO 8601)',
},
metrics: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Comma-separated metrics: positive, negative, occurrences',
},
dimensions: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Comma-separated dimensions: theme, date, region, topic, model, asset_name, tag, prompt, sentiment_type, persona',
},
dateInterval: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Date interval: hour, day, week, month, year',
},
filters: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'JSON array of filter objects, e.g. [{"field":"asset_name","operator":"is","value":"Company"}]',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results (default 10000, max 50000)',
},
},
request: {
url: 'https://api.tryprofound.com/v1/reports/sentiment',
method: 'POST',
headers: (params) => ({
'X-API-Key': params.apiKey,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
category_id: params.categoryId,
start_date: params.startDate,
end_date: params.endDate,
metrics: params.metrics.split(',').map((m) => m.trim()),
}
if (params.dimensions) {
body.dimensions = params.dimensions.split(',').map((d) => d.trim())
}
if (params.dateInterval) {
body.date_interval = params.dateInterval
}
if (params.filters) {
try {
body.filters = JSON.parse(params.filters)
} catch {
throw new Error('Invalid JSON in filters parameter')
}
}
if (params.limit != null) {
body.pagination = { limit: params.limit }
}
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to query sentiment report')
}
return {
success: true,
output: {
totalRows: data.info?.total_rows ?? 0,
data: (data.data ?? []).map((row: { metrics: number[]; dimensions: string[] }) => ({
metrics: row.metrics ?? [],
dimensions: row.dimensions ?? [],
})),
},
}
},
outputs: {
totalRows: {
type: 'number',
description: 'Total number of rows in the report',
},
data: {
type: 'json',
description: 'Report data rows with metrics and dimension values',
properties: {
metrics: {
type: 'json',
description: 'Array of metric values matching requested metrics order',
},
dimensions: {
type: 'json',
description: 'Array of dimension values matching requested dimensions order',
},
},
},
},
}
+422
View File
@@ -0,0 +1,422 @@
import type { ToolResponse } from '@/tools/types'
/** Shared report response shape (visibility, sentiment, citations, bots, referrals, query fanouts, prompt volume) */
interface ProfoundReportResponse extends ToolResponse {
output: {
totalRows: number
data: Array<{
metrics: number[]
dimensions: string[]
}>
}
}
/** Shared report query params for category-based reports */
interface ProfoundCategoryReportParams {
apiKey: string
categoryId: string
startDate: string
endDate: string
metrics: string
dimensions?: string
dateInterval?: string
filters?: string
limit?: number
}
/** Shared report query params for domain-based reports */
interface ProfoundDomainReportParams {
apiKey: string
domain: string
startDate: string
endDate?: string
metrics: string
dimensions?: string
dateInterval?: string
filters?: string
limit?: number
}
// --- Organization endpoints ---
export interface ProfoundListCategoriesParams {
apiKey: string
}
export interface ProfoundListCategoriesResponse extends ToolResponse {
output: {
categories: Array<{
id: string
name: string
}>
}
}
export interface ProfoundListRegionsParams {
apiKey: string
}
export interface ProfoundListRegionsResponse extends ToolResponse {
output: {
regions: Array<{
id: string
name: string
}>
}
}
export interface ProfoundListModelsParams {
apiKey: string
}
export interface ProfoundListModelsResponse extends ToolResponse {
output: {
models: Array<{
id: string
name: string
}>
}
}
export interface ProfoundListDomainsParams {
apiKey: string
}
export interface ProfoundListDomainsResponse extends ToolResponse {
output: {
domains: Array<{
id: string
name: string
createdAt: string
}>
}
}
export interface ProfoundListAssetsParams {
apiKey: string
}
export interface ProfoundListAssetsResponse extends ToolResponse {
output: {
assets: Array<{
id: string
name: string
website: string
alternateDomains: string[] | null
isOwned: boolean
createdAt: string
logoUrl: string
categoryId: string
categoryName: string
}>
}
}
export interface ProfoundListPersonasParams {
apiKey: string
}
export interface ProfoundListPersonasResponse extends ToolResponse {
output: {
personas: Array<{
id: string
name: string
categoryId: string
categoryName: string
persona: {
behavior: { painPoints: string | null; motivations: string | null }
employment: {
industry: string[]
jobTitle: string[]
companySize: string[]
roleSeniority: string[]
}
demographics: { ageRange: string[] }
}
}>
}
}
// --- Category-specific endpoints ---
export interface ProfoundCategoryTopicsParams {
apiKey: string
categoryId: string
}
export interface ProfoundCategoryTopicsResponse extends ToolResponse {
output: {
topics: Array<{
id: string
name: string
}>
}
}
export interface ProfoundCategoryTagsParams {
apiKey: string
categoryId: string
}
export interface ProfoundCategoryTagsResponse extends ToolResponse {
output: {
tags: Array<{
id: string
name: string
}>
}
}
export interface ProfoundCategoryPromptsParams {
apiKey: string
categoryId: string
limit?: number
cursor?: string
orderDir?: string
promptType?: string
topicId?: string
tagId?: string
regionId?: string
platformId?: string
}
export interface ProfoundCategoryPromptsResponse extends ToolResponse {
output: {
totalRows: number
nextCursor: string | null
prompts: Array<{
id: string
prompt: string
promptType: string
topicId: string
topicName: string
tags: Array<{ id: string; name: string }>
regions: Array<{ id: string; name: string }>
platforms: Array<{ id: string; name: string }>
createdAt: string
}>
}
}
export interface ProfoundCategoryAssetsParams {
apiKey: string
categoryId: string
}
export interface ProfoundCategoryAssetsResponse extends ToolResponse {
output: {
assets: Array<{
id: string
name: string
website: string
alternateDomains: string[] | null
isOwned: boolean
createdAt: string
logoUrl: string
}>
}
}
export interface ProfoundCategoryPersonasParams {
apiKey: string
categoryId: string
}
export interface ProfoundCategoryPersonasResponse extends ToolResponse {
output: {
personas: Array<{
id: string
name: string
persona: {
behavior: { painPoints: string | null; motivations: string | null }
employment: {
industry: string[]
jobTitle: string[]
companySize: string[]
roleSeniority: string[]
}
demographics: { ageRange: string[] }
}
}>
}
}
// --- Reports ---
export type ProfoundVisibilityReportParams = ProfoundCategoryReportParams
export type ProfoundVisibilityReportResponse = ProfoundReportResponse
export type ProfoundSentimentReportParams = ProfoundCategoryReportParams
export type ProfoundSentimentReportResponse = ProfoundReportResponse
export type ProfoundCitationsReportParams = ProfoundCategoryReportParams
export type ProfoundCitationsReportResponse = ProfoundReportResponse
export type ProfoundQueryFanoutsParams = ProfoundCategoryReportParams
export type ProfoundQueryFanoutsResponse = ProfoundReportResponse
export type ProfoundBotsReportParams = ProfoundDomainReportParams
export type ProfoundBotsReportResponse = ProfoundReportResponse
export type ProfoundReferralsReportParams = ProfoundDomainReportParams
export type ProfoundReferralsReportResponse = ProfoundReportResponse
// --- Prompts ---
export interface ProfoundPromptAnswersParams {
apiKey: string
categoryId: string
startDate: string
endDate: string
filters?: string
limit?: number
}
export interface ProfoundPromptAnswersResponse extends ToolResponse {
output: {
totalRows: number
data: Array<{
prompt: string | null
promptType: string | null
response: string | null
mentions: string[] | null
citations: string[] | null
topic: string | null
region: string | null
model: string | null
asset: string | null
createdAt: string | null
}>
}
}
// --- Agent Analytics ---
export interface ProfoundRawLogsParams {
apiKey: string
domain: string
startDate: string
endDate?: string
dimensions?: string
filters?: string
limit?: number
}
export interface ProfoundRawLogsResponse extends ToolResponse {
output: {
totalRows: number
data: Array<{
metrics: number[]
dimensions: string[]
}>
}
}
export interface ProfoundBotLogsParams {
apiKey: string
domain: string
startDate: string
endDate?: string
dimensions?: string
filters?: string
limit?: number
}
export interface ProfoundBotLogsResponse extends ToolResponse {
output: {
totalRows: number
data: Array<{
metrics: number[]
dimensions: string[]
}>
}
}
// --- Content ---
export interface ProfoundListOptimizationsParams {
apiKey: string
assetId: string
limit?: number
offset?: number
}
export interface ProfoundListOptimizationsResponse extends ToolResponse {
output: {
totalRows: number
optimizations: Array<{
id: string
title: string
createdAt: string
extractedInput: string | null
type: string
status: string
}>
}
}
export interface ProfoundOptimizationAnalysisParams {
apiKey: string
assetId: string
contentId: string
}
export interface ProfoundOptimizationAnalysisResponse extends ToolResponse {
output: {
content: {
format: string
value: string
}
aeoContentScore: {
value: number
targetZone: { low: number; high: number }
} | null
analysis: {
breakdown: Array<{
title: string
weight: number
score: number
}>
}
recommendations: Array<{
title: string
status: string
impact: { section: string; score: number } | null
suggestion: { text: string; rationale: string }
}>
}
}
// --- Prompt Volumes ---
export interface ProfoundPromptVolumeParams {
apiKey: string
startDate: string
endDate: string
metrics: string
dimensions?: string
dateInterval?: string
filters?: string
limit?: number
}
export interface ProfoundPromptVolumeResponse extends ToolResponse {
output: {
totalRows: number
data: Array<{
metrics: number[]
dimensions: string[]
}>
}
}
export interface ProfoundCitationPromptsParams {
apiKey: string
inputDomain: string
}
export interface ProfoundCitationPromptsResponse extends ToolResponse {
output: {
data: unknown
}
}
@@ -0,0 +1,145 @@
import type { ToolConfig } from '@/tools/types'
import type { ProfoundVisibilityReportParams, ProfoundVisibilityReportResponse } from './types'
export const profoundVisibilityReportTool: ToolConfig<
ProfoundVisibilityReportParams,
ProfoundVisibilityReportResponse
> = {
id: 'profound_visibility_report',
name: 'Profound Visibility Report',
description: 'Query AI visibility report for a category in Profound',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Profound API Key',
},
categoryId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Category ID (UUID)',
},
startDate: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Start date (YYYY-MM-DD or ISO 8601)',
},
endDate: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'End date (YYYY-MM-DD or ISO 8601)',
},
metrics: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'Comma-separated metrics: share_of_voice, mentions_count, visibility_score, executions, average_position',
},
dimensions: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Comma-separated dimensions: date, region, topic, model, asset_name, prompt, tag, persona',
},
dateInterval: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Date interval: hour, day, week, month, year',
},
filters: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'JSON array of filter objects, e.g. [{"field":"asset_name","operator":"is","value":"Company"}]',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results (default 10000, max 50000)',
},
},
request: {
url: 'https://api.tryprofound.com/v1/reports/visibility',
method: 'POST',
headers: (params) => ({
'X-API-Key': params.apiKey,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
category_id: params.categoryId,
start_date: params.startDate,
end_date: params.endDate,
metrics: params.metrics.split(',').map((m) => m.trim()),
}
if (params.dimensions) {
body.dimensions = params.dimensions.split(',').map((d) => d.trim())
}
if (params.dateInterval) {
body.date_interval = params.dateInterval
}
if (params.filters) {
try {
body.filters = JSON.parse(params.filters)
} catch {
throw new Error('Invalid JSON in filters parameter')
}
}
if (params.limit != null) {
body.pagination = { limit: params.limit }
}
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.detail?.[0]?.msg || 'Failed to query visibility report')
}
return {
success: true,
output: {
totalRows: data.info?.total_rows ?? 0,
data: (data.data ?? []).map((row: { metrics: number[]; dimensions: string[] }) => ({
metrics: row.metrics ?? [],
dimensions: row.dimensions ?? [],
})),
},
}
},
outputs: {
totalRows: {
type: 'number',
description: 'Total number of rows in the report',
},
data: {
type: 'json',
description: 'Report data rows with metrics and dimension values',
properties: {
metrics: {
type: 'json',
description: 'Array of metric values matching requested metrics order',
},
dimensions: {
type: 'json',
description: 'Array of dimension values matching requested dimensions order',
},
},
},
},
}