@mambalabsdev/mcp-talent-agency-lookup 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mamba Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # Influencer Talent Agency Lookup MCP Server
2
+
3
+ [![Smithery](https://smithery.ai/badge/mambabuilt/mcp-talent-agency-lookup)](https://smithery.ai/servers/mambabuilt/mcp-talent-agency-lookup) [![Glama score](https://glama.ai/mcp/servers/mambalabsdev/mcp-talent-agency-lookup/badges/score.svg)](https://glama.ai/mcp/servers/mambalabsdev/mcp-talent-agency-lookup) [![MCP Registry](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fregistry.modelcontextprotocol.io%2Fv0%2Fservers%3Fsearch%3Dcom.mambabuilt%252Fmcp-talent-agency-lookup%26limit%3D1&query=%24.servers%5B0%5D._meta%5B%22io.modelcontextprotocol.registry%2Fofficial%22%5D.status&label=mcp%20registry&color=blue)](https://registry.modelcontextprotocol.io/v0/servers?search=com.mambabuilt/mcp-talent-agency-lookup&limit=1) [![npm version](https://img.shields.io/npm/v/@mambalabsdev/mcp-talent-agency-lookup)](https://www.npmjs.com/package/@mambalabsdev/mcp-talent-agency-lookup) [![npm downloads](https://img.shields.io/npm/dm/@mambalabsdev/mcp-talent-agency-lookup)](https://www.npmjs.com/package/@mambalabsdev/mcp-talent-agency-lookup) [![license](https://img.shields.io/github/license/mambalabsdev/mcp-talent-agency-lookup)](https://github.com/mambalabsdev/mcp-talent-agency-lookup/blob/main/LICENSE) [![mcpservers.org](https://img.shields.io/badge/mcpservers.org-listed-blue)](https://mcpservers.org/servers/mambalabsdev/mcp-talent-agency-lookup)
4
+
5
+ MCP server for the Mamba Labs [Influencer Talent Agency Lookup](https://apify.com/mambalabs/talent-agency-lookup) actor on Apify.
6
+
7
+ Creator handles in, the talent agency behind each one out. Or an agency in, its public creator roster out.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npx -y @mambalabsdev/mcp-talent-agency-lookup
13
+ ```
14
+
15
+ ### Claude Desktop
16
+
17
+ ```json
18
+ {
19
+ "mcpServers": {
20
+ "mamba-talent-agency-lookup": {
21
+ "command": "npx",
22
+ "args": ["-y", "@mambalabsdev/mcp-talent-agency-lookup"],
23
+ "env": { "APIFY_TOKEN": "your-apify-token" }
24
+ }
25
+ }
26
+ }
27
+ ```
28
+
29
+ Get an Apify token at [console.apify.com/account/integrations](https://console.apify.com/account/integrations).
30
+
31
+ ## Tool
32
+
33
+ ### `lookup_influencer_talent_agency`
34
+
35
+ Match an influencer to their talent agency, or an agency to its public creator roster.
36
+
37
+ Pass `handles` for creator to agency, or `agency_domains` or `agency_names` for agency to roster. Both directions can run in one call.
38
+
39
+ | Input | Type | Required | Notes |
40
+ | --- | --- | --- | --- |
41
+ | `handles` | array | no | One per line. A profile URL on any supported platform (https://www.tiktok.com/@name, https://www.instagram.com/name/, https://www.youtube.com/@name, a Pinterest, Twitch, or Threads profile, an Apple Podcasts show page, or a Spotify show), or platform:@handle (tiktok:@name). A bare @handle needs `platforms` and is looked up on each listed platform. One entry is a single run; a list is a batch. Duplicates are removed before any fetch. |
42
+ | `platforms` | array | no | Which platforms a bare @handle is looked up on. A full profile URL carries its own platform and ignores this. This actor does not search; pass the creators you want read. Supported: TikTok, Instagram, YouTube, Pinterest, Twitch, Threads, and podcasts. Not X, not Facebook pages, not LinkedIn. |
43
+ | `agency_domains` | array | no | Agency to roster: one domain per line (for example viralnation.com). Returns one row per creator on the agency's public roster. |
44
+ | `agency_names` | array | no | Agency to roster by name when you do not have the domain. Exact or partial name match against the list. |
45
+ | `render_unreadable_pages` | boolean | no | Off by default. Some agency roster pages build their talent grid in the browser and return an empty shell to a plain fetch, so the roster reads as empty. Turn this on to render those pages in a headless browser and read the roster from the rendered page. Charged per page rendered (event browser-render) to cover the browser compute, and only when the rendered page comes back readable. A page that answers with a bot challenge is recorded as blocked and is never rendered. Default `false`. |
46
+ | `escalate_on_block` | boolean | no | On by default. A profile fetch that comes back as a bot detection page is retried once over the residential proxy. On Instagram the bio, bio link, and following are read from the profile page over residential when the embed and the datacenter API did not carry them, and a page that comes back readable charges instagram-bio-fetch ($0.010). Uncheck it to never pay that event: a blocked profile then returns a labeled error row, and Instagram rows keep an empty bio and bio link on about half of the reads. Default `true`. |
47
+ | `batch_size` | integer | no | Rows fetched at once. Leave empty for the measured per platform default; the measurement is in the README. Higher is faster and, above the measured point, loses rows. |
48
+ | `contribute_to_shared_pool` | boolean | no | On by default. The run contributes the public records it finds to a shared creator and agency pool that all users of this actor read from, so a later run reads what this one found. Only public data that is already in your own output rows is sent: nothing from your Apify account, your input list, your API keys, or your own notes. Nothing is charged for a contribution. Turn this off and the run still reads the pool and writes nothing to it. Default: true. |
49
+
50
+ Nothing is required, but a call with no `handles`, no `agency_domains`, and no `agency_names` has nothing to look up and the run fails with a message saying so.
51
+
52
+ ## Pricing
53
+
54
+ Influencer Talent Agency Lookup is pay per event on Apify. Every price below is flat across the FREE, BRONZE, SILVER, GOLD, PLATINUM, and DIAMOND tiers.
55
+
56
+ | Event | Charged for | Price | Fires when |
57
+ | --- | --- | ---: | --- |
58
+ | `actor-start` | Actor start | $0.001 | Once per run, on start. Covers the run overhead. |
59
+ | `agency-lookup` | Agency lookup | $0.008 | Once per row returned by the talent agency lookup, in either direction (creator to agency, or agency to a roster creator). |
60
+ | `browser-render` | Browser render | $0.004 | Once per agency roster page rendered in the headless browser because the plain fetch returned an empty shell, and only when the rendered page came back readable. Only when `render_unreadable_pages` is on. |
61
+ | `instagram-bio-fetch` | Instagram bio fetch | $0.01 | Once per Instagram profile row when the bio, bio link, and following were read from the profile page over the residential proxy and came back readable. Only when `escalate_on_block` is on. |
62
+
63
+ ## Reading the output
64
+
65
+ Every row carries `agency_match_method`: `email_domain_exact`, `email_domain_parent`, `public_roster`, `bio_wording`, or `no_match`. A `no_match` row names the email domains it tried in `error_reason`. Every row also carries `row_status` and `error_reason`, so a creator or an agency the actor could not read comes back as a row saying why, not as a gap in the list. Filter on `row_status` before loading a table.
66
+
67
+ Coverage is partial by construction. The bundled list holds 143 agencies and 557 roster creators, seeded from 13 public directories, and it grows through the shared pool.
68
+
69
+ ## What this actor shares
70
+
71
+ This run contributes the records it finds to a shared creator and agency pool that all users of this actor read from. What one run finds, the next run can read.
72
+
73
+ This actor reads the pool before it reads anything else. When the pool is unreachable it falls back to the agency list bundled in the build, so a run never fails on the pool.
74
+
75
+ The toggle is `contribute_to_shared_pool`. It is a boolean input and it is on by default. Turn it off and the run still reads the pool and writes nothing to it.
76
+
77
+ **What this actor contributes.** The agency rows and the roster rows read during the run: agency name, domain, public contact, country, classification, roster URL, and one roster row per creator with the platform and the handle.
78
+
79
+ **Only public data that is already in your own output.** Every field written to the pool is a field this run returned to you, read from a page the platform or the creator publishes to anyone without a login. Nothing from your Apify account, your input list, your API keys, or your own notes is sent. A contribution never deletes anything from the pool.
80
+
81
+ **What a contribution is labeled with.** The actor ID, the run ID, the pool key issued to the actor build, and a hash of the calling IP address, used for the rate limit and nothing else. Your Apify account and your user ID are not recorded.
82
+
83
+ **Contributing is free.** No event is charged for a write to the pool. If the pool is unreachable the run finishes as normal, the rows are dropped, and the run log says so.
84
+
85
+ ## Actor
86
+
87
+ Actor ID `zCuX4Mgyg6JvXgGzd`. The wrapper calls the actor by that immutable ID rather than by its Store slug, so a Store rename never breaks it.
88
+
89
+ ## Suite
90
+
91
+ | Actor | Actor ID | MCP server |
92
+ | --- | --- | --- |
93
+ | [Influencer Finder](https://apify.com/mambalabs/creator-finder) | `tpkedmloIIWtXx6sg` | [`@mambalabsdev/mcp-creator-finder`](https://www.npmjs.com/package/@mambalabsdev/mcp-creator-finder) |
94
+ | [Influencer Profile Scraper](https://apify.com/mambalabs/creator-profile-reader) | `EqEnklDVMIcB1V499` | [`@mambalabsdev/mcp-creator-profile-reader`](https://www.npmjs.com/package/@mambalabsdev/mcp-creator-profile-reader) |
95
+ | [Link in Bio Scraper and Newsletter Detector](https://apify.com/mambalabs/link-in-bio-newsletter-checker) | `OorucdheTIgu7RFzK` | [`@mambalabsdev/mcp-link-in-bio-newsletter-checker`](https://www.npmjs.com/package/@mambalabsdev/mcp-link-in-bio-newsletter-checker) |
96
+ | [Influencer Change Monitor](https://apify.com/mambalabs/creator-change-monitor) | `d2VVgahNL6UmcLkhg` | [`@mambalabsdev/mcp-creator-change-monitor`](https://www.npmjs.com/package/@mambalabsdev/mcp-creator-change-monitor) |
97
+ | [Influencer Lead List Builder](https://apify.com/mambalabs/creator-lead-list-all-in-one) | `KnmByszcv135yM30G` | [`@mambalabsdev/mcp-creator-lead-list-all-in-one`](https://www.npmjs.com/package/@mambalabsdev/mcp-creator-lead-list-all-in-one) |
98
+ | [Influencer Talent Agency Lookup](https://apify.com/mambalabs/talent-agency-lookup) | `zCuX4Mgyg6JvXgGzd` | [`@mambalabsdev/mcp-talent-agency-lookup`](https://www.npmjs.com/package/@mambalabsdev/mcp-talent-agency-lookup) |
99
+
100
+ Built by [Mamba Labs](https://mambabuilt.com).
package/SECURITY.md ADDED
@@ -0,0 +1,31 @@
1
+ # Security Policy
2
+
3
+ ## Supported versions
4
+
5
+ The latest published `1.x` release of
6
+ `@mambalabsdev/mcp-talent-agency-lookup` receives security updates.
7
+
8
+ | Version | Supported |
9
+ | ------- | ------------------ |
10
+ | 1.x | :white_check_mark: |
11
+ | < 1.0 | :x: |
12
+
13
+ ## Reporting a vulnerability
14
+
15
+ Please do not open a public GitHub issue for security reports.
16
+
17
+ Email security reports to **labs@mambamindsetgrowth.com** with:
18
+
19
+ - a description of the issue and its impact,
20
+ - steps to reproduce, and
21
+ - any relevant logs or proof of concept.
22
+
23
+ We aim to acknowledge reports within 3 business days and to provide a remediation
24
+ timeline after triage. Coordinated disclosure is appreciated.
25
+
26
+ ## Handling of credentials
27
+
28
+ This server reads an `APIFY_TOKEN` from the environment and uses it only to call
29
+ the Apify API on your behalf. The token is never logged or written to disk by this
30
+ package. Treat your `APIFY_TOKEN` as a secret and rotate it at
31
+ https://console.apify.com/account/integrations if you believe it has been exposed.
package/build/index.js ADDED
@@ -0,0 +1,198 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from "node:fs";
3
+ import { fileURLToPath } from "node:url";
4
+ import { dirname, join } from "node:path";
5
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
+ import { z } from "zod";
8
+ const here = dirname(fileURLToPath(import.meta.url));
9
+ const pkg = JSON.parse(readFileSync(join(here, "..", "package.json"), "utf8"));
10
+ // Distinctive UA so Apify run meta.userAgent marks MCP-originated runs.
11
+ const USER_AGENT = `mambalabs-mcp ${pkg.name}@${pkg.version}`;
12
+ // Drop undefined values so optional inputs are not sent to the actor.
13
+ function compact(obj) {
14
+ const out = {};
15
+ for (const [k, v] of Object.entries(obj)) {
16
+ if (v !== undefined)
17
+ out[k] = v;
18
+ }
19
+ return out;
20
+ }
21
+ // How long the actor run itself is allowed to take, in seconds. The suite's
22
+ // slowest measured run on 2026-09-22 was 152 s on a seven platform search, so
23
+ // 1800 s is roughly twelve times that: headroom for a slow day without letting a
24
+ // hung run bill indefinitely.
25
+ const ACTOR_RUN_TIMEOUT_SECS = 1800;
26
+ // How long this wrapper waits for that run, in milliseconds. The actor's own
27
+ // timeout plus two minutes, so the run's own TIMED-OUT status is what the
28
+ // caller sees rather than the wrapper giving up first and reporting nothing.
29
+ const WRAPPER_WAIT_MS = (ACTOR_RUN_TIMEOUT_SECS + 120) * 1000;
30
+ const POLL_INTERVAL_MS = 3000;
31
+ const TERMINAL = new Set(["SUCCEEDED", "FAILED", "TIMED-OUT", "ABORTED", "ABORTING"]);
32
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
33
+ // Shared caller. actorPath is the actor's immutable Apify actor ID (a stable key
34
+ // that survives Store renames). The /v2/acts/{id} endpoint accepts it directly,
35
+ // so a Store rename never breaks these calls.
36
+ //
37
+ // START AND POLL, NOT RUN-SYNC. Apify's synchronous endpoints carry a platform
38
+ // ceiling of 300 seconds on the HTTP wait itself and answer 408 past it whatever
39
+ // the timeout parameter says, so a long run reads as a timeout even though the
40
+ // actor goes on to finish. Starting the run, polling it to a terminal status and
41
+ // then reading the dataset is the only way to wait as long as the actor needs.
42
+ //
43
+ // The token is read here rather than at module load, so the tool registers
44
+ // unconditionally and a server started without APIFY_TOKEN still advertises its
45
+ // capabilities instead of reporting none.
46
+ async function runActor(actorPath, actorLabel, input) {
47
+ const APIFY_TOKEN = process.env.APIFY_TOKEN;
48
+ if (!APIFY_TOKEN) {
49
+ return { isError: true, content: [{ type: "text", text: "APIFY_TOKEN is not set. Create a token at https://console.apify.com/account/integrations and set it as the APIFY_TOKEN environment variable." }] };
50
+ }
51
+ const headers = {
52
+ Authorization: `Bearer ${APIFY_TOKEN}`,
53
+ "Content-Type": "application/json",
54
+ "User-Agent": USER_AGENT,
55
+ };
56
+ const httpError = async (response) => {
57
+ let detail = "";
58
+ try {
59
+ const body = (await response.json());
60
+ if (body?.error?.message)
61
+ detail = ` ${body.error.message}`;
62
+ }
63
+ catch {
64
+ detail = "";
65
+ }
66
+ switch (response.status) {
67
+ case 400:
68
+ return `The ${actorLabel} run was rejected as invalid input.${detail}`;
69
+ case 401:
70
+ return "Invalid Apify token. Check your APIFY_TOKEN environment variable.";
71
+ case 402:
72
+ return "Insufficient Apify credits. Check your account balance at https://console.apify.com/billing";
73
+ default:
74
+ return `Apify request to ${actorLabel} failed with status ${response.status}.${detail}`;
75
+ }
76
+ };
77
+ // 1. Start the run.
78
+ let started;
79
+ try {
80
+ started = await fetch(`https://api.apify.com/v2/acts/${actorPath}/runs?timeout=${ACTOR_RUN_TIMEOUT_SECS}`, { method: "POST", headers, body: JSON.stringify(input) });
81
+ }
82
+ catch (err) {
83
+ const message = err instanceof Error ? err.message : String(err);
84
+ return { isError: true, content: [{ type: "text", text: `Could not reach the Apify API: ${message}` }] };
85
+ }
86
+ if (!started.ok) {
87
+ return { isError: true, content: [{ type: "text", text: await httpError(started) }] };
88
+ }
89
+ let run;
90
+ try {
91
+ run = (await started.json()).data ?? {};
92
+ }
93
+ catch (err) {
94
+ const message = err instanceof Error ? err.message : String(err);
95
+ return { isError: true, content: [{ type: "text", text: `The ${actorLabel} run start returned a response that could not be parsed: ${message}` }] };
96
+ }
97
+ const runId = run.id;
98
+ if (!runId) {
99
+ return { isError: true, content: [{ type: "text", text: `The ${actorLabel} run start returned no run id, so there is nothing to wait for.` }] };
100
+ }
101
+ // 2. Poll to a terminal status.
102
+ const deadline = Date.now() + WRAPPER_WAIT_MS;
103
+ let status = run.status ?? "READY";
104
+ let datasetId = run.defaultDatasetId;
105
+ while (!TERMINAL.has(status)) {
106
+ if (Date.now() >= deadline) {
107
+ return {
108
+ isError: true,
109
+ content: [{ type: "text", text: `The ${actorLabel} run ${runId} was still ${status} after ${Math.round(WRAPPER_WAIT_MS / 1000)} seconds and this call stopped waiting. The run itself is still on Apify: read it at https://console.apify.com/actors/runs/${runId}` }],
110
+ };
111
+ }
112
+ await sleep(POLL_INTERVAL_MS);
113
+ let poll;
114
+ try {
115
+ poll = await fetch(`https://api.apify.com/v2/actor-runs/${runId}`, { headers });
116
+ }
117
+ catch (err) {
118
+ const message = err instanceof Error ? err.message : String(err);
119
+ return { isError: true, content: [{ type: "text", text: `Lost contact with the Apify API while waiting for ${actorLabel} run ${runId}: ${message}` }] };
120
+ }
121
+ if (!poll.ok) {
122
+ return { isError: true, content: [{ type: "text", text: await httpError(poll) }] };
123
+ }
124
+ const body = (await poll.json());
125
+ status = body.data?.status ?? status;
126
+ datasetId = body.data?.defaultDatasetId ?? datasetId;
127
+ }
128
+ // 3. A run that did not succeed is a failure the caller must see, never an
129
+ // empty success. Surfacing it here is what keeps a crashed run from reading
130
+ // as "no results found".
131
+ if (status !== "SUCCEEDED") {
132
+ return {
133
+ isError: true,
134
+ content: [{ type: "text", text: `The ${actorLabel} run did not succeed (run ID: ${runId}, status: ${status}).` }],
135
+ };
136
+ }
137
+ if (!datasetId) {
138
+ return { isError: true, content: [{ type: "text", text: `The ${actorLabel} run ${runId} succeeded but reported no dataset, so there is nothing to return.` }] };
139
+ }
140
+ // 4. Read the dataset.
141
+ let ds;
142
+ try {
143
+ ds = await fetch(`https://api.apify.com/v2/datasets/${datasetId}/items?format=json`, { headers });
144
+ }
145
+ catch (err) {
146
+ const message = err instanceof Error ? err.message : String(err);
147
+ return { isError: true, content: [{ type: "text", text: `Could not read the ${actorLabel} dataset: ${message}` }] };
148
+ }
149
+ if (!ds.ok) {
150
+ return { isError: true, content: [{ type: "text", text: await httpError(ds) }] };
151
+ }
152
+ let items;
153
+ try {
154
+ items = await ds.json();
155
+ }
156
+ catch (err) {
157
+ const message = err instanceof Error ? err.message : String(err);
158
+ return { isError: true, content: [{ type: "text", text: `The ${actorLabel} run returned a response that could not be parsed: ${message}` }] };
159
+ }
160
+ if (!Array.isArray(items)) {
161
+ const asObj = items;
162
+ const detail = asObj?.error?.message
163
+ ? `${asObj.error.message}`
164
+ : JSON.stringify(items);
165
+ return { isError: true, content: [{ type: "text", text: `The ${actorLabel} run did not return a dataset. ${detail}` }] };
166
+ }
167
+ return { content: [{ type: "text", text: JSON.stringify(items, null, 2) }] };
168
+ }
169
+ const server = new McpServer({
170
+ name: "mamba-talent-agency-lookup",
171
+ version: pkg.version,
172
+ });
173
+ // Influencer Talent Agency Lookup (immutable actor ID zCuX4Mgyg6JvXgGzd)
174
+ server.registerTool("lookup_influencer_talent_agency", {
175
+ title: "Look Up an Influencer's Talent Agency or an Agency's Roster",
176
+ description: "Works in two directions. Creator to agency: pass creator handles or profile URLs and get one row per creator with the talent agency that represents them, its domain, its public contact, and agency_match_method (email_domain_exact, email_domain_parent, public_roster, bio_wording, or no_match with the email domains tried in error_reason). Agency to roster: pass agency domains or agency names and get one row per creator on the agency's public roster, with platform, handle, and profile URL. The agency list is seeded from 13 public directories and holds 143 agencies and 557 roster creators; coverage is partial and every row says how it matched. Supports TikTok, Instagram, YouTube, Pinterest, Twitch, Threads, and podcasts. Charges $0.001 per run plus $0.008 per row returned, $0.004 per agency roster page rendered in the headless browser when render_unreadable_pages is on and the rendered page comes back readable, and $0.01 per Instagram bio fetch when one is needed. Contributes the public records it finds to a shared creator and agency pool that all users of this actor read from, so a later run reads what this one found; `contribute_to_shared_pool` is on by default and turning it off leaves the run reading the pool and writing nothing. Only public data already in the returned rows is contributed, nothing from your Apify account or your input, and a contribution is not charged. Requires an APIFY_TOKEN and consumes Apify credits. Read only.",
177
+ annotations: {
178
+ title: "Look Up an Influencer's Talent Agency or an Agency's Roster",
179
+ readOnlyHint: true,
180
+ destructiveHint: false,
181
+ idempotentHint: true,
182
+ openWorldHint: true,
183
+ },
184
+ inputSchema: {
185
+ handles: z.array(z.string()).optional().describe("One per line. A profile URL on any supported platform (https://www.tiktok.com/@name, https://www.instagram.com/name/, https://www.youtube.com/@name, a Pinterest, Twitch, or Threads profile, an Apple Podcasts show page, or a Spotify show), or platform:@handle (tiktok:@name). A bare @handle needs `platforms` and is looked up on each listed platform. One entry is a single run; a list is a batch. Duplicates are removed before any fetch."),
186
+ platforms: z.array(z.enum(["tiktok", "instagram", "youtube", "pinterest", "twitch", "threads", "podcast"])).optional().describe("Which platforms a bare @handle is looked up on. A full profile URL carries its own platform and ignores this. This actor does not search; pass the creators you want read. Supported: TikTok, Instagram, YouTube, Pinterest, Twitch, Threads, and podcasts. Not X, not Facebook pages, not LinkedIn."),
187
+ agency_domains: z.array(z.string()).optional().describe("Agency to roster: one domain per line (for example viralnation.com). Returns one row per creator on the agency's public roster."),
188
+ agency_names: z.array(z.string()).optional().describe("Agency to roster by name when you do not have the domain. Exact or partial name match against the list."),
189
+ render_unreadable_pages: z.boolean().optional().describe("Off by default. Some agency roster pages build their talent grid in the browser and return an empty shell to a plain fetch, so the roster reads as empty. Turn this on to render those pages in a headless browser and read the roster from the rendered page. Charged per page rendered (event browser-render) to cover the browser compute, and only when the rendered page comes back readable. A page that answers with a bot challenge is recorded as blocked and is never rendered."),
190
+ escalate_on_block: z.boolean().optional().describe("On by default. A profile fetch that comes back as a bot detection page is retried once over the residential proxy. On Instagram the bio, bio link, and following are read from the profile page over residential when the embed and the datacenter API did not carry them, and a page that comes back readable charges instagram-bio-fetch ($0.010). Uncheck it to never pay that event: a blocked profile then returns a labeled error row, and Instagram rows keep an empty bio and bio link on about half of the reads."),
191
+ batch_size: z.number().int().min(1).max(10).optional().describe("Rows fetched at once. Leave empty for the measured per platform default; the measurement is in the README. Higher is faster and, above the measured point, loses rows."),
192
+ // Shared pool toggle. The actor input field shipped in build 0.1.11; this mirror exists so an
193
+ // MCP caller can turn the contribution off.
194
+ contribute_to_shared_pool: z.boolean().optional().describe("On by default. The run contributes the public records it finds to a shared creator and agency pool that all users of this actor read from, so a later run reads what this one found. Only public data that is already in your own output rows is sent: nothing from your Apify account, your input list, your API keys, or your own notes. Nothing is charged for a contribution. Turn this off and the run still reads the pool and writes nothing to it. Default: true."),
195
+ },
196
+ }, async (args) => runActor("zCuX4Mgyg6JvXgGzd", "Influencer Talent Agency Lookup", compact(args)));
197
+ const transport = new StdioServerTransport();
198
+ await server.connect(transport);
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@mambalabsdev/mcp-talent-agency-lookup",
3
+ "displayName": "Influencer Talent Agency Lookup",
4
+ "version": "1.0.1",
5
+ "description": "MCP server for the Mamba Labs Influencer Talent Agency Lookup actor on Apify. Creator handles in, the talent agency behind each one out, or an agency in, its public roster out.",
6
+ "type": "module",
7
+ "bin": {
8
+ "mcp-talent-agency-lookup": "./build/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc && chmod +x build/index.js",
12
+ "test": "npm run build && node --test test/*.test.js",
13
+ "prepublishOnly": "npm run build"
14
+ },
15
+ "keywords": [
16
+ "mcp",
17
+ "model-context-protocol",
18
+ "influencer-marketing",
19
+ "talent-agency",
20
+ "creator-roster",
21
+ "apify",
22
+ "clay"
23
+ ],
24
+ "tags": [
25
+ "mcp",
26
+ "influencer-marketing",
27
+ "talent-agency",
28
+ "creator-roster",
29
+ "apify",
30
+ "clay"
31
+ ],
32
+ "category": "productivity",
33
+ "author": {
34
+ "name": "Mamba Labs",
35
+ "email": "labs@mambamindsetgrowth.com"
36
+ },
37
+ "license": "MIT",
38
+ "logo": "https://mmg-brand-assets.netlify.app/apify-actor-pngs/apify_webhook.png",
39
+ "homepage": "https://mambabuilt.com",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/mambalabsdev/mcp-talent-agency-lookup"
43
+ },
44
+ "engines": {
45
+ "node": ">=18"
46
+ },
47
+ "dependencies": {
48
+ "@modelcontextprotocol/sdk": "^1.12.0",
49
+ "zod": "^4.5.4"
50
+ },
51
+ "devDependencies": {
52
+ "@types/node": "^26.4.1",
53
+ "typescript": "5.9.3"
54
+ },
55
+ "files": [
56
+ "build",
57
+ "README.md",
58
+ "LICENSE",
59
+ "SECURITY.md"
60
+ ],
61
+ "mcpServers": ".mcp.json",
62
+ "mcpName": "com.mambabuilt/mcp-talent-agency-lookup"
63
+ }