Skip to main content

kbforge-mcp

kbforge-mcp lets an MCP server be a kbforge source. Install it alongside kbforge and a source becomes a block of YAML instead of a Python package: name a select tool that says which documents are worth reading and a read tool that fetches one verbatim by id, and the connector does the rest — it registers itself under the kbforge.connectors entry-point group, so kbforge list shows mcp with no further wiring. What it fetches goes through the same pipeline as any other connector: normalize, mirror, diff, and a reviewed proposal. It is a retriever: it hands kbforge the source's own bytes and never its own summary of them, which is what keeps a concept's provenance followable back to the document it came from.

Configure a source

The AWS Documentation server, which needs network but no credentials:

system: aws_docs                 # per-instance identity; prefixes every doc_id
transport:
  kind: stdio                    # explicit; never sniffed from the config shape
  command: uvx
  args: [awslabs.aws-documentation-mcp-server@latest]
  env: [AWS_DOCUMENTATION_PARTITION]   # names of variables to pass through
select:
  tool: search_documentation
  args: { search_phrase: S3 bucket naming rules, limit: 3 }
  ids: { list: search_results, id: url, title: title }
read:
  tool: read_documentation
  id_arg: url                    # the argument name the reader takes the id under

kbforge takes connector config as repeated YAML-typed --set pairs, so that is one key per flag:

kbforge run --connector mcp \
  --set system=aws_docs \
  --set 'transport={kind: stdio, command: uvx, args: [awslabs.aws-documentation-mcp-server@latest]}' \
  --set 'select={tool: search_documentation, args: {search_phrase: S3 bucket naming rules, limit: 3}, ids: {list: search_results, id: url, title: title}}' \
  --set 'read={tool: read_documentation, id_arg: url}' \
  --mirror .kbforge/mirror --out .kbforge/out --state .kbforge/state

An HTTP server differs only where it must — transport: {kind: http, url: ..., auth_env: GITHUB_TOKEN}. auth_env and transport.env hold environment variable names; a credential never appears in config or on a command line. read also takes static_args for constants the reader needs alongside the id (an owner and repo, say), and a source whose select tool cannot be mapped is configured with an explicit static_ids list instead. A run checks all of this before it opens a session, so a misconfigured source fails offline rather than mid-fetch.

The two-tool guarantee

The set of tools this connector can call is exactly the two you configured. Not a default allowlist, not a filtered discovery loop — there is no code path that calls a third tool. Point it at a write-capable server and the write tools are unreachable, because nothing enumerates them.

This is structural because it cannot be anything else: MCP tells a client a tool's name and schema, never whether it has side effects, so search and delete_all are indistinguishable. Two weaker layers sit on top. A tool whose read_only_hint annotation is explicitly false is refused (an unset hint is permitted — most servers never set one). And where a server publishes a read-only endpoint, config should prefer it; that is the only layer enforced outside kbforge's own process.

What none of the three can do is tell you whether the tool you named as the reader is side-effect-free. Naming a mutating tool there is a deployment error kbforge cannot detect.

A limit worth knowing before you adopt it

Response mapping is protocol-first: ids come from MCP's own resource links or from structuredContent, and a select response that is neither is refused rather than guessed at. That refusal is a real constraint, not a corner case. A server can be perfectly machine-readable and still be unusable as a selector here. GitHub's search_code returns machine-readable JSON inside a text block and declares no structuredContent; kbforge-mcp refuses it, and kbforge's own live test against GitHub supplies a static_ids list instead of using its search tool at all.

So "a new source is configuration" is unqualified for the reader, and conditional for the selector: it holds when the server publishes its result ids as resource links or as structuredContent, and otherwise you enumerate the corpus by hand in static_ids. Check that before you plan around this connector. The option that would close the gap — an opt-in flag to parse a text block as JSON — is not built (design note §10.3).

When reads fail

A read that fails — the tool reports an error, or its response cannot be mapped — skips that one document and marks the fetch incomplete; the rest of the run proceeds, and a warning on stderr names each failed id. A run whose reads all fail stops with ReadsFailed and a non-zero exit instead: zero documents would otherwise reach the pipeline as "no change detected", and a scheduled job whose key was revoked would never alert. A selection that is simply empty — a search that found nothing — is still a quiet no-op.

One page, two selectors

A document's title comes from its selector by default: the search hit's title, or the id itself for static_ids. So a page reached both ways — a curated list and a search over the same site, under one system — takes whichever title the last run supplied, and shows as modified every time the two alternate. A reader that returns structuredContent can own the title instead, so it no longer depends on who selected the page:

read: {tool: read, id_arg: url, text_key: markdown, title_key: title}

title_key needs text_key (it reads the same structured response); a missing or blank title falls back to the selector's.

What a source's own framing does to a concept

This connector is a retriever: it hands kbforge the source's bytes and does not tidy them. So a source's own framing survives into the rendered concept, and you will see it in the first bundle you produce. Two instances are known — the AWS documentation server prefixes every document with AWS Documentation from <url>:, and a whole markdown document brings its own # heading, which ends up below the # {title} synthesis renders, giving you two headings. Neither is something this connector will fix; a connector that edited source bytes would no longer be a retriever, and provenance is what that buys. The fix, if you want one, belongs in synthesis — see docs/architecture.md §4.1.

Design

The design note holds what remains deferred, including deletion support: this connector re-selects every run and lets the mirror diff do the work, and it emits no tombstones, so a document removed at the source leaves a stale concept until the deletion manifest lands. The shipped design — the selector/reader split, the protocol-first mapping, the read-only posture — is in docs/architecture.md §4.1.

Release files for kbforge-mcp 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for kbforge-mcp 0.2.0
File Size Uploaded
kbforge_mcp-0.2.0.tar.gz 47.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for kbforge-mcp 0.2.0
File Interpreter ABI Platform
kbforge_mcp-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 75.0 kB

Release files / kbforge_mcp-0.2.0.tar.gz

Download URL kbforge_mcp-0.2.0.tar.gz
Size 47.4 kB
Tags Source
SHA-256 checksum
How to use checksums
92cbff31c80ec0453261d1149896e623e27a57890e3a967d42fb261746252644
BLAKE2b-256 checksum
How to use checksums
4afb60648dddc0ba73228b0a9f8c1296cdcb969981fb870b2e076bcad0fad53c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.

Transparency log

Release files / kbforge_mcp-0.2.0-py3-none-any.whl

Download URL kbforge_mcp-0.2.0-py3-none-any.whl
Size 27.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4feec51621fd1db1544525602af58da3271b4bb886a0e0bd03bc718d89956f62
BLAKE2b-256 checksum
How to use checksums
a316959d2f47e04c07553efb2f93ce417358db89debde48dfe2b7296473d2e31
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page