# Connect MCP server

> Configure @paca-ai/paca-mcp with npx for Claude Desktop, VS Code, or any MCP client; env vars, agent mode, and plugin tool loading.

- Repository: Paca-AI/paca
- GitHub: https://github.com/Paca-AI/paca
- Human docs: https://www.grok-wiki.com/public/docs/paca-ai-paca-f238b2ab3d25
- Complete Markdown: https://www.grok-wiki.com/public/docs/paca-ai-paca-f238b2ab3d25/llms-full.txt

## Source Files

- `docs/guides/mcp-server-setup.md`
- `apps/mcp/README.md`
- `apps/mcp/src/server.ts`
- `apps/mcp/src/index.ts`
- `apps/mcp/.env.example`
- `apps/mcp/src/plugin-loader.ts`

---

---
title: "Connect MCP server"
description: "Configure @paca-ai/paca-mcp with npx for Claude Desktop, VS Code, or any MCP client; env vars, agent mode, and plugin tool loading."
---

`@paca-ai/paca-mcp` is a stdio MCP server that authenticates to the Paca REST API with `X-API-Key`, optionally impersonates a project agent via `X-Agent-ID`, filters core tools by permissions at startup, and merges plugin-contributed tools from `GET /api/v1/plugins`. MCP clients launch it with `npx -y @paca-ai/paca-mcp` and pass configuration through environment variables.

## Prerequisites

<Steps>
<Step title="Run Paca and create an API key">

- A running Paca stack (local or deployed). See [Installation](/installation) and [Quickstart](/quickstart).
- Node.js 18+ with `npx` on `PATH`.
- A Paca API key from **Settings → API Keys** in the web UI (user mode), or the server `AGENT_API_KEY` value (agent mode).

</Step>
<Step title="Confirm API reachability">

```bash
curl -s "${PACA_API_URL:-http://localhost:8080}/api/healthz"
```

A healthy API returns HTTP 200. If you use the nginx gateway on port 80, set `PACA_API_URL` to that gateway base URL instead of the internal API port.

</Step>
</Steps>

## Package and launch command

| Field | Value |
|---|---|
| npm package | `@paca-ai/paca-mcp` |
| Launch command | `npx -y @paca-ai/paca-mcp` |
| Transport | stdio (Model Context Protocol) |
| Version check | `npx @paca-ai/paca-mcp --version` |

<Note>
The package is published to npm. No local clone or build step is required for client integration.
</Note>

## Client configuration

Every MCP client needs the same subprocess shape: `command` = `npx`, `args` = `["-y", "@paca-ai/paca-mcp"]`, plus the environment variables in the next section.

<Tabs>
<Tab title="Claude Desktop">

Edit the Claude Desktop config:

- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`

```json
{
  "mcpServers": {
    "paca": {
      "command": "npx",
      "args": ["-y", "@paca-ai/paca-mcp"],
      "env": {
        "PACA_API_KEY": "your-api-key-here",
        "PACA_API_URL": "http://localhost:8080"
      }
    }
  }
}
```

Restart Claude Desktop after saving.

</Tab>
<Tab title="VS Code">

Add to user `settings.json` or workspace `.vscode/mcp.json`:

```json
{
  "mcp": {
    "servers": {
      "paca": {
        "command": "npx",
        "args": ["-y", "@paca-ai/paca-mcp"],
        "env": {
          "PACA_API_KEY": "your-api-key-here",
          "PACA_API_URL": "http://localhost:8080"
        }
      }
    }
  }
}
```

Reload the window or restart the MCP host after editing.

</Tab>
<Tab title="Claude Code">

**Project-level** (recommended for teams): create `.claude/mcp.json` in the repo root with the same `mcpServers.paca` block as Claude Desktop.

**Global CLI**:

```bash
claude mcp add paca \
  --env PACA_API_KEY=<key> \
  --env PACA_API_URL=<url> \
  -- npx -y @paca-ai/paca-mcp
```

<Warning>
Do not commit API keys. Add `.claude/mcp.json` to `.gitignore` when it contains secrets, or inject keys from the shell environment.
</Warning>

</Tab>
<Tab title="Any MCP client">

```json
{
  "name": "paca",
  "command": "npx",
  "args": ["-y", "@paca-ai/paca-mcp"],
  "env": {
    "PACA_API_KEY": "your-api-key-here",
    "PACA_API_URL": "http://localhost:8080"
  }
}
```

Programmatic clients can use `@modelcontextprotocol/sdk` with `StdioClientTransport` pointed at the same command and `env` map.

</Tab>
</Tabs>

## Environment variables

<ParamField body="PACA_API_KEY" type="string" required>
API key sent as `X-API-Key` on every Paca request. In user mode, use a personal key from **Settings → API Keys**. In agent mode, use the server-wide `AGENT_API_KEY` configured on the Paca API service.
</ParamField>

<ParamField body="PACA_API_URL" type="string" default="http://localhost:8080">
Base URL for Paca REST calls (`/api/v1/...`). Defaults to `http://localhost:8080` when unset.
</ParamField>

