#
tokens: 8907/50000 13/13 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .env.example
├── .gitignore
├── build
│   ├── api
│   │   ├── getBeerInfo.js
│   │   ├── getBeerSearch.js
│   │   └── getBreweryInfo.js
│   ├── config.js
│   ├── constants.js
│   ├── index.js
│   ├── libs
│   │   ├── format.js
│   │   └── guards.js
│   └── types
│       └── untappedApi.js
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── src
│   ├── api
│   │   ├── getBeerInfo.ts
│   │   ├── getBeerSearch.ts
│   │   └── getBreweryInfo.ts
│   ├── constants.ts
│   ├── index.ts
│   ├── libs
│   │   ├── format.ts
│   │   └── guards.ts
│   └── types
│       └── untappedApi.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------

```
# Untapped API
# Rename this file to .env after you add your values
UNTAPPED_API_CLIENT_ID=
UNTAPPED_API_CLIENT_SECRET=
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next
out

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# vuepress v2.x temp and cache directory
.temp
.cache

# Docusaurus cache and generated files
.docusaurus

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

# idea
.idea
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# untapped-mcp

A Untapped MCP server to be used with claude.

## Setup

### Get API Key

### Usage with Claude Desktop

Add the following to your `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "Untappd": {
      "command": "node",
      "args": ["/Users/user/projects/untapped-mcp/build/index.js"],
      "env": {
        "UNTAPPED_API_CLIENT_ID": "<YOUR_CLIENT_ID>",
        "UNTAPPED_API_CLIENT_SECRET": "<YOUR_CLIENT_SECRET>"
      }
    }
  }
}
```

```

--------------------------------------------------------------------------------
/src/constants.ts:
--------------------------------------------------------------------------------

```typescript
// Untapped
export const UNTAPPED_API_BASE = "https://api.untappd.com/v4/";
export const UNTAPPED_API_SEARCH = "search/beer";
export const UNTAPPED_API_INFO = "beer/info";
export const UNTAPPED_API_BREWERY_INFO = "/brewery/info/";

```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./build",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

```

--------------------------------------------------------------------------------
/src/libs/guards.ts:
--------------------------------------------------------------------------------

```typescript
import { UntappdApiErrorResponse } from "../types/untappedApi.js";

export function isUntappdApiError(
  value: unknown,
): value is UntappdApiErrorResponse {
  return (
    typeof value === "object" &&
    value !== null &&
    "meta" in value &&
    typeof (value as any).meta === "object" &&
    "code" in (value as any).meta &&
    "error_detail" in (value as any).meta &&
    "error_type" in (value as any).meta
  );
}

```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "untapped-mcp",
  "version": "1.0.0",
  "description": "A Untapped MCP server to be used with claude.",
  "main": "index.js",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/etoxin/untapped-mcp.git"
  },
  "keywords": [],
  "author": "Adam Lusted",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/etoxin/untapped-mcp/issues"
  },
  "homepage": "https://github.com/etoxin/untapped-mcp#readme",
  "type": "module",
  "bin": {
    "untapped": "./build/index.js"
  },
  "files": [
    "build"
  ],
  "scripts": {
    "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.6.1",
    "axios": "^1.8.2",
    "dotenv": "^16.4.7",
    "zod": "^3.24.2"
  },
  "devDependencies": {
    "@types/node": "^22.13.9",
    "typescript": "^5.8.2"
  }
}

```

--------------------------------------------------------------------------------
/src/api/getBeerInfo.ts:
--------------------------------------------------------------------------------

```typescript
import axios from "axios";
import { UNTAPPED_API_BASE, UNTAPPED_API_INFO } from "../constants.js";
import { isUntappdApiError } from "../libs/guards.js";
import { UntappdBeerInfoResult } from "../types/untappedApi.js";
import { config } from "../index.js";

export async function GetBeerInfo(bid: string) {
  try {
    const response = await axios.get<UntappdBeerInfoResult>(
      `${UNTAPPED_API_BASE}${UNTAPPED_API_INFO}/${bid}`,
      {
        params: {
          client_id: config.untappd.clientId,
          client_secret: config.untappd.clientSecret,
        },
      },
    );

    return response.data;
  } catch (e: unknown) {
    if (axios.isAxiosError(e) && e.response) {
      throw new Error(`HTTP error! status: ${e.response.status}`);
    }
    if (e instanceof Error) {
      return e.message;
    }
    if (isUntappdApiError(e)) {
      return e.meta.error_detail;
    }
    return "An unknown error occurred";
  }
}

```

