- Overview
- Purpose
- Installation
- Development with Makefile
- Usage
- Key Features
- Demo using MCP Inspector
- MCP Composer Client with Chatbot UI
The MCP Composer is a FastMCP based Composer that manages multiple MCP servers and tools. Servers and tools can be registered at runtime using structured JSON configurations. The MCP Composer serves as an orchestrator for tool execution and forwards tool requests to the correct upstream MCP server or interface.
The MCP Composer supports multiple tool types, such as OpenAPI (REST), GraphQL, CLI-based tools, client SDKs, and nested MCP servers.
The goal of the MCP Composer is to handle dynamic tool registration, authentication, invocation dispatching, and health monitoring. It abstracts underlying protocol, authentication and routing complexities, and allows tools to be called through a single, unified interface. The MCP Composer exposes a set of MCP-compliant functions that allow listing tools, invoking them, updating credentials, and removing them.
The goal is to provide a single unified MCP Composer that:
- Discovers and registers new MCP tools on startup or via API.
- Mounts and unmounts member servers dynamically.
- Exposes all tools across registered servers.
Update the pyproject.toml if you need to install both mcp_composer
and mcp_composer_app
[tool.setuptools.packages.find]
where = ["src"]
include = ["mcp_composer", "mcp_composer_app"]
If we only want to install mcp_composer
[tool.setuptools.packages.find]
where = ["src"]
include = ["mcp_composer"]
For multiple installation methods, please refer to the Installation Guide
- Python 3.10+
- uv (Recommended for environment management)
-
Clone the repository
git clone https://github.com/IBM/mcp-composer.git cd mcp-composer
-
Create virtual Environment:
uv venv
-
Activate the virtual environment.
source .venv/bin/activate
-
Environment Configuration (Required)
The MCP Composer requires environment variables to be set:
Setup
cp env.example .env
Edit the
.env
file and set the required environment variables:SERVER_CONFIG_FILE_PATH
: Path to the server configuration file (default:member_servers.json
)
Note: If you don't set up the
.env
file, you'll get aKeyError: 'SERVER_CONFIG_FILE_PATH'
error when trying to import the module. -
Database Configuration (Optional)
The MCP Composer supports multiple database backends for storing server configurations, tools, prompts, and resources:
Default Behavior: If no database configuration is provided, MCP Composer runs without persistent storage (no database).
Environment Variable Configuration (Recommended):
You can configure the database using environment variables, which take priority over programmatic configuration:
# For Cloudant database export MCP_DATABASE_TYPE="cloudant" export MCP_DATABASE_API_KEY="your_cloudant_api_key" export MCP_DATABASE_SERVICE_URL="https://your-cloudant-instance.cloudantnosqldb.appdomain.cloud" export MCP_DATABASE_DB_NAME="mcp_servers" # Optional, defaults to "mcp_servers" # For local file storage export MCP_DATABASE_TYPE="local_file" export MCP_DATABASE_FILE_PATH="/path/to/your/servers.json" # Optional # Enable local file storage when no other database config is provided export MCP_USE_LOCAL_FILE_STORAGE="true"
MCP_DATABASE_TYPE
: Database type ("cloudant"
or"local_file"
)MCP_DATABASE_API_KEY
: API key for Cloudant (required for cloudant type)MCP_DATABASE_SERVICE_URL
: Service URL for Cloudant (required for cloudant type)MCP_DATABASE_DB_NAME
: Database name (optional, defaults to"mcp_servers"
)MCP_DATABASE_FILE_PATH
: File path for local storage (optional for local_file type)MCP_USE_LOCAL_FILE_STORAGE
: Set to"true"
to enable local file storage when no other config is provided
Programmatic Database Configuration:
You can also provide a
database_config
dictionary when initializing MCPComposer:from mcp_composer import MCPComposer # Cloudant configuration database_config = { "type": "cloudant", "api_key": "your_cloudant_api_key", "service_url": "https://your-cloudant-instance.cloudantnosqldb.appdomain.cloud", "db_name": "mcp_servers" # Optional, defaults to "mcp_servers" } composer = MCPComposer( name="my-composer", database_config=database_config )
Local File Storage Configuration:
from mcp_composer import MCPComposer # Local file storage configuration database_config = { "type": "local_file", "file_path": "/path/to/your/servers.json" # Optional } composer = MCPComposer( name="my-composer", database_config=database_config )
Required Cloudant Parameters:
type
: Must be set to"cloudant"
api_key
: Your IBM Cloudant API keyservice_url
: Your Cloudant service URL (must start withhttp://
orhttps://
)
Optional Cloudant Parameters:
db_name
: Database name (defaults to"mcp_servers"
)
Custom Database Interface:
You can also provide a custom database implementation by passing a
DatabaseInterface
instance:from mcp_composer.store.database import DatabaseInterface class MyCustomDatabase(DatabaseInterface): # Implement required methods pass custom_db = MyCustomDatabase() composer = MCPComposer( name="my-composer", database_config=custom_db )
Configuration Priority:
- Environment variables (highest priority)
- Programmatic configuration
- No database (default behavior)
Error Handling: If database configuration is provided but missing required keys or has invalid values, the composer will log warnings and fall back to no database configuration.
-
Synchronize the environment.
First time setup: Run this to create the initial
uv.lock
file:uv sync
Subsequent runs: Use the frozen lock file for consistency:
uv sync --frozen # Strict install (uses lock file exactly) - recommended as ensuring consistency across different environments
Update dependencies: If you need to update dependencies:
rm uv.lock && uv sync # Refresh child dependencies, commit uv.lock
-
Add a new dependency; automatically creates a virtual environment if necessary
uv add <my-package>
-
Leverage the project's virtual environment:
uv run <command&args>
-
Test if MCP Composer is installed successfully or not
uv run python -c "import mcp_composer; print(mcp_composer.__version__)"
- Update the
pyproject.toml
with the following:
[tool.hatch.metadata]
allow-direct-references = true
Then you can run the command:
uv add <path to mcp-composer folder>
Then, the pyproject.toml file is updated with the below lines:
[tool.uv.sources]
mcp-composer = { path = "mcp-composer" }
Add mcp-composer to the dependencies section, example of the final pyproject.toml file:
[project]
name = "py_project"
[tool.hatch.metadata]
allow-direct-references = true
dependencies = [
"mcp-composer",
... ...
]
[tool.uv.sources]
mcp-composer= { path = "mcp-composer" }
To ensure the package is properly installed and importable in the consumer project, the following command must be run manually:
uv pip install -e ../mcp-composer
-
Run the following command to install mcp-composer as a tool:
uv tool install -e /<absolute path>/mcp-composer
-
Add the tool to $PATH:
export PATH="/<absolute path>/.local/bin:$PATH"
-
Check the installation:
which mcp-composer
-
Run the following command to uninstall mcp-composer as a tool:
uv tool uninstall mcp-composer
The MCP Composer project includes a comprehensive Makefile that provides convenient commands for development tasks, quality assurance, and package management.
Run all quality assurance checks in sequence:
make all
make format
- Format code with blackmake lint
- Lint code with ruffmake type-check
- Run type checks with mypymake test
- Run tests with coveragemake coverage
- Generate coverage reportmake check
- Run all checks in sequence
make test-module module=<module_name>
- Run unit tests for a specific modulemake test-modules
- Run unit tests for all modules
make clean
- Clean up generated files and caches
make build module=<module_name> version=<x.y.z>
- Build wheel for a specific modulemake check-release module=<module_name> version=<x.y.z>
- Verify wheel with twinemake upload-testpypi module=<module_name> version=<x.y.z>
- Upload to TestPyPImake upload-pypi module=<module_name> version=<x.y.z>
- Upload to PyPI
make status
- Show environment status for releases
# Run all QA checks
make all
# Test a specific module
make test-module module=mcp_composer
# Build a module for release
make build module=mcp_composer version=1.0.0
# Check release artifacts
make check-release module=mcp_composer version=1.0.0
# Upload to TestPyPI
make upload-testpypi module=mcp_composer version=1.0.0
# Clean up development artifacts
make clean
For PyPI uploads, you'll need to set environment variables:
# For TestPyPI
export TEST_TWINE_USERNAME="your_test_username"
export TEST_TWINE_PASSWORD="your_test_password"
# For PyPI
export TWINE_USERNAME="your_username"
export TWINE_PASSWORD="your_p assword"
For more detailed information about available commands, run:
make help
- Run MCP Server using MCP Composer Tool with oauth authentication with following command
uvx mcp-composer -sseurl --sse-url <url to remote sse mcp server> --auth_type oauth --env OAUTH_HOST <host> --env OAUTH_PORT <port> --env OAUTH_SERVER_URL <server url> --env OAUTH_CALLBACK_PATH <callback path> --env OAUTH_CLIENT_ID=<client id> --env OAUTH_CLIENT_SECRET <secret> --env OAUTH_AUTH_URL <auth url> -e-env OAUTH_TOKEN_URL <token url> --env OAUTH_MCP_SCOPE user --env OAUTH_PROVIDER_SCOPE=openid
- Register or remove tools at runtime using structured JSON configurations.
- Support a range of tool types (openapi, graphql, client, mcp, etc.)
- Handles multiple authentication strategies.
- Automatically forwards each request to the correct upstream server or tool.
- List tools and metadata by name or server.
- Database Support: Configurable database backends including IBM Cloudant and local file storage for persistent server configurations, tools, prompts, and resources.
- Environment Variable Configuration: Database configuration through environment variables with validation and fallback support.
- Database Configuration Validation: Strict validation of database configuration with fail-fast behavior to prevent startup with invalid database settings.
To add an MCP server from a local python file, use the builder with a configuration containing the python file path:
Example:
[
{
"id": "mcp-local-news",
"type": "stdio",
"command": "uv",
"args": [
"--directory",
"/<absolute path of the directory>",
"run",
"<name of the python file>.py"
],
"_id": "mcp-local-news"
}
]
Run
uv run test/test_composer.py
test_composer.py can run on either stdio
or http
type.
This will create a FastMCP server instance using the python file and its dependencies and also mount it on mcp-composer.
To add an MCP server from an OpenAPI spec, use the builder with a configuration containing the OpenAPI details:
Example:
from mcp_composer.member_servers.builder import MCPServerBuilder
config = {
"id": "my-openapi-server",
"type": "openapi",
"open_api": {
"endpoint": "https://api.example.com",
"spec_url": "https://api.example.com/openapi.json",
# Optional: "custom_routes": "path/to/custom_routes.json"
},
"auth_strategy": "bearer",
"auth": {
"token": "your-token"
}
}
builder = MCPServerBuilder(config)
mcp_server = await builder.build()
This will create a FastMCP server instance using the OpenAPI specification and authentication details provided.
To add an MCP server from a GraphQL schema, use the builder with a configuration containing the GraphQL endpoint:
Example:
from mcp_composer.member_servers.builder import MCPServerBuilder
config = {
"id": "my-graphql-server",
"type": "graphql",
"endpoint": "https://graphql.example.com/graphql",
# Add any other required config options
}
builder = MCPServerBuilder(config)
mcp_server = await builder.build()
This will create a FastMCP server instance with a GraphQL tool registered, allowing you to interact with the GraphQL API through MCP Composer.
When initializing MCPComposer, you can configure the database backend for persistent storage of server configurations:
Example with Environment Variables (Recommended):
# Set environment variables
export MCP_DATABASE_TYPE="cloudant"
export MCP_DATABASE_API_KEY="your_cloudant_api_key"
export MCP_DATABASE_SERVICE_URL="https://your-cloudant-instance.cloudantnosqldb.appdomain.cloud"
export MCP_DATABASE_DB_NAME="mcp_servers"
from mcp_composer import MCPComposer
# Initialize composer - database config will be loaded from environment variables
composer = MCPComposer(name="my-composer")
# Server configurations will be automatically persisted to Cloudant
await composer.setup_member_servers()
Example with Programmatic Cloudant Configuration:
from mcp_composer import MCPComposer
# Configure Cloudant database programmatically
database_config = {
"type": "cloudant",
"api_key": "your_cloudant_api_key",
"service_url": "https://your-cloudant-instance.cloudantnosqldb.appdomain.cloud",
"db_name": "mcp_servers" # Optional
}
# Initialize composer with database configuration
composer = MCPComposer(
name="my-composer",
database_config=database_config
)
# Server configurations will be automatically persisted to Cloudant
await composer.setup_member_servers()
Example with Local File Storage:
# Option 1: Using environment variables
export MCP_DATABASE_TYPE="local_file"
export MCP_DATABASE_FILE_PATH="/path/to/servers.json"
# Option 2: Enable local file storage when no other config is provided
export MCP_USE_LOCAL_FILE_STORAGE="true"
from mcp_composer import MCPComposer
# Option 1: Environment variables will be used automatically
composer = MCPComposer(name="my-composer")
# Option 2: Programmatic configuration
database_config = {
"type": "local_file",
"file_path": "/path/to/servers.json" # Optional
}
composer = MCPComposer(
name="my-composer",
database_config=database_config
)
# Server configurations will be stored locally
await composer.setup_member_servers()
Example with No Database (Default):
from mcp_composer import MCPComposer
# No database configuration - runs without persistent storage
composer = MCPComposer(name="my-composer")
# Server configurations will not be persisted
await composer.setup_member_servers()
Benefits of Database Configuration:
- Persistence: Server configurations, tools, prompts, and resources are saved across restarts
- Scalability: Cloudant provides distributed storage for multi-instance deployments
- Management: Tools for enabling/disabling tools, prompts, and resources per server
- Versioning: Support for configuration versioning and rollback capabilities
MCP Composer can now be launched directly via a CLI using the mcp-composer
entry point. This provides a lightweight and flexible way to spin up the composer using either HTTP or stdio mode.
mcp-composer --mode <http|stdio> [--host HOST] [--port PORT] [--log-level LEVEL] [--path PATH] [--config <config.json>]
Flag | Description | Default |
---|---|---|
--mode |
Mode to run the Composer in: http or stdio |
http |
--host |
Host to bind to (for http mode) |
0.0.0.0 |
--port |
Port to run on (for http mode) |
9000 |
--log-level |
Log level (e.g. debug , info , warning ) |
debug |
--path |
URL path to mount the MCP Composer on | /mcp |
register_mcp_server
: Register a single server.delete_mcp_server
: Delete a single server.member_health
: Get status for all member servers.activate_mcp_server
: Reactivates a previously deactivated member server by loading its config, updating status in DB, and mounting it.deactivate_mcp_server
: Deactivates a member server by unmounting it and marking it as deactivated in DB.list_member_servers
: List status of all member servers (active or deactivated).
get_tool_config_by_name
: Get a tool configuration detailsget_tool_config_by_server
: Get all tool configuration details of a specific member serverdisable_tools
: Disable a tool or multiple from the servers and Composerenable_tools
: Enable a tool or multiple from the servers and Composerupdate_tool_description
: Update tool description of member servers- add_tools: Add tool using curl command or Python script.
- add_tools_from_openapi: Add tool using the OpenAPI specifications
add_prompts
: Add one or more prompts to the composerget_all_prompts
: Get all registered prompts as JSON strings (excluding disabled ones)list_prompts_per_server
: List all prompts from a specific server (excluding disabled ones)filter_prompts
: Filter prompts based on criteria like name, description, tagsenable_prompts
: Enable prompts from a specific serverdisable_prompts
: Disable prompts from a specific server
add_resource_template
: Add a resource template to the composercreate_resource
: Create a new resource in the composerlist_resources
: List all available resources (actual resources)list_resource_templates
: List all available resource templateslist_resources_per_server
: List all resources and templates from a specific serverfilter_resources
: Filter resources based on criteria like name, description, tagsenable_resources
: Enable resources or templates from a specific serverdisable_resources
: Disable resources or templates from a specific server
Curl command:
{
"name": "event",
"tool_type": "curl",
"curl_config": {
"value": "curl 'https://www.eventbriteapi.com/v3/users/me/organizations/' --header 'Authorization: Bearer XXXXXXXX'"
},
"description": "sample test",
"permission": {
"role 1": "permission 1 "
}
}
Python script:
{
"name": "test",
"tool_type": "script",
"script_config": {
"value": "def search_news(keyword: str) -> str:\n '''Simulate news search using a ticker and return top articles.'''\n import yfinance as yf\n import json\n stock = yf.Ticker(keyword.upper())\n news = stock.news[:5]\n result = []\n for article in news:\n result.append({\n 'title': article.get('title'),\n 'publisher': article.get('publisher'),\n 'link': article.get('link'),\n 'providerPublishTime': article.get('providerPublishTime'),\n })\n return json.dumps(result, indent=2)"
},
"description": "Search top 5 news articles related to a stock ticker using yfinance.",
"permission": {
"role 1": "permission 1"
}
}
input: openapi_spec
{
"openapi": "3.0.1",
"info": {
"title": "IBM Concert API v1.1.0",
"version": "1.1.0",
...
...
}
}
input: auth_config
{
"auth_strategy": "basic",
"auth": {
"username": "user1",
"password": "xxxxxxxx"
}
}
-
add_prompts(prompt_config: list[dict]) -> list[str]
Registers one or more prompts with the composer. -
Arguments:
prompt_config
: A list of dictionaries, each describing a prompt. Each dictionary should contain at least aname
,description
, andtemplate
field.
-
Returns: A list of registered prompt names.
Example:
prompt_config = [
{
"name": "promo_http_avg_response",
"description": "Average response time of promo HTTP calls handled by a cluster",
"template": "What is the average response time of promo HTTP calls handled by Kubernetes cluster {{ cluster }}?",
"arguments": [
{
"name": "cluster",
"type": "string",
"required": true,
"description": "The name of the Kubernetes cluster"
}
]
}
]
added = await composer.add_prompts(prompt_config)
-
get_all_prompts() -> list[str]
Retrieves all registered prompts as JSON strings, with internal function references stripped. -
Returns: A list of JSON strings, each representing a prompt (excluding the
fn
field).
Example:
prompts = await composer.get_all_prompts()
for prompt_json in prompts:
print(prompt_json)
-
list_prompts_per_server(server_id: str) -> list[dict]
Lists all prompts from a specific server (excluding disabled ones). -
Arguments:
server_id
: The ID of the server to list prompts from.
-
Returns: A list of dictionaries containing prompt information with server_id included.
Example:
prompts = await composer.list_prompts_per_server("my-server")
for prompt in prompts:
print(f"Prompt: {prompt['name']} from server: {prompt['server_id']}")
print(f" Description: {prompt['description']}")
print(f" Template: {prompt['template']}")
-
enable_prompts(prompts: list[str], server_id: str) -> str
Enables prompts from a specific server. -
Arguments:
prompts
: A list of prompt names to enable.server_id
: The ID of the server containing the prompts.
-
Returns: A status message indicating success or failure.
Example:
result = await composer.enable_prompts(["app_top_errors_yesterday"], "mcp-prompt")
print(result) # "Enabled ['mcp-prompt_app_top_errors_yesterday'] prompts from server mcp-prompt"
-
disable_prompts(prompts: list[str], server_id: str) -> str
Disables prompts from a specific server. -
Arguments:
prompts
: A list of prompt names to disable.server_id
: The ID of the server containing the prompts.
-
Returns: A status message indicating success or failure.
Example:
result = await composer.disable_prompts(["app_top_errors_yesterday"], "mcp-prompt")
print(result) # "Disabled ['mcp-prompt_app_top_errors_yesterday'] prompts from server mcp-prompt"
-
filter_prompts(filter_criteria: dict) -> list[dict]
Filters prompts based on criteria like name, description, tags, etc. -
Arguments:
filter_criteria
: A dictionary containing filter criteria (name, description, tags).
-
Returns: A list of dictionaries containing matching prompts.
Example:
# Filter by name
result = await composer.filter_prompts({"name": "test"})
# Filter by description
result = await composer.filter_prompts({"description": "response"})
# Filter by multiple criteria
result = await composer.filter_prompts({
"name": "prompt",
"description": "test"
})
-
add_resource_template(resource_config: dict) -> str
Registers a resource template with the composer. -
Arguments:
resource_config
: A dictionary describing the resource template. Should contain at least aname
field.
-
Returns: A success message.
Example:
resource_config = {
"name": "my_template",
"description": "A template for creating resources",
"uri_template": "resource://{name}",
"mime_type": "text/plain",
"tags": ["template", "example"],
"enabled": True
}
result = await composer.add_resource_template(resource_config)
-
create_resource(resource_config: dict) -> str
Creates an actual resource in the composer. -
Arguments:
resource_config
: A dictionary describing the resource. Should contain at least aname
field.
-
Returns: A success message.
Example:
resource_config = {
"name": "my_resource",
"description": "An actual resource with content",
"uri": "resource://my_resource",
"mime_type": "text/plain",
"content": "This is the actual content of the resource",
"tags": ["resource", "example"],
"enabled": True
}
result = await composer.create_resource(resource_config)
-
list_resources() -> list[dict]
Lists all actual resources from composer and mounted servers. -
list_resource_templates() -> list[dict]
Lists all resource templates from composer and mounted servers.
Example:
# List actual resources
resources = await composer.list_resources()
for resource in resources:
print(f"Resource: {resource['name']} - {resource['description']}")
# List resource templates
templates = await composer.list_resource_templates()
for template in templates:
print(f"Template: {template['name']} - {template['description']}")
-
list_resources_per_server(server_id: str) -> list[dict]
Lists all resources and templates from a specific server. -
Arguments:
server_id
: The ID of the server to list resources from.
-
Returns: A list of dictionaries containing resource information with server_id and type included.
Example:
resources = await composer.list_resources_per_server("my-server")
for resource in resources:
print(f"Resource: {resource['name']} from server: {resource['server_id']}")
print(f" Type: {resource['type']}") # 'resource' or 'template'
-
enable_resources(resources: list[str], server_id: str) -> str
Enables resources or templates from a specific server. -
Arguments:
resources
: A list of resource names to enable.server_id
: The ID of the server containing the resources.
-
Returns: A status message indicating success or failure.
Example:
result = await composer.enable_resources(["finance_reference"], "mcp-stock-info")
print(result) # "Enabled ['mcp-stock-info_finance_reference'] resources/templates from server mcp-stock-info"
-
disable_resources(resources: list[str], server_id: str) -> str
Disables resources or templates from a specific server. -
Arguments:
resources
: A list of resource names to disable.server_id
: The ID of the server containing the resources.
-
Returns: A status message indicating success or failure.
Example:
result = await composer.disable_resources(["finance_reference"], "mcp-stock-info")
print(result) # "Disabled ['mcp-stock-info_finance_reference'] resources/templates from server mcp-stock-info"
-
filter_resources(filter_criteria: dict) -> list[dict]
Filters resources based on criteria like name, description, tags, etc. -
Arguments:
filter_criteria
: A dictionary containing filter criteria (name, description, tags, type).
-
Returns: A list of dictionaries containing matching resources.
Example:
# Filter by name
result = await composer.filter_resources({"name": "resource"})
# Filter by description
result = await composer.filter_resources({"description": "test"})
# Filter by type (resource or template)
result = await composer.filter_resources({"type": "resource"})
# Filter by multiple criteria
result = await composer.filter_resources({
"name": "resource",
"description": "test",
"type": "template"
})
-
Run the MCP Inspector as a background process, and take note of the session token/url with token pre-filled:
npx @modelcontextprotocol/inspector
To run a specific version use the following command
npx @modelcontextprotocol/inspector@0.14.3
-
Navigate to
mcp-composer/
and create a.env
file by copying the contents of.env.example
.Then, set the Server and Tool config path in env file accordingly:cp mcp-composer/.env.example mcp-composer/.env
-
Run the following command
uv run test/test_composer.py
-
Open the MCP Inspector in a browser with token pre-filled from the first step above. You can also open on
localhost:6274
and provide thetoken
from the first step as theProxy Session Token
. -
set transport type and URL from the previous step above and press
Connect
: -
Go to Tools in the MCP Inspector and List Tools:
-
We will first use the
register_mcp_server
tool and registerstock_info
MCP server using the bellow config:{ "id": "mcp-stock-info", "type": "http", "endpoint": "https://mcp-stock-info.1vgzmntiwjzl.eu-es.codeengine.appdomain.cloud/mcp" }
-
Once the tool is run, it will be successfully registered:
-
Clear Tool and List Tool again - This is where it might take long time or throw time out error based on the configuration of the MCP Inspector. In case you have time out error disconnect the server and connect again and run the List Tool, it will show all the tools from composer and all tools from
mcp-stockinfo
: -
Run any tools:
Navigate to mcp-composer
and create a .env
file by copying the contents of .env.example
:
cp mcp-composer/.env.example mcp-composer/.env
Open .env
and replace the placeholder values with your actual OAuth provider details (e.g., client ID, client secret, redirect URI, etc.).
Execute the following command to start the server and test the OAuth integration:
uv run test/test_composer_oauth.py
MCP composer client provides an agent backend service to provide chatbot service that talks with all the tools from MCP Composer server.
A chatbot UI demo is also provided just for testing purpose Demo-Chatbot-UI.
In the same .env
(copied from mcp-composer/.env.example
), setup the following variables:
CHAT_MODEL_NAME
: watsonx or ollamaWATSONX_CHAT_MODEL
: meta-llama/llama-4-maverick-17b-128e-instruct-fp8, ibm/granite-3-3-8b-instruct, etcWATSONX_URL
: Watsonx Instance URLWATSONX_API_KEY
: API-Key of Watsonx instanceWATSONX_PROJECT_ID
: Watsonx Project IDCHAT_MODEL_NAME
: local ollama model (ifCHAT_MODEL_NAME=ollama
)
Config file config/mcp_composer_client.yaml
defines what MCP servers are connected, at current stage, it supports:
- Remote MCP-Composer Server
- Remote SSE/Http MCP-Server (testing purpose)
- Stdio MCP-Server (testing purpose)
Each server config has a boolean field enabled
to enable the server or disable it.
(Before running chatbot agent service, make sure all enabled: true
server in config/mcp_composer_client.yaml
is properly setup and can be connected.)
- Option-1. Run agent service in local development environment
Launch agent service:
uv run src/mcp_composer_client/acp_server.py
It should output agent server URL in terminal:
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://localhost:8000 (Press CTRL+C to quit)
-
Option-2. Build and Run Docker Image
Build the image, taking default tag as "chatbot":
docker build -t chatbot -f Dockerfile_Client .
Run the image in container interactively (for Windows/Mac), by default, it uses
MCP_BASE_URL
to connect to MCP composer server.docker run -it -e HOST=0.0.0.0 -p 8000:8000 chatbot
(In Linux,
-e HOST=0.0.0.0
can be removed.)If using
config/mcp_composer_client.yaml
to config multiple MCP servers, set envUSER_CONFIG_FILE
toyes
:docker run -it -e USER_CONFIG_FILE=yes -e HOST=0.0.0.0 -p 8000:8000 chatbot
Follow instruction in Demo-Chatbot-UI, open browser and input chatbot UI URL. Interact with the chatbot.
-
KeyError: 'SERVER_CONFIG_FILE_PATH'
- Cause: Missing environment variable configuration
- Solution: Copy
env.example
to.env
and set the required environment variables
cp env.example .env
-
error: Unable to find lockfile at uv.lock
- Cause: Missing lock file (first-time setup)
- Solution: Run
uv sync
first to create the initial lock file
uv sync
-
Import errors when testing installation
- Cause: Environment variables not loaded
- Solution: Ensure
.env
file exists and contains required variables
# Check if .env file exists ls -la .env # If not, create it cp env.example .env
-
Package not found when adding as local dependency
- Cause: Path issues or missing development install
- Solution: Use the full path and install in editable mode
uv add /full/path/to/mcp-composer --frozen uv pip install -e /full/path/to/mcp-composer
-
Database configuration not working
- Cause: Invalid environment variables or missing required parameters
- Solution: Check environment variable format and required parameters
# Check environment variables echo $MCP_DATABASE_TYPE echo $MCP_DATABASE_API_KEY echo $MCP_DATABASE_SERVICE_URL # For Cloudant, ensure URL starts with http:// or https:// export MCP_DATABASE_SERVICE_URL="https://your-instance.cloudantnosqldb.appdomain.cloud" # For local file storage, ensure file path is valid export MCP_DATABASE_FILE_PATH="/path/to/valid/file.json"
-
Environment variables not taking effect
- Cause: Environment variables not properly set or loaded
- Solution: Ensure environment variables are set before running the application
# Set environment variables in your shell export MCP_DATABASE_TYPE="cloudant" export MCP_DATABASE_API_KEY="your_key" export MCP_DATABASE_SERVICE_URL="https://your-instance.cloudantnosqldb.appdomain.cloud" # Or add them to your .env file echo "MCP_DATABASE_TYPE=cloudant" >> .env echo "MCP_DATABASE_API_KEY=your_key" >> .env echo "MCP_DATABASE_SERVICE_URL=https://your-instance.cloudantnosqldb.appdomain.cloud" >> .env
-
Server fails to start with database configuration errors
- Cause: Invalid database configuration (missing required fields, invalid URLs, etc.)
- Solution: Fix the database configuration errors
# Check the error logs for specific database configuration issues # Common issues: missing API keys, invalid service URLs, unsupported database types # For Cloudant, ensure all required fields are set: export MCP_DATABASE_TYPE="cloudant" export MCP_DATABASE_API_KEY="your_valid_api_key" export MCP_DATABASE_SERVICE_URL="https://your-instance.cloudantnosqldb.appdomain.cloud" # For local file storage: export MCP_DATABASE_TYPE="local_file" export MCP_DATABASE_FILE_PATH="/path/to/valid/file.json"
-
Server starts but some servers are not mounted
- Cause: Individual server configuration errors (unsupported types, invalid endpoints, etc.)
- Solution: Check logs for mount errors and fix the problematic server configurations
# Look for "Failed to mount server" messages in the logs # Fix the specific server configurations that are failing # Server will continue to run with successfully mounted servers