# Obsidian MCP Actor (`tillzero/obsidian-mcp-actor`) Actor

Bridge the web and your Obsidian vault! Scrapes articles, strips ads, downloads images locally, generates AI tags, and syncs perfectly formatted Markdown notes directly to your hard drive via the MCP protocol.

- **URL**: https://apify.com/tillzero/obsidian-mcp-actor.md
- **Developed by:** [K S](https://apify.com/tillzero) (community)
- **Categories:** Automation, Developer tools, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Obsidian MCP Actor

The **Obsidian MCP Actor** bridges the gap between your local Obsidian knowledge base and the web. It uses Apify's powerful scraping capabilities (Crawlee) to fetch content from the internet, uses LLMs (OpenAI) to generate intelligent tags and summaries, downloads all media assets, and securely pushes the newly created notes directly into your local Obsidian vault via the Model Context Protocol (MCP).

---

### 🌟 Key Features

#### 1. **Automated Note Creation (Reader Mode)**
Scrapes any webpage and transforms its main content into clean, GitHub-Flavored Markdown. It uses Mozilla's `Readability.js` (the exact engine behind Firefox's Reader View) to instantly strip out all advertisements, navigation sidebars, cookie banners, and footers, leaving only the pure article text.

#### 2. **Media & Image Downloading (Local Resilience)**
No more broken image links! If enabled, the Actor will automatically detect all images in the scraped article, download them, and save them directly to an attachments folder inside your Obsidian Vault. The Markdown will then be rewritten to use native Obsidian embeds (e.g., `![[image.jpg]]`).

#### 3. **Intelligent Tagging & Summarization (Optional AI Extraction)**
Uses OpenAI to read the scraped content and generate metadata for you. By default, it generates a brief summary and relevant tags. You can easily pass a **Custom AI Prompt** to extract specialized information (e.g., "Extract all quotes", "List the top 5 companies mentioned", or "Format the output into a specific Obsidian YAML template").

#### 4. **High-Performance & Cost-Effective**
The headless browser is configured to actively block the loading of heavy CSS, fonts, and tracking scripts, drastically reducing execution time and saving your Apify compute credits.

#### 5. **Seamless Obsidian Integration**
Connects directly to your Obsidian vault's local MCP Server using Server-Sent Events (SSE). It creates the `.md` files directly on your local hard drive—no manual copying and pasting required.

---

### 🛠️ Architecture: How it works

Because Obsidian runs locally on your computer, the Apify Actor (running in the cloud) needs a secure way to talk to it.

1. **The MCP Server**: You install an MCP Server plugin in your Obsidian vault. This opens a local port (e.g., `http://127.0.0.1:3000`).
2. **The Secure Tunnel**: You use a tool like `ngrok` to expose that local port to the internet securely (e.g., `https://1a2b3c4d.ngrok.app`).
3. **The MCP Client**: The Apify Actor acts as the client. It connects to your `ngrok` URL.
4. **The Scraping Loop**:
   - The Actor visits a URL from your `startUrls` list.
   - It extracts and cleans the text via Readability.js.
   - It downloads all images and saves them to your vault via the MCP `write_file` tool.
   - It sends the final Markdown note to your vault using the MCP `create_note` or `write_file` tool.

---

### 🚀 Step-by-Step Setup Guide

#### Phase 1: Obsidian Setup
1. Open your Obsidian vault.
2. Install an MCP Server plugin (such as `obsidian-mcp` or `cyanheads/obsidian-mcp-server`) via the Community Plugins tab, or manually if it's in early development.
3. Enable the plugin. Take note of the port it runs on (usually `3000` or `3001`).

