# Tools Reference

Technical reference for the CrowdListen Harness MCP tools (v3). The harness exposes **3 tools** and **8 skills**. Tools are the primitives agents call directly. Skills are SKILL.md workflow packs that teach agents expert methodology for composing those primitives.

**Auth model**: All tools authenticate via Supabase session token stored at `~/.crowdlisten/auth.json`. Sign in with `npx -y -p @crowdlisten/harness crowdlisten-harness login`.

**Error responses**: All tool failures return structured errors with `error` (what happened), `suggestion` (what to do), and `docs` (link to relevant docs).

---

## Shared saved skills

Company analysis definitions in Insights can be read and configured through `recall` modes `skills`, `routines`, `skill_runs`, `ingest` destinations `skills`/`routine`, and `analyze` action `skill`. These are shared records, distinct from the packaged methodology guides. See [saved skills, one-off execution and schedules](SAVED_SKILLS.md), including required deployment versions and spending authorization.

## Tool Summary

| Tool | Purpose | Backend |
|------|---------|---------|
| `recall` | Search across all CrowdListen data | Hybrid (direct DB + agent proxy) |
| `analyze` | Run audience analysis and synthesis | Agent-proxied |
| `ingest` | Upload content or submit feedback | Direct DB + agent proxy |

---

## `recall`

Search CrowdListen data through explicit retrieval modes. Versioned synthesized insights use `knowledge`; original sources/snapshots use `knowledge_source`; incremental revision reads use `knowledge_changes`. These require `entity_id` and the matching backend/migration. [Full knowledge contract](KNOWLEDGE_RECORDS.md).

### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `mode` | string | No | What to recall: `search`, `entities`, `workspaces`, `insights`, `actions`, `intelligence`, `content`, `knowledge`, `knowledge_source`, `knowledge_changes`. Default: `search` |
| `query` | string | No | Search query (for `search` and `content` modes) |
| `entity_name` | string | No | Entity name for `insights`/`intelligence`/`content` mode (fuzzy match) |
| `entity_id` | string | No | Entity UUID (if known) |
| `window_days` | number | No | Intelligence window from 7 to 365 days. Default: `30` |
| `category` | string | No | Filter insights by category: `churn`, `opportunity`, `competitive`, `visibility`, `pain_point`, `feature_request`, `marketing_narrative` |
| `include_evidence` | boolean | No | Include source evidence with insights. Default: `true` |
| `limit` | number | No | Max results. Default: `20` |
| `project_id` | string | No | Scope to project (optional) |
| `tags` | string[] | No | Filter by tags (optional) |

### Mode: `search` (default)

Semantic search across KB pages, research docs, and content_store. Falls back to keyword search if the agent proxy is unavailable.

```
recall({ query: "pricing complaints" })
recall({ query: "onboarding friction", project_id: "abc-123", tags: ["ux"] })
```

**Returns**: `{ results: [{ id, path, title, content, tags, updated_at }], source }` — source is `"semantic"` or `"keyword_fallback"`.

### Mode: `entities`

List all tracked entities (companies, products, competitors) with their workspace assignments.

```
recall({ mode: "entities" })
```

**Returns**: `{ entities: [{ id, name, type, slug, description, keywords, workspace_id, workspace }], count }`

### Mode: `workspaces`

List all workspaces with their entities.

```
recall({ mode: "workspaces" })
```

**Returns**: `{ workspaces: [{ id, name, entities: [{ id, name, type, slug }] }] }`

### Mode: `insights`

Get pre-computed audience insights for an entity WITH source evidence (quotes, platforms, dates). This is the primary way to retrieve structured intelligence about a tracked entity.

```
recall({ mode: "insights", entity_name: "Acme" })
recall({ mode: "insights", entity_id: "uuid-here", category: "pain_point" })
recall({ mode: "insights", entity_name: "Vizard", include_evidence: false })
```

**Requires**: Either `entity_name` or `entity_id`.

**Returns**: `{ entity_id, count, insights: [{ id, title, description, category, urgency, impact_score, impact_label, signal_count, affected_segments, sentiment, suggested_actions, trend, pinned, created_at, evidence: [{ snippet, platform, source_url, published_at, relevance }] }] }`

