Kenn Forge MCP¶
Forge can expose cached maintainer workflows to local MCP clients from the running daemon. The companion reads the same in-process repository, activity, workflow, and workspace services as the UI. It does not force provider refreshes or add provider mutations.
On by default¶
With mcp.enabled and mcp.port omitted, the companion runs on Forge's own
port when host is a loopback IP, such as 127.0.0.1 or ::1, and Forge
isn't an execution worker. Other hosts, such as 0.0.0.0 or a tailnet IP,
require enabled = true. With the default backend port, the endpoint is:
To turn it off, save this in ~/.kenn/forge/config.toml:
For a separate listener, set enabled = true. Its port defaults to the backend
port plus one; set port to choose another. A saved port with enabled
omitted keeps the companion off. Existing explicit enabled = true
configurations keep their endpoint. Re-enabling in Settings uses the default
endpoint for the saved host and port. With no MCP port set, it leaves out
main-listener bearer and Tailscale Serve access; saving a port in Settings
stores enabled = true and keeps that access.
Older versions dropped enabled = false when saving. Configurations with no
enabled key and no MCP port start serving /mcp after upgrade when the default
applies, so set enabled = false to keep it off.
Restart the daemon after changing MCP enablement or its listener port:
Loopback MCP follows [api].require_auth, same as the HTTP API.
When authentication is enabled, send the daemon bearer token.
Runtime discovery publishes the resolved MCP listener and URL, and
kenn-forge daemon status --json shows both the listener address and the token
file path.
With explicit [mcp].enabled = true, /mcp on the main listener also accepts
daemon bearer holders and, with [api.tailscale_serve], allowlisted Serve users.
The omitted default enables only the direct loopback companion.
For a machine-readable listener list, run:
The command returns an array with pid, transport: "http", url,
backend_url, and an optional token_path. It reports the active listener,
including when saved settings require a restart. A stopped daemon or inactive
companion returns []; the command never starts either. Use --config to
select another configuration and --timeout to set the daemon request timeout.
Token contents never appear in the output. For client setup instructions and
restart guidance, use kenn-forge mcp quickstart.
Configure your MCP client with the HTTP URL. For clients that accept a JSON server catalog, the shape is typically similar to:
With [api].require_auth enabled, add the bearer token as an Authorization
header. The daemon mints the token into the auth_token file in its data
directory (mode 0600); read it from there rather than pasting it into shared
configuration:
{
"mcpServers": {
"kenn-forge": {
"type": "http",
"url": "http://127.0.0.1:8091/mcp",
"headers": {
"Authorization": "Bearer ${KENN_FORGE_API_TOKEN}"
}
}
}
}
Set the referenced environment variable from the token file, for example
export KENN_FORGE_API_TOKEN="$(cat ~/.kenn/forge/auth_token)", using the
token_path reported by kenn-forge daemon status --json.
For Tailscale Serve access, set explicit [mcp].enabled = true.
If Forge is published with Tailscale Serve and your Tailscale login is allowed
(see Federated Forge), point the client at the Serve origin
instead. No token is needed:
Connect from another machine or a container¶
The companion listener stays on loopback. A client elsewhere on your network,
such as an MCP gateway in a container, connects to /mcp on the main Forge
port with the daemon bearer token instead. Bind the main listener to an
address that client can reach, list the name it uses in allowed_hosts, keep
[mcp].enabled = true, and turn on API authentication:
host = "192.0.2.10"
allowed_hosts = ["forge.lan:8091"]
[api]
require_auth = true
[mcp]
enabled = true
Background start (kenn-forge daemon start or restart) only binds loopback,
so run this setup in the foreground with kenn-forge serve, for example under
a service manager.
Then point the client at http://forge.lan:8091/mcp with the
Authorization: Bearer ${KENN_FORGE_API_TOKEN} header shown above. The main
port requires the token for MCP whether or not [api].require_auth is set,
but that setting is what protects the rest of the API on the same port.
Without it, anyone who can reach the address can use the web UI and API
without a token. Plain HTTP sends the token in the clear, so use this only on
a network you trust, such as a tailnet or a private container network, or put
an HTTPS proxy in front.
Requests whose Host isn't in allowed_hosts or the bind address get 403, as
do requests with an Origin header other than the matching https:// origin;
server-side clients normally send no Origin.
Earlier previews exposed a standalone kenn-forge mcp command with a stdio
transport. That command and transport are removed without a compatibility
shim: MCP is served only by the running daemon over the HTTP endpoint above,
so update any client configuration that launched the old command.
Review workflow¶
Call kenn_forge_list_repos first to discover provider-aware repository filters,
stable repository keys, and sync freshness. Each repository has a
platform_repo_id, or a bitbucket_repository_uuid for Bitbucket Cloud. Copy
the returned key into every later repository or item reference; route-only
references are not accepted. Rediscover the current route after a repository rename. For review
triage:
- Call
kenn_forge_find_review_candidateswith the desired time window and item types. - Inspect likely items with
kenn_forge_get_item_context. - For pull requests, use
kenn_forge_get_item_diffin summary mode first. - Use
kenn_forge_get_stack_contextbefore claiming work on a stacked pull request. - Set local workflow state with
expected_statusso a stale agent does not overwrite another actor. Useforce: trueonly for a deliberate override.
kenn_forge_search_items finds quiet cached pull requests and issues that are
absent from recent activity. Candidate output defaults to 25 items and never
exceeds 100.
Diff files produced by kenn_forge_get_item_diff are temporary files on the
daemon host. Forge keeps the most recently requested files within the
[mcp].diff_cache_mb limit, which defaults to 128 MiB. Older files may be
deleted before daemon shutdown.
Coding-agent handoff¶
Call kenn_forge_list_agent_targets to discover configured coding agents,
including custom targets. Use the returned target key as agent_target; it
does not need to match the agent name reported by hooks. The launched agent
must report a supported hook session for the handoff to complete.
Call kenn_forge_spawn_workspace_with_agent with one source:
- a provider-aware pull request or issue; or
- a provider-aware repository with an optional ad-hoc branch.
The tool creates or reuses a workspace, launches a new agent runtime, submits
one initial message, and then waits for the resulting live hook session. It does
not clean up a workspace or runtime after a later failure. The response uses
stage and initial_message.state as the authoritative handoff evidence.
If prompt submission or hook observation times out after the response includes
a workspace ID and runtime session key, call the same tool with resume instead
of source. Pass those two IDs with the same agent_target and normalized
initial_message. Resume targets the existing runtime and does not launch a
second agent.
Use kenn_forge_list_workspace_agent_sessions to inspect live agent runtimes
and fresh coding sessions. A runtime with hook_observed=false has launched but
has not reported its first hook.
To send another instruction after the initial handoff, call
kenn_forge_send_agent_message with the persisted workspace ID, the live
runtime session key, and the message. The tool submits the prompt to that
running agent through the same runtime input path as the initial message. It
does not launch a runtime, resume a handoff, or wait for later agent activity.
Historical sessions and arbitrary terminal bytes are outside the MCP surface.
Build worklists¶
An agent can hand the maintainer a ranked worklist of synced pull requests and issues. None of these tools call provider APIs.
kenn_forge_put_worklistwrites a list with a title, a recipe, and ranked items. Each item can carry a score, section, note, and caveat. Every item must be a pull request or issue Forge has synced; copy its identity from a Forge read. Passworklist_idto replace an existing list, or omit it to create a new one.kenn_forge_get_worklistreads a list with its recipe and, for each item, whether it is fresh, changed, or gone since the list was written, what changed, and when the maintainer marked it handled.kenn_forge_list_worklistslists worklists, most recently updated first, with their item counts. Passinclude_dismissedto include dismissed lists.kenn_forge_dismiss_worklisthides a list. Writing the list again brings it back.kenn_forge_clear_worklist_marksclears every handled mark on a list. Call it only when the maintainer asks.kenn_forge_import_worklistcreates the list a worklist link carries, as opening the link in Forge does. It accepts akenn://link, an HTTP(S) link to/worklists/import#v1..., or the barev1...fragment.
To refresh a list, read its recipe, rerun the searches, and put the whole list again. Handled marks survive a rewrite for items that stay on the list.
Troubleshooting¶
If the endpoint is unavailable, check kenn-forge mcp quickstart, restart the
daemon, and inspect daemon status for the resolved listener. A separate MCP
port must be free at startup.
A 401 response means [api].require_auth is enabled and the bearer token is
missing or incorrect. A 403 response means the request did not arrive as a
direct same-origin loopback request. On the main port, 401 means the request
had neither the daemon bearer nor an allowed Tailscale Serve login, and 403
means its Host is not allowed or its Origin names another site.