Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/mcp.yml
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions DISTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,9 @@ 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 |
| [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 |

Expand Down Expand Up @@ -384,6 +386,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 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.

Expand Down
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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"

Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions build-aux/debian/gknowledge-mcp.1
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
44 changes: 44 additions & 0 deletions build-aux/pypi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# gknowledge-mcp

<!-- mcp-name: io.github.openapi/gknowledge -->

**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).
21 changes: 21 additions & 0 deletions build-aux/pypi/build.sh
Original file line number Diff line number Diff line change
@@ -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"
43 changes: 43 additions & 0 deletions build-aux/pypi/pyproject.toml
Original file line number Diff line number Diff line change
@@ -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__" }
8 changes: 7 additions & 1 deletion gknowledge/mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions glama.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"$schema": "https://glama.ai/mcp/schemas/server.json",
"maintainers": ["francescobianco"]
}
37 changes: 37 additions & 0 deletions server.json
Original file line number Diff line number Diff line change
@@ -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
}
]
}
]
}
23 changes: 22 additions & 1 deletion tests/test_mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()]
Expand Down Expand Up @@ -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()
Loading