MCP reference
LogiKFlow's MCP server lets an agent read a pull request and save its reading order.
| Endpoint | https://logikflow.dev/mcp |
| Transport | Streamable HTTP (stateless, JSON responses) |
| Authorization | OAuth 2.1 with PKCE; scope reviews |
| Server name | logikflow |
Tools
get_ordering_guide
Returns the ordering guide as Markdown. Call it once before saving an order. It takes no input and is read-only.
get_pull_request
Returns a pull request's changed files with reading hints, the saved order and its version, whether you may change the order, and the review link. Read-only.
| Input | Type | Required | Description |
|---|---|---|---|
owner | string | yes | Repository owner, for example acme |
repo | string | yes | Repository name, for example app |
number | integer | yes | Pull request number |
include_patches | boolean | no | Include each file's diff. Default false. Each file is capped at 20,000 characters and the response at 400,000. |
Result (JSON text):
{
"reviewUrl": "https://logikflow.dev/r/acme/app/pull/123",
"repository": "acme/app",
"pullRequest": {
"number": 123, "title": "Add team invites", "state": "open",
"author": "octo", "base": "main", "head": "team-invites", "headSha": "9fce14f", "url": "https://github.com/acme/app/pull/123"
},
"canArrange": true,
"filesTruncatedByGitHub": false,
"files": [
{ "path": "models/invite.ts", "status": "added", "additions": 20, "deletions": 0 },
{ "path": "services/invite-service.ts", "status": "added", "additions": 112, "deletions": 0, "dependsOn": ["models/invite.ts"] },
{ "path": "services/invite-service.test.ts", "status": "added", "additions": 60, "deletions": 0, "dependsOn": ["services/invite-service.ts"], "testOf": "services/invite-service.ts" },
{ "path": "package-lock.json", "status": "modified", "additions": 4, "deletions": 4, "likelyGenerated": true },
{
"path": "app/router.ts", "status": "modified", "additions": 9, "deletions": 1, "dependsOn": ["services/invite-service.ts"],
"changes": [
{ "id": "h1pdwiwd", "header": "@@ -12,6 +12,7 @@ import", "preview": "+ import { invites } from \"./invites\";", "additions": 1, "deletions": 0 },
{ "id": "hyzm99a", "header": "@@ -40,4 +41,12 @@ export const routes", "preview": "+ { path: \"/invites\", view: invites },", "additions": 8, "deletions": 1 }
]
}
],
"currentOrder": null,
"baseVersion": 0
}currentDiagram, when a diagram exists, has version, updatedBy, savedBeforeLatestCommits and diagram (the same shape save_diagram takes). diagramBaseVersion is its version, or 0. diagramPreference is always, ask or never, from the repository's conventions file (ask when it doesn't say).
currentOrder, when an order exists, has version, updatedBy, source (web or agent), savedBeforeLatestCommits (true when commits were pushed after it was saved), overview (when one was written), sections in the same shape save_reading_order takes, and suggestions: what would make the current order easier to follow (see Suggestions). status is GitHub's: added, removed, modified, renamed, copied, changed or unchanged. Renamed files also have previousPath. Files with two or more changes (diff hunks) have changes, one entry per change: id (use it in hunks to split the file), header (the @@ line), preview (the first changed line, up to 120 characters), additions and deletions. IDs come from the change's content, so they survive line shifts from other changes.
Reading hints appear on a file only when LogiKFlow could work them out: dependsOn (changed files it imports, from import lines visible in the diff), testOf (the changed file this test covers, from naming conventions) and likelyGenerated (lockfiles and minified, vendored or generated output). See Reading hints.
save_reading_order
Saves the reading order, replacing the current one. Earlier versions stay in history. Requires write access to the repository.
| Input | Type | Required | Description |
|---|---|---|---|
owner | string | yes | Repository owner |
repo | string | yes | Repository name |
number | integer | yes | Pull request number |
base_version | integer | yes | baseVersion from get_pull_request; 0 when no order exists |
overview | string | no | Up to 2,000 characters: two or three sentences reviewers read before the first section. Backticks render as code. |
sections | array | yes | 1 to 50 sections, in reading order |
Each section:
| Field | Type | Description |
|---|---|---|
title | string, optional | Up to 200 characters. Omit only for a single untitled group in a tiny pull request. |
layer | string, optional | contracts, models, logic, storage, app, ui, wiring, resources or tooling. Shown next to the title. |
note | string, optional | Up to 4,000 characters. Backticks render as code. |
checks | string[], optional | Up to 20 checklist items, each up to 300 characters |
files | array | Files in reading order: { "path", "note"?, "checks"?, "focus"?, "hunks"? } with the same limits. focus is key, skim or generated. hunks is a list of change IDs; see below. |
Splitting a file into parts
To show a file's changes in different sections, list the file more than once, each time with hunks naming the change IDs for that part:
- Each change ID may appear in only one part of the file.
- At most one appearance may leave out
hunks. It takes every change not named elsewhere, including changes from later commits. - If every appearance has
hunks, every change of the file must be named. - Files that GitHub gives no diff for (binary or very large) can't be split.
{ "title": "Routes", "files": [{ "path": "app/router.ts", "hunks": ["hyzm99a"], "note": "Part 2: the `/invites` route." }] }Example input:
{
"owner": "acme", "repo": "app", "number": 123, "base_version": 0,
"overview": "Adds team invites behind the `invitesEnabled` flag. Start with the flag and migration, then the model and service, then the dialog that calls it. The replace-on-reinvite rule in `InviteService` is the part to read slowly.",
"sections": [
{
"title": "Flag and schema",
"layer": "contracts",
"note": "Everything new is behind `invitesEnabled`, off by default.",
"checks": ["With the flag off, the members page is unchanged"],
"files": [{ "path": "lib/flags.ts" }, { "path": "db/migrations/0003_invites.sql" }]
},
{
"title": "Invite model and service",
"layer": "logic",
"note": "Builds on the schema above. `InviteService` owns the lifecycle; the model is a plain record.",
"files": [
{ "path": "models/invite.ts" },
{ "path": "services/invite-service.ts", "note": "Re-inviting the same email replaces the open invite.", "focus": "key" }
]
}
]
}Result:
{ "saved": true, "version": 1, "reviewUrl": "https://logikflow.dev/r/acme/app/pull/123", "overview": true, "sections": 2, "files": 4, "checklistItems": 1, "suggestions": [], "nextStep": "Done. Share the review link with the user. The pull request is small, so skip the diagram unless the user asks for one." }suggestions lists what would make the order easier to follow, in plain sentences the agent can act on; it is empty when the order follows the ordering guide. The save already happened, so the agent applies what fits and saves again with the new version. nextStep says so when there are suggestions, then tells the agent what to do about a diagram: nothing, ask the user, or draw one because the repository wants one for every pull request.
A checklist item with the same text in the same section or file keeps its identity across saves, so reviewers' ticks survive a re-arrange.
save_diagram
Saves an optional flow diagram, shown in the pull request's Diagram tab. It replaces the current diagram and doesn't change the reading order. Requires write access to the repository. See Diagrams in the ordering guide for when and how to draw one.
| Input | Type | Required | Description |
|---|---|---|---|
owner, repo, number | yes | As above | |
base_version | integer | yes | diagramBaseVersion from get_pull_request; 0 when there is no diagram |
diagram | object | yes | { "title"?, "groups"?, "nodes", "edges" } |
| Field | Type | Description |
|---|---|---|
title | string, optional | Up to 200 characters |
groups | array, optional | Up to 12 boxes: { "id", "title", "note"? }. Titles up to 80 characters. |
nodes | array | 2 to 40: { "id", "title", "kind"?, "group"?, "lines"?, "file"?, "hunk"? }. kind is trigger, logic (default), decision, data, ui, test, config, external or warning. lines is up to 4 { "label", "text" } rows. file must be a changed file; hunk a change id of that file. |
edges | array | Up to 80: { "from", "to", "label"?, "dashed"? }, between node ids. Labels up to 40 characters. |
Ids are 1 to 40 letters, digits, - or _, unique across groups and nodes.
{
"owner": "acme", "repo": "app", "number": 123, "base_version": 0,
"diagram": {
"title": "How an invite is accepted",
"groups": [{ "id": "server", "title": "Server" }, { "id": "tests", "title": "Coverage" }],
"nodes": [
{ "id": "link", "title": "Invite link opened", "kind": "trigger", "lines": [{ "label": "example", "text": "/invite/abc123" }] },
{ "id": "accept", "title": "Accept invite", "kind": "logic", "group": "server", "file": "services/invite-service.ts", "lines": [{ "label": "change", "text": "closes the invite after joining" }] },
{ "id": "expired", "title": "Reject expired", "kind": "warning", "group": "server", "file": "services/invite-service.ts" },
{ "id": "spec", "title": "Invite service spec", "kind": "test", "group": "tests", "file": "services/invite-service.test.ts" }
],
"edges": [
{ "from": "link", "to": "accept", "label": "token" },
{ "from": "accept", "to": "expired", "label": "if expired" },
{ "from": "accept", "to": "spec", "label": "covered by", "dashed": true }
]
}
}Result:
{ "saved": true, "version": 1, "diagramUrl": "https://logikflow.dev/r/acme/app/pull/123#diagram", "nodes": 4, "edges": 3, "groups": 2, "linkedFiles": 2 }Errors
Tool errors come back as a result with isError: true and a message the agent can act on. Nothing is saved when a save fails.
| Situation | Message starts with |
|---|---|
| A changed file is missing, unknown or listed twice | The order must list every changed file |
| A split names an unknown change, leaves changes unplaced, or splits a file without a diff | The order must list every changed file, and place every change of a split file exactly once |
| Someone saved a newer version | Conflict: … saved version N |
| No write access | … doesn't have write access |
| Invalid input, such as an empty checklist item | The order is invalid |
| A diagram node points at a file or change not in the pull request | The diagram points at files or changes that aren't in the pull request |
| Invalid diagram, such as an edge to an unknown node | The diagram is invalid |
| Someone saved a newer diagram | Conflict: … saved diagram version N |
| Pull request not visible | GitHub says this pull request doesn't exist or this account can't see it |
| GitHub token expired or revoked | GitHub access for this connection has expired |
| GitHub rate limit | GitHub's rate limit was reached |
Authorization
LogiKFlow follows the MCP authorization specification.
| Endpoint | Path |
|---|---|
| Protected resource metadata | /.well-known/oauth-protected-resource/mcp |
| Authorization server metadata | /.well-known/oauth-authorization-server |
| Authorization (consent page) | /authorize |
| Token | /oauth/token |
| Dynamic client registration | /oauth/register |
Client ID metadata documents are also accepted. Unauthenticated requests to /mcp get 401 with a WWW-Authenticate challenge.
- Access tokens last up to one hour and are refreshed automatically.
- Refresh tokens last 30 days from last use and at most 180 days.
- Rate limit: 120 MCP requests per minute per person.