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
| 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 |
# 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/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.jsonThe 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,
},
},
}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}`,
},
},
}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
},
},
}# 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/clibun install # Install dependencies
bun run build # Build all packages
bun run check # Run prettier, oxlint, typescript
bun run fix # Fix formatting and lint issuesbun changeset # Describe your changes
git add .changeset/*.md
git commit -m "feat: add feature"
git push # CI creates release PRMIT