keygraph 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. keygraph-0.2.0/.gitignore +9 -0
  2. keygraph-0.2.0/AGENTS.md +46 -0
  3. keygraph-0.2.0/CHANGELOG.md +24 -0
  4. keygraph-0.2.0/CITATION.cff +25 -0
  5. keygraph-0.2.0/LICENSE +21 -0
  6. keygraph-0.2.0/PKG-INFO +334 -0
  7. keygraph-0.2.0/README.md +305 -0
  8. keygraph-0.2.0/llms.txt +34 -0
  9. keygraph-0.2.0/pyproject.toml +65 -0
  10. keygraph-0.2.0/src/keygraph/__init__.py +8 -0
  11. keygraph-0.2.0/src/keygraph/__main__.py +3 -0
  12. keygraph-0.2.0/src/keygraph/adapters/__init__.py +33 -0
  13. keygraph-0.2.0/src/keygraph/adapters/base.py +57 -0
  14. keygraph-0.2.0/src/keygraph/adapters/clickhouse.py +67 -0
  15. keygraph-0.2.0/src/keygraph/adapters/dump.py +169 -0
  16. keygraph-0.2.0/src/keygraph/adapters/fetch.py +93 -0
  17. keygraph-0.2.0/src/keygraph/adapters/live.py +50 -0
  18. keygraph-0.2.0/src/keygraph/adapters/metabase.py +78 -0
  19. keygraph-0.2.0/src/keygraph/adapters/mongo.py +93 -0
  20. keygraph-0.2.0/src/keygraph/adapters/mysql.py +30 -0
  21. keygraph-0.2.0/src/keygraph/adapters/postgres.py +68 -0
  22. keygraph-0.2.0/src/keygraph/adapters/sqlite.py +24 -0
  23. keygraph-0.2.0/src/keygraph/adapters/sqlserver.py +65 -0
  24. keygraph-0.2.0/src/keygraph/areas.py +225 -0
  25. keygraph-0.2.0/src/keygraph/cli.py +497 -0
  26. keygraph-0.2.0/src/keygraph/context.py +387 -0
  27. keygraph-0.2.0/src/keygraph/docs_out.py +199 -0
  28. keygraph-0.2.0/src/keygraph/explorer.py +128 -0
  29. keygraph-0.2.0/src/keygraph/graph.py +163 -0
  30. keygraph-0.2.0/src/keygraph/hubs.py +48 -0
  31. keygraph-0.2.0/src/keygraph/infer.py +344 -0
  32. keygraph-0.2.0/src/keygraph/mcp_server.py +147 -0
  33. keygraph-0.2.0/src/keygraph/mirrors.py +109 -0
  34. keygraph-0.2.0/src/keygraph/model.py +149 -0
  35. keygraph-0.2.0/src/keygraph/notes.py +114 -0
  36. keygraph-0.2.0/src/keygraph/overrides.py +112 -0
  37. keygraph-0.2.0/src/keygraph/probe.py +119 -0
  38. keygraph-0.2.0/src/keygraph/profile.py +262 -0
  39. keygraph-0.2.0/src/keygraph/query.py +376 -0
  40. keygraph-0.2.0/src/keygraph/report.py +92 -0
  41. keygraph-0.2.0/src/keygraph/search.py +175 -0
  42. keygraph-0.2.0/src/keygraph/static/DAGRE_LICENSE +19 -0
  43. keygraph-0.2.0/src/keygraph/static/OFL-Inter.txt +93 -0
  44. keygraph-0.2.0/src/keygraph/static/OFL-JetBrainsMono.txt +93 -0
  45. keygraph-0.2.0/src/keygraph/static/dagre-1.1.4.min.js +801 -0
  46. keygraph-0.2.0/src/keygraph/static/explorer.html +69 -0
  47. keygraph-0.2.0/src/keygraph/static/explorer.js +1076 -0
  48. keygraph-0.2.0/src/keygraph/static/favicon.svg +1 -0
  49. keygraph-0.2.0/src/keygraph/static/keygraph-logo.svg +1 -0
  50. keygraph-0.2.0/src/keygraph/static/keygraph-mark.svg +1 -0
  51. keygraph-0.2.0/src/keygraph/static/keygraph.inline.css +843 -0
  52. keygraph-0.2.0/src/keygraph/static/kg-diagram.js +267 -0
  53. keygraph-0.2.0/src/keygraph/static/kg-theme.js +269 -0
  54. keygraph-0.2.0/src/keygraph/summary.py +60 -0
