# MCP Enricher — Look Up Every Row in Your Own Apps (`alaudinburki/mcp-enricher`) Actor

Enrich every row of a dataset by calling a tool on an app you connected as an Apify MCP connector — a CRM, a project tracker, an internal database — and merge the reply back into the row. Your credentials never touch the Actor. Dry run by default.

- **URL**: https://apify.com/alaudinburki/mcp-enricher.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 Enricher — Look Up Every Row in Your Own Apps

Give it a dataset and a connector. For every row it calls a tool on **an app you have connected as an Apify MCP connector** — a CRM, a project tracker, an internal database — and merges the reply back into the row.

**Your credentials never touch this Actor.** You authorise the connector once in Apify Console; the platform injects the credentials through a proxy at run time.

This is the mirror of [MCP Output Router](../113-mcp-output-router/README.md): that one *writes* your rows out, this one *reads* facts back in. It is Apify's own documented pattern — *"an Actor crawls a list of companies, then enriches the output by calling MCP tools the user has connected."*

### What you get

**Every source row comes back**, enriched or not. A row the tool could not answer keeps its data and carries its own error:

| Field | Meaning |
|---|---|
| `<outputField>` | The tool's reply, flattened to text |
| `<outputField>_ok` | `true`/`false` — so you can filter |
| `<outputField>_error` | Why it failed, when it did |
| `<outputField>_tool` | Which tool answered |

**A failed lookup never deletes a row.** Dropping rows that could not be enriched would turn "we could not check these five" into "there were five of them", which is the kind of silent gap this portfolio exists to avoid.

### Dry run first, and it is the default

`dryRun` is **on**. Nothing is called. You get back the connector's tools, which one would be called and why, and the exact arguments for your first row:

```json
{
  "tool": "get_contact_by_domain",
  "tool_reason": "matches a tool that reads",
  "rows_ready": 2,
  "sample_arguments": "{\"query\":\"Acme Ltd\"}",
  "missing_required": null
}
```

Turn it off deliberately when the plan looks right.

### Nothing is invented

- **A named tool that does not exist is refused, not substituted.** Ask for `get_contact` and the connector has no such tool, and the run stops and names it. Quietly calling a different tool is the failure this Actor must never have.
- **With no tool named**, the Actor picks the tool that looks like a **lookup** (`get`, `search`, `find`, `list`, `read`, `query`…) and says which and why. An enricher wants a reader, not a writer.
- **A number that will not parse is left alone, not zeroed** — sending `0` where you meant `"twelve"` would corrupt a real record.
- **Arguments are coerced to the tool's declared schema**, and unknown arguments are dropped only when the tool forbids extras.

### Cost

Charged once per row enriched, plus the start event. A dry run writes one row and is effectively free. Set `maxItems` to bound it: it is a hard cap on tool calls.

### Adjusting it

Everything that decides which tool is called, and what is passed to it, 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 a copy of the MCP Output Router's copy** and the two must be changed together, because Apify Actors deploy independently and cannot import across folders.

### What it deliberately does not do

- **It does not write anything back** to the connector. Use MCP Output Router for that.
- **It does not cache.** Every run calls the tool again.
- **It does not guess a mapping.** With no `fieldMap`, only fields the tool already declares are passed.

### 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 Enricher — Look Up Every Row in 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 Enricher include tool, reason, unmapped, args and status.
- MCP Enricher needs no API key, login or account of any kind before its first run.
- Every one of MCP Enricher's 9 inputs is optional, so it runs with no configuration at all.
- Every number published about MCP Enricher is read from the Apify API rather than estimated, and any figure the register cannot source is not shown at all.
- MCP Enricher 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 Enricher 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 Enricher 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 Enricher require to run?**

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

**Does MCP Enricher need an API key?**

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

**How is MCP Enricher 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 query. 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 tool that looks like a lookup (get, search, find, list, read…), 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. {"query":"company_name"}. Leave empty and row fields whose names the tool already declares are passed through unchanged.

## `outputField` (type: `string`):

The field the tool's reply is written into. The companion fields <name>\_ok, <name>\_error and <name>\_tool are written beside it, so a failed lookup is visible rather than blank.

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

Hard limit on tool calls. The first N rows are enriched, in the order the dataset gives them.

## `skipOnError` (type: `boolean`):

ON by default: a row the tool could not answer is kept with its error recorded, and the run continues. Turn it off to stop at the first failure.

## `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, without calling it.

## Actor input object example

```json
{
  "datasetId": "",
  "items": [
    {
      "company_name": "Acme Ltd",
      "domain": "acme.example"
    },
    {
      "company_name": "Globex",
      "domain": "globex.example"
    }
  ],
  "toolName": "",
  "fieldMap": "{\"query\":\"company_name\"}",
  "outputField": "enrichment",
  "maxItems": 100,
  "skipOnError": true,
  "dryRun": true
}
```

# Actor output Schema

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

Every source row, enriched where the tool answered. A row the tool could not answer carries its own error field, so a gap stays visible instead of the row disappearing.

# 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": [
        {
            "company_name": "Acme Ltd",
            "domain": "acme.example"
        },
        {
            "company_name": "Globex",
            "domain": "globex.example"
        }
    ],
    "toolName": "",
    "fieldMap": "{\"query\":\"company_name\"}",
    "outputField": "enrichment",
    "maxItems": 100,
    "skipOnError": true,
    "dryRun": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("alaudinburki/mcp-enricher").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": [
        {
            "company_name": "Acme Ltd",
            "domain": "acme.example",
        },
        {
            "company_name": "Globex",
            "domain": "globex.example",
        },
    ],
    "toolName": "",
    "fieldMap": "{\"query\":\"company_name\"}",
    "outputField": "enrichment",
    "maxItems": 100,
    "skipOnError": True,
    "dryRun": True,
}

# Run the Actor and wait for it to finish
run = client.actor("alaudinburki/mcp-enricher").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": [
    {
      "company_name": "Acme Ltd",
      "domain": "acme.example"
    },
    {
      "company_name": "Globex",
      "domain": "globex.example"
    }
  ],
  "toolName": "",
  "fieldMap": "{\\"query\\":\\"company_name\\"}",
  "outputField": "enrichment",
  "maxItems": 100,
  "skipOnError": true,
  "dryRun": true
}' |
apify call alaudinburki/mcp-enricher --silent --output-dataset

```

## MCP server setup

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

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/hvAxmM7dTjJHNDOr0/builds/DkuCQgi41NMeSbMnA/openapi.json
