Self-Hosted MCP Server
Run the ProBeya MCP server on your own infrastructure for maximum data sovereignty and security.
Self-Hosted MCP Server
Run the ProBeya MCP server on your own infrastructure when you need full control over data residency, network access, and operational security. The self-hosted server is identical to the cloud-hosted version — same tools, resources, prompts, and authentication model.
Prerequisites
Before deploying the MCP server, ensure your environment meets these requirements:
| Requirement | Version | Purpose |
|---|---|---|
| Node.js | 20+ | Runtime for the MCP server process |
| PostgreSQL | 16 | ProBeya database with populated schema |
| pnpm | 9+ | Package manager (if building from source) |
| ProBeya API Key | — | probeya_sk_live_... or probeya_mcp_live_... |
The MCP server connects directly to the ProBeya PostgreSQL database via the DATABASE_URL environment variable. It does not proxy through the web application — all queries run against the database with the same tenant isolation enforced by tRPC procedures.
Installation
From the Monorepo (Development)
If you have the ProBeya monorepo cloned locally:
# Install all workspace dependencies
pnpm install
# Build the MCP package and its dependencies
pnpm --filter @probeya/mcp build
# Run the server
node apps/mcp/dist/index.js
From npm (Production)
npm install -g @probeya/mcp
From Source
git clone https://github.com/actigence/probeya.git
cd probeya
pnpm install
pnpm --filter @probeya/mcp build
Environment Variables
The MCP server requires the following environment variables:
| Variable | Required | Description |
|---|---|---|
PRPROBEYA_API_KEY | Yes | API key for authentication. Supports both REST keys (probeya_sk_live_...) and MCP-specific keys (probeya_mcp_live_...). |
DATABASE_URL | Yes | PostgreSQL connection string (e.g., postgresql://user:pass@host:5432/probeya) |
NEXT_PUBLIC_APP_URL | No | Base URL for deep links in QR codes and entity URLs (default: https://probeya.com) |
MCP-Specific vs. REST API Keys
The server supports two key types with different capabilities:
| Feature | REST Key (probeya_sk_live_) | MCP Key (probeya_mcp_live_) |
|---|---|---|
| Tool access | All tools | Configurable per-tool allowlist |
| Resource access | All resources | Configurable per-resource allowlist |
| Token budget | Unlimited | Monthly token cap |
| Authentication table | api_keys | mcp_api_keys |
MCP keys provide granular AI-specific controls. REST keys work as a fallback with full access.
Running with stdio Transport
The stdio transport is used by local AI clients (Claude Desktop, Claude Code, VS Code extensions). The AI client spawns the MCP server as a child process and communicates over stdin/stdout.
Claude Desktop Configuration
Add the following to your claude_desktop_config.json:
{
"mcpServers": {
"probeya": {
"command": "node",
"args": ["/path/to/probeya/apps/mcp/dist/index.js"],
"env": {
"PRPROBEYA_API_KEY": "probeya_sk_live_...",
"DATABASE_URL": "postgresql://user:pass@localhost:5432/probeya"
}
}
}
}
Claude Code
claude mcp add probeya -- node /path/to/probeya/apps/mcp/dist/index.js
Or using npx (if published to npm):
claude mcp add probeya -- npx @probeya/mcp
VS Code (Copilot MCP)
Add to your VS Code settings.json:
{
"mcp.servers": {
"probeya": {
"command": "node",
"args": ["/path/to/probeya/apps/mcp/dist/index.js"],
"env": {
"PRPROBEYA_API_KEY": "probeya_sk_live_...",
"DATABASE_URL": "postgresql://user:pass@localhost:5432/probeya"
}
}
}
}
Development Mode
For development with auto-reload on file changes:
cd apps/mcp
PRPROBEYA_API_KEY="probeya_sk_live_..." \
DATABASE_URL="postgresql://probeya:probeya@localhost:5432/probeya" \
pnpm dev
This uses tsx watch to recompile on save.
Running with HTTP Transport
For shared team access, CI/CD integrations, or cloud-hosted AI agents, run the MCP server as an HTTP service. The HTTP transport is provided by the @modelcontextprotocol/sdk SSE or Streamable HTTP transports.
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { authenticateMcpConnection } from "@probeya/mcp/auth";
import { createProbeyaMcpServer } from "@probeya/mcp/server";
import http from "node:http";
const { ctx, scopes, mcpKeyMetadata } = await authenticateMcpConnection();
const mcpServer = createProbeyaMcpServer(ctx, scopes, mcpKeyMetadata);
const httpServer = http.createServer(async (req, res) => {
const transport = new StreamableHTTPServerTransport("/mcp", res);
await mcpServer.connect(transport);
await transport.handleRequest(req, res);
});
httpServer.listen(3100, () => {
console.log("MCP HTTP server listening on port 3100");
});
Docker Deployment
Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY . .
RUN corepack enable && corepack prepare pnpm@9 --activate
RUN pnpm install --frozen-lockfile
RUN pnpm --filter @probeya/mcp build
FROM node:20-alpine AS runtime
WORKDIR /app
COPY /app/apps/mcp/dist ./dist
COPY /app/apps/mcp/package.json .
COPY /app/node_modules ./node_modules
COPY /app/packages ./packages
ENV NODE_ENV=production
CMD ["node", "dist/index.js"]
Docker Compose
services:
probeya-mcp:
build:
context: .
dockerfile: apps/mcp/Dockerfile
environment:
- PRPROBEYA_API_KEY=${PRPROBEYA_API_KEY}
- DATABASE_URL=postgresql://probeya:${DB_PASSWORD}@postgres:5432/probeya
- NEXT_PUBLIC_APP_URL=https://acme.probeya.com
depends_on:
- postgres
restart: unless-stopped
Kubernetes
For production Kubernetes deployments, use a Deployment with the API key stored in a Secret:
apiVersion: v1
kind: Secret
metadata:
name: probeya-mcp-secrets
type: Opaque
stringData:
PRPROBEYA_API_KEY: "probeya_sk_live_..."
DATABASE_URL: "postgresql://user:pass@postgres-svc:5432/probeya"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: probeya-mcp
spec:
replicas: 1
template:
spec:
containers:
- name: mcp
image: probeya/mcp:latest
envFrom:
- secretRef:
name: probeya-mcp-secrets
Security
Network Isolation
- The MCP server requires direct PostgreSQL access. Place it in the same network segment as your database.
- For stdio transport, the process runs locally — no network ports are opened.
- For HTTP transport, use TLS termination via a reverse proxy (nginx, Caddy, cloud load balancer).
API Key Security
- The
PRPROBEYA_API_KEYis read from environment variables, not stdin, to avoidps auxleakage. - Failed authentication causes immediate process termination — no retry or fallback.
- API keys are verified using timing-safe bcrypt comparison.
- The key is never logged or included in error messages.
Scope Enforcement
Every tool call is checked against the API key’s scopes before execution:
- API key scope check — e.g.,
read:items,write:kpis - MCP key tool permission — only allowed tools can be invoked (if using MCP key)
- MCP key token budget — monthly token limit enforcement (if configured)
- Audit trail logging — every tool invocation is logged
Tenant Isolation
All database queries filter by organizationId derived from the API key’s owner. The MCP server inherits the same multi-tenant isolation as the REST API — cross-tenant data access is architecturally impossible.
Available Capabilities
The MCP server exposes the following capability categories:
| Category | Tools | Description |
|---|---|---|
| Navigation | list_workspaces, list_projects, get_board | Browse org hierarchy |
| Item CRUD | create_item, update_item, delete_item, move_item | Manage board items |
| Search | search | Full-text search across items, projects, workspaces |
| KPIs | list_kpis, set_kpi_value, get_kpi_alerts | Monitor and update KPIs |
| Actions | list_actions, create_action, update_action, escalate_action | Manage action items |
| Dashboard | get_dashboard_stats, list_members | Org-level analytics |
| Collaboration | add_comment | Add comments to items |
Resources available: probeya://dashboard, probeya://board/{boardId}, probeya://project/{projectId}
Troubleshooting
Server fails to start with “Authentication failed”
- Verify
PRPROBEYA_API_KEYis set and the key is active (not revoked or expired) - Check that the key exists in the database (
api_keysormcp_api_keystable) - Ensure
DATABASE_URLpoints to the correct database with the ProBeya schema
“Insufficient scope” errors on tool calls
- Check the API key’s scopes — an empty scopes array means full access
- For MCP keys, verify the
allowedToolsarray includes the tool being called - Review the scope requirements in the Tools reference
Connection refused on DATABASE_URL
- Verify PostgreSQL is running and accepting connections
- Check network connectivity between the MCP server and database host
- Ensure the database user has SELECT/INSERT/UPDATE/DELETE permissions
MCP key token budget exceeded
- Check
tokensUsedThisMonthvsmaxTokensPerMonthin themcp_api_keystable - Token counters reset on the first day of each month
- Set
maxTokensPerMonthto null for unlimited usage
Tools not appearing in Claude Desktop
- Verify the
commandandargspaths inclaude_desktop_config.jsonare absolute - Check Claude Desktop logs for MCP connection errors
- Ensure the built
dist/index.jsfile exists (runpnpm --filter @probeya/mcp build)
Dependencies
The MCP server package (@probeya/mcp) depends on:
| Package | Version | Purpose |
|---|---|---|
@modelcontextprotocol/sdk | ^1.0.0 | MCP protocol implementation |
@probeya/api | workspace | tRPC routers and business logic |
@probeya/db | workspace | Drizzle ORM database access |
@probeya/shared | workspace | Validators and shared types |
bcryptjs | ^3.0.2 | API key hash verification |
drizzle-orm | ^0.45.2 | Database query builder |
zod | ^3.25.76 | Input validation |
Was this page helpful?