--------------------------------------------------------------------------------
/src/api/getBreweryInfo.ts:
--------------------------------------------------------------------------------

```typescript
import axios from "axios";
import {
  UNTAPPED_API_BASE,
  UNTAPPED_API_BREWERY_INFO,
  UNTAPPED_API_INFO,
} from "../constants.js";
import { isUntappdApiError } from "../libs/guards.js";
import { UntappdBreweryInfoResult } from "../types/untappedApi.js";
import { config } from "../index.js";

export async function GetBreweryInfo(breweryId: string) {
  try {
    const response = await axios.get<UntappdBreweryInfoResult>(
      `${UNTAPPED_API_BASE}${UNTAPPED_API_BREWERY_INFO}/${breweryId}`,
      {
        params: {
          client_id: config.untappd.clientId,
          client_secret: config.untappd.clientSecret,
        },
      },
    );

    return response.data;
  } catch (e: unknown) {
    if (axios.isAxiosError(e) && e.response) {
      throw new Error(`HTTP error! status: ${e.response.status}`);
    }
    if (e instanceof Error) {
      return e.message;
    }
    if (isUntappdApiError(e)) {
      return e.meta.error_detail;
    }
    return "An unknown error occurred";
  }
}

```

--------------------------------------------------------------------------------
/src/api/getBeerSearch.ts:
--------------------------------------------------------------------------------

```typescript
import axios from "axios";
import { UNTAPPED_API_SEARCH, UNTAPPED_API_BASE } from "../constants.js";
import { isUntappdApiError } from "../libs/guards.js";
import { UntappdBeerSearchResult } from "../types/untappedApi.js";
import { config } from "../index.js";

export async function getBeerSearch(query: string) {
  try {
    const response = await axios.get<UntappdBeerSearchResult>(
      `${UNTAPPED_API_BASE}${UNTAPPED_API_SEARCH}`,
      {
        params: {
          q: query,
          client_id: config.untappd.clientId,
          client_secret: config.untappd.clientSecret,
        },
      },
    );

    return response.data;
  } catch (e: unknown) {
    if (axios.isAxiosError(e) && e.response) {
      throw new Error(
        `HTTP error! status: ${e.response.status}: ${JSON.stringify(e)}`,
      );
    }
    if (e instanceof Error) {
      return e.message;
    }
    if (isUntappdApiError(e)) {
      return e.meta.error_detail;
    }
    return "An unknown error occurred";
  }
}

```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { getBeerSearch } from "./api/getBeerSearch.js";
import {
  formatUntappdBeerInfoResult,
  formatUntappdBeerSearchResult,
  formatUntappdBreweryInfoResult,
} from "./libs/format.js";

import dotenv from "dotenv";
import { GetBeerInfo } from "./api/getBeerInfo.js";
import { GetBreweryInfo } from "./api/getBreweryInfo.js";
dotenv.config();

export const config = {
  untappd: {
    clientId: process.env.UNTAPPED_API_CLIENT_ID,
    clientSecret: process.env.UNTAPPED_API_CLIENT_SECRET,
  },
};

// Create server instance
const server = new McpServer({
  name: "untapped",
  version: "1.0.0",
});

// Register untapped tools
server.tool(
  "Beer_Search",
  "Search beers on untapped",
  {
    beer: z.string().describe("The name of the beer you want to search"),
  },
  async ({ beer }) => {
    const beersData = await getBeerSearch(beer);

    if (!beersData) {
      return {
        content: [
          {
            type: "text",
            text: "Failed to retrieve untapped beer data.",
          },
        ],
      };
    }

    if (typeof beersData === "string") {
      return {
        content: [
          {
            type: "text",
            text: `Failed to retrieve untapped beer data: ${beersData}`,
          },
        ],
      };
    }

    const formattedBeerData = formatUntappdBeerSearchResult(beersData);

    return {
      content: [
        {
          type: "text",
          text: formattedBeerData,
        },
      ],
    };
  },
);

