index.js•2.19 kB
import dotenv from "dotenv";
import { z } from "zod";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { scrapeProfile } from "./scraper.js";
dotenv.config();
const envMaxPosts = Number.parseInt(process.env.MAX_POSTS ?? "", 10);
const DEFAULT_MAX_POSTS = Number.isFinite(envMaxPosts) ? envMaxPosts : 12;
const requestSchema = z.object({
username: z
.string()
.min(1, "username is required")
.describe("Instagram username or handle (leading @ optional)."),
maxPosts: z
.number()
.int()
.positive()
.max(50)
.optional()
.describe("Number of recent posts to include in the analysis."),
headless: z.boolean().optional().describe("Override Playwright headless mode."),
storageStatePath: z
.string()
.optional()
.describe("Path to Playwright storageState.json file with a logged-in session."),
});
const server = new McpServer({
name: "instagram-investigator",
version: "0.1.0",
description: "Scrapes Instagram profiles via Playwright and returns structured data about recent posts.",
});
server.registerTool(
"instagram_profile_report",
{
description: "Scrape a public Instagram profile (requires logged-in cookies) and return recent post data.",
inputSchema: requestSchema,
},
async (input) => {
const parsed = requestSchema.parse(input);
const { username } = parsed;
const maxPosts = parsed.maxPosts ?? DEFAULT_MAX_POSTS;
const scrapeResult = await scrapeProfile(username, {
maxPosts,
headless: parsed.headless,
storageStatePath: parsed.storageStatePath,
logger: (message) => console.error(`[scraper] ${message}`),
});
return {
content: [
{
type: "text",
text: JSON.stringify(scrapeResult, null, 2),
},
],
};
}
);
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("instagram-investigator MCP server is ready");
}
main().catch((error) => {
console.error("Failed to start MCP server", error);
process.exit(1);
});