Investors MCP Server
Enables semantic search and ingestion of investor content using Upstash Vector and Vercel Blob storage, with optional filtering by author, company, and date.
README
Public reference fork — sanitized copy of Elephant's internal investors-mcp for X Engagement Reply Agent candidates. Operational watchlists, scraped data, and internal profiles are excluded. See docs/public-reference.md.
Investors MCP Server
A Next.js-based MCP (Model Context Protocol) server that provides semantic search over investor content using Upstash Vector and Vercel Blob storage.
Uses mcp-handler
Features
- Semantic Search: Query investor content using natural language via
queryInvestorContenttool - Content Ingestion: Add new paragraphs via
addInvestorParagraphtool (authenticated) - Author Filtering: Optionally filter results by investor/author name
- Temporal Filtering: Optionally filter by publication date (
dateFrom/dateTo), original content creation datetime (contentCreatedAtFrom/contentCreatedAtTo), and indexed record creation datetime (createdAtFrom/createdAtTo) - RAG-Ready: Returns full document content from Vercel Blob for retrieval-augmented generation
- Streamable HTTP Transport: Built for modern MCP clients
Deploy to Vercel
Quick Deploy Steps
-
Deploy the project to Vercel via the button above or by connecting your repo
-
Add Upstash Vector from Vercel Marketplace:
- Go to your project's Storage tab in Vercel dashboard
- Click Connect Store → Create New → Upstash Vector
- Important: Create an index with 3072 dimensions (for
text-embedding-3-large) - This automatically sets
UPSTASH_VECTOR_REST_URLandUPSTASH_VECTOR_REST_TOKEN
-
Add Vercel Blob storage:
- In the Storage tab, click Connect Store → Create New → Blob
- This automatically sets
BLOB_READ_WRITE_TOKEN
-
Add AI Gateway API Key:
- Go to Vercel AI Gateway and create an API key
- In your project's Settings → Environment Variables, add:
AI_GATEWAY_API_KEY= your API key
-
Redeploy to pick up the new environment variables
Environment Variables
| Variable | Source | Description |
|---|---|---|
AI_GATEWAY_API_KEY |
Vercel AI Gateway | Required for embeddings |
UPSTASH_VECTOR_REST_URL |
Vercel Marketplace (auto) | Upstash Vector endpoint |
UPSTASH_VECTOR_REST_TOKEN |
Vercel Marketplace (auto) | Upstash Vector auth token |
BLOB_READ_WRITE_TOKEN |
Vercel Storage (auto) | Vercel Blob access token |
MCP_WRITE_TOKEN |
Self-generated | Optional. Bearer token for write operations (addInvestorParagraph) |
X_BEARER_TOKEN |
X Developer Portal | Optional. Enables authenticated X API article/full-text enrichment in ingestion scripts |
AUTOMATION_TRIGGER_TOKEN |
Self-generated | Bearer token for /api/automation/monitor-x trigger endpoint |
CRON_SECRET |
Self-generated | Optional. If set, Vercel Cron can authenticate to trigger endpoint |
ASANA_ACCESS_TOKEN |
Asana | Required to create tasks from monitored posts |
ASANA_PROJECT_GID |
Asana | Project where monitored-post tasks are created |
ASANA_ASSIGNEE_GID |
Asana | Optional default assignee for created tasks |
ASANA_ARTICLE_THRESHOLD_ASSIGNEE_GID |
Asana | Optional assignee when a post meets the article recommendation threshold |
ASANA_SECTION_GID |
Asana | Optional section to place newly created tasks into |
ASANA_WORKSPACE_GID |
Asana | Optional workspace override for task creation |
ASANA_SIMILARITY_SCORE_CUSTOM_FIELD_GID |
Asana | Optional shared number custom field GID for both parent tasks and recommendation subtasks |
ASANA_TASK_SIMILARITY_SCORE_CUSTOM_FIELD_GID |
Asana | Optional parent-task-specific number custom field GID; overrides the shared GID for parent tasks |
ASANA_SUBTASK_SIMILARITY_SCORE_CUSTOM_FIELD_GID |
Asana | Optional subtask-specific number custom field GID; overrides the shared GID for recommendation subtasks |
AUTOMATION_WATCH_AUTHORS |
Optional list | Optional comma-separated subset of watched authors |
RAG_AUTOREPLY_MODEL |
Optional | Legacy draft-model override (currently unused by Asana task creation) |
X_MONITOR_TWSTALKER_ORIGIN |
Optional | TwStalker base URL override (default https://twstalker.com) |
When the similarity-score custom field env vars are configured, the Asana create flow writes the raw vector similarity value into Asana:
- parent task: highest recommended-article raw similarity score
- recommendation subtask: that subtask's article raw similarity score
Generating MCP_WRITE_TOKEN
To enable write operations, generate a secure random token:
# Linux/macOS
openssl rand -hex 32
# Or with Node.js
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
Add the generated token as MCP_WRITE_TOKEN in your Vercel Environment Variables. Clients must include this token in the Authorization: Bearer <token> header when calling write tools.
Index Requirements
Your Upstash Vector index must be 3072 dimensions to match openai/text-embedding-3-large embeddings.
Available Tools
echo
Simple echo tool for testing connectivity.
Parameters:
message(string, required): Message to echo back
queryInvestorContent
Query relevant investor content using semantic search.
Parameters:
query(string, required): The search query to find relevant investor contentauthor(string, optional): Author name to filter results (e.g., "Paul Graham", "Marc Andreessen")company(string, optional): Company name to filter results (e.g., "16z", "Y Combinator")dateFrom(string, optional): Publication date lower bound, inclusive (YYYY-MM-DD)dateTo(string, optional): Publication date upper bound, inclusive (YYYY-MM-DD)contentCreatedAtFrom(string, optional): Original content creation datetime lower bound, inclusive (ISO 8601 datetime with timezone)contentCreatedAtTo(string, optional): Original content creation datetime upper bound, inclusive (ISO 8601 datetime with timezone)createdAtFrom(string, optional): Record creation-time lower bound, inclusive (ISO 8601 datetime with timezone)createdAtTo(string, optional): Record creation-time upper bound, inclusive (ISO 8601 datetime with timezone)topK(number, optional): Number of results to return (1-20, default: 5)
Response Structure:
{
"query": "what makes a good startup idea?",
"author": "Paul Graham",
"authorNormalized": "paulgraham",
"company": "Y Combinator",
"topK": 5,
"matchCount": 3,
"matches": [
{
"id": "essay-123",
"score": 0.89,
"key": "investors/paulgraham/essay-123.json",
"metadata": {
"blobKey": "investors/paulgraham/essay-123.json",
"authorNormalized": "paulgraham",
"company": "Y Combinator",
"date": "2012-11-01"
},
"blob": {
"title": "How to Get Startup Ideas",
"content": "...",
"author": "Paul Graham",
"company": "Y Combinator",
"date": "2012-11-01"
}
}
]
}
addInvestorParagraph
Add a new investor paragraph to the knowledge base. Requires authentication with write:investors scope.
Authentication:
Include Authorization: Bearer <MCP_WRITE_TOKEN> header in your MCP client request.
Parameters:
content(string, required): The paragraph text content (10-10000 characters)author(string, required): The primary author name (e.g., "Paul Graham")company(string, optional): Company/organization name (e.g., "16z", "Y Combinator")date(string, optional): Publication date in ISO format (YYYY-MM-DD)contentCreatedAt(string, optional): Original content creation datetime in ISO 8601 format with timezonetitle(string, optional): Title or context for the paragraphauthors(string[], optional): List of all authors if multiplesourceUri(string, optional): Source URL where the content was originally published
Request Example:
{
"content": "The best startup ideas are often ones that the founders themselves want...",
"author": "Paul Graham",
"company": "Y Combinator",
"date": "2012-11-01",
"contentCreatedAt": "2012-11-01T00:00:00.000Z",
"title": "How to Get Startup Ideas",
"sourceUri": "https://paulgraham.com/startupideas.html"
}
Response Structure:
{
"success": true,
"vectorId": "investors/paulgraham/paragraph-abc123.json",
"blobKey": "investors/paulgraham/paragraph-abc123.json",
"blobUrl": "https://xyz.public.blob.vercel-storage.com/investors/paulgraham/paragraph-abc123.json",
"authorNormalized": "paulgraham",
"company": "Y Combinator",
"date": "2012-11-01",
"contentCreatedAt": "2012-11-01T00:00:00.000Z",
"createdAt": "2024-01-15T10:30:00.000Z"
}
Error Response (Unauthorized):
{
"error": "Unauthorized",
"message": "This tool requires authentication with write:investors scope. Please provide a valid Authorization header with Bearer token."
}
Data Schema
Upstash Vector Metadata
Each vector record should have metadata with:
blobKey(string, required): Key/URL to retrieve full content from Vercel BlobauthorNormalized(string, optional): Normalized author name for filtering (lowercase, no spaces)company(string, optional): Company/organization name for filteringdate(string, optional): Publication date in ISO format (YYYY-MM-DD)title(string, optional): Title or contextsourceUri(string, optional): Source URLdateTs(number, optional): Publication date as UTC epoch milliseconds (used for range filtering)contentCreatedAt(string, optional): Original content creation datetime in ISO 8601 formatcontentCreatedAtTs(number, optional): Original content creation datetime as UTC epoch milliseconds (used for range filtering)createdAt(string, optional): When the record was added to the systemcreatedAtTs(number, optional): Record creation datetime as UTC epoch milliseconds (used for range filtering)kind(string, optional): Type of content (e.g., "paragraph")
Vercel Blob Content
Store your content as JSON in Vercel Blob. The full JSON object is returned in the blob field of each match.
Local Development
# Install dependencies
pnpm install
# Copy environment variables
cp .env.example .env.local
# Then fill in your values in .env.local
# Start development server
pnpm dev
Sample MCP Client
# Test with Streamable HTTP transport
node scripts/test-streamable-http-client.mjs http://localhost:3000
# Test with SSE transport (requires Redis - see below)
node scripts/test-client.mjs http://localhost:3000
Data Output Structure
Generated artifacts are stored outside the repository root:
data/scraping/: scraper and enrichment outputs (for examplescraping-output-*.json)data/reports/: ingestion and dedupe reports
Ingesting X Article Text (Authenticated)
For x.com/i/article/... stubs and long-form post content, use authenticated X API enrichment:
export X_BEARER_TOKEN=<your-x-api-bearer-token>
# Option A: fetch + enrich in one step (TwStalker + X API + syndication fallback)
node scripts/fetch-twstalker-profile.mjs \
--handle ssafavi \
--author "Soofi Safavi" \
--company elephant-xyz \
--output data/scraping/scraping-output-soofi.json
# Option B: enrich an existing scraping-output file
node scripts/enrich-x-article-posts.mjs \
--input data/scraping/scraping-output-soofi.json \
--output data/scraping/scraping-output-soofi-enriched.json
Then upload to MCP:
MCP_WRITE_TOKEN=<token> node scripts/bulk-upload-paragraphs.mjs \
--input data/scraping/scraping-output-soofi-enriched.json \
--origin https://investors-mcp.vercel.app
Reporting Ingestion Coverage
Use the reporting script to audit who is in the RAG and each author's date coverage:
# Table output
BLOB_READ_WRITE_TOKEN=<token> node scripts/report-rag-ingestion.mjs
# JSON output (with full publication date list per author)
BLOB_READ_WRITE_TOKEN=<token> node scripts/report-rag-ingestion.mjs \
--json \
--include-date-list \
--output data/reports/ingestion-report.json
Tracking Author Replies (e.g., Soofi on X)
Use this script to detect who an author replied to, based on reply-style posts
that begin with one or more @handle mentions. The JSON output also includes
daily post and reply counts for the author:
BLOB_READ_WRITE_TOKEN=<token> node scripts/report-author-replies.mjs \
--author "Soofi Safavi" \
--output data/reports/soofi-replies-report.json
Optional JSON output:
BLOB_READ_WRITE_TOKEN=<token> node scripts/report-author-replies.mjs \
--author "Soofi Safavi" \
--json
Reporting X Polling Runs
Use this script to summarize /api/automation/monitor-x runs from Postgres-backed run records, including
when polling ran and how many posts were fetched/new:
POSTGRES_URL_NON_POOLING=<url> node scripts/report-x-monitor-runs.mjs \
--output data/reports/x-monitor-runs-report.json
Optional JSON output:
POSTGRES_URL_NON_POOLING=<url> node scripts/report-x-monitor-runs.mjs \
--json
Real-Time X Trigger Automation
GET/POST /api/automation/monitor-x runs the real-time polling pipeline:
- polls watched X authors for new posts
- ingests new posts into RAG
- contextually compares each post against Soofi Safavi's RAG content
- drafts a response
- creates an Asana task with source post + draft + context matches
Watchlist source:
managed_authorsin Postgres (author/handle/company registry and polling source of truth)data/reports/ingestion-report.json(author/company universe used to fill company context)
Trigger Parameters
dryRun=true|false(defaultfalse): run pipeline without RAG writes or Asana task creationbackfill=true|false(defaultfalse): process historical unseen posts on first runingestToRag=true|false(defaulttrue): control RAG ingestion stepbatchSize=<n>(default5): number of authors processed per triggermaxPostsPerAuthor=<n>(default20): cap per-author post scantopK=<n>(default6): Soofi context matches used for draftingauthor=<author-or-handle>: run only one author
Example (manual dry-run)
curl -H "Authorization: Bearer $AUTOMATION_TRIGGER_TOKEN" \
"https://investors-mcp.vercel.app/api/automation/monitor-x?dryRun=true&batchSize=2"
Deduplicating Records
Remove duplicate ingested records by (author + sourceUri) while keeping one canonical record:
# Preview only
BLOB_READ_WRITE_TOKEN=<token> \
UPSTASH_VECTOR_REST_URL=<url> \
UPSTASH_VECTOR_REST_TOKEN=<token> \
node scripts/dedupe-rag-records.mjs --report data/reports/dedupe-report.json
# Apply deletions in vector + blob
BLOB_READ_WRITE_TOKEN=<token> \
UPSTASH_VECTOR_REST_URL=<url> \
UPSTASH_VECTOR_REST_TOKEN=<token> \
node scripts/dedupe-rag-records.mjs --apply --report data/reports/dedupe-report.json
Reporting UI
A simple reporting dashboard is available at:
https://investors-mcp.vercel.app/reporting(production)http://localhost:3000/reporting(local)
The page reads data/reports/ingestion-report.json and can also read:
data/reports/soofi-replies-report.json- Postgres-backed
monitor_runsrecords for the Polling Runs tab
It provides:
- per-author coverage table
- search and sorting
- CSV export
- Soofi replies reporting
Dashboard Auth
The reporting routes (/authors, /polling, /replies, /reporting) are protected by Google sign-in.
Required environment variables:
NEXTAUTH_SECRETGOOGLE_CLIENT_IDGOOGLE_CLIENT_SECRETAUTH_ALLOWED_GOOGLE_DOMAIN
Recommended value for AUTH_ALLOWED_GOOGLE_DOMAIN:
elephant-labs.xyz
For local development, add Google OAuth callback URLs for:
http://localhost:3000/api/auth/callback/googlehttps://investors-mcp.vercel.app/api/auth/callback/google- polling run reporting (run timestamp, duration, fetched/new posts, and task counts)
Notes for Production on Vercel
- Fluid Compute: Enable Fluid compute for efficient execution
- Max Duration: With Vercel Pro/Enterprise, you can increase
maxDurationto 800 invercel.json - SSE Transport: Requires Redis. Add Upstash Redis from Vercel Marketplace and set
disableSse: falseinapp/mcp/route.ts
Architecture
┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ MCP Client │────▶│ app/mcp/route.ts │────▶│ Vercel AI │
│ │ │ │ │ Gateway │
└─────────────┘ └──────────────────┘ │ (embeddings) │
│ └─────────────────┘
│
▼
┌──────────────────┐
│ Upstash Vector │
│ (semantic search)│
└──────────────────┘
│
▼
┌──────────────────┐
│ Vercel Blob │
│ (full content) │
└──────────────────┘
License
See LICENSE file.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。