campertunity-mcp-server 0.0.7 → 0.1.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.
package/README.md CHANGED
@@ -6,6 +6,21 @@ This server implements the Model Context Protocol (MCP) for Campertunity, provid
6
6
 
7
7
  ## MCP Client Config
8
8
 
9
+ ```
10
+ {
11
+ "mcpServers": {
12
+ "campground-search-mcp-server": {
13
+ "command": "npx",
14
+ "args": ["-y", "campertunity-mcp-server@latest"]
15
+ }
16
+ }
17
+ }
18
+ ```
19
+
20
+ ## Setup
21
+
22
+ No API key is required to get started. To get higher rate limits, get an API key from [https://campertunity.com/mcp](https://campertunity.com/mcp) and set it as an environment variable:
23
+
9
24
  ```
10
25
  {
11
26
  "mcpServers": {
@@ -20,20 +35,13 @@ This server implements the Model Context Protocol (MCP) for Campertunity, provid
20
35
  }
21
36
  ```
22
37
 
23
- ## Setup
24
-
25
- 1. Get your API key from [https://campertunity.com/mcp](https://campertunity.com/mcp)
26
- 2. Set the environment variable:
27
- ```
28
- CAMPERTUNITY_API_KEY=your_api_key_here
29
- ```
30
-
31
38
  ## Available Tools
32
39
 
33
- ### place-search
34
- Search for camping places with various filters and criteria.
40
+ ### listing-search
41
+ Search for camping listings with various filters and criteria, or browse all listings with cursor pagination.
35
42
  - **Parameters:**
36
43
  - `limit`: Number of results (default: 50, max: 1000)
44
+ - `cursor`: Pagination cursor from a previous response (for browsing without search params)
37
45
  - `startDate`: Start date for availability (YYYY-MM-DD)
38
46
  - `endDate`: End date for availability (YYYY-MM-DD)
39
47
  - `adults`: Number of adults (default: 1)
@@ -41,26 +49,30 @@ Search for camping places with various filters and criteria.
41
49
  - `latitude`: Center point latitude
42
50
  - `longitude`: Center point longitude
43
51
  - `radius`: Search radius in kilometers (default: 20)
52
+ - `region`: Region/state to search in (geocoded if lat/lng not provided)
53
+ - `city`: City to search in (geocoded if lat/lng not provided)
54
+ - `country`: Country to search in (geocoded if lat/lng not provided)
55
+ - `countryCode`: Country code to search in, e.g. "US", "CA" (geocoded if lat/lng not provided)
44
56
  - `filters`: Array of tags to filter by (see Tag enum below)
45
57
  - `campgroundDescription`: Natural language description of desired campground features
46
58
 
47
- ### place-details
48
- Get detailed information about a specific camping place.
59
+ ### listing-details
60
+ Get detailed information about a specific listing.
49
61
  - **Parameters:**
50
- - `placeId`: ID of the place to get details for
62
+ - `listingId`: ID of the listing to get details for
51
63
 
52
- ### place-availability
53
- Check availability of camping sites at a specific place.
64
+ ### listing-availability
65
+ Check availability of camping sites at a specific listing.
54
66
  - **Parameters:**
55
- - `placeId`: ID of the place to check
67
+ - `listingId`: ID of the listing to check
56
68
  - `siteIds`: Optional array of specific site IDs to check
57
69
  - `startDate`: Start date (YYYY-MM-DD)
58
70
  - `endDate`: End date (YYYY-MM-DD)
59
71
 
60
- ### place-book
61
- Book a camping site.
72
+ ### listing-book
73
+ Get a booking URL for a listing.
62
74
  - **Parameters:**
63
- - `placeId`: ID of the place to book
75
+ - `listingId`: ID of the listing to book
64
76
  - `startDate`: Start date (YYYY-MM-DD)
65
77
  - `endDate`: End date (YYYY-MM-DD)
66
78
  - `adults`: Number of adults (default: 1)
@@ -1,15 +1,17 @@
1
1
  const CAMPERTUNITY_API_URL = process.env.CAMPERTUNITY_API_URL || "https://campertunity.com/public/api";
2
2
  const CAMPERTUNITY_API_KEY = process.env.CAMPERTUNITY_API_KEY;
3
- if (!CAMPERTUNITY_API_KEY) {
4
- throw new Error("CAMPERTUNITY_API_KEY environment variable is required");
5
- }
6
3
  export class CampertunityClient {
4
+ get headers() {
5
+ const h = {};
6
+ if (CAMPERTUNITY_API_KEY) {
7
+ h["Authorization"] = `Bearer ${CAMPERTUNITY_API_KEY}`;
8
+ }
9
+ return h;
10
+ }
7
11
  async get(path) {
8
12
  const response = await fetch(`${CAMPERTUNITY_API_URL}${path}`, {
9
13
  method: "GET",
10
- headers: {
11
- Authorization: `Bearer ${CAMPERTUNITY_API_KEY}`,
12
- },
14
+ headers: this.headers,
13
15
  });
14
16
  if (!response.ok) {
15
17
  throw new Error(`Campertunity API error: ${response.statusText}`);
@@ -20,7 +22,7 @@ export class CampertunityClient {
20
22
  const response = await fetch(`${CAMPERTUNITY_API_URL}${path}`, {
21
23
  method: "POST",
22
24
  headers: {
23
- Authorization: `Bearer ${CAMPERTUNITY_API_KEY}`,
25
+ ...this.headers,
24
26
  "Content-Type": "application/json",
25
27
  },
26
28
  body: JSON.stringify(data),
package/dist/index.js CHANGED
@@ -2,10 +2,10 @@
2
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
3
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
4
  import { CampertunityClient } from "./campertunity/client.js";
5
- import { placeAvailabilityTool } from "./tools/place_availability.js";
6
- import { placeBookTool } from "./tools/place_book.js";
7
- import { placeDetailsTool } from "./tools/place_details.js";
8
- import { placeSearchTool } from "./tools/place_search.js";
5
+ import { listingAvailabilityTool } from "./tools/listing_availability.js";
6
+ import { listingBookTool } from "./tools/listing_book.js";
7
+ import { listingDetailsTool } from "./tools/listing_details.js";
8
+ import { listingSearchTool } from "./tools/listing_search.js";
9
9
  const campertunityClient = new CampertunityClient();
10
10
  const server = new McpServer({
11
11
  name: "campertunity-model-context-protocol-server",
@@ -15,10 +15,10 @@ const server = new McpServer({
15
15
  tools: {},
16
16
  },
17
17
  });
18
- placeAvailabilityTool(server, campertunityClient);
19
- placeBookTool(server, campertunityClient);
20
- placeDetailsTool(server, campertunityClient);
21
- placeSearchTool(server, campertunityClient);
18
+ listingAvailabilityTool(server, campertunityClient);
19
+ listingBookTool(server, campertunityClient);
20
+ listingDetailsTool(server, campertunityClient);
21
+ listingSearchTool(server, campertunityClient);
22
22
  async function runServer() {
23
23
  const transport = new StdioServerTransport();
24
24
  await server.connect(transport);
@@ -1,14 +1,13 @@
1
1
  import { z } from 'zod';
2
- export const placeAvailabilityTool = (server, campertunityClient) => {
3
- server.tool('place-availability', {
4
- placeId: z.string().describe('The id of the place to check availability for.'),
2
+ export const listingAvailabilityTool = (server, campertunityClient) => {
3
+ server.tool('listing-availability', 'Check availability for specific campsites at a listing', {
4
+ listingId: z.string().describe('The id of the listing to check availability for.'),
5
5
  siteIds: z.array(z.string()).optional().describe('The ids of the sites to check availability for.'),
6
6
  startDate: z.string().describe('The start date to check availability for. Format: YYYY-MM-DD'),
7
7
  endDate: z.string().describe('The end date to check availability for. Format: YYYY-MM-DD'),
8
- }, async ({ placeId, siteIds, startDate, endDate }) => {
8
+ }, async ({ listingId, siteIds, startDate, endDate }) => {
9
9
  try {
10
- const availability = await campertunityClient.post(`/place/availability`, {
11
- placeId,
10
+ const availability = await campertunityClient.post(`/listings/campgrounds/${encodeURIComponent(listingId)}/availability`, {
12
11
  siteIds,
13
12
  startDate,
14
13
  endDate,
@@ -1,22 +1,21 @@
1
1
  import { z } from 'zod';
2
- export const placeBookTool = (server, campertunityClient) => {
3
- server.tool('place-book', {
4
- placeId: z.string().describe('The id of the place to book.'),
2
+ export const listingBookTool = (server, campertunityClient) => {
3
+ server.tool('listing-book', 'Get a booking URL for a campground or recreation site', {
4
+ listingId: z.string().describe('The id of the listing to book.'),
5
5
  startDate: z.string().optional().describe('The start date of the booking. Format: YYYY-MM-DD'),
6
6
  endDate: z.string().optional().describe('The end date of the booking. Format: YYYY-MM-DD'),
7
7
  adults: z.number().default(1).describe('Number of adults. Default is 1.'),
8
8
  children: z.number().default(0).describe('Number of children. Default is 0.'),
9
- }, async ({ placeId, startDate, endDate, adults, children }) => {
9
+ }, async ({ listingId, startDate, endDate, adults, children }) => {
10
10
  try {
11
- const availability = await campertunityClient.post(`/place/book`, {
12
- placeId,
11
+ const result = await campertunityClient.post(`/listings/campgrounds/${encodeURIComponent(listingId)}/book`, {
13
12
  startDate,
14
13
  endDate,
15
14
  adults,
16
15
  children,
17
16
  });
18
17
  return {
19
- content: [{ type: 'text', text: JSON.stringify(availability), mimeType: 'application/json' }],
18
+ content: [{ type: 'text', text: JSON.stringify(result), mimeType: 'application/json' }],
20
19
  };
21
20
  }
22
21
  catch (error) {
@@ -1,16 +1,15 @@
1
1
  import { z } from 'zod';
2
- // NOTE: This is a tool since tools are more supported by the MCP protocol
3
- export const placeDetailsTool = (server, campertunityClient) => {
4
- server.tool('place-details', {
5
- placeId: z.string().describe('The id of the place to get details for.'),
6
- }, async ({ placeId }) => {
2
+ export const listingDetailsTool = (server, campertunityClient) => {
3
+ server.tool('listing-details', 'Get detailed information about a specific listing', {
4
+ listingId: z.string().describe('The id of the listing to get details for.'),
5
+ }, async ({ listingId }) => {
7
6
  try {
8
- const place = await campertunityClient.get(`/place/details?placeId=${placeId}`);
7
+ const listing = await campertunityClient.get(`/listings/campgrounds/${encodeURIComponent(listingId)}`);
9
8
  return {
10
9
  content: [
11
10
  {
12
11
  type: 'text',
13
- text: JSON.stringify(place),
12
+ text: JSON.stringify(listing),
14
13
  mimeType: 'application/json',
15
14
  },
16
15
  ],
@@ -49,9 +49,10 @@ export var Tag;
49
49
  Tag["waterfall"] = "waterfall";
50
50
  Tag["creek"] = "creek";
51
51
  })(Tag || (Tag = {}));
52
- export const placeSearchTool = (server, campertunityClient) => {
53
- server.tool('place-search', {
54
- limit: z.number().default(50).optional().describe('Number of places to return. Default is 50, max is 1000.'),
52
+ export const listingSearchTool = (server, campertunityClient) => {
53
+ server.tool('listing-search', 'Search for campgrounds and outdoor recreation listings', {
54
+ limit: z.number().default(50).optional().describe('Number of listings to return. Default is 50, max is 1000.'),
55
+ cursor: z.string().optional().describe('Pagination cursor from a previous response. Used for browsing all listings without search params.'),
55
56
  startDate: z.string().optional().describe('Start date for availability search. Format: YYYY-MM-DD'),
56
57
  endDate: z.string().optional().describe('End date for availability search. Format: YYYY-MM-DD'),
57
58
  adults: z.number().optional().describe('Number of adults. Default is 1.'),
@@ -59,13 +60,19 @@ export const placeSearchTool = (server, campertunityClient) => {
59
60
  latitude: z.number().optional().describe('Latitude to filter by.'),
60
61
  longitude: z.number().optional().describe('Longitude to filter by.'),
61
62
  radius: z.number().optional().default(20).describe('Radius to filter by (in km).'),
62
- filters: z.array(z.enum(Object.values(Tag))).optional().describe('Filter out places that have specific tags.'),
63
+ region: z.string().optional().describe('Region/state to search in. Will be geocoded to coordinates if latitude/longitude not provided.'),
64
+ city: z.string().optional().describe('City to search in. Will be geocoded to coordinates if latitude/longitude not provided.'),
65
+ country: z.string().optional().describe('Country to search in. Will be geocoded to coordinates if latitude/longitude not provided.'),
66
+ countryCode: z.string().optional().describe('Country code to search in (e.g. "US", "CA"). Will be geocoded to coordinates if latitude/longitude not provided.'),
67
+ filters: z.array(z.enum(Object.values(Tag))).optional().describe('Filter out listings that have specific tags.'),
63
68
  campgroundDescription: z.string().optional().describe('Describe the campground you are looking for. Note: not the location, but something about the campground like "has a pool" or "near a lake" or "has a playground"'),
64
- }, async ({ limit, startDate, endDate, adults, children, latitude, longitude, radius, filters, campgroundDescription }) => {
69
+ }, async ({ limit, cursor, startDate, endDate, adults, children, latitude, longitude, radius, region, city, country, countryCode, filters, campgroundDescription }) => {
65
70
  try {
66
71
  const params = new URLSearchParams();
67
72
  if (limit)
68
73
  params.set('limit', limit.toString());
74
+ if (cursor)
75
+ params.set('cursor', cursor);
69
76
  if (startDate)
70
77
  params.set('startDate', startDate);
71
78
  if (endDate)
@@ -80,13 +87,21 @@ export const placeSearchTool = (server, campertunityClient) => {
80
87
  params.set('longitude', longitude.toString());
81
88
  if (radius)
82
89
  params.set('radius', radius.toString());
90
+ if (region)
91
+ params.set('region', region);
92
+ if (city)
93
+ params.set('city', city);
94
+ if (country)
95
+ params.set('country', country);
96
+ if (countryCode)
97
+ params.set('countryCode', countryCode);
83
98
  if (filters)
84
99
  params.set('filters', filters.join(','));
85
100
  if (campgroundDescription)
86
101
  params.set('campgroundDescription', campgroundDescription);
87
- const places = await campertunityClient.get(`/place/search?${params.toString()}`);
102
+ const listings = await campertunityClient.get(`/listings?${params.toString()}`);
88
103
  return {
89
- content: [{ type: 'text', text: JSON.stringify(places), mimeType: 'application/json' }],
104
+ content: [{ type: 'text', text: JSON.stringify(listings), mimeType: 'application/json' }],
90
105
  };
91
106
  }
92
107
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "campertunity-mcp-server",
3
- "version": "0.0.7",
3
+ "version": "0.1.0",
4
4
  "description": "MCP Server for Campertunity - A Model Context Protocol server for interacting with camping and outdoor recreation data",
5
5
  "type": "module",
6
6
  "bin": {