server.tool(
  "Beer_Info",
  "Get detailed info of a beer.",
  {
    bid: z
      .string()
      .describe(
        "Beer ID (string): The 'bid' can be retrieved from 'Beer Search'.",
      ),
  },
  async ({ bid }) => {
    const beerInfoData = await GetBeerInfo(bid);

    if (!beerInfoData) {
      return {
        content: [
          {
            type: "text",
            text: "Failed to retrieve untapped beer info data.",
          },
        ],
      };
    }

    if (typeof beerInfoData === "string") {
      return {
        content: [
          {
            type: "text",
            text: `Failed to retrieve untapped beer info data: ${beerInfoData}`,
          },
        ],
      };
    }

    const formattedBeerInfoData = formatUntappdBeerInfoResult(beerInfoData);

    return {
      content: [
        {
          type: "text",
          text: formattedBeerInfoData,
        },
      ],
    };
  },
);

server.tool(
  "Brewery_Info",
  "Get detailed info of a brewery.",
  {
    brewery_id: z
      .string()
      .describe(
        "brewery_id (string): The 'brewery_id' can be retrieved from a beer.",
      ),
  },
  async ({ brewery_id }) => {
    const breweryInfoData = await GetBreweryInfo(brewery_id);

    if (!breweryInfoData) {
      return {
        content: [
          {
            type: "text",
            text: "Failed to retrieve untapped brewery info data.",
          },
        ],
      };
    }

    if (typeof breweryInfoData === "string") {
      return {
        content: [
          {
            type: "text",
            text: `Failed to retrieve untapped brewery info data: ${breweryInfoData}`,
          },
        ],
      };
    }

    const formattedBreweryInfoData =
      formatUntappdBreweryInfoResult(breweryInfoData);

    return {
      content: [
        {
          type: "text",
          text: formattedBreweryInfoData,
        },
      ],
    };
  },
);

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("Untapped MCP Server running on stdio");
}

main().catch((error) => {
  console.error("Fatal error in main():", error);
  process.exit(1);
});

```

--------------------------------------------------------------------------------
/src/types/untappedApi.ts:
--------------------------------------------------------------------------------

```typescript
// Common types

// Contact information for brewery
interface UntappdContact {
  twitter?: string;
  facebook?: string;
  instagram?: string;
  url?: string;
}

// Location information for brewery
interface UntappdLocation {
  brewery_city?: string;
  brewery_state?: string;
  venue_address?: string;
  venue_city?: string;
  venue_state?: string;
  lat?: number;
  lng?: number;
}

// Brewery details
interface UntappdBrewery {
  brewery_id: number;
  brewery_name: string;
  brewery_slug?: string;
  brewery_label: string;
  country_name: string;
  contact: UntappdContact;
  location: UntappdLocation;
  brewery_active?: number;
  beer_count?: number; // Only in breweries section
}

// Beer details
interface UntappdBeer {
  bid: number;
  beer_name: string;
  beer_label: string;
  beer_abv: number;
  beer_ibu: number;
  beer_description: string;
  created_at: string;
  beer_style: string;
  auth_rating: number;
  wish_list: boolean;
  in_production?: number;
  beer_slug?: string;
  beer_style_id?: number;
  beer_active?: number;
  is_in_production?: number;
  is_vintage?: number;
  is_variant?: number;
  is_homebrew?: number;
  rating_count?: number;
  rating_score?: number;
}

// Beer stats
interface UntappdBeerStats {
  total_count: number;
  monthly_count: number;
  total_user_count: number;
  user_count: number;
}

// User information
interface UntappdUser {
  uid: number;
  user_name: string;
  first_name: string;
  last_name: string;
  user_avatar: string;
  relationship: string;
  is_private: number;
}

// Venue category
interface UntappdVenueCategory {
  category_name: string;
  category_id: string;
  is_primary: boolean;
}

// Venue categories section
interface UntappdVenueCategories {
  count: number;
  items: UntappdVenueCategory[];
}

// Foursquare venue info
interface UntappdFoursquareVenue {
  foursquare_id: string;
  foursquare_url: string;
}

// Venue icon
interface UntappdVenueIcon {
  sm: string;
  md: string;
  lg: string;
}

