Search and read GitHub issues
Search GitHub issues and read whole threads back, without your AI ever holding the PAT.
Give your AI read access to GitHub issues through a pipe whose fine-grained PAT it never touches. Send a GitHub search query and get the matching issues back with their numbers, ids, titles, states, labels and assignees already on them. Send a list of issue ids instead and get those issues in full, with bodies, authors and recent comments. One pipe covers both because they are the same call to the same place with the same credential. Choose your scope. The github family is a ladder. File only: github-create-issue on its own, which runs on the free Deckhand plan and fits a developer logging bugs without leaving the code. Read only: github-search-issues on its own, which also runs on the free Deckhand plan and needs only a PAT scoped Issues (Read), and fits a developer checking their own assignments or a lead reading the queue. Read and file: both of those, which needs two pipe slots (Bosun). Full management: add github-update-issues for three pipe slots (First Mate) and a PAT scoped Issues (Read and write), for someone grooming a backlog. Climbing the ladder stacks pipes rather than swapping them, because each member does something the others cannot, so budget a slot per member you install.
The credential never enters the model context: it lives in the FlurryPORT secret store, deliveries are signed server-side, and every send returns a receipt your agent can quote.
What this is
Your AI reads GitHub issues through a pipe whose fine-grained PAT it never touches. It sends a search query, or a list of issue ids, and FlurryPORT adds the credential server-side and returns GitHub's answer as a receipt.One pipe does both jobs. Search gives you the shape of the queue: numbers, ids, titles, states, labels, assignees, all on the result already. Reading by id gives you the substance: bodies, authors, recent comments. They are the same call to the same endpoint with the same credential, so folding them costs you nothing and saves a pipe slot.
Setup in two steps
- GitHub side. Settings, Developer settings, Fine-grained tokens: generate a token for the repositories you want readable, with Issues (Read) and Metadata (Read). Nothing broader; reading needs nothing broader.
- FlurryPORT side. Your AI requests secret setup and FlurryPORT emails you a secure page. Paste the token there and the pipe goes live.
There is nothing to configure at install. Repository scope lives in the search query, so one pipe serves every repository your token can see.
How to drive it
Search first, then read what interests you.q: "repo:owner/name is:issue is:open label:bug"Every result carries an
id. Feed those ids back to read the issues in full:ids: ["I_kwDOAn8RLM8AAAABMW1TZA"]Issue numbers and node ids are different things. People quote numbers; this pipe reads by node id. Search returns both, which is why searching first is the fast path rather than a detour.
What to expect
Results land on the replay execution, not on the capture. Read them withget_replay_execution and they will normally be there within a second or two, so check once and retry briefly rather than sleeping.On plans without full response bodies the stored response is capped at 4096 bytes. The default page size of 10 is chosen to fit inside that cap against real repositories with long titles. If you raise it, check
ResponseBodyTruncated before parsing, because a truncated JSON body will not parse at all.Credential custody
The PAT is stored encrypted, added at delivery, and scrubbed from stored responses. Scoping it to read means even a fully compromised pipe could not change anything in your repositories.Pairs with create a GitHub issue for filing and update GitHub issues for grooming. The three together give an AI the same working relationship with GitHub that the azure-devops family gives it with Azure Boards.
Install with your agent
npx flurryport mcp
Point your agent at the FlurryPORT MCP server (npx flurryport mcp) and ask it for the flurryport:github-search-issues recipe. Works from AI clients that can run a local process: desktop apps and terminal agents. Web-only chat clients cannot reach a local MCP server; open a desktop client instead.
Tools your agent gains
flry_github_search_issues- Search GitHub issues, or read specific issues in full. Input: q (a GitHub search query) with optional first, OR ids (issue node ids) to read those issues with bodies and recent comments. Results arrive on the replay execution the fire created, not inline: read them with get_replay_execution. Carry the id field from a search result into ids to read an issue in full.
Setup walkthrough
GITHUB_PAT: GitHub Settings, Developer settings, Fine-grained tokens: generate one for the repositories you want readable. This recipe on its own needs only Issues (Read) plus Metadata (Read); the same token needs Issues (Read and write) if it also serves github-create-issue or github-update-issues. Paste it in the FlurryPORT secret page. Fine-grained tokens work with the GraphQL API this pipe calls, proven live 2026-08-12.
Intent schema
{
"type": "object",
"properties": {
"q": {
"type": "string",
"description": "A GitHub issue search query, the same syntax the search box on github.com takes. Scope by repository in the query, not at install: repo:owner/name is:issue is:open assignee:@me returns the open issues assigned to whoever owns the PAT."
},
"first": {
"type": "integer",
"description": "How many issues to return. Defaults to 10, which is chosen to stay inside the 4096-byte stored response on plans without full response bodies. Raise it only when your plan stores full bodies."
},
"ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "Issue node ids to read in full, as returned in the id field of a search. These are GraphQL node ids like I_kwDOAn8RLM8AAAABMW1TZA, NOT the issue numbers people quote."
}
}
}Transformation
{ "body": $exists($body.ids) ? { "query": "query($ids:[ID!]!){ nodes(ids:$ids){ ... on Issue { number id title state url createdAt body author{ login } labels(first:20){ nodes{ name } } assignees(first:10){ nodes{ login } } comments(last:10){ totalCount nodes{ author{ login } body createdAt } } } } }", "variables": { "ids": $body.ids } } : { "query": "query($q:String!,$n:Int!){ search(query:$q, type:ISSUE, first:$n){ issueCount nodes{ ... on Issue { number id title state createdAt url labels(first:10){ nodes{ name } } assignees(first:5){ nodes{ login } } } } } }", "variables": { "q": $body.q, "n": $exists($body.first) ? $body.first : 10 } } }Delivery target
POST https://api.github.com/graphql
Placeholders like $secrets.NAME resolve server-side at delivery, never in the agent.
Gotchas
- Bind this pipe's transformation with the predicate $exists($body.q) or $exists($body.ids). When two or more github pipes share one capture endpoint, every binding must carry its routing predicate at bind time, and the family's predicates are mutually exclusive by design: github-create-issue routes on $exists($body.title), github-search-issues on $exists($body.q) or $exists($body.ids), github-update-issues on $exists($body.items). An unpredicated binding fires on every intent that reaches the endpoint.
- The transformation wraps the payload in an explicit body envelope, and that wrapper is load-bearing in a way that only bites sometimes. A GraphQL payload with variables has the root keys query and variables, which passes through fine. The same document with no variables has only the root key query, which is entirely inside the envelope word set, so FlurryPORT reads it as routing metadata and delivers a null body. The delivery then fails closed as the bare string transform failed with no HTTP status. Wrapping in body makes the pipe immune either way (proven live 2026-08-12).
- Results arrive on the replay execution the fire created, not on the capture. Read them with get_replay_execution.
- Repository scope is a query fact, not an install setting. There are no install-time parameters: repo:owner/name lives in your search string. That means this pipe can read PUBLIC issues in any repository GitHub search reaches, not only yours. Private repositories stay limited to what the fine-grained PAT was granted.
- GitHub speaks two kinds of issue identity and they are not interchangeable. Issue NUMBERS are what people quote (#42) and what the REST API takes. Node IDs are opaque strings like I_kwDOAn8RLM8AAAABMW1TZA and are what the ids input takes. Every search result carries both, so search first, then read by id.
- On plans without full response bodies the stored response is a 4096-byte preview, so a long result list is cut off mid-record and will not parse. The default page size of 10 is chosen to fit: a 10-issue search of a real repository measured 3273 bytes and stored whole, while the same search at 20 measured 6597 bytes and was truncated mid-URL on the 13th issue. Check ResponseBodyTruncated on the execution before parsing, and narrow with first or a tighter query rather than retrying.
- The Accept-Encoding identity header in the target template is load-bearing. GitHub does not compress unless asked, but the firing client's own headers ride through the capture to the destination, and MCP clients typically send Accept-Encoding: br, gzip, deflate. Without the identity override the pipe asks for compression on your behalf and the stored response preview becomes unreadable binary.
- GraphQL rate limiting is a points budget, not a request count: the response carries X-RateLimit-Limit 5000 and X-RateLimit-Resource graphql, and a heavy query costs more than one point. Read the remaining count off the execution's response headers rather than guessing.
- A 404 from GitHub usually means the PAT is not granted to that repository: GitHub hides existence rather than answering 403. On GraphQL the same condition arrives as a NOT_FOUND entry in the errors array with a 200 status.
- Safest test fire: a query scoped to one repository you own with a small page, such as q set to repo:owner/name is:issue is:open and first set to 3. It returns at most three issues and changes nothing.
Where this fits
Related recipes
Let your AI file GitHub issues with a fine-grained PAT it never touches.
Create and update Azure DevOps work items in one batched pipe: priority, state, tags, and sprint included.
Query Azure DevOps work items with WIQL and read the matching ids back, without your AI ever holding the PAT.
Read fields for a batch of Azure DevOps work items by id: title, state, tags, priority, iteration.
Let your AI file Azure DevOps work items (bugs, tasks) the model never holds credentials for.
Let your AI file Jira Cloud issues with an API token it never touches.