duo-metrics-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@duo-metrics-mcphow much XP did I earn today?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
duo-metrics-mcp
Personal Duolingo metrics MCP server for AI assistants.
duo-metrics-mcp lets an MCP-compatible assistant answer daily language-learning
questions such as:
Did I practice Duolingo today?
How much XP did I earn today?
How many sessions did I complete?
How much time did I spend practicing?
What is my current streak?
What did my last week or month of Duolingo activity look like?
The server uses Duolingo's unofficial web endpoints. Public profile lookup works without authentication. Daily XP summaries require an authenticated Duolingo web session cookie.
Status
This is an unofficial personal-use MCP server. Duolingo does not publish or support these endpoints for third-party API use, so endpoint behavior can change without notice.
The current implementation is intentionally lightweight:
no database
no background scheduler
no password login
live API reads on demand
optional local ignored session file for authenticated calls
Related MCP server: Strava MCP Server
Tools
duolingo_get_public_profile
Fetches public profile data by username.
Authentication: not required.
Useful for:
user id discovery
public streak data
total XP
active course
course XP split
Input:
{
"username": "JoaoPedroS572669"
}duolingo_get_authenticated_profile
Fetches the richer authenticated profile by user id.
Authentication: required.
Input:
{
"userId": 955434155472097
}duolingo_get_xp_summaries
Fetches daily XP summaries, including sessions, total practice time, and streak extension status.
Authentication: required.
Input:
{
"userId": 955434155472097,
"startDate": "2026-06-01",
"endDate": "2026-06-20",
"timezone": "America/Sao_Paulo",
"readable": true
}Example output:
{
"userId": 955434155472097,
"timezone": "America/Sao_Paulo",
"summaries": [
{
"date": "2026-06-20",
"gainedXp": 353,
"numSessions": 11,
"totalSessionTimeSeconds": 1057,
"totalSessionTimeMinutes": 17.6,
"streakExtended": true,
"frozen": false,
"repaired": false,
"dailyGoalXp": 1,
"shielded": false
}
]
}duolingo_today_status
Returns a compact daily status for assistant workflows.
Authentication: required.
Input:
{
"userId": 955434155472097,
"timezone": "America/Sao_Paulo"
}Example output:
{
"userId": 955434155472097,
"date": "2026-06-20",
"timezone": "America/Sao_Paulo",
"playedToday": true,
"xpToday": 353,
"sessionsToday": 11,
"minutesToday": 17.6,
"streakExtended": true
}duolingo_auth_help
Explains the supported authentication configuration to the assistant.
Authentication: not required.
Requirements
Node.js 20 or newer
npm
an MCP-compatible client that supports stdio servers
Installation
Clone and build:
git clone https://github.com/Tavaresiqueira/duo-metrics-mcp.git
cd duo-metrics-mcp
npm install
npm run buildRun the server manually:
npm startFor development:
npm run devRun the smoke test:
npm run smokeThe smoke test builds the project, starts the MCP server through stdio, lists the available tools, calls the public profile tool, and verifies the missing-auth path for authenticated tools.
Configuration
The server reads defaults from environment variables:
$env:DUOLINGO_USERNAME = "JoaoPedroS572669"
$env:DUOLINGO_USER_ID = "955434155472097"These defaults let assistants call tools without repeating the username or user id on every request.
Authentication
Public profile data does not require authentication.
Daily XP summaries require an authenticated Duolingo session cookie. The server supports two auth modes:
DUOLINGO_COOKIEenvironment variable.duolingo-session.jsonin the repo root
Environment Variable
Set the full Cookie header before starting the MCP server:
$env:DUOLINGO_COOKIE = "jwt_token=...; logged_out_uuid=...; ..."
npm startLocal Session File
Create .duolingo-session.json:
{
"cookie": "jwt_token=...; logged_out_uuid=...; ...",
"createdAt": "2026-06-20T18:00:00Z",
"source": "manual browser session"
}This file is ignored by git.
You can also create it from the environment variable:
$env:DUOLINGO_COOKIE = "jwt_token=...; logged_out_uuid=...; ..."
npm run save-session
Remove-Item Env:\DUOLINGO_COOKIEGetting a Cookie
Use a browser where you are already logged in to Duolingo:
Open
https://www.duolingo.com/learn.Open browser developer tools.
Go to the Network tab.
Reload the page.
Select a request to
www.duolingo.com.Copy the request
Cookieheader.Store it as
DUOLINGO_COOKIEor in.duolingo-session.json.
Do not commit cookies, tokens, or session files.
MCP Client Setup
Use the built server over stdio.
Example config:
{
"mcpServers": {
"duo-metrics": {
"command": "node",
"args": [
"C:\\Users\\joao.siqueira\\Documents\\duo-metrics-mcp\\dist\\index.js"
],
"env": {
"DUOLINGO_USERNAME": "JoaoPedroS572669",
"DUOLINGO_USER_ID": "955434155472097"
}
}
}
}If you want authenticated tools without a .duolingo-session.json file, include
DUOLINGO_COOKIE in the MCP environment block. Prefer the local session file for
personal desktop use so the cookie does not sit in a shared config file.
Best Usage
This MCP works best as a small daily workflow source, not as a full Duolingo clone.
Good assistant prompts:
Check whether I completed Duolingo today.Show my Duolingo XP for the last 7 days.Include my Duolingo streak and today's XP in my evening shutdown.Compare today's Duolingo effort against the last 14 days.Before I stop working, confirm whether my Duolingo streak is safe.Recommended workflow:
Keep
DUOLINGO_USERNAMEandDUOLINGO_USER_IDconfigured.Keep
.duolingo-session.jsonlocal and ignored.Let the assistant call
duolingo_today_statusduring daily check-ins.Use
duolingo_get_xp_summariesfor weekly/monthly review prompts.Refresh the session cookie only when Duolingo starts returning
401.
Troubleshooting
Authenticated tools return missing session
Set DUOLINGO_COOKIE or create .duolingo-session.json.
Authenticated tools return 401
Your session probably expired. Log in to Duolingo in the browser again, copy a
fresh Cookie header, and update .duolingo-session.json.
Public profile lookup fails
Check that the username is the Duolingo username, not the email address. The username is case-insensitive for lookup, but the returned profile may use Duolingo's canonical casing.
Dates look off by one day
Pass the timezone explicitly:
{
"timezone": "America/Sao_Paulo"
}Privacy
This server can access personal Duolingo account data when configured with a
session cookie. Treat .duolingo-session.json like a secret.
The project intentionally avoids password-based login. It only uses an existing web session cookie that you provide.
Development
Useful commands:
npm run build
npm run typecheck
npm run smokeProject layout:
src/index.ts MCP server and tool registration
src/duolingoClient.ts Duolingo endpoint client and normalization
src/config.ts Environment/session-file configuration
src/types.ts Shared TypeScript types
scripts/save-session.mjs
scripts/smoke-test.mjsLimitations
Duolingo endpoints are unofficial.
Session cookies can expire.
Some browser cookies are
HttpOnly, so they cannot be read fromdocument.cookie.The server does not currently persist long-term history.
The server does not currently automate login.
Available Tools
5 toolsduolingo_auth_helpDuolingo auth setup helpA
Explain how to configure a Duolingo browser session cookie for authenticated tools.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries full burden. It describes a non-destructive action (explaining) but does not explicitly state that it has no side effects or return format. Minimal but adequate for a help tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single, clear sentence that conveys the purpose without any extraneous words. Excellent conciseness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple help tool with no parameters and no output schema, the description is complete. It covers what the tool does adequately.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are zero parameters, and input schema coverage is 100% (trivially). The description does not need to add parameter info. Baseline score of 4 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: 'Explain how to configure a Duolingo browser session cookie for authenticated tools.' It uses a specific verb ('Explain') and a unique resource ('Duolingo browser session cookie'), differentiating it from sibling tools that retrieve data.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage context by specifying 'for authenticated tools,' suggesting it should be used before accessing other tools that require authentication. However, it lacks explicit when-not or alternative guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
duolingo_get_authenticated_profileGet authenticated Duolingo profileC
Fetch the richer authenticated profile for a Duolingo user id.
| Name | Required | Description | Default |
|---|---|---|---|
| userId | No | Duolingo numeric user id. Defaults to DUOLINGO_USER_ID. | |
| username | No | Used only to resolve userId when no userId is provided. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, and the description does not disclose any behavioral traits such as authentication requirements, rate limits, error conditions, or the structure of the returned data. For a tool fetching user data, this is a significant gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence with no wasted words. It could be longer to include more context, but as a concise statement it functions adequately.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has no output schema and the description does not explain what the 'richer authenticated profile' contains. Missing information about prerequisites (e.g., authentication), error handling, and return structure. Not minimal viable for a profile fetch tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% with clear parameter descriptions. The description adds no extra meaning beyond the schema, so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action ('Fetch') and the resource ('authenticated profile') for a given user id. It distinguishes from sibling tools like duolingo_get_public_profile by using 'authenticated' and 'richer', but does not explicitly differentiate from other siblings like duolingo_get_xp_summaries.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance on when to use this tool versus alternatives (e.g., when to use authenticated vs public profile, or when to use other tools). The user must infer context from the tool name and siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
duolingo_get_public_profileGet public Duolingo profileB
Fetch public Duolingo profile, streak, total XP, and courses by username.
| Name | Required | Description | Default |
|---|---|---|---|
| username | No | Duolingo username. Defaults to DUOLINGO_USERNAME. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so description is the sole source. It discloses the fetched data types (profile, streak, XP, courses) but does not mention any side effects, error behavior (e.g., on missing username), rate limits, or that the data is public and requires no authentication. Behavioral traits like 'only public data' are implied but not explicitly stated.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One clear sentence that states the action and what is fetched. It is concise but could be slightly more efficient by removing the word 'public' since it's in the tool name. No wasted words, but front-loading is adequate.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, so description should explain return values. It lists key return items (profile, streak, XP, courses) but is vague on format or additional metadata. For a simple fetch tool with 1 parameter, it is moderately complete but leaves questions about structure.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% (the only parameter 'username' has a description). The tool description adds 'Defaults to DUOLINGO_USERNAME' which is already in the schema description. No additional meaning beyond what the schema provides, so baseline score of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description clearly states verb 'Fetch' and resource 'public Duolingo profile, streak, total XP, and courses by username.' It distinguishes from siblings like 'duolingo_get_authenticated_profile' (which requires auth) and 'duolingo_get_xp_summaries' (which focuses on XP). The purpose is specific and unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage for fetching public profile by username but provides no explicit guidance on when to use vs alternatives (e.g., when to choose this over duolingo_get_authenticated_profile or duolingo_today_status). No when-not-to-use conditions are mentioned.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
duolingo_get_xp_summariesGet Duolingo XP summariesB
Fetch authenticated daily XP summaries with sessions and practice time.
| Name | Required | Description | Default |
|---|---|---|---|
| userId | No | Duolingo numeric user id. Defaults to DUOLINGO_USER_ID. | |
| endDate | No | End date in YYYY-MM-DD. | |
| readable | No | Return normalized date/minute fields. | |
| timezone | No | America/Sao_Paulo | |
| username | No | Used only to resolve userId when no userId is provided. | |
| startDate | No | Start date in YYYY-MM-DD. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must disclose behavior. It only states 'Fetch authenticated daily XP summaries' without mentioning authentication requirements, rate limits, data freshness, or any side effects. This is insufficient for safe invocation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single concise sentence front-loaded with the core purpose. However, it could be slightly more informative without becoming verbose, hence 4 instead of 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has 6 parameters and no output schema, yet the description does not mention return format, pagination, or limitations. It lacks completeness for an agent to understand the full context of usage.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is high (83%), so baseline is 3. The description adds no parameter-specific meaning beyond the schema; it does not explain how parameters like startDate/endDate affect results. No added value for parameter understanding.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool fetches 'daily XP summaries' with specific details 'sessions and practice time'. It uses a specific verb 'Fetch' and resource, distinguishing from sibling tools like duolingo_get_public_profile which focuses on public profile data.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No explicit guidance on when to use this tool vs alternatives. It implies authenticated access (via 'authenticated') but does not mention prerequisites, when not to use, or differentiation from siblings like duolingo_today_status which may also return XP data.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
duolingo_today_statusGet today's Duolingo statusB
Return whether the user practiced today, XP gained, sessions, minutes, and streak status.
| Name | Required | Description | Default |
|---|---|---|---|
| userId | No | Duolingo numeric user id. Defaults to DUOLINGO_USER_ID. | |
| timezone | No | America/Sao_Paulo | |
| username | No | Used only to resolve userId when no userId is provided. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must cover behavioral traits. It does not mention if authentication is needed, rate limits, or side effects. The output fields are listed but not fully detailed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Single sentence of 15 words directly states the tool's purpose. No unnecessary words, front-loads the key action.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description fails to disclose the return format (e.g., types, structure). Lacks details on default behavior for parameters and error handling.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 67%. The description does not add meaning beyond listing output fields; the timezone parameter is unexplained despite having no schema description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool returns today's Duolingo status, including specific data fields like XP, sessions, and streak. It effectively distinguishes from siblings such as profile or XP summarization tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No explicit guidance on when to use this tool versus alternatives. The use case is implied but not clarified, e.g., no mention of prerequisites or scenarios.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
v0.1.0- First observed
duolingo_auth_help - First observed
duolingo_get_authenticated_profile - First observed
duolingo_get_public_profile - First observed
duolingo_get_xp_summaries - First observed
duolingo_today_status
TDQS
Scored across 5 tools
Each tool targets a distinct aspect of Duolingo data: public profile, authenticated profile, XP summaries, today's status, and auth configuration. There is no overlap in purpose.
All tools share the 'duolingo_' prefix and use snake_case, but 'today_status' and 'auth_help' deviate from the 'get_' verb pattern used by the other three. Minor inconsistency.
Five tools is ideal for a focused metrics server covering public info, authenticated details, daily summaries, and configuration. No tool feels redundant or missing.
Core Duolingo metrics (profile, XP, streak, daily activity) are covered. Missing historical trends or leaderboard data, but the set is practical for most use cases.
Maintenance
Related MCP Connectors
Query your Twitch streams, events, supporters, raids & rankings from an AI assistant via OAuth.
- mcpOAuthcom.vibgrate
Query your team's drift, vulnerability, and upgrade data from any AI assistant. OAuth 2.1, 51 tools.
Your strength-training data for any AI assistant: workouts, progress, muscle volume, routines.
Your WHOOP data in the assistant, read-only: recovery, sleep, strain, workouts, cycles and body meas
Related MCP Servers
- AlicenseBqualityDmaintenanceIntegrates with the Strava API to allow AI assistants to access fitness data including athlete profiles, activity history, and segment statistics. It enables users to query detailed performance metrics and explore geographic segment data through natural language commands.843 npmMIT
- AlicenseBqualityDmaintenanceEnables users to interact with their Strava data through natural language to analyze workouts, track fitness progress, and explore routes. It supports retrieving detailed activity stats, heart rate data, and segment insights directly within AI assistants.26338 npmMIT
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to query live Garmin Connect health and fitness data, including daily metrics, activities, sleep analysis, and trends via natural language.MIT
- AlicenseBqualityCmaintenanceRetrieves daily health, fitness, and activity statistics from Garmin Connect and exposes them to LLMs via tools like steps, heart rate, sleep, and more.15MIT