### Mode: `intelligence`

Returns required-channel coverage, current-versus-prior stored source volume,
evidence-backed competitor signal gaps, specific tracked-account opinions, and
official/employee/advocate content performance. Public web discovery remains
labeled `sampled`; only cursor-backed collectors that record `reached_end` can
claim exhaustive coverage.

```
recall({ mode: "intelligence", entity_name: "Fireworks AI", window_days: 30 })
recall({ mode: "intelligence", entity_id: "uuid-here", window_days: 90 })
```

**Requires**: Either `entity_name` or `entity_id`.

### Mode: `actions`

Returns the persisted Product or Marketing Actions. This is the agent-facing
view of the same objects shown in the signed-in CrowdListen home; it does not
re-run source extraction or analysis.

```
recall({ mode: "actions", entity_id: "uuid-here" })
recall({ mode: "actions", entity_id: "uuid-here", object_type: "pain_point" })
recall({ mode: "actions", entity_id: "uuid-here", status: "acting" })
```

**Requires**: `entity_id`.

**Returns**: `{ entity_id, mode, object_types, objects: [{ id, object_type, title, summary, status, urgency, impact_score, signal_count, evidence, payload: { action_plan }, last_seen_at }], count }`. A saved `action_plan` includes the organization skill, objective, execution steps, owner, timeframe, success metric, explicit planning assumptions, and labeled projected reach and impact.

### Mode: `content`

Search raw content_store entries by entity and/or query. Returns original social posts, articles, and comments.

```
recall({ mode: "content", entity_name: "Acme", query: "pricing" })
recall({ mode: "content", entity_id: "uuid-here", limit: 50 })
```

**Returns**: `{ results: [{ id, title, platform, source_url, published_at, content }], count }`

---

## `analyze`

Run audience analysis. Searches the knowledge base (or live web) for crowd signals, synthesizes themes and sentiment, and returns insight cards.

### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `action` | string | No | `run` (default), `start` a durable entity-research job, or inspect `status` |
| `question` | string | For `run`/`start` | Research question |
| `job_id` | string | For `status` | Durable research job UUID |
| `project_id` | string | No | Project UUID (optional) |
| `entity_id` | string | No | Entity UUID used to scope stored evidence and persist resulting insights |
| `search_mode` | string | No | `knowledge` (default -- search stored content) or `multi_source` (live web search across platforms) |
| `platforms` | string[] | No | Platforms for `multi_source` mode: `reddit`, `youtube`, `tiktok`, `twitter`, `linkedin`, `instagram`, `hackernews`, `producthunt`, `web` |
| `depth` | string | No | `brief` (default) or `deep` (extract comments + transcripts) |

### Examples

Search existing knowledge base:
```
analyze({ question: "What do users think about our pricing?" })
analyze({ question: "Compare Acme vs Globex in developer sentiment", project_id: "abc-123" })
```

Durable entity research:
```
analyze({
  action: "start",
  question: "Where does onboarding fail?",
  entity_id: "entity-uuid",
  platforms: ["reddit", "web"],
  depth: "deep"
})
analyze({ action: "status", job_id: "research-job-uuid" })
```

Live web search:
```
analyze({
  question: "What are the top complaints about Figma's AI features?",
  search_mode: "multi_source",
  platforms: ["reddit", "twitter", "hackernews"],
  depth: "deep"
})
```

**Returns**: `run` returns streamed analysis results with themes, sentiment, insight cards, and evidence citations. `start` returns a durable job ID; `status` returns `queued`, `running`, `completed`, `partial`, or `failed` plus the final ingestion and enrichment result when available.

---

## `ingest`

Upload content to CrowdListen. Has four routing paths depending on parameters.

### Route 1: Save to Knowledge Base

Save notes, research, or documents. Content is auto-embedded for semantic search.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `title` | string | **Yes** | Content title |
| `content` | string | **Yes** | Content to save (markdown) |
| `path` | string | No | Storage path (e.g., `notes/my-note`, `research/entity-name`). Auto-generated from title if omitted. |
| `tags` | string[] | No | Tags for filtering and discovery |
| `project_id` | string | No | Project/workspace UUID |
| `entity_id` | string | No | Optional entity reference for the curated page |