@@ -0,0 +1,9 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ .pytest_cache/
5
+ dist/
6
+ *.egg-info/
7
+ keygraph-out/
8
+ uv.lock.bak
9
+ .DS_Store
@@ -0,0 +1,46 @@
1
+ # AGENTS.md: using keygraph as an AI agent
2
+
3
+ keygraph maps how the tables in a database connect (keys, indexes, declared and inferred foreign keys, business context, sampled value profiles). Use it to find tables and join paths before you write SQL. It is read-only and deterministic. It never runs SQL for you and makes no LLM calls.
4
+
5
+ Commands below assume a built output directory (default `keygraph-out`, set with `--dir` or `KEYGRAPH_DIR`). If the keygraph MCP server is connected, call the tool with the same name instead of the CLI; it returns the same JSON as `--json` (except `areas`, which wraps the list as `{"areas": [...]}`). Table names resolve leniently: a bare name works when it is unique across schemas, otherwise write `schema.table`.
6
+
7
+ ## Sequence
8
+
9
+ 1. Locate the data. `keygraph search "<business term>" --json` ranks tables by meaning and says why each matched. If you do not know where to start, `keygraph areas --json` lists business areas and `keygraph area <name> --json` shows one area's tables, grain and relations. (The MCP `areas` tool returns the list wrapped as `{"areas": [...]}`.)
10
+ 2. Understand each candidate. `keygraph describe <table> --json` gives the row grain, keys, columns, status codes, pitfalls and sampled profiles.
11
+ 3. Find the join path before writing any join. `keygraph path <A> <B> --json` returns ranked routes with columns, confidence, evidence, fan-out warnings and a `JOIN` snippet. Use the best route unless a hop is weak.
12
+ 4. Choose index-friendly filters. `keygraph keys <table> --json` lists the primary key, unique keys and indexes in column order. Filter on the leading column of an index first, especially on large tables.
13
+ 5. Check a single relation or column when needed. `keygraph explain <table.column> --json` shows what it references, what references it, and the evidence.
14
+
15
+ ## How to read confidence
16
+
17
+ Declared foreign keys have confidence 1.0. Inferred relations are guesses from names, types, indexes and key shapes.
18
+
19
+ - 0.8 and above: use it.
20
+ - 0.6 to 0.8: read the evidence lines first.
21
+ - Below 0.6: never treat it as a fact. Verify it with a small `SELECT` (for example a count of rows whose value has no match) before relying on it. These edges are hidden by default; `path --min-confidence 0.4` shows them.
22
+ - A hop marked `fan-out` turns one row into many. Aggregate before the join, or summing a column across it will count rows repeatedly.
23
+ - Polymorphic columns (`x_type` plus `x_id`) have no single target.
24
+ - Hub tables (such as a `users` table everything points to) appear as labels, and routes through hubs, log, archive or scratch tables are ranked lower on purpose.
25
+ - Tables in a mirror schema (a copy of another schema) are skipped unless you pass `--include-mirrors`.
26
+
27
+ ## Profiles and context
28
+
29
+ Value profiles (top values, ranges, empty rates) come from a bounded sample and carry `profiled_at`, sample size and method. Treat them as samples that may be out of date, not as the full distribution. For personal data columns raw values are never stored; do not try to recover them.
30
+
31
+ When you confirm what a table, column or status code means, save it so the next search finds it: `keygraph context add --kind column table=schema.t column=status codes=1:new,2:paid meaning="..." verified=true`. Mark `verified=true` only if you checked it against data or code. Record a wrong or right relation in an overrides file (`confirm`, `reject`, `add`, `aliases`) and rebuild.
32
+
33
+ ## Do not
34
+
35
+ - Do not ask keygraph to run SQL. It has no query tool. Run your own read-only query with your own database access.
36
+ - Do not write a multi-table join from column-name guesses when `path` can answer it.
37
+ - Do not present an inferred relation below 0.6 as established.
38
+ - Do not assume a profile represents every row.
39
+
40
+ ## Files you can read directly
41
+
42
+ `areas/index.md` (every area), `areas/<area>.md` (tables, joins, a Mermaid ER diagram), `tables/<schema.table>.md` (grain, keys, columns, relations in both directions), `RELATIONS_REPORT.md` (all relations with evidence) and `graph.json` (everything, machine readable).
43
+
44
+ ## Build the graph
45
+
46
+ If no output directory exists: `keygraph scan --url <sqlalchemy-url> [--schema S]` (needs `keygraph[live]`) or `keygraph load --columns columns.json ...` from `information_schema` dumps. Setup for MCP clients is in the README: https://github.com/alikarami76/keygraph#mcp
@@ -0,0 +1,24 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0 - 2026-11-03
4
+
5
+ - Question-first explorer views: Areas, Area, Path, Table, Column and Search, with a canvas limit of 40 boxes, 25 tables shown first in large areas, and groups of more than 8 relations collapsed.
6
+ - Hubs (top 1% by incoming relations, or 60% or more from role columns such as `created_by_id`) shown as labels, and business areas grouped deterministically, with curation through an `areas:` block in the context file.
7
+ - Mirror schemas detected and kept out of areas, hub counts and join paths (`--include-mirrors` restores them).
8
+ - Join path ranking with penalties for hub, fan-out (one-to-many) and log, archive or scratch tables, and fan-out warnings in the output.
9
+ - Agent documents written with each build: `areas/index.md`, `areas/<area>.md` with Mermaid ER diagrams, and `tables/<schema.table>.md`.
10
+ - New commands `areas` and `area`; `--json` on query commands; `--include-mirrors` on `path`, `areas` and `area`.
11
+ - Value profiles (`keygraph profile`): bounded sampling, null rate, ranges, top values, pattern hints, flags for undocumented or unseen status codes, and the PII rule that stores no raw values for personal data.
12
+ - Source adapters: MySQL/MariaDB, PostgreSQL, SQLite, SQL Server, ClickHouse, Metabase, MongoDB, information_schema dumps and external read-only commands, all limited to read-only access.
13
+ - MCP server (`keygraph mcp`) with nine read-only tools and two resources.
14
+ - Design system, logo and README visuals; fictional demo webshop in `examples/shop`; "How keygraph works" PDF.
15
+ - README, AGENTS.md, llms.txt, CITATION.cff and package metadata for discoverability.
16
+ - Python 3.11 works again (the CLI failed to import, and `context add --kind ... key=value` was rejected).
17
+ - Search leaves short words and stopwords out of prefix matching and ranks whole-phrase hits higher, so a status label such as "in transit" finds its column first.
18
+ - Malformed curation entries (area rules without a name or match, `hubs:` or `mirrors:` that are not mappings) become build warnings instead of crashes.
19
+ - `keygraph.html` carries a Content-Security-Policy; the MCP server closes its database handles on errors, reads older `keygraph.db` files, and caps `neighbors` depth at 3.
20
+ - Mermaid diagrams use safe entity names (no leading digit, ASCII only) and say when an area's diagram shows only its 25 most connected tables.
21
+
22
+ ## 0.1.0
23
+
24
+ - Initial release: keys and indexes from the schema, declared and inferred foreign keys with confidence and evidence, join path search, business context in SQLite with full-text search, a self-contained HTML explorer, and the Claude Code skill.
@@ -0,0 +1,25 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use keygraph, please cite it as below."
3
+ title: "keygraph"
4
+ type: software
5
+ version: 0.2.0
6
+ date-released: 2026-11-03
7
+ license: MIT
8
+ url: "https://github.com/alikarami76/keygraph"
9
+ repository-code: "https://github.com/alikarami76/keygraph"
10
+ authors:
11
+ - family-names: Karami
12
+ given-names: Ali
13
+ abstract: >-
14
+ keygraph maps how the tables in a database connect: keys, indexes, declared and inferred
15
+ foreign keys with confidence and evidence, business context and value profiles. It finds join
16
+ paths between tables and gives people an offline HTML explorer and AI agents an MCP server,
17
+ a CLI with JSON output and markdown files. It is read-only and deterministic.
18
+ keywords:
19
+ - database
20
+ - schema
21
+ - foreign keys
22
+ - join path
23
+ - ER diagram
24
+ - text-to-SQL
25
+ - MCP
keygraph-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ali Karami
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,334 @@
1
+ Metadata-Version: 2.5
2
+ Name: keygraph
3
+ Version: 0.2.0
4
+ Summary: keygraph maps how the tables in your database connect (keys, indexes, declared and inferred foreign keys, business context and value profiles) so people and AI agents can find the right join path and the right data.
5
+ Project-URL: Homepage, https://github.com/alikarami76/keygraph
6
+ Project-URL: Documentation, https://github.com/alikarami76/keygraph#readme
7
+ Project-URL: Issues, https://github.com/alikarami76/keygraph/issues
8
+ Project-URL: Changelog, https://github.com/alikarami76/keygraph/blob/main/CHANGELOG.md
9
+ Author-email: Ali Karami <52026333+alikarami76@users.noreply.github.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: ai-agents,claude-code,clickhouse,data-catalog,data-profiling,database,er-diagram,erd,foreign-keys,join-path,llm,mcp,metabase,mongodb,mysql,postgresql,schema,schema-inference,sql,text-to-sql
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Database
21
+ Requires-Python: >=3.11
22
+ Provides-Extra: live
23
+ Requires-Dist: sqlalchemy>=2.0; extra == 'live'
24
+ Provides-Extra: mcp
25
+ Requires-Dist: mcp<3,>=2; extra == 'mcp'
26
+ Provides-Extra: yaml
27
+ Requires-Dist: pyyaml>=6; extra == 'yaml'
28
+ Description-Content-Type: text/markdown
29
+
30
+ <picture>
31
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/alikarami76/keygraph/main/design/visuals/readme-hero-dark.svg">
32
+ <img alt="keygraph: map how your tables connect, for people and AI agents" src="https://raw.githubusercontent.com/alikarami76/keygraph/main/design/visuals/readme-hero-light.svg">
33
+ </picture>
34
+
35
+ # keygraph
36
+
37
+ keygraph maps how the tables in your database connect: keys, indexes, declared and inferred foreign keys, business context and value profiles. People and AI agents use the map to find the right join path and the right data. It reads only metadata, makes no LLM calls, and gives the same output for the same input.
38
+
39
+ [![PyPI](https://img.shields.io/pypi/v/keygraph)](https://pypi.org/project/keygraph/) [![Python](https://img.shields.io/pypi/pyversions/keygraph)](https://pypi.org/project/keygraph/) [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/alikarami76/keygraph/blob/main/LICENSE)
40
+
41
+ ## When to use keygraph
42
+
43
+ Use keygraph when you need to:
44
+
45
+ - find the join path between two tables, with the join columns, a confidence for each hop and a ready `JOIN` snippet;
46
+ - infer missing foreign keys in a database that declares few or none;
47
+ - get an ER diagram of an existing database, grouped into business areas so it stays readable;
48
+ - give an LLM agent schema context for text-to-SQL: keys, indexes, join paths, grain, status codes and pitfalls, through MCP, a CLI with `--json`, or markdown files;
49
+ - find which table holds a business concept such as "net revenue" or "in transit";
50
+ - learn what an enum or status code means, and compare the documented codes with the values actually in the data;
51
+ - profile column values (top values, empty rate, ranges) with personal data masked;
52
+ - browse a data catalog from one offline HTML file, without running a server.
53
+
54
+ <picture>
55
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/alikarami76/keygraph/main/design/visuals/section-quickstart-dark.svg">
56
+ <img alt="Quickstart" src="https://raw.githubusercontent.com/alikarami76/keygraph/main/design/visuals/section-quickstart-light.svg">
57
+ </picture>
58
+
59
+ ## Quickstart
60
+
61
+ keygraph needs Python 3.11 or newer. Install it, build a graph from a live database, and open the explorer:
62
+
63
+ ```bash
64
+ pip install 'keygraph[live]' # or: uvx --from 'keygraph[live]' keygraph ...
65
+ keygraph scan --url postgresql://user@host/db --schema public # read-only metadata scan, writes ./keygraph-out
66
+ keygraph ui # opens keygraph-out/keygraph.html
67
+ ```
68
+
69
+ `scan` takes any SQLAlchemy URL, so add the driver for your database (for example `psycopg` for PostgreSQL or `pymysql` for MySQL). Then ask questions from the command line:
70
+
71
+ ```bash
72
+ keygraph path customers shipments # ranked join paths with a JOIN snippet
73
+ keygraph search "monthly revenue" # which tables hold this
74
+ keygraph keys orders # primary key, unique keys, indexes in column order
75
+ ```
76
+
77
+ To try it without a database, clone the repository and build the bundled fictional webshop (35 tables, three schemas):
78
+
79
+ ```bash
80
+ git clone https://github.com/alikarami76/keygraph && cd keygraph
81
+ uv sync && uv run bash examples/shop/build.sh && uv run keygraph ui --dir examples/shop/out
82
+ ```
83
+
84
+ The build writes `graph.json`, `RELATIONS_REPORT.md`, `keygraph.html` (a self-contained explorer that works from disk, offline), `keygraph.db` (SQLite with full-text search) and the agent pages `areas/*.md` and `tables/*.md`. Details of every step are in [How keygraph works (PDF)](https://github.com/alikarami76/keygraph/blob/main/docs/how-it-works.pdf) and [examples/shop](https://github.com/alikarami76/keygraph/blob/main/examples/shop/README.md).
85
+
86
+ If you cannot connect to the database, build from `information_schema` dumps instead:
87
+
88
+ ```bash
89
+ keygraph load --columns columns.jsonl [--statistics stats.jsonl] [--key-columns kcu.jsonl] [--tables tables.jsonl]
90
+ ```
91
+
92
+ Dumps use `information_schema` field names. `--columns-map '{"TABLE_SCHEMA":"db"}'`, `--flatten raw` and `--where field=value` adapt other shapes. Without STATISTICS the tool falls back to `COLUMN_KEY` (PRI/UNI/MUL), and without KEY_COLUMN_USAGE every relation is inferred. `keygraph fetch --cmd 'my-sql-cli --sql {sql}' --out dir` fetches those two tables through any read-only command that prints JSON rows.
93
+
94
+ ## Supported sources
95
+
96
+ Each source is a module in `keygraph.adapters`, read-only by construction. SQL statements pass `assert_select` (a single SELECT, no write keywords, no `INTO OUTFILE` or `FOR UPDATE`); SQLite is read through the SQLAlchemy inspector, and MongoDB and Metabase are limited to the calls listed below.
97
+
98
+ | Source | What keygraph reads | How you use it |
99
+ |---|---|---|
100
+ | MySQL, MariaDB | `information_schema` (columns, indexes with column order, foreign keys, table facts, enum members) | `keygraph scan --url`, dumps, or the `mysql` adapter |
101
+ | PostgreSQL | `information_schema` and `pg_catalog` (indexes, foreign keys, enum types, row estimates, comments, checks) | `keygraph scan --url`, or the `postgres` adapter |
102
+ | SQLite | the SQLAlchemy inspector (metadata only) | `keygraph scan --url sqlite:///file.db` |
103
+ | SQL Server | `INFORMATION_SCHEMA` and `sys.*`; samples use `SELECT TOP n` | `sqlserver` adapter, `keygraph profile --source sqlserver` |
104
+ | ClickHouse | `system.tables`, `system.columns`, `system.data_skipping_indices`; the primary key becomes the PRIMARY index, `ORDER BY` an index | `clickhouse` adapter, `keygraph profile --source clickhouse` |
105
+ | Metabase | REST `GET /api/database/:id/metadata` (tables, fields, semantic types, foreign-key targets, field fingerprints) and native SELECTs through `POST /api/dataset`; no other request | `metabase` adapter (Python API) |
106
+ | MongoDB | schema inferred from sampled documents (nested paths become columns), indexes, `_id` as primary key, DBRef values as references; only `list_collection_names`, `find`, `index_information` and `estimated_document_count` can be called | `mongodb` adapter, `keygraph profile --source mongodb` |
107
+ | `information_schema` dumps (JSON, JSONL, CSV) | the files you give it | `keygraph load` |
108
+ | Any read-only command that prints JSON rows | the rows | `keygraph fetch --cmd` |
109
+
110
+ The Python entry point for the adapters is `keygraph.adapters.open_source(kind, ...)`, which returns an object with `schema()` and `sample()`.
111
+
112
+ <picture>
113
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/alikarami76/keygraph/main/design/visuals/section-explorer-views-dark.svg">
114
+ <img alt="Explorer views" src="https://raw.githubusercontent.com/alikarami76/keygraph/main/design/visuals/section-explorer-views-light.svg">
115
+ </picture>
116
+
117
+ ## Explorer views
118
+
119
+ `keygraph.html` is one file with no network requests. It is organised around the questions people ask: where does something live, how does it connect, what is in it. A canvas never draws more than 40 boxes: bigger areas are grouped and say what they hide, and an area over 25 tables first shows its 25 most connected tables.
120
+
121
+ **Areas.** The map of the whole database. Each box is a business area; the number on a line is how many relations join two areas. Start here when you do not know where something lives.
122
+
123
+ <img alt="keygraph Areas view: the database as business areas" src="https://raw.githubusercontent.com/alikarami76/keygraph/main/docs/how-it-works/shots/areas.png" width="900">
124
+
125
+ **Area.** One area as a table diagram. Lines attach to the exact column that joins. Solid lines are declared foreign keys, dashed lines are inferred and carry their confidence. Hubs (tables that half the database points to) appear as a small label on each box instead of as dozens of lines.
126
+
127
+ <img alt="keygraph Area view: tables with key columns and relations" src="https://raw.githubusercontent.com/alikarami76/keygraph/main/docs/how-it-works/shots/area.png" width="900">
128
+
129
+ **Path.** Pick two tables and get ranked routes, a copyable `JOIN`, and a warning at every step that can multiply rows (fan-out).
130
+
131
+ <img alt="keygraph Path view: join routes between two tables with fan-out warnings" src="https://raw.githubusercontent.com/alikarami76/keygraph/main/docs/how-it-works/shots/path.png" width="900">
132
+
133
+ **Table.** One page per table: what a row is, pitfalls, the metrics that use it, and every column with its keys, value ranges and empty rate.
134
+
135
+ <img alt="keygraph Table view: grain, pitfalls, metrics and columns" src="https://raw.githubusercontent.com/alikarami76/keygraph/main/docs/how-it-works/shots/table.png" width="900">
136
+
137
+ **Search.** Type a business word. Metrics, concepts, tables, columns and profiled values come from one index, so a status label such as "in transit" finds the column that stores it. Each result says why it matched.
138
+
139
+ <img alt="keygraph Search view: results grouped by type with the reason for each match" src="https://raw.githubusercontent.com/alikarami76/keygraph/main/docs/how-it-works/shots/search.png" width="900">
140
+
141
+ There is also a Column view that sets documented status codes next to the values found in a sample. All screenshots come from the fictional demo in [examples/shop](https://github.com/alikarami76/keygraph/blob/main/examples/shop/README.md).
142
+
143
+ <picture>
144
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/alikarami76/keygraph/main/design/visuals/section-ai-agents-dark.svg">
145
+ <img alt="Use with AI agents" src="https://raw.githubusercontent.com/alikarami76/keygraph/main/design/visuals/section-ai-agents-light.svg">
146
+ </picture>
147
+
148
+ ## Use with AI agents
149
+
150
+ An agent can use the same graph four ways: MCP tools, the Claude Code skill, the CLI with `--json`, and plain markdown files. The recommended order for an agent is in [AGENTS.md](https://github.com/alikarami76/keygraph/blob/main/AGENTS.md): search or list the areas, describe the table, find the path before writing any join, and use `keys` to choose index-friendly filters.
151
+
152
+ Every query command (`search`, `describe`, `keys`, `explain`, `path`, `neighbors`, `areas`, `area`) takes `--json`. The build also writes `areas/index.md`, `areas/<area>.md` (tables, joins and a Mermaid ER diagram) and `tables/<schema.table>.md` (grain, keys, columns, relations in both directions) that an agent can read like any other file.
153
+
154
+ The Claude Code skill is in [skills/keygraph/SKILL.md](https://github.com/alikarami76/keygraph/blob/main/skills/keygraph/SKILL.md). It tells an agent when to use each command and how to read confidence.
155
+
156
+ ### MCP
157
+
158
+ `keygraph mcp` serves the read-only queries to any MCP client over stdio. It needs the `mcp` extra and a built output
159
+ directory (`<out>` below, e.g. `keygraph-out`). Tools: `search`, `describe`, `keys`, `explain`, `path`, `neighbors`,
160
+ `areas`, `area` and `profile_summary` (stored, sampled profiles; PII values are withheld). Each returns the same JSON as
161
+ the CLI's `--json`, except that `areas` wraps the list of areas as `{"areas": [...]}` because MCP tool results must be objects. `path`, `areas` and `area` take `include_mirrors`. Resources: `keygraph://areas/index` and
162
+ `keygraph://tables/{schema.table}`. The server never runs SQL.
163
+
164
+ Use absolute paths for `--dir` and `--directory`. GUI clients (Claude Desktop, Cursor) often do not see your shell
165
+ `PATH`, so put the absolute path to `uvx` or `uv` (`which uvx`) in `command` if the server fails to start. The checkout
166
+ variant runs from a clone: `uv --directory /abs/path/to/keygraph run --extra mcp keygraph mcp --dir /abs/path/to/out`.
167
+
168
+ Claude Code:
169
+
170
+ claude mcp add keygraph -- uvx --from 'keygraph[mcp]' keygraph mcp --dir /abs/path/to/out
171
+ claude mcp add keygraph -- uv --directory /abs/path/to/keygraph run --extra mcp keygraph mcp --dir /abs/path/to/out
172
+
173
+ Claude Desktop (`claude_desktop_config.json`) and Cursor (`.cursor/mcp.json`):
174
+
175
+ {
176
+ "mcpServers": {
177
+ "keygraph": {
178
+ "command": "uvx",
179
+ "args": ["--from", "keygraph[mcp]", "keygraph", "mcp", "--dir", "/abs/path/to/out"]
180
+ }
181
+ }
182
+ }
183
+
184
+ {
185
+ "mcpServers": {
186
+ "keygraph": {
187
+ "command": "uv",
188
+ "args": ["--directory", "/abs/path/to/keygraph", "run", "--extra", "mcp", "keygraph", "mcp", "--dir", "/abs/path/to/out"]
189
+ }
190
+ }
191
+ }
192
+
193
+ Codex (`~/.codex/config.toml`):
194
+
195
+ [mcp_servers.keygraph]
196
+ command = "uvx"
197
+ args = ["--from", "keygraph[mcp]", "keygraph", "mcp", "--dir", "/abs/path/to/out"]
198
+
199
+ # or, from a checkout
200
+ [mcp_servers.keygraph]
201
+ command = "uv"
202
+ args = ["--directory", "/abs/path/to/keygraph", "run", "--extra", "mcp", "keygraph", "mcp", "--dir", "/abs/path/to/out"]
203
+
204
+ (Use one `[mcp_servers.keygraph]` block, not both.)
205
+
206
+ <picture>
207
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/alikarami76/keygraph/main/design/visuals/section-relations-inferred-dark.svg">
208
+ <img alt="How relations are inferred" src="https://raw.githubusercontent.com/alikarami76/keygraph/main/design/visuals/section-relations-inferred-light.svg">
209
+ </picture>
210
+
211
+ ## How relations are inferred
212
+
213
+ Declared foreign keys score 1.0. For every other column, keygraph matches names and key shapes: a column such as `customer_id` or `customerId` is matched to a table named `customer(s)` whose primary or unique key is a single column. Plurals, common prefixes, role prefixes (`buyer_user_id` to `users`), `*_by_id` columns (mapped to a users-like table), `parent_id` self-references and columns named like another table's primary key are handled. `<x>_type` plus `<x>_id` pairs are marked polymorphic, with no single target.
214
+
215
+ Each inferred relation carries a confidence between 0 and 1 and the evidence lines that produced it. A compatible type, an index on the column, a primary-key target and a business relation in the context file raise it. A type mismatch, a cross-schema match and more than one possible target lower it. The optional `keygraph probe` samples values (SELECT only) to measure the real match rate. Read the number like this: 0.8 and above, use it; 0.6 to 0.8, read the evidence first; below 0.6, check it with a small SELECT. The explorer hides edges below 0.6 until you switch on "Show weak relations" (floor 0.4).
216
+
217
+ **Hubs.** A table in the top 1% by incoming relations, or one where at least 60% of incoming relations come from role columns such as `created_by_id`, is a hub. It is shown as a label, not as lines, so one `users` table does not turn every diagram into a hairball.
218
+
219
+ **Areas.** Tables that join each other (hubs removed) form a business area. The grouping is deterministic. A short `areas:` curation in the context file renames areas or pins tables to them.
220
+
221
+ **Path ranking.** `keygraph path` multiplies the confidence of each hop and applies penalties for routes through hubs, one-to-many (fan-out) steps and log, archive or scratch tables (names with a whole token such as `log`, `shadow` or `archive`), then prefers fewer hops. Each fan-out step is flagged in the output, because summing a column across it counts rows more than once.
222
+
223
+ **Mirrors.** A schema that holds copies of another schema's tables (a nightly reporting copy, say) is detected, marked as a mirror, and kept out of areas, hub counts and join paths so nothing is counted twice. `--include-mirrors` brings it back.
224
+
225
+ An overrides file (YAML or JSON) supports `aliases`, `role_aliases`, `prefixes`, `confirm`, `reject` and `add`:
226
+
227
+ ```yaml
228
+ aliases: {seller: shop.sellers}
229
+ confirm: [{from: shop.orders.customer_id, to: shop.customers.id}]
230
+ reject: [{from: shop.logs.ref_id}]
231
+ ```
232
+
233
+ Pass it with `--overrides overrides.yaml` to `scan` or `load` and the next build uses it.
234
+
235
+ ## Business context and value profiles
236
+
237
+ A schema knows column names, not that status 3 means "shipped". Context lives in `keygraph.db`: concepts (with multilingual synonyms), links from concepts to tables and columns, concept relations, column semantics (meaning, unit, status codes, null meaning, PII), metrics, and table notes (grain, freshness, soft-delete and date columns, pitfalls). Every row records its source, a verified flag and an update time.
238
+
239
+ ```bash
240
+ keygraph context import context/ # YAML/JSON files
241
+ keygraph context import-notes notes/ # one markdown note per table
242
+ keygraph context add --kind column table=shop.orders column=status codes=1:new,2:paid meaning="order state"
243
+ keygraph context export --out context.yaml
244
+ ```
245
+
246
+ Unknown tables or columns produce warnings, not failures. Concept relations that match a graph edge add evidence to it.
247
+
248
+ `keygraph profile --source mysql --url ... [--tables a,b] [--top-hubs 30] [--with-context] [--sample 10000]` samples each table with one bounded SELECT (never a full scan) and stores, per column: null rate, distinct values in the sample, min and max, percentiles, length statistics, date range and recency, top values with counts, and pattern hints (json, email, phone, url, uuid, Persian text, monetary). Each profile records `profiled_at`, the sample size and the method, so readers know how fresh and approximate it is. Codes seen in data but undocumented, and documented codes never seen, are flagged. Treat profiles as samples.
249
+
250
+ **PII rule.** A column is treated as personal data when its name matches (email, mobile, phone, national id, first, last or full name, address, postal code, token, password, iban, card number, birth date, ip address, api key and similar), when sampled values look like email addresses or phone numbers, or when a context entry sets `pii`. For those columns raw values are never stored: no top values, minimum, maximum or percentiles, only counts, null rate, length statistics and the pattern name. The rule is enforced in `profile_values` and covered by tests.
251
+
252
+ ## Comparison
253
+
254
+ These tools solve neighbouring problems. The table says what each is for and where keygraph overlaps or stops.
255
+
256
+ | Tool | What it is for | How keygraph differs |
257
+ |---|---|---|
258
+ | SchemaSpy | Generates static HTML documentation and ER diagrams from a live database. | SchemaSpy also guesses implied relationships from column names (`-noimplied` turns this off). keygraph adds a confidence score and evidence for each guess, ranked join paths with fan-out warnings, business context, value profiles, MCP and agent files. SchemaSpy is the more established documentation generator. |
259
+ | dbdocs (DBML) | Publishes database documentation from DBML files that describe a schema. | keygraph reads an existing database or its dumps instead of a hand-written schema file. It does not host documentation online. |
260
+ | DataHub, OpenMetadata | Metadata platforms run as servers: ingestion, lineage, ownership, governance, glossary. | keygraph is a command-line tool that writes a folder of files. It has no server, users, lineage or governance. It is smaller in scope and needs no deployment. |
261
+ | Database IDE diagrams (DBeaver, DataGrip) | Generate ER diagrams from an existing database, mostly from its declared foreign keys. | keygraph groups a large schema into areas so each diagram stays readable, and shows inferred relations next to declared ones. |
262
+ | Diagram design tools (dbdiagram) | Design a schema as code and draw it, usually before the database exists. | keygraph starts from an existing database and does not design or change schemas. |
263
+ | Text-to-SQL schema linking | Choose which tables and columns are relevant to a natural-language question, often with a model. | keygraph supplies the inputs such a system can use (keys, join paths with fan-out warnings, grain, status codes). It does not turn a question into SQL and it makes no LLM calls. |
264
+
265
+ What keygraph does not do: it does not run your queries, generate SQL beyond a `JOIN` snippet, track lineage, monitor data quality, manage access, or edit a database.
266
+
267
+ <picture>
268
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/alikarami76/keygraph/main/design/visuals/section-faq-dark.svg">
269
+ <img alt="FAQ" src="https://raw.githubusercontent.com/alikarami76/keygraph/main/design/visuals/section-faq-light.svg">
270
+ </picture>
271
+
272
+ ## FAQ
273
+
274
+ ### How do I find the join path between two tables?
275
+ Run `keygraph path A B`. It returns ranked routes with the join columns, each hop's confidence and evidence, fan-out warnings and a ready `JOIN ... ON ...` snippet. Use `--max-hops` and `--min-confidence` (default 0.6) to widen or narrow the search, and `--json` for agents. The Path view in the explorer shows the same routes.
276
+
277
+ ### How do I infer missing foreign keys?
278
+ Build the graph with `keygraph scan` or `keygraph load`. Every column that looks like a reference gets an inferred relation with a confidence and the reasons. Names such as `customer_id` are matched to a table with a single-column key, and an index or a primary-key target raises the score. `keygraph probe` can then sample values to check the match rate.
279
+
280
+ ### How do I get an ER diagram from an existing database?
281
+ Build the graph and open `keygraph.html`, or read `areas/<area>.md`, which contains a Mermaid ER diagram for each business area. Large databases are split into areas. The Mermaid diagram draws at most an area's 25 most connected tables and says so on the page when it leaves tables out; the explorer's canvas never draws more than 40 boxes. In the explorer, declared foreign keys are solid lines and inferred ones are dashed.
282
+
283
+ ### How do I give an LLM schema context for text-to-SQL?
284
+ Connect the keygraph MCP server, or let the agent call the CLI with `--json`. The agent should search, describe the tables, ask for the path before writing a join, and use `keys` for index-friendly filters. Grain, status codes, pitfalls and fan-out warnings come with the answers, which are the things a model otherwise has to guess. See [AGENTS.md](https://github.com/alikarami76/keygraph/blob/main/AGENTS.md).
285
+
286
+ ### How do I find which table holds some data?
287
+ Run `keygraph search "net revenue"`. Search ranks tables by names, synonyms, concepts, metrics, notes, column codes and profiled values, and each hit says why it matched, what one row is, and which columns to filter on. Business meaning comes from the context file you provide, so the better it is, the better the answers.
288
+
289
+ ### How do I find out what an enum or status code means?
290
+ Run `keygraph describe table` or `keygraph explain table.column`. Documented codes come from your context file, enum members come from the schema, and after `keygraph profile` the sampled values are shown beside them. Codes present in data but not documented are flagged.
291
+
292
+ ### Is keygraph read-only and safe on production?
293
+ Yes, it never writes to your database. A scan reads metadata only. `profile` and `probe` run single, bounded SELECT statements, and every statement passes a check that rejects anything that is not one SELECT. Even so, point it at a replica if your policy requires it.
294
+
295
+ ### Does keygraph send data anywhere?
296
+ No. It makes no network requests of its own beyond the connection to the database you choose, and it calls no LLM. The explorer is one HTML file that works offline, with its libraries bundled.
297
+
298
+ ### Does it work on large databases?
299
+ It is built for them. The overview shows areas, not tables. An area over 25 tables first shows its 25 most connected tables (in the explorer and in the Mermaid diagram of `areas/<area>.md`), and no explorer canvas draws more than 40 boxes. Profiling samples with one bounded SELECT per table and can skip tables above a row limit (`--skip-over-rows`). No benchmarks are published here.
300
+
301
+ ### How do I set up the MCP server?
302
+ Install `keygraph[mcp]`, build an output directory, then register `keygraph mcp --dir /abs/path/to/out` with your client. Copy-paste configs for Claude Code, Claude Desktop, Cursor and Codex are in the [MCP section](#mcp) above. The server exposes nine read-only tools and never runs SQL.
303
+
304
+ ### How does confidence work?
305
+ Confidence is a score from 0 to 1 for an inferred relation. Declared foreign keys are 1.0; inferred ones are raised or lowered by name match, types, indexes, key targets, ambiguity and cross-schema matches, and every reason is listed as evidence. Use 0.8 and above freely, read the evidence between 0.6 and 0.8, and verify anything lower.
306
+
307
+ ### How do I correct a wrong relation?
308
+ Write it in an overrides file with `confirm`, `reject`, `add` or `aliases`, and rebuild with `--overrides`. To rename areas or move tables between them, use the `areas:` curation in the context file. Business meaning goes back through `keygraph context add`.
309
+
310
+ ## More
311
+
312
+ - [How keygraph works](https://github.com/alikarami76/keygraph/blob/main/docs/how-it-works.pdf) (PDF with screenshots)
313
+ - [examples/shop](https://github.com/alikarami76/keygraph/blob/main/examples/shop/README.md), a fictional webshop you can build in a minute
314
+ - [AGENTS.md](https://github.com/alikarami76/keygraph/blob/main/AGENTS.md), [skills/keygraph/SKILL.md](https://github.com/alikarami76/keygraph/blob/main/skills/keygraph/SKILL.md), [llms.txt](https://github.com/alikarami76/keygraph/blob/main/llms.txt)
315
+ - [Design system](https://github.com/alikarami76/keygraph/blob/main/design/DESIGN.md) and [CHANGELOG](https://github.com/alikarami76/keygraph/blob/main/CHANGELOG.md)
316
+
317
+ ## Read-only guarantees
318
+
319
+ - `scan` reads metadata only. It never reads table rows.
320
+ - `profile` and `probe` send single SELECT statements, one bounded sample per table. Anything else is rejected before it reaches the database.
321
+ - Metabase: GET metadata plus native SELECT. MongoDB: list, find and index reads only.
322
+ - Personal data is never stored in profiles (the PII rule above).
323
+ - The explorer is one self-contained HTML file with no runtime network fetches.
324
+
325
+ ## Development
326
+
327
+ ```bash
328
+ uv sync
329
+ uv run pytest
330
+ ```
331
+
332
+ ## License
333
+
334
+ MIT. See [LICENSE](https://github.com/alikarami76/keygraph/blob/main/LICENSE). If you use keygraph in research, see [CITATION.cff](https://github.com/alikarami76/keygraph/blob/main/CITATION.cff).