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:

RequirementVersionPurpose
Node.js20+Runtime for the MCP server process
PostgreSQL16ProBeya database with populated schema
pnpm9+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:

VariableRequiredDescription
PRPROBEYA_API_KEYYesAPI key for authentication. Supports both REST keys (probeya_sk_live_...) and MCP-specific keys (probeya_mcp_live_...).
DATABASE_URLYesPostgreSQL connection string (e.g., postgresql://user:pass@host:5432/probeya)
NEXT_PUBLIC_APP_URLNoBase 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:

FeatureREST Key (probeya_sk_live_)MCP Key (probeya_mcp_live_)
Tool accessAll toolsConfigurable per-tool allowlist
Resource accessAll resourcesConfigurable per-resource allowlist
Token budgetUnlimitedMonthly token cap
Authentication tableapi_keysmcp_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 --from=builder /app/apps/mcp/dist ./dist
COPY --from=builder /app/apps/mcp/package.json .
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /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_KEY is read from environment variables, not stdin, to avoid ps aux leakage.
  • 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:

  1. API key scope check — e.g., read:items, write:kpis
  2. MCP key tool permission — only allowed tools can be invoked (if using MCP key)
  3. MCP key token budget — monthly token limit enforcement (if configured)
  4. 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:

CategoryToolsDescription
Navigationlist_workspaces, list_projects, get_boardBrowse org hierarchy
Item CRUDcreate_item, update_item, delete_item, move_itemManage board items
SearchsearchFull-text search across items, projects, workspaces
KPIslist_kpis, set_kpi_value, get_kpi_alertsMonitor and update KPIs
Actionslist_actions, create_action, update_action, escalate_actionManage action items
Dashboardget_dashboard_stats, list_membersOrg-level analytics
Collaborationadd_commentAdd comments to items

Resources available: probeya://dashboard, probeya://board/{boardId}, probeya://project/{projectId}

Troubleshooting

Server fails to start with “Authentication failed”

  • Verify PRPROBEYA_API_KEY is set and the key is active (not revoked or expired)
  • Check that the key exists in the database (api_keys or mcp_api_keys table)
  • Ensure DATABASE_URL points 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 allowedTools array 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 tokensUsedThisMonth vs maxTokensPerMonth in the mcp_api_keys table
  • Token counters reset on the first day of each month
  • Set maxTokensPerMonth to null for unlimited usage

Tools not appearing in Claude Desktop

  • Verify the command and args paths in claude_desktop_config.json are absolute
  • Check Claude Desktop logs for MCP connection errors
  • Ensure the built dist/index.js file exists (run pnpm --filter @probeya/mcp build)

Dependencies

The MCP server package (@probeya/mcp) depends on:

PackageVersionPurpose
@modelcontextprotocol/sdk^1.0.0MCP protocol implementation
@probeya/apiworkspacetRPC routers and business logic
@probeya/dbworkspaceDrizzle ORM database access
@probeya/sharedworkspaceValidators and shared types
bcryptjs^3.0.2API key hash verification
drizzle-orm^0.45.2Database query builder
zod^3.25.76Input validation