Investors MCP Server
Creates tasks in Asana from monitored X posts, with configurable project, assignee, section, and similarity score custom fields.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Investors MCP Serverfind recent posts by Marc Andreessen about AI"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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
queryInvestorContenttoolContent 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
Related MCP server: SEC Filing MCP Server
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 |
| Required for embeddings | |
| Vercel Marketplace (auto) | Upstash Vector endpoint |
| Vercel Marketplace (auto) | Upstash Vector auth token |
| Vercel Storage (auto) | Vercel Blob access token |
| Self-generated | Optional. Bearer token for write operations ( |
| X Developer Portal | Optional. Enables authenticated X API article/full-text enrichment in ingestion scripts |
| Self-generated | Bearer token for |
| Self-generated | Optional. If set, Vercel Cron can authenticate to trigger endpoint |
| Asana | Required to create tasks from monitored posts |
| Asana | Project where monitored-post tasks are created |
| Asana | Optional default assignee for created tasks |
| Asana | Optional assignee when a post meets the article recommendation threshold |
| Asana | Optional section to place newly created tasks into |
| Asana | Optional workspace override for task creation |
| Asana | Optional shared number custom field GID for both parent tasks and recommendation subtasks |
| Asana | Optional parent-task-specific number custom field GID; overrides the shared GID for parent tasks |
| Asana | Optional subtask-specific number custom field GID; overrides the shared GID for recommendation subtasks |
| Optional list | Optional comma-separated subset of watched authors |
| Optional | Legacy draft-model override (currently unused by Asana task creation) |
| Optional | TwStalker base URL override (default |
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 devSample 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:3000Data 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.jsonThen 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.appReporting 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.jsonTracking 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.jsonOptional JSON output:
BLOB_READ_WRITE_TOKEN=<token> node scripts/report-author-replies.mjs \
--author "Soofi Safavi" \
--jsonReporting 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.jsonOptional JSON output:
POSTGRES_URL_NON_POOLING=<url> node scripts/report-x-monitor-runs.mjs \
--jsonReal-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.jsonReporting 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.jsonPostgres-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/googlepolling 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.jsonSSE 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.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/prismteam-ai/investors-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server