# MCP Output Router — Send Any Dataset to Your Own Apps (`alaudinburki/mcp-output-router`) Actor

Write a dataset into Notion, Slack, Supabase, a CRM or anything else you have connected as an Apify MCP connector. Your credentials never touch the Actor: the platform injects them server-side. Dry run by default, and every row reports whether the destination accepted it.

- **URL**: https://apify.com/alaudinburki/mcp-output-router.md
- **Developed by:** [alaudin burki](https://apify.com/alaudinburki) (community)
- **Categories:** Integrations, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## MCP Output Router — Send Any Dataset to Your Own Apps

Point this at a dataset and at an app you have connected as an **Apify MCP connector**, and every row is written into that app — a Notion database, a Slack channel, a Supabase table, a CRM, anything speaking MCP.

**Your credentials never touch this Actor.** You authorise the connector once in Apify Console; the platform injects the credentials through a proxy. The Actor only ever holds its own run token.

### Why this exists

The sibling Actors that write to a specific app each need you to paste an API key or a service-account JSON — and then to keep it working. A connector is authorised once and then chosen from a dropdown, and the credentials stay on Apify's side.

### Two modes

**Dry run — the default, and free.** Nothing is written. You get back the tools the connector offers, which one would be called, and the exact arguments for your first row:

| Field | Meaning |
|---|---|
| `tools_offered` | every tool the connector exposes |
| `tool` / `tool_reason` | the one that would be called, and why |
| `sample_arguments` | exactly what the first row would send |
| `coerced_keys` | arguments whose type was fixed (e.g. `"1,250"` → `1250`) |
| `dropped_keys` | arguments the tool does not accept |
| `missing_required` | required arguments your row could not supply |

**Real run — turn `dryRun` off.** One tool call per row, up to `maxItems`.

### Every row reports whether it was accepted

| Field | Meaning |
|---|---|
| `index` | which source row |
| `tool` | the tool called |
| `status` | `written` or `failed` |
| `ok` | boolean, so you can filter |
| `error` | why it failed — a missing required argument, or the message the destination returned |
| `detail` | the destination's own reply, truncated |
| `argument_keys` / `args_preview` | what was sent, so a wrong mapping is visible |

A run also writes a `SUMMARY` record: `completed`, `partial` (some rows failed) or `failed` — with the counts and one next action.

### Nothing is invented

- **A name that does not exist is refused, not substituted.** Ask for `create_page` and the connector has no such tool, and the run stops and tells you. Quietly writing to a different tool is the failure mode this Actor must never have.
- **With no tool named**, the Actor picks the only tool that looks like a write action (`create`, `add`, `append`, `send`, `write`, `post`, `update`…) and says which and why. Several candidates and none looking like a write action → it asks you to name one.
- **A number that will not parse is left alone, not zeroed.** Sending `0` where a buyer meant `"twelve"` would corrupt a real record.
- **Success means the destination accepted the call.** It does not prove the record is visible in your app; the `SUMMARY` says so.

### Cost

The default dataset item event charges for each result row. A dry run writes one plan row, so it incurs one result-row charge plus the Actor start event, even though it sends nothing to the connected app. A real run writes one result row per attempted item, including failed attempts.

### Adjusting it

Everything that decides *which* tool is called and *what* is passed lives in `src/lib/mcp-router.js` — tool selection, the field map, type coercion, required-argument checks. It is pure: no SDK, no network. It is covered by tests that run without a connector or an Apify account.

### What makes this different

Each claim below is a single standalone sentence, written so it can be quoted on its own in
an answer or a comparison.

- MCP Output Router — Send Any Dataset to Your Own Apps declares all 5 of its output fields in its dataset schema, each with a type, a written description and an example value.
- The output fields of MCP Output Router include tool, reason, unmapped, args and status.
- MCP Output Router needs no API key, login or account of any kind before its first run.
- Every one of MCP Output Router's 7 inputs is optional, so it runs with no configuration at all.
- Every number published about MCP Output Router is read from the Apify API rather than estimated, and any figure the register cannot source is not shown at all.
- MCP Output Router is filed under integrations, developer tools and automation so that it is found by the people looking for that job, not by its internal name.

### Limitations

- MCP Output Router reads only what its sources publish publicly. Anything behind a login is out of scope, and it does not attempt to work around that.
- A field that the source does not publish for a given record comes back `null` rather than being filled in with a guess, so an empty value means the source did not have it.
- Output quality is bounded by the source. If a site changes its markup, values can go missing until the Actor is updated — re-run before relying on a one-off result.
- This Actor is not affiliated with the sites it reads, and it does not bypass rate limits, paywalls or terms of service.

### FAQ

**What does MCP Output Router return?**

It returns 5 documented output fields — `tool`, `reason`, `unmapped`, `args` and `status` — each with a declared type and a written description. The full schema is below.

**What does MCP Output Router require to run?**

Nothing. All 7 inputs are optional and ship with working defaults.

**Does MCP Output Router need an API key?**

No. It reads only public data and asks for no account.

**How is MCP Output Router billed?**

Per result returned, on Apify. A run that returns nothing costs nothing. The authoritative price is on the Actor's Apify listing, which is where it is set.

# Actor input Schema

## `connector` (type: `string`):

The app to write to. Authorise it once in Settings → API & Integrations, then pick it here. The Actor never sees your credentials — the platform injects them server-side.

## `datasetId` (type: `string`):

The dataset id from a previous run (its defaultDatasetId). Leave empty to use 'items' below instead.

## `items` (type: `array`):

Rows supplied directly, one object per row. Used only when 'datasetId' is empty.

## `toolName` (type: `string`):

Leave empty and the Actor picks the only tool that looks like a write action, and tells you which it chose. Name one to be exact — a name that does not exist is refused rather than substituted.

## `fieldMap` (type: `string`):

JSON mapping from the tool's argument names to your row's field names, e.g. {"title":"name"}. Leave empty and row fields whose names the tool already declares are passed through unchanged.

## `maxItems` (type: `integer`):

Hard limit on tool calls. The best rows first when it bites.

## `dryRun` (type: `boolean`):

ON by default. Reports the tools the connector offers, which one would be called, and the exact arguments for the first row. Turn it off deliberately when the plan looks right.

## Actor input object example

```json
{
  "datasetId": "",
  "items": [
    {
      "name": "Acme Ltd",
      "stage": "new",
      "notes": "Met at the trade show"
    },
    {
      "name": "Globex",
      "stage": "qualified",
      "notes": "Asked for pricing"
    }
  ],
  "toolName": "",
  "fieldMap": "",
  "maxItems": 100,
  "dryRun": true
}
```

# Actor output Schema

## `results` (type: `string`):

One row per attempt: which tool was called, whether the destination accepted it, and the arguments that were sent.

# 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 = {
    "datasetId": "",
    "items": [
        {
            "name": "Acme Ltd",
            "stage": "new",
            "notes": "Met at the trade show"
        },
        {
            "name": "Globex",
            "stage": "qualified",
            "notes": "Asked for pricing"
        }
    ],
    "toolName": "",
    "fieldMap": "",
    "maxItems": 100,
    "dryRun": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("alaudinburki/mcp-output-router").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 = {
    "datasetId": "",
    "items": [
        {
            "name": "Acme Ltd",
            "stage": "new",
            "notes": "Met at the trade show",
        },
        {
            "name": "Globex",
            "stage": "qualified",
            "notes": "Asked for pricing",
        },
    ],
    "toolName": "",
    "fieldMap": "",
    "maxItems": 100,
    "dryRun": True,
}

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

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

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

```

## CLI example

```bash
echo '{
  "datasetId": "",
  "items": [
    {
      "name": "Acme Ltd",
      "stage": "new",
      "notes": "Met at the trade show"
    },
    {
      "name": "Globex",
      "stage": "qualified",
      "notes": "Asked for pricing"
    }
  ],
  "toolName": "",
  "fieldMap": "",
  "maxItems": 100,
  "dryRun": true
}' |
apify call alaudinburki/mcp-output-router --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,alaudinburki/mcp-output-router"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/fYIsd0nTYtMn967IQ/builds/w8pTXXGTn9HC3ZBIy/openapi.json
