Fabric MCP Gateway
Use Fabric as a unified MCP server from Claude Desktop, Cursor, VS Code, and any MCP-compatible client. Access all your projects, documents, libraries, workflows, and connected tools through a single endpoint.
The Fabric MCP Gateway turns your entire Fabric instance into a single MCP server. Instead of configuring each tool separately in every AI client, you connect once to Fabric and get access to everything — your projects, documents, libraries, workflows, and all your connected MCP servers — through one URL.
Why Use the Gateway?
| Without Gateway | With Gateway |
|---|---|
| Configure each MCP server separately in each client | One URL in your client config |
| Manage API keys per tool per client | Fabric manages all credentials centrally |
| OAuth flows broken in most clients | Fabric handles OAuth + token refresh |
| No visibility across project artifacts | Full access to projects, docs, libraries |
| Credentials scattered in config files | Credentials encrypted in Fabric, never leave the server |
| No usage controls | Time-limited, human-approved runtime authority |
Quick Setup
Sign in from your coding agent
Connect from the project you want the agent to work on. The project has its own
gateway URL, https://your-fabric.com/api/mcp-gateway/projects/<project-id>:
add it to your tool with no key, then sign in from the tool. The first request
is answered 401 with a WWW-Authenticate challenge naming that URL's sign-in
metadata, the tool opens your browser, and you approve the tool once. You are
never asked to choose an organization or a project: the URL already says which.
The project's Connect your agent dialog shows the same entries with your
deployment's URL and the project filled in.
Run it in the project's folder:
claude mcp add --scope local --transport http fabric-<end-of-project-id> https://your-fabric.com/api/mcp-gateway/projects/<project-id>The server is named fabric- and the last six letters and digits of the project's id, the same name Codex and fabric instructions init use, so a server added from the dialog is the one init finds. A server you already named fabric that points at this project keeps working and is not duplicated. Then run /mcp in Claude Code and choose Authenticate.
Use Add to VS Code in the Connect dialog, or add the server to your MCP
configuration with no headers. VS Code installs the server for every folder
it opens, so the Connect dialog names it after the project and the end of
its id (fabric-<project-name>-<id>), and two projects do not collide, even
when they share a name:
{
"servers": {
"fabric-example-project-a1b2c3": {
"type": "http",
"url": "https://your-fabric.com/api/mcp-gateway/projects/<project-id>"
}
}
}VS Code asks you to sign in the first time the server starts.
Use Add to Cursor in the Connect dialog, or add a server with only the
project's URL, https://your-fabric.com/api/mcp-gateway/projects/<project-id>.
Cursor installs it for every folder it opens, so the Connect dialog names
it fabric-<project-name>-<id>, with the end of the project's id so two
projects of one name stay apart. Cursor opens the browser to sign in.
Codex keeps one list of servers for every project on the machine, so the
Connect dialog names this project's server fabric- and the last six letters
and digits of its id (fabric-ewrite for project-checkout-rewrite), and
fabric instructions init registers the same name. Two projects then stay
apart in Codex's list:
codex mcp add fabric-<end-of-project-id> --url https://your-fabric.com/api/mcp-gateway/projects/<project-id>The command opens your browser to sign in and finishes once you approve. If
the sign-in did not finish, run codex mcp login fabric-<end-of-project-id>.
Any client that implements MCP authorization works from the server entry
alone. It contains no credential, so a project can commit it as .mcp.json:
{
"mcpServers": {
"fabric": {
"type": "http",
"url": "https://your-fabric.com/api/mcp-gateway/projects/<project-id>"
}
}
}What you approve is the same for every tool, and it is for that project alone: read the project, its features and bugs and their decisions and revisions, its documents, its context, and search its knowledge; read its coding instructions; and suggest changes to them, held for review and never published by the agent. The consent page names the project and its organization, and what you approve is exactly what the page showed: if the connection changes while the page is open, nothing is approved and the tool has to start the sign-in again. The agent works on that project and nothing else in Fabric: the gateway offers it the project's tools only, and no organization-wide tool and no connected server's. A token issued for one project works only at that project's URL, so it cannot be pointed at another project or at the organization-wide gateway. If you can no longer open the project, the agent's next request is refused.
The tool stays signed in until you revoke it under Account → Connected agents, which lists one row per project and organization grant and signs the tool out of it at its next request. To work on another project, connect from that project.
A client must send the project's URL as the resource of its authorization, as
the MCP authorization specification requires. The sign-in metadata at
/.well-known/oauth-protected-resource/api/mcp-gateway/projects/<project-id>
names exactly that URL, and a project URL never falls back to an
organization-wide sign-in.
The organization-wide gateway
Connections made before projects had their own URL, at
https://your-fabric.com/api/mcp-gateway, keep working unchanged: they are
signed in for one organization, which is chosen on the sign-in page when you
belong to several, and reach all of its projects. The Connect dialog no longer
offers it. A token issued for one project is refused there, and the refusal
names the project's URL to use instead.
The sign-in page shows the organization it will use, and what you approve is
exactly what the page showed: if the organization the server would use differs,
nothing is approved and the tool has to start the sign-in again. mcp:read,
the scope behind "Read this organization's projects and context", covers that
organization's projects, features and bugs with their decisions and revisions,
documents, project context, coding instructions, document libraries,
workflows and frames, plus the list of connected servers. It does not cover your personal AI
chats, and the identity tools list only the organization you signed in for.
An OAuth sign-in never holds mcp:write, so it cannot call a connected server's
tools unless that server declares them read-only.
Or use an API key (CI and headless)
Where nobody can approve a sign-in in a browser, authenticate with a key.
1. Create an API Key
Go to Settings → API Keys and create a new key. Copy the key — it starts with fab_.
For organization context, create the key from your organization settings page.
A new organization key starts with MCP Read only, so it can read and cannot change anything until you tick a write scope. MCP Write (mcp:write) grants every write tool, including workflows and pages. For an agent that should do one thing, tick the narrow scope instead, such as workflows:write, frames:write or features:write. Keys you created earlier keep the scopes they have.
2. Configure Your Client
Edit your claude_desktop_config.json:
{
"mcpServers": {
"fabric": {
"url": "https://your-fabric.com/api/mcp-gateway",
"headers": {
"Authorization": "Bearer fab_xxxx_xxxxxxxxxx"
}
}
}
}Go to Settings → MCP Servers → Add Server:
- Name: Fabric
- URL:
https://your-fabric.com/api/mcp-gateway - Headers:
Authorization: Bearer fab_xxxx_xxxxxxxxxx
Add to your MCP configuration:
{
"servers": {
"fabric": {
"url": "https://your-fabric.com/api/mcp-gateway",
"headers": {
"Authorization": "Bearer fab_xxxx_xxxxxxxxxx"
}
}
}
}Any MCP-compatible client can connect using the Streamable HTTP transport:
Endpoint: POST https://your-fabric.com/api/mcp-gateway
Auth: Authorization: Bearer fab_xxxx_xxxxxxxxxx
Protocol: MCP 2025-03-26 (Streamable HTTP)Health check: GET /api/mcp-gateway returns server info (no auth needed).
3. Organization Context (Optional)
A personal API key (fab_) belongs to you, not to an organization. If you belong to
more than one, name the organization the connection should start in with the
X-Organization-Id header:
{
"headers": {
"Authorization": "Bearer fab_xxxx_xxxxxxxxxx",
"X-Organization-Id": "your-org-id"
}
}Fabric checks that you are a member of that organization and refuses the request
otherwise. The header chooses where the connection starts. It does not confine the
key: a personal key can still move between your organizations with
fabric_switch_organization, and a browser session follows the organization you are
working in. A connection that must stay in one organization uses an organization API
key (org_) or an OAuth sign-in. Both are fixed to one organization when they are
issued, ignore this header, and cannot switch.
Coding Instructions
A project's Coding Instructions tab publishes one version of its skills, agents, rules, and setup files, and serves them to a connected agent over the six tools below. The files come from one of two places: a folder uploaded in Fabric, or a branch of the project's repository that Fabric keeps in sync.
How the instructions and agent memory move between your repository, Fabric and the coding tool, and what each project can configure:
Connect a tool from the tab
Open the tab and choose Connect your agent, beside Fabric MCP in the status strip under the heading. The dialog has one tab for each of five tools, Claude Code, Codex, VS Code, Cursor and Other, and one action for each. You approve the connection once in your browser: there is no key to create or paste.
| Tab | What you do | What it sets up |
|---|---|---|
| Claude Code and Codex | Choose where the repository is, copy the one line shown and run it (see below) | The Fabric CLI your deployment serves, run straight from its URL, so nothing is installed first. It signs in through your browser and, by default, installs the project's instructions as a plugin for that tool, with no clone (see Instructions as a plugin) |
| VS Code and Cursor | Choose Add to VS Code or Add to Cursor | The gateway as an MCP server. There is no hook; the agent compares its checkout with the published version at each session start |
| Other | Copy the server entry (it holds no credential) | Any MCP client that supports sign-in asks you to approve it the first time it connects |
The setup line. For Claude Code and Codex the dialog asks Where's the repository? and
shows one line per answer, with <tool> the tab you are on (claude-code or codex) and
<tgz> your deployment's tarball, https://your-fabric.com/cli/fabric-<version>-<build>.tgz.
You need Node.js 22 or later and nothing else: npx comes with it.
- No clone (recommended).
npx -y <tgz> instructions init --project <id> --tool <tool> --plugin. Run it in the folder your agent works in, usually your product's own checkout. Nothing is cloned: the instructions arrive as a plugin built from what the project publishes, and the project's memory folders are mirrored as plain files. A plaininitdoes the same, unless you pass--cloneor run it inside a clone of the instructions' repository. - Already cloned.
npx -y <tgz> instructions init --tool <tool>. Run it in the top folder of your clone of the project's repository, or, when the project's instructions live in a folder of that repository, in that folder (<rootPath>/inside the clone). Not in any other folder: the CLI refuses and says where to go. It finds the project from the checkout's git remote. - Clone it for me. One command,
npx -y <tgz> instructions init --project <id> --tool <tool> --clone <dir>. It makes<dir>(a folder that does not exist yet, or is empty), clones the project's repository into it with your own git credentials, and finishes setting up inside it, in the project's own folder when the instructions live in a subfolder. Open your coding tool in that folder afterwards. - An upload project has no repository to find it from, so its line names the project:
npx -y <tgz> instructions init --project <id> --tool <tool>. Run it in the folder your agent works in.
Each is one command with no && and no cd, so it runs in Windows PowerShell 5.1 as well as
bash. The dialog adds --base-url https://your-fabric.com, the address you are on, unless the
CLI was built for exactly that address (see below), so the line works on any deployment with
nothing configured. After it runs, the CLI opens your browser to approve the connection once.
Run by an agent with no terminal, it prints the URL for you to open and waits up to five
minutes for you to finish. Under CI it starts no browser sign-in at all: it stops and names
the sign-in command, so a CI run signs in with an API key (below).
Your checkout uses your own git credentials; Fabric never hands out repository credentials.
If git cannot sign in to the repository yet, the dialog's
Git can't sign in to the repository section shows how to sign git in for the provider:
gh auth login for GitHub, glab auth login for GitLab, and for Azure DevOps one
git ls-remote of the repository, which Git Credential Manager signs in the first time.
The CLI is the one built from your deployment's own source, listed with its version, its
integrity hash, the oldest version the deployment accepts and the address it was built for at
/.well-known/fabric-cli.json. The tarball's name carries the build, ten hex digits of the
CLI's own hash: the version changes only on a release, npx keeps running whatever it first
fetched from a URL, and a build that changed has to be at a URL it has not seen. A name from an
older deployment is answered with a redirect to the current one. Baking the address in is
optional: a build that knows where it will be served (FABRIC_CLI_ORIGIN,
NEXT_PUBLIC_SITE_URL or APP_URL set when it is built) records it, and a build that does
not, such as a preview or staging deployment whose address is not known until it runs,
records none. Either way --base-url wins over what is baked in, so a CLI built for another
address is harmless. A deployment that serves no CLI says so in the dialog and leaves the MCP
entries. The package on the npm registry is not a way in: its release predates --base-url.
For CI and headless machines, the dialog's CI or headless? Use an API key section
mints a key that carries mcp:read, instructions:read and instructions:write and
expires after 90 days. It can read the instructions and suggest a change, but it cannot
publish one.
Instructions as a plugin
With No clone, the project's coding instructions reach your coding tool as a plugin that Fabric builds from the published version. No one clones the instructions' repository, and the next session picks up whatever the project published last.
| From the instructions | In the plugin |
|---|---|
skills/<name>/SKILL.md (and the rest of skills/<name>/) | Skills, named fabric-<id>:<name> |
agents/<name>.md | Agents |
commands/** | Commands |
CLAUDE.md, else AGENTS.md | Handed to every session as context when it starts |
| (added by Fabric) | The project's MCP server (named fabric in Claude Code), fabric-instruction-access for finding and reading instruction files, and fabric-living-memory for specs and notes |
Standard layout. Keep the instructions in these folders, at the root of the instructions
or under .claude/ (.claude/skills/<name>/SKILL.md works the same as
skills/<name>/SKILL.md; folder names are matched without regard to case):
| Path | What it is |
|---|---|
skills/<name>/SKILL.md | A skill: a folder per skill |
agents/<name>.md | An agent: one markdown file directly in agents/ |
commands/** | Commands |
rules/**/*.md | Project rules. Fabric delivers them automatically into .claude/rules/fabric-<id>/ for Claude Code and AGENTS.override.md for Codex, and keeps passive reference copies in the plugin |
CLAUDE.md or AGENTS.md | The memory file every session starts with |
Rules. Claude Code loads the markdown files of .claude/rules/ in your project. A rule
without paths: frontmatter loads at the start of every session like CLAUDE.md; a rule with
paths: loads when Claude works on a matching file. Fabric copies each rule byte for byte,
frontmatter included, into .claude/rules/fabric-<id>/ at the start of every session, and when
you run the setup, so the first session already has them. Rules you add or edit in
.claude/rules/ yourself are never touched. A file in that folder that you edit is kept, and
Fabric stops updating it. Codex has no rules folder, so the rules are added after the memory
file in AGENTS.override.md, each under its own heading; a rule with paths: is marked as
applying when working on those files. Both are limited (64 files and 256 KiB for Claude Code,
about 24,000 characters for Codex); the setup says when rules were left out of automatic
delivery. The plugin keeps the rule text as passive reference files, indexed by original
path in the fabric-instruction-access catalog. The skill directs the agent to inspect
rule scopes and read every unscoped rule and every matching scoped rule before affected
work, including files beyond automatic delivery limits.
Read referenced files on demand. Every session receives a pointer to the generated
fabric-instruction-access skill, even when there is no root CLAUDE.md or AGENTS.md.
Its catalog.json lists all instruction paths and available local copies. Search relevant
entries instead of loading the full catalog into context. Files outside plugin folders,
such as standalone scripts, settings and knowledge files, remain listed. The agent uses
fabric_list_project_instructions to find paths and fabric_get_project_instruction to
read their original text through the project's authorized Fabric MCP connection. For
example, @MAIN.md in the instructions' root CLAUDE.md refers to that instruction tree's
MAIN.md, even when the project checkout has a different file with the same name.
The skill explains how to retain the repository generation and commit across list and text
pages, and how to detect changes to an uploaded publication. It directs the agent to read
each relevant file until nextOffset is null. Access denials, missing files, binary files and size refusals
remain explicit limits. Reading a script does not execute it or materialize its dependencies;
a workflow requiring local execution needs an authorized checkout. The Coding Instructions
tab and CLI still report which files lack a native plugin slot.
After the sign-in, the dialog has one more step for the tool you picked.
Claude Code: Turn on the plugin in Claude Code. The setup registers the plugin for the folder and then asks you to approve the plugin's command once. Claude Code shows the exact command and takes the answer only in your own terminal, never from inside a coding session. If the setup could not ask you, the dialog shows the command to run in the folder:
claude plugin install fabric-<id>@fabric-<id> --scope local<id> is the last eight letters and digits of the project's id, and fabric-<id> is the
plugin's name. Then start a new session. To let the agent call Fabric's tools too, run /mcp
in Claude Code, pick the server named plugin:fabric-<id>:fabric (the plugin's MCP server is
named fabric, and Claude Code lists it with the built-in servers because the plugin brings
it) and choose Authenticate. Your browser opens so you can approve it once. The
instructions and the memory folders do not need this sign-in, because the setup's own sign-in
covers them. A plugin set up before the server was named fabric listed it as
plugin:fabric-<id>:fabric-<id>; once its CLI copy updates, /mcp shows
plugin:fabric-<id>:fabric as Needs authentication, so choose Authenticate once more.
Codex: Turn on the plugin in Codex. The setup renders the plugin into
~/.codex/plugins/fabric-<id>, lists it in your personal marketplace
(~/.agents/plugins/marketplace.json) and installs it in Codex itself. Codex keeps its own
copy of an installed plugin, so at each session start the hook installs it again whenever
that copy is older than what the project publishes; Codex loads the new copy from the next
session. If the install did not work, the dialog shows the command to run:
codex plugin add fabric-<id>@personalpersonal is the name of the marketplace file; if yours is named differently, the setup
prints the line with your name. Trust the project hook once with /hooks. Codex has no slot
for an always-on file, so the setup also writes AGENTS.override.md in the folder, kept out
of your commits: your own AGENTS.md first, then the project's instructions. In Codex the
plugin's MCP server keeps the name fabric-<id>, because every project's Codex plugin shares
one personal marketplace.
One plugin, one project. A plugin belongs to one project on one deployment. Its name comes from the last eight letters and digits of the project's id, and its description names the project and the deployment it was built for. If a name is already taken by another project (or by the same project on another deployment), the setup refuses and says which one holds it, instead of repointing the folders already set up for that one. Remove the other setup's plugin and folder first, then run the setup again.
To set a folder up the clone way instead (a checkout of the instructions and a session hook),
pass --no-plugin.
Save specs and notes to the repository
While they work, agents write specs, task breakdowns and notes. With write folders, those files reach the project's repository through Fabric and come back as Living Memory for the next agent, without anyone cloning that repository or running git.
- A project admin names the folders once. In the Context tab, open the menu beside
Sync now in the Living Memory section and choose Write folders (see
Write folders). Each folder lands its files as a
pull request (the default) or, if an admin turns it on, as a direct commit. A folder is
also either Index for AI search (the default) or Store only. Store only keeps
its files in Living Memory and readable by agents, but out of AI search, keyword search
and summaries: pick it for a folder that will hold thousands of task files, so they do not
crowd out everything else. In the project's Context tab, a Store only folder's files show
as Ready with a "Store only" label and are not counted among the sources still processing.
fabric_memory_targetsreports each folder'sindexMode. - Each developer runs the setup line once (No clone). It mirrors the write folders
into the folder as plain files at their repository paths, such as
docs/ai/, kept out of your own commits. - Agents write ordinary files there. The
fabric-living-memoryskill tells the agent which folders to use.
From then on it runs by itself:
| When | What happens |
|---|---|
| A session starts | The folders' current files are brought in, so the agent sees what others wrote |
| A Claude Code turn ends | New, changed and deleted files are sent. Fabric commits them, or opens a pull request, with the repository connection's own credential |
| Codex | Codex has no end-of-turn hook, so files are sent when the next session starts |
| A few seconds later | The next sync reads committed files into Living Memory. A file in an open pull request joins Living Memory once someone merges it |
On a Push directly folder, a write that loses a race with another write that reached the
branch first is retried on the new branch tip. Only a branch that refuses the push (branch
protection, a server hook, or missing write permission) turns the write into a pull request,
with reason protected.
Tell agents where to write in your instructions. A file lands at the same path in the
repository that it has in your folder: docs/ai/specs/login/spec.md here becomes
docs/ai/specs/login/spec.md there. Skills often hard-code where they save their output:
make that path a write folder, or add a line to your instructions such as Save specs to
docs/ai/specs/<feature>/spec.md. A file outside every write folder stays on your machine.
What a write folder takes. Text files ending in .md, .markdown, .txt, .json,
.yaml or .yml, up to 2 MiB each. Other files, larger ones and links are reported and not
sent, and nothing whose name starts with a dot (a file such as .draft.md, or anything in a
folder such as .notes/) is sent. A path your own repository tracks
with git is left to git. A change of more than fifty files is sent as several writes.
Nothing you changed is overwritten. If someone else changed a file since you last
received it, your change is not written and the file stays as you left it. The CLI reports
the conflict, and you choose: fabric memory pull --accept-remote <path> takes their version
(or, if they deleted the file, removes yours), and fabric memory status --clear dismisses
the report. Here fabric is the CLI the way the setup line runs it, from the folder you set
up: npx -y <tgz> memory status, plus the setup line's --base-url if it had one.
Check that a file arrived. fabric memory status reports pull requests with their links,
and conflicts, refusals and changes not sent yet. An agent can ask too: the id of each write
the session hook sent is in .fabric/memory.lock (in its outbox), and
fabric_memory_write_status reads its outcome. fabric_memory_targets lists the folders,
their branch and the commit the last sync read.
Resume a work item. Keep one work item's specs and tasks together in
docs/ai/specs/<work-item-id>/, inside a write folder. To pick it up, an agent lists that folder
with fabric_memory_read (prefix), then reads a file by its path, a page at a time for a
large one. This needs no write scope: a coding tool's default sign-in can do it, and it works
for Store only folders too. The same files are on disk in your project folder at the same paths
when the setup line has been run, so the agent can also read them there. The plugin's
fabric-living-memory skill tells the agent so.
The agent cannot write through Fabric's tools on its own. fabric_memory_write needs
memory:write, a scope that a coding tool's sign-in never carries, so an agent's files reach
the repository through the folder and the hooks above. See
Living Memory writes for the tools and the
scope.
Example: a password reset feature, end to end
A team keeps its coding instructions in Fabric and wants every spec an agent writes to land in the repository, where the next agent (and Fabric's own AI features) will read it. The steps below follow one feature from start to finish with Claude Code.
- An admin chooses where specs go, once. In the project's Context tab, open the menu
beside Sync now in the Living Memory section, choose Write folders, then
Add a write folder:
docs/ai, landing as Pull request (or Push directly to skip review). - The team says where specs belong, once. In the project's coding instructions, the
CLAUDE.mdgets a line such as Save specs todocs/ai/specs/<feature>/spec.mdand task lists todocs/ai/specs/<feature>/tasks.md. Every session receives it from the plugin. - Each developer sets up their machine, once. In the Coding Instructions tab, choose Connect your agent, then Claude Code and No clone (recommended). Run the line it shows in the product's own checkout, approve the sign-in in the browser, then confirm the plugin in the terminal (step Turn on the plugin in Claude Code). Start a new session.
- A developer asks for the spec. In Claude Code: Write a spec for password reset. At
session start the team's skills and rules came from the plugin and the specs teammates
already wrote were brought into
docs/ai/. The agent writesdocs/ai/specs/password-reset/spec.md. When its turn ends, the hook sends the file and Fabric opens a pull request with it (or, with Push directly, commits it to the branch). - The spec becomes team memory. Once the pull request is merged (or straight after a direct commit), the next sync reads the file into Living Memory. Any agent connected to the project can now find it, and Fabric's AI features use it.
- A teammate picks it up. On another machine, the next session brings
docs/ai/specs/password-reset/spec.mdin by itself, and an agent without the local files can listdocs/ai/specs/password-reset/and read the spec withfabric_memory_read. If both people edit it before either has the other's change, nothing is overwritten: the CLI reports the conflict, andfabric memory pull --accept-remote docs/ai/specs/password-reset/spec.mdtakes the committed version. To keep your own changes, copy them aside first, take the committed version, then make your changes on top of it: the next turn sends them.
With Codex the steps are the same, except that the setup also installs the plugin in Codex (step Turn on the plugin in Codex), and files are sent when the next session starts.
What an agent can do
| Tool | What it does |
|---|---|
fabric_list_project_instructions | Browse or search the published files, or ask what changed since a sinceDigest |
fabric_get_project_instruction | Read one published file by path, paged for large files |
fabric_get_project_instruction_bundle | Install the whole published tree as one zip, and report what changed since a sinceDigest |
fabric_instruction_checks | Report whether the session is set up the way the instructions expect: the credential's scope, project access, the published version, whether the installed copy is current, and the environment variables and tools the project declares. Every suggested fix is advice for the developer, not authority to change anything |
fabric_propose_project_instruction_change | Suggest an edit (a file's whole new content, or a deletion). It opens a proposal for someone who can edit the instructions to approve; it changes nothing the project reads |
fabric_add_instruction_lesson | Record a lesson, a mistake the team should not repeat, as a new file under Lessons/, through the same proposal path |
The two writing tools are held for review: the agent reports a suggestion as awaiting review, never as applied. Only a person with permission to edit coding instructions publishes anything.
Project readers can propose adding, editing, or deleting a file from the Coding Instructions tab. Proposals pass the same validation and secret scanning as uploads before an instruction editor can review their changes and approve publication. Rejecting a proposal leaves the published files unchanged. If the published version changes before approval, submit the proposal again against the current version. You can cancel your own proposals once validation is no longer running. Each project allows up to 25 active proposals, with a limit of five per proposer. Large file bodies may be omitted from the review preview; editors can open a paged view of the full text. Canceled uploads can continue to count toward the limit until their upload links expire and cleanup finishes. On a project whose instructions come from a repository, the files are changed in git. A suggestion from the tab or from an agent becomes a commit on the proposer's own branch there, with one pull request collecting each person's changes, and it is reviewed and merged in the repository. A merged pull request syncs on its own.
Only published versions are available through MCP and local sync. A pending proposal doesn't change the files your connected agents receive.
Work on a repository project from the tab
On a project whose instructions come from a repository, the tab works the way git does: the branch is the history, and Fabric's copy follows it.
- Commit. Someone who can edit coding instructions presses Edit on a file, writes a
commit message, and chooses Commit to the branch. The message starts as
Update <file>; Add file, Rename and Delete file start fromAdd <path>,Rename <old> to <new>andDelete <path>, and a rename is one commit that git reads as a rename. Fabric runs the secret scan an upload gets before anything is pushed, and a change it refuses is not committed, with its findings on the page. The commit names you as author, with a Fabric no-reply address, and Fabric as committer. Fabric's copy takes the commit a few seconds later, from a sync of the branch's real files, and the status block says so until it has. - Suggest as a pull request. The alternative to committing, and the only choice for a reader once the project lets read-only members propose. If the branch is protected, or keeps moving, Fabric opens the pull request for you and links it. If a teammate changed the same file since you started, nothing is written: you choose between retrying the commit, suggesting it as a pull request and cancelling, and what you typed stays.
- Commits. The button that is History on an upload project reads Commits here: the branch's own history within the synced folder, newest first, 30 at a time. Each row has the short commit (a link to GitHub, GitLab or Azure DevOps), its author, its age and its subject, and is marked Published when Fabric's copy is of it, Refused by scan when the secret scan refused its files, or Not synced yet when it is newer than the published commit and Fabric has not taken it. A commit message that looks like it contains a secret is shown as withheld. Nothing in Commits publishes, rolls back to or deletes a version: Fabric's copy follows the branch.
- Revert. Undoes a commit with a new commit on the branch, as
git revertdoes. It is refused, with the reason, when a later commit changed the same files, when a file it would restore looks like it contains a credential, for a merge commit or a repository's first commit, and on a protected branch, where you revert in the repository instead. - Compare with parent. Shows what a commit changed within the synced folder: the files added, removed and changed, and a line diff of each changed file once you open it. A file Fabric will not show (it is in a commit the secret scan refused, it holds a secret, it is not text, or it is too large) is described in one line instead of a diff. Comparing needs the same right as committing; everyone who can read the tab sees the commits themselves.
A project in Read-only mode makes no commit and no revert from Fabric, whatever the member's role, and says so; suggesting and reading are unchanged.
Move uploaded instructions into a repository
An upload project can hand its published files to a repository without anyone copying them. Settings offers Move to a repository… to a member who can create and update coding instructions, once something is published and a repository is connected to the project.
- Start. Choose the repository, the branch and a folder. The folder must not hold any files yet, and a new folder is fine; leave it empty to put the files at the repository root, as long as they would not overwrite anything there. Fabric opens one pull request carrying the published files. It refuses, with the reason, a folder that already has files, a project that already has a repository sync set up, and a project with nothing published.
- While it is open. The status under the summary follows the pull request: Preparing the pull request… while it is opened, then the repository, the branch and the pull request awaiting its merge, with a link to it. The instructions stay as published until it merges. Uploads, edits, publishing, ignore rules, repository settings, reverts and manual syncs are paused, and each says why: Moving to the repository: pull request #n is open. Everyone who reads the tab sees the status; Cancel move is for members who can create and update.
- If it is blocked. The status gives the reason in a sentence and offers Retry when a retry can help. A failure no retry can fix says to cancel the move and start it again. If the project was switched to the repository by something other than the move, Cancel move and Retry are refused and the status says so: the only way out is Switch to upload mode in Settings.
- When it merges. The status reads Merged — switching the project to the repository… until the first sync from the repository has succeeded. The repository is then the source of truth and the tab works as on any repository project. If the pull request is closed without merging, the move ends, the instructions stay as published, and the tab says so once. A pull request that was merged into a different branch than the one the project reads ends the move the same way: Fabric does not sync it, and the status says it was merged into another branch.
- If the first sync cannot finish. While the project is switching over, Switch to upload mode in Settings ends the move and gives the tab back its uploads. It is offered only while a move is switching over or blocked, and nowhere else a move is open; whatever was already merged in the repository stays there.
- Cancel. Cancel move closes the pull request without merging it and gives the tab its controls back. A pull request that has already merged cannot be canceled: its files are in the repository and the project is switching to them.
Keep a checkout current
This applies to Already cloned and Clone it for me; with No clone the plugin already follows the published version. The session-start hook asks, every time a Claude Code or Codex session starts, whether the published version moved, and tells the agent when it did.
- A project that syncs from a repository. The hook fast-forwards the branch the project
follows to its newest commit when your checkout is clean and on that branch. It is a
fast-forward and nothing else: it never merges, rebases, resets or stashes, and when it
cannot (uncommitted changes, another branch, a detached
HEAD, a diverged history, a shallow clone) it leaves the checkout exactly as it was and prints one line saying why.init --report-onlywrites a hook that only reports and never fetches. - An upload project. The hook reports when the published instructions changed; it applies
nothing unless
initwas given--apply.
Codex asks whether to trust the folder the first time it opens it. Trust it, then run /hooks
once to trust the project hook. VS Code and Cursor have no hook, so the agent makes the
comparison through fabric_instruction_checks at the start of a session. An agent connected
over MCP only reports: it never pulls, and the pull itself stays with you.
What No clone leaves on your machine. None of it is in your repository's history:
- In the folder, for Claude Code:
.claude/settings.local.json, which turns the plugin on, names the marketplace it comes from, and holdsSessionStarthooks that runinstructions rulesandmemory pull, and aStophook that runsmemory push. Also for Claude Code: the project's rules in.claude/rules/fabric-<id>/, with a.fabric-managed.jsonthat records what Fabric wrote there. For Codex:.codex/hooks.jsonandAGENTS.override.md(the memory file and the rules). For both:.fabric/, which records the project and what the mirror last agreed with Fabric, and the mirrored write folders. The setup keeps all of these out of your commits, adding to the repository's own.git/info/excludewhatever git does not already ignore. - In the CLI's config folder (below): the CLI's own copy, the plugin's marketplace under
cache/marketplaces/, and the built plugins undercache/plugins/. For Codex, the plugin in~/.codex/plugins/and its entry in~/.agents/plugins/marketplace.json.
To undo it, run claude plugin uninstall fabric-<id>@fabric-<id> --scope local in the folder
(or remove the plugin in Codex), then delete the hook entries, .fabric/, the mirrored
folders and the rules folder .claude/rules/fabric-<id>/ (keep any file in it you edited and
want). For Codex, delete AGENTS.override.md if you did not write it yourself. Files already sent stay in the repository.
What Already cloned and Clone it for me leave, and how to check it and undo it. Two things, and neither is in your repository's history:
- A hook entry in the checkout:
.claude/settings.local.jsonfor Claude Code,.codex/hooks.jsonfor Codex.initadds both files to the repository's own.git/info/exclude. The entry is aSessionStartcommand that starts withnode, then the CLI's own copy, theninstructions check(orsync) with your project and your deployment. - That copy of the CLI, one file per deployment:
cli/<deployment>/fabric.mjsinside the CLI's config folder (%APPDATA%\fabricai-nodejs\Configon Windows,~/.config/fabricai-nodejson Linux,~/Library/Preferences/fabricai-nodejson macOS). It is why the hook works with nofabriccommand installed. Each run of the setup line replaces it, and the copy also keeps itself current (below). A smallupdate-check.jsonbeside it records when it last asked.
The copy updates itself. Once a day, at the end of a session-start hook run and after the
hook has printed what it has to say, the copy asks the deployment the hook is bound to which
CLI it serves now. When that is a different build from the one running, the copy downloads it
from that deployment, checks it against the integrity hash the deployment publishes, and
replaces itself. The new copy runs from the next session. Only that deployment is ever asked
(over https, or plain http to localhost for a deployment you run yourself), nothing
downloaded is run, and only the copy and its update-check.json are written, inside the CLI's
config folder. It only runs when at least five seconds of the hook's ten-second limit are
still unspent, and any failure (no network, a hash that does not match, a copy another session
holds open) leaves the earlier copy as it was, prints at most one line on stderr and never
touches the hook's own output. To turn it off, set FABRIC_CLI_NO_SELF_UPDATE=1; it is also
off whenever CI is set. Run the setup line again to update by hand.
When a message from the CLI tells you to run a command, it gives the short npx line for the
deployment, even from the copy the hook runs. That line keeps working after the deployment
moves on, because a tarball name it served earlier answers with a redirect to the current one.
To check it, run the setup line's CLI with instructions doctor --project <id>: it reads the
hook entry, says whether it is in the form this CLI would write, and warns when the file it
runs is gone. To undo it, delete the project's instructions check or instructions sync
entry from the hook file (and its lesson-prompt entry, if you set up lesson capture), and
delete the deployment's folder under cli/ if no other checkout uses it. auth logout
removes the saved sign-in.
The tab shows the published side. For a repository project, a status block under the summary states which version, commit and branch are published, whether Fabric's copy is current or was held back because a commit was refused by the secret scan (with a link to the findings), and whether automatic sync is on. Your own checkout is not tracked there: the hook in your coding tool is what tells you when it is behind.
A connected agent finds them on its own. Every project fabric_get_project and
fabric_list_projects return carries a codingInstructions field: { "published": false }
when there is nothing to load, and the snapshot's version, fileCount, digest and
publishedAt when there is. The connection handshake tells the agent what to do with it:
call fabric_get_project_instruction_bundle to install the whole approved tree in one step,
or fabric_list_project_instructions and fabric_get_project_instruction to read individual
files, and follow them while working on that project. The field is omitted entirely for a key
that does not hold the instructions:read scope (or a coarse mcp:read/mcp:write).
Keeping an installed copy current does not always mean downloading it again. Pass the
digest you last installed as sinceDigest to either
fabric_get_project_instruction_bundle or fabric_list_project_instructions:
- If the digest still matches, you get
"unchanged": trueand nothing else: no file list, no manifest, and no zip URL, so nothing is built or transferred. This is the only case that short-circuits. - If it has moved on, you get the response you would have got anyway, in full, plus
changes(the added, removed and changed paths) andsince, the snapshot that digest names.sinceDigesttells you what changed; it does not send you a smaller download. - If Fabric has never published that digest for this project, or has since pruned it, you get
"changes": nullalongside the same full response. Treat it as a fresh install.
You do not need to tell the agent any of this: the connection handshake already carries it. To point an agent at one specific project in a chat, name the project and ask it to load that project's coding instructions.
Bootstrap a Project
A new project's Context tab is often empty, which leaves Fabric's AI features little to
work from. A connected coding agent can fill it from the repository you already have open: it sets the project's description, then pushes your README and key docs as
knowledge sources. The connection handshake tells the agent to offer this when a project's
fabric_list_project_contexts comes back with few or no sources.
The flow uses two tools. fabric_update_project sets the description, and
fabric_upsert_project_context pushes one file per call, keyed by the file's path in the
repository:
{ "name": "fabric_update_project",
"arguments": { "projectId": "proj_example", "description": "Billing service: invoicing, payment retries and dunning." } }
{ "name": "fabric_upsert_project_context",
"arguments": { "projectId": "proj_example", "sourcePath": "README.md", "content": "# Billing service\n..." } }
{ "name": "fabric_upsert_project_context",
"arguments": { "projectId": "proj_example", "sourcePath": "docs/architecture.md", "content": "# Architecture\n..." } }Pushed files appear in the project's Context tab under Living Memory, grouped by the folder of their path.
Pushing the same path again with the same content answers unchanged and writes nothing,
and content identical to a source already in the project (another pushed file, or a file,
link or note added in the Context tab) answers duplicate. To replace a
file that is already there, the agent passes the contentHash it read from
fabric_get_project_context or fabric_list_project_contexts as expectedContentHash; if
someone else has changed the file since, the push answers conflict with the current hash and
who changed it, and nothing is overwritten. If the file was deleted since, the push answers
conflict with current set to null; call again without expectedContentHash to recreate it,
which answers duplicate instead if that content already exists elsewhere in the project.
When a file was renamed or moved without changing, push it at its new path with the old path as
movedFromSourcePath and the old path's contentHash as expectedContentHash: the stored source
is renamed in place and answers moved, keeping its history, instead of leaving a second copy
behind.
Coding-instruction files (CLAUDE.md, AGENTS.md, anything under .claude/, and skills,
agents, hooks, rules and scripts) do not go through this flow. The agent proposes those with
fabric_propose_project_instruction_change, which opens a proposal on the Coding Instructions
tab for a person to review. The agent should also leave out secrets, .env files and
generated output.
Pushing context needs a key with the projects:write or mcp:write scope (the key
that Connect your agent mints cannot do it) and, as with every key, your own permission to
add context sources to that project. If those docs already sit together in one folder, the
Fabric CLI can push the whole folder instead: fabric context push <dir> --project <id> sends
each text file by its path with the same unchanged, duplicate and conflict answers, and
remembers what it sent so that later runs send only what changed. It needs the same
projects:write scope. To start the agent flow, paste a sentence like this into your agent:
Use the Fabric MCP server to bootstrap the context for the project "Your Project" from this
repository: set its description and push the README and key docs, and tell me what you pushed.What Tools Are Available?
When your client connects, it receives two categories of tools:
Platform Tools (Always Available)
These give the AI direct access to your Fabric data — no extra setup needed:
| Tool | Description |
|---|---|
fabric_get_identity | See your user context, active organization, and available orgs |
fabric_list_organizations | List all organizations you belong to |
fabric_switch_organization | Switch between personal and org context |
fabric_list_projects | List your projects with filtering and search, each with its published codingInstructions |
fabric_get_project | Get full project details, including its published codingInstructions |
fabric_create_project | Create a new project |
fabric_update_project | Update project name or description |
fabric_search_project_knowledge | Search feature descriptions, document contents, and available project source text with ranked excerpts |
fabric_list_documents | List documents in a project (PRDs, specs, etc.) |
fabric_get_document | Get full document content in markdown |
fabric_list_project_contexts | List a project's Context tab sources — uploads, meeting transcripts, crawled links, notes |
fabric_get_project_context | Read one context source in full: transcript text, extracted file text, or crawled markdown |
fabric_upsert_project_context | Push a text file into a project's Context by its relative path: unchanged content is skipped, changed content replaces the stored version and is re-indexed, and replacing a file requires the contentHash you last read so nobody's edit is silently overwritten |
fabric_list_project_instructions | Browse or search a project's published coding-instruction files, or ask what changed since a sinceDigest |
fabric_get_project_instruction | Read one published coding-instruction file by path, paged for large files |
fabric_get_project_instruction_bundle | Install the whole published tree as one zip, and report what changed since a sinceDigest |
fabric_instruction_checks | Check that this session is set up the way the project's coding instructions expect: scope, access, published version, whether your installed copy is current, and the variables and tools the project declares |
fabric_propose_project_instruction_change | Suggest an edit to the published coding instructions; held for review, never published by the agent |
fabric_add_instruction_lesson | Record a lesson as a new file under Lessons/, through the same review-gated proposal path |
fabric_memory_targets | List a project's Living Memory write folders and where their files land |
fabric_memory_read | List the files under a Living Memory write folder, or read one file's text a page at a time; works with a coding tool's default sign-in |
fabric_memory_write | Write or delete plain files in a Living Memory write folder as one commit or pull request; needs the memory:write scope |
fabric_memory_write_status | Read the receipt of a Living Memory write |
fabric_list_workspaces | List your document libraries (the Libraries page). The tool keeps its name for existing clients |
fabric_get_workspace | Get a document library's details and document list |
fabric_query_workspace | Semantic search (RAG) across a document library's documents |
fabric_list_frames | List your frames; with surface: "workspace" the organization's Workspaces you can open, with surface: "artifact" and a projectId a project's Artifacts |
fabric_get_frame | Read a frame's blocks, surface and runtime tools; outline:true returns block sizes without content for partial editing |
fabric_create_frame | Create a page. Pass the whole page in blocks, a surface (artifact or workspace), and the tools its script calls in runtimeTools. A Workspace is shared with the organization unless sharedWithOrganization is false; any other page is private to its creator. shareOnCreate: true also makes a public link, where the organization allows public links. Asks the person in chat first when runtimeTools is set |
fabric_share_frame | Change who can open a page you own. scope is required: PUBLIC (a link anyone can open, only where the organization allows public links), WORKSPACE_AND_EMAILS, EMAILS_ONLY or PRIVATE. Asks the person in chat first (frames:write) |
fabric_edit_frame_block | Edit part of a page block: read an outline, read the needed source window, then submit literal replacements with expectedUpdatedAt |
fabric_update_frame | Add, remove or reorder blocks, or rewrite a page you created; with no frameId it changes the Workspace the user has open. Asks the person in chat first when it sets runtimeTools |
fabric_list_workflows | List your automation workflows, optionally only the scheduled ones |
fabric_get_workflow | Get workflow details: schedule, project, nodes, edges and the latest run |
fabric_list_workflow_runs | List a workflow's recent runs, with the page each one published |
fabric_get_workflow_execution | Check workflow execution status |
fabric_execute_workflow | Start a published workflow now, including a scheduled one (workflows:run) |
fabric_create_workflow | Create a draft workflow from a graph, or from a preset that runs an agent on a schedule and publishes a page. dryRun returns the draft without writing (workflows:write) |
fabric_update_workflow | Edit a workflow's graph, or the fields of a preset (workflows:write) |
fabric_publish_workflow | Publish a workflow as a new version and put it on its schedule (workflows:write) |
fabric_unpublish_workflow | Take a workflow back to draft and stop its schedule (workflows:write) |
fabric_pause_workflow / fabric_resume_workflow | Stop and restart a published workflow's schedule (workflows:write) |
Writes need an API key with the workflows:write scope; a signed-in coding agent (OAuth) stays read-only. Each write is checked against what the key's owner may do now, so a scope never grants more than the role behind it.
| fabric_list_chats | List recent AI conversations |
| fabric_list_connected_servers | See all your connected MCP servers |
Search project knowledge
Call fabric_search_project_knowledge with an explicit projectId and a nonempty
query to find a literal, case-insensitive keyword or phrase across feature
(including bug) titles and descriptions, authored document titles and contents,
and available project context text. Crawled URL pages and captured channel
conversations participate; repository code-index contexts and sources without
readable text are excluded. Feature rich-text descriptions are searched as text
nodes rather than editor attributes. Search performs no model or embedding calls.
The tool requires both projects:read and features:read, or an existing
mcp:read, mcp:write, or wildcard umbrella. Every call checks the creator's
current project access. Organization keys and OAuth credentials remain bound to
the organization that issued them; invited guests using browser or personal-key
credentials read the project's hosting organization.
Exact titles or feature identifiers rank first (3), title substrings next (2),
and body matches last (1). Ties sort by source kind, then ID. Each result provides
its ID, source kind, bounded title and match-centered excerpt, excerptField
(body, title, or identifier), truncation flags, and a readTool
with readArguments for reading the source in full. Child page and conversation
matches also include their parent contextId and pageId or bundleId.
An unusually long source URL is omitted with sourceUrlOmitted: true; the ID-based
read remains available.
limit defaults to 20 and accepts 1–50 results. maxBytes defaults to 12000 and
accepts 8192–50000 UTF-8 bytes of the complete serialized JSON response text,
including pagination metadata. Results are complete records from the ranked
prefix; the response never cuts JSON. returnedCount, hasMore, and
omissionReason distinguish a complete response from a page stopped by
result_limit or response_budget. The tool reports exclusions rather than
presenting unavailable source text as an empty search match.
When hasMore is true, pass nextCursor with the same project and query to
continue. You may change limit or maxBytes. Cursors bind to the project,
query, and hosting organization and resume after the last emitted record, so a
record omitted for the byte budget remains eligible on the next page. This is
live keyset pagination, not a snapshot: edits between calls can change ranks or
matches. Restart the search when you need to include changed material.
Connected Server Tools (Require Authority)
Every MCP server you've connected in Settings → MCP Servers becomes available, namespaced by server name:
linear__list_issues → List issues from your Linear connection
linear__create_issue → Create an issue in Linear
github__list_repos → List repos from your GitHub connection
slack__send_message → Send a message via your Slack connection
notion__search → Search your Notion workspace
fizzy__get_cards → Get cards from Fizzy boardsA connected server's tools are held to the credential's scopes and, for any tool that
may change something, to runtime authority. A tool counts as a read only when its
server declares readOnlyHint: true; for an OAuth sign-in or an organization API key
nothing else makes it a read, whatever its name. Everything else needs the mcp:write
scope (an OAuth sign-in can never hold it, so it cannot call those tools) and a
runtime grant. Your own session and your personal API key keep the name-based
guess (get_, list_ and the like) for deciding whether a grant is asked for,
and still need mcp:read or mcp:write. See Runtime Authority for details.
Per-User Tool Visibility
Each user only sees tools from MCP servers they personally connected in their current context:
- User A connected Fizzy only → sees
fizzy__*tools - User B connected Linear + Fizzy → sees
linear__*+fizzy__*tools - In org context → sees tools from servers connected within that organization
- Personal connections never appear in org context, and vice versa
Choose Which Servers a Client Sees
By default a client of the organization-wide gateway gets every platform tool
and every tool of each connected server. To give one client fewer, add the
servers parameter to the URL it connects to, with the servers, or single
tools, it should see separated by commas:
https://your-fabric.com/api/mcp-gateway?servers=fabric
https://your-fabric.com/api/mcp-gateway?servers=fabric,linear
https://your-fabric.com/api/mcp-gateway?servers=github_2
https://your-fabric.com/api/mcp-gateway?servers=fabric,fizzy:fizzy_get_cards,fizzy:fizzy_create_cardEach entry is one of:
-
nameorname:*: every tool of that server. -
name:tool: only that tool.toolis the name the server itself gives the tool, which is what follows<prefix>__in the gateway's name for it: the Fizzy server'sfizzy__fizzy_get_cardsisfizzy:fizzy_get_cards. Forfabric, use the full platform name, such asfabric:fabric_list_projects. Severalname:toolentries for one server add up, andname(orname:*) alongside them means the whole server. -
fabricnames the platform tools (fabric_*). -
Any other name is a connected server's prefix: the part before
__in its tool names.linear__list_issuesbelongs tolinear. A server's prefix comes from its connection's name: lowercased, each run of characters other than letters and digits turned into one_, and cut to 30 characters. When two connections would share a prefix, the second gets_2, the third_3, and so on.fabricis kept for the platform tools, so a connection named "Fabric" getsfabric_2. Renaming a connection changes its prefix, so update any URL that names it. -
Server names are case-insensitive. Tool names are case-sensitive and must be written exactly as the server gives them. Spaces around either are ignored, and repeating an entry has no effect.
-
Leave the parameter out to see every server, as before.
The selection is read once, when the client's session starts, and holds for the
whole session. The tools outside it are left out of tools/list and a call to
one is refused with an error, even by its exact name. After
fabric_switch_organization, a selected server that the new organization does
not have contributes no tools. The selection comes from the connection URL.
If the gateway replaces the session because your credential now resolves to
another organization, the client's next request, which carries the same URL,
gets a new session with the same selection, checked again in the new
organization. If the URL names a server or tool that organization lacks, the
request fails with the same error as an initialize would, and so does every
retry with that URL. Connect without the parameter to see every server.
If the parameter is empty, has an empty entry or an entry with nothing after
: (such as fizzy:), or names a server you do not have in the organization
the session runs in, the connection fails at initialize with an error that
names the bad entries and lists the valid server names, rather than quietly
offering fewer tools. A fabric:<tool> entry that is not a platform tool fails
the same way. A <server>:<tool> entry for a connected server is checked
against that server's tools: if the tool is not one of them, the error lists the
server's tools, or, when it has more than 25, says how many there are and how to
see them. If the server's tools cannot be discovered when the session starts,
because the server is unreachable or does not answer in time and Fabric holds
no earlier list of them, the entry is accepted so that an outage does not stop
the connection; a tool the server turns
out not to have is then simply never listed. A project's URL
(/api/mcp-gateway/projects/<project-id>) has no connected servers to choose
from and refuses the parameter.
The parameter narrows what one client sees and may call. It is not a
permission: connecting without it shows every tool your permissions allow. It
grants nothing and takes away no permission: each call to a selected tool is
still checked against your scopes, project access and
runtime authority, and a platform tool you leave out (such as fabric_request_authority) is simply not
available on that connection.
Discovery Mode for Clients With a Tool Limit
Some clients cannot take the gateway's full tool list. Windsurf stops at 100
tools, VS Code Copilot at 128, and ChatGPT connectors limit how much text the
tool definitions may take. For a client like that, add mode=discovery to the
URL it connects to. Both the organization-wide URL and a project's URL accept
it:
https://your-fabric.com/api/mcp-gateway?mode=discovery
https://your-fabric.com/api/mcp-gateway?mode=discovery&servers=fabric,linear
https://your-fabric.com/api/mcp-gateway/projects/<project-id>?mode=discoveryIn discovery mode tools/list returns three tools instead of every tool:
| Tool | What it does |
|---|---|
search_tools | Finds tools by keywords in their names and descriptions (query, and an optional limit, default 10, at most 25). Each match has its name, a one-line summary, its server, its read-only and destructive hints, and whether it needs runtime authority. The result also says how many tools matched in all. |
get_tool_schema | Returns one tool's full definition (name): its description, input schema, hints, and whether it needs runtime authority. |
call_tool | Runs a tool (name, with its own arguments in arguments). |
A client can also call a tool by its real name once it knows it, as in full mode.
Discovery mode changes how tools are presented, not what a connection may do.
It combines with servers: search and schema cover only the selected servers
and tools, and, at a project's URL, only that project's tools. They also leave
out any tool your API key's or sign-in's scopes do not reach. Every
call_tool call is checked exactly as a direct call to that tool is, against
your scopes, the server selection, project access and
runtime authority. A tool that search lists can still be
refused when it is called, for example because a project is not yours to edit,
because the tool checks a further scope of its own, or because authority has
not been approved yet.
The mode is read once, when the client's session starts, and holds for the
whole session. A later request that adds, drops or changes the parameter does
not change the mode of the session it quotes. To switch modes, reconnect with a
different URL. Any value other than discovery fails the connection at
initialize with an error that names the accepted value.
Sessions are kept in the memory of the Fabric instance that created them. If a session is lost, for example after a restart, the client's next request starts a new one from the same URL, with the same mode and selection. Runtime authority approved for the old session is bound to that session and does not carry over, so request it again.
Runtime Authority
Connected server tools are protected by runtime authority — a Pipes-style session-scoped authorization model. This means:
- Credentials are persistent — your MCP server connections stay configured
- Runtime access is ephemeral — AI agents must request temporary permission before calling a tool that writes; read-only tools run without it
- Approval is human-only — agents can request authority, but only humans can approve it
The Authority Flow
Authority Tools
| Tool | Description |
|---|---|
fabric_request_authority | Request access to providers (e.g., linear, github) with READ or WRITE level and TTL |
fabric_check_authority | Check which providers are currently authorized |
fabric_revoke_authority | End all active authority immediately |
Authority Metadata in tools/list
Connected tools include metadata saying whether a call needs authority. A read-only tool runs without a grant:
{
"name": "linear__list_issues",
"description": "[Linear] List issues",
"_meta": {
"requiresAuthority": false,
"providerKey": "linear"
}
}A tool that writes needs one. providerAuthorityStatus says whether the session has any
approved grant for the provider; a grant limited to other tools, to reads or to one request
can still refuse the call:
{
"name": "linear__create_issue",
"description": "[Linear] Create a new issue",
"_meta": {
"requiresAuthority": true,
"providerAuthorityStatus": "granted",
"providerKey": "linear"
}
}For more details, see Runtime Authority.
Example Conversations
Cross-Platform Project Sync
"Show me the features in the Acme project, then create a Linear issue for each one that doesn't have a corresponding ticket"
The AI will:
fabric_list_projects→ find Acmefabric_list_documents→ get feature docsfabric_request_authorityfor Linear (WRITE)- Wait for your approval in Fabric UI
linear__list_issues→ compare with featureslinear__create_issue→ create missing tickets
Research From Project Context
"Read the last two design meetings on the Acme project and tell me what we decided about rate limiting"
The AI will:
fabric_list_projects→ find Acmefabric_list_project_contextswithtype: "MEETING_TRANSCRIPT"→ list the synced transcriptsfabric_get_project_contexton the two most recent → read the transcript text- Answer from the transcripts, paging with
nextOffsetif a transcript is long
This is the same material the Context tab's Download All export covers. Two things to know before you rely on it:
- Repository code-index entries (
CODE_FILE,CODE_FILE_SUMMARY) are hidden from an unfiltered listing — an indexed repo produces thousands. PassincludeCodeContexts: trueor filter by that type to see them. - A context that pins a monitored Teams or Slack conversation carries no transcript of
its own: those messages are analysed into backlog proposals rather than stored on the
context. Such rows come back with
contentAvailable: falseand anunavailableReasonsaying so, rather than an empty body.
Knowledge Base Search
"Search the engineering library for our database migration strategy and summarize the key decisions"
The AI will:
fabric_list_workspaces→ find the engineering libraryfabric_query_workspace→ semantic search for "database migration strategy"- Return a summary based on the matching document chunks
Workflow Execution
"Run the weekly report workflow and send the output to #team-updates on Slack"
The AI will:
fabric_list_workflows→ find weekly reportfabric_execute_workflow→ trigger itfabric_get_workflow_execution→ wait for completionfabric_request_authorityfor Slack (WRITE)slack__send_message→ post the results
Protocol Details
Fabric provides three MCP entry points backed by the same authentication model and ui://fabric/* MCP Apps:
| Entry Point | URL / Command | Transport | Purpose |
|---|---|---|---|
| Primary MCP Gateway | POST /api/mcp-gateway (or /api/mcp-gateway/projects/<id>) | Streamable HTTP (2025-11-25) | Full 169-tool platform catalog + connected 3rd-party MCP servers + OAuth 2.1 PKCE + project scoping + 24-hour idempotent durable calls |
Canonical /mcp Endpoint | POST /mcp (?stateless=1 supported) | Streamable HTTP (2025-11-25) | Short-path MCP endpoint for clients that auto-discover /mcp and .well-known/oauth-protected-resource |
Stdio Bridge (@fabricorg/sdk-mcp) | fabric mcp serve / npx -y @fabricorg/sdk-mcp | stdio JSON-RPC | Local stdio process for stdio-only desktop clients using fabric login credentials |
The HTTP gateway implements the MCP Streamable HTTP transport:
| Method | Endpoint | Purpose |
|---|---|---|
POST | /api/mcp-gateway or /mcp | JSON-RPC requests (initialize, tools/list, tools/call, resources/list, resources/read) |
GET | /api/mcp-gateway or /mcp | Server info & capability discovery (health check) |
DELETE | /api/mcp-gateway | Terminate session |
Interactive MCP Apps (ui://fabric/*)
When connected from an MCP App-capable host (Fabric Desktop, Claude Desktop, claude.ai, ChatGPT, or VS Code), tool calls return _meta.ui.resourceUri pointing to self-contained text/html;profile=mcp-app cards built with the Fabric UI Kit and A2UI v0.9.1 runtime:
ui://fabric/project-dashboard— Renders Fabric Projects and Fabric Artifacts & Workspaces cards forfabric_list_projects,fabric_get_project,fabric_list_frames,fabric_get_frame,fabric_list_workspaces, andfabric_list_artifacts.ui://fabric/agent-delegation— Renders Tickets Assigned to You (fabric_list_featureswithassignedToMe: true,fabric_list_project_pm_assigned_items,fabric_list_tickets) and Agent Delegation & Subscription Credits cards with 1-click handoff to Fabric Desktop (fabricagents://) or a Remote Fabric Agents Server.ui://fabric/workflow-visualizer— Renders DAG pipeline steps returned by workflow tools (fabric_list_workflows,fabric_execute_workflow); it does not invent steps when a result omits them.
Durable calls use the authenticated user, organization, credential, project binding, and selected server or MCP configuration, not tool arguments, as authorization context. Send X-Mcp-Durable: true (or _meta.durable: true) and an explicit key in X-Mcp-Idempotency-Key, _meta.idempotencyKey, or _idempotencyKey. A matching completed call within 24 hours never runs again: the gateway either replays a safe stored result or returns a non-error receipt with the execution ID and completion time. A receipt with _meta.durableExecution.resultWithheld: true omits the historical result and tells the client to use the matching get/list tool for current state. A live call with that key reports that it is in progress; a failed or expired receipt, or a RUNNING receipt older than 10 minutes, can be reclaimed. Without a key, the tool runs normally and reports that it was not deduplicated.
ui://fabric/genie-insights— Renders workspace RAG citations, documents, and Lakebase query results (fabric_query_workspace,fabric_search_knowledge).ui://fabric/visualize— Renders custom multi-surface A2UI v0.9.1 interactive charts, tables, comparisons, and plans (fabric_visualize).
Session Management
- Sessions are created automatically on first request
- Session ID is returned in the
Mcp-Session-Idresponse header - Sessions last 24 hours
- Active authority sessions are completed when the gateway session ends
Authentication
| Method | Header | Use Case |
|---|---|---|
| Sign-in (OAuth 2.1) | Authorization: Bearer fat_xxx, obtained by the client | Coding agents (recommended) |
| API Key | Authorization: Bearer fab_xxx or org_xxx | CI and headless clients |
| Session Cookie | Automatic | Browser-based clients |
An unauthenticated request is answered 401 with
WWW-Authenticate: Bearer resource_metadata="<origin>/.well-known/oauth-protected-resource/api/mcp-gateway".
That document names the authorization server (<origin>/api/auth), whose
metadata is at <origin>/.well-known/oauth-authorization-server/api/auth.
Clients register themselves (RFC 7591) as public clients, use PKCE S256, and
request the token for the resource <origin>/api/mcp-gateway.