* Refactored menu on spec pages
* Broke v2_tech_stack.md into v2_web_client.md, v2_server.md, and v2_storage.md * Added v2 components with information on the presenttional components pattern.
This commit is contained in:
parent
78f7a1b95c
commit
1801f557a2
11 changed files with 152 additions and 90 deletions
|
|
@ -1,7 +1,6 @@
|
|||
# Inspector V2 Brief
|
||||
|
||||
### Brief | [V1 Problems](v1_problems.md) | [V2 Scope](v2_scope.md) | [V2 Tech Stack](v2_tech_stack.md)
|
||||
### UX: [Overview](v2_ux.md) | [Features](v2_ux_features.md) | [Handlers](v2_ux_handlers.md) | [Screenshots](v2_screenshots.md)
|
||||
### Brief | [V1 Problems](v1_problems.md) | [V2 Scope](v2_scope.md) | [V2 Tech Stack](v2_web_client.md) | [V2 UX](v2_ux.md)
|
||||
|
||||
## Table of Contents
|
||||
* [Motivation and Context](#motivation-and-context)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Inspector V1 Problems
|
||||
|
||||
### [Brief](README.md) | V1 Problems | [V2 Scope](v2_scope.md) | [V2 Tech Stack](v2_tech_stack.md)
|
||||
### UX: [Overview](v2_ux.md) | [Features](v2_ux_features.md) | [Handlers](v2_ux_handlers.md) | [Screenshots](v2_screenshots.md)
|
||||
### [Brief](README.md) | V1 Problems | [V2 Scope](v2_scope.md) | [V2 Tech Stack](v2_web_client.md) | [V2 UX](v2_ux.md)
|
||||
|
||||
|
||||
## Table of Contents
|
||||
* [Monolithic components](#monolithic-components)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Inspector V2 Scope
|
||||
|
||||
### [Brief](README.md) | [V1 Problems](v1_problems.md) | V2 Scope | [V2 Tech Stack](v2_tech_stack.md)
|
||||
### UX: [Overview](v2_ux.md) | [Features](v2_ux_features.md) | [Handlers](v2_ux_handlers.md) | [Screenshots](v2_screenshots.md)
|
||||
### [Brief](README.md) | [V1 Problems](v1_problems.md) | V2 Scope | [V2 Tech Stack](v2_web_client.md) | [V2 UX](v2_ux.md)
|
||||
|
||||
|
||||
## Table of Contents
|
||||
* [Protocol Features](#protocol-features)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
# Inspector V2 UX - Visual Reference
|
||||
|
||||
### [Brief](README.md) | [V1 Problems](v1_problems.md) | [V2 Scope](v2_scope.md) | [V2 Tech Stack](v2_tech_stack.md)
|
||||
### UX: [Overview](v2_ux.md) | [Features](v2_ux_features.md) | [Handlers](v2_ux_handlers.md) | Screenshots
|
||||
### [Brief](README.md) | [V1 Problems](v1_problems.md) | [V2 Scope](v2_scope.md) | [V2 Tech Stack](v2_web_client.md) | V2 UX
|
||||
#### [Overview](v2_ux.md) | [Features](v2_ux_features.md) | [Handlers](v2_ux_handlers.md) | Screenshots | [Components](v2_ux_components.md)
|
||||
|
||||
---
|
||||
|
||||
This document provides visual screenshots from the **Shadcn UI prototype** (`v2/prototype/shadcn`). These complement the ASCII wireframes in the UX specification documents.
|
||||
|
||||
**Note:** The V2 implementation will use **Mantine** (see [V2 Tech Stack](v2_tech_stack.md#mantine-rationale)). These Shadcn screenshots remain as historical reference showing how the UX spec translates to a working UI. The Mantine implementation will have the same screens and functionality with slightly different visual styling.
|
||||
**Note:** The V2 implementation will use **Mantine** (see [V2 Tech Stack](v2_web_client.md#mantine-rationale)). These Shadcn screenshots remain as historical reference showing how the UX spec translates to a working UI. The Mantine implementation will have the same screens and functionality with slightly different visual styling.
|
||||
|
||||
## Table of Contents
|
||||
* [Server List](#server-list)
|
||||
|
|
|
|||
111
specification/v2_server.md
Normal file
111
specification/v2_server.md
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
# Inspector V2 Tech Stack - Server
|
||||
|
||||
### [Brief](README.md) | [V1 Problems](v1_problems.md) | [V2 Scope](v2_scope.md) | V2 Tech Stack | [V2 UX](v2_ux.md)
|
||||
|
||||
#### [Web Client](v2_web_client.md) | Server | [Storage](v2_storage.md)
|
||||
|
||||
## Overview
|
||||
### Language and Runtime
|
||||
* Typescript
|
||||
* Node
|
||||
|
||||
### Transport Operation
|
||||
Let's consider how to operate the server transports.
|
||||
* -[x] [Hono](https://hono.dev/)
|
||||
* -[ ] [Express](https://expressjs.com/)
|
||||
* -[ ] Node:[http](https://nodejs.org/docs/v22.18.0/api/http.html)
|
||||
|
||||
#### Hono Rationale
|
||||
|
||||
Hono is selected based on community consensus (PR #945 discussion) for alignment with modern web standards and the TypeScript SDK v2 direction.
|
||||
|
||||
**Why Hono over Express:**
|
||||
|
||||
| Requirement | Hono | Express |
|
||||
|-------------|------|---------|
|
||||
| Bundle size | 12kb | ~1mb |
|
||||
| Web Standards (Request/Response) | Yes - Native | No - Shimmed |
|
||||
| TypeScript native | Yes | No - @types package |
|
||||
| Tree-shakable | Yes - Fully | No |
|
||||
| HTTP/2 support | Yes | No (SPDY dropped) |
|
||||
| Built-in middleware | Yes - Body parsing, auth, etc. | No - Requires plugins |
|
||||
| Edge/serverless deployment | Yes - Native | Partial - Requires adapters |
|
||||
|
||||
**Benefits:**
|
||||
|
||||
1. **Web Standards Alignment** - Uses native `Request`/`Response` objects, enabling deployment across Node, Deno, Bun, serverless, and edge environments
|
||||
2. **TypeScript Native** - Full type safety without external type packages, no monkey-patching of request objects
|
||||
3. **Future-proofing** - HTTP/2 support enables potential gRPC transport; aligns with TypeScript SDK v2 plans
|
||||
4. **Developer Experience** - Simpler API, type-safe context, smaller learning curve
|
||||
5. **Bundle Efficiency** - Dramatically smaller footprint benefits both development and deployment
|
||||
|
||||
### Logging
|
||||
Let's step up our logging capability with an advanced logger:
|
||||
* -[ ] [Winston](https://github.com/winstonjs/winston?tab=readme-ov-file#winston)
|
||||
* -[x] [Pino](https://github.com/pinojs/pino?tab=readme-ov-file#pino)
|
||||
* -[ ] [Morgan](https://github.com/expressjs/morgan?tab=readme-ov-file#morgan)
|
||||
* -[ ] [Log4js](https://github.com/stritti/log4js?tab=readme-ov-file#log4js)
|
||||
* -[ ] [Bunyan](https://github.com/trentm/node-bunyan)
|
||||
|
||||
#### Pino Rationale
|
||||
|
||||
Pino is selected for synergy with the **History Screen** feature. The log file serves dual purposes:
|
||||
1. **Server diagnostics** - Standard application logging
|
||||
2. **History persistence** - Request/response replay source
|
||||
|
||||
**Why Pino over Winston:**
|
||||
|
||||
| Requirement | Pino | Winston |
|
||||
|-------------|------|---------|
|
||||
| Default JSON format | Yes - NDJSON | No - Text (needs config) |
|
||||
| Line-by-line parsing | Yes - Native | No - Extra work |
|
||||
| High-throughput logging | Yes - Very fast | Partial - Slower |
|
||||
| Log rotation | Yes - pino-roll | Yes - winston-daily-rotate |
|
||||
| Dev-friendly output | Yes - pino-pretty | Yes - Built-in |
|
||||
|
||||
**Architecture:**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Server │
|
||||
│ │
|
||||
│ MCP Request ──▶ Pino Logger ──┬──▶ history.ndjson (file) │
|
||||
│ │ │
|
||||
│ └──▶ Console (pino-pretty) │
|
||||
│ │
|
||||
│ History API: GET /api/history?method=tools/call&limit=50 │
|
||||
│ (parses history.ndjson, returns filtered JSON) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Log Entry Schema:**
|
||||
|
||||
Each MCP operation logs a request/response pair:
|
||||
|
||||
```json
|
||||
{"ts":1732987200000,"level":"info","type":"mcp_request","method":"tools/call","target":"echo","params":{"message":"hello"},"requestId":"abc123","serverId":"my-server"}
|
||||
{"ts":1732987200045,"level":"info","type":"mcp_response","requestId":"abc123","result":{"content":[{"type":"text","text":"hello"}]},"duration":45,"success":true}
|
||||
```
|
||||
|
||||
**Schema fields:**
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `ts` | Unix timestamp (ms) |
|
||||
| `level` | Log level (info, error) |
|
||||
| `type` | `mcp_request` or `mcp_response` |
|
||||
| `method` | MCP method (tools/call, resources/read, prompts/get, etc.) |
|
||||
| `target` | Tool name, resource URI, or prompt name |
|
||||
| `params` | Request parameters |
|
||||
| `requestId` | Correlation ID linking request to response |
|
||||
| `serverId` | Server identifier |
|
||||
| `result` | Response data (for mcp_response) |
|
||||
| `error` | Error message (for failed requests) |
|
||||
| `duration` | Response time in ms |
|
||||
| `success` | Boolean success indicator |
|
||||
|
||||
**Dependencies:**
|
||||
- `pino` - Core logger
|
||||
- `pino-pretty` - Dev console formatting
|
||||
- `pino-roll` - Log rotation (optional)
|
||||
- `pino-http` - Express integration
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
# V2 Storage Specification
|
||||
# Inspector V2 Tech Stack - Storage Specification
|
||||
|
||||
**Status:** Draft
|
||||
**Issue:** [#983](https://github.com/modelcontextprotocol/inspector/issues/983)
|
||||
**Last Updated:** 2026-02-01
|
||||
### Brief | [V1 Problems](v1_problems.md) | [V2 Scope](v2_scope.md) | V2 Tech Stack | [V2 UX](v2_ux.md)
|
||||
|
||||
#### [Web Client](v2_web_client.md) | [Server](v2_server.md) | Storage
|
||||
|
||||
## Overview
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ The Pino logger on the proxy writes MCP request/response records to NDJSON forma
|
|||
- Caching fetched history for UI performance
|
||||
- User preferences that don't belong on the server
|
||||
|
||||
See [v2_tech_stack.md](./v2_tech_stack.md#pino-rationale) for Pino configuration details.
|
||||
See [v2_server.md](./v2_server.md#pino-rationale) for Pino configuration details.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# Inspector V2 UX Specification
|
||||
# Inspector V2 UX - Specification
|
||||
|
||||
### [Brief](README.md) | [V1 Problems](v1_problems.md) | [V2 Scope](v2_scope.md) | [V2 Tech Stack](v2_tech_stack.md) | V2 UX
|
||||
### [Brief](README.md) | [V1 Problems](v1_problems.md) | [V2 Scope](v2_scope.md) | [V2 Tech Stack](v2_web_client.md) | V2 UX
|
||||
#### Overview | [Features](v2_ux_features.md) | [Handlers](v2_ux_handlers.md) | [Screenshots](v2_screenshots.md) | [Components](v2_ux_components.md)
|
||||
|
||||
## Table of Contents
|
||||
* [Design Principles](#design-principles)
|
||||
|
|
|
|||
66
specification/v2_ux_components.md
Normal file
66
specification/v2_ux_components.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# Inspector V2 UX - Component Development
|
||||
|
||||
### [Brief](README.md) | [V1 Problems](v1_problems.md) | [V2 Scope](v2_scope.md) | [V2 Tech Stack](v2_web_client.md) | V2 UX
|
||||
#### [Overview](v2_ux.md) | [Features](v2_ux_features.md) | [Handlers](v2_ux_handlers.md) | [Screenshots](v2_screenshots.md) | Components
|
||||
|
||||
We are developing React components with Mantine, Zustand, and Storybook using a pattern called "Presentational Components" that emphasizes separation of concerns and testability.
|
||||
|
||||
## The Presentational / Container Split
|
||||
|
||||
The idea is that a component receives everything it needs through props — the data it renders *and* the callbacks it fires when the user interacts. The component itself has no idea where the data came from or what happens when a button is clicked. It just calls `onSave(formData)` and trusts that someone upstream is handling it.
|
||||
|
||||
This creaes clean layering: Zustand stores own the state and logic, container components (or hooks) wire stores to presentational components, and the presentational components themselves are pure rendering machines. Storybook targets that bottom layer exclusively.
|
||||
|
||||
## What Storybook Does
|
||||
|
||||
Storybook gives you a dev server that renders individual components in isolation, outside your application's component tree. You write "stories" — which are essentially named configurations of a component with specific props. A story for a `UserCard` might look like:
|
||||
|
||||
```typescript
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
user: { name: 'Ada Lovelace', role: 'Engineer' },
|
||||
onEdit: fn(),
|
||||
onDelete: fn(),
|
||||
},
|
||||
};
|
||||
|
||||
export const LongName: Story = {
|
||||
args: {
|
||||
user: { name: 'A Very Long Username That Might Break Layout', role: 'Admin' },
|
||||
onEdit: fn(),
|
||||
onDelete: fn(),
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
Each story appears in a sidebar, and you click through them to visually verify the component under different conditions. The `fn()` helper from Storybook creates a mock function that logs calls to an "Actions" panel, so you can see "onDelete was called with these arguments" without any real logic executing.
|
||||
|
||||
## Storybook Development Loop
|
||||
|
||||
The typical cycle is: define the component's prop interface (the "model" in your framing), write the component to render based on those props, write stories that exercise various prop combinations (empty states, error states, loading, overflowing content, etc.), and then visually verify in the Storybook UI. You do all of this before the component ever touches real data or lives inside the actual app.
|
||||
|
||||
This naturally pushes you toward building bottom-up — atoms first (buttons, inputs, badges), then molecules (form groups, cards), then organisms (full panels, modals). You can't easily skip ahead and build a page-level component first because it would need too many dependencies you haven't isolated yet.
|
||||
|
||||
## Disciplines That Keep Logic Out of Components
|
||||
|
||||
This is where the real challenge lives, because React makes it *very* easy to let logic creep in. A few principles worth internalizing:
|
||||
|
||||
**Props as the complete contract.** If a component needs to know whether a user has permission to delete something, don't pass in the user's role and let the component figure it out. Pass `canDelete: boolean`. The decision logic belongs in the store or a hook, not in the rendering layer. This also makes stories trivial to write — you just set `canDelete` to `true` or `false`.
|
||||
|
||||
**Callbacks over store access.** If a presentational component imports a Zustand store directly, it's no longer testable in isolation without mocking the store. Instead, have a thin wrapper hook or container that does `const { items, addItem } = useStore(...)` and passes those down. The presentational component just sees `items: Item[]` and `onAdd: (item: Item) => void`.
|
||||
|
||||
**Derived state lives outside.** Filtering, sorting, computed totals — all of that should happen in Zustand selectors or custom hooks, not inside the component. The component receives the *already filtered* list.
|
||||
|
||||
**Local UI state is the exception.** Things like "is this dropdown open" or "which tab is active" are legitimately component-internal. That's fine — that's UI state, not application state. The heuristic is: if the application would need to know about it (say, to restore state on navigation), it belongs in Zustand. If only the component cares, `useState` is appropriate.
|
||||
|
||||
## Mantine-Specific Considerations
|
||||
|
||||
Since Mantine provides a theming layer and its own component library, we will want our Storybook to wrap stories in Mantine's `MantineProvider` with our theme. This is done through Storybook's `decorators` in the config, so every story gets the correct styling context. We can just write global decorators manually, but there's a `storybook-addon-mantine` package that simplifies this.
|
||||
|
||||
## Is Storybook the Only Option?
|
||||
|
||||
It's the most mature choice, but it's not the only one. Ladle is a lighter alternative with a similar API. Histoire is popular in the Vue world but has React support. Some teams skip a dedicated tool entirely and just create a `/dev` route in their app that renders a component gallery — less infrastructure, but you lose the story-based organization and addon ecosystem.
|
||||
|
||||
For your stack, Storybook is the most well-trodden path and has the best TypeScript support for auto-generating controls from your prop types, which is genuinely useful. It will infer knobs/controls from your interfaces so you can tweak props interactively in the browser.
|
||||
|
||||
The bottom line: your instinct is right. Design your components around a typed model (the props interface), keep decision-making in Zustand, and Storybook becomes the natural place to develop and verify the visual layer independently. The discipline isn't really about Storybook — it's about prop interface design. Storybook just makes it obvious when you've violated the boundary, because a component that reaches outside its props becomes painful to write stories for.
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
# Inspector V2 UX - Feature Screens
|
||||
|
||||
### [Brief](README.md) | [V1 Problems](v1_problems.md) | [V2 Scope](v2_scope.md) | [V2 Tech Stack](v2_tech_stack.md)
|
||||
### UX: [Overview](v2_ux.md) | Features | [Handlers](v2_ux_handlers.md) | [Screenshots](v2_screenshots.md)
|
||||
### [Brief](README.md) | [V1 Problems](v1_problems.md) | [V2 Scope](v2_scope.md) | [V2 Tech Stack](v2_web_client.md) | V2 UX
|
||||
#### [Overview](v2_ux.md) | Features | [Handlers](v2_ux_handlers.md) | [Screenshots](v2_screenshots.md) | [Components](v2_ux_components.md)
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Inspector V2 UX - Handlers & Patterns
|
||||
|
||||
### [Brief](README.md) | [V1 Problems](v1_problems.md) | [V2 Scope](v2_scope.md) | [V2 Tech Stack](v2_tech_stack.md)
|
||||
### UX: [Overview](v2_ux.md) | [Features](v2_ux_features.md) | Handlers | [Screenshots](v2_screenshots.md)
|
||||
### [Brief](README.md) | [V1 Problems](v1_problems.md) | [V2 Scope](v2_scope.md) | [V2 Tech Stack](v2_web_client.md) | V2 UX
|
||||
#### [Overview](v2_ux.md) | [Features](v2_ux_features.md) | Handlers | [Screenshots](v2_screenshots.md) | [Components](v2_ux_components.md)
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
61
specification/v2_web_client.md
Normal file
61
specification/v2_web_client.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# Inspector V2 Tech Stack
|
||||
|
||||
### [Brief](README.md) | [V1 Problems](v1_problems.md) | [V2 Scope](v2_scope.md) | V2 Tech Stack | [V2 UX](v2_ux.md)
|
||||
|
||||
#### Web Client | [Server](v2_server.md) | [Storage](v2_storage.md)
|
||||
|
||||
## Overview
|
||||
### Language and Framework
|
||||
* Typescript
|
||||
* React
|
||||
|
||||
### Components and Theme
|
||||
* -[x] [Mantine](https://ui.mantine.dev/)
|
||||
* -[ ] [Shadcn](https://ui.shadcn.com/)
|
||||
|
||||
#### Mantine Rationale
|
||||
|
||||
Mantine is recommended based on evaluation of both prototype implementations (`v2/prototype/shadcn` and `v2/prototype/mantine`) and group discussion.
|
||||
|
||||
**Notes from Prototype Comparison:**
|
||||
|
||||
Shadcn lacks layout components, requiring extensive Tailwind class management for containers, spacing, and alignment. This adds friction and cognitive load, based on our experiences with the current Inspector. Mantine's layout components (`Flex`, `Stack`, etc.) make the code more concise and easier to understand.
|
||||
|
||||
Although the code may be less directly customizable, we don't expect extensive theming or branding to be a priority for Inspector as a debugging tool.
|
||||
|
||||
See [PR #980 discussion](https://github.com/modelcontextprotocol/inspector/pull/980#issuecomment-3667102518) for example code comparison.
|
||||
|
||||
| Requirement | Mantine | Shadcn |
|
||||
|-------------|---------|--------|
|
||||
| Layout components | Yes - Flex, Stack, Group, Grid | No - Use Tailwind divs |
|
||||
| Out-of-box experience | Yes - Comprehensive | Partial - Assemble yourself |
|
||||
| Code verbosity | Concise JSX props | Verbose Tailwind classes |
|
||||
| Styling approach | Props + theme config | Tailwind utility classes |
|
||||
| Documentation | Extensive API docs | Component examples |
|
||||
|
||||
**Benefits:**
|
||||
|
||||
1. **Built-in Layout Components** - Mantine provides layout primitives as JSX components:
|
||||
- `Flex`, `Stack`, `Group`, `Grid`, `Center`, `Container`
|
||||
- No need to manage `div` elements with Tailwind classes
|
||||
- More declarative, readable code
|
||||
|
||||
2. **Reduced Class Management** - Compare the same UI element:
|
||||
```tsx
|
||||
// Mantine - concise
|
||||
<Alert icon={<IconAlertTriangle />} color="yellow" title="Warning">
|
||||
{message}
|
||||
</Alert>
|
||||
|
||||
// Shadcn + Tailwind - verbose class strings
|
||||
<div className="flex items-start gap-3 rounded-lg border border-yellow-200
|
||||
bg-yellow-50 p-4 text-yellow-800 dark:border-yellow-900 dark:bg-yellow-950
|
||||
dark:text-yellow-200">
|
||||
<IconAlertTriangle className="h-5 w-5 flex-shrink-0" />
|
||||
<div className="flex-1"><p className="font-medium">Warning</p>{message}</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
3. **Better Out-of-Box Experience**:
|
||||
- Single install provides all components
|
||||
- Consistent API across all components
|
||||
Loading…
Reference in a new issue