Company Data MCP Server
Company Data MCP Demo
A multi-source Model Context Protocol (MCP) demo that connects a LangChain/OpenAI chatbot to four company data sources through one read-only MCP server:
PostgreSQL
MongoDB Atlas
Local flat files
A private GitHub repository
The chatbot can investigate business records, application logs, operational guidance, and source-code implementation in one workflow.
Architecture
User
|
v
chatbot.py
LangChain Agent + OpenAI
|
v
MCP Client
|
| Streamable HTTP
v
MCP Server
|
+------------------+------------------+------------------+------------------+
| | | |
v v v v
PostgreSQL MongoDB Atlas Flat Files GitHub
employees application_logs error_codes.csv payment-service-demo
paymentsThe MCP server is independent of the chatbot. Any compatible MCP client can connect to it.
Current MCP Tools
PostgreSQL
list_sql_tables
describe_sql_table
query_sql_tableMongoDB
list_mongo_collections
find_mongo_documentsFlat files
list_data_files
read_data_file
search_data_filesGitHub
list_github_repository_files
read_github_repository_file
search_github_repositoryThe tools are generic but constrained. The server does not expose unrestricted SQL, arbitrary MongoDB commands, unrestricted filesystem access, or GitHub write operations.
Project Structure
company-data-mcp-demo/
|
|-- .env
|-- .env.example
|-- .gitignore
|-- requirements.txt
|-- README.md
|-- chatbot.py
|
|-- data/
| |-- error_codes.csv
| |-- notes.txt
| `-- runbook.json
|
`-- mcp_server/
|-- __init__.py
|-- server.py
|
`-- connectors/
|-- __init__.py
|-- sql_connector.py
|-- mongo_connector.py
|-- file_connector.py
`-- github_connector.pyPrerequisites
Install or have access to:
Python
PostgreSQL
Node.js
MongoDB Atlas account
OpenAI API key
GitHub account
VS Code or another editor
Useful checks:
python --version
node --version
npm --version
psql --versionPython Environment
Create a virtual environment:
python -m venv .venvActivate on Windows CMD:
.venv\Scripts\activateActivate on PowerShell:
.venv\Scripts\Activate.ps1Install dependencies:
python -m pip install -r requirements.txtValidate:
python -m pip checkExpected:
No broken requirements found.requirements.txt
Use the pinned environment for the MCP/LangChain stack:
# MCP
mcp==1.29.0
langchain-mcp-adapters==0.3.2
# LLM / Agent
langchain==1.3.14
langchain-openai==1.4.0
openai==2.47.0
# Environment variables
python-dotenv==1.2.1
# PostgreSQL
psycopg[binary]==3.3.4
# MongoDB Atlas
pymongo==3.12.0
dnspython>=1.16.0
# GitHub REST API
httpxThis project intentionally remains on MCP 1.x because langchain-mcp-adapters==0.3.2 requires MCP below 2.
The MCP server uses:
from mcp.server.fastmcp import FastMCPand starts with Streamable HTTP.
Environment Variables
Create .env in the project root:
OPENAI_API_KEY=your_openai_api_key
# PostgreSQL
DB_HOST=localhost
DB_PORT=5432
DB_NAME=company_demo
DB_USER=mcp_user
DB_PASSWORD=your_postgresql_password
# MongoDB Atlas
MONGODB_USER=mongo_mcp_user
MONGODB_PASSWORD=your_mongodb_password
MONGODB_HOST=your_cluster_host.mongodb.net
MONGODB_DB=company_demo
# GitHub
GITHUB_TOKEN=your_fine_grained_github_token
GITHUB_OWNER=your_github_username
GITHUB_REPO=payment-service-demo
GITHUB_BRANCH=mainCreate .env.example with placeholders only:
OPENAI_API_KEY=your_openai_api_key_here
DB_HOST=localhost
DB_PORT=5432
DB_NAME=company_demo
DB_USER=mcp_user
DB_PASSWORD=your_postgres_password_here
MONGODB_USER=mongo_mcp_user
MONGODB_PASSWORD=your_mongodb_password_here
MONGODB_HOST=your_cluster_host.mongodb.net
MONGODB_DB=company_demo
GITHUB_TOKEN=your_github_token_here
GITHUB_OWNER=your_github_username
GITHUB_REPO=payment-service-demo
GITHUB_BRANCH=mainRecommended .gitignore:
.env
.venv/
venv/
__pycache__/
*.pyc
*.pyo
*.pyd
.vscode/
.DS_Store
Thumbs.dbNever commit .env.
PostgreSQL Setup
Create the database
Start PostgreSQL and connect as an administrator:
psql -U postgresCreate:
CREATE DATABASE company_demo;Connect:
\c company_demoCreate the employees table
CREATE TABLE employees (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
department VARCHAR(100),
role VARCHAR(100),
email VARCHAR(255)
);Create the payments table
CREATE TABLE payments (
id SERIAL PRIMARY KEY,
employee_id INTEGER REFERENCES employees(id),
payment_id VARCHAR(100) UNIQUE NOT NULL,
amount NUMERIC(10,2),
status VARCHAR(50),
error_code VARCHAR(50)
);Insert sample employee data
INSERT INTO employees (
name,
department,
role,
email
)
VALUES
(
'Alice',
'Engineering',
'Developer',
'alice@example.com'
),
(
'Bob',
'Finance',
'Analyst',
'bob@example.com'
),
(
'Charlie',
'Security',
'Security Engineer',
'charlie@example.com'
);Verify:
SELECT * FROM employees;Expected records include:
1 | Alice | Engineering | Developer
2 | Bob | Finance | Analyst
3 | Charlie | Security | Security EngineerInsert sample payment data
INSERT INTO payments (
employee_id,
payment_id,
amount,
status,
error_code
)
VALUES
(
1,
'PAY-001',
5000.00,
'FAILED',
'E109'
),
(
2,
'PAY-002',
2500.00,
'SUCCESS',
NULL
),
(
1,
'PAY-003',
1200.00,
'SUCCESS',
NULL
);Verify:
SELECT * FROM payments;Important demo relationship:
Alice -> PAY-001 -> 5000.00 -> FAILED -> E109
Bob -> PAY-002 -> 2500.00 -> SUCCESS
Alice -> PAY-003 -> 1200.00 -> SUCCESSCreate a dedicated read-only PostgreSQL user
Do not use the postgres administrator account from the MCP server.
Create:
CREATE USER mcp_user WITH PASSWORD 'choose_a_strong_password';Grant database access:
GRANT CONNECT ON DATABASE company_demo TO mcp_user;Grant schema access:
GRANT USAGE ON SCHEMA public TO mcp_user;Grant read-only table access:
GRANT SELECT ON ALL TABLES IN SCHEMA public TO mcp_user;Ensure future tables also receive read access:
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO mcp_user;Verify read-only behavior
Exit:
\qConnect as the MCP user:
psql -U mcp_user -d company_demo -h localhostReading should work:
SELECT * FROM employees;A write should fail:
DELETE FROM employees WHERE id = 1;That failure is expected.
MongoDB Atlas Setup
Create or select a deployment
In MongoDB Atlas:
Sign in
Create or select a project
Create or select a cluster/deployment
Wait until it is ready
Configure Network Access
Open the Atlas Network Access area.
Add your current public IP address.
For production, avoid unnecessarily broad network rules such as:
0.0.0.0/0unless that exposure is intentional.
Create the database and collection
In Atlas Data Explorer create:
Database:
company_demo
Collection:
application_logsInsert sample application logs
PAY-001
{
"payment_id": "PAY-001",
"service": "payment-service",
"error_code": "E109",
"message": "Gateway timeout while contacting payment provider",
"retry_count": 0,
"severity": "ERROR"
}PAY-002
{
"payment_id": "PAY-002",
"service": "payment-service",
"message": "Payment completed successfully",
"retry_count": 0,
"severity": "INFO"
}PAY-003
{
"payment_id": "PAY-003",
"service": "payment-service",
"message": "Payment completed successfully",
"retry_count": 0,
"severity": "INFO"
}The important correlation is:
PostgreSQL:
PAY-001 -> FAILED -> E109
MongoDB:
PAY-001 -> Gateway timeout -> retry_count 0Create a read-only MongoDB database user
Do not use an Atlas admin/database setup account from the MCP application.
Create a new database user:
Username:
mongo_mcp_userUse password/SCRAM authentication.
Assign:
Role:
read
Database:
company_demoAvoid broad permissions such as:
atlasAdmin
readWriteAnyDatabaseTest the MongoDB read-only user
Connect using mongosh:
mongosh "mongodb+srv://YOUR_CLUSTER_HOST/" --username mongo_mcp_userSelect:
use company_demoReading should work:
db.application_logs.find()A write should fail:
db.application_logs.insertOne({
test: "should fail"
})MongoDB passwords with special characters
If the username or password contains reserved URI characters such as:
@
:
/
?
#
%
&
+they must be URL encoded.
The connector handles this by encoding credentials before placing them into the MongoDB URI.
Flat-File Setup
Create:
data/inside the project root.
error_codes.csv
Create:
data/error_codes.csvContents:
error_code,meaning,recommended_action
E109,Gateway timeout,Retry up to 3 times with exponential backoff
E201,Invalid account details,Verify account information before retrying
E305,Provider unavailable,Wait and retry laterThis provides the documented operational guidance used in the demo.
The key relationship is:
PostgreSQL
PAY-001 -> FAILED -> E109
MongoDB
PAY-001 -> Gateway timeout
Flat file
E109 -> Retry up to 3 times with exponential backoffnotes.txt
Optional:
Payment failures must be investigated using transaction records,
application logs, and documented error handling guidance.runbook.json
Optional:
{
"payment_service": {
"owner": "Payments Team",
"critical_severity": "ERROR"
}
}Supported file types
The current connector supports:
.csv
.json
.txt
.log
.md
.yaml
.ymlThe connector is sandboxed to the project's data/ directory and should reject attempts to leave that directory.
GitHub Repository Setup
The GitHub connector reads a separate repository representing the application implementation.
Recommended repository:
payment-service-demoKeep this repository separate from the MCP server repository.
Example structure:
payment-service-demo/
|
|-- README.md
|
|-- app/
| |-- __init__.py
| |-- config.py
| |-- gateway.py
| `-- payment.py
|
`-- tests/
`-- test_payment.pyThe demo repository intentionally implements three retry attempts but uses a fixed delay instead of exponential backoff.
That gives the MCP agent a meaningful implementation mismatch to detect.
Repository visibility
For this demo, use:
PrivateA private repository proves that the GitHub connector is authenticating successfully rather than simply reading public files.
Create a fine-grained GitHub Personal Access Token
In GitHub:
Settings
-> Developer settings
-> Personal access tokens
-> Fine-grained tokens
-> Generate new tokenConfigure:
Repository access:
Only select repositoriesSelect:
payment-service-demoGrant:
Repository permissions:
Contents -> Read-onlyDo not give write permissions.
Copy the generated token and store it only in the MCP project's .env:
GITHUB_TOKEN=your_token_hereDo not place the real token in:
README.md
.env.example
source code
Git commits
chat promptsGitHub environment configuration
Add:
GITHUB_TOKEN=your_fine_grained_github_token
GITHUB_OWNER=your_github_username
GITHUB_REPO=payment-service-demo
GITHUB_BRANCH=mainto .env.
The GitHub token is used by the MCP server to access the private repository. It is not passed to the LLM.
GitHub connector behavior
The connector:
lists approved repository files
reads approved source/text files
searches approved files for text
limits file extensions
limits file sizes
uses the configured repository and branch
performs read-only GitHub operations
No GitHub write, commit, pull-request, merge, or delete functionality is exposed.
Test the GitHub connector directly
Run:
python -m mcp_server.connectors.github_connectorExpected output should include files such as:
README.md
app/__init__.py
app/config.py
app/gateway.py
app/payment.py
tests/test_payment.pyIf this works, the following are confirmed:
GitHub PAT
->
private repository access
->
GitHub REST API
->
Python connectorStart the MCP Server
Activate the virtual environment:
.venv\Scripts\activateStart:
python -m mcp_server.serverThe endpoint is:
http://localhost:8000/mcpKeep this terminal open.
MCP Inspector
Start Inspector in another terminal:
npx @modelcontextprotocol/inspector@latestChoose:
Transport:
Streamable HTTPUse:
http://localhost:8000/mcpExpected tools:
list_sql_tables
describe_sql_table
query_sql_table
list_mongo_collections
find_mongo_documents
list_data_files
read_data_file
search_data_files
list_github_repository_files
read_github_repository_file
search_github_repositorySuggested Inspector Tests
PostgreSQL
List tables:
list_sql_tablesQuery Alice:
{
"table_name": "employees",
"filter_column": "name",
"filter_value": "Alice",
"limit": 20
}MongoDB
Find PAY-001:
{
"collection_name": "application_logs",
"field": "payment_id",
"value": "PAY-001",
"limit": 20
}Flat files
Search:
{
"query": "E109",
"max_results": 50
}Expected match:
E109,Gateway timeout,Retry up to 3 times with exponential backoffGitHub
List repository files:
list_github_repository_filesRead:
{
"path": "app/payment.py"
}Search:
{
"query": "retry",
"max_results": 50
}Useful searches include:
retry
sleep
backoff
timeout
E109Start the Chatbot
Keep the MCP server running.
In another terminal:
.venv\Scripts\activate
python chatbot.pyThe chatbot should discover all 11 MCP tools.
The terminal trace shows:
[AGENT SELECTED TOOL]
Tool: ...
[TOOL RESULT]
Tool: ...
Result: ...This provides observable tool-selection and tool-result logging.
Main End-to-End Demo
Ask:
Investigate Alice's failed payment,
explain why it failed,
tell me the documented recommended action,
and check whether our payment-service code implements that recommendation.Expected investigation:
PostgreSQL
|
| Alice -> employee id 1
| PAY-001 -> FAILED -> E109
v
MongoDB
|
| Gateway timeout while contacting payment provider
| retry_count = 0
v
Flat files
|
| E109
| Retry up to 3 times with exponential backoff
v
GitHub
|
| Inspect payment-service-demo
| Three retry attempts implemented
| Fixed delay implemented
| Exponential backoff missing
v
Final grounded answerExpected conclusion:
Alice's PAY-001 payment failed with error code E109.
The application log shows that the payment service encountered
a gateway timeout while contacting the payment provider.
The documented recommendation for E109 is to retry up to three
times with exponential backoff.
The payment-service implementation does retry up to three times,
but it uses a fixed retry delay instead of exponential backoff.
Therefore, the code only partially implements the documented
E109 remediation guidance.Grounding Rules
The chatbot may say:
PAY-001 failed.because PostgreSQL contains that fact.
It may say:
The application logged a gateway timeout.because MongoDB contains that fact.
It may say:
The documented recommendation is to retry up to three times
with exponential backoff.because error_codes.csv contains that guidance.
It may say:
The implementation retries three times using a fixed delay.only after retrieving the relevant GitHub source.
The chatbot should not claim:
The external provider definitely had an outage.The available evidence only establishes a gateway timeout.
It should not claim:
I retried the payment.No write/retry tool exists.
It should not claim:
I changed the repository.The GitHub connector is read-only.
Security Summary
PostgreSQL
Credential:
mcp_user
Database permissions:
read onlyThe connector also restricts allowed tables and columns.
MongoDB
Credential:
mongo_mcp_user
Role:
read on company_demoThe connector also restricts collections and fields.
Flat Files
Only the approved:
data/directory is accessible.
GitHub
Credential:
Fine-grained PAT
Repository:
payment-service-demo
Permission:
Contents -> Read-onlyNo GitHub write operations are exposed.
Secrets
All real credentials belong only in:
.envNever commit .env.
Rotate any secret immediately if it is accidentally exposed.
Recommended Testing Order
Direct connector test
|
v
MCP Inspector
|
v
ChatbotThis makes failures easy to isolate:
Connector fails
-> source / credentials / API problem
Connector works but Inspector fails
-> MCP server/tool registration problem
Inspector works but chatbot fails
-> MCP client / agent / prompt problemProject Status
OpenAI chatbot DONE
LangChain agent DONE
MCP server DONE
MCP Inspector DONE
PostgreSQL connector DONE
MongoDB Atlas connector DONE
Flat-file connector DONE
GitHub repository connector DONE
Agent/tool trace logging DONE
Four-source investigation READYThe current demo supports:
Business records
+
application logs
+
documented remediation
+
source-code verificationthrough one MCP server.