Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Internal Data MCP ServerFind all software engineers in the Engineering department"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Internal Data MCP Server
A production-grade Model Context Protocol (MCP) server that exposes internal employee directory and project management systems to AI models.
Overview
This MCP server standardizes how AI assistants access your internal data. Instead of building custom integrations for each AI model, you define your data once, and any MCP-compatible client—Claude, ChatGPT, or your custom app—can use it without modification.
Key Capabilities
Employee Directory Search – Find team members by name, email, role, or department
Project Management – Query active, completed, and on-hold projects
Team Composition – Identify who's working on what projects
Organization Structure – Explore departments and team hierarchies
Secure Access Control – Role-based data filtering per user
Audit Logging – Complete audit trail of all data access
Production Ready – Health checks, error handling, Docker-ready
Architecture
┌─────────────────┐
│ AI Models │
│ (Claude, etc) │
└────────┬────────┘
│
HTTP/stdio
│
┌────────▼──────────────────────┐
│ MCP Server (Node.js) │
│ - Tools (Data Operations) │
│ - Resources (Context Data) │
│ - Authentication/Audit │
└────────┬──────────────────────┘
│
┌────┴──────┬──────────┐
│ │ │
┌───▼──┐ ┌────▼───┐ ┌──▼────┐
│ DB │ │ APIs │ │ Docs │
└──────┘ └────────┘ └────────┘Quick Start
Prerequisites
Node.js 22+
npm or yarn
PostgreSQL (for production use; can be mocked in dev)
Installation
# Clone or download the repository
cd internal-data-mcp
# Install dependencies
npm install
# Build TypeScript
npm run buildDevelopment
# Run the HTTP server (dev mode)
npm run dev
# Run with stdio transport (for Claude Desktop)
npm run stdio
# Type check
npm run type-checkThe server will start on http://localhost:3100/mcp by default.
Production
# Build for production
npm run build
# Start the server
npm start
# Or use Docker
docker build -t internal-data-mcp .
docker run -p 3100:3100 -e INTERNAL_DB_URL="postgres://..." internal-data-mcpConfiguration
Environment Variables
# Database connection string
INTERNAL_DB_URL=postgres://user:password@localhost/internal_data
# Server port (default: 3100)
PORT=3100
# Node environment
NODE_ENV=productionDatabase Schema
The server expects the following PostgreSQL tables:
-- Employees table
CREATE TABLE employees (
id UUID PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
department TEXT NOT NULL,
role TEXT NOT NULL,
manager_id UUID,
start_date DATE NOT NULL
);
-- Projects table
CREATE TABLE projects (
id UUID PRIMARY KEY,
name TEXT NOT NULL,
status VARCHAR(20) NOT NULL CHECK (status IN ('active', 'completed', 'on_hold')),
lead_id UUID NOT NULL REFERENCES employees(id),
department TEXT NOT NULL,
deadline DATE
);
-- Project members bridge table
CREATE TABLE project_members (
project_id UUID NOT NULL REFERENCES projects(id),
employee_id UUID NOT NULL REFERENCES employees(id),
PRIMARY KEY (project_id, employee_id)
);API Endpoints
Core MCP Endpoint
POST/GET /mcp– Main MCP protocol endpoint (requires authentication)
Health Checks
GET /health– Full health check with dependency statusGET /health/live– Liveness probeGET /health/ready– Readiness probe
Information
GET /info– Server capabilities and available tools
Tools
search_employees
Search the employee directory by name, email, or role.
Parameters:
query(string, required) – Search termdepartment(string, optional) – Filter by department
Example:
{
"name": "search_employees",
"arguments": {
"query": "engineer",
"department": "Engineering"
}
}list_projects
List projects filtered by status.
Parameters:
status(enum["active", "completed", "on_hold"], required) – Project status
Example:
{
"name": "list_projects",
"arguments": {
"status": "active"
}
}get_project_team
Get all team members for a specific project.
Parameters:
project_id(UUID, required) – Project identifier
Example:
{
"name": "get_project_team",
"arguments": {
"project_id": "550e8400-e29b-41d4-a716-446655440000"
}
}Resources
org-structure
Organization structure overview with all departments.
URI: internal://org-structure
department-info
Detailed information about a specific department.
URI: internal://departments/{name}
Examples:
internal://departments/Engineeringinternal://departments/Marketing
usage-guide
Guide on how to use the MCP server.
URI: internal://usage-guide
Authentication
The server supports multiple authentication methods:
Bearer Token (Recommended)
Include a Bearer token in the Authorization header:
curl -H "Authorization: Bearer your-token" http://localhost:3100/mcpToken format (demo): Base64-encoded {userId}:{orgId}:{role}
Development Mode
Without an Authorization header, the server grants admin access (for development/demo only). In production, this should return 401.
Customizing Auth
Edit src/auth-middleware.ts to integrate with your authentication system:
JWT verification
API key validation
OAuth 2.0
Custom token validation
Access Control
User access is controlled through roles:
admin – Full access to all data
hr – Full employee data access, salary info allowed
manager – Restricted to their department
employee – Limited to public information
Edit src/auth-middleware.ts to customize access policies.
Logging & Audit Trail
All tool invocations are logged to the audit trail with:
User ID and organization
Tool name and parameters
Result size and execution time
Timestamp
This data is essential for compliance and security auditing. Configure your logging destination in src/audit.ts.
Connecting to Claude Desktop
Add to your ~/Library/Application Support/Claude/claude_desktop_config.json (macOS):
{
"mcpServers": {
"internal-data": {
"command": "node",
"args": ["path/to/internal-data-mcp/dist/index.js"],
"env": {
"INTERNAL_DB_URL": "postgres://...",
"PORT": "3100"
}
}
}
}Or use the built-in Stdio transport:
{
"mcpServers": {
"internal-data": {
"command": "tsx",
"args": ["path/to/internal-data-mcp/src/stdio.ts"]
}
}
}Connecting a Custom Client
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const transport = new StreamableHTTPClientTransport(
new URL("http://localhost:3100/mcp"),
{
requestInit: {
headers: {
Authorization: `Bearer ${userToken}`,
},
},
}
);
const client = new Client({
name: "my-app",
version: "1.0.0",
});
await client.connect(transport);
const { tools } = await client.listTools();See src/client-example.ts for a full example.
Project Structure
internal-data-mcp/
├── src/
│ ├── index.ts # HTTP server entry point
│ ├── stdio.ts # Stdio transport entry point
│ ├── db.ts # Database access layer
│ ├── tools.ts # MCP tool definitions
│ ├── resources.ts # MCP resource definitions
│ ├── auth-middleware.ts # Authentication & access control
│ ├── audit.ts # Logging & audit trail
│ └── client-example.ts # Example MCP client
├── Dockerfile # Multi-stage production build
├── claude_desktop_config.json # Claude Desktop configuration
├── package.json # Dependencies
├── tsconfig.json # TypeScript configuration
└── README.md # This fileCommon Patterns
Adding a New Tool
Add a function to
src/db.tsto query your dataRegister the tool in
src/tools.tsusingserver.tool()Define the Zod schema for parameters
Return structured content
server.tool(
"get_user_profile",
"Get a user's profile information",
{
user_id: z.string().uuid().describe("User ID"),
},
async ({ user_id }) => {
const user = await getUserProfile(user_id);
return {
content: [
{
type: "text",
text: formatUserProfile(user),
},
],
};
}
);Adding a New Resource
Resources provide background context the AI can read:
server.resource(
"api-docs",
"internal://api/docs",
{
description: "API documentation",
mimeType: "text/markdown",
},
async (uri) => ({
contents: [
{
uri: uri.href,
mimeType: "text/markdown",
text: "# API Documentation\n...",
},
],
})
);Integrating with an Internal API
server.tool(
"get_ticket",
"Look up a support ticket",
{
ticket_id: z.string().describe("Ticket ID"),
},
async ({ ticket_id }) => {
const response = await fetch(
`${process.env.TICKETING_API_URL}/tickets/${ticket_id}`,
{
headers: {
Authorization: `Bearer ${process.env.TICKETING_API_TOKEN}`,
"X-On-Behalf-Of": getUserContext().userId, // User identity
},
}
);
if (!response.ok) {
return {
content: [
{
type: "text",
text: "Ticket not found or access denied",
},
],
};
}
const ticket = await response.json();
return {
content: [{ type: "text", text: formatTicket(ticket) }],
};
}
);Best Practices
Descriptive Tool Names & Descriptions – The AI decides when to call a tool based on its description. Be specific.
Typed Parameters – Always use Zod and
.describe()for each parameter so the AI understands what's expected.Structured Responses – Return formatted text or markdown, not raw JSON. The AI processes text better than deeply nested structures.
Rate Limiting – Implement circuit breakers to prevent tool abuse:
function checkRateLimit(userId: string, limit = 30, windowMs = 60000) {
// Implementation in audit.ts
}Error Handling – Always return user-friendly error messages, never stack traces.
Access Control – Scope all queries to the authenticated user. Never leak data across organizational boundaries.
Audit Everything – Log every tool call with user ID, parameters, timestamp, and result size.
Troubleshooting
Database Connection Issues
# Test database connectivity
psql $INTERNAL_DB_URL -c "SELECT 1"
# Check environment variable
echo $INTERNAL_DB_URL
# Review logs
npm run dev 2>&1 | grep -i errorAuthentication Issues
For development, requests without a Bearer token use demo credentials
In production, remove this fallback in
src/auth-middleware.tsCheck token format:
base64(userId:orgId:role)
Tool Not Found
Make sure you registered the tool in
src/tools.tsVerify the tool name matches what the AI is calling
Check tool descriptions are clear and specific
Claude Desktop Integration
Copy
claude_desktop_config.jsonto your Claude config directoryRestart Claude Desktop
Check that the MCP server is running on the specified port
Review logs:
npm run dev
Production Deployment
Docker
Build and run in Docker:
docker build -t internal-data-mcp:latest .
docker run -d \
-p 3100:3100 \
-e INTERNAL_DB_URL="postgres://..." \
-e NODE_ENV=production \
--name internal-data-mcp \
internal-data-mcp:latestKubernetes
Basic deployment example:
apiVersion: apps/v1
kind: Deployment
metadata:
name: internal-data-mcp
spec:
replicas: 3
selector:
matchLabels:
app: internal-data-mcp
template:
metadata:
labels:
app: internal-data-mcp
spec:
containers:
- name: mcp
image: internal-data-mcp:latest
ports:
- containerPort: 3100
env:
- name: INTERNAL_DB_URL
valueFrom:
secretKeyRef:
name: mcp-secrets
key: db-url
livenessProbe:
httpGet:
path: /health/live
port: 3100
initialDelaySeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 3100
initialDelaySeconds: 5Environment-Specific Configs
Create .env.production:
INTERNAL_DB_URL=postgres://prod-user:secure-pass@prod-db.internal:5432/internal_data
PORT=3100
NODE_ENV=productionSecurity Considerations
Authentication – Always use HTTPS in production and validate tokens
Authorization – Enforce role-based access control at the tool level
Data Filtering – Filter sensitive fields based on user role
Rate Limiting – Prevent abuse and brute-force attacks
Logging – Maintain complete audit trails for compliance
Network – Isolate the MCP server from the public internet if possible
Secrets – Store credentials in environment variables or secure secret managers
Dependencies – Keep npm packages updated for security patches
Contributing
To extend this server:
Add database queries to
src/db.tsRegister tools in
src/tools.tsAdd resources in
src/resources.tsUpdate access control in
src/auth-middleware.tsTest with
npm run devBuild and verify:
npm run build && npm start
License
MIT
Resources
Support
For issues, questions, or contributions, refer to the project repository or contact the development team.
This server cannot be installed
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.