context-fs
Expose context to AI agents as a dynamic filesystem.
AI coding agents already know how to navigate filesystems - ls, cat, find, symlinks. context-fs lets you expose your APIs, databases, and services using an interface they already understand.
/linear
├── issues/
│ ├── by-id/
│ │ └── ENG-123/
│ │ ├── content.md # Read/write issue content
│ │ ├── team -> /teams/engineering
│ │ └── assignee -> /users/alice
│ └── search/
│ └── auth%20bug/ # Search results as symlinks
├── teams/
│ └── engineering/
│ ├── info.json
│ └── issues/
│ ├── active/ # Symlinks to open issues
│ └── triage/
└── users/
└── alice/
├── info.json
└── issues/
└── assigned/ # Symlinks to assigned issues
Screen.Recording.2026-01-10.at.18.33.25.mov
Packages
| Package | Description |
|---|---|
@context-fs/core |
Virtual filesystem core - define trees with handlers |
@context-fs/nfs |
Mount as NFS - appears as real directory |
@context-fs/cli |
CLI framework for building context-fs tools |
@context-fs/linear |
Linear issue tracker as a filesystem |
@context-fs/just-bash |
Run bash scripts against virtual filesystems |
Quick Start
Use an existing filesystem
# Mount your Linear workspace npx @context-fs/linear --mount /tmp/linear # Agent can now explore ls /tmp/linear/teams/ cat /tmp/linear/issues/by-id/ENG-123/content.md ls /tmp/linear/issues/search/authentication%20bug/
Build your own
import { createFileSystem, read, list, link } from "@context-fs/core"; import { mount } from "@context-fs/nfs"; const vfs = createFileSystem({ projects: { ":projectId": { [list]: () => getProjects().map((p) => ({ projectId: p.id })), "readme.md": { [read]: (c) => getProjectReadme(c.params.projectId), }, tasks: { ":taskId": { [list]: (c) => getTasks(c.params.projectId).map((t) => ({ taskId: t.id })), "content.md": { [read]: (c) => getTaskContent(c.params.projectId, c.params.taskId), }, // Symlink to the assignee assignee: { [link]: (c) => { const task = getTask(c.params.taskId); return `/users/${task.assigneeId}`; }, }, }, }, }, }, users: { ":userId": { [list]: () => getUsers().map((u) => ({ userId: u.id })), "profile.json": { [read]: (c) => c.json(getUser(c.params.userId)), }, }, }, }); await using handle = await mount(vfs, { mountPoint: "/tmp/projects" });
Now an agent can:
# Discover available projects ls /tmp/projects/projects/ # Read a project overview cat /tmp/projects/projects/website-redesign/readme.md # List tasks ls /tmp/projects/projects/website-redesign/tasks/ # Read a specific task cat /tmp/projects/projects/website-redesign/tasks/implement-auth/content.md # Follow the assignee symlink to learn about who's working on it cat /tmp/projects/projects/website-redesign/tasks/implement-auth/assignee/profile.json
Key Concepts
Dynamic paths with list
The list handler tells the filesystem what entries exist in a dynamic directory:
{ ":issueId": { [list]: async () => { const issues = await fetchIssues(); return issues.map(i => ({ issueId: i.id })); }, "content.md": { [read]: (c) => getIssue(c.params.issueId).description, }, }, }
Relationships with symlinks
Use the link handler to express relationships as symlinks:
{ "author": { [link]: (c) => `/users/${getPost(c.params.postId).authorId}`, }, "related": { ":relatedId": { [list]: (c) => getRelatedPosts(c.params.postId).map(p => ({ relatedId: p.id })), [link]: (c) => `/posts/${c.params.relatedId}`, }, }, }
Write support
Enable agents to make changes with the write handler:
{ "content.md": { [read]: (c) => getContent(c.params.id), [write]: async (c, data) => { const text = new TextDecoder().decode(data); await updateContent(c.params.id, text); return {}; // Success }, }, }
Installation
# Core library (browser-safe) npm install @context-fs/core # With NFS mounting (Node.js) npm install @context-fs/core @context-fs/nfs # CLI framework for building tools npm install @context-fs/cli
Development
bun install # Install dependencies bun run build # Build all packages bun run check # Run prettier, oxlint, typescript bun run fix # Fix formatting and lint issues
Creating a Release
bun changeset # Describe your changes git add .changeset/*.md git commit -m "feat: add feature" git push # CI creates release PR
License
MIT