Nenjo Docs
Agents

MCP Integration

Nenjo as an MCP server, resource-family platform tools, agent subresource routing, external MCP servers, and scope filtering.

Nenjo integrates with the Model Context Protocol (MCP) in two ways: as an MCP server that exposes platform tools, and as an MCP client that connects to external MCP servers.

Nenjo as MCP Server

The backend exposes an MCP endpoint at POST /mcp using the Streamable HTTP transport. This endpoint provides a small set of resource-family platform tools.

Authentication

MCP requests are authenticated via API key:

curl -X POST https://your-instance.com/mcp \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list",
    "params": {}
  }'

API keys with empty scopes have full access. Keys with specific scopes only see tools matching those scopes. See API Keys for details on scope configuration.

Scope Filtering

Each tool is assigned scope behavior. The tools/list response only includes tools the authenticated key has access to. The has_scope logic works as follows:

  • Empty scopes = full access to all tools
  • Exact match: projects:read grants access to tools with scope projects:read
  • Write implies read: projects:write also grants access to projects:read tools

How the Worker Uses MCP

The worker connects to the backend MCP endpoint using a full-access API key, then filters tools on the client side based on the role's platform_scopes. This means:

  1. Worker calls tools/list and gets all available tools
  2. The backend returns the generic Nenjo platform tools plus any external MCP tools
  3. The worker filters by the role's platform_scopes (plus any additional_scopes from active modes)
  4. Only matching tools are registered in the agent's tool set

Built-In Platform Tools

Nenjo's built-in MCP surface is intentionally small:

ToolDescription
app.nenjo.platform/agentsAgents plus prompt, scopes, abilities, context blocks, and assigned MCP servers
app.nenjo.platform/projectsProjects plus tasks, documents, executions, and dependency graph queries
app.nenjo.platform/routinesRoutines plus steps, edges, and councils
app.nenjo.platform/mcp_serversExternal MCP server configurations
app.nenjo.platform/chatChat sessions, messages, and notifications
app.nenjo.platform/modelsModel configurations

Each tool uses action and optional subresource arguments instead of encoding every operation in the tool name.

Request Shape

{
  "action": "get",
  "subresource": "prompt",
  "id": "<agent-id>"
}

For projects, project_id is the scope selector and id remains the actual target resource id. This avoids treating the project as a parent object for every nested operation.

Supported agents subresources:

  • prompt
  • scopes
  • abilities
  • context_blocks
  • mcp

Scope Simplicity

Subresources do not create new scope names. The permission model stays coarse:

  • agents:read allows reads against the root agent resource and its subresources
  • agents:write allows writes against the root agent resource and its subresources

That keeps the permission model easy for users while giving the MCP API a clearer shape for agents.

Examples

Read an agent prompt:

{
  "subresource": "prompt",
  "action": "get",
  "id": "<agent-id>"
}

Update agent scopes:

{
  "subresource": "scopes",
  "action": "update",
  "id": "<agent-id>",
  "data": {
    "scopes": ["projects:read", "agents:read"]
  }
}

Assign an MCP server to an agent:

{
  "subresource": "mcp",
  "action": "assign",
  "id": "<agent-id>",
  "data": {
    "mcp_server_id": "<mcp-server-id>"
  }
}

List project tasks:

{
  "subresource": "tasks",
  "action": "list",
  "project_id": "<project-id>",
  "filters": {
    "status": "open"
  }
}

External MCP Servers

Nenjo can connect to external MCP servers to give agents access to third-party tools (GitHub, Linear, Slack, etc.). External servers are configured in the platform and assigned to roles.

Transport Types

TransportDescription
stdioChild process communicating via stdin/stdout JSON-RPC
httpStreamable HTTP transport via JSON-RPC over HTTP POST

Server Configuration

External MCP servers are configured via the API:

# Create an external MCP server
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "github",
    "display_name": "GitHub MCP",
    "transport": "stdio",
    "command": "npx",
    "args": ["-y", "@modelcontextprotocol/server-github"],
    "env_schema": [
      {"key": "GITHUB_PERSONAL_ACCESS_TOKEN", "label": "GitHub Token", "type": "secret"}
    ]
  }' \
  https://your-instance.com/api/v1/mcp-servers

For HTTP transport servers:

{
  "name": "custom-api",
  "display_name": "Custom API Server",
  "transport": "http",
  "url": "https://mcp.example.com/rpc",
  "env_schema": [
    {"key": "api_key", "label": "API Key", "type": "secret"}
  ]
}

Credential Resolution for External MCP Servers

External MCP server credentials are resolved from two sources, checked in order:

1. Environment Variables

Format: NENJO_MCP_{SERVER_NAME}_{FIELD_KEY} (uppercased, non-alphanumeric replaced with _)

export NENJO_MCP_GITHUB_GITHUB_PERSONAL_ACCESS_TOKEN="ghp_xxxxxxxxxxxx"

2. Local Credentials File

~/.nenjo/credentials.toml under [mcp.{server_name}]:

[mcp.github]
GITHUB_PERSONAL_ACCESS_TOKEN = "ghp_xxxxxxxxxxxx"

[mcp.linear]
api_key = "lin_xxxxxxxxxxxx"

For HTTP transport, resolved credentials named authorization, api_key, token, or bearer_token are automatically sent as Authorization: Bearer ... headers. Credentials with keys starting with header_ are injected as custom headers.

For stdio transport, resolved credentials are injected as environment variables into the child process.

Role Assignments

External MCP servers are assigned to roles to make their tools available:

# Assign an MCP server to a role
curl -X POST -H "Authorization: Bearer $TOKEN" \
  https://your-instance.com/api/v1/agent-roles/:id/mcp/:server_id

# List MCP servers assigned to a role
curl -H "Authorization: Bearer $TOKEN" \
  https://your-instance.com/api/v1/agent-roles/:id/mcp

# Remove an MCP server from a role
curl -X DELETE -H "Authorization: Bearer $TOKEN" \
  https://your-instance.com/api/v1/agent-roles/:id/mcp/:server_id

Mode-Based Activation

In addition to permanent role assignments, modes can temporarily activate external MCP servers using the activate_mcp field:

{
  "tools": {
    "activate_mcp": ["github", "linear"]
  }
}

This enables workflows where an agent only has access to certain external services when operating in a specific mode.

Connection Lifecycle

The worker maintains a pool of connected external MCP servers:

  1. Bootstrap -- On startup, the worker connects to all assigned MCP servers
  2. Initialize -- Each server receives an initialize request with protocol version 2025-03-26
  3. Tool discovery -- tools/list is called to discover available tools
  4. Reconcile -- When bootstrap data changes (via NATS notification), the pool is reconciled: new servers are connected, removed servers are shut down
  5. Tool calls -- During execution, tool calls are proxied to the appropriate server via JSON-RPC

External MCP tools are namespaced with the server name prefix (e.g., github_create_issue) to avoid name collisions between servers.

All external MCP tools are classified as Write category by default, which means they require a mode to be active (or the understanding filter to be bypassed) for the agent to use them.

On this page