~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/knotless

git that lives in your Atmosphere account

knotless.dev
1 collaborator

knotless 0.13.0 Latest 2 packages, 1 image

1
10

Pulls, issues, diffs and files are MCP resources

Merged
Merged chadtmiller.com asked to merge into main from chadtmiller.com/knotless:mcp/resources

What changes

Resources. knotless-mcp over stdio and /mcp on a server offer six resource templates, so a client that reads MCP resources (omp's read mcp://knotless://…, Claude Code's @ mentions) opens a pull, an issue, a pull's diff or a file by URI, as read pr://N does for GitHub. The URIs mirror knotless.dev's pages under knotless://:

  • knotless://{owner}/{repo}/pulls and /issues: as pull_list and issue_list with no query
  • knotless://{owner}/{repo}/pull/{did}/{rkey}, /issue/{did}/{rkey}: as pull_show and issue_show of at://{did}/dev.knotless.pull/{rkey} (or .issue)
  • knotless://{owner}/{repo}/pull/{did}/{rkey}/diff: as pull_diff, or its said/files summary when too long
  • knotless://{owner}/{repo}/file/{rev}/{+path}: as repo_read at rev; a binary file as a base64 blob

JSON answers are application/json, diffs and files text/plain. A part is percent-decoded as a template's expansion escapes it (topic%2Fmore, did%3Aplc%3A…). resources/list lists none. A URI no template makes is a resource_not_found error naming the templates; a read that fails is an error with the tool's message (failed, with the sign-in-again hint); a server older than the appview asks for refuses reads as it refuses tools. --read-only serves them all.

Docs. docs/reference/mcp.md has a Resources section; docs/guide/agents.md says resources can be opened by URI, and that omp and Claude Code can install knotless-mcp with a knotless skill as a plugin from https://knotless.dev/chadtmiller.com/agent-plugins.

Shared bodies. repo_read and pull_diff move into Server::file and Server::diff beside the existing items and show; each tool and its resource call the same function. The server's get_info is written out (tools and resources enabled, the same name and instructions, which now say resources exist).

Remote. /mcp forwards the resource methods; a read is made as the calling account, takes a turn in the same per-account limits as a tool call, and is logged as mcp resource / mcp resource failed and counted as the mcp.resource event, named for the tool that answers it. The logging and counting a call gets is one function for both. The lexicons' known values, docs/observability.md and deploy/README.md name them.

Why

Agents read pulls, diffs and files as resources elsewhere (pr://N); with knotless they had to make tool calls. Answering each resource with its tool's own function keeps the two the same, and URIs like the pages' are ones a user can write from a page they have open.

How it was checked

  • agents_read_resources (tests/mcp.rs): the templates listed, resources listed empty, each template read against its tool for the same subject (a pull with a comment, its diff, an issue, both lists, a file on a / branch with a space in its path and the owner as an escaped DID), a binary file as a blob, a failing read with the tool's message, and malformed URIs as resource_not_found naming the templates.
  • an_old_version_is_refused_and_a_new_one_told_of: a refused server refuses a read.
  • tests/http.rs: the capability in initialize, templates over /mcp, a read as the caller counted as mcp.resource repo_read true <did>, an unknown URI counted as failed, and a read past the minute's limit told to wait.
  • cargo clippy -p knotless-mcp --tests here; the rest is left to CI.

Shortcomings

  • No completions for template variables, and no subscriptions.
  • A resource is read whole: a file's start/end and a diff's paths are the tools'.
  • A failed read is an internal_error with the tool's message whatever the cause, as the tool cannot tell a missing pull from a failing PDS.

Review

Start at crates/knotless-mcp/src/resources.rs, then Remote::read_resource in http.rs.

Approved

This adds six MCP resource templates (pulls, issues, a pull, an issue, a pull's diff, and a file) to knotless-mcp over stdio and to /mcp. repo_read and pull_diff move into Server::file and Server::diff, so each tool and its resource share one function. I read every file in the diff and found nothing that blocks the merge.

  • URI parsing: parse splits on / before percent-decoding. A %2F in a branch name or a DID therefore stays inside its own part, and a malformed or empty part becomes resource_not_found. The caller's own account is what makes the read, so a decoded part can't reach anything the caller couldn't already read.
  • Remote reads: /mcp reads go through the same turn limits and logging as tool calls. Both paths now share one told function, so the tool logging and counts are unchanged.
  • Docs and metadata: the changeset, docs, lexicon known values and the tests for the new paths are all in the pull.

I did not run the checks. The body says CI is left to run them.

One small thing, not worth holding the pull for: an unknown URI is counted as mcp.resource with an empty subject. The test pins this (mcp.resource false), so it is deliberate. A placeholder such as unknown would read better in the operator's usage counts.

Approved

The only change since my approval is in tests/mcp.rs. The wait_until in agents_read_resources now waits for the pull list to show the comment (pulls[0]["comments"] == 1), which makes the test less likely to flake. Nothing I approved is affected.

Merged as 935592d