Medallia MCP Server
Medallia MCP Server
An MCP (Model Context Protocol) server that integrates with the Medallia Query API to enable AI applications to query customer feedback, customer profiles, and analytics.
What is MCP?
The Model Context Protocol (MCP) is a standardized protocol that enables AI applications to interact with external tools, data sources, and resources. This server exposes:
Tools: Functions to query Medallia data
Resources: Connection status and metadata
Prerequisites
Node.js 20 or later
npm or yarn
Medallia account with API credentials (OAuth 2.0 token)
Installation
npm installConfiguration
Create a .env file in the project root (you can copy .env.example) and set the following values:
cp .env.example .envThen populate:
# Required: OAuth client credentials
MEDALLIA_CLIENT_ID="your_client_id_here"
MEDALLIA_CLIENT_SECRET="your_client_secret_here"
# Required for the token request endpoint
MEDALLIA_REPORTING_INSTANCE="instance.medallia.com"
MEDALLIA_TENANT_NAME="tenant"
# Optional: override the full token endpoint if needed
# MEDALLIA_OAUTH_TOKEN_URL="https://instance.medallia.com/oauth/tenant/token"
# Optional: GraphQL endpoint (defaults to https://api.medallia.com/v2/graphql)
MEDALLIA_API_ENDPOINT="https://api.medallia.com/v2/graphql"
# Optional: default verbatim fields for queryFeedback when verbatimFieldIds is omitted
# Comma-separated list or JSON array string
MEDALLIA_DEFAULT_VERBATIM_FIELD_IDS="q_comment,q_followup_comment"
# Optional: MCP transport mode (defaults to stdio)
# MCP_TRANSPORT="stdio"
#
# Optional: HTTP transport settings (used when MCP_TRANSPORT=http)
# MCP_HTTP_HOST="127.0.0.1"
# MCP_HTTP_PORT="3000"
# MCP_HTTP_PATH="/mcp"
#
# Optional: host/origin validation for non-loopback HTTP deployments
# Comma-separated hostnames, no scheme.
# MCP_ALLOWED_HOSTS="mcp.example.com"
# MCP_ALLOWED_ORIGINS="app.example.com,copilot.microsoft.com"Notes:
The server reads
.env(and alsoenvfor compatibility) from the project root.Variable expansion is supported, so values like
MEDALLIA_OAUTH_TOKEN_URL=https://${MEDALLIA_REPORTING_INSTANCE}/oauth/${MEDALLIA_TENANT_NAME}/tokenwork.You can override the file path with
ENV_FILE(for exampleENV_FILE=.env.local npm run dev).
Getting Medallia Credentials
Log into your Medallia Experience Cloud instance
Navigate to Administration → API Credentials
Create or retrieve your OAuth 2.0 client ID and client secret
Ensure your API account has the Query API capability enabled
Use the reporting instance and tenant values from your Medallia Experience Cloud environment to fetch an access token dynamically
See Medallia Authentication Docs for detailed instructions.
Development
Run the server in development mode with live TypeScript compilation:
npm run devBuilding
Build the TypeScript to JavaScript:
npm run buildProduction
Run the compiled server:
npm startRemote HTTP transport (for online MCP access)
The server now supports Streamable HTTP transport in addition to stdio.
MCP_TRANSPORT=http MCP_HTTP_HOST=0.0.0.0 MCP_HTTP_PORT=3000 npm startThis exposes the MCP endpoint at:
http://<host>:3000/mcpNotes:
stdioremains the default transport for local IDE integrations.On loopback hosts (
localhost,127.0.0.1,::1), localhost host/origin checks are enabled automatically.On non-loopback hosts, set
MCP_ALLOWED_HOSTSandMCP_ALLOWED_ORIGINSto enable host/origin validation.
Testing
Test the server without Medallia credentials (will show graceful error handling):
npm run build
node test-medallia.jsTo test with actual Medallia data:
MEDALLIA_API_TOKEN="your_token" node test-medallia.jsFeatures
Tools
1. getPrograms
List available Experience Programs in your Medallia tenant.
Parameters:
limit(optional, number): Number of programs to return (default: 10)
Returns: JSON with program metadata including name, status, response count, creation date
Example:
Tool: getPrograms
Parameters: { limit: 5 }2. queryFeedback
Query feedback records with optional filtering by date range and field selection.
Parameters:
limit(optional, number): Records per page (default: 10)dateFrom(optional, string): Filter from date (ISO 8601)dateTo(optional, string): Filter to date (ISO 8601)programId(optional, string): Filter by Experience Program IDfieldIds(optional, string[]): Field IDs to return per feedback record (any field type supported by your Medallia tenant)If omitted, the tool uses
MEDALLIA_DEFAULT_VERBATIM_FIELD_IDSfrom.envwhen set.
Use getFieldCatalog to discover available field IDs for your tenant.
Field Data Types: The response includes fieldData with support for StringFieldData and CommentFieldData types from Medallia's GraphQL schema. Additional field types can be queried and will be returned based on your tenant configuration.
Returns: JSON with feedback nodes, total count, pagination info, and fieldData per node when requested (or when default fields are configured in .env)
Example:
Tool: queryFeedback
Parameters: {
limit: 20,
dateFrom: "2024-01-01",
dateTo: "2024-12-31",
fieldIds: ["q_comment", "q_followup_comment"]
}3. queryCustomers
Query customer profiles from Medallia CX Profiles (if enabled).
Parameters:
limit(optional, number): Number of customers (default: 10)firstName(optional, string): Filter by first nameemail(optional, string): Filter by email
Returns: JSON with customer data (ID, email, name, phone) and pagination
Example:
Tool: queryCustomers
Parameters: { limit: 10 }4. getAggregates
Calculate aggregate metrics from feedback data.
Parameters:
metric(required, enum): Type of aggregationcount- Count of recordsaverage- Average valuesum- Sum of valuescustomCalculationcustomcountUniquebucketCountpercentagetopicPercentageglobalRecordsTopicPercentagetopicImpactscaledTopicImpactsentimentCountsentimentPercentagesentimentNetPercentageregressionBetaregressionCorrelationsegmentAveragesegmentPercentilefilteredsumOfProductmin/max(legacy compatibility)
fieldId(optional, string): Field to aggregate (default: configured default aggregate field)secondaryFieldId(optional, string): Required forsumOfProductunless custom metric config is providedcustomCalculationName(optional, string): Required forcustomCalculationunless custom metric config is providedmetricConfigGraphQL(optional, string): Raw GraphQL metric body for advanced metricsdateFrom(optional, string): Filter from date (ISO 8601)dateTo(optional, string): Filter to date (ISO 8601)
Returns: JSON with metric result and applied filters
Use getFieldCatalog to discover the current valid field IDs for your tenant.
Example:
Tool: getAggregates
Parameters: {
metric: "average",
fieldId: "q_sgb_ltr_scale11",
dateFrom: "2024-01-01"
}Advanced metricConfigGraphQL examples (by metric):
// count (plain record count)
{ metric: "count" }
// count on a field
{ metric: "count", fieldId: "q_sgb_ltr_scale11" }
// average / sum / countUnique / custom
{ metric: "average", fieldId: "q_sgb_ltr_scale11" }
{ metric: "sum", fieldId: "q_sgb_ltr_scale11" }
{ metric: "countUnique", fieldId: "q_sgb_ltr_scale11" }
{ metric: "custom", fieldId: "r_bp_branch_net_promoter_score" }
// customCalculation (requires customCalculationName)
{
metric: "customCalculation",
fieldId: "q_sgb_ltr_scale11",
customCalculationName: "bp_count"
}
// sumOfProduct (two fields)
{
metric: "sumOfProduct",
fieldId: "q_driver_1",
secondaryFieldId: "q_driver_2"
}
// min / max (legacy compatibility)
{ metric: "min", fieldId: "q_sgb_ltr_scale11" }
{ metric: "max", fieldId: "q_sgb_ltr_scale11" }// Advanced metrics use metricConfigGraphQL.
// Pass the metric body exactly as required by your Medallia tenant/schema.
// bucketCount
{ metric: "bucketCount", metricConfigGraphQL: 'fieldFilter: { fieldIds: ["e_responsedate"], gte: "2026-01-01", lt: "2026-02-01" }' }
// percentage (Medallia docs note: aggregateTable-focused metric)
{ metric: "percentage", metricConfigGraphQL: 'axis: ROW' }
// topic metrics
{ metric: "topicPercentage", metricConfigGraphQL: 'topic: { sentiment: POSITIVE }' }
{ metric: "globalRecordsTopicPercentage", metricConfigGraphQL: 'topic: { sentiment: POSITIVE }' }
{ metric: "topicImpact", metricConfigGraphQL: 'metric: { average: { field: { id: "q_sgb_ltr_scale11" } } }' }
{ metric: "scaledTopicImpact", metricConfigGraphQL: 'metric: { average: { field: { id: "q_sgb_ltr_scale11" } } }' }
// sentiment metrics
{ metric: "sentimentCount", metricConfigGraphQL: 'sentiments: [POSITIVE, STRONGLY_POSITIVE]' }
{ metric: "sentimentPercentage", metricConfigGraphQL: 'sentiments: [POSITIVE, STRONGLY_POSITIVE]' }
{ metric: "sentimentNetPercentage", metricConfigGraphQL: '{}' }
// regression metrics
{ metric: "regressionBeta", metricConfigGraphQL: 'dependentField: { id: "q_sgb_ltr_scale11" }, independentFields: [{ id: "q_driver_1" }, { id: "q_driver_2" }]' }
{ metric: "regressionCorrelation", metricConfigGraphQL: 'dependentField: { id: "q_sgb_ltr_scale11" }, independentFields: [{ id: "q_driver_1" }, { id: "q_driver_2" }]' }
// segment metrics
{ metric: "segmentAverage", metricConfigGraphQL: 'metric: { average: { field: { id: "q_sgb_ltr_scale11" } } }, segment: { field: { id: "e_unitid" }, key: "12345" }' }
{ metric: "segmentPercentile", metricConfigGraphQL: 'metric: { average: { field: { id: "q_sgb_ltr_scale11" } } }, percentile: 90, segment: { field: { id: "e_unitid" }, key: "12345" }' }
// filtered (wrapper metric)
{ metric: "filtered", metricConfigGraphQL: 'filter: { fieldIds: ["e_responsedate"], gte: "2026-01-01", lt: "2026-02-01" }, metric: { average: { field: { id: "q_sgb_ltr_scale11" } } }' }For advanced metrics, validate exact shape with your tenant using getFieldCatalog + small trial queries, since Medallia setup/custom modules can affect accepted metric payloads.
Resources
medallia-status
URI: medallia://connection-status
Provides real-time status of the Medallia API connection.
Returns: JSON with:
connected- Whether GraphQL client is initializedauthenticated- Whether API token is configuredendpoint- Configured API endpointstatus- Human-readable status messagetimestamp- Current time
Architecture
src/
├── index.ts # Main server with Medallia tools & resources
test-medallia.js # Integration test client
test-client.js # Generic MCP test harnessMedallia Query API
This server wraps the Medallia Query API, which is a GraphQL API for accessing customer feedback, analytics, and metadata.
Key Concepts
Feedback & Invitations: Records of survey responses and events
Customers: CX Profile data (requires CX Profiles add-on)
Aggregates: Calculated metrics (NPS, averages, counts, etc.)
Programs: Experience Programs define the data schema
Fields: Individual survey questions and data fields
Rate Limits
The Medallia Query API has the following limits:
70 requests per second
975,000 requests per 24-hour window
3,000,000 cost-unit limit per query
See Rate Limits for details.
Error Handling
The server gracefully handles errors:
Missing credentials: Returns clear error message instructing user to set
MEDALLIA_API_TOKENInvalid queries: Returns GraphQL error from Medallia API
Network issues: Returns timeout or connection error
Example error response:
{
"content": [
{
"type": "text",
"text": "Error querying programs: MEDALLIA_API_TOKEN environment variable not set."
}
]
}Next Steps
To extend this server:
Add more GraphQL queries for specific use cases
Implement caching of frequently-accessed data
Add more aggregate calculation types
Create custom tools for your specific data schema
Add prompt templates for common analyses
Learn More
Troubleshooting
"MEDALLIA_API_TOKEN environment variable not set"
This is expected when the environment variable is not configured. Set it before running:
export MEDALLIA_API_TOKEN="your_token"
node dist/index.js"Query cost exceeds limit"
Your GraphQL query is too expensive. Try:
Reducing the
limitparameterAdding more specific filters
Splitting into multiple queries
Connection refused
Check that:
Your API endpoint is correct
Your OAuth token is valid and not expired
Your network can reach
api.medallia.com
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/yiannis-h-dev/MCP-Hyundai'
If you have feedback or need assistance with the MCP directory API, please join our Discord server