loading…
Search for a command to run...
loading…
Exposes internal employee directories and project management systems to AI models through standardized tools and resources. It enables AI assistants to search f
Exposes internal employee directories and project management systems to AI models through standardized tools and resources. It enables AI assistants to search for team members, query project statuses, and explore organizational hierarchies with secure role-based access control.
A production-grade Model Context Protocol (MCP) server that exposes internal employee directory and project management systems to AI models.
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.
┌─────────────────┐
│ AI Models │
│ (Claude, etc) │
└────────┬────────┘
│
HTTP/stdio
│
┌────────▼──────────────────────┐
│ MCP Server (Node.js) │
│ - Tools (Data Operations) │
│ - Resources (Context Data) │
│ - Authentication/Audit │
└────────┬──────────────────────┘
│
┌────┴──────┬──────────┐
│ │ │
┌───▼──┐ ┌────▼───┐ ┌──▼────┐
│ DB │ │ APIs │ │ Docs │
└──────┘ └────────┘ └────────┘
# Clone or download the repository
cd internal-data-mcp
# Install dependencies
npm install
# Build TypeScript
npm run build
# Run the HTTP server (dev mode)
npm run dev
# Run with stdio transport (for Claude Desktop)
npm run stdio
# Type check
npm run type-check
The server will start on http://localhost:3100/mcp by default.
# 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-mcp
# Database connection string
INTERNAL_DB_URL=postgres://user:password@localhost/internal_data
# Server port (default: 3100)
PORT=3100
# Node environment
NODE_ENV=production
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)
);
POST/GET /mcp – Main MCP protocol endpoint (requires authentication)GET /health – Full health check with dependency statusGET /health/live – Liveness probeGET /health/ready – Readiness probeGET /info – Server capabilities and available toolsSearch the employee directory by name, email, or role.
Parameters:
query (string, required) – Search termdepartment (string, optional) – Filter by departmentExample:
{
"name": "search_employees",
"arguments": {
"query": "engineer",
"department": "Engineering"
}
}
List projects filtered by status.
Parameters:
status (enum["active", "completed", "on_hold"], required) – Project statusExample:
{
"name": "list_projects",
"arguments": {
"status": "active"
}
}
Get all team members for a specific project.
Parameters:
project_id (UUID, required) – Project identifierExample:
{
"name": "get_project_team",
"arguments": {
"project_id": "550e8400-e29b-41d4-a716-446655440000"
}
}
Organization structure overview with all departments.
URI: internal://org-structure
Detailed information about a specific department.
URI: internal://departments/{name}
Examples:
internal://departments/Engineeringinternal://departments/MarketingGuide on how to use the MCP server.
URI: internal://usage-guide
The server supports multiple authentication methods:
Include a Bearer token in the Authorization header:
curl -H "Authorization: Bearer your-token" http://localhost:3100/mcp
Token format (demo): Base64-encoded {userId}:{orgId}:{role}
Without an Authorization header, the server grants admin access (for development/demo only). In production, this should return 401.
Edit src/auth-middleware.ts to integrate with your authentication system:
User access is controlled through roles:
Edit src/auth-middleware.ts to customize access policies.
All tool invocations are logged to the audit trail with:
This data is essential for compliance and security auditing. Configure your logging destination in src/audit.ts.
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"]
}
}
}
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.
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 file
src/db.ts to query your datasrc/tools.ts using server.tool()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),
},
],
};
}
);
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...",
},
],
})
);
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) }],
};
}
);
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.
# 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 error
src/auth-middleware.tsbase64(userId:orgId:role)src/tools.tsclaude_desktop_config.json to your Claude config directorynpm run devBuild 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:latest
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: 5
Create .env.production:
INTERNAL_DB_URL=postgres://prod-user:[email protected]:5432/internal_data
PORT=3100
NODE_ENV=production
To extend this server:
src/db.tssrc/tools.tssrc/resources.tssrc/auth-middleware.tsnpm run devnpm run build && npm startMIT
For issues, questions, or contributions, refer to the project repository or contact the development team.
Добавь это в claude_desktop_config.json и перезапусти Claude Desktop.
{
"mcpServers": {
"internal-data-mcp-server": {
"command": "npx",
"args": []
}
}
}