#### Phase 2: Exposing the Server (ngrok)
To allow Apify to reach your local computer, we need a tunnel.
1. Download and install [ngrok](https://ngrok.com/).
2. Run the following command in your terminal to expose your MCP server port:
   ```bash
   ngrok http 3000
````

3. Copy the secure HTTPS URL ngrok provides (e.g., `https://1a2b3c4d.ngrok.app`). **Keep this terminal window open** while you want the integration to work.

#### Phase 3: Apify Setup

1. Deploy this Actor to your Apify account.
2. In the Actor's Input configuration, configure the following:
   - **Start URLs**: Add the web pages you want to scrape.
   - **Obsidian MCP Server URL**: Paste your ngrok URL here, appending `/sse` (e.g., `https://1a2b3c4d.ngrok.app/sse`).
   - **Target Folder**: Where notes should be saved in your vault (e.g., `Research/Web`).

#### Phase 4: Optional Features Setup

- **Image Downloading**: Toggle `Download Images locally` and set your `Attachment Folder` (e.g., `Research/Attachments`).
- **AI Extraction**: Toggle `Enable AI Extraction` and paste your OpenAI API Key. You can leave the `Custom AI Prompt` blank for default summarization, or write your own specific instructions.

***

### ⚙️ Input Parameters Reference

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `startUrls` | Array | **Yes** | The list of URLs you want to scrape and turn into notes. |
| `obsidianMcpUrl` | String | **Yes** | The public URL of your Obsidian MCP Server (e.g., `https://xyz.ngrok.app/sse`). |
| `obsidianMcpApiKey` | String | No | If your MCP server requires an authorization token. |
| `targetFolder` | String | **Yes** | The folder in your Obsidian vault where notes will be saved. |
| `downloadImages` | Boolean | No | Check to download all images from the article directly to your vault. |
| `attachmentFolder` | String | No | The folder for downloaded images. Only used if `downloadImages` is true. |
| `aiExtraction` | Boolean | No | Toggle to enable LLM-based summaries and tags. |
| `aiPrompt` | String | No | Override the default extraction prompt to get exactly the data you want. |
| `openaiApiKey` | String | No | Required if AI extraction is enabled. |

***

### ⚠️ Troubleshooting & FAQs

**Q: The Actor fails immediately with a "Failed to connect" error.**

- Ensure your `ngrok` terminal is running.
- Ensure your Obsidian app is open and the MCP plugin is enabled.
- Check that the `obsidianMcpUrl` in the Apify input ends with `/sse` if your server uses Server-Sent Events.

**Q: Images are not downloading or rendering.**

- Ensure your `attachmentFolder` exists in Obsidian. Some MCP servers cannot create nested folders automatically.
- Check if your MCP server supports writing base64 binary files via the `write_file` tool. If the images appear as text files containing base64 strings in your vault, the server may require an update to support binary encoding.

**Q: AI Extraction isn't working.**

- Ensure your `openaiApiKey` is valid and has sufficient credits.
- If you used a custom prompt, ensure it explicitly asks for a JSON output with `summary` and `tags` keys, otherwise the parser may fail.

***

### 📝 Example Output Note

```markdown
---
title: "The Future of AI Agents"
source: https://example.com/future-ai-agents
date_scraped: 2025-10-22
tags: [artificialintelligence, agents, future]
---

## The Future of AI Agents

> **AI Summary**: This article discusses the evolution of AI agents and how they are transitioning from simple chat interfaces to autonomous systems that can interact with APIs and local environments.

### Content

Here is an image of the architecture:

![[img_7f8a9b2c.jpg]]

[Clean, reader-mode markdown representation of the article...]
```

# Actor input Schema

## `startUrls` (type: `array`):

URLs to scrape and import into Obsidian.

## `obsidianMcpUrl` (type: `string`):

The URL where your Obsidian MCP Server is exposed (e.g., via ngrok). Supports SSE/HTTP connections.

## `obsidianMcpApiKey` (type: `string`):

Optional API key or token for the MCP server.

## `targetFolder` (type: `string`):

The folder path inside Obsidian where notes should be created.

## `downloadImages` (type: `boolean`):

If enabled, downloads all images from the article and saves them to your vault.

## `attachmentFolder` (type: `string`):

The folder in your Obsidian vault where downloaded images should be saved (e.g., 'Research/Attachments'). Only used if 'Download Images' is enabled.

## `aiExtraction` (type: `boolean`):

If enabled, uses an LLM to generate tags, summaries, and wikilinks.

## `aiPrompt` (type: `string`):

Optional custom instructions for the AI on how to summarize or extract data from the article.

## `openaiApiKey` (type: `string`):

Required if AI Extraction is enabled.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://example.com"
    }
  ],
  "targetFolder": "Research/Scraped",
  "downloadImages": false,
  "attachmentFolder": "Research/Attachments",
  "aiExtraction": false
}
```

# Actor output Schema

## `resultsDataset` (type: `string`):

Click here to view the scraped notes dataset in the Apify Console.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        {
            "url": "https://example.com"
        }
    ],
    "targetFolder": "Research/Scraped",
    "attachmentFolder": "Research/Attachments"
};

// Run the Actor and wait for it to finish
const run = await client.actor("tillzero/obsidian-mcp-actor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "startUrls": [{ "url": "https://example.com" }],
    "targetFolder": "Research/Scraped",
    "attachmentFolder": "Research/Attachments",
}

# Run the Actor and wait for it to finish
run = client.actor("tillzero/obsidian-mcp-actor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    {
      "url": "https://example.com"
    }
  ],
  "targetFolder": "Research/Scraped",
  "attachmentFolder": "Research/Attachments"
}' |
apify call tillzero/obsidian-mcp-actor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=tillzero/obsidian-mcp-actor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Obsidian MCP Actor",
        "description": "Bridge the web and your Obsidian vault! Scrapes articles, strips ads, downloads images locally, generates AI tags, and syncs perfectly formatted Markdown notes directly to your hard drive via the MCP protocol.",
        "version": "1.0",
        "x-build-id": "AFw4qckzUOF4lQMAk"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/tillzero~obsidian-mcp-actor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-tillzero-obsidian-mcp-actor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/tillzero~obsidian-mcp-actor/runs": {
            "post": {
                "operationId": "runs-sync-tillzero-obsidian-mcp-actor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/tillzero~obsidian-mcp-actor/run-sync": {
            "post": {
                "operationId": "run-sync-tillzero-obsidian-mcp-actor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "startUrls",
                    "obsidianMcpUrl",
                    "targetFolder"
                ],
                "properties": {
                    "startUrls": {
                        "title": "Start URLs",
                        "type": "array",
                        "description": "URLs to scrape and import into Obsidian.",
                        "items": {
                            "type": "object",
                            "required": [
                                "url"
                            ],
                            "properties": {
                                "url": {
                                    "type": "string",
                                    "title": "URL of a web page",
                                    "format": "uri"
                                }
                            }
                        }
                    },
                    "obsidianMcpUrl": {
                        "title": "Obsidian MCP Server URL",
                        "type": "string",
                        "description": "The URL where your Obsidian MCP Server is exposed (e.g., via ngrok). Supports SSE/HTTP connections."
                    },
                    "obsidianMcpApiKey": {
                        "title": "Obsidian MCP API Key",
                        "type": "string",
                        "description": "Optional API key or token for the MCP server."
                    },
                    "targetFolder": {
                        "title": "Target Folder",
                        "type": "string",
                        "description": "The folder path inside Obsidian where notes should be created."
                    },
                    "downloadImages": {
                        "title": "Download Images locally",
                        "type": "boolean",
                        "description": "If enabled, downloads all images from the article and saves them to your vault.",
                        "default": false
                    },
                    "attachmentFolder": {
                        "title": "Attachment Folder",
                        "type": "string",
                        "description": "The folder in your Obsidian vault where downloaded images should be saved (e.g., 'Research/Attachments'). Only used if 'Download Images' is enabled."
                    },
                    "aiExtraction": {
                        "title": "Enable AI Extraction (Intelligent Tagging & Linking)",
                        "type": "boolean",
                        "description": "If enabled, uses an LLM to generate tags, summaries, and wikilinks.",
                        "default": false
                    },
                    "aiPrompt": {
                        "title": "Custom AI Prompt",
                        "type": "string",
                        "description": "Optional custom instructions for the AI on how to summarize or extract data from the article."
                    },
                    "openaiApiKey": {
                        "title": "OpenAI API Key",
                        "type": "string",
                        "description": "Required if AI Extraction is enabled."
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
