@signal4/s4
v2.6.1
Published
Signal4 media intelligence — CLI and MCP server for AI agents
Maintainers
Readme
@signal4/s4
Media intelligence platform — CLI, MCP server, and TypeScript client.
Search quotes, analyze discourse, track trends, and explore speakers across thousands of hours of audiovisual content.
Architecture
This package contains three surfaces backed by one HTTP client:
@signal4/s4
├── client.ts ← Typed HTTP client (38 functions wrapping the REST API)
├── types.ts ← TypeScript types for all API responses
├── config.ts ← Config loader (~/.signal4/config)
├── cli.ts ← Terminal interface (s4 search, s4 speaker, ...)
├── index.ts ← MCP server for Claude Desktop / Cursor
└── handlers/ ← Human-readable formatters (shared by CLI + MCP)Client is the core — everything else is a consumer. Use it directly for programmatic access; use the CLI or MCP server for interactive use.
| Surface | Entry point | Reads from | Formats with |
|---------|-------------|------------|-------------|
| TypeScript client | import from '@signal4/s4/client' | client.ts | — (raw typed responses) |
| CLI | npx @signal4/s4 search ... | client.ts | handlers/ → text |
| MCP server | s4 --mcp | client.ts | handlers/ → text |
TypeScript Client
For applications that need typed access to the Signal4 API (frontends, agents, pipelines, scripts):
import { searchQuotes, getSpeaker, analyze } from '@signal4/s4/client';
import type { QuoteSearchResult, SpeakerProfile } from '@signal4/s4/types';
// Search for quotes about a topic
const results = await searchQuotes({
topic: 'AI regulation',
speaker_name: 'Tucker Carlson',
limit: 10,
start_date: '2026-05-01',
});
// Get a speaker profile
const speaker = await getSpeaker(12345);
// Run an AI analysis
const analysis = await analyze({
query: 'How are podcasters talking about tariffs?',
workflow: 'deep_analysis',
days: 30,
});Sub-path exports
import { searchQuotes, detectTrend, ... } from '@signal4/s4/client'; // HTTP client
import type { Quote, SpeakerProfile, ... } from '@signal4/s4/types'; // TypeScript types
import { applyConfig } from '@signal4/s4/config'; // Config loaderConfiguration
The client reads from environment variables:
| Variable | Purpose | Default |
|----------|---------|---------|
| SIGNAL4_API_KEY | API key (required) | — |
| SIGNAL4_API_URL | API base URL | https://api.signal4.ca |
| SIGNAL4_CLIENT_ID | Client identifier for analytics | — |
applyConfig() loads these from ~/.signal4/config if not already set in the environment. The CLI and MCP server call this automatically; programmatic consumers should call it explicitly if they want the file-based fallback.
Client functions
Search
searchQuotes(params)— Semantic/keyword search across transcriptssearchClaims(params)— Search extracted claims with stance and observation countsdetectTrend(params)— Compare recent vs baseline mention volumegetTimeline(params)— Topic mention volume over time
Entities
searchSpeakers(query, limit?)— Autocomplete speaker searchgetSpeaker(id)/getSpeakerByName(name)— Speaker profilegetSpeakerSegments(params)— Recent segments by speakersearchSpeakerSegments(params)— Semantic search within a speaker's segmentsgetChannel(id)/getChannelByName(name)— Channel profilegetEpisode(contentId)— Episode detailsgetTranscript(contentId)— Full episode transcriptgetSegment(segmentId)— Single segment with audio clip URLgetSegmentThread(segmentId, n?)— Conversation context around a segment
Analysis (SSE streaming, handled internally)
analyze(params)— AI-powered analysis (workflows:simple_rag,deep_analysis,speaker_compare,speaker_dossier,contradiction_check)
Reports
getTopSpeakers(params)/getTopChannels(params)— LeaderboardsgetSpeakerNetwork(params)— Co-appearance network graphgetTopics(params)— Trending topic clustersgetReportStats(days?)/getProjectStats()— Platform statisticsgetDiscourseSummary(params)— AI discourse briefing
Claims
getClaim(claimId)— Full claim detailgetClaimObservations(params)— Observations supporting a claimgetRelatedClaims(claimId)— Similar/related claims
Ads
getAdStats(days?)/getAdvertisers(params)/getAdvertiser(id)— Ad intelligencegetAdsByChannel(channelId, days?)/getAdTrends(days?)— Channel-level and trending ads
Bookmarks
createBookmark(params)/listBookmarks(params)/deleteBookmark(id)
Auth
whoami()— Verify key and see access scope
Error handling
All functions throw on error. HTTP 502/503 are retried automatically (3 attempts with backoff). Wrap calls in try/catch:
try {
const result = await searchQuotes({ topic: 'tariffs' });
} catch (err) {
console.error((err as Error).message); // "HTTP 401: ..."
}Installing as a dependency
For projects within the signal4 monorepo:
pnpm add @signal4/s4@file:../mcpFor external projects (once published):
npm install @signal4/s4CLI
npm install -g @signal4/s4
s4 setup # Save your API key to ~/.signal4/config
s4 whoami # Verify access and see available commandsCommands
# Search
s4 search "immigration" --speaker "Tucker Carlson"
s4 claims "AI regulation" --entity "OpenAI"
s4 trend "tariffs"
s4 timeline "carbon tax" --days 90
# Explore
s4 speaker "Joe Rogan"
s4 speaker "Tucker Carlson" "economy" # discourse search
s4 channel "Power & Politics"
s4 episode pod_8f1e55513f70 --transcript
s4 segment 28553756
s4 thread 28553756
# Analyze (AI-powered, slower)
s4 analyze "carbon tax" --days 14
s4 frames "midterm elections" --days 30
s4 compare "tariffs" --speakers "Trump,Biden"
s4 dossier "immigration" --speaker "Carlson"
s4 contradictions "vaccines" --speaker "Trump"
s4 briefing
# Reports
s4 stats
s4 top-speakers --ranking talkative
s4 top-channels
s4 network
s4 topics
# Ads
s4 ads --trending
# Bookmarks
s4 bookmarks
s4 bookmarks add segment 12345 --note "key quote"All commands support --json for raw JSON output (bypasses the handler formatting layer and calls the client directly).
MCP Server
For Claude Desktop, Cursor, or other MCP clients:
{
"mcpServers": {
"signal4": {
"command": "npx",
"args": ["-y", "@signal4/s4", "--mcp"],
"env": {
"SIGNAL4_API_KEY": "your-api-key" # pragma: allowlist secret
}
}
}
}Tool definitions are loaded dynamically from the API based on your key's access level. The MCP server uses the same handlers as the CLI for formatting responses.
Remote server (streamable-http)
The same server is also deployable as a remote streamable-http service (this replaced the retired Python MCP server in core/src/mcp/):
node dist/http.js --host 127.0.0.1 --port 7998- Env:
SIGNAL4_API_URL— backend base URL (defaulthttps://api.signal4.ca; PM2 deployments sethttp://localhost:7999) - MCP endpoint:
POST /mcp - Auth: the client's own Signal4 API key (
sk_...), sent asX-API-Key: <key>orAuthorization: Bearer <key>. The server needs no service key and no database access — the key passes through to the backend, which validates it, enforces scopes/projects, rate-limits, and records usage. - Health check:
GET /health→{"status": "ok", "service": "signal4-mcp", "transport": "http"}
In production it runs under PM2 as signal4-mcp (on-prem: core/ecosystem.config.js, bound 0.0.0.0:7998; VPS: ~/signal4/start-mcp.sh synced from the parent repo's scripts/start-mcp.vps.sh, bound 127.0.0.1:7998).
Requirements
- Node.js 18+
- A Signal4 API key (signal4.ca)
