# 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");
}
```