DEV Community

SM
SM

Posted on

What I Built the Day Apify Launched MCP Connectors

Notion MCP Challenge Submission 🧠

Apify launched MCP connectors on June 9, 2026. The idea: actors can now write directly to authenticated external apps β€” Notion, Slack, GitHub β€” without ever holding the user's credentials. I'd been running an African economic stress monitor on Apify for a few weeks, with results disappearing into datasets nobody looked at. MCP connectors seemed like a way to fix that.

Here's what it took to add Notion output, what I learned building it, and what the result actually looks like.


What MCP Connectors Actually Are

Before this launch, Apify actors could scrape the open web and return datasets. Anything requiring login β€” writing to your Notion workspace, posting to your Slack β€” had to happen outside Apify through separate integrations.

MCP connectors fix this with a proxy layer. Apify hosts an MCP server; your actor connects to it at runtime. The proxy holds the external app credentials β€” your actor authenticates to the proxy using its own Apify token, and the proxy forwards requests to Notion on your behalf. Your actor never sees Notion credentials directly.

The architecture:

Actor (Node.js)
  └── @modelcontextprotocol/sdk Client
        └── StreamableHTTPClientTransport
              └── POST https://mcp.apify.com/{connectorId}
                    └── Apify MCP Proxy
                          └── Notion API
                                └── Your Notion Database
Enter fullscreen mode Exit fullscreen mode

The Result First

Before the technical walkthrough β€” here's what appeared in my Notion workspace after the first run:

A board view grouped by verdict. 9 stable, 2 on watch (CΓ΄te d'Ivoire and Rwanda). Updates automatically on every actor run. Zero manual work after the initial setup.

The Today view shows the current snapshot across all 11 covered economies:

And filtering by a specific country gives you the full history β€” here's Ghana's last four days:

Ghana moved from watch to stable on June 10. The cedi's 30-day depreciation dropped below the 5% threshold that fires the FX momentum signal. That transition is visible directly in the Notion log.


One-Time Setup in Apify Console

Before touching any actor code, you connect Notion to Apify once:

Console β†’ Settings β†’ API & Integrations β†’ MCP Connectors β†’ Add connector

Choose Notion, authorize via OAuth. Apify creates a connector with a stable ID tied to your Notion workspace. That connector ID is what you pass into the actor as an input β€” Apify injects the proxy URL and auth at runtime.


What Changed in the Actor

Step 1 β€” Declare the connector input in input_schema.json:

"notionConnector": {
    "type": "string",
    "resourceType": "mcpConnector",
    "mcpServers": [{ "url": "https://mcp.notion.com/mcp" }]
}
Enter fullscreen mode Exit fullscreen mode

The resourceType: "mcpConnector" field is the key. It tells Apify this input is a connector reference, not a plain string. The Console UI renders a dropdown of your connected MCP connectors instead of a text field. No token management in the actor.

Step 2 β€” Connect at runtime:

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from 
  '@modelcontextprotocol/sdk/client/streamableHttp.js';

const transport = new StreamableHTTPClientTransport(
    new URL(`${process.env.APIFY_MCP_PROXY_URL}/${connectorId}`),
    { 
      requestInit: { 
        headers: { Authorization: `Bearer ${process.env.APIFY_TOKEN}` } 
      } 
    },
);
const client = new Client({ name: 'my-actor', version: '1.0.0' });
await client.connect(transport);
Enter fullscreen mode Exit fullscreen mode

Apify injects APIFY_MCP_PROXY_URL and APIFY_TOKEN automatically when the run starts. The actor authenticates to the proxy with its own token β€” not the user's Notion credentials.

Step 3 β€” Discover available tools:

const toolsResult = await client.listTools();
Enter fullscreen mode Exit fullscreen mode

The proxy exposes Notion-scoped tools: notion-search, notion-retrieve-page, notion-create-pages, notion-update-page, notion-retrieve-database. The one I needed was notion-create-pages.

Step 4 β€” Write results to Notion:

const args = {
    parent: { database_id: "your-database-id" },
    pages: countries.map(country => ({
        properties: {
            Name: `${country.code} β€” ${country.date}`,
            Country: country.name,
            Verdict: country.verdict,
            "Vulnerability Level": country.vulnerabilityLevel,
            "Coverage Level": country.coverageLevel,
            "Stress Score": country.stressScore,
            "Fired Signal": country.firedSignals.join(", ") || null,
            "date:Last Updated:start": country.asOf,
            "Actor Run URL": runUrl,
        }
    }))
};

const result = await client.callTool({ 
  name: 'notion-create-pages', 
  arguments: args 
});

if (result.isError) {
    console.error('Notion write failed:', result.content[0].text);
}
Enter fullscreen mode Exit fullscreen mode

The Gotchas

This is the part I wish I'd read before building.

Property values are scalars, not Notion blocks. The MCP wrapper uses a simplified encoding β€” string | number | null values only. If you pass raw Notion API property blocks ({ title: [{ text: { content: "..." } }] }), you get invalid_union at path: []. The wrapper handles type-mapping to Notion's internal format based on the database column schema.

Date fields use a special key format. You can't use the column name directly for date fields. The format is:

"date:COLUMN_NAME:start"  β†’  ISO 8601 string
Enter fullscreen mode Exit fullscreen mode

So "Last Updated": "2026-06-10T14:30:00.000Z" silently fails or errors. The correct key is "date:Last Updated:start": "2026-06-10T14:30:00.000Z". This is specific to the Apify MCP wrapper β€” not a Notion API convention.

isError is a flag, not an exception. A bad schema or Notion API error returns isError: true on the result object with details in content[0].text. It doesn't throw. You must check it explicitly.

Dockerfile gotcha. If your Dockerfile has COPY . ./ after RUN npm run build, it overwrites your freshly compiled dist/. Add dist/ to .dockerignore to prevent this. Cost me an embarrassing number of deploys.

Tool schemas get truncated in Apify console logs. Log lines are truncated. If you need to inspect the full tool schema, write it to a key-value store with Actor.setValue() and read it via the Console's KV viewer.


The Signals View

One unexpected benefit of pushing to Notion: the chart views. Here's the signal distribution across all 11 economies today:

The dominant signals aren't FX crises or inflation β€” they're debt distress and political stability concerns, each affecting 4 of 11 economies. That's not what I expected when I started building this. But it's what the data shows.


What This Enables

A few workflows that weren't practical before this integration:

The canary view β€” a Notion filtered view showing only countries where the verdict changed since the previous run. Usually empty. Noticeable when it isn't. Ghana's move from watch to stable on June 10 would have appeared here automatically.

Verdict change alerts β€” Notion automations (available on paid plans) can trigger when a property value changes. When a country flips from stable to watch, send an email or Slack message. No Zapier required.

Cross-referencing with your own data β€” add a "Portfolio Exposure" column to the same database alongside the automated signals. Build a linked view that filters to countries where you have exposure AND vulnerability is HIGH. The automated and manual data live in the same Notion workspace.


The Actor

The full African Economic Stress Monitor is available on Apify Store and RapidAPI. It covers Nigeria, Ghana, Kenya, South Africa, Zambia, Tanzania, Uganda, Morocco, CΓ΄te d'Ivoire, Ethiopia, and Rwanda β€” two analytical gauges (acute stress and structural vulnerability), every signal explained, no black-box scores.

β†’ Apify Store
β†’ RapidAPI
β†’ Free monthly findings newsletter


MCP connectors documentation: docs.apify.com/platform/integrations/mcp-connectors

Top comments (0)