Pedometer
Lightweight pedometer app for Android using the hardware step sensor
Take our skin type quiz and get personalized product recommendations
Translation pending
HealthTypeScript · JavaScriptGitHub @robertozerbiniOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
MCP server for SkinGuide: search skincare products by Baumann skin type, product category, country, and budget. Exposes AI-friendly endpoints for dry, sensitive, and other skin types
Taken from the repository README
Loading the full README.
Read the full README ↗MCP server for SkinGuide: search skincare products by Baumann skin type, product category, budget, and ingredients. Exposes AI-friendly endpoints for dry, oily, sensitive, and all other skin types.
| Website | skinguide.beauty |
| GitHub | robertozerbini/skinguide-mcp-server |
| Protocol | Model Context Protocol 2024-11-05 |
| Transports | stdio (npx from GitHub, or local node) |
SkinGuide MCP Server implements the Model Context Protocol to expose skincare product search and skin type information as structured AI tools.
AI agents, LLM applications, and developer tools can:
| Tool | Description |
|---|---|
search_products | Search products by skin type, brand, category, budget, keyword, and ingredient |
get_skin_type_info | Detailed info for a Baumann skin type code |
list_skin_types | All 16 Baumann codes with names, categories, and descriptions |
get_product_types | All available product categories |
get_routine | Step-by-step skincare routine for a Baumann skin type (AM/PM, gender) |
get_brands | All available brands in the database |
get_test_questions | All Baumann skin type quiz questions (grouped by dimension) |
submit_test_answers | Submit quiz answers and compute the user's 4-letter skin type code |
get_product_reviews | User reviews for a product — rating, comment, reviewer skin type, date |
get_product_ingredients | Ingredient list (INCI names) for a product (by product_id) |
get_ingredient_info | Structured facts for a cosmetic ingredient (functions, comedogenicity, irritancy), credited to Incidecoder |
compare_products | Top-N products of a category side-by-side (cheapest first by default, or by rating) |
build_routine | Full routine for a skin type with product candidates per step and cost summary |
skinguide-mcp-server/
├── Dockerfile # Optional containerized runtime
├── src/
│ ├── index.ts # MCP stdio entry-point (TypeScript)
│ ├── tools.ts # Tool handlers + live API proxy
│ └── types.ts # Shared interfaces/types
├── dist/ # Compiled output (generated)
├── package.json
├── tsconfig.json
├── examples/
│ ├── blogcreation.ipynb # Colab notebook: autonomous blog-writing agent
│ ├── direct_api_client.py
│ ├── node_client.js # Node.js demo (built-in fetch)
│ └── python_client.py # Python MCP stdio demo
└── .well-known/
└── mcp.json # MCP discovery document
npm install
npm start
The package is not published on npm; npx installs it straight from this GitHub repository. The prepare script compiles TypeScript to dist/ during install, so nothing needs to be built by hand. The server fetches live data from https://skinguide.beauty/api.
npx -y github:robertozerbini/skinguide-mcp-server#v1.2.0
The package has a single executable (skinguide), so npx runs it without a --package flag. The first run takes a little longer while it installs and compiles.
docker build -t skinguide-mcp .
docker run -i --rm skinguide-mcp
Edit ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"skinguide": {
"command": "npx",
"args": ["-y", "github:robertozerbini/skinguide-mcp-server#v1.2.0"]
}
}
}
Edit .cursor/mcp.json:
{
"mcpServers": {
"skinguide": {
"command": "npx",
"args": ["-y", "github:robertozerbini/skinguide-mcp-server#v1.2.0"]
}
}
}
Edit .vscode/mcp.json:
{
"servers": {
"skinguide": {
"type": "stdio",
"command": "node",
"args": ["dist/index.js"]
}
}
}
curl https://skinguide.beauty/.well-known/mcp.json
| Variable | Default | Description |
|---|---|---|
LIVE_API_URL | https://skinguide.beauty/api | Base URL for the live SkinGuide API |
All tools are invoked by the MCP client (Claude, Cursor, VS Code) over stdio. The JSON-RPC 2.0 envelope used internally:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "<tool_name>",
"arguments": {}
}
}
List all 16 Baumann skin types. Input: none.
Response excerpt:
{
"skinTypes": [
{
"code": "DSPW",
"name": "Dry, Sensitive, Pigmented, Wrinkled",
"category": "dry-sensitive",
"difficulty": 5,
"description": "The most complex skin type. Needs rich hydration, anti-inflammatory care, brightening, and anti-aging support simultaneously."
},
{
"code": "DSNW",
"name": "Dry, Sensitive, Non-pigmented, Wrinkled",
"category": "dry-sensitive",
"difficulty": 4,
"description": "Dry and sensitive with visible aging but even tone. Prioritise deep hydration, barrier repair, and anti-aging actives."
}
],
"total": 16
}
(14 more types omitted — all 16 returned in the real response)
List all available product categories. Input: none.
{
"productTypes": [
{ "id": "Acne Treatment" },
{ "id": "Anti-age Product" },
{ "id": "Anti-inflammatory Product" },
{ "id": "Antioxidant Serum" },
{ "id": "Cleanser" },
{ "id": "Eye Cream" },
{ "id": "Facial Water" },
{ "id": "Foundation" },
{ "id": "Mask" },
{ "id": "Moisturizer" },
{ "id": "Moisturizer Night" },
{ "id": "Oil-control Product" },
{ "id": "Scrub" },
{ "id": "Self-tanning" },
{ "id": "Skin Lightener" },
{ "id": "Sunscreen" }
],
"total": 16
}
Input: skinType (string, required) — 4-letter Baumann code, e.g. OSPT, DRNT, DSPW.
Response:
{
"code": "OSPT",
"name": "Oily, Sensitive, Pigmented, Tight",
"category": "oily-sensitive",
"difficulty": 4,
"description": "Very common in teens and young adults. Oily, acne-prone, and sensitive with post-inflammatory dark spots."
}
Tip: If
total=0, retry with fewer filters. Ifingredientreturns 0, trykeyword=<ingredient_name>instead — many products have incomplete ingredient data.skinTypemust be a 4-letter Baumann code (e.g.OSPT), not natural language.
Input parameters:
| Parameter | Type | Description |
|---|---|---|
type | string | Product category — one of the 16 values returned by get_product_types |
skinType | string | 4-letter Baumann code, e.g. OSPT. Do NOT use natural language like "oily". |
od | "O" or "D" | Oily or Dry axis |
sr | "S" or "R" | Sensitive or Resistant axis |
pn | "P" or "N" | Pigmented or Non-pigmented axis |
wt | "W" or "T" | Wrinkled or Tight axis |
brand | string | Filter by brand name (partial match, case-insensitive). E.g. CeraVe, La Roche. |
keyword | string | Search keyword matched against product name and tags. Use as fallback when ingredient returns 0. |
ingredient | string | Exact text match against the stored ingredient list. Returns 0 when data is incomplete — use keyword instead. |
budget | number | Maximum price in dollars (use 5, 10, 20, 50, 100, or 101 for over $100) |
country | "US" or "AE" | Country market filter. Defaults to US. Aliases: UAE→AE, USA→US. |
sort | "price-asc", "price-desc" or "rating-desc" | Optional. Omit it for the fast default order (lowest price first). rating-desc is slow (about 10s or more) and only a few hundred products have ratings |
limit | integer | Max results, 1–50 (default 50) |
When ingredient is used, the response includes available_without_ingredient_filter — the number of products that matched all other filters before the ingredient check. If that value is > 0 but total is 0, retry with keyword instead.
Example — oily + sensitive skin (od=O, sr=S), budget $30:
{ "od": "O", "sr": "S", "budget": 30, "limit": 5 }
Example — search by skin type with ingredient fallback to keyword:
{ "skinType": "OSPT", "keyword": "niacinamide", "limit": 10 }
Response:
total is the number of matching products across the whole catalog; returned is how many are in this response (at most limit). All filters are applied by the SkinGuide API.
{
"total": 5,
"returned": 1,
"query": { "type": "all", "skinType": null, "od": "O", "sr": "S", "pn": null, "wt": null, "budget": 30, "keyword": null, "ingredient": null },
"products": [
{
"id": 534,
"name": "Persa-Gel 10 Oil-Free Acne Spot Treatment",
"brand": "Clean & Clear",
"type": "Treatment",
"price": 5.97,
"currency": "$",
"vegan": false,
"ingredients": ["Benzoyl Peroxide 10%"],
"image": "https://m.media-amazon.com/images/I/612wW4IikOL._AC_UL320_.jpg",
"link": "https://amzn.to/4aTFj3m",
"country": "US",
"skinTypes": ["OSPT"]
}
]
}
Get the step-by-step skincare routine for a Baumann skin type.
Input parameters:
| Parameter | Type | Description |
|---|---|---|
skinType | string, required | 4-letter Baumann code, e.g. OSPT |
gender | "male" or "female" | Optional — filter steps by gender |
timeOfDay | "AM" or "PM" | Optional — filter to morning or evening steps |
Example — AM routine for OSPT:
{ "skinType": "OSPT", "timeOfDay": "AM" }
Response:
{
"skinType": "OSPT",
"title": "...",
"url": "https://skinguide.beauty/routine/OSPT",
"routineDescription": "...",
"total": 4,
"steps": [
{ "step": 1, "productType": "Cleanser", "action": "Cleanse", "timeOfDay": "AM" },
{ "step": 2, "productType": "Toner", "action": "Tone", "timeOfDay": "AM" },
{ "step": 3, "productType": "Serum", "action": "Apply serum", "timeOfDay": "AM" },
{ "step": 4, "productType": "Sunscreen", "action": "Apply sunscreen", "timeOfDay": "AM" }
]
}
Get the skincare brands in the catalog. The full list has several thousand entries, so pass the optional query (case-insensitive substring) to narrow it.
| Parameter | Type | Description |
|---|---|---|
query | string | Optional — e.g. "cera" |
Response (unfiltered):
{
"total": 5986,
"brands": [
"Acure",
"Biossance",
"CeraVe",
"Clean & Clear",
"La Roche-Posay",
"Neutrogena"
]
}
Get all Baumann skin type quiz questions. Input: none.
Returns the 15 questions, each tagged with its skin dimension (section: O_D, S_R, P_N, W_T) and answer options with numeric scores. Source: GET https://skinguide.beauty/api/testQuestions.
Response excerpt:
{
"questions": [
{
"id": "0",
"section": "O_D",
"text": "How does your skin generally feel and look throughout the day (including after cleansing, without moisturizer, facial shine, and pore visibility)?",
"options": [
{ "text": "Very Dry: Feels very dry, flaky, or cracks without moisturizer; ...", "score": 8 },
{ "text": "Balanced: Feels tight but not flaky, ...", "score": 16 }
]
}
],
"total": 15,
"sections": ["O_D", "S_R", "P_N", "W_T"],
"sectionLabels": { "O_D": "Oily vs. Dry", "S_R": "Sensitive vs. Resistant", "P_N": "Pigmented vs. Non-Pigmented", "W_T": "Wrinkled vs. Tight" }
}
Submit answers for the Baumann skin type test and receive the computed 4-letter skin type code.
Requires a signed-in SkinGuide account. The live endpoint (
POST https://skinguide.beauty/api/submitTestAnswers) returns 401 without a Firebase ID token and saves the result to that user's profile. The server reads the token from theSKINGUIDE_AUTH_TOKENenvironment variable. Without it the tool returns an error that sends the user to skinguide.beauty to take the test. The score-to-type thresholds are stored server-side, so the tool does not compute the type locally.
Input parameters:
| Parameter | Type | Description |
|---|---|---|
answers | array | One entry per question: { "questionId": "0", "value": 3 } |
Send all 15 answers. questionId is the id returned by get_test_questions, and value is the score of the selected option.
Example:
{
"answers": [
{ "questionId": "0", "value": 3 },
{ "questionId": "1", "value": 2 }
]
}
Response:
{
"skinType": "OSPT",
"scores": { "O_D": 3.2, "S_R": 2.8, "P_N": 3.5, "W_T": 1.1 },
"acne": true,
"darkSpots": false
}
Get user reviews for a specific product. Source: the productReviews Cloud Function (https://us-central1-vanity-e971f.cloudfunctions.net/productReviews?product_id=<id>), because reviews are not behind the skinguide.beauty/api routes.
sort=ratingcurrently fails upstream (HTTP 500). When that happens the tool fetches reviews newest-first, sorts them by rating locally, and adds a_notesaying so.
Input parameters:
| Parameter | Type | Description |
|---|---|---|
product_id | string, required | The product ID as returned by search_products |
sort | "recent" or "rating" | Sort order — newest first (default) or highest rated first |
limit | number | Max reviews to return (default 50, max 100) |
Example:
{ "product_id": "0be8e8e29fe881af821bed6979799c32", "limit": 10 }
Response:
{
"product_id": "0be8e8e29fe881af821bed6979799c32",
"product_name": null,
"avg_rating": null,
"review_count": null,
"total": 1,
"reviews": [
{
"reviewId": "0be8e8e29fe881af821bed6979799c32_...",
"rating": 5,
"comment": "I have dry, slightly sensitive skin, and this Hydro Jelly Fruit Mask was such a pleasant surprise...",
"userSkinType": "DSNW",
"userAgeRange": "35–44",
"createdAt": "2026-05-05T03:27:45.446Z"
}
]
}
Get the full ingredient list for a specific product. Source: the productIngredients Cloud Function (…cloudfunctions.net/productIngredients?product_id=<id>).
At the time of writing the live function returns ingredient names only. If an entry ever carries a
detailobject, the server passes through only the same structured fields asget_ingredient_info(never prose) and adds a_data_credit. Useget_ingredient_infofor an ingredient's functions and ratings.
Input parameters:
| Parameter | Type | Description |
|---|---|---|
product_id | string, required | The product ID as returned by search_products |
Example:
{ "product_id": "f839d81fabc8bfd2662226c7579738a7" }
Response (excerpt):
{
"product_id": "f839d81fabc8bfd2662226c7579738a7",
"total": 74,
"ingredients": [
{ "name": "Aqua" }
]
}
Look up detailed information about a cosmetic ingredient. Pass slug (kebab-case) or name (label text); at least one is required. Source: the ingredientInfo Cloud Function (…cloudfunctions.net/ingredientInfo?name=<name> or ?slug=<slug>). An unknown ingredient returns found: false.
Data credit. Only structured fields are returned: functions, comedogenicity, irritancy, the editorial rating (
our_take), and also-called names. They come from Incidecoder (now INKEEDecoder), and_data_creditsays so. The catalog's prosedescriptionandquick_factsare Incidecoder's text and are deliberately not returned. This follows skinguide.beauty's own rule: "Functions and safety ratings sourced from Incidecoder." When you present these fields, credit Incidecoder with thesource_url.
Input parameters:
| Parameter | Type | Description |
|---|---|---|
slug | string | Kebab-case slug, e.g. "niacinamide" or "hyaluronic-acid" |
name | string | Label name, e.g. "Niacinamide" or "Hyaluronic Acid" |
Example:
{ "slug": "niacinamide" }
Response:
{
"found": true,
"ingredient": {
"name": "Niacinamide",
"slug": "niacinamide",
"also_called": "vitamin B3, nicotinamide",
"what_it_does": "cell-communicating ingredient, skin brightening, anti-acne, ...",
"all_functions": "smoothing",
"comedogenicity": null,
"irritancy": null,
"our_take": "superstar",
"source_url": "https://incidecoder.com/ingredients/niacinamide"
},
"_data_credit": "Functions and safety ratings sourced from Incidecoder (now INKEEDecoder): https://incidecoder.com/ingredients/niacinamide. Ingredient descriptions are not included.",
"_source": "skinguide.beauty",
"_note": "Ingredient data is served by skinguide.beauty, but these fields come from Incidecoder (INKEEDecoder). ..."
}
Return the top-N products of a given category structured for side-by-side comparison.
Input parameters:
| Parameter | Type | Description |
|---|---|---|
type | string, required | Product category (same enum as search_products) |
skinType | string | 4-letter Baumann code to filter the pool |
country | "US" or "AE" | Country market. Defaults to US. |
budget | number | Maximum price per product |
sortBy | "rating" or "price" | Optional. Omit it for the fast default (lowest price first). rating is slow (about 10s) and few products are rated. |
limit | integer 2–5 | Number of products to return. Defaults to 3. |
Example:
{ "type": "Cleanser", "skinType": "OSPT", "country": "US", "limit": 3 }
Response:
{
"type": "Cleanser",
"skinType": "OSPT",
"country": "US",
"sortBy": "default",
"total": 3,
"products": [
{ "id": 12, "name": "CeraVe Foaming Cleanser", "brand": "CeraVe", "price": 14.99, ... },
{ "id": 87, "name": "La Roche-Posay Toleriane", "brand": "La Roche-Posay", "price": 18.5, ... },
{ "id": 34, "name": "Neutrogena Oil-Free Wash", "brand": "Neutrogena", "price": 8.97, ... }
]
}
Build a complete skincare routine for a Baumann skin type with ranked product candidates per step and a cost summary.
Input parameters:
| Parameter | Type | Description |
|---|---|---|
skinType | string, required | 4-letter Baumann code |
budget | number | Budget cap (interpretation depends on budgetMode) |
budgetMode | "per-product" or "total" | per-product (default) caps each step independently; total allocates greedily across all steps |
country | "US" or "AE" | Country market. Defaults to US. |
sortBy | "rating" or "price" | Candidate sort within each step. Omit it for the fast default (lowest price first). rating is slow (about 10s per step). |
gender | "male" or "female" | Restrict to gendered steps. Omit for all. |
timeOfDay | "AM" or "PM" | Restrict to morning or evening steps. Omit for both. |
limit | integer 1–5 | Candidate products per step. Defaults to 3. |
Example — AM routine for OSPT, max $30 per product:
{ "skinType": "OSPT", "timeOfDay": "AM", "budget": 30, "budgetMode": "per-product", "limit": 2 }
Response:
{
"skinType": "OSPT",
"country": "US",
"budgetMode": "per-product",
"budgetCap": 30,
"totalSteps": 4,
"estimatedMinCost": 38.93,
"steps": [
{
"step": 1,
"productType": "Cleanser",
"action": "Cleanse",
"timeOfDay": "AM",
"lowestPrice": 8.97,
"candidates": [
{ "id": 34, "name": "Neutrogena Oil-Free Wash", "price": 8.97, ... },
{ "id": 12, "name": "CeraVe Foaming Cleanser", "price": 14.99, ... }
]
}
]
}
⚠️ This tool is available in the server but is not exposed in the current MCP discovery document. It can be re-enabled by adding it back to
.well-known/mcp.jsonandsrc/index.ts.
Get illustration image URL(s) for a Baumann skin type. Returns portrait images (hosted on Firebase Storage) representing the skin type across different ethnicities.
Input parameters:
| Parameter | Type | Description |
|---|---|---|
skinType | string, required | 4-letter Baumann code, e.g. OSPT |
race | "Asian", "Black", "Latin", or "White" | Optional — filter to a specific ethnicity. Omit for all four. |
The Baumann Skin Type Indicator (BSTI) classifies skin along four binary axes:
| Axis | Options | Meaning |
|---|---|---|
| O/D | O = Oily, D = Dry | Sebum production |
| S/R | S = Sensitive, R = Resistant | Skin reactivity |
| P/N | P = Pigmented, N = Non-pigmented | Pigmentation tendency |
| W/T | W = Wrinkled, T = Tight | Aging / wrinkle tendency |
Combined: 16 unique types (DRNT = easiest, DSPW = most complex).
| Code | Name | Category | Difficulty |
|---|---|---|---|
| DRNT | Dry, Resistant, Non-pigmented, Tight | dry-resistant | 1 |
| DRNW | Dry, Resistant, Non-pigmented, Wrinkled | dry-resistant | 2 |
| DRPT | Dry, Resistant, Pigmented, Tight | dry-resistant | 2 |
| DRPW | Dry, Resistant, Pigmented, Wrinkled | dry-resistant | 3 |
| DSNT | Dry, Sensitive, Non-pigmented, Tight | dry-sensitive | 2 |
| DSNW | Dry, Sensitive, Non-pigmented, Wrinkled | dry-sensitive | 4 |
| DSPT | Dry, Sensitive, Pigmented, Tight | dry-sensitive | 3 |
| DSPW | Dry, Sensitive, Pigmented, Wrinkled | dry-sensitive | 5 |
| ORNT | Oily, Resistant, Non-pigmented, Tight | oily-resistant | 1 |
| ORNW | Oily, Resistant, Non-pigmented, Wrinkled | oily-resistant | 2 |
| ORPT | Oily, Resistant, Pigmented, Tight | oily-resistant | 2 |
| ORPW | Oily, Resistant, Pigmented, Wrinkled | oily-resistant | 3 |
| OSNT | Oily, Sensitive, Non-pigmented, Tight | oily-sensitive | 3 |
| OSNW | Oily, Sensitive, Non-pigmented, Wrinkled | oily-sensitive | 4 |
| OSPT | Oily, Sensitive, Pigmented, Tight | oily-sensitive | 4 |
| OSPW | Oily, Sensitive, Pigmented, Wrinkled | oily-sensitive | 5 |
The MCP server communicates over stdio and works with any AI client that supports MCP. Configure it as shown in the Quick Start section above.
For a deeper look at the JSON-RPC 2.0 request/response format used over the wire, see the demo clients in the examples/ directory.
An agent skill that teaches AI agents when and how to use these tools (the source of the Agensi listing) lives in agensi-skill/.
A Jupyter notebook demonstrating an AI agent that writes blog posts using SkinGuide:
examples/blogcreation.ipynb
Features:
Usage:
OPENAI_API_KEY to Colab Secretsuser_topic variable to generate custom blog postsYou can also call the live API directly without the MCP server. The API accepts POST requests with JSON payload:
import requests
import json
url = "https://skinguide.beauty/api/products"
payload = {
"type": "Moisturizer Day",
"od": "D", # Dry
"sr": "S", # Sensitive
"budget": 20,
"country": "US",
"limit": 20
}
response = requests.post(url, json=payload)
print("Status Code:", response.status_code)
print("Response:", json.dumps(response.json(), indent=2))
Supported parameters:
| Parameter | Type | Description |
|---|---|---|
type | string | Product category (see get_product_types for full list) |
country | "US" or "UAE" | Country for product availability |
od | "O" or "D" | Oily or Dry |
sr | "S" or "R" | Sensitive or Resistant |
pn | "P" or "N" | Pigmented or Non-pigmented |
wt | "W" or "T" | Wrinkled or Tight |
budget | number | Maximum price in dollars (5, 10, 20, 50, 100, or 101 for over $100) |
limit | number | Maximum number of results (default 50) |
Three demo clients are included in the examples/ directory:
# Direct API client (stdlib only — no Node.js required, works in Colab)
python3 examples/direct_api_client.py
# MCP stdio clients (require Node.js + npm install)
node examples/node_client.js
python3 examples/python_client.py
GET https://skinguide.beauty/.well-known/mcp.json
The document describes server name, version, URL, transport options, and all tools with full JSON Schema input/output definitions — ready for AI agent auto-discovery.
git checkout -b feat/my-featuregit commit -m 'feat: add my feature'Please follow Conventional Commits.
MIT — © SkinGuide · skinguide.beauty