Skip to main content
Glama

add-article

Add articles to a Notion database by providing a URL and database ID, with the option to generate a summary. Simplify content organization and access within your Notion workspace.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
databaseIdYes
generateSummaryNo
urlYes

Implementation Reference

  • src/index.ts:747-748 (registration)
    Registration of the 'add-article' tool on the MCP server using server.tool(name, inputSchema, handler).
    "add-article", {
  • Zod input schema validation for the add-article tool parameters.
    { url: z.string().url(), databaseId: z.string(), generateSummary: z.boolean().default(true) },
  • Main execution handler for add-article tool. Fetches article metadata from URL, auto-maps to Notion database properties, creates new database entry, generates summary if requested, appends article content as blocks.
    async ({ url, databaseId, generateSummary }) => { try { // First retrieve database to get property types const databaseInfo = await notion.databases.retrieve({ database_id: databaseId }); // Get all available property types const propertyInfoMap = databaseInfo.properties || {}; // Auto-detect property names const urlPropertyName = findMatchingProperty(propertyInfoMap, [ "URL", "Link", "Website", "Address", "Source Link" ]); const titlePropertyName = findMatchingProperty(propertyInfoMap, [ "Title", "Name", "Article Title", "Headline", "Topic" ]); const publicationPropertyName = findMatchingProperty(propertyInfoMap, [ "Publication", "Publisher", "Source", "Site", "Website Name", "Origin" ]); const authorPropertyName = findMatchingProperty(propertyInfoMap, [ "Author", "Author(s)", "Writer", "Creator", "By" ]); const datePropertyName = findMatchingProperty(propertyInfoMap, [ "Date", "Published", "Published Date", "Publish Date", "Release Date", "Post Date" ]); const summaryPropertyName = findMatchingProperty(propertyInfoMap, [ "Summary", "Article Summary", "TLDR", "Description", "Brief" ]); // Get property types for the detected properties const titlePropertyType = getPropertyType(propertyInfoMap, titlePropertyName); const publicationPropertyType = getPropertyType(propertyInfoMap, publicationPropertyName); const authorPropertyType = getPropertyType(propertyInfoMap, authorPropertyName); const datePropertyType = getPropertyType(propertyInfoMap, datePropertyName); const summaryPropertyType = getPropertyType(propertyInfoMap, summaryPropertyName); const urlPropertyType = getPropertyType(propertyInfoMap, urlPropertyName); // Log the detected fields console.log(`Using field mapping: - Title: "${titlePropertyName}" (${titlePropertyType}) - URL: "${urlPropertyName}" (${urlPropertyType}) - Publication: "${publicationPropertyName}" (${publicationPropertyType}) - Author: "${authorPropertyName}" (${authorPropertyType}) - Date: "${datePropertyName}" (${datePropertyType}) - Summary: "${summaryPropertyName}" (${summaryPropertyType})`); // Extract metadata from the URL const metadata = await extractMetadataFromUrl(url); const { publication, author, date, content } = metadata; // Use the URL's title or domain as the article title if not extracted let title = ""; // Try to extract title from HTML try { const response = await axios.get(url, { headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', }, timeout: 10000, maxRedirects: 5 }); const $ = cheerio.load(response.data); title = $('title').text().trim() || $('meta[property="og:title"]').attr('content') || $('meta[name="twitter:title"]').attr('content') || new URL(url).hostname; } catch (error) { // If we can't access the URL, use the domain as title try { title = new URL(url).hostname; } catch (e) { title = url; } } // Generate summary if needed let summary = ""; if (generateSummary && content) { // For now, use a simple summarization method summary = createSimpleSummary(content); } // Create the page properties const properties: any = {}; // Set the title property if (titlePropertyName && titlePropertyType) { if (titlePropertyType === 'title') { properties[titlePropertyName] = createTitleProperty(title); } else if (titlePropertyType === 'rich_text') { properties[titlePropertyName] = createRichTextProperty(title); } } // Set the URL property if (urlPropertyName && urlPropertyType) { if (urlPropertyType === 'url') { properties[urlPropertyName] = { url }; } else if (urlPropertyType === 'rich_text') { properties[urlPropertyName] = createRichTextProperty(url); } } // Set the publication property if (publicationPropertyName && publicationPropertyType && publication) { if (publicationPropertyType === 'select') { properties[publicationPropertyName] = createSelectProperty(publication); } else if (publicationPropertyType === 'rich_text') { properties[publicationPropertyName] = createRichTextProperty(publication); } else if (publicationPropertyType === 'title') { properties[publicationPropertyName] = createTitleProperty(publication); } } // Set the author property if (authorPropertyName && authorPropertyType && author) { if (authorPropertyType === 'multi_select') { properties[authorPropertyName] = createMultiSelectProperty(parseAuthors(author)); } else if (authorPropertyType === 'select') { properties[authorPropertyName] = createSelectProperty(author); } else if (authorPropertyType === 'rich_text') { properties[authorPropertyName] = createRichTextProperty(author); } } // Set the date property if (datePropertyName && datePropertyType === 'date' && date) { properties[datePropertyName] = createDateProperty(date); } // Set the summary property if (summaryPropertyName && summaryPropertyType && summary) { if (summaryPropertyType === 'rich_text') { properties[summaryPropertyName] = createRichTextProperty(summary); } else if (summaryPropertyType === 'select') { properties[summaryPropertyName] = createSelectProperty(summary); } else if (summaryPropertyType === 'multi_select') { properties[summaryPropertyName] = createMultiSelectProperty([summary]); } } // Create the page in Notion const response = await notion.pages.create({ parent: { database_id: databaseId }, properties: properties }); // Add content blocks if we have content if (content && response.id) { try { // Create content blocks (paragraphs) const contentBlocks = createContentBlocks(content); await notion.blocks.children.append({ block_id: response.id, children: contentBlocks }); } catch (err: any) { console.error(`Error updating content: ${err.message}`); } } // Return success with extracted fields return { content: [{ type: "text", text: `✅ Article added to your database!\n\n` + `🔗 URL: ${url}\n` + `📝 Title: ${title}\n` + (publication ? `📰 Publication: ${publication}\n` : '') + (author ? `✍️ Author: ${author}\n` : '') + (date ? `📅 Date: ${date}\n` : '') + (summary ? `\n📌 Summary: ${summary}` : '') }] }; } catch (error: any) { return { content: [{ type: "text", text: `Error adding article: ${error.message}` }], isError: true }; } }
  • Helper function to automatically detect relevant Notion database property names (e.g., URL, Title, Author) by exact, case-insensitive, and partial matching.
    function findMatchingProperty(propertyInfoMap: any, possibleNames: string[]): string { const availableProperties = Object.keys(propertyInfoMap); // First try exact match for (const name of possibleNames) { if (availableProperties.includes(name)) { return name; } } // Then try case-insensitive match for (const name of possibleNames) { const match = availableProperties.find(prop => prop.toLowerCase() === name.toLowerCase() ); if (match) { return match; } } // Then try partial match (contains) for (const name of possibleNames) { const match = availableProperties.find(prop => prop.toLowerCase().includes(name.toLowerCase()) || name.toLowerCase().includes(prop.toLowerCase()) ); if (match) { return match; } } // Default to the first possible name if no match found return possibleNames[0]; }
  • Key helper to scrape article metadata from URL using axios and cheerio, extracting publication, author, date, and main content via specialized sub-helpers.
    async function extractMetadataFromUrl(url: string) { // Fetch the webpage const response = await axios.get(url, { headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', 'Accept': 'text/html,application/xhtml+xml,application/xml', 'Accept-Language': 'en-US,en;q=0.9' }, timeout: 10000, maxRedirects: 5 }); // Parse HTML const $ = cheerio.load(response.data); // Extract metadata const publication = extractPublication($, url); const author = extractAuthor($); const date = extractDate($); const content = extractContent($); return { publication, author, date, content }; }

Other Tools

Related Tools

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/SAhmadUmass/notion-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server