From ee364df7b594cc89daa176f74333b1a792ecf588 Mon Sep 17 00:00:00 2001 From: francesco Date: Thu, 15 Oct 2026 15:24:31 +0200 Subject: [PATCH 1/4] docs(distributing): record awesome-hacktoberfest lists check Co-Authored-By: Claude Opus 5.5 --- DISTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DISTRIBUTING.md b/DISTRIBUTING.md index e761947..8bd596c 100644 --- a/DISTRIBUTING.md +++ b/DISTRIBUTING.md @@ -384,6 +384,7 @@ Al 14 ottobre 2026 queste ricerche non hanno trovato copertura dei competitor; s - OSCA (Open Source Community Africa): la lista awesome-open-source non ha merge da gennaio 2024; nessun processo di showcase. - EndoTheDev/Awesome-Ollama, rafska/awesome-local-llm: nessun merge recente, decine o centinaia di PR in coda. vince-lam/awesome-local-llms è generato da uno scraper. - firstcontributions (/contribute-to-opensource): ultima aggiunta gennaio 2026, bassa priorità. +- Liste awesome-hacktoberfest (verificate il 15 ottobre 2026): OtacilioN/awesome-hacktoberfest (862★) non ha merge da ottobre 2023 e ha 23 PR in coda; mattjegan/awesome-hacktoberfest (133★) non aggiunge progetti da ottobre 2024; Piyushhbhutoria/awesome-hacktoberfest-2026 (62★) ha fatto l'ultimo merge a novembre 2025, ha poco pubblico ed è l'unica che si potrebbe ancora tentare. Dato che Hacktoberfest 2026 non conta più le PR (§8.4), le etichette `hacktoberfest`, `hacktoberfest-accepted` e `spam` non servono. - Newsletter in spagnolo, indonesiano, vietnamita o hindi che accettino segnalazioni via issue GitHub: nessuna trovata. - Masakhane, Zindi, Deep Learning Indaba, Data Science Nigeria: comunità di ricerca ML o competizioni, fuori tema per un'app desktop. From b0939526224a7d23b6c910343e1e0e274e6a3c9d Mon Sep 17 00:00:00 2001 From: francesco Date: Thu, 15 Oct 2026 15:26:43 +0200 Subject: [PATCH 2/4] docs(distributing): awesome-hacktoberfest-2026 PR opened Co-Authored-By: Claude Opus 5.5 --- DISTRIBUTING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DISTRIBUTING.md b/DISTRIBUTING.md index 8bd596c..f68efeb 100644 --- a/DISTRIBUTING.md +++ b/DISTRIBUTING.md @@ -318,6 +318,7 @@ Il percorso seguente "ripercorre i passi" dei progetti studiati nell'ordine in c | V2EX, Juejin, LINUX DO | comunità cinesi | 13 ott 2026 | bloccate (invito, telefono, codice) | testi in `docs/launch/zh.md` | | [up-for-grabs #6258](https://github.com/up-for-grabs/up-for-grabs.net/pull/6258) | elenco di progetti per principianti | 13 ott 2026 | **merged** | nessuno | | [awesome-for-beginners #2143](https://github.com/MunGell/awesome-for-beginners/pull/2143) | lista per principianti (90k★, pubblico forte in India e Africa) | 14 ott 2026 | PR aperta (`data.json`, Python, `good first issue`) | attendere merge | +| [awesome-hacktoberfest-2026 #22](https://github.com/Piyushhbhutoria/awesome-hacktoberfest-2026/pull/22) | lista Hacktoberfest (62★), sezione Coding for Beginners | 15 ott 2026 | PR aperta; ultimo merge del maintainer a nov 2025 | attendere merge | | [ollama/ollama #19001](https://github.com/ollama/ollama/pull/19001) | README Community Integrations (183k★), sezione RAG & Knowledge Bases | 14 ott 2026 | PR aperta; 80+ PR README in coda, merge rari | attendere | | PyCoder's Weekly | newsletter Python (form Google) | 14 ott 2026 | modulo compilato, **invio bloccato** dal classificatore auto mode | inviare a mano con i testi di `emerging.md` | | TabNews, r/developersIndia, Hashnode, Viblo, Techpoint Africa, organizzatori Hack Day | comunità e stampa in Africa e paesi emergenti | 14 ott 2026 | testi pronti in `emerging.md` | pubblicare dal proprio account; r/developersIndia solo nel thread Showcase dell'8 nov | @@ -384,7 +385,7 @@ Al 14 ottobre 2026 queste ricerche non hanno trovato copertura dei competitor; s - OSCA (Open Source Community Africa): la lista awesome-open-source non ha merge da gennaio 2024; nessun processo di showcase. - EndoTheDev/Awesome-Ollama, rafska/awesome-local-llm: nessun merge recente, decine o centinaia di PR in coda. vince-lam/awesome-local-llms è generato da uno scraper. - firstcontributions (/contribute-to-opensource): ultima aggiunta gennaio 2026, bassa priorità. -- Liste awesome-hacktoberfest (verificate il 15 ottobre 2026): OtacilioN/awesome-hacktoberfest (862★) non ha merge da ottobre 2023 e ha 23 PR in coda; mattjegan/awesome-hacktoberfest (133★) non aggiunge progetti da ottobre 2024; Piyushhbhutoria/awesome-hacktoberfest-2026 (62★) ha fatto l'ultimo merge a novembre 2025, ha poco pubblico ed è l'unica che si potrebbe ancora tentare. Dato che Hacktoberfest 2026 non conta più le PR (§8.4), le etichette `hacktoberfest`, `hacktoberfest-accepted` e `spam` non servono. +- Liste awesome-hacktoberfest (verificate il 15 ottobre 2026): OtacilioN/awesome-hacktoberfest (862★) non ha merge da ottobre 2023 e ha 23 PR in coda; mattjegan/awesome-hacktoberfest (133★) non aggiunge progetti da ottobre 2024; Piyushhbhutoria/awesome-hacktoberfest-2026 (62★) ha fatto l'ultimo merge a novembre 2025, ha poco pubblico ed è l'unica tentata (PR #22, §8.1). Dato che Hacktoberfest 2026 non conta più le PR (§8.4), le etichette `hacktoberfest`, `hacktoberfest-accepted` e `spam` non servono. - Newsletter in spagnolo, indonesiano, vietnamita o hindi che accettino segnalazioni via issue GitHub: nessuna trovata. - Masakhane, Zindi, Deep Learning Indaba, Data Science Nigeria: comunità di ricerca ML o competizioni, fuori tema per un'app desktop. From d48a0fe171482fe6ad45289fcf2e1fceb6f49ce7 Mon Sep 17 00:00:00 2001 From: francesco Date: Fri, 16 Oct 2026 09:49:19 +0200 Subject: [PATCH 3/4] docs(distributing): awesome-context-engineering PR opened Co-Authored-By: Claude Opus 5.5 --- DISTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DISTRIBUTING.md b/DISTRIBUTING.md index f68efeb..0b78ef7 100644 --- a/DISTRIBUTING.md +++ b/DISTRIBUTING.md @@ -320,6 +320,7 @@ Il percorso seguente "ripercorre i passi" dei progetti studiati nell'ordine in c | [awesome-for-beginners #2143](https://github.com/MunGell/awesome-for-beginners/pull/2143) | lista per principianti (90k★, pubblico forte in India e Africa) | 14 ott 2026 | PR aperta (`data.json`, Python, `good first issue`) | attendere merge | | [awesome-hacktoberfest-2026 #22](https://github.com/Piyushhbhutoria/awesome-hacktoberfest-2026/pull/22) | lista Hacktoberfest (62★), sezione Coding for Beginners | 15 ott 2026 | PR aperta; ultimo merge del maintainer a nov 2025 | attendere merge | | [ollama/ollama #19001](https://github.com/ollama/ollama/pull/19001) | README Community Integrations (183k★), sezione RAG & Knowledge Bases | 14 ott 2026 | PR aperta; 80+ PR README in coda, merge rari | attendere | +| [awesome-context-engineering #88](https://github.com/yzfly/awesome-context-engineering/pull/88) | lista awesome (211★, molto attiva, merge quasi quotidiani), sezione Memory & Compression accanto a LWC e Dory | 16 ott 2026 | PR aperta (EN + CN). La #69 per openapi-llm-wiki era stata chiusa come "vendor-specific catalog": il maintainer vuole metodi o strumenti riutilizzabili, descritti per meccanismo; ringraziato sulla #69 con link alla #88 | attendere merge | | PyCoder's Weekly | newsletter Python (form Google) | 14 ott 2026 | modulo compilato, **invio bloccato** dal classificatore auto mode | inviare a mano con i testi di `emerging.md` | | TabNews, r/developersIndia, Hashnode, Viblo, Techpoint Africa, organizzatori Hack Day | comunità e stampa in Africa e paesi emergenti | 14 ott 2026 | testi pronti in `emerging.md` | pubblicare dal proprio account; r/developersIndia solo nel thread Showcase dell'8 nov | From 9eeeb4f69e95d3769741cab018fa1b875384516d Mon Sep 17 00:00:00 2001 From: francesco Date: Fri, 16 Oct 2026 10:15:03 +0200 Subject: [PATCH 4/4] feat(mcp): package gknowledge-mcp for PyPI and the MCP registry - build-aux/pypi: a gknowledge-mcp distribution with the headless core and the MCP server only (no GTK), with its own PyPI README carrying the mcp-name marker; built by build-aux/pypi/build.sh or make pypi - server.json for the official MCP registry (io.github.openapi/gknowledge) and glama.json to claim the Glama listing - .github/workflows/mcp.yml publishes both on version tags or by hand, via PyPI trusted publishing and GitHub OIDC - gknowledge-mcp --init creates the project folder, so the server works from uvx without the desktop app Refs #22 Co-Authored-By: Claude Opus 5.5 --- .github/workflows/mcp.yml | 37 ++++++++++++++++++++++++++ Makefile | 6 ++++- build-aux/debian/gknowledge-mcp.1 | 8 ++++++ build-aux/pypi/README.md | 44 +++++++++++++++++++++++++++++++ build-aux/pypi/build.sh | 21 +++++++++++++++ build-aux/pypi/pyproject.toml | 43 ++++++++++++++++++++++++++++++ gknowledge/mcp.py | 8 +++++- glama.json | 4 +++ server.json | 37 ++++++++++++++++++++++++++ tests/test_mcp.py | 23 +++++++++++++++- 10 files changed, 228 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/mcp.yml create mode 100644 build-aux/pypi/README.md create mode 100755 build-aux/pypi/build.sh create mode 100644 build-aux/pypi/pyproject.toml create mode 100644 glama.json create mode 100644 server.json diff --git a/.github/workflows/mcp.yml b/.github/workflows/mcp.yml new file mode 100644 index 0000000..3299924 --- /dev/null +++ b/.github/workflows/mcp.yml @@ -0,0 +1,37 @@ +name: MCP package +# Publishes gknowledge-mcp to PyPI (trusted publishing, environment "pypi") +# and server.json to the official MCP registry (GitHub OIDC). +on: + push: + tags: ["v*"] + workflow_dispatch: +jobs: + publish: + runs-on: ubuntu-latest + environment: pypi + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - run: pip install build + - run: build-aux/pypi/build.sh dist/pypi + - uses: pypa/gh-action-pypi-publish@release/v1 + with: + packages-dir: dist/pypi + skip-existing: true + # server.json always follows the version in gknowledge/__init__.py. + - run: | + version=$(python3 -c 'import re; print(re.search(r"__version__ = \"(.+)\"", open("gknowledge/__init__.py").read())[1])') + jq --arg v "$version" '.version = $v | .packages[0].version = $v' server.json > server.tmp + mv server.tmp server.json + - run: | + curl -fsSL https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_linux_amd64.tar.gz | tar xz mcp-publisher + ./mcp-publisher login github-oidc + # The registry checks the README on PyPI, which can take a minute to appear. + - run: | + for i in 1 2 3 4 5; do ./mcp-publisher publish && exit 0; sleep 30; done + exit 1 diff --git a/Makefile b/Makefile index c9b87b1..788884c 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ METAINFODIR := $(DATADIR)/metainfo INSTALL := install -.PHONY: all help check test run dev start demo-svg install uninstall clean dist flatpak +.PHONY: all help check test run dev start demo-svg install uninstall clean dist pypi flatpak all: help @@ -31,6 +31,7 @@ help: @echo " make install Install locally into $(PREFIX)" @echo " make uninstall Remove a local install" @echo " make dist Build a wheel/sdist with the Python build backend" + @echo " make pypi Build the gknowledge-mcp package for PyPI into dist/pypi" @echo " make flatpak Build a Flatpak (needs flatpak-builder)" @echo " make clean Remove build artifacts" @@ -105,6 +106,9 @@ uninstall: dist: $(PYTHON) -m build +pypi: + build-aux/pypi/build.sh dist/pypi + flatpak: flatpak-builder --user --install --force-clean build-aux/flatpak-build \ build-aux/flatpak/$(APP_ID).yaml diff --git a/build-aux/debian/gknowledge-mcp.1 b/build-aux/debian/gknowledge-mcp.1 index 79d0758..b46acff 100644 --- a/build-aux/debian/gknowledge-mcp.1 +++ b/build-aux/debian/gknowledge-mcp.1 @@ -3,6 +3,7 @@ gknowledge-mcp \- Model Context Protocol server for a gknowledge wiki .SH SYNOPSIS .B gknowledge-mcp +.RB [ \-\-init ] .I PROJECT .SH DESCRIPTION .B gknowledge-mcp @@ -12,6 +13,13 @@ output and offers the tools search_wiki, read_page, list_pages, ask, add_source and recent_activity. .PP It is normally started by the MCP client, not by hand. +.SH OPTIONS +.TP +.B \-\-init +Create +.I PROJECT +if it is not a gknowledge project yet. Without it, the server exits +when the folder is not a project. .SH ARGUMENTS .TP .I PROJECT diff --git a/build-aux/pypi/README.md b/build-aux/pypi/README.md new file mode 100644 index 0000000..5499855 --- /dev/null +++ b/build-aux/pypi/README.md @@ -0,0 +1,44 @@ +# gknowledge-mcp + + + +**An MCP server for [gknowledge](https://github.com/openapi/gknowledge) wikis.** Claude Code, Codex, Cursor and any other Model Context Protocol client can search, read and grow a linked Markdown wiki that an LLM keeps up to date, following [Karpathy's LLM Wiki pattern](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f). + +Sources are compiled into the wiki once. Agents then read the wiki instead of re-reading raw documents on every question. + +- **Plain files.** Markdown with `[[wikilinks]]` and YAML frontmatter, an `index.md` catalogue and an append-only `log.md`. The folder opens in Obsidian or any editor. +- **Cited and incremental.** Each new source updates only the pages it touches. Every page records its sources, and answers cite pages. +- **Offline by default.** A built-in heuristic engine ingests and answers with no model and no API key. For better synthesis, set `provider`, `base_url` and `model` in the project's `settings/settings.json` (or from the desktop app) to use Ollama or any OpenAI-compatible endpoint. +- **No dependencies.** Standard library only; `pip install "gknowledge-mcp[pdf]"` adds PDF import. + +## Install and register + +```bash +claude mcp add gknowledge -- uvx gknowledge-mcp --init ~/Knowledge/my-project # Claude Code +codex mcp add gknowledge -- uvx gknowledge-mcp --init ~/Knowledge/my-project # Codex CLI +``` + +For Cursor or other clients: + +```json +{ "mcpServers": { "gknowledge": { "command": "uvx", "args": ["gknowledge-mcp", "--init", "/home/you/Knowledge/my-project"] } } } +``` + +`--init` creates the project folder the first time. Without it, the server only opens an existing project. You can also `pipx install gknowledge-mcp` and use `gknowledge-mcp` as the command. + +## Tools + +| Tool | What the agent can do | +|---|---| +| `search_wiki` | find pages by keyword | +| `read_page` | read a page with its sources and backlinks | +| `list_pages` | list pages by type | +| `ask` | get a cited answer, optionally saved back into the wiki | +| `add_source` | import and ingest a file | +| `recent_activity` | see what changed recently | + +## The desktop app + +This package is the headless part of gknowledge. The full app is a native GNOME application (GTK 4 and Libadwaita) with sources, wiki, assistant and a review queue in one window, and it opens the same project folders. Install it from the [signed APT repository or Flatpak](https://openapi.github.io/gknowledge). + +MIT licensed. Issues and contributions: [github.com/openapi/gknowledge](https://github.com/openapi/gknowledge). diff --git a/build-aux/pypi/build.sh b/build-aux/pypi/build.sh new file mode 100755 index 0000000..ef95126 --- /dev/null +++ b/build-aux/pypi/build.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# Build the gknowledge-mcp wheel and sdist for PyPI. +# +# build-aux/pypi/build.sh [outdir] (default: dist/pypi) +# +# Only the headless core and the MCP server are packaged: the GTK interface +# (app.py, __main__.py, ui/) stays out, so the package installs anywhere. +set -euo pipefail + +root=$(cd "$(dirname "$0")/../.." && pwd) +out=$(realpath -m "${1:-$root/dist/pypi}") +stage=$(mktemp -d) +trap 'rm -rf "$stage"' EXIT + +cp "$root/build-aux/pypi/pyproject.toml" "$root/build-aux/pypi/README.md" "$root/LICENSE" "$stage/" +mkdir "$stage/gknowledge" +cp "$root/gknowledge/__init__.py" "$root/gknowledge/mcp.py" "$stage/gknowledge/" +cp -r "$root/gknowledge/core" "$root/gknowledge/providers" "$stage/gknowledge/" +find "$stage" -name __pycache__ -type d -prune -exec rm -rf {} + + +python3 -m build --outdir "$out" "$stage" diff --git a/build-aux/pypi/pyproject.toml b/build-aux/pypi/pyproject.toml new file mode 100644 index 0000000..9deec4c --- /dev/null +++ b/build-aux/pypi/pyproject.toml @@ -0,0 +1,43 @@ +# PyPI distribution of the MCP server: the headless core and gknowledge-mcp, +# without the GTK interface. Built by build-aux/pypi/build.sh, which stages +# these files next to a copy of the package. + +[build-system] +requires = ["setuptools>=64"] +build-backend = "setuptools.build_meta" + +[project] +name = "gknowledge-mcp" +dynamic = ["version"] +description = "MCP server for gknowledge: let Claude Code, Codex or Cursor search, read and grow a Karpathy-style LLM wiki." +readme = "README.md" +requires-python = ">=3.10" +license = { file = "LICENSE" } +authors = [{ name = "gknowledge contributors" }] +keywords = ["mcp", "mcp-server", "llm-wiki", "knowledge-base", "markdown", "claude-code", "codex", "cursor"] +classifiers = [ + "Environment :: Console", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Topic :: Text Processing :: Markup :: Markdown", +] +dependencies = [] + +[project.optional-dependencies] +pdf = ["pypdf>=4.0"] + +[project.urls] +Homepage = "https://openapi.github.io/gknowledge" +Source = "https://github.com/openapi/gknowledge" +Issues = "https://github.com/openapi/gknowledge/issues" + +[project.scripts] +gknowledge-mcp = "gknowledge.mcp:main" + +[tool.setuptools] +packages = ["gknowledge", "gknowledge.core", "gknowledge.providers"] + +[tool.setuptools.dynamic] +version = { attr = "gknowledge.__version__" } diff --git a/gknowledge/mcp.py b/gknowledge/mcp.py index fa5c957..fbfaccd 100644 --- a/gknowledge/mcp.py +++ b/gknowledge/mcp.py @@ -5,6 +5,7 @@ JSON-RPC 2.0 over stdio (newline-delimited). gknowledge-mcp /path/to/project + gknowledge-mcp --init /path/to/project # create the project if missing python3 -m gknowledge.mcp /path/to/project Register with Claude Code: @@ -186,9 +187,14 @@ def handle(self, msg: dict) -> dict | None: def main(argv: list[str] | None = None) -> int: argv = sys.argv[1:] if argv is None else argv + init = "--init" in argv + argv = [a for a in argv if a != "--init"] path = argv[0] if argv else os.environ.get("GKNOWLEDGE_PROJECT", "") + if path and init and not Project.is_project(path): + Project.create(path).close() if not path or not Project.is_project(path): - print("usage: gknowledge-mcp /path/to/gknowledge/project", file=sys.stderr) + print("usage: gknowledge-mcp [--init] /path/to/gknowledge/project", + file=sys.stderr) return 2 server = WikiServer(Project.open(path)) for line in sys.stdin: diff --git a/glama.json b/glama.json new file mode 100644 index 0000000..da3474f --- /dev/null +++ b/glama.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://glama.ai/mcp/schemas/server.json", + "maintainers": ["francescobianco"] +} diff --git a/server.json b/server.json new file mode 100644 index 0000000..62b52b9 --- /dev/null +++ b/server.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", + "name": "io.github.openapi/gknowledge", + "title": "gknowledge", + "description": "Search, read and grow a Karpathy-style LLM wiki of linked Markdown pages, with cited answers.", + "repository": { + "url": "https://github.com/openapi/gknowledge", + "source": "github" + }, + "websiteUrl": "https://openapi.github.io/gknowledge", + "version": "0.2.2", + "packages": [ + { + "registryType": "pypi", + "registryBaseUrl": "https://pypi.org", + "identifier": "gknowledge-mcp", + "version": "0.2.2", + "runtimeHint": "uvx", + "transport": { + "type": "stdio" + }, + "packageArguments": [ + { + "type": "positional", + "value": "--init", + "description": "Create the project folder the first time" + }, + { + "type": "positional", + "valueHint": "project_folder", + "description": "Path to the gknowledge project folder (the wiki)", + "isRequired": true + } + ] + } + ] +} diff --git a/tests/test_mcp.py b/tests/test_mcp.py index 2b633b7..59b1cfa 100644 --- a/tests/test_mcp.py +++ b/tests/test_mcp.py @@ -26,7 +26,11 @@ def _project(tmp_path): def _session(path, messages): - proc = subprocess.run([sys.executable, "-m", "gknowledge.mcp", str(path)], + return _session_args([str(path)], messages) + + +def _session_args(args, messages): + proc = subprocess.run([sys.executable, "-m", "gknowledge.mcp", *args], input="\n".join(json.dumps(m) for m in messages) + "\n", capture_output=True, text=True, cwd=ROOT, timeout=60) return [json.loads(l) for l in proc.stdout.splitlines() if l.strip()] @@ -62,3 +66,20 @@ def test_mcp_round_trip(tmp_path): assert "[1]" in by_id[5]["result"]["content"][0]["text"] assert "Mixture of Experts" in by_id[6]["result"]["content"][0]["text"] assert by_id[7]["result"]["isError"] is True + + +def test_mcp_init_creates_project(tmp_path): + path = tmp_path / "new-wiki" + out = _session_args(["--init", str(path)], [ + {"jsonrpc": "2.0", "id": 1, "method": "tools/call", + "params": {"name": "list_pages", "arguments": {}}}, + ]) + assert Project.is_project(path) + assert out[0]["id"] == 1 and "result" in out[0] + + +def test_mcp_without_init_refuses_missing_project(tmp_path): + proc = subprocess.run([sys.executable, "-m", "gknowledge.mcp", str(tmp_path / "nope")], + input="", capture_output=True, text=True, cwd=ROOT, timeout=60) + assert proc.returncode == 2 + assert not (tmp_path / "nope").exists()