// Venue information
interface UntappdVenue {
  venue_id: number;
  venue_name: string;
  primary_category: string;
  parent_category_id: string;
  categories: UntappdVenueCategories;
  location: UntappdLocation;
  contact: UntappdContact;
  private_venue: boolean;
  foursquare: UntappdFoursquareVenue;
  venue_icon: UntappdVenueIcon;
}

// Photo information
interface UntappdPhoto {
  photo_img_sm: string;
  photo_img_md: string;
  photo_img_lg: string;
  photo_img_og: string;
}

// Media item for beer
export interface UntappdMediaItem {
  photo_id: number;
  photo: UntappdPhoto;
  created_at: string;
  checkin_id: number;
  beer: UntappdBeer;
  brewery: UntappdBrewery;
  user: UntappdUser;
  venue: UntappdVenue[];
}

// Media section
export interface UntappdMedia {
  count: number;
  items: UntappdMediaItem | UntappdMediaItem[]; // API inconsistently returns object or array
}

// Similar beer item
interface UntappdSimilarBeerItem {
  rating_score: number;
  beer: UntappdBeer;
  brewery: UntappdBrewery;
  friends: {
    items: any[]; // Can be more specific if needed
    count: number;
  };
}

// Similar beers section
interface UntappdSimilarBeers {
  count: number;
  items: UntappdSimilarBeerItem | UntappdSimilarBeerItem[]; // API inconsistently returns object or array
}

// Friends section
interface UntappdFriends {
  count: number;
  items: any[]; // Can be more specific if needed
}

// Vintage item
interface UntappdVintageItem {
  beer: {
    bid: number;
    beer_label: string;
    beer_slug: string;
    beer_name: string;
    is_vintage: number;
    is_variant: number;
  };
}

// Vintages section
interface UntappdVintages {
  count: number;
  items: UntappdVintageItem[];
}

// Complete beer info
export interface UntappdBeerInfo extends UntappdBeer {
  stats: UntappdBeerStats;
  brewery: UntappdBrewery;
  media: UntappdMedia;
  similar: UntappdSimilarBeers;
  friends: UntappdFriends;
  vintages: UntappdVintages;
}

// Beer item in search results
export interface UntappdBeerItem {
  checkin_count: number;
  have_had: boolean;
  your_count: number;
  beer: UntappdBeer;
  brewery: UntappdBrewery;
}

// Brewery item in search results
interface UntappdBreweryItem {
  brewery: UntappdBrewery;
}

// Beer section in response
interface UntappdBeerSection {
  count: number;
  items: UntappdBeerItem[];
}

// Brewery section in response
interface UntappdBrewerySection {
  count: number;
  items: UntappdBreweryItem[];
}

// Response wrapper with metadata (common to all Untappd API responses)
export interface UntappdApiResponse<T> {
  meta: {
    code: number;
    response_time: {
      time: number;
      measure: string;
    };
    error_detail?: string;
    error_type?: string;
    developer_friendly?: string;
  };
  notifications: Record<string, unknown>;
  response: T;
}

// Untappd API Error Response
export interface UntappdApiErrorResponse {
  meta: {
    code: number;
    error_detail: string;
    error_type: string;
    developer_friendly?: string;
    response_time: {
      time: number;
      measure: string;
    };
  };
}

// Complete beer info response
export interface UntappdBeerInfoResponse {
  beer: UntappdBeerInfo;
}

// Complete search response
export interface UntappdBeerSearchResponse {
  found: number;
  offset: number;
  limit: number;
  term: string;
  parsed_term: string;
  beers: UntappdBeerSection;
  homebrew: UntappdBeerSection;
  breweries: UntappdBrewerySection;
}

// Additional Untappd types for brewery information

// Claimed status information for brewery
interface UntappdClaimedStatus {
  is_claimed: boolean;
  claimed_slug: string;
  follow_status: boolean;
  follower_count: number;
  uid: number;
  mute_status: string;
}

// Brewery rating information
interface UntappdBreweryRating {
  count: number;
  rating_score: number;
}

// Extended brewery statistics
interface UntappdBreweryStats {
  total_count: number;
  unique_count: number;
  monthly_count: number;
  weekly_count: number;
  user_count: number;
  age_on_service: number;
}

// Brewery owners section
interface UntappdBreweryOwners {
  count: number;
  items: any[]; // Could be more specific if needed
}

