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:readgrants access to tools with scopeprojects:read - Write implies read:
projects:writealso grants access toprojects:readtools
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:
- Worker calls
tools/listand gets all available tools - The backend returns the generic Nenjo platform tools plus any external MCP tools
- The worker filters by the role's
platform_scopes(plus anyadditional_scopesfrom active modes) - Only matching tools are registered in the agent's tool set
Built-In Platform Tools
Nenjo's built-in MCP surface is intentionally small:
| Tool | Description |
|---|---|
app.nenjo.platform/agents | Agents plus prompt, scopes, abilities, context blocks, and assigned MCP servers |
app.nenjo.platform/projects | Projects plus tasks, documents, executions, and dependency graph queries |
app.nenjo.platform/routines | Routines plus steps, edges, and councils |
app.nenjo.platform/mcp_servers | External MCP server configurations |
app.nenjo.platform/chat | Chat sessions, messages, and notifications |
app.nenjo.platform/models | Model 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:
promptscopesabilitiescontext_blocksmcp
Scope Simplicity
Subresources do not create new scope names. The permission model stays coarse:
agents:readallows reads against the root agent resource and its subresourcesagents:writeallows 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
| Transport | Description |
|---|---|
stdio | Child process communicating via stdin/stdout JSON-RPC |
http | Streamable 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-serversFor 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_idMode-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:
- Bootstrap -- On startup, the worker connects to all assigned MCP servers
- Initialize -- Each server receives an
initializerequest with protocol version2025-03-26 - Tool discovery --
tools/listis called to discover available tools - Reconcile -- When bootstrap data changes (via NATS notification), the pool is reconciled: new servers are connected, removed servers are shut down
- 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.