```
ingest({
  title: "Pricing Research Summary",
  content: "## Key Findings\n\nUsers consistently report...",
  tags: ["research", "pricing"],
  project_id: "abc-123"
})

ingest({
  title: "Competitor Battlecard: Globex",
  content: "# Globex vs Acme\n\n...",
  path: "research/globex-battlecard",
  entity_id: "entity-uuid"
})
```

**Returns**: `{ ingested: true, kind, title, id }`

### Route 2: Add Evidence to the Enrichment Layer

Send an approved agent workflow trace, meeting note, Slack decision, or other entity-scoped record into the canonical evidence store. This route persists to `content_store` and triggers extraction into opinions, insights, and Actions. It does not passively capture agent conversations.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `destination` | `"evidence"` | **Yes** | Select the evidence path |
| `entity_id` | string | **Yes** | Entity whose business context should compound |
| `title` | string | **Yes** | Evidence title |
| `content` | string | **Yes** | Approved source text or workflow outcome |
| `source_platform` | string | No | `agent`, `codex`, `claude_code`, `slack`, `granola`, or `web`; defaults to `agent` |
| `source_url` | string | No | Original URL or stable trace URI |
| `author` | string | No | Source author, participant, or executor |
| `published_at` | string | No | Original ISO 8601 timestamp |
| `metadata` | object | No | Provenance, task, tools, outcome, approval, and sensitivity labels |

```
ingest({
  destination: "evidence",
  entity_id: "entity-uuid",
  title: "Renewal escalation workflow",
  content: "Accounts above $10k ARR receive a same-day owner...",
  source_platform: "claude_code",
  source_url: "claude-code://session/trace-123",
  metadata: { outcome: "approved", sensitivity: "internal" }
})
```

**Returns**: the ingestion counts, a stable `collection_run_id`, and a persisted terminal sweep result including `run_id`, `status`, `stage`, and pipeline/materialization statistics. A caller should require the sweep result to be `completed` before relying on a new Insight or Action.

### Route 3: Create or Update an Action Execution

Create an auditable handoff only after a human has approved the bounded steps:

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `destination` | `"execution"` | **Yes** | Select the execution path |
| `action_id` | string | On create | Action receiving the approved execution handoff |
| `execution_id` | string | On update | Existing execution to update |
| `executor_platform` | string | On create | `agent`, `codex`, `claude_code`, `human`, or `other` |
| `approved_for_execution` | `true` | On create | Explicit human approval gate |
| `steps` | string[] | On create | Ordered approved steps |
| `execution_status` | string | No | `approved`, `running`, `completed`, `failed`, or `cancelled` |
| `step_results` | object[] | On completion | Bounded `{ step, status, result? }` records |
| `outcome_summary` | string | On completion | Observable result, not a projection |
| `metrics` | object | No | Measured outcome values |
| `review_status` | string | Owner review | `pending`, `approved`, or `rejected` |
| `training_scopes` | string[] | Owner review | Explicit portable-use scopes |

```js
ingest({
  destination: "execution",
  action_id: "action-uuid",
  executor_platform: "codex",
  approved_for_execution: true,
  steps: ["Prepare the approved segment", "Draft, but do not send, the outreach"]
})
```

Creation requires `action_id`, `executor_platform`, at least one non-empty step, and `approved_for_execution: true`. It posts to the Action-scoped execution endpoint and returns the persisted execution.

After the host performs the approved work, update the same execution. This branch does not require `title` or `content`:

```js
ingest({
  destination: "execution",
  execution_id: "execution-uuid",
  execution_status: "completed",
  step_results: [{ step: "Prepare the approved segment", status: "completed", result: "Prepared 12 accounts" }],
  outcome_summary: "The segment was prepared; no message was sent.",
  metrics: { accounts_prepared: 12, messages_sent: 0 },
  review_status: "approved",
  training_scopes: ["internal_harness"]
})
```

