@hasdata/yellowpages-mcp 1.0.0

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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +380 -0
  3. package/index.mjs +24 -0
  4. package/package.json +41 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 HasData
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,380 @@
1
+ # Yellow Pages MCP Server
2
+
3
+ <!-- mcp-name: com.hasdata/yellowpages -->
4
+
5
+ A hosted Model Context Protocol (MCP) server that gives Claude, Cursor, Windsurf and any other MCP client two read-only Yellow Pages tools. Search local businesses by keyword and location, then read one listing in full with its phone, hours, services and photos, both as structured JSON, with nothing to host.
6
+
7
+ It reads public Yellow Pages listings that a signed-out visitor can see, on `yellowpages.com` and `yellowpages.ca`.
8
+
9
+ **1,000 free credits every month, no card required**, which is 100 Yellow Pages calls at the 10-credit rate.
10
+
11
+ ```
12
+ https://mcp.hasdata.com/api/mcp?apis=yellowpages
13
+ ```
14
+
15
+ [![Glama score](https://glama.ai/mcp/servers/HasData/yellowpages-mcp/badges/score.svg)](https://glama.ai/mcp/servers/HasData/yellowpages-mcp)
16
+ [![tool contract](https://github.com/HasData/yellowpages-mcp/actions/workflows/contract.yml/badge.svg)](https://github.com/HasData/yellowpages-mcp/actions/workflows/contract.yml)
17
+ [![MCP](https://img.shields.io/badge/MCP-remote%20%7C%20streamable%20HTTP-6366f1?style=flat-square)](https://mcp.hasdata.com/api/mcp?apis=yellowpages)
18
+ [![Tools](https://img.shields.io/badge/tools-2-10b981?style=flat-square)](#tools)
19
+ [![npm](https://img.shields.io/npm/v/@hasdata/yellowpages-mcp?style=flat-square&logo=npm&label=npm&color=cb3837)](https://www.npmjs.com/package/@hasdata/yellowpages-mcp)
20
+ [![PyPI](https://img.shields.io/pypi/v/hasdata-yellowpages-mcp?style=flat-square&logo=pypi&logoColor=white&label=PyPI&color=3775a9)](https://pypi.org/project/hasdata-yellowpages-mcp/)
21
+ [![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](LICENSE)
22
+
23
+ ## Contents
24
+
25
+ - [What you need](#what-you-need)
26
+ - [Quick start](#quick-start)
27
+ - [Example prompts](#example-prompts)
28
+ - [Tools](#tools)
29
+ - [Errors and failure paths](#errors-and-failure-paths)
30
+ - [Pricing, free tier and limits](#pricing-free-tier-and-limits)
31
+ - [Tool selection](#tool-selection)
32
+ - [How it compares](#how-it-compares)
33
+ - [FAQ](#faq)
34
+ - [HasData links](#hasdata-links)
35
+ - [Development](#development)
36
+ - [Contributing](#contributing)
37
+ - [License](#license)
38
+
39
+ ## What you need
40
+
41
+ An MCP client and a HasData API key from the [dashboard](https://app.hasdata.com/sign-up?utm_source=github&utm_medium=syndication&utm_campaign=yellowpages-mcp), free to create with no card, and the free tier covers about 100 calls a month at the 10-credit rate. This is a remote server, so the simplest path is a URL and an `x-api-key` header, with no container to run. A client that only speaks stdio reaches it through a thin launcher, published as `@hasdata/yellowpages-mcp` on npm and `hasdata-yellowpages-mcp` on PyPI, shown below.
42
+
43
+ ## Quick start
44
+
45
+ The server URL is the same for every client. We run it hands-on in Claude Code and Claude Desktop. The other blocks follow each client's own documented format for a remote server.
46
+
47
+ | Field | Value |
48
+ | :--- | :--- |
49
+ | URL | `https://mcp.hasdata.com/api/mcp?apis=yellowpages` |
50
+ | Transport | HTTP, streamable |
51
+ | Auth header | `x-api-key: HASDATA_API_KEY` |
52
+
53
+ Clients with OAuth support can add the same URL as a connector and sign in without putting a key in a config file.
54
+
55
+ <details>
56
+ <summary><b>Claude Code</b></summary>
57
+
58
+ ```bash
59
+ claude mcp add --transport http yellowpages "https://mcp.hasdata.com/api/mcp?apis=yellowpages" \
60
+ --header "x-api-key: HASDATA_API_KEY"
61
+ ```
62
+
63
+ </details>
64
+
65
+ <details>
66
+ <summary><b>Claude Desktop</b></summary>
67
+
68
+ Settings, then Connectors, then Add custom connector, then paste `https://mcp.hasdata.com/api/mcp?apis=yellowpages` and sign in.
69
+
70
+ For the config-file route, Claude Desktop loads only local (stdio) servers, so it reaches a remote server through a stdio launcher. The `@hasdata/yellowpages-mcp` package is that launcher, and it reads the key from the environment. Add this to `claude_desktop_config.json`:
71
+
72
+ ```json
73
+ {
74
+ "mcpServers": {
75
+ "yellowpages": {
76
+ "command": "npx",
77
+ "args": ["-y", "@hasdata/yellowpages-mcp"],
78
+ "env": { "HASDATA_API_KEY": "YOUR_KEY" }
79
+ }
80
+ }
81
+ }
82
+ ```
83
+
84
+ For Python instead of Node, swap the launcher for the PyPI package, which `uvx` runs without a manual install:
85
+
86
+ ```json
87
+ {
88
+ "mcpServers": {
89
+ "yellowpages": {
90
+ "command": "uvx",
91
+ "args": ["hasdata-yellowpages-mcp"],
92
+ "env": { "HASDATA_API_KEY": "YOUR_KEY" }
93
+ }
94
+ }
95
+ }
96
+ ```
97
+
98
+ </details>
99
+
100
+ <details>
101
+ <summary><b>Cursor</b></summary>
102
+
103
+ `~/.cursor/mcp.json` for every project, or `.cursor/mcp.json` for one:
104
+
105
+ ```json
106
+ {
107
+ "mcpServers": {
108
+ "yellowpages": {
109
+ "url": "https://mcp.hasdata.com/api/mcp?apis=yellowpages",
110
+ "headers": { "x-api-key": "HASDATA_API_KEY" }
111
+ }
112
+ }
113
+ }
114
+ ```
115
+
116
+ </details>
117
+
118
+ <details>
119
+ <summary><b>Windsurf</b></summary>
120
+
121
+ `~/.codeium/windsurf/mcp_config.json`. Windsurf calls the field `serverUrl`, not `url`:
122
+
123
+ ```json
124
+ {
125
+ "mcpServers": {
126
+ "yellowpages": {
127
+ "serverUrl": "https://mcp.hasdata.com/api/mcp?apis=yellowpages",
128
+ "headers": { "x-api-key": "HASDATA_API_KEY" }
129
+ }
130
+ }
131
+ }
132
+ ```
133
+
134
+ </details>
135
+
136
+ <details>
137
+ <summary><b>VS Code</b></summary>
138
+
139
+ `.vscode/mcp.json` in the workspace:
140
+
141
+ ```json
142
+ {
143
+ "servers": {
144
+ "yellowpages": {
145
+ "type": "http",
146
+ "url": "https://mcp.hasdata.com/api/mcp?apis=yellowpages",
147
+ "headers": { "x-api-key": "HASDATA_API_KEY" }
148
+ }
149
+ }
150
+ }
151
+ ```
152
+
153
+ </details>
154
+
155
+ ## Example prompts
156
+
157
+ Each of these lands on one tool, or on two in sequence when the second needs the URL the first returns.
158
+
159
+ - Find plumbers in Austin, TX and rank them by rating against review count.
160
+ - List every HVAC contractor in this zipcode with a phone number and hours.
161
+ - Which of these businesses have been trading for more than 20 years?
162
+ - Read this Yellow Pages listing and tell me which brands they service.
163
+ - Pull page 2 and 3 of roofers in Austin and merge them into one list.
164
+ - Sort dentists in this city by average rating rather than by relevance.
165
+
166
+ A prompt that names a niche and a city goes to the search tool. Reading services, brands and payment methods takes a second call per business, so a prospect list wants the search tool and an enrichment pass wants the place tool.
167
+
168
+ ## Tools
169
+
170
+ Two tools, 10 credits per successful call.
171
+
172
+ ### Get Yellow Pages search results
173
+
174
+ [`hasdata_yellowpages_search_getSearchResults`](https://docs.hasdata.com/apis/yellowpages/search?utm_source=github&utm_medium=syndication&utm_campaign=yellowpages-mcp)
175
+
176
+ A page of businesses for a keyword in a place, 30 to a page.
177
+
178
+ | Parameter | Type | Required | Notes |
179
+ | :--- | :--- | :--- | :--- |
180
+ | `keyword` | string | yes | What to search for, such as `plumber` |
181
+ | `location` | string | yes | Where to search, such as `Austin, TX` |
182
+ | `sort` | string | | `default`, `distance`, `averageRating` or `name` |
183
+ | `domain` | string | | `www.yellowpages.com` or `www.yellowpages.ca` |
184
+ | `page` | number | | Result page, starting at 1 |
185
+
186
+ Returns `searchInformation` with the echoed query and `totalResults`, an `organicResults` array, and `pagination` with `currentPage`, `totalPages`, `perPage`, `nextPageUrl` and `otherPageUrls`.
187
+
188
+ Almost every field on a result is optional, because Yellow Pages shows what each business paid for or filled in. Across the 30 results in the sample, `title`, `phone`, `url`, `categories`, `country` and `position` arrived on all of them, `address` on 22, `rating` and `reviews` on 11, and `contactUs` on 5. Read defensively rather than assuming a shape.
189
+
190
+ ```json
191
+ {
192
+ "position": 2,
193
+ "title": "Clarke Kent Plumbing",
194
+ "url": "https://www.yellowpages.com/austin-tx/mip/clarke-kent-plumbing-10674347?lid=1002194068759",
195
+ "phone": "(512) 766-0970",
196
+ "address": "1408 W Ben White Blvd",
197
+ "city": "Austin",
198
+ "region": "TX",
199
+ "zipcode": "78704",
200
+ "country": "US",
201
+ "website": "http://www.clarkekentplumbing.com",
202
+ "directions": "https://www.yellowpages.com/listings/1002194068759/directions",
203
+ "categories": ["Plumbers", "Plumbing-Drain & Sewer Cleaning"],
204
+ "rating": 2.87,
205
+ "reviews": 15,
206
+ "workingHours": ["Mo-Fr 09:00-17:00"],
207
+ "openState": "open now",
208
+ "badges": ["40 Years in Business", "1 Year with Yellow Pages"]
209
+ }
210
+ ```
211
+
212
+ ### Get Yellow Pages place details
213
+
214
+ [`hasdata_yellowpages_place_getPlaceDetails`](https://docs.hasdata.com/apis/yellowpages/place?utm_source=github&utm_medium=syndication&utm_campaign=yellowpages-mcp)
215
+
216
+ One listing in full, by its Yellow Pages URL.
217
+
218
+ | Parameter | Type | Required | Notes |
219
+ | :--- | :--- | :--- | :--- |
220
+ | `url` | string | yes | The listing URL, as the search tool returns it |
221
+
222
+ Returns four blocks rather than one flat object.
223
+
224
+ `overview` repeats the name, location, phone, hours, badges and website, and adds `paymentAccepted` and a `breadcrumbs` array showing where the listing sits in the Yellow Pages taxonomy. `ratings` holds the star rating. `details` holds the long-form copy the business wrote. `images` is an array of photo URLs.
225
+
226
+ The `details` block is where the enrichment value sits, and it arrives as comma-joined text rather than as arrays. `generalInfo` is the business description, `servicesProducts` the service list, `brands` the brands they carry, `paymentMethod` the payment types and `categories` the full category list as one string.
227
+
228
+ ```json
229
+ {
230
+ "overview": {
231
+ "title": "ARS Rescue Rooter",
232
+ "phone": "(833) 947-9225",
233
+ "city": "Austin",
234
+ "region": "TX",
235
+ "zipcode": "78754",
236
+ "workingHours": ["Mo-Su"],
237
+ "openState": "Open 24 hours",
238
+ "paymentAccepted": "visa, amex, master card",
239
+ "badges": ["1 Year with Yellow Pages"],
240
+ "breadcrumbs": ["TX", "Austin", "Building Contractors", "Plumbers"]
241
+ },
242
+ "ratings": { "rating": 3.5 },
243
+ "details": {
244
+ "generalInfo": "ARS/Rescue Rooter has a proven track record of providing reliable, long-lasting repair services...",
245
+ "servicesProducts": "Air Conditioner Repair and Replacement, Air Duct Repair and Replacement, Air Filter Installation...",
246
+ "brands": "Goodman, Mitsubishi, Bosch, Diakin, Bradford White, April Aire Indoor Air Quality",
247
+ "paymentMethod": "visa, amex, master card"
248
+ },
249
+ "images": ["https://i4.ypcdn.com/blob/ce73451958465ab47dd7be41922973a98bc847af_640.jpg"]
250
+ }
251
+ ```
252
+
253
+ ## Errors and failure paths
254
+
255
+ Plan for these rather than assuming a happy path.
256
+
257
+ **For a review count, read the search result rather than the place detail.** On the search tool, `rating` and `reviews` are what they look like, such as `2.87` and `15`. On the place tool, `ratings.reviews` comes back equal to `ratings.rating` on every listing we checked, so it carries no count. Take the number from the search result and enrich from there.
258
+
259
+ **`ratings` can be absent from a place response entirely.** One of the four listings we pulled had no block at all rather than an empty one.
260
+
261
+ **`details` fields are comma-joined strings, and `categories` changes type between the tools.** In a search result `categories` is an array. In `details` it is one string. Split on the comma if you need a list, and expect the odd category name to contain one.
262
+
263
+ **`website` sometimes points back at Yellow Pages.** Several listings carry a `yellowpages.com` tracking URL in the field where you would expect the business's own site. Check the host before you follow it or store it.
264
+
265
+ **Years in business is a badge, not a field.** `badges` mixes two different things: how long the business has traded, as `"40 Years in Business"`, and how long it has paid Yellow Pages, as `"11 Years with Yellow Pages"`. Read the wording rather than the first number.
266
+
267
+ **A street address is not guaranteed.** `address` held the street line on 22 of 30 results, and `city`, `region` and `zipcode` arrive as separate fields alongside it. Build the address from the parts you have.
268
+
269
+ **`openState` is the state at the moment of the call.** It says `closed` or `Open 24 hours` for the time the request ran, so it is a snapshot rather than a property of the business. `workingHours` is the durable field.
270
+
271
+ **Paging is by URL, and a query can run long.** `pagination.totalPages` reached 17 for one city and one keyword, at 30 results a page. Cost scales with pages, so narrow the keyword before you walk them all.
272
+
273
+ Results that carry data also carry a `requestMetadata.id` worth quoting in support.
274
+
275
+ ## Pricing, free tier and limits
276
+
277
+ Each Yellow Pages tool costs **10 credits per successful call**. Response size does not change the price, so a 30-business page costs the same as one listing detail.
278
+
279
+ The free tier is **1,000 credits every month with no card**, which is 100 Yellow Pages calls at the base rate. It renews with the billing cycle, so a low-volume agent runs on the free tier indefinitely.
280
+
281
+ Paid plans start at **$49 a month** for 200,000 credits, which is 20,000 calls. The unit price falls with volume, from **$2.45 per 1,000 calls** on the entry plan to **$1.00** on Business, **$0.84** on Growth and **$0.74** on the largest [high-volume plans](https://hasdata.com/prices?utm_source=github&utm_medium=syndication&utm_campaign=yellowpages-mcp).
282
+
283
+ Your plan also sets concurrency. The free tier allows 1 request at a time, Startup 15, Business 30, Growth 50, and the high-volume plans run from 200 to 1,500. Retry on the 429 with a backoff in anything unattended, because an agent that enriches a page of businesses will reach the ceiling before you do.
284
+
285
+ A request that comes back non-200 is not billed. A successful call that finds nothing is still a call.
286
+
287
+ ## Tool selection
288
+
289
+ Start from what the prompt gives you. A niche and a city go to the search tool, and a Yellow Pages URL goes straight to the place tool.
290
+
291
+ Then weigh the enrichment. The search result already carries name, phone, address, categories, hours, badges and, where Yellow Pages shows them, the rating and review count. That covers a prospect list, a coverage count or a rating comparison in one call. The place tool adds the description, the service list, the brands and the photos, and it costs one call per business, so 30 businesses enriched cost 300 credits against the 10 the page cost.
292
+
293
+ Sort server-side when the question is about order. `sort: averageRating` is one parameter, where pulling several pages to sort locally is several calls.
294
+
295
+ ## How it compares
296
+
297
+ Yellow Pages has no public API, so the realistic alternatives are the two big local-data APIs.
298
+
299
+ | | Google Places API | Yelp Fusion API | This server |
300
+ | :--- | :--- | :--- | :--- |
301
+ | Eligibility | A billed Google Cloud project | An approved developer app | An API key |
302
+ | Services and brands | Not returned | Not returned | The `details` block |
303
+ | Payment methods | Not returned | Partly, as attributes | As written |
304
+ | Years in business | Not returned | Not returned | In `badges` |
305
+ | Photos | Via a separate billed call | Included | An array of URLs |
306
+ | Canada | Covered | Covered | `yellowpages.ca` |
307
+ | Coverage | Broadest | Consumer-heavy | Trades and services |
308
+
309
+ The row that decides it is what a listing says about itself. Google and Yelp return a structured record, and Yellow Pages returns the copy a contractor wrote about their own services, brands and payment terms, which is the part a trades prospect list is built on. For coverage, hours accuracy and consumer categories, Google Places is the stronger source.
310
+
311
+ ## FAQ
312
+
313
+ ### Is there an official Yellow Pages MCP server?
314
+
315
+ Yellow Pages does not publish one, and it does not publish a public API either. This one is maintained by HasData and reads public Yellow Pages listings.
316
+
317
+ ### What is a Yellow Pages MCP server?
318
+
319
+ An MCP server exposes tools an AI client can call. This one turns Yellow Pages searches and listings into JSON an agent can reason over, without a browser or a scraping library in your stack.
320
+
321
+ ### Do I need a Yellow Pages account?
322
+
323
+ No. The only credential is your HasData key.
324
+
325
+ ### Which countries are covered?
326
+
327
+ The US on `www.yellowpages.com` and Canada on `www.yellowpages.ca`. Pass `domain` to switch.
328
+
329
+ ### Why is a field missing from some results?
330
+
331
+ Because Yellow Pages shows what each business filled in or paid for. A rating arrived on 11 of 30 results in our sample and a street address on 22. Treat everything except the name, phone, URL and categories as optional.
332
+
333
+ ### How do I get a review count?
334
+
335
+ From the search result, where `reviews` is a count. The place tool's `ratings.reviews` mirrors the rating rather than counting reviews, so it is not the field for that.
336
+
337
+ ### Can I use this together with other HasData APIs?
338
+
339
+ Yes. One key covers everything, and one endpoint serves them all through the `apis` parameter. Point a client at `?apis=yellowpages,google_maps` to get both tool sets in one connection, or at [`mcp.hasdata.com/api/mcp`](https://docs.hasdata.com/mcp-server?utm_source=github&utm_medium=syndication&utm_campaign=yellowpages-mcp) for the full catalogue.
340
+
341
+ ### Is HasData affiliated with Yellow Pages?
342
+
343
+ No. HasData is an independent service and is not affiliated with, endorsed by, or sponsored by Thryv or the Yellow Pages brand. Yellow Pages is a trademark of its respective owner. The tools work with publicly available data only, and you are responsible for using the results in line with the site's terms and the law that applies to you.
344
+
345
+ ### Compliance and personal data
346
+
347
+ These listings are business records, and the obvious use is a lead list. That is where the care belongs, because calling and texting the phone numbers you collect is regulated separately from collecting them. In the US the TCPA governs calls and texts to those numbers, including to businesses in several respects, and the FTC's telemarketing rules apply on top. A sole trader's listing can also carry their own name and mobile number, which makes it personal data as well as a business record. Collecting the list is the easy part, so check what you are allowed to do with it before you build the outreach.
348
+
349
+ ## HasData links
350
+
351
+ - [Yellow Pages Scraper API](https://hasdata.com/apis/yellow-pages-api?utm_source=github&utm_medium=syndication&utm_campaign=yellowpages-mcp), the REST endpoints behind these tools
352
+ - [API documentation](https://docs.hasdata.com/apis/yellowpages/search?utm_source=github&utm_medium=syndication&utm_campaign=yellowpages-mcp)
353
+ - [MCP server documentation](https://docs.hasdata.com/mcp-server?utm_source=github&utm_medium=syndication&utm_campaign=yellowpages-mcp)
354
+ - [Pricing](https://hasdata.com/prices?utm_source=github&utm_medium=syndication&utm_campaign=yellowpages-mcp)
355
+ - [Dashboard](https://app.hasdata.com/sign-up?utm_source=github&utm_medium=syndication&utm_campaign=yellowpages-mcp)
356
+
357
+ Other HasData MCP servers: [Google Maps](https://github.com/HasData/google-maps-mcp), [Yelp](https://github.com/HasData/yelp-mcp), [Google Search](https://github.com/HasData/google-search-mcp), [Google Trends](https://github.com/HasData/google-trends-mcp), [Google Flights](https://github.com/HasData/google-flights-mcp), [DuckDuckGo](https://github.com/HasData/duckduckgo-mcp), [YouTube](https://github.com/HasData/youtube-mcp), [TikTok](https://github.com/HasData/tiktok-mcp), [Instagram](https://github.com/HasData/instagram-mcp), [Amazon](https://github.com/HasData/amazon-mcp), [Walmart](https://github.com/HasData/walmart-mcp), [Shopify](https://github.com/HasData/shopify-mcp), [Zillow](https://github.com/HasData/zillow-mcp), [Redfin](https://github.com/HasData/redfin-mcp), [Airbnb](https://github.com/HasData/airbnb-mcp), [Booking.com](https://github.com/HasData/booking-mcp), [Indeed](https://github.com/HasData/indeed-mcp), [Glassdoor](https://github.com/HasData/glassdoor-mcp).
358
+
359
+ ## Development
360
+
361
+ The launcher is a thin stdio bridge to the remote server, so there is nothing to build.
362
+
363
+ ```bash
364
+ npm install
365
+ HASDATA_API_KEY=your_key_here npm test
366
+ ```
367
+
368
+ The tests in `test/` assert the tool contract, the part that can break without a commit here. They check that `?apis=yellowpages` returns the expected tool count, that no name changed, that every tool still declares its required parameters and carries a description, that `sort` still offers the four orders, and that the key in use is actually accepted. That last check calls a tool for real and costs 10 credits, which is the price of a canary that can fail for the right reason.
369
+
370
+ One test asserts that a live search still carries a real review count on the results that have one. The README sends readers to the search tool for that number precisely because the place tool does not give it, and the advice only holds while the field does.
371
+
372
+ The contract suite also runs weekly on a schedule, because the upstream tool list can change without anyone touching this repository.
373
+
374
+ ## Contributing
375
+
376
+ A tool table, a response sample or a documented behaviour that does not match reality is worth an issue. There is a template for exactly that. Pull requests are welcome for the same, and for anything in the launcher.
377
+
378
+ ## License
379
+
380
+ MIT, see [LICENSE](LICENSE).
package/index.mjs ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ // Thin launcher: connects an MCP client to HasData's hosted Yellow Pages MCP server
3
+ // (streamable HTTP) through the mcp-remote stdio bridge. The server runs remotely.
4
+ // This package only proxies, so nothing here scrapes anything.
5
+ import { spawn } from 'node:child_process';
6
+ import { createRequire } from 'node:module';
7
+ import { dirname, join } from 'node:path';
8
+
9
+ const URL = 'https://mcp.hasdata.com/api/mcp?apis=yellowpages';
10
+ const key = process.env.HASDATA_API_KEY;
11
+ if (!key) {
12
+ process.stderr.write('HASDATA_API_KEY is not set. Create a free key at https://app.hasdata.com and set HASDATA_API_KEY.\n');
13
+ process.exit(1);
14
+ }
15
+ // Resolve mcp-remote's CLI from its own package.json bin, so a future layout change
16
+ // or an exports map does not break a hardcoded deep path.
17
+ const require = createRequire(import.meta.url);
18
+ const pkg = require('mcp-remote/package.json');
19
+ const proxy = join(dirname(require.resolve('mcp-remote/package.json')), pkg.bin['mcp-remote']);
20
+ const child = spawn(process.execPath, [proxy, URL, '--header', `x-api-key:${key}`], { stdio: 'inherit' });
21
+ child.on('exit', (code, signal) => {
22
+ if (signal) process.kill(process.pid, signal);
23
+ else process.exit(code ?? 0);
24
+ });
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@hasdata/yellowpages-mcp",
3
+ "mcpName": "com.hasdata/yellowpages",
4
+ "version": "1.0.0",
5
+ "description": "MCP server for Yellow Pages through HasData's hosted API: search local businesses by keyword and location, and read one listing in full with phone, hours and years in business. 1,000 free credits every month.",
6
+ "type": "module",
7
+ "bin": {
8
+ "hasdata-yellowpages-mcp": "index.mjs"
9
+ },
10
+ "files": [
11
+ "index.mjs",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "scripts": {
16
+ "test": "node --test"
17
+ },
18
+ "dependencies": {
19
+ "mcp-remote": "^0.1.43"
20
+ },
21
+ "engines": {
22
+ "node": ">=18"
23
+ },
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/HasData/yellowpages-mcp.git"
28
+ },
29
+ "homepage": "https://hasdata.com/apis/yellow-pages-api",
30
+ "keywords": [
31
+ "mcp",
32
+ "yellowpages",
33
+ "yellowpages-mcp",
34
+ "local-business",
35
+ "lead-generation",
36
+ "local-seo",
37
+ "model-context-protocol",
38
+ "hasdata",
39
+ "scraper"
40
+ ]
41
+ }