import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, Tool, } from "@modelcontextprotocol/sdk/types.js"; import axios from "axios"; // Environment variables const OUTLINE_API_TOKEN = process.env.OUTLINE_API_TOKEN; const OUTLINE_BASE_URL = process.env.OUTLINE_BASE_URL; if (!OUTLINE_API_TOKEN || !OUTLINE_BASE_URL) { console.error("Missing required environment variables: OUTLINE_API_TOKEN or OUTLINE_BASE_URL"); process.exit(1); } const axiosInstance = axios.create({ baseURL: OUTLINE_BASE_URL, headers: { Authorization: `Bearer ${OUTLINE_API_TOKEN}`, "Content-Type": "application/json", Accept: "application/json", }, }); // Define tools const tools: Tool[] = [ { name: "outline_search_documents", description: "Search for documents in Outline.", inputSchema: { type: "object", properties: { query: { type: "string", description: "Search query" }, collectionId: { type: "string", description: "Optional collection ID to scope search" }, }, required: ["query"], }, }, { name: "outline_get_document", description: "Get a document by its ID.", inputSchema: { type: "object", properties: { id: { type: "string", description: "The ID of the document" }, }, required: ["id"], }, }, { name: "outline_create_document", description: "Create a new document.", inputSchema: { type: "object", properties: { collectionId: { type: "string", description: "The ID of the collection" }, title: { type: "string", description: "Document title" }, text: { type: "string", description: "Document content in Markdown" }, publish: { type: "boolean", description: "Whether to publish the document" }, }, required: ["collectionId", "title", "text"], }, }, { name: "outline_update_document", description: "Update an existing document.", inputSchema: { type: "object", properties: { id: { type: "string", description: "The ID of the document" }, title: { type: "string", description: "New title" }, text: { type: "string", description: "New content in Markdown" }, }, required: ["id"], }, }, { name: "outline_create_collection", description: "Create a new collection.", inputSchema: { type: "object", properties: { name: { type: "string", description: "Collection name" }, description: { type: "string", description: "Collection description" }, }, required: ["name"], }, }, ]; const server = new Server( { name: "at-mintel-outline-mcp", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { try { switch (request.params.name) { case "outline_search_documents": { const { query, collectionId } = request.params.arguments as any; const res = await axiosInstance.post("/api/documents.search", { query, collectionId, }); return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }], }; } case "outline_get_document": { const { id } = request.params.arguments as any; const res = await axiosInstance.post("/api/documents.info", { id }); return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }], }; } case "outline_create_document": { const { collectionId, title, text, publish } = request.params.arguments as any; const res = await axiosInstance.post("/api/documents.create", { collectionId, title, text, publish: publish ?? true, }); return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }], }; } case "outline_update_document": { const { id, title, text } = request.params.arguments as any; const res = await axiosInstance.post("/api/documents.update", { id, title, text, }); return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }], }; } case "outline_create_collection": { const { name, description } = request.params.arguments as any; const res = await axiosInstance.post("/api/collections.create", { name, description, }); return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }], }; } default: throw new Error(`Unknown tool: ${request.params.name}`); } } catch (error: any) { const errorMsg = error.response?.data ? JSON.stringify(error.response.data) : error.message; return { content: [{ type: "text", text: `API Error: ${errorMsg}` }], isError: true, }; } }); async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("Outline MCP Server running on stdio"); } main().catch((error) => { console.error("Fatal error:", error); process.exit(1); });