An update requires `execution_id` and at least one status, result, outcome, metric, review, or training-scope change. `training_scopes` may contain `internal_harness`, `external_harness`, or `fine_tuning`. A persisted scope does not by itself export data; the reviewed-execution converter also requires a caller-side execution-ID allowlist.

### Route 4: Submit Feedback Observations

Submit raw feedback signals (feature requests, bug reports, pain points, praise) that auto-classify into themes.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `observations` | array | **Yes** | Array of 1-50 observation objects |
| `observations[].content` | string | **Yes** | Observation text |
| `observations[].source_platform` | string | No | Platform origin: `discord`, `slack`, `manual`, etc. |
| `observations[].observation_type` | string | No | `feature_request`, `bug_report`, `pain_point`, `praise`, `question`, `general` |
| `observations[].metadata` | object | No | Additional context (author, channel, thread_id, etc.) |
| `project_id` | string | No | Project UUID |
| `entity_id` | string | No | Entity UUID to tag observations |

```
ingest({
  observations: [
    {
      content: "Dashboard keeps crashing when I filter by date range",
      source_platform: "slack",
      observation_type: "bug_report",
      metadata: { channel: "#product-feedback", author: "user123" }
    },
    {
      content: "Would love CSV export for the analytics page",
      source_platform: "discord",
      observation_type: "feature_request"
    }
  ],
  project_id: "abc-123",
  entity_id: "entity-uuid"
})
```

**Returns**: Observation submission result from the backend pipeline.

**Routing logic**: `observations` submits feedback first. `destination: "execution"` creates or updates a structured execution before title/content validation. Other calls require `title` + `content` and route to evidence enrichment or curated context.

---

## Skills (8 SKILL.md Workflow Packs)

Skills contain no tools. They deliver expert methodology and step-by-step workflow instructions when activated. The agent receives the SKILL.md content as context and composes the 3 tools above to execute the workflow.

| Skill | Description |
|-------|-------------|
| `audience-research` | Full-cycle crowd intelligence -- from research question to compiled truth. Teaches agents WHEN to research, HOW to evaluate evidence, and WHERE to store knowledge so it compounds. |
| `competitive-analysis` | Research competitors and generate a positioning and messaging comparison with content gaps, opportunities, and threats. Sales battlecards, positioning gaps, competitor impact assessment. |
| `content-strategy` | Data-driven content strategy grounded in audience demand. Topic demand analysis, content gaps, platform optimization, voice matching, campaign tracking. |
| `user-stories` | Turn social listening data into product decisions. JTBD extraction, feature demand scoring, persona generation, user story generation from real audience data. |
| `product-spec` | Write a feature spec or PRD from a problem statement or feature idea. Structured goals, non-goals, success metrics, acceptance criteria, phased delivery. |
| `support-triage` | Triage and prioritize a support ticket or customer issue. P1-P4 categorization, team routing, duplicate detection, initial response suggestion. |
| `support-response` | Draft a professional customer-facing response tailored to the situation and relationship. Escalations, outages, bad news, feature declines, billing issues. |
| `support-docs` | Draft a knowledge base article from a resolved issue or common question. Self-service documentation for recurring tickets and known workarounds. |

### Skill Activation

All skills compose the 3 core tools (`recall`, `analyze`, `ingest`). No additional tools are unlocked -- the skill provides workflow instructions that guide the agent through the research process.

## Public provider discovery (requires matching harness release)

`recall({mode: "discover", query: "Jev", platforms: ["twitter"], limit: 40, date_from: "2026-09-15"})` calls the authenticated CrowdListen source provider API. This consumes the configured provider's access/credits, returns bounded results and does not automatically ingest or analyze them. Inspect `discovery_provider`, `direct_provider` and collection coverage. Search context is not original post text; capture the actual source before citing it. X currently returns a bounded first page and does not establish complete date-range coverage or trending growth. Omit entity/project/workspace scope and pagination fields; choose ownership when saving via ingest.

Check the installed recall schema for discover. If absent, update to the matching released harness; do not claim the local checkout changed the published package. The equivalent existing HTTP route is `POST /agent/v1/search`, using an authorized session from secure client configuration.