// Beer list item in brewery response
interface UntappdBeerListItem {
  has_had: boolean;
  total_count: number;
  beer: UntappdBeer;
  brewery: UntappdBrewery;
  friends: any[]; // Could be more specific if needed
}

// Beer list section in brewery response
interface UntappdBeerList {
  is_super: boolean;
  sort: string;
  filter: string;
  count: number;
  items: UntappdBeerListItem | UntappdBeerListItem[]; // API inconsistently returns object or array
  beer_count: number;
}

// Extended brewery information
export interface UntappdBreweryInfo extends UntappdBrewery {
  brewery_in_production: number;
  is_independent: number;
  claimed_status: UntappdClaimedStatus;
  brewery_type: string;
  brewery_type_id: number;
  brewery_description: string;
  rating: UntappdBreweryRating;
  stats: UntappdBreweryStats;
  owners: UntappdBreweryOwners;
  media: UntappdMedia;
  beer_list: UntappdBeerList;
}

// Complete brewery info response
export interface UntappdBreweryInfoResponse {
  brewery: UntappdBreweryInfo;
}

// Complete typed response for brewery info
export type UntappdBreweryInfoResult =
  UntappdApiResponse<UntappdBreweryInfoResponse>;

// Complete typed response for beer search
export type UntappdBeerSearchResult =
  UntappdApiResponse<UntappdBeerSearchResponse>;

// Complete typed response for beer info
export type UntappdBeerInfoResult = UntappdApiResponse<UntappdBeerInfoResponse>;

```

--------------------------------------------------------------------------------
/src/libs/format.ts:
--------------------------------------------------------------------------------

```typescript
import {
  UntappdBeerInfo,
  UntappdBeerInfoResult,
  UntappdBeerItem,
  UntappdBeerSearchResult,
  UntappdBreweryInfoResult,
  UntappdMediaItem,
} from "../types/untappedApi.js";