<ParamField body="PACA_GATEWAY_URL" type="string">
Optional base URL for resolving plugin MCP bundle paths (for example `/plugins-mcp/<id>/mcp.js`). In Docker deployments the nginx gateway serves these bundles, not the API service. When set, plugin `remoteEntryUrl` values resolve against this URL instead of `PACA_API_URL`.
</ParamField>

<ParamField body="PACA_AGENT_ID" type="string (UUID)">
Agent UUID forwarded as `X-Agent-ID` on API requests. Requires `PACA_PROJECT_ID`. Only honored when `PACA_API_KEY` is the server `AGENT_API_KEY`.
</ParamField>

<ParamField body="PACA_PROJECT_ID" type="string (UUID)">
Scopes the MCP server to one project. Required when `PACA_AGENT_ID` is set. In single-project mode, tool calls that pass a different `projectId` argument are rejected.
</ParamField>

| Variable | Required | Default | Used for |
|---|---|---|---|
| `PACA_API_KEY` | Yes | — | `X-API-Key` authentication |
| `PACA_API_URL` | No | `http://localhost:8080` | REST API base URL |
| `PACA_GATEWAY_URL` | No | `PACA_API_URL` | Plugin MCP bundle URL resolution |
| `PACA_AGENT_ID` | No* | — | Agent impersonation (`X-Agent-ID`) |
| `PACA_PROJECT_ID` | No* | — | Single-project scope and permission fetch |

\* `PACA_PROJECT_ID` is required when `PACA_AGENT_ID` is set. The server exits at startup if agent ID is present without project ID.

## Operating modes

The MCP server derives its mode from `PACA_AGENT_ID` and `PACA_PROJECT_ID`, then fetches permissions once at startup to filter the core tool list.

| Mode | Env trigger | `PACA_API_KEY` | Permission source | Tool scope |
|---|---|---|---|---|
| User global | Neither ID set | Personal API key | Filtering disabled (all core tools exposed) | All projects; project-scoped tools may fail without `projectId` |
| User single-project | `PACA_PROJECT_ID` only | Personal API key | `GET /api/v1/users/me/global-permissions` + `GET /api/v1/projects/{id}/members/me/permissions` | One project |
| Agent single-project | Both IDs set | Server `AGENT_API_KEY` | `GET /api/v1/projects/{id}/members/me/permissions` with `X-Agent-ID` | One project; `projectId` arguments locked |

### Agent mode configuration

AI agents inside Paca run in single-project mode. The global agent key is configured on the API service, not per agent:

```bash
# On the Paca server
echo "$AGENT_API_KEY"
```

<CodeGroup>
```json Claude Desktop / Claude Code
{
  "mcpServers": {
    "paca": {
      "command": "npx",
      "args": ["-y", "@paca-ai/paca-mcp"],
      "env": {
        "PACA_API_KEY": "<AGENT_API_KEY from server>",
        "PACA_API_URL": "http://localhost:8080",
        "PACA_AGENT_ID": "550e8400-e29b-41d4-a716-446655440000",
        "PACA_PROJECT_ID": "660e8400-e29b-41d4-a716-446655440001"
      }
    }
  }
}
```
```json Docker stack (with plugin bundles)
{
  "mcpServers": {
    "paca": {
      "command": "npx",
      "args": ["-y", "@paca-ai/paca-mcp"],
      "env": {
        "PACA_API_KEY": "<AGENT_API_KEY from server>",
        "PACA_API_URL": "http://api:8080",
        "PACA_GATEWAY_URL": "http://gateway",
        "PACA_AGENT_ID": "<agent-uuid>",
        "PACA_PROJECT_ID": "<project-uuid>"
      }
    }
  }
}
```
</CodeGroup>

<Tip>
Set `PACA_PROJECT_ID` even in user mode to reduce exposed tools and avoid project-scoped calls without context.
</Tip>

### Permission filtering behavior

- Core tools map to permission keys such as `tasks.read`, `tasks.write`, `docs.read`, and `project.members.write`. Tools without a mapping are allowed by default.
- Plugin tools are **not** filtered at the MCP layer; the Paca API enforces authorization when plugin handlers call back into the platform.
- If permission fetching fails, core tools remain available for backward compatibility.
- Restart the MCP subprocess after changing roles, project membership, or agent permissions so the startup cache refreshes.

## Plugin tool loading

At startup the server calls `GET /api/v1/plugins`, selects enabled plugins whose manifest includes `mcp.remoteEntryUrl`, dynamically imports each entry module, validates the `PluginMCPEntry` contract (`tools` array + `handleToolCall`), and merges definitions into `ListTools`.

```mermaid
sequenceDiagram
    participant Client as MCP client
    participant MCP as @paca-ai/paca-mcp
    participant API as Paca API
    participant GW as nginx gateway
    participant Plugin as Plugin mcp.js

    Client->>MCP: spawn npx -y @paca-ai/paca-mcp (stdio)
    MCP->>API: GET /api/v1/plugins (X-API-Key)
    API-->>MCP: enabled plugins + remoteEntryUrl
    alt relative bundle URL
        MCP->>GW: fetch /plugins-mcp/{id}/mcp.js
    else https remoteEntryUrl
        MCP->>Plugin: import(remoteEntryUrl)
    end
    MCP->>API: fetch project/global permissions
    Client->>MCP: tools/list
    MCP-->>Client: core tools + plugin tools
    Client->>MCP: tools/call
    alt plugin-owned tool name
        MCP->>Plugin: handleToolCall
        Plugin->>API: plugin-scoped HTTP
    else core tool
        MCP->>API: /api/v1/...
    end
```

<AccordionGroup>
<Accordion title="Plugin entry URL rules">

- Relative paths such as `/plugins-mcp/my-plugin/mcp.js` resolve against `PACA_GATEWAY_URL` when set, otherwise `PACA_API_URL`.
- Allowed schemes: `https://`, `file://`, and `http://` (localhost, loopback, or the configured gateway host only).
- `https://` hostnames are DNS-checked to block private/internal IPs (SSRF guard).
- A broken third-party plugin is logged and skipped; other plugins and core tools still load.

</Accordion>
<Accordion title="Duplicate tool names">

If two plugins register the same tool name, the first loaded plugin wins and the duplicate is skipped with a warning.

</Accordion>
</AccordionGroup>

## Verify the connection

<Steps>
<Step title="Check package version">

```bash
npx @paca-ai/paca-mcp --version
```

</Step>
<Step title="Restart the MCP host">

Restart Claude Desktop, VS Code, or Claude Code so the client spawns a fresh MCP subprocess with your env vars.

</Step>
<Step title="Exercise a tool from the client">

Ask the connected model:

- "List my Paca projects"
- "What Paca MCP tools are available?"

In user single-project or agent mode, project-scoped tools such as `list_tasks` should appear only when permissions and `PACA_PROJECT_ID` align.

</Step>
</Steps>

<RequestExample>
```text User prompt
List all projects in my Paca workspace
```
</RequestExample>

<ResponseExample>
```json Tool result (abbreviated)
{
  "content": [
    {
      "type": "text",
      "text": "[{\"id\":\"...\",\"name\":\"My Project\",\"task_id_prefix\":\"PAC\"}]"
    }
  ]
}
```
</ResponseExample>

## Troubleshooting

| Symptom | Likely cause | Fix |
|---|---|---|
| `PACA_API_KEY environment variable is required` | Missing env in MCP client config | Add `PACA_API_KEY` to the `env` block; restart client |
| `PACA_PROJECT_ID ... required when using PACA_AGENT_ID` | Agent mode without project scope | Set both `PACA_AGENT_ID` and `PACA_PROJECT_ID` |
| Connection refused / fetch failed | Wrong `PACA_API_URL` or API down | Confirm `curl $PACA_API_URL/api/healthz`; match gateway vs direct API port |
| 401 Unauthorized | Invalid, revoked, or wrong key type | Regenerate personal key; for agent mode use server `AGENT_API_KEY` |
| Agent actions run as user, not agent | User API key with `PACA_AGENT_ID` | `X-Agent-ID` is ignored unless the key is `AGENT_API_KEY` |
| `projectId must be <uuid> in single-project agent mode` | Tool args use a different project | Pass the configured `PACA_PROJECT_ID` in every project-scoped call |
| No plugin tools listed | Plugin disabled or missing `mcp.remoteEntryUrl` | Install/enable plugin; set `PACA_GATEWAY_URL` in gateway deployments |
| `npx: command not found` | Node.js not installed | Install Node.js 18+ and ensure `npx` is on `PATH` |
| Client shows no Paca tools | JSON syntax error or stale subprocess | Validate config JSON; fully quit and restart the host app |

<Info>
MCP server diagnostics are written to stderr (for example `[server]`, `[permissions]`, `[plugin-loader]` prefixes). Run with `DEBUG="*"` in the environment for verbose SDK logging when debugging outside a GUI client.
</Info>

For local development and MCP Inspector testing, clone the repo and run `npm run inspector` from `apps/mcp`.

## Security notes

- Store API keys in environment variables or OS-specific secret stores, not in version control.
- Use HTTPS for production `PACA_API_URL` and remote plugin `https://` entry URLs.
- Scope agent MCP configs to a single project with `PACA_PROJECT_ID` to prevent cross-project tool calls.
- Rotate personal API keys and `AGENT_API_KEY` on the same schedule as other service secrets.

## Related pages

<CardGroup>
<Card title="Quickstart" href="/quickstart">
Start the stack, create a project, and generate the API key used by MCP.
</Card>
<Card title="MCP tools reference" href="/mcp-tools-reference">
Core tool names, input schemas, and agent-mode constraints.
</Card>
<Card title="Configure AI agents" href="/configure-ai-agents">
Create agents and wire MCP config for in-platform OpenHands conversations.
</Card>
<Card title="Plugin system" href="/plugin-system">
How plugins declare `mcp.remoteEntryUrl` and contribute runtime tools.
</Card>
<Card title="Claude Code skills" href="/claude-code-skills">
Install `/paca` slash commands on top of the MCP server.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Broader install and runtime failures including MCP connection errors.
</Card>
</CardGroup>
