# /docs/start/quickstart # Fabric MCP — Quickstart Under 1 minute from `npx` to indexing your repo. ## Prerequisites - Node.js 18+ - One of the following MCP-compatible clients installed and signed in: - [Claude Code](https://docs.claude.com/en/docs/claude-code) - [Cursor](https://cursor.com/docs) - [Codex](https://developers.openai.com/codex/quickstart?setup=cli) ## Install ```bash npx -y @cognisos/fabric-mcp setup ``` The setup command signs you in via your browser and registers Fabric as an MCP server. > **Claude Code** — restart Claude Code and run `/mcp` to verify `fabric` is listed. > **Cursor** — restart Cursor; open chat, click the tools icon, and confirm `fabric.*` tools appear. > **Codex** — restart your Codex session; Fabric tools will be available automatically. ## Verify Confirm the tools are registered in your client (see the client note above), then in any project ask your AI assistant to run: ``` fabric_status ``` You should see node/edge counts for the local index. ## First workflow Ask your AI assistant to: > Use `fabric_index` to index this project. Indexing takes a few seconds for a small repo. The local index lives under `~/.fabric/index/v2/`. If anything looks off, run `npx @cognisos/fabric-mcp doctor`. --- # /docs/fabric-indexer # Fabric Indexer **MCP server that turns any codebase into a typed knowledge graph for AI assistants.** ## What it is `fabric-indexer` is a local-first [Model Context Protocol](https://modelcontextprotocol.io) server that parses your source tree into a typed graph of modules, functions, types, tests, dependencies, contracts, and tech debt. It exposes that graph to any MCP-compatible AI client (Claude Code, Cursor, Codex, Claude Desktop) as structural-query tools. The graph lives in an embedded local store on your machine, persists across sessions, and is incrementally maintained via a file watcher. ## Quickstart ```bash npx @cognisos/fabric-mcp ``` Your AI client launches the server for you. See the integration pages for client-specific config: - [Claude Code](/docs/integrations/claude-code) - [Cursor](/docs/integrations/cursor) Once configured, ask your client to run `fabric_index` on your project, then `fabric_status`. ## What it indexes 13 languages with tree-sitter parsing. Modules, functions, types, interfaces, tests, and dependencies as graph nodes; structural edges (`Contains`, `Imports`, `Tests`, `Implements`, `Extends`, `Exports`) between them. - **Full call-graph resolution** — TypeScript, Python, Go, Rust, Java - **Structural extraction** — C, C++, C#, Ruby, PHP, Swift, Kotlin, Scala - **Regex fallback** — Shell, Lua, R, Perl, and others `fabric_index` respects `.gitignore` and `.fabricignore`. For the full tool surface, see the [MCP reference](/docs/mcp). ## Performance Median **73 K LoC/sec** with **< 70 MB peak RSS**, measured cold-cache against production OSS repos on M-series macOS. ## Status Public beta. Pin to a version tag (`@cognisos/fabric-mcp@`) for production use. See the [changelog](/docs/changelog/fabric-mcp). ## License MIT. --- # /docs/mcp # @cognisos/fabric-mcp Fabric MCP server for Claude Code, Cursor, Windsurf, and any other client that speaks the Model Context Protocol. Builds a local knowledge graph of your codebase and exposes it as tools. ## Tools - **Code intelligence** (9 tools): `fabric_index`, `fabric_reindex`, `fabric_query`, `fabric_slice`, `fabric_impact`, `fabric_contracts`, `fabric_staleness`, `fabric_recent_changes`, `fabric_explain` — tree-sitter-parsed graph over your codebase. - **Governance & context** (4 tools): `fabric_debt`, `fabric_context_gen`, `fabric_govern`, `fabric_govern_add` — team rules, tech-debt tracking, auto-generated `CLAUDE.md` / `.cursorrules` / `copilot-instructions.md`. - **Cloud sync** (3 tools): `fabric_sync_now`, `fabric_list_snapshots`, `fabric_pull` — knowledge graph backup, listing, and cross-device restore. - **Status** (1 tool): `fabric_status` — indexer health + per-type node/edge counts. ## Install (one command) ```bash npx -y @cognisos/fabric-mcp setup ``` That command opens your browser, signs you in at cognisos.ai, stores an API key in your OS keychain, and registers Fabric as a local stdio MCP in Claude Code. Restart Claude Code, type `/mcp`, and the tools are available. ## How it works - **Indexer tools** spawn a local Rust binary that reads your code, parses it with tree-sitter, and builds a local knowledge graph at `~/.fabric/index`. Your code never leaves your machine. - **Cloud sync** uploads snapshots of your local store (graph only, no source) to your Fabric account for disaster recovery and cross-device restore. - **API key** is stored in your OS keychain (macOS Keychain, GNOME Keyring, Windows Credential Manager) via `@napi-rs/keyring`. Never written to disk in plaintext. ## Platforms | Platform | Supported | |---|---| | macOS Apple Silicon (darwin-arm64) | ✅ | | macOS Intel (darwin-x64) | ✅ | | Linux x64 | ✅ | | Linux ARM64 | ✅ | | Windows x64 | ✅ | ## Subcommands ```bash npx @cognisos/fabric-mcp # run the MCP server in stdio mode (what Claude Code spawns) npx @cognisos/fabric-mcp setup # one-time install: OAuth + keychain + claude mcp add npx @cognisos/fabric-mcp login # re-auth (opens browser again, updates keychain) npx @cognisos/fabric-mcp logout # wipe keychain entry npx @cognisos/fabric-mcp doctor # environment health check ``` ## Manual install (for CI / headless) If `setup` can't open a browser (SSH, tmux, container): ```bash # 1. Get an API key from https://cognisos.ai/dashboard → Developer # 2. Register the MCP with the key passed as an env var claude mcp add fabric -s user \ -e FABRIC_API_KEY=lim_xxxxxxxxxxxxx \ -- npx -y @cognisos/fabric-mcp ``` ## License Proprietary — © Cognisos, Inc. --- # /docs/mcp/tools ## MCP Tool Reference Fabric exposes the following tools over the Model Context Protocol (MCP). Each tool page includes the full description, input schema, and required fields. | Tool | Description | | ---- | ----------- | | [fabric_context_gen](/docs/mcp/tools/fabric_context_gen) | Auto-generate context files from the indexed knowledge graph. | | [fabric_contracts](/docs/mcp/tools/fabric_contracts) | List every exported symbol in the indexed graph, ranked by inbound fan-in (CALLS edge count). | | [fabric_debt](/docs/mcp/tools/fabric_debt) | Inventory TODO/FIXME/HACK/@deprecated markers with graph context — each debt node surfaces alongside its direct incoming edges so you can see what calls or tests the debt-bearing code. | | [fabric_explain](/docs/mcp/tools/fabric_explain) | Full structural summary of a single symbol: name, node type, file, callers, callees, dependencies, tests covering it, API contracts it satisfies, governance rules that apply, and tech debt it carries. | | [fabric_govern](/docs/mcp/tools/fabric_govern) | Check proposed code against governance rules. | | [fabric_govern_add](/docs/mcp/tools/fabric_govern_add) | Add a governance rule. | | [fabric_impact](/docs/mcp/tools/fabric_impact) | Call before refactoring to see "what breaks if I change this?" Reverse-walks call, import, test, and contract edges to surface every node that would be affected — with a risk label (LOW/MEDIUM/HIGH), the specific API contracts that would break, and tests that may go stale. | | [fabric_index](/docs/mcp/tools/fabric_index) | Index a codebase directory. | | [fabric_list_snapshots](/docs/mcp/tools/fabric_list_snapshots) | List cloud snapshots for a repo. | | [fabric_pull](/docs/mcp/tools/fabric_pull) | Restore a snapshot from the Fabric cloud into the local index. | | [fabric_query](/docs/mcp/tools/fabric_query) | Ask "what symbols match this name and what do they connect to?" Matches nodes by name (case-insensitive substring + optional Damerau–Levenshtein fuzzy), then walks edges up to `depth` hops. | | [fabric_recent_changes](/docs/mcp/tools/fabric_recent_changes) | Files sorted by recency — discover what changed most recently without enumerating paths first. | | [fabric_reindex](/docs/mcp/tools/fabric_reindex) | Call after editing files to keep the graph current without reparsing the whole repo. | | [fabric_slice](/docs/mcp/tools/fabric_slice) | Build an LLM-ready context window for a target symbol, bounded by a real token budget (cl100k_base BPE, the same tokenizer GPT-4 and Claude use). | | [fabric_staleness](/docs/mcp/tools/fabric_staleness) | Does the indexed state match disk? | | [fabric_status](/docs/mcp/tools/fabric_status) | Call this first to orient on the local code index: node/edge counts, governance rule count, store size, and per-type breakdown. | | [fabric_sync_now](/docs/mcp/tools/fabric_sync_now) | Upload a snapshot of the local knowledge graph to the Fabric cloud for disaster recovery. | | [fabric_text_search](/docs/mcp/tools/fabric_text_search) | Graph-aware regex grep. | | [fabric_visualize](/docs/mcp/tools/fabric_visualize) | Open an interactive 3D code graph of the indexed codebase in your default browser. | --- # /docs/integrations/claude-code # Claude Code Add Fabric to [Claude Code](https://claude.com/claude-code) so the agent has persistent memory and code-graph tools across sessions. ## Prerequisites Node.js 20+, Claude Code 2.x, and a repo you want indexed. ## Install ```bash npx -y @cognisos/fabric-mcp setup ``` The setup wizard registers Fabric in `~/.claude/mcp_settings.json`. ## Verify Restart Claude Code, then run `/mcp` — you should see `fabric` listed as **connected**. ## Try it ```text > Index this repo with fabric_index, then use fabric_explain to tell me what calls `parseDoc`. ``` Claude answers from the local graph — no source upload. ## See also - [Cursor](/docs/integrations/cursor) — same Fabric, different host --- # /docs/integrations/cursor # Cursor Add Fabric MCP to [Cursor](https://cursor.com) so its agent shares the same code-graph and memory layer as Claude Code. ## Prerequisites Node.js 20+, Cursor 0.43+, and a repo you want indexed. ## Configure Add Fabric to `~/.cursor/mcp.json` (global) or `/.cursor/mcp.json` (per project): ```json { "mcpServers": { "fabric": { "command": "npx", "args": ["-y", "@cognisos/fabric-mcp"], "env": { "FABRIC_REPO_ROOT": "${workspaceFolder}" } } } } ``` ## Verify Restart Cursor. Open chat, click the tool icon, and confirm `fabric.*` tools are listed. ```text > @fabric find every reference to `useDocsToc` ``` ## See also - [Claude Code](/docs/integrations/claude-code) — same install, different host --- # /docs/connectors/obsidian # Obsidian Connector **Two-way sync between an Obsidian vault and the Fabric graph — local, file-watcher driven, no plugin install.** ## What it does Points the self-hosted Fabric daemon at one or more Obsidian vault folders. The connector: - **Ingests** every `.md` note as a Fabric node — YAML frontmatter, body, `#tags`, and `[[wikilinks]]` extracted automatically. - **Stays current** via a filesystem watcher (changes flow into Fabric within ~200 ms), with a 60 s interval poll as a fallback. - **Emits back** when Fabric writes a note — atomic tempfile + rename, conflict-safe sidecar on concurrent external edits. - **Tracks renames** by content-derived stable IDs (frontmatter `id:` if present, else SHA-256 of body) so wikilink targets survive moves. No Obsidian plugin, no auth flow. The daemon reads the vault directory directly. ## Prerequisites - Self-hosted Fabric daemon (`rsc-api-server`) running on the same host as the vault — local-host only in v1. - Obsidian vault is a plain directory of `.md` files (no encrypted/iCloud-locked vaults). - POSIX filesystem for atomic emit. macOS and Linux are first-class; Windows is best-effort (non-atomic rename across volumes). ## Configure Set `OBSIDIAN_VAULTS` before starting the daemon: ```bash export OBSIDIAN_VAULTS="research=/Users/me/Obsidian/Research,journal=/Users/me/Obsidian/Journal" ``` The value is a comma-separated list of `vault_id=path` pairs. The `vault_id` is a stable handle you choose — it appears in install IDs and the `provenance.source_id` of every ingested record. Optional: | Variable | Default | Purpose | | -------------------- | ---------------------------------------- | ---------------------------------------------------------------------------------------- | | `OBSIDIAN_TENANT_ID` | `0b51d1a4-0b51-4d1a-b51d-1a40b51d1a40` | Tenant UUID for ingested records. Override when running multi-tenant. | | `OBSIDIAN_USER_ID` | (per-vault derived) | Pin every install to one user profile. **Single-vault only** — rejects 2+ vaults. | Bad UUIDs in either variable fail boot with a clear error. Bad paths in `OBSIDIAN_VAULTS` skip just that install and log a warning; other vaults still register. ## Verify Start the daemon and check the boot log for one line per vault: ```text INFO Obsidian per-install scheduler started (watcher + interval) install_id=… vault_id=research ``` Then drop a note in the vault — it should appear in the graph within a few seconds. Tags become tag edges, wikilinks become link edges, frontmatter fields land on the node. ## What gets ingested For each `.md` file: - **Frontmatter** — full YAML mapping kept on the node. Top-level sequences or scalars are rejected (the daemon logs and dead-letters the note rather than silently dropping the frontmatter). - **Tags** — both YAML `tags:` and inline `#hashtag` syntax. Tags inside fenced ``` ``` ``` or inline `` `code` `` are ignored. - **Wikilinks** — `[[Target]]` and `[[Target|alias]]` become edges. Pipe-aliases are stripped; the target is canonical. Wikilinks inside code blocks are ignored. - **Body** — the markdown after the frontmatter, verbatim. Hidden directories (`.obsidian/`, `.git/`, `.trash/`) are skipped. ## Emit behavior When Fabric writes a note back to the vault: - The write goes to a tempfile in the same directory, then a `rename(2)` swaps it onto the canonical path — readers never see a partial file. - If the canonical file's mtime has advanced past Fabric's last-seen value (something else changed it), the emit writes to `.conflict--.md` instead. **No clobber.** Operators can grep `tracing` warns for `emit_conflict` to find them. - A `rel_path` containing `..` or absolute path is rejected up front. Dead-letter persistence for emit conflicts is deferred to v1.5 — the sidecar file IS the recovery surface in v1. ## Replay a backfill If the cursor gets out of sync (e.g. you restored the vault from backup), drive a fresh backfill without restarting the daemon: ```bash curl -X POST "http://localhost:3000/api/v1/connectors/installs//backfill" ``` The endpoint refuses if the install isn't `active`. ## Limitations - **Local-host only.** Vault and daemon share a filesystem. Multi-tenant SaaS needs a per-tenant agent (deferred — see [issue #233](https://github.com/cognisos-ai/Prod_Fabric/issues/233)). - **Last-writer-wins** on the canonical path; concurrent writes during the brief read-mtime → rename window can be overwritten silently. Acceptable for a single-user local vault. - **No `PATCH /config`** for Obsidian installs — vault path is fixed at boot. Change `OBSIDIAN_VAULTS` and restart. ## See also - [Fabric Indexer](/docs/fabric-indexer) — the graph engine that stores ingested notes - [MCP Reference](/docs/mcp) — query the indexed vault from your AI client --- # /docs/connectors/slack # Slack Connector **Two-way sync between a Slack workspace and the Fabric graph — channel/DM messages, threads, and reactions. Emits `chat.postMessage` and `reactions.add` back.** ## What it does - **Ingests** every message + reaction in channels the bot is invited to, plus DMs and group DMs. - **Backfills** the workspace history via `conversations.list` → `history` → `replies`, page by page. - **Emits back** when Fabric posts a message or adds a reaction — atomic dedup on echo via a two-tier filter so the bot's own writes never feed back into the graph. - **Survives reinstall** — uninstall is transactional: workspace claim, credentials, status all flip atomically; reinstalling the same workspace afterward just works. One install per Slack workspace **across the entire deployment** — Slack's single-app identity model means two tenants installing the same workspace would share bot identity at Slack's side, so we refuse it. See [Conflicts](#conflicts) below. ## Prerequisites - Slack admin who can install apps in the target workspace. - A reachable deployment URL for OAuth — during install, Slack redirects the installer's browser to your `redirect_uri` callback, so the deployment must be reachable from the installer (typically a public URL). - For **HTTP transport**: Slack must also be able to POST Events API payloads to your `/api/v1/connectors/slack/events` endpoint — inbound from Slack's network. - For **Socket Mode**: no inbound traffic needed. The daemon opens an outbound WSS connection to Slack; OAuth is still subject to the redirect rule above. - `FABRIC_MASTER_KEY` set on the daemon — the Slack bot token is encrypted at rest with this key. ## Create the Slack app 1. Go to → **Create New App** → **From a manifest**. 2. Paste the contents of [`slack-app-manifest.yml`](https://github.com/cognisos-ai/Prod_Fabric/blob/main/slack-app-manifest.yml) from this repo (replace `your-host` with your deployment hostname). 3. **Save** → Slack provisions the app. Visit the app's **Basic Information** page and copy: - **Signing Secret** → daemon env `SLACK_SIGNING_SECRET` (HTTP transport). - **App-Level Token** (generate one with `connections:write`) → daemon env `SLACK_APP_TOKEN` (Socket Mode). ## Pick a transport The connector supports **two** mutually-exclusive transports — pick one per deployment: ### HTTP Events API (default) Slack POSTs events to your deployment over the public internet. Easier to debug (events flow through your normal HTTP stack) and matches how production SaaS apps usually deploy. ```bash export SLACK_CLIENT_ID=… # from app's "Basic Information" export SLACK_CLIENT_SECRET=… export SLACK_SIGNING_SECRET=… export SLACK_APP_TIER=non_marketplace # or "marketplace" if approved # SLACK_USE_SOCKET_MODE unset / false ``` ### Socket Mode Daemon opens an outbound WSS connection to Slack — no inbound HTTP needed, useful behind a NAT/firewall. ```bash export SLACK_CLIENT_ID=… export SLACK_CLIENT_SECRET=… export SLACK_APP_TOKEN=xapp-… # from app's "App-Level Tokens" export SLACK_APP_TIER=non_marketplace export SLACK_USE_SOCKET_MODE=true # SLACK_SIGNING_SECRET unset — HTTP events route won't be mounted ``` Also flip **Settings → Socket Mode → Enable** in the Slack app config. Slack enforces mutual exclusion at its side too — having both transports armed would either double-deliver events or silently break depending on the app config order. The daemon reconnects with exponential backoff (1s → 60s, full jitter) if the WSS connection drops. ## Install into a workspace Once env is set and the daemon is running, walk the OAuth flow per tenant: ```bash # 1. Mint an install URL — the response carries `authorize_url`. curl -sX POST \ "http://localhost:3000/api/v1/connectors/slack/oauth/start" \ -H "Authorization: Bearer $FABRIC_API_KEY" \ -d '{"redirect_uri": "https://your-host.example.com/api/v1/connectors/oauth/callback"}' # 2. Open the returned authorize_url in a browser; Slack walks you through # install + scope consent and redirects back to the callback URL above. # The callback finalizes the install and writes credentials. # 3. Verify the install: curl -s "http://localhost:3000/api/v1/connectors/installs?connector_id=slack" \ -H "Authorization: Bearer $FABRIC_API_KEY" ``` The connector then: - Encrypts the bot token under `FABRIC_MASTER_KEY` and stores it in `connector_credentials`. - Inserts a `slack_workspace_installs` row claiming the workspace globally. - Starts the backfill orchestrator (paginated `conversations.list` → `history` → `replies`). - Begins receiving live events on whichever transport is active. ### Conflicts A second install of the **same** Slack workspace — even from a different tenant — fails with HTTP **409** at the OAuth callback. The Slack-side bot identity is shared across that one app, so two tenants would step on each other; the conflict is enforced atomically by the `slack_workspace_installs.team_id` primary key. ### Reinstall after uninstall Uninstall (`DELETE /api/v1/connectors/installs/`) is transactional: the workspace claim row, credentials revocation, and install status flip happen in one Postgres transaction. After uninstall, the next install of the same workspace **just works** — no 409, no manual cleanup. (Pre-v1 this required operator intervention because the `slack_workspace_installs` row was left orphaned.) ## Emit a message ```bash curl -sX POST \ "http://localhost:3000/api/v1/connectors/installs//emit" \ -H "Authorization: Bearer $FABRIC_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "kind": "slack.message", "body": { "channel_id": "C0123456789", "text": "Hello from Fabric" } }' ``` Response carries `EmitReceipt.provider_id` = Slack's `ts` (use it to reply in-thread later). Emitted messages are **not** ingested back into the graph — the two-tier self-emit filter drops them on the inbound side. ### Rate limits Slack throttles `chat.postMessage` at ~1 msg/sec **per channel** on top of the workspace-wide write budget. The emit route charges both buckets atomically — a throttled per-channel post returns **429** with a `Retry-After` header **without** consuming a workspace-wide token. Burst-posting to unrelated channels still works at the workspace cap (~100/min). ### React to a message ```bash curl -sX POST \ "http://localhost:3000/api/v1/connectors/installs//emit" \ -H "Authorization: Bearer $FABRIC_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "kind": "slack.reaction", "body": { "channel_id": "C0123456789", "timestamp": "1736000000.000001", "name": "thumbsup" } }' ``` ## Uninstall ```bash curl -X DELETE \ "http://localhost:3000/api/v1/connectors/installs/" \ -H "Authorization: Bearer $FABRIC_API_KEY" ``` Effects: - Slack token revoked at Slack (`auth.revoke`). - Local credentials marked revoked, install status flipped to `uninstalled`, workspace claim DELETEd — all in one transaction. - Backfill worker stops ingesting (it gates on `status == "active"`). If the Slack `auth.revoke` call succeeds but the local transaction then fails, the Slack-side token is dead while the install row stays `active`. Retrying uninstall converges — `auth.revoke` is idempotent at Slack's end and the local tx either commits cleanly or surfaces the same error for ops to investigate. ## Limitations - **One install per workspace per deployment.** Cross-tenant workspace sharing is rejected with 409 — see [Conflicts](#conflicts). - **Files are referenced, not downloaded.** A message with attachments lands in Fabric with `payload.files = [{ file_id, name, mimetype, size, permalink }]` — the connector does not fetch file bytes in v1. - **DM coverage requires scope.** If `mpim:history` is missing from the install consent, MPIMs silently drop out of backfill; the connector logs the missing scope at install time. ## See also - [All connectors](/docs#connectors) — other available connector integrations. - [`slack-app-manifest.yml`](https://github.com/cognisos-ai/Prod_Fabric/blob/main/slack-app-manifest.yml) — Slack app manifest to paste into the App Directory. - [Issue #231](https://github.com/cognisos-ai/Prod_Fabric/issues/231) — the design issue for this connector. --- # /docs/changelog/fabric-mcp # fabric-mcp Changelog All notable changes to `@cognisos/fabric-mcp` are documented here. ## Unreleased - Initial dedicated changelog file. Pre-release history is recorded in the project root `CHANGELOG.md` and in git history of `packages/fabric-mcp`. ## 0.2.0-rc.22 - Latest release-candidate. Full per-rc notes live in the root [Cognisos Changelog](../../CHANGELOG.md). For per-commit detail run: ```bash git log --oneline -- packages/fabric-mcp ```