export function formatUntappdBeerItem(beerItem: UntappdBeerItem): string {
  return [
    `Basic beer item properties`,
    `---`,
    `checkin_count: ${beerItem.checkin_count}`,
    `have_had: ${beerItem.have_had}`,
    `your_count: ${beerItem.your_count}`,

    `Beer properties`,
    `---`,
    `bid: ${beerItem.beer.bid}`,
    `beer_name: ${beerItem.beer.beer_name}`,
    `beer_label: ${beerItem.beer.beer_label}`,
    `beer_abv: ${beerItem.beer.beer_abv}`,
    `beer_ibu: ${beerItem.beer.beer_ibu}`,
    `beer_description: ${beerItem.beer.beer_description}`,
    `created_at: ${beerItem.beer.created_at}`,
    `beer_style: ${beerItem.beer.beer_style}`,
    `auth_rating: ${beerItem.beer.auth_rating}`,
    `wish_list: ${beerItem.beer.wish_list}`,

    `Optional beer properties`,
    `---`,
    ...(beerItem.beer.in_production !== undefined
      ? [`in_production: ${beerItem.beer.in_production}`]
      : []),
    ...(beerItem.beer.beer_slug
      ? [`beer_slug: ${beerItem.beer.beer_slug}`]
      : []),
    ...(beerItem.beer.beer_style_id
      ? [`beer_style_id: ${beerItem.beer.beer_style_id}`]
      : []),
    ...(beerItem.beer.beer_active !== undefined
      ? [`beer_active: ${beerItem.beer.beer_active}`]
      : []),
    ...(beerItem.beer.is_in_production !== undefined
      ? [`is_in_production: ${beerItem.beer.is_in_production}`]
      : []),
    ...(beerItem.beer.is_vintage !== undefined
      ? [`is_vintage: ${beerItem.beer.is_vintage}`]
      : []),
    ...(beerItem.beer.is_variant !== undefined
      ? [`is_variant: ${beerItem.beer.is_variant}`]
      : []),
    ...(beerItem.beer.is_homebrew !== undefined
      ? [`is_homebrew: ${beerItem.beer.is_homebrew}`]
      : []),
    ...(beerItem.beer.rating_count !== undefined
      ? [`rating_count: ${beerItem.beer.rating_count}`]
      : []),
    ...(beerItem.beer.rating_score !== undefined
      ? [`rating_score: ${beerItem.beer.rating_score}`]
      : []),

    `Brewery properties`,
    `---`,
    `brewery_id: ${beerItem.brewery.brewery_id}`,
    `brewery_name: ${beerItem.brewery.brewery_name}`,
    `brewery_label: ${beerItem.brewery.brewery_label}`,
    `country_name: ${beerItem.brewery.country_name}`,

    `Optional brewery properties`,
    `---`,
    ...(beerItem.brewery.brewery_slug
      ? [`brewery_slug: ${beerItem.brewery.brewery_slug}`]
      : []),
    ...(beerItem.brewery.brewery_active !== undefined
      ? [`brewery_active: ${beerItem.brewery.brewery_active}`]
      : []),
    ...(beerItem.brewery.beer_count !== undefined
      ? [`brewery_beer_count: ${beerItem.brewery.beer_count}`]
      : []),

    `Brewery contact information`,
    `---`,
    ...(beerItem.brewery.contact?.twitter
      ? [`brewery_twitter: ${beerItem.brewery.contact.twitter}`]
      : []),
    ...(beerItem.brewery.contact?.facebook
      ? [`brewery_facebook: ${beerItem.brewery.contact.facebook}`]
      : []),
    ...(beerItem.brewery.contact?.instagram
      ? [`brewery_instagram: ${beerItem.brewery.contact.instagram}`]
      : []),
    ...(beerItem.brewery.contact?.url
      ? [`brewery_url: ${beerItem.brewery.contact.url}`]
      : []),

    `Brewery location`,
    `---`,
    ...(beerItem.brewery.location?.brewery_city
      ? [`brewery_city: ${beerItem.brewery.location.brewery_city}`]
      : []),
    ...(beerItem.brewery.location?.brewery_state
      ? [`brewery_state: ${beerItem.brewery.location.brewery_state}`]
      : []),
    ...(beerItem.brewery.location?.venue_address
      ? [`venue_address: ${beerItem.brewery.location.venue_address}`]
      : []),
    ...(beerItem.brewery.location?.venue_city
      ? [`venue_city: ${beerItem.brewery.location.venue_city}`]
      : []),
    ...(beerItem.brewery.location?.venue_state
      ? [`venue_state: ${beerItem.brewery.location.venue_state}`]
      : []),
    ...(beerItem.brewery.location?.lat !== undefined
      ? [`latitude: ${beerItem.brewery.location.lat}`]
      : []),
    ...(beerItem.brewery.location?.lng !== undefined
      ? [`longitude: ${beerItem.brewery.location.lng}`]
      : []),
    "+++ End",
  ].join("\n");
}

