Skip to content

Commit 096058c

Browse files
committed
fix(readme): drop stale + missing screenshot refs; test meta-tool activity store
- README referenced screenshots that are stale (client-detail, featureset-detail show the pre-redesign IA and the old "Default" FeatureSet) or never captured (workspaces, tool-optimization). Remove those image refs so the README isn''t broken or misleading; the feature text stays. Remaining images (servers, discover, discover-web, space-switcher) are current. The full set still needs a capture run on a machine with a display (see docs/screenshots/README.md). - Add a unit test for metaToolActivityStore (ring-buffer trim, most-recent-first, clear) — guards the "activity vanished on tab change" fix. Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com>
1 parent ff27114 commit 096058c

2 files changed

Lines changed: 52 additions & 8 deletions

File tree

README.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -111,20 +111,14 @@ Create isolated Spaces — each with their own servers, credentials, and permiss
111111

112112
Your AI client tells McpMux which folder it's working in (its MCP *root*). McpMux uses that to **route each workspace to its own toolset** — open your backend repo and the AI sees your database and deploy tools; open a docs folder and it sees only search and filesystem. Map a folder once in the **Workspaces** tab (or let the AI do it — see below) and every future session from that exact path resolves automatically. Matching is per-folder and exact, so nothing leaks across projects.
113113

114-
![Workspaces — route each folder to its own FeatureSet](docs/screenshots/workspaces.png)
115-
116114
### Control What Each Client Can Do
117115

118116
Not every AI client should have the same power. Create Feature Sets — permission bundles that control exactly which tools, prompts, and resources a client can access. Build a "Read Only" set for cautious workflows, a "React Development" set with just GitHub and Filesystem, or a "Full Stack Dev" set with everything. Assign them per-client so each tool only goes where you want it.
119117

120-
![Feature Sets — granular per-server tool selection](docs/screenshots/featureset-detail.png)
121-
122118
### See and Manage Every Connected Client
123119

124120
Cursor, VS Code, Windsurf, Claude Code — see every AI client connected to your gateway in real time. Click any client to manage its workspace, grant or revoke feature sets, and see exactly which tools it can access. New clients authenticate via OAuth with a one-click approval flow.
125121

126-
![Client Management — per-client permissions and effective features](docs/screenshots/client-detail.png)
127-
128122
### Let Your AI Curate Its Own Toolset
129123

130124
Hand an assistant a hundred tools and it burns tokens and reaches for the wrong one. McpMux ships a built-in **Tool Optimization** capability so the AI can keep *itself* lean — straight from chat, no config files.
@@ -139,8 +133,6 @@ Reads are silent; anything that changes your setup pops a **one-click approval d
139133

140134
> *"@mux build a minimal toolset for this Next.js repo and pin it to this folder."*
141135
142-
![Tool Optimization — the AI composes and pins its own toolset, gated by your approval](docs/screenshots/tool-optimization.png)
143-
144136
---
145137

146138
## Security
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* Guards the fix for "Recent meta-tool activity vanished on tab change":
3+
* rows live in a global store (not component-local state), so this verifies
4+
* the store's ring-buffer semantics directly.
5+
*/
6+
7+
import { describe, it, expect, beforeEach } from 'vitest';
8+
import {
9+
useMetaToolActivityStore,
10+
MAX_META_TOOL_ROWS,
11+
} from '@/stores/metaToolActivityStore';
12+
import type { MetaToolAuditEvent } from '@/lib/api/metaTools';
13+
14+
function ev(tool: string, i: number): MetaToolAuditEvent {
15+
return {
16+
client_id: `client-${i}`,
17+
tool_name: tool,
18+
decision: 'read',
19+
summary: '',
20+
timestamp: new Date(2026, 0, 1, 0, 0, i).toISOString(),
21+
} as MetaToolAuditEvent;
22+
}
23+
24+
describe('metaToolActivityStore', () => {
25+
beforeEach(() => useMetaToolActivityStore.getState().clear());
26+
27+
it('keeps rows most-recent-first', () => {
28+
const { push } = useMetaToolActivityStore.getState();
29+
push(ev('first', 1));
30+
push(ev('second', 2));
31+
expect(useMetaToolActivityStore.getState().rows.map((r) => r.tool_name)).toEqual([
32+
'second',
33+
'first',
34+
]);
35+
});
36+
37+
it('trims to the ring-buffer size, dropping the oldest', () => {
38+
const { push } = useMetaToolActivityStore.getState();
39+
for (let i = 0; i < MAX_META_TOOL_ROWS + 10; i++) push(ev('t', i));
40+
const rows = useMetaToolActivityStore.getState().rows;
41+
expect(rows.length).toBe(MAX_META_TOOL_ROWS);
42+
// Newest kept, oldest evicted.
43+
expect(rows[0].client_id).toBe(`client-${MAX_META_TOOL_ROWS + 9}`);
44+
expect(rows.some((r) => r.client_id === 'client-0')).toBe(false);
45+
});
46+
47+
it('clear empties the buffer', () => {
48+
useMetaToolActivityStore.getState().push(ev('a', 1));
49+
useMetaToolActivityStore.getState().clear();
50+
expect(useMetaToolActivityStore.getState().rows).toEqual([]);
51+
});
52+
});

0 commit comments

Comments
 (0)