Nenjo Docs
Agents

MCP Integration

Nenjo as an MCP server, platform tools by resource group, 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 tools for reading and writing Nenjo platform resources (tickets, projects, pipelines, etc.).

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 a scope. 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: tickets:read grants access to tools with scope tickets:read
  • Write implies read: tickets:write also grants access to tickets: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. Each tool definition includes its scope field
  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

Platform Tools by Resource Group

Tickets

ToolScopeDescription
tickets_listtickets:readList tickets with optional filters (project_id, status, priority, type, tags, limit, offset)
tickets_gettickets:readGet a ticket by ID with full details
tickets_createtickets:writeCreate a new ticket
tickets_updatetickets:writeUpdate a ticket (partial update)
tickets_deletetickets:writeDelete a ticket by ID

Projects

ToolScopeDescription
projects_listprojects:readList all accessible projects
projects_getprojects:readGet a project by ID
projects_createprojects:writeCreate a new project
projects_updateprojects:writeUpdate a project (partial update)

Documents

ToolScopeDescription
documents_listdocuments:readList documents in a project
documents_readdocuments:readRead document content by ID
documents_deletedocuments:writeDelete a document from a project

Pipelines

ToolScopeDescription
pipelines_listpipelines:readList all pipelines
pipelines_getpipelines:readGet a pipeline by ID with all steps and edges
pipelines_createpipelines:writeCreate a new pipeline
pipelines_updatepipelines:writeUpdate a pipeline (partial update)
pipelines_deletepipelines:writeDelete a pipeline by ID
pipeline_steps_createpipelines:writeAdd a step to a pipeline
pipeline_edges_createpipelines:writeAdd an edge between two pipeline steps

Executions

ToolScopeDescription
executions_listexecutions:readList execution runs with optional filters
executions_getexecutions:readGet an execution run by ID

Agents

ToolScopeDescription
agents_listagents:readList agents, optionally filtered by project
agents_getagents:readGet an agent by ID
agents_createagents:writeCreate a new agent
agents_updateagents:writeUpdate an agent (partial update)
agents_deleteagents:writeDelete an agent by ID
roles_listagents:readList all agent roles
roles_getagents:readGet an agent role by ID

Councils

ToolScopeDescription
councils_listcouncils:readList all councils
councils_getcouncils:readGet a council by ID with leader and member details
councils_createcouncils:writeCreate a new council
councils_updatecouncils:writeUpdate a council (partial update)
councils_deletecouncils:writeDelete a council by ID

Chat

ToolScopeDescription
chat_sessions_listchat:readList chat sessions for a project and role
chat_messages_listchat:readList chat messages with optional pagination

Graph

ToolScopeDescription
graph_dependenciesgraph:readGet the dependency graph for a project
graph_execution_ordergraph:readGet the execution order based on the dependency graph

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