export function formatUntappdBeerInfo(beerInfo: UntappdBeerInfo): string {
  return [
    `Beer Information`,
    `---`,
    `bid: ${beerInfo.bid}`,
    `beer_name: ${beerInfo.beer_name}`,
    `beer_label: ${beerInfo.beer_label}`,
    `beer_abv: ${beerInfo.beer_abv}`,
    `beer_ibu: ${beerInfo.beer_ibu}`,
    `beer_description: ${beerInfo.beer_description}`,
    `created_at: ${beerInfo.created_at}`,
    `beer_style: ${beerInfo.beer_style}`,
    `auth_rating: ${beerInfo.auth_rating}`,

    `Optional Beer Properties`,
    `---`,
    ...(beerInfo.in_production !== undefined
      ? [`in_production: ${beerInfo.in_production}`]
      : []),
    ...(beerInfo.beer_slug ? [`beer_slug: ${beerInfo.beer_slug}`] : []),
    ...(beerInfo.beer_style_id
      ? [`beer_style_id: ${beerInfo.beer_style_id}`]
      : []),
    ...(beerInfo.beer_active !== undefined
      ? [`beer_active: ${beerInfo.beer_active}`]
      : []),
    ...(beerInfo.is_in_production !== undefined
      ? [`is_in_production: ${beerInfo.is_in_production}`]
      : []),
    ...(beerInfo.is_vintage !== undefined
      ? [`is_vintage: ${beerInfo.is_vintage}`]
      : []),
    ...(beerInfo.is_variant !== undefined
      ? [`is_variant: ${beerInfo.is_variant}`]
      : []),
    ...(beerInfo.is_homebrew !== undefined
      ? [`is_homebrew: ${beerInfo.is_homebrew}`]
      : []),
    ...(beerInfo.rating_count !== undefined
      ? [`rating_count: ${beerInfo.rating_count}`]
      : []),
    ...(beerInfo.rating_score !== undefined
      ? [`rating_score: ${beerInfo.rating_score}`]
      : []),

    `Beer Stats`,
    `---`,
    `total_count: ${beerInfo.stats.total_count}`,
    `monthly_count: ${beerInfo.stats.monthly_count}`,
    `total_user_count: ${beerInfo.stats.total_user_count}`,
    `user_count: ${beerInfo.stats.user_count}`,

    `Brewery Information`,
    `---`,
    `brewery_id: ${beerInfo.brewery.brewery_id}`,
    `brewery_name: ${beerInfo.brewery.brewery_name}`,
    `brewery_label: ${beerInfo.brewery.brewery_label}`,
    `country_name: ${beerInfo.brewery.country_name}`,

    `Optional Brewery Properties`,
    `---`,
    ...(beerInfo.brewery.brewery_slug
      ? [`brewery_slug: ${beerInfo.brewery.brewery_slug}`]
      : []),

    `Brewery Location`,
    `---`,
    ...(beerInfo.brewery.location?.brewery_city
      ? [`brewery_city: ${beerInfo.brewery.location.brewery_city}`]
      : []),
    ...(beerInfo.brewery.location?.brewery_state
      ? [`brewery_state: ${beerInfo.brewery.location.brewery_state}`]
      : []),
    ...(beerInfo.brewery.location?.venue_address
      ? [`venue_address: ${beerInfo.brewery.location.venue_address}`]
      : []),
    ...(beerInfo.brewery.location?.venue_city
      ? [`venue_city: ${beerInfo.brewery.location.venue_city}`]
      : []),
    ...(beerInfo.brewery.location?.venue_state
      ? [`venue_state: ${beerInfo.brewery.location.venue_state}`]
      : []),
    ...(beerInfo.brewery.location?.lat !== undefined
      ? [`latitude: ${beerInfo.brewery.location.lat}`]
      : []),
    ...(beerInfo.brewery.location?.lng !== undefined
      ? [`longitude: ${beerInfo.brewery.location.lng}`]
      : []),

    `Media Information`,
    `---`,
    `media_count: ${beerInfo.media.count}`,

    `Similar Beers`,
    `---`,
    `similar_beers_count: ${beerInfo.similar.count}`,

    `Friends Information`,
    `---`,
    `friends_count: ${beerInfo.friends.count}`,

    `Vintages Information`,
    `---`,
    `vintages_count: ${beerInfo.vintages.count}`,
    ...(beerInfo.vintages.count > 0 ? [`Has vintage versions available`] : []),

    "Media Information",
    Array.isArray(beerInfo.media.items)
      ? beerInfo.media.items.map((m) => formatUntappdMediaItem(m))
      : formatUntappdMediaItem(beerInfo.media.items),
    `+++ End`,
  ].join("\n");
}

export function formatUntappdMediaItem(item: UntappdMediaItem): string {
  return [
    `Media Item Information`,
    `---`,
    `created_at: ${item.created_at}`,

    `Beer Information`,
    `---`,
    `bid: ${item.beer.bid}`,
    `beer_name: ${item.beer.beer_name}`,
    `beer_style: ${item.beer.beer_style}`,
    `beer_abv: ${item.beer.beer_abv}`,

    `Brewery Information`,
    `---`,
    `brewery_id: ${item.brewery.brewery_id}`,
    `brewery_name: ${item.brewery.brewery_name}`,
    `country_name: ${item.brewery.country_name}`,

    `Venue Information`,
    `---`,
    ...(Array.isArray(item.venue) && item.venue.length > 0
      ? [
          `venue_id: ${item.venue[0].venue_id}`,
          `venue_name: ${item.venue[0].venue_name}`,
          `primary_category: ${item.venue[0].primary_category}`,
          ...(item.venue[0].location?.venue_city
            ? [`venue_city: ${item.venue[0].location.venue_city}`]
            : []),
          ...(item.venue[0].location?.venue_state
            ? [`venue_state: ${item.venue[0].location.venue_state}`]
            : []),
        ]
      : [`No venue information available`]),
    `+++ End`,
  ].join("\n");
}

export function formatUntappdBeerSearchResult(
  result: UntappdBeerSearchResult,
): string {
  const payload: string[] = [];

  result.response.beers.items.forEach((beer: UntappdBeerItem) => {
    payload.push(formatUntappdBeerItem(beer));
  });

  return payload.join("\n");
}

export function formatUntappdBeerInfoResult(
  result: UntappdBeerInfoResult,
): string {
  return formatUntappdBeerInfo(result.response.beer);
}

export function formatUntappdBreweryInfoResult(
  result: UntappdBreweryInfoResult,
): string {
  const breweryInfo = result.response.brewery;

  return [
    `Brewery Information`,
    `---`,
    `brewery_id: ${breweryInfo.brewery_id}`,
    `brewery_name: ${breweryInfo.brewery_name}`,
    `country_name: ${breweryInfo.country_name}`,
    `brewery_in_production: ${breweryInfo.brewery_in_production}`,
    `is_independent: ${breweryInfo.is_independent}`,
    ...(breweryInfo.brewery_slug
      ? [`brewery_slug: ${breweryInfo.brewery_slug}`]
      : []),
    ...(breweryInfo.brewery_type
      ? [`brewery_type: ${breweryInfo.brewery_type}`]
      : []),
    ...(breweryInfo.brewery_type_id
      ? [`brewery_type_id: ${breweryInfo.brewery_type_id}`]
      : []),
    ...(breweryInfo.brewery_description
      ? [`brewery_description: ${breweryInfo.brewery_description}`]
      : []),
    `beer_count: ${breweryInfo.beer_count}`,

    `Contact Information`,
    `---`,
    ...(breweryInfo.contact?.url
      ? [`website: ${breweryInfo.contact.url}`]
      : []),

    `Location`,
    `---`,
    ...(breweryInfo.location?.venue_address
      ? [`address: ${breweryInfo.location.venue_address}`]
      : []),
    ...(breweryInfo.location?.brewery_city
      ? [`city: ${breweryInfo.location.brewery_city}`]
      : []),
    ...(breweryInfo.location?.brewery_state
      ? [`state: ${breweryInfo.location.brewery_state}`]
      : []),
    ...(breweryInfo.location?.lat !== undefined
      ? [`latitude: ${breweryInfo.location.lat}`]
      : []),
    ...(breweryInfo.location?.lng !== undefined
      ? [`longitude: ${breweryInfo.location.lng}`]
      : []),

    `Rating`,
    `---`,
    `rating_count: ${breweryInfo.rating.count}`,
    `rating_score: ${breweryInfo.rating.rating_score}`,

    `Statistics`,
    `---`,
    `total_check_ins: ${breweryInfo.stats.total_count}`,
    `unique_users: ${breweryInfo.stats.unique_count}`,
    `monthly_check_ins: ${breweryInfo.stats.monthly_count}`,
    `weekly_check_ins: ${breweryInfo.stats.weekly_count}`,
    `user_count: ${breweryInfo.stats.user_count}`,
    `age_on_service: ${breweryInfo.stats.age_on_service}`,

    `Beer List`,
    `---`,
    `is_super: ${breweryInfo.beer_list.is_super}`,
    `sort: ${breweryInfo.beer_list.sort || "default"}`,
    `filter: ${breweryInfo.beer_list.filter}`,
    `beer_count: ${breweryInfo.beer_list.beer_count}`,
    `displayed_beers: ${breweryInfo.beer_list.count}`,

    `Brewery Beers`,
    `---`,
    ...(breweryInfo.beer_list.count > 0
      ? Array.isArray(breweryInfo.beer_list.items)
        ? breweryInfo.beer_list.items.map((beer) => [
            `bid: ${beer.beer.bid}`,
            `beer_name: ${beer.beer.beer_name}`,
            `beer_style: ${beer.beer.beer_style}`,
            `beer_abv: ${beer.beer.beer_abv}`,
            `beer_ibu: ${beer.beer.beer_ibu}`,
            `beer_description: ${beer.beer.beer_description}`,
            `rating_score: ${beer.beer.rating_score}`,
            `rating_count: ${beer.beer.rating_count}`,
            "\n",
          ])
        : ""
      : [`No beers available to display`]),

    `+++ End`,
  ].join("\n");
}

```