This repository has been mirrored from the original source: https://github.com/SalesforceAIResearch/MCP-Universe on 13 Sep-2025.
This repository hosts the replication package for a study on improving tool descriptions for the Model Context Protocol (MCP). It provides tools to empirically evaluate, analyze, and augment tool descriptions to enhance the performance of Foundation Model (FM)-based agents.
The key contributions of this repository are:
- A scoring rubric with six components to systematically assess tool description quality.
- An FM-based scanner to automatically detect "smells" (defects) in tool descriptions.
- A semi-automated pipeline to resolve these smells and generate optimized tool descriptions.
- The ability to run the MCP-Universe benchmark to evaluate the impact of these augmented descriptions on agent performance, including success rates and execution costs.
- Support for ablation studies to analyze the importance of different description components.
MCP-Universe is a comprehensive framework designed for developing, testing, and benchmarking AI agents. It offers a robust platform for building and evaluating both AI agents and LLMs across a wide range of task environments. The framework also supports seamless integration with external MCP servers and facilitates sophisticated agent orchestration workflows.
Unlike existing benchmarks that rely on overly simplistic tasks, MCP-Universe addresses critical gaps by evaluating LLMs in real-world scenarios through interaction with actual MCP servers, capturing real application challenges such as:
- 🎯 Long-horizon reasoning across multi-step tasks
- 🔧 Large, unfamiliar tool spaces with diverse MCP servers
- 🌍 Real-world data sources and live environments
- ⚡ Dynamic evaluation with time-sensitive ground truth
- Python: Requires version 3.10-3.12 (Python 3.13 is not yet supported).
- Docker: Used for running Dockerized MCP servers.
- PostgreSQL (optional): Used for database storage and persistence.
-
Clone the repository
git clone https://github.com/SalesforceAIResearch/MCP-Universe.git cd MCP-Universe -
Create and activate virtual environment
python3 -m venv venv source venv/bin/activate -
Install dependencies
pip install -r requirements.txt pip install -r dev-requirements.txt
-
Platform-specific requirements
Linux:
sudo apt-get install libpq-dev
macOS:
brew install postgresql
-
Configure pre-commit hooks
pre-commit install
-
Environment configuration
cp .env.example .env # Edit .env with your API keys and configuration
Copy the environment template and configure your API credentials:
cp .env.example .envConfigure the following environment variables in your .env file. The required keys depend on which benchmark domains you plan to evaluate:
| Environment Variable | Provider | Description | Required For |
|---|---|---|---|
OPENAI_API_KEY |
OpenAI | API key for GPT models (gpt-5, etc.) | All domains |
ANTHROPIC_API_KEY |
Anthropic | API key for Claude models | All domains |
GEMINI_API_KEY |
API key for Gemini models | All domains |
Note: You only need to configure the API key for the LLM provider you intend to use in your evaluation.
| Environment Variable | Service | Description | Setup Instructions |
|---|---|---|---|
SERP_API_KEY |
SerpAPI | Web search API for search benchmark evaluation | Get API key |
GOOGLE_MAPS_API_KEY |
Google Maps | Geolocation and mapping services | Setup Guide |
GITHUB_PERSONAL_ACCESS_TOKEN |
GitHub | Personal access token for repository operations | Token Setup |
GITHUB_PERSONAL_ACCOUNT_NAME |
GitHub | Your GitHub username | N/A |
NOTION_API_KEY |
Notion | Integration token for Notion workspace access | Integration Setup |
NOTION_ROOT_PAGE |
Notion | Root page ID for your Notion workspace | See configuration example below |
Place your .env file in the repository root (the same directory as this README.md) so the workflow helpers can discover it. Populate the following entries to enable the enhanced tool discovery flow:
| Environment Variable | Description | Example |
|---|---|---|
DB_URL |
PostgreSQL connection string containing historical tool executions | postgresql://user:pass@localhost:5432/mcp |
The task similarity search also reuses OPENAI_API_KEY from the core provider configuration to generate embeddings.
| Environment Variable | Description | Example |
|---|---|---|
MAX_TOKEN_LEN |
Maximum number of tokens from a tool response to keep before invoking the LLM | 16000 |
| Environment Variable | Description | Example |
|---|---|---|
BLENDER_APP_PATH |
Full path to Blender executable (we used v4.4.0) | /Applications/Blender.app/Contents/MacOS/Blender |
MCPUniverse_DIR |
Absolute path to your MCP-Universe repository | /Users/username/MCP-Universe |
Notion Root Page ID: If your Notion page URL is:
https://www.notion.so/your_workspace/MCP-Evaluation-1dd6d96e12345678901234567eaf9eff
Set NOTION_ROOT_PAGE=MCP-Evaluation-1dd6d96e12345678901234567eaf9eff
Blender Installation:
- Download Blender v4.4.0 from blender.org
- Install our modified Blender MCP server following the installation guide
- Set the path to the Blender executable
🔒 IMPORTANT SECURITY NOTICE
Please read and follow these security guidelines carefully before running benchmarks:
-
🚨 GitHub Integration: CRITICAL - We strongly recommend using a dedicated test GitHub account for benchmark evaluation. The AI agent will perform real operations on GitHub repositories, which could potentially modify or damage your personal repositories.
-
🔐 API Key Management:
- Store API keys securely and never commit them to version control
- Use environment variables or secure key management systems
- Regularly rotate your API keys for enhanced security
-
🛡️ Access Permissions:
- Grant minimal necessary permissions for each service integration
- Review and limit API key scopes to only required operations
- Monitor API usage and set appropriate rate limits
-
⚡ Blender Operations: The 3D design benchmarks will execute Blender commands that may modify or create files on your system. Ensure you have adequate backups and run in an isolated environment if necessary.
Each benchmark domain has a dedicated YAML configuration file located in mcpuniverse/benchmark/configs/test/. To evaluate your LLM/agent, modify the appropriate configuration file:
| Domain | Configuration File | Description |
|---|---|---|
| Web Search | web_search.yaml |
Search engine and information retrieval tasks |
| Location Navigation | location_navigation.yaml |
Geographic and mapping-related queries |
| Browser Automation | browser_automation.yaml |
Web interaction and automation scenarios |
| Financial Analysis | financial_analysis.yaml |
Market data analysis and financial computations |
| Repository Management | repository_management.yaml |
Git operations and code repository tasks |
| 3D Design | 3d_design.yaml |
Blender-based 3D modeling and design tasks |
In each configuration file, update the LLM specification to match your target model:
kind: llm
spec:
name: llm-1
type: openai # or anthropic, google, etc.
config:
model_name: gpt-4o # Replace with your target modelPass --tool-description-type 1 to replace MCP-supplied tool descriptions with augmented entries stored in
the mcp_servers database table whenever they are available.
# Location Navigation
python tests/benchmark/test_benchmark_location_navigation.py --tool-description-type 1
# Browser Automation
python tests/benchmark/test_benchmark_browser_automation.py --tool-description-type 1
# Financial Analysis
python tests/benchmark/test_benchmark_financial_analysis.py --tool-description-type 1
# Repository Management
python tests/benchmark/test_benchmark_repository_management.py --tool-description-type 1
# Web Search
python tests/benchmark/test_benchmark_web_search.py --tool-description-type 1
# 3D Design
python tests/benchmark/test_benchmark_3d_design.py --tool-description-type 1The script scripts/equivalence_mcp_universe_vs_rest.py evaluates whether the MCP-Universe benchmark subset is practically similar to the remaining tool corpus on the six description-quality rubrics.
It uses hardcoded inputs:
all-mcp-universe-tools-after-optimization-consolidation.csvtool_description_quality_scores/tool-description-quality-updated-consolidated-pupose.csvtool_description_quality_scores/tool-description-quality-updated-consolidated-usage-guidelines.csvtool_description_quality_scores/tool-description-quality-updated-consolidated-limitations.csvtool_description_quality_scores/tool-description-quality-updated-consolidated-parameter_explanation.csvtool_description_quality_scores/tool-description-quality-updated-consolidated-examples.csvtool_description_quality_scores/tool-description-quality-updated-consolidated-length.csv
For each rubric, the script:
- splits tools into
Only-MCP-UniverseandWithout-MCP-Universeusing(mcp_server_name, tool_name) - recomputes each tool's rubric score as the mean of
gpt-41-mini,haiku-35, andqwen3-32b - runs a Welch TOST equivalence test with a
+/- 0.5raw-score margin - applies Holm-Bonferroni correction across the six rubric tests
Run it with:
python scripts/equivalence_mcp_universe_vs_rest.pyThe CLI output reports the unmatched benchmark tools excluded from analysis, then one result block per rubric containing:
- the raw and tested split sizes
- the two group means and standard deviations
- the mean difference (
Only-MCP-Universe - Without-MCP-Universe) - the 90% confidence interval used for TOST
- the raw and Holm-adjusted equivalence p-values
- the final equivalence decision
python tests/benchmark/test_benchmark_financial_analysis.py --tool-description-type 1 --components Purpose,Examples
All possible components are : Purpose, Examples, Limitations, UsageGuideline, Parameter_Explanation,
Define agent(s) and benchmark in a YAML file. Here’s a simple weather forecast benchmark:
kind: llm
spec:
name: llm-1
type: openai
config:
model_name: gpt-4o
---
kind: agent
spec:
name: ReAct-agent
type: react
config:
llm: llm-1
instruction: You are an agent for weather forecasting.
servers:
- name: weather
---
kind: benchmark
spec:
description: Test the agent for weather forecasting
agent: ReAct-agent
tasks:
- dummy/tasks/weather.jsonThe benchmark definition mainly contains two parts: the agent definition and the benchmark configuration. The benchmark configuration is simple—you just need to specify the agent to use (by the defined agent name) and a list of tasks to evaluate. Each task entry is the task config file path. It can be a full file path or a partial file path. If it is a partial file path (like "dummy/tasks/weather.json"), it should be put in the folder mcpuniverse/benchmark/configs in this repo.
This framework offers a flexible way to define both simple agents (such as ReAct) and more complex, multi-step agent workflows.
- Specify LLMs: Begin by declaring the large language models (LLMs) you want the agents to use. Each LLM component
must be assigned a unique name (e.g.,
"llm-1"). These names serve as identifiers that the framework uses to connect the different components together. - Define an agent: Next, define an agent by providing its name and selecting an agent class. Agent classes are
available in
the mcpuniverse.agent package.
Commonly used classes include
"basic","function-call", and"react". Within the agent specification (spec.config), you must also indicate which LLM instance the agent should use by setting the"llm"field. - Create complex workflows: Beyond simple agents, the framework supports the definition of sophisticated, orchestrated workflows where multiple agents interact or collaborate to solve more complex tasks.
For example:
kind: llm
spec:
name: llm-1
type: openai
config:
model_name: gpt-4o
---
kind: agent
spec:
name: basic-agent
type: basic
config:
llm: llm-1
instruction: Return the latitude and the longitude of a place.
---
kind: agent
spec:
name: function-call-agent
type: function-call
config:
llm: llm-1
instruction: You are an agent for weather forecast. Please return the weather today at the given latitude and longitude.
servers:
- name: weather
---
kind: workflow
spec:
name: orchestrator-workflow
type: orchestrator
config:
llm: llm-1
agents:
- basic-agent
- function-call-agent
---
kind: benchmark
spec:
description: Test the agent for weather forecasting
agent: orchestrator-workflow
tasks:
- dummy/tasks/weather.jsonThe evaluate_tool_descriptions CLI loads server definitions from an MCP
configuration file (defaulting to mcpuniverse/mcp/configs/server_list.json),
launches each server through the configured transport, and evaluates every
exposed tool description using two dedicated LLM prompts. One prompt determines
whether the tool is a consolidated workflow, while the other audits the
description for missing best-practice elements. Results are saved to a CSV file
compatible with our internal Node.js tooling, making it easy to compare outputs
across implementations.
export OPENAI_API_KEY=sk-... # or pass --api-key explicitly
python -m mcpuniverse.scripts.evaluate_tool_descriptions \
--model gpt-4o-mini \
--output /tmp/mcp_tool_audit.csvTo target a subset of configured servers, pass one or more --server flags.
python -m mcpuniverse.scripts.evaluate_tool_descriptions \
--model gpt-4o-mini \
--output /tmp/mcp_tool_audit.csv \
--server github --server dateYou can also add ad-hoc server scripts (not yet present in the config file) by
supplying --config values; each path is converted into a temporary MCP
configuration entry before evaluation.
python -m mcpuniverse.scripts.evaluate_tool_descriptions \
--model gpt-4o-mini \
--output /tmp/mcp_tool_audit.csv \
--config mcpuniverse/mcp/servers/github/server.jsonUse scripts/mcp_tool_source_parser_v3.py to extract implementation code for
each MCP tool before running grounded description evaluation. The parser joins
tool metadata with a repository map, clones missing GitHub repositories into a
local cache, detects Python, JavaScript/TypeScript, or Go MCP implementations,
and writes a CSV keyed by (mcp_server_name, tool_name).
The repository map must include:
mcp_server_name,repository_url
yfinance,https://github.com/SalesforceAIResearch/MCP-Universe
github,https://github.com/github/github-mcp-serverThe grounding test map is already provided at
mcpuniverse/mcp/configs/grounding_test_repo_map.csv.
Extract source code for the grounding test servers:
python scripts/mcp_tool_source_parser_v3.py \
--server-list mcpuniverse/mcp/configs/grounding_test_server_list.json \
--repo-map-csv mcpuniverse/mcp/configs/grounding_test_repo_map.csv \
--repo-dir repos \
--output mcp_tool_source_code_v3.csv \
--metadata-source auto \
--transport stdioUseful variants:
# Reuse already-cloned repositories and leave code blank for missing repos.
python scripts/mcp_tool_source_parser_v3.py \
--server-list mcpuniverse/mcp/configs/grounding_test_server_list.json \
--repo-map-csv mcpuniverse/mcp/configs/grounding_test_repo_map.csv \
--repo-dir repos \
--output mcp_tool_source_code_v3.csv \
--metadata-source auto \
--skip-clone
# Avoid live MCP server startup and read tool names/descriptions only from CSV metadata.
python scripts/mcp_tool_source_parser_v3.py \
--server-list mcpuniverse/mcp/configs/grounding_test_server_list.json \
--tool-metadata-csv path/to/tool_metadata.csv \
--metadata-source csv \
--repo-map-csv mcpuniverse/mcp/configs/grounding_test_repo_map.csv \
--output mcp_tool_source_code_v3.csvThe output CSV contains the columns expected by grounded evaluation, including
mcp_server_name, tool_name, tool_description, and
all_tool_code_with_dependencies.
Run the evaluator with the extracted source-code CSV:
export OPENAI_API_KEY=sk-...
python -m mcpuniverse.scripts.evaluate_tool_descriptions \
--model openai:gpt-4.1-mini \
--config mcpuniverse/mcp/configs/grounding_test_server_list.json \
--metadata-source auto \
--transport stdio \
--grounding mcp_tool_source_code_v3.csv \
--output mcpuniverse/scripts/rubric-eval-grounding-test-gpt-41-mini.csvTo compare grounded and ungrounded scoring, run the same command without
--grounding and write to a separate output file:
python -m mcpuniverse.scripts.evaluate_tool_descriptions \
--model openai:gpt-4.1-mini \
--config mcpuniverse/mcp/configs/grounding_test_server_list.json \
--metadata-source auto \
--transport stdio \
--output mcpuniverse/scripts/rubric-eval-grounding-test-gpt-41-mini-no-source.csvGrounding is applied only when the CSV has a non-empty
all_tool_code_with_dependencies value for the exact (server, tool) pair.
Tools without matching source code are still evaluated normally.
Key flags:
| Flag | Description |
|---|---|
--model MODEL_NAME |
Required. Target OpenAI model used for both evaluations. |
--output PATH |
Required. Destination CSV path for the combined scores. |
--config PATH |
Optional. Alternate MCP server configuration file (default: mcpuniverse/mcp/configs/server_list.json). |
--transport {stdio,sse,auto} |
Optional. Preferred transport; auto falls back to SSE when stdio is unavailable. |
--server NAME |
Optional. Limit evaluation to specific servers (repeatable). |
--server-path PATH |
Optional. Explicit path to a server script or directory. Paths are merged into the loaded MCP configuration. |
--pattern GLOB |
Optional. Filename pattern for locating scripts inside provided --server-path directories (default: server.py). |
--metadata-source {dynamic,auto,csv} |
Optional. Load tool metadata from live MCP servers, CSV files, or both. |
--tool-metadata-csv PATH ... |
Optional. CSV metadata used when --metadata-source is csv or auto. |
--grounding PATH |
Optional. Source-code CSV produced by mcp_tool_source_parser_v3.py; must include mcp_server_name, tool_name, and all_tool_code_with_dependencies. |
--limit N |
Optional. Evaluate only the first N discovered tools. |
--dry-run |
Skip LLM calls and emit placeholder rows (useful for connectivity tests). |
The CLI expects access to OpenAI's Chat Completions API. Provide the API key via
OPENAI_API_KEY, the --api-key flag, or a custom --base-url if you are
using a compatible proxy. Each tool evaluation spawns the corresponding MCP
server through its stdio transport, lists available tools, and records both LLM
assessments in the output CSV.
The optimize_tool_descriptions CLI connects to every MCP server defined in a
JSON configuration file, retrieves their tools, and rewrites each tool's
description with the help of an LLM following a built-in rubric. Augmented
descriptions are versioned and stored in the mcp_servers database table so you
can track how wording evolves over time.
python -m mcpuniverse.scripts.optimize_tool_descriptions \
--model <MODEL_ALIAS_OR_ALIAS:MODEL_NAME> \
[--config path/to/server_list.json] \
[--transport stdio|sse|auto] \
[--rubric-file path/to/custom_rubric.txt] \
[--db-url postgres://user:pass@host:port/db] \
[--grounding] \
[--grounding-source-code-csv mcp_tool_source_code.csv]Key notes:
- The
--model(-m) flag accepts either a registered alias (for exampleopenai) or a combination in the formalias:model_namesuch asopenai:gpt-4.1-mini. When only a provider-specific model name is supplied, the CLI attempts to infer the correct alias (e.g.gpt-models map to the OpenAI client). Any API keys needed for that provider are read from the environment. - Database connectivity defaults to the
DB_URLorDATABASE_URLenvironment variables. Use--db-urlto override them explicitly. - Server definitions default to
mcpuniverse/mcp/configs/server_list.json. Provide--configwhen you want to point at a different configuration file. - Supply
--rubric-fileto replace the built-in rubric with custom guidance for the LLM. - Use
--groundingto add source-code context to the optimization prompt. When enabled, the script loadsmcp_tool_source_code.csvby default and matches rows by(mcp_server_name, tool_name). If a matching row hasall_tool_code_with_dependencies, that code is included as grounding context; tools without matching code are still optimized normally without grounding. - Use
--grounding-source-code-csvto point at a different source-code mapping CSV. The CSV must includemcp_server_name,tool_name, andall_tool_code_with_dependencies.
Before running the CLI, ensure the destination database contains the
mcp_servers table with the schema expected by the script. The tool logs which
server/tool pairs were updated and exits with a non-zero status if no
descriptions could be stored.
Before dissecting descriptions, run the database migration that adds the
tool_description_components column to the mcp_servers table:
python -m mcpuniverse.app.db.migration \
--db-url postgresql+asyncpg://user:pass@host:5432/dbnameThe CLI reads the database URL from --db-url or from DB_SOURCE, DB_URL, or
DATABASE_URL. Provide an async-compatible SQLAlchemy URL (for PostgreSQL, use
the postgresql+asyncpg:// scheme).
Once the column exists, use the dissect_tool_descriptions CLI to split each
tool description into the required documentation components:
python -m mcpuniverse.scripts.dissect_tool_descriptions \
--model <MODEL_ALIAS_OR_ALIAS:MODEL_NAME> \
--db-url postgresql://user:pass@host:5432/dbname \
[--additional-descriptions path/to/additional_tool_description.json] \
[--all-versions] \
[--include-existing] \
[--dry-run]Key notes:
- The CLI combines each row's
tool_optimized_descriptionwith the server/tool-specific examples stored inmcpuniverse/mcp/additional_tool_description.json. The merged text and the component description (when present) are passed to the LLM. --modelaccepts the same alias oralias:model_nameformat used by the other scripts. Configure the relevant provider credentials before running the CLI.- Supply a standard psycopg-compatible connection string through
--db-url(or setDB_URL/DATABASE_URL). Only rows missingtool_description_componentsare processed by default; use--include-existingto reprocess populated rows. - Enable
--dry-runto preview the structured output without persisting it. - Successful runs store a JSON object with the keys
Purpose,UsageGuideline,Parameter_Explanation,Limitation, andExamplesfor each processed server/tool pair.
The evaluate_db_tool_descriptions CLI evaluates optimized descriptions that
have already been split into components in the mcp_servers table. It fetches
rows where tool_description_components is populated, reconstructs a single
description from Purpose, UsageGuideline, Limitation,
Parameter_Explanation, and Examples, then applies the existing rubric
evaluator and writes the scores to CSV.
By default, the query keeps the latest updated_at row per
(mcp_server_name, tool_name). Use --version to restrict the query to a
specific mcp_servers.version value before the latest-row selection is applied.
If multiple rows exist for the same server/tool within that version, the most
recently updated row in that version is evaluated.
PYTHONPATH=.:mcpuniverse/scripts python mcpuniverse/scripts/evaluate_db_tool_descriptions.py \
--model openai:gpt-4.1-mini \
--version 2 \
--output mcpuniverse/scripts/version-2-component-evaluation-gpt-41-mini.csvTo restrict evaluation to a subset of servers, repeat --server:
PYTHONPATH=.:mcpuniverse/scripts python mcpuniverse/scripts/evaluate_db_tool_descriptions.py \
--model openai:gpt-4.1-mini \
--version 2 \
--output mcpuniverse/scripts/version-2-component-evaluation-gpt-41-mini.csv \
--server date \
--server weather \
--server githubThe scripts/wilcoxon_version_component_scores.py script compares two
component-evaluation CSVs, such as version 1 versus version 2. It performs an
inner join on (mcp_server_name, tool_name), so when version 2 is a subset of
version 1 the analysis automatically uses only the paired tools available in
both files. It writes both the paired score table used for the analysis and the
Wilcoxon signed-rank test results.
python scripts/wilcoxon_version_component_scores.py \
--old mcpuniverse/scripts/version-1-component-evaluation-gpt-41-mini.csv \
--new mcpuniverse/scripts/version-2-component-evaluation-gpt-41-mini.csv \
--results-output analysis_output/wilcoxon_v1_v2_grounding_results.csv \
--paired-output analysis_output/wilcoxon_v1_v2_grounding_paired_scores.csvThe script compares the overall description_quality_score and the six rubric
dimension scores. It uses SciPy's Wilcoxon implementation when SciPy is
available and falls back to a built-in normal approximation otherwise.
The scripts/wilcoxon_deepeval_component_grounding.py script compares
DeepEval grounding detail CSVs, such as version 1 versus version 2. It joins on
(mcp_server_name, tool_name, component) so each test is paired at the
tool-component level. For every component, it runs separate Wilcoxon
signed-rank tests for faithfulness_score and hallucination_score, including
rank-biserial effect size and improved/declined/unchanged counts.
python scripts/wilcoxon_deepeval_component_grounding.py \
--old analysis_output/deepeval_component_grounding_details_v1.csv \
--new analysis_output/deepeval_component_grounding_details_v2.csv \
--results-output analysis_output/wilcoxon_deepeval_component_grounding_results.csv \
--paired-output analysis_output/wilcoxon_deepeval_component_grounding_paired_scores.csv \
--table-output analysis_output/wilcoxon_deepeval_component_grounding_table.csvIf the version 1 file was written without a suffix, use
analysis_output/deepeval_component_grounding_details.csv as --old.
The scripts/statistical_test_grounding_vs_non_grounding.py script compares
paired binary success outcomes between the second and third columns of
grounding_vs_non_grounding_statistical_test.csv. It uses McNemar's test,
which is appropriate when each row is the same task evaluated under two
conditions, such as success_grounding versus success_non_grounding.
python scripts/statistical_test_grounding_vs_non_grounding.py --exactUse --exact to run the exact binomial McNemar test. Without --exact, the
script uses the continuity-corrected chi-square McNemar test. The output
includes the contingency table, both success rates, p-value, signed effect
direction, and whether the difference is significant at alpha=0.05.
The generate_synthetic_tasks script connects to every MCP server defined in a
config file, fetches each tool's description and input schema, and calls the
OpenAI chat-completions API to produce realistic benchmark tasks at multiple
difficulty levels. Each generated task still uses the benchmark task schema
used by mcpuniverse/benchmark/configs/test/, but the script now writes a
single aggregated JSON file per server for each run.
Difficulty assignment is intentionally biased toward harder cases first:
1task per tool →very hard2tasks per tool →very hard,hard3tasks per tool →very hard,hard,medium>3tasks per tool → continues filling in descending hardness order:very hard, thenhard, thenmedium
Hard and very hard tasks deliberately target edge cases, boundary values, unusual inputs, brittle parameter combinations, or tool-specific failure boundaries.
The OpenAI API key is read exclusively from the OPENAI_API_KEY environment
variable.
Minimal setup from a fresh checkout:
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
cp .env.example .envAdd the required keys to .env or export them in your shell. Synthetic task
generation requires OPENAI_API_KEY; example and limitation discovery requires
OPENROUTER_API_KEY because it runs the ReAct agent and synthesis step through
OpenRouter. The target MCP servers may require their own service credentials
from the Environment Configuration section.
End-to-end command sequence for one server:
source venv/bin/activate
export OPENAI_API_KEY=sk-...
export OPENROUTER_API_KEY=or-...
# 1. Generate a server task bundle from tool descriptions.
python -m mcpuniverse.scripts.utils.generate_synthetic_tasks \
--model gpt-4.1-mini \
--config mcpuniverse/mcp/configs/server_list.json \
--output data/synthetic_tasks \
--server yfinance \
--tasks-per-tool 3 \
--transport stdio
# 2. Pick the generated bundle for the server.
TASK_FILE=$(ls -t data/synthetic_tasks/mcp_server_yfinance_synthetic_tasks_*.json | head -n 1)
# 3. Execute the tasks and synthesize observed examples and limitations.
python -m mcpuniverse.scripts.utils.discover_examples_limitations \
--model minimax/minimax-m2.5 \
--synthesis-model openai/gpt-4.1 \
--task-file "$TASK_FILE" \
--config mcpuniverse/mcp/configs/server_list.json \
--output data/discovered_additional_descriptions.json \
--mergeTo make the discovered examples and limitations available to the component dissection pipeline, pass the generated file explicitly:
python -m mcpuniverse.scripts.dissect_tool_descriptions \
--model openai:gpt-4.1-mini \
--db-url postgresql://user:pass@host:5432/dbname \
--additional-descriptions data/discovered_additional_descriptions.jsonexport OPENAI_API_KEY=sk-...
# generate 3 tasks per tool for all servers
python -m mcpuniverse.scripts.utils.generate_synthetic_tasks \
--model gpt-4.1-mini \
--output data/synthetic_tasks
# restrict to specific servers
python -m mcpuniverse.scripts.utils.generate_synthetic_tasks \
--model gpt-4.1-mini \
--output data/synthetic_tasks \
--server date --server weather \
--tasks-per-tool 4
# use a custom server config
python -m mcpuniverse.scripts.utils.generate_synthetic_tasks \
--model gpt-4o \
--config path/to/my_server_list.json \
--output data/synthetic_tasksOutput files are written as:
<output>/mcp_server_<server_name>_synthetic_tasks_<datetime>.json
Each file contains:
server_namegenerated_attask_counttasks: the list of generated benchmark task objects
Each task also includes _meta.tool_name so bundled tasks remain traceable to
their source tool after aggregation.
Key flags:
| Flag | Default | Description |
|---|---|---|
--model / -m |
(required) | OpenAI model name, e.g. gpt-4.1-mini, gpt-4o |
--config |
mcpuniverse/mcp/configs/server_list.json |
MCP server list JSON config |
--output / -o |
data/synthetic_tasks |
Output directory |
--server |
(all) | Restrict to specific server names (repeatable) |
--tasks-per-tool |
3 |
Total tasks per tool (minimum 1; difficulty assigned in descending hardness order) |
--transport |
stdio |
MCP transport (stdio, sse, auto) |
The discover_examples_limitations script closes the loop between synthetic task
generation and documentation. It replays every task from a server task bundle
through a live ReAct agent, captures the exact tool arguments and responses
produced during execution, and feeds that evidence to an LLM to synthesise a
concise additional_description block — containing concrete Examples and
observed Limitations — in the same JSON format used by
mcpuniverse/mcp/additional_tool_description.json.
Typical workflow
generate_synthetic_tasks → task bundle JSON
↓
discover_examples_limitations → discovered_additional_description_*.json
The agent model and synthesis model both use OpenRouter (key:
OPENROUTER_API_KEY). Pass --synthesis-model to use a different model for
the synthesis step while keeping a lighter model for execution.
export OPENROUTER_API_KEY=or-...
# run tasks for the yfinance server and write descriptions beside the task file
python -m mcpuniverse.scripts.utils.discover_examples_limitations \
--model minimax/minimax-m2.5 \
--task-file data/synthetic_tasks/mcp_server_yfinance_synthetic_tasks_20260426_130032.json
# use a stronger model for synthesis only
python -m mcpuniverse.scripts.utils.discover_examples_limitations \
--model minimax/minimax-m2.5 \
--synthesis-model openai/gpt-4.1 \
--task-file data/synthetic_tasks/mcp_server_yfinance_synthetic_tasks_20260426_130032.json \
--output data/discovered_additional_descriptions.jsonThe default output file is placed in the same directory as --task-file and
named after the task file:
mcp_server_yfinance_synthetic_tasks_20260426_130032.json
→ discovered_additional_description_yfinance_synthetic_20260426_130032.json
By default the script merges new entries into an existing output file,
overwriting only the (server, tool) pairs that were re-observed. Use
--no-merge to overwrite the file completely.
Key flags:
| Flag | Default | Description |
|---|---|---|
--model / -m |
(required) | OpenRouter model for the ReAct agent, e.g. minimax/minimax-m2.5 |
--task-file |
(required) | Path to a server task bundle produced by generate_synthetic_tasks |
--synthesis-model |
same as --model |
OpenRouter model for the synthesis step |
--config |
mcpuniverse/mcp/configs/server_list.json |
MCP server list JSON config |
--output / -o |
auto-derived from task file | Destination JSON file |
--merge / --no-merge |
merge enabled | Merge into or overwrite the output file |
Each entry in the output JSON has three fields:
{
"mcp_server_name": "yfinance",
"tool_name": "get_historical_stock_prices",
"additional_description": "**Examples**\n...\n\n**Limitations**\n..."
}This file can be passed directly to dissect_tool_descriptions via
--additional-descriptions, or referenced wherever
load_additional_tool_descriptions() is called.
The stratified_sample script draws a statistically justified random sample
from a tool-description quality CSV (e.g. data/tool_description_quality.csv)
using Cramér/Cochran's formula with a finite-population correction. The
population is split into strata by MCP server name and each stratum is sampled
proportionally, guaranteeing at least one row per server.
Sample-size formula
n₀ = (Z² × 0.5 × 0.5) / E² # infinite-population estimate (p = 0.5, conservative)
n = n₀ / (1 + (n₀ − 1) / N) # finite-population correction
nₕ = ceil(n × Nₕ / N) # proportional allocation per stratum h
python -m mcpuniverse.scripts.utils.stratified_sample \
--input data/tool_description_quality.csv \
--output data/tool_description_quality_sample.csv \
--confidence 0.80 \
--error 0.05Key flags:
| Flag | Default | Description |
|---|---|---|
--input / -i |
data/tool_description_quality.csv |
Input CSV file |
--output / -o |
<input-stem>_sample.csv |
Output CSV file (saved beside the input by default) |
--confidence / -C |
0.80 |
Desired confidence level — accepts 0.80 or 80 |
--error / -E |
0.05 |
Tolerable margin of error — accepts 0.05 or 5 |
--stratum-col |
mcp_server_name |
Column that defines the strata |
--seed |
42 |
Random seed for reproducibility |
--db-table |
mcp_servers |
Table to look up tool_original_description (mcp_servers, mcp_servers_livemcpbench, mcp_servers_livemcpplus) |
After sampling, the script queries the specified table for each
(mcp_server_name, tool_name) pair and appends a
tool_original_description column to the output CSV. Rows with no
matching DB entry receive N/A. The DB connection URL is read from the
DB_URL or DATABASE_URL environment variable; the lookup is skipped
when neither is set.
The script prints a summary table showing the Z-score, required sample size, actual sample size, and per-stratum allocation before writing the output CSV.
If you use this paper in your research, please cite our paper:
@article{hasan2026model,
title={Model Context Protocol (MCP) Tool Descriptions Are Smelly! Towards Improving AI Agent Efficiency with Augmented MCP Tool Descriptions},
author={Hasan, Mohammed Mehedi and Li, Hao and Rajbahadur, Gopi Krishnan and Adams, Bram and Hassan, Ahmed E},
journal={arXiv preprint arXiv:2602.14878},
year={2026}
}As our paper has used MCP-Universe paper, if you want to check anything in MCP-Universe paper, please feel free to cite them too.
@misc{mcpuniverse,
title={MCP-Universe: Benchmarking Large Language Models with Real-World Model Context Protocol Servers},
author={Ziyang Luo and Zhiqi Shen and Wenzhuo Yang and Zirui Zhao and Prathyusha Jwalapuram and Amrita Saha and Doyen Sahoo and Silvio Savarese and Caiming Xiong and Junnan Li},
year={2025},
eprint={2508.14704},
archivePrefix={arXiv},
primaryClass={cs.AI},
url={https://arxiv.org/abs/2508.14704},
}