docs: update documentation to use singular voice
Replace plural pronouns (we, our, us) with singular/project voice throughout documentation files to represent a singular developer perspective. Changes: - CONTRIBUTING.md: Replace "We are" with "This project is", "We use" with "This project uses", "our" with "the" - README.md: Replace "our" with "the", add OS to bug report checklist - docs/VERSION_HISTORY.md: Replace "we reset" with passive voice "the version was reset"
This commit is contained in:
@@ -15,7 +15,7 @@ Thank you for your interest in contributing to the Obsidian MCP Server Plugin! T
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
We are committed to providing a welcoming and inclusive environment. Please be respectful and constructive in all interactions.
|
||||
This project is committed to providing a welcoming and inclusive environment. Please be respectful and constructive in all interactions.
|
||||
|
||||
## Getting Started
|
||||
|
||||
@@ -100,7 +100,7 @@ For feature requests, please describe:
|
||||
- `refactor/cleanup-utils` for refactoring
|
||||
|
||||
2. **Make your changes:**
|
||||
- Write code following our [Code Guidelines](#code-guidelines)
|
||||
- Write code following the [Code Guidelines](#code-guidelines)
|
||||
- Add tests for new functionality
|
||||
- Update documentation as needed
|
||||
|
||||
@@ -305,7 +305,7 @@ Before submitting, ensure:
|
||||
|
||||
### Versioning
|
||||
|
||||
We use [Semantic Versioning](https://semver.org/):
|
||||
This project uses [Semantic Versioning](https://semver.org/):
|
||||
- **Major** (1.0.0): Breaking changes
|
||||
- **Minor** (0.1.0): New features, backward compatible
|
||||
- **Patch** (0.0.1): Bug fixes, backward compatible
|
||||
|
||||
19
README.md
19
README.md
@@ -284,13 +284,23 @@ npm run build # Production build
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! If you'd like to contribute to this plugin:
|
||||
Contributions are welcome! Please see the [Contributing Guidelines](CONTRIBUTING.md) for detailed information on:
|
||||
|
||||
- Development setup and workflow
|
||||
- Code style and architecture guidelines
|
||||
- Testing requirements
|
||||
- Pull request process
|
||||
- Release procedures
|
||||
|
||||
### Quick Start for Contributors
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
||||
3. Commit your changes
|
||||
4. Push to the branch (`git push origin feature/amazing-feature`)
|
||||
5. Open a Pull Request
|
||||
3. Make your changes with tests
|
||||
4. Run `npm test` and `npm run build`
|
||||
5. Commit your changes
|
||||
6. Push to the branch (`git push origin feature/amazing-feature`)
|
||||
7. Open a Pull Request
|
||||
|
||||
### Reporting Issues
|
||||
|
||||
@@ -301,6 +311,7 @@ Found a bug or have a feature request? Please open an issue on GitHub:
|
||||
When reporting bugs, please include:
|
||||
- Obsidian version
|
||||
- Plugin version
|
||||
- Operating system
|
||||
- Steps to reproduce the issue
|
||||
- Any error messages from the Developer Console (Ctrl+Shift+I)
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ This plugin's first public release is marked as **version 1.0.0**.
|
||||
|
||||
Prior to public release, the plugin went through private development with internal versions 1.0.0 through 3.0.0. These versions were used during development and testing but were never publicly released.
|
||||
|
||||
When preparing for public release, we reset the version to 1.0.0 to clearly mark this as the first public version available to users.
|
||||
When preparing for public release, the version was reset to 1.0.0 to clearly mark this as the first public version available to users.
|
||||
|
||||
### Why Reset to 1.0.0?
|
||||
|
||||
|
||||
@@ -1,293 +0,0 @@
|
||||
# GitHub Release Workflow Design
|
||||
|
||||
**Date**: 2025-10-26
|
||||
**Status**: Approved
|
||||
**Author**: William Ballou with Claude Code
|
||||
|
||||
## Overview
|
||||
|
||||
This document describes the design for an automated GitHub Actions workflow that builds and releases the Obsidian MCP Server plugin when version tags are pushed to the repository.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Functional Requirements
|
||||
- Trigger on semantic version tags (e.g., `1.0.0`, `1.2.3`)
|
||||
- Validate version consistency across `package.json`, `manifest.json`, and git tag
|
||||
- Run test suite and fail if tests don't pass
|
||||
- Build plugin using production build process
|
||||
- Create draft GitHub release with required Obsidian plugin files
|
||||
- Allow manual review and release note writing before publishing
|
||||
|
||||
### Non-Functional Requirements
|
||||
- Simple, single-job architecture for easy debugging
|
||||
- Clear error messages when validation or tests fail
|
||||
- Deterministic builds using `npm ci`
|
||||
- Fast execution using GitHub Actions caching
|
||||
|
||||
## Design Decisions
|
||||
|
||||
### Automation Level: Semi-Automated
|
||||
The workflow creates a **draft release** rather than auto-publishing. This provides:
|
||||
- Safety: Manual review before users can download
|
||||
- Quality: Time to write detailed release notes
|
||||
- Flexibility: Can delete and re-run if issues found
|
||||
- Control: Verify attached files are correct
|
||||
|
||||
### Test Policy: Block on Failure
|
||||
Tests must pass for release to proceed. This prevents:
|
||||
- Releasing broken code
|
||||
- Regressions reaching users
|
||||
- Build-time type errors in production
|
||||
|
||||
Trade-off: Urgent hotfixes require fixing or temporarily skipping failing tests.
|
||||
|
||||
### Version Validation: Required
|
||||
Workflow validates that version numbers match across:
|
||||
- Git tag (e.g., `1.2.3`)
|
||||
- `package.json` version field
|
||||
- `manifest.json` version field
|
||||
|
||||
This catches common mistakes and ensures consistency required by Obsidian plugin spec.
|
||||
|
||||
### Architecture: Single Job
|
||||
All steps run sequentially in one job:
|
||||
1. Checkout code
|
||||
2. Validate versions
|
||||
3. Setup Node.js and dependencies
|
||||
4. Run tests
|
||||
5. Build plugin
|
||||
6. Create draft release
|
||||
|
||||
**Rationale**: Simpler than multi-job approach, easier to debug, acceptable runtime (~3-5 minutes).
|
||||
|
||||
## Workflow Architecture
|
||||
|
||||
### Trigger Pattern
|
||||
```yaml
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "[0-9]+.[0-9]+.[0-9]+"
|
||||
```
|
||||
|
||||
Matches semantic version tags without `v` prefix (per CLAUDE.md requirement).
|
||||
|
||||
### Step 1: Version Validation
|
||||
|
||||
**Purpose**: Ensure tag, package.json, and manifest.json versions all match.
|
||||
|
||||
**Implementation**:
|
||||
```bash
|
||||
TAG_VERSION="${GITHUB_REF#refs/tags/}"
|
||||
PKG_VERSION=$(node -p "require('./package.json').version")
|
||||
MANIFEST_VERSION=$(node -p "require('./manifest.json').version")
|
||||
|
||||
if [ "$TAG_VERSION" != "$PKG_VERSION" ] || [ "$TAG_VERSION" != "$MANIFEST_VERSION" ]; then
|
||||
echo "❌ Version mismatch detected!"
|
||||
echo "Git tag: $TAG_VERSION"
|
||||
echo "package.json: $PKG_VERSION"
|
||||
echo "manifest.json: $MANIFEST_VERSION"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
**Error cases**:
|
||||
- Forgot to run `npm version` before tagging
|
||||
- Manually edited version in one file but not others
|
||||
- Typo in git tag name
|
||||
|
||||
### Step 2: Dependency Setup
|
||||
|
||||
**Node.js version**: 18.x (LTS)
|
||||
|
||||
**Installation**: Use `npm ci` for:
|
||||
- Deterministic builds from package-lock.json
|
||||
- Faster than `npm install`
|
||||
- Catches lock file inconsistencies
|
||||
|
||||
**Caching**: GitHub Actions built-in npm cache:
|
||||
```yaml
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '18'
|
||||
cache: 'npm'
|
||||
```
|
||||
|
||||
### Step 3: Test Execution
|
||||
|
||||
**Command**: `npm test`
|
||||
|
||||
**Behavior**:
|
||||
- Runs Jest test suite
|
||||
- Workflow fails if any test fails
|
||||
- Test output appears in Actions logs
|
||||
- No coverage report (development tool, not needed for releases)
|
||||
|
||||
**Error handling**:
|
||||
- Test failures → workflow stops, no release created
|
||||
- Clear error logs for debugging
|
||||
|
||||
### Step 4: Build Process
|
||||
|
||||
**Command**: `npm run build`
|
||||
|
||||
**What it does**:
|
||||
1. Type checking: `tsc -noEmit -skipLibCheck`
|
||||
2. Bundling: `node esbuild.config.mjs production`
|
||||
3. Output: `main.js` in repository root
|
||||
|
||||
**Validation**: After build, verify these files exist:
|
||||
- `main.js` (build artifact)
|
||||
- `manifest.json` (repo file)
|
||||
- `styles.css` (repo file)
|
||||
|
||||
If any missing → workflow fails.
|
||||
|
||||
### Step 5: Draft Release Creation
|
||||
|
||||
**Tool**: GitHub CLI (`gh release create`)
|
||||
|
||||
**Command**:
|
||||
```bash
|
||||
gh release create "$TAG_VERSION" \
|
||||
--title="$TAG_VERSION" \
|
||||
--draft \
|
||||
main.js \
|
||||
manifest.json \
|
||||
styles.css
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- Tag: Extracted from `GITHUB_REF` (e.g., `1.2.3`)
|
||||
- Title: Same as tag version
|
||||
- `--draft`: Creates unpublished release
|
||||
- Files: Three required Obsidian plugin files
|
||||
|
||||
**Authentication**: Uses built-in `GITHUB_TOKEN` secret (no manual setup needed)
|
||||
|
||||
## Developer Workflow
|
||||
|
||||
### Creating a Release
|
||||
|
||||
**Step 1**: Update version
|
||||
```bash
|
||||
npm version patch # or minor/major
|
||||
```
|
||||
This triggers `version-bump.mjs` which updates `manifest.json` and `versions.json`.
|
||||
|
||||
**Step 2**: Commit version bump
|
||||
```bash
|
||||
git commit -m "chore: bump version to X.Y.Z"
|
||||
```
|
||||
|
||||
**Step 3**: Create and push tag
|
||||
```bash
|
||||
git tag X.Y.Z
|
||||
git push && git push --tags
|
||||
```
|
||||
|
||||
**Step 4**: Wait for GitHub Actions
|
||||
- Navigate to Actions tab
|
||||
- Watch workflow progress
|
||||
- Check for success or errors
|
||||
|
||||
**Step 5**: Review draft release
|
||||
- Navigate to Releases tab
|
||||
- Find draft release
|
||||
- Review attached files (download and verify if needed)
|
||||
|
||||
**Step 6**: Write release notes
|
||||
- Document what's new
|
||||
- Note breaking changes
|
||||
- List bug fixes
|
||||
- Credit contributors
|
||||
|
||||
**Step 7**: Publish release
|
||||
- Click "Publish release" button
|
||||
- Release becomes visible to users
|
||||
|
||||
### Handling Failures
|
||||
|
||||
**Version mismatch**:
|
||||
- Fix version in mismatched file(s)
|
||||
- Commit fix
|
||||
- Delete incorrect tag: `git tag -d X.Y.Z && git push origin :refs/tags/X.Y.Z`
|
||||
- Recreate tag and push
|
||||
|
||||
**Test failures**:
|
||||
- Fix failing tests locally
|
||||
- Commit fixes
|
||||
- Delete tag and recreate (or create new patch version)
|
||||
|
||||
**Build failures**:
|
||||
- Check workflow logs for error
|
||||
- Fix build issue locally
|
||||
- Verify `npm run build` succeeds
|
||||
- Delete tag and recreate (or create new patch version)
|
||||
|
||||
**Release creation failures**:
|
||||
- Check if release already exists for tag
|
||||
- Delete existing release if needed
|
||||
- Re-run workflow or manually trigger
|
||||
|
||||
## File Locations
|
||||
|
||||
- Workflow file: `.github/workflows/release.yml`
|
||||
- Build output: `main.js` (root directory)
|
||||
- Required assets: `manifest.json`, `styles.css` (root directory)
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Version Validation Errors
|
||||
**Detection**: Before any build steps
|
||||
**Message**: Clear output showing all three versions
|
||||
**Resolution**: Fix mismatched files, delete/recreate tag
|
||||
|
||||
### Test Failures
|
||||
**Detection**: After dependencies installed
|
||||
**Message**: Jest output showing which tests failed
|
||||
**Resolution**: Fix tests, commit, delete/recreate tag
|
||||
|
||||
### Build Failures
|
||||
**Detection**: During build step
|
||||
**Message**: TypeScript or esbuild errors
|
||||
**Resolution**: Fix build errors locally, verify, delete/recreate tag
|
||||
|
||||
### Missing Files
|
||||
**Detection**: After build completes
|
||||
**Message**: Which required file is missing
|
||||
**Resolution**: Investigate why file wasn't created, fix, retry
|
||||
|
||||
### Release Creation Failures
|
||||
**Detection**: During gh CLI command
|
||||
**Message**: GitHub API error (e.g., release already exists)
|
||||
**Resolution**: Delete existing release or use different tag
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Not Included in Initial Design
|
||||
- Automated release notes generation (manual preferred for quality)
|
||||
- Slack/Discord notifications on release
|
||||
- Automatic changelog updates
|
||||
- Release to Obsidian community plugin registry (separate process)
|
||||
- Build artifact signing/verification
|
||||
|
||||
These can be added later if needed.
|
||||
|
||||
## Success Criteria
|
||||
|
||||
The workflow is successful if:
|
||||
1. ✅ Triggers only on semantic version tags
|
||||
2. ✅ Catches version mismatches before building
|
||||
3. ✅ Prevents releases with failing tests
|
||||
4. ✅ Produces correct build artifacts
|
||||
5. ✅ Creates draft releases with all required files
|
||||
6. ✅ Provides clear error messages on failures
|
||||
7. ✅ Completes in under 5 minutes for typical builds
|
||||
|
||||
## References
|
||||
|
||||
- Obsidian plugin guidelines: https://docs.obsidian.md/Plugins/Releasing/Plugin+guidelines
|
||||
- GitHub Actions docs: https://docs.github.com/en/actions
|
||||
- Semantic Versioning: https://semver.org/
|
||||
- Project CLAUDE.md: Requirements and constraints
|
||||
@@ -1,242 +0,0 @@
|
||||
# MCP Configuration UI Improvements - Design Document
|
||||
|
||||
**Date:** 2025-10-26
|
||||
**Status:** Implemented
|
||||
**Author:** Design brainstorming session
|
||||
|
||||
## Overview
|
||||
|
||||
Improve the MCP configuration UI in the Obsidian plugin settings to make it easier for users to configure different MCP clients. The current configuration is nested under Authentication and only shows a single Windsurf example. This design adds tab-based navigation to show client-specific configurations that are complete and ready to copy with a single click.
|
||||
|
||||
## Goals
|
||||
|
||||
- **Ease of use:** Single-click copy of complete, ready-to-paste configurations
|
||||
- **Multi-client support:** Provide configurations for Windsurf and Claude Code
|
||||
- **Clear guidance:** Show file location and usage instructions inline
|
||||
- **Better organization:** Combine Authentication and MCP Configuration into one collapsible section without nesting
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- Each config is complete with API key and port pre-populated
|
||||
- One-click copy button for immediate paste into the client's config location
|
||||
- Clear indication of where each config should be pasted
|
||||
- Tab interface allows easy switching between client configs
|
||||
|
||||
## Design
|
||||
|
||||
### Overall Structure
|
||||
|
||||
**Current state:** MCP Configuration is nested inside Authentication (settings.ts:204-255)
|
||||
|
||||
**New structure:**
|
||||
```
|
||||
Settings Page
|
||||
├── Server Status (unchanged)
|
||||
├── Auto-start setting (unchanged)
|
||||
├── Port setting (unchanged)
|
||||
├── Authentication & Configuration ← Renamed from "Authentication"
|
||||
│ ├── API Key Management (unchanged)
|
||||
│ ├── MCP Client Configuration ← No longer nested, same level as API Key
|
||||
│ │ ├── Tab: Windsurf
|
||||
│ │ └── Tab: Claude Code
|
||||
└── UI Notifications (unchanged)
|
||||
```
|
||||
|
||||
**Key changes:**
|
||||
1. Rename "Authentication" section to "Authentication & Configuration"
|
||||
2. Remove the nested `<details>` element for "MCP Client Configuration"
|
||||
3. Place "MCP Client Configuration" at the same level as "API Key Management"
|
||||
4. Add tab interface to switch between client configs
|
||||
|
||||
### Tab Interface Design
|
||||
|
||||
**Visual structure:**
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ [Windsurf] [Claude Code] │ ← Tab buttons
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ Configuration file location: │
|
||||
│ ~/.windsurf/config.json │ ← File path
|
||||
│ │
|
||||
│ [📋 Copy Configuration] │ ← Copy button
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────┐│
|
||||
│ │ { ││
|
||||
│ │ "mcpServers": { ││
|
||||
│ │ "obsidian": { ... } ││ ← JSON config
|
||||
│ │ } ││
|
||||
│ │ } ││
|
||||
│ └─────────────────────────────────────────────┘│
|
||||
│ │
|
||||
│ After copying, paste into the config file │ ← Usage note
|
||||
│ and restart the MCP client. │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Tab switching:**
|
||||
- Button-based tabs (not native `<details>` elements)
|
||||
- Only one tab's content visible at a time
|
||||
- Active tab gets visual highlight (background color + border)
|
||||
- Clicking a tab switches the displayed configuration
|
||||
|
||||
**Content for each tab:**
|
||||
1. Configuration file location path
|
||||
2. Copy button
|
||||
3. Complete JSON configuration (pre-formatted, monospace)
|
||||
4. Brief usage note
|
||||
|
||||
### Client-Specific Configurations
|
||||
|
||||
#### 1. Windsurf Tab
|
||||
|
||||
**File location:** `~/.windsurf/config.json`
|
||||
|
||||
**Configuration:**
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"obsidian": {
|
||||
"serverUrl": "http://127.0.0.1:3000/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer <actual-api-key>"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Usage note:** "After copying, paste into the config file and restart Windsurf."
|
||||
|
||||
**Dynamic values:**
|
||||
- Port: `this.plugin.settings.port`
|
||||
- API Key: `this.plugin.settings.apiKey`
|
||||
|
||||
#### 2. Claude Code Tab
|
||||
|
||||
**File location:** `~/.claude.json`
|
||||
|
||||
**Configuration:**
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"obsidian": {
|
||||
"type": "http",
|
||||
"url": "http://127.0.0.1:3000/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer <actual-api-key>"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Usage note:** "After copying, paste into the config file and restart Claude Code."
|
||||
|
||||
**Dynamic values:**
|
||||
- Port: `this.plugin.settings.port`
|
||||
- API Key: `this.plugin.settings.apiKey`
|
||||
|
||||
**Key difference from Windsurf:**
|
||||
- Adds `"type": "http"` field
|
||||
- Uses same URL format as Windsurf
|
||||
|
||||
#### Note on Claude Desktop
|
||||
|
||||
Claude Desktop configuration is omitted for now as it has not been tested with HTTP transport.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### HTML/CSS Structure
|
||||
|
||||
**Tab buttons:**
|
||||
- Container with `display: flex` for horizontal layout
|
||||
- Active tab styling: distinct background + border-bottom
|
||||
- Inactive tabs: subtle hover effect
|
||||
- Consistent spacing and padding
|
||||
|
||||
**Content area:**
|
||||
- Consistent padding and background color
|
||||
- File path in monospace font with muted color
|
||||
- Pre-formatted text block for JSON with syntax preservation
|
||||
- Copy button with icon for clear affordance
|
||||
|
||||
### Component State
|
||||
|
||||
**Tab state management:**
|
||||
- Add `activeConfigTab` property to `MCPServerSettingTab` class
|
||||
- Type: `'windsurf' | 'claude-code'`
|
||||
- Default: `'windsurf'`
|
||||
- No need to persist in settings (ephemeral UI state)
|
||||
|
||||
**Rendering strategy:**
|
||||
- Re-render only the config content area when tab changes
|
||||
- Keep tab buttons static
|
||||
- Avoid full page re-render for tab switches
|
||||
|
||||
### Configuration Generation
|
||||
|
||||
**Helper method:**
|
||||
```typescript
|
||||
generateConfigForClient(client: 'windsurf' | 'claude-code'): {
|
||||
filePath: string;
|
||||
config: object;
|
||||
usageNote: string;
|
||||
}
|
||||
```
|
||||
|
||||
**Returns:**
|
||||
- `filePath`: Configuration file location for the client
|
||||
- `config`: Complete JSON configuration object
|
||||
- `usageNote`: Brief usage instructions
|
||||
|
||||
**Dynamic interpolation:**
|
||||
- API key from `this.plugin.settings.apiKey`
|
||||
- Port from `this.plugin.settings.port`
|
||||
- Generated at render time to always reflect current settings
|
||||
|
||||
## Migration Notes
|
||||
|
||||
### Code Changes in settings.ts
|
||||
|
||||
1. **Line 146-153:** Rename section
|
||||
- Change "Authentication" to "Authentication & Configuration"
|
||||
|
||||
2. **Line 204-211:** Remove nested details
|
||||
- Remove the `<details>` wrapper for "MCP Client Configuration"
|
||||
- Keep "MCP Client Configuration" as a heading/label
|
||||
|
||||
3. **Line 213-255:** Replace single config with tabs
|
||||
- Remove current single Windsurf config display
|
||||
- Add tab button container
|
||||
- Add tab content rendering logic
|
||||
- Implement tab switching
|
||||
|
||||
4. **Add helper method:** `generateConfigForClient()`
|
||||
- Generate client-specific configurations
|
||||
- Return structured data for rendering
|
||||
|
||||
5. **Add state property:** `activeConfigTab`
|
||||
- Track which tab is selected
|
||||
- Default to 'windsurf'
|
||||
|
||||
### Backward Compatibility
|
||||
|
||||
- No breaking changes to settings structure
|
||||
- No changes to stored settings (API key, port, etc.)
|
||||
- Only UI presentation changes
|
||||
|
||||
## Testing Considerations
|
||||
|
||||
- Verify both tab configs copy correctly to clipboard
|
||||
- Test with different API keys and ports
|
||||
- Ensure tab switching works smoothly
|
||||
- Verify JSON formatting is preserved in clipboard
|
||||
- Test that configs work when pasted into actual client config files
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- Add Claude Desktop configuration once HTTP transport is tested
|
||||
- Add validation to check if config files exist
|
||||
- Add "Open config file" button that launches the file in system editor
|
||||
- Add config file templates for other MCP clients
|
||||
- Show detected MCP clients on the system
|
||||
@@ -1,480 +0,0 @@
|
||||
# MCP Configuration UI Improvements Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Add tab-based MCP client configuration UI with single-click copy for Windsurf and Claude Code configurations.
|
||||
|
||||
**Architecture:** Extend the existing settings UI to replace the nested MCP configuration section with a tab-based interface. Add helper methods to generate client-specific configurations dynamically. Use component state to track active tab without persisting to settings.
|
||||
|
||||
**Tech Stack:** TypeScript, Obsidian Plugin API, HTML/CSS for UI
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Add Tab State and Configuration Generator
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/settings.ts:6-13` (class properties)
|
||||
- Modify: `src/settings.ts:70-76` (display method initialization)
|
||||
|
||||
**Step 1: Add activeConfigTab property to class**
|
||||
|
||||
In `src/settings.ts`, add the tab state property after line 8:
|
||||
|
||||
```typescript
|
||||
export class MCPServerSettingTab extends PluginSettingTab {
|
||||
plugin: MCPServerPlugin;
|
||||
private notificationDetailsEl: HTMLDetailsElement | null = null;
|
||||
private activeConfigTab: 'windsurf' | 'claude-code' = 'windsurf';
|
||||
```
|
||||
|
||||
**Step 2: Add configuration generator helper method**
|
||||
|
||||
Add this method after the `renderNotificationSettings` method (after line 68):
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* Generate client-specific MCP configuration
|
||||
*/
|
||||
private generateConfigForClient(client: 'windsurf' | 'claude-code'): {
|
||||
filePath: string;
|
||||
config: object;
|
||||
usageNote: string;
|
||||
} {
|
||||
const port = this.plugin.settings.port;
|
||||
const apiKey = this.plugin.settings.apiKey || 'YOUR_API_KEY_HERE';
|
||||
|
||||
if (client === 'windsurf') {
|
||||
return {
|
||||
filePath: '~/.windsurf/config.json',
|
||||
config: {
|
||||
"mcpServers": {
|
||||
"obsidian": {
|
||||
"serverUrl": `http://127.0.0.1:${port}/mcp`,
|
||||
"headers": {
|
||||
"Authorization": `Bearer ${apiKey}`
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
usageNote: 'After copying, paste into the config file and restart Windsurf.'
|
||||
};
|
||||
} else { // claude-code
|
||||
return {
|
||||
filePath: '~/.claude.json',
|
||||
config: {
|
||||
"mcpServers": {
|
||||
"obsidian": {
|
||||
"type": "http",
|
||||
"url": `http://127.0.0.1:${port}/mcp`,
|
||||
"headers": {
|
||||
"Authorization": `Bearer ${apiKey}`
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
usageNote: 'After copying, paste into the config file and restart Claude Code.'
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 3: Initialize tab state in display method**
|
||||
|
||||
In the `display()` method, after line 76 (clearing notificationDetailsEl), add:
|
||||
|
||||
```typescript
|
||||
// Reset tab state for fresh render
|
||||
this.activeConfigTab = 'windsurf';
|
||||
```
|
||||
|
||||
**Step 4: Run type check**
|
||||
|
||||
Run: `npm run build`
|
||||
Expected: No type errors
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/settings.ts
|
||||
git commit -m "feat: add tab state and config generator for MCP clients"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Rename Authentication Section
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/settings.ts:146-153`
|
||||
|
||||
**Step 1: Update section title**
|
||||
|
||||
Change line 153 from:
|
||||
```typescript
|
||||
authSummary.setText('Authentication');
|
||||
```
|
||||
|
||||
To:
|
||||
```typescript
|
||||
authSummary.setText('Authentication & Configuration');
|
||||
```
|
||||
|
||||
**Step 2: Run type check**
|
||||
|
||||
Run: `npm run build`
|
||||
Expected: No type errors
|
||||
|
||||
**Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add src/settings.ts
|
||||
git commit -m "feat: rename Authentication section to Authentication & Configuration"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Remove Nested MCP Configuration Details Element
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/settings.ts:203-211`
|
||||
|
||||
**Step 1: Remove nested details wrapper**
|
||||
|
||||
Delete lines 204-211 (the nested `<details>` element and its summary):
|
||||
|
||||
```typescript
|
||||
// DELETE THESE LINES:
|
||||
const configDetails = authDetails.createEl('details');
|
||||
configDetails.style.marginTop = '16px';
|
||||
const configSummary = configDetails.createEl('summary');
|
||||
configSummary.style.fontSize = '1em';
|
||||
configSummary.style.fontWeight = 'bold';
|
||||
configSummary.style.marginBottom = '8px';
|
||||
configSummary.style.cursor = 'pointer';
|
||||
configSummary.setText('MCP Client Configuration');
|
||||
```
|
||||
|
||||
**Step 2: Add section heading instead**
|
||||
|
||||
After line 202 (after the API key display), add:
|
||||
|
||||
```typescript
|
||||
// MCP Client Configuration heading
|
||||
const configHeading = authDetails.createEl('h4', {text: 'MCP Client Configuration'});
|
||||
configHeading.style.marginTop = '24px';
|
||||
configHeading.style.marginBottom = '12px';
|
||||
```
|
||||
|
||||
**Step 3: Update parent container reference**
|
||||
|
||||
On line 213, change `configDetails` to `authDetails`:
|
||||
|
||||
Before:
|
||||
```typescript
|
||||
const configContainer = configDetails.createDiv({cls: 'mcp-config-snippet'});
|
||||
```
|
||||
|
||||
After:
|
||||
```typescript
|
||||
const configContainer = authDetails.createDiv({cls: 'mcp-config-snippet'});
|
||||
```
|
||||
|
||||
**Step 4: Run type check**
|
||||
|
||||
Run: `npm run build`
|
||||
Expected: No type errors
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/settings.ts
|
||||
git commit -m "refactor: remove nested MCP config details element"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 4: Replace Single Config with Tab Buttons
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/settings.ts:213-255`
|
||||
|
||||
**Step 1: Replace description with tab container**
|
||||
|
||||
Delete lines 216-221 (the config description paragraph).
|
||||
|
||||
Replace with tab button container:
|
||||
|
||||
```typescript
|
||||
// Tab buttons for switching between clients
|
||||
const tabContainer = configContainer.createDiv({cls: 'mcp-config-tabs'});
|
||||
tabContainer.style.display = 'flex';
|
||||
tabContainer.style.gap = '8px';
|
||||
tabContainer.style.marginBottom = '16px';
|
||||
tabContainer.style.borderBottom = '1px solid var(--background-modifier-border)';
|
||||
|
||||
// Windsurf tab button
|
||||
const windsurfTab = tabContainer.createEl('button', {text: 'Windsurf'});
|
||||
windsurfTab.style.padding = '8px 16px';
|
||||
windsurfTab.style.border = 'none';
|
||||
windsurfTab.style.background = 'none';
|
||||
windsurfTab.style.cursor = 'pointer';
|
||||
windsurfTab.style.borderBottom = this.activeConfigTab === 'windsurf'
|
||||
? '2px solid var(--interactive-accent)'
|
||||
: '2px solid transparent';
|
||||
windsurfTab.style.fontWeight = this.activeConfigTab === 'windsurf' ? 'bold' : 'normal';
|
||||
windsurfTab.addEventListener('click', () => {
|
||||
this.activeConfigTab = 'windsurf';
|
||||
this.display();
|
||||
});
|
||||
|
||||
// Claude Code tab button
|
||||
const claudeCodeTab = tabContainer.createEl('button', {text: 'Claude Code'});
|
||||
claudeCodeTab.style.padding = '8px 16px';
|
||||
claudeCodeTab.style.border = 'none';
|
||||
claudeCodeTab.style.background = 'none';
|
||||
claudeCodeTab.style.cursor = 'pointer';
|
||||
claudeCodeTab.style.borderBottom = this.activeConfigTab === 'claude-code'
|
||||
? '2px solid var(--interactive-accent)'
|
||||
: '2px solid transparent';
|
||||
claudeCodeTab.style.fontWeight = this.activeConfigTab === 'claude-code' ? 'bold' : 'normal';
|
||||
claudeCodeTab.addEventListener('click', () => {
|
||||
this.activeConfigTab = 'claude-code';
|
||||
this.display();
|
||||
});
|
||||
```
|
||||
|
||||
**Step 2: Run type check**
|
||||
|
||||
Run: `npm run build`
|
||||
Expected: No type errors
|
||||
|
||||
**Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add src/settings.ts
|
||||
git commit -m "feat: add tab buttons for MCP client selection"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 5: Replace Static Config with Dynamic Tab Content
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/settings.ts:224-255`
|
||||
|
||||
**Step 1: Delete old config generation and display code**
|
||||
|
||||
Delete lines 224-255 (old mcpConfig object, button container, and config display).
|
||||
|
||||
**Step 2: Add dynamic config content area**
|
||||
|
||||
Replace with:
|
||||
|
||||
```typescript
|
||||
// Get configuration for active tab
|
||||
const {filePath, config, usageNote} = this.generateConfigForClient(this.activeConfigTab);
|
||||
|
||||
// Tab content area
|
||||
const tabContent = configContainer.createDiv({cls: 'mcp-config-content'});
|
||||
tabContent.style.marginTop = '16px';
|
||||
|
||||
// File location label
|
||||
const fileLocationLabel = tabContent.createEl('p', {text: 'Configuration file location:'});
|
||||
fileLocationLabel.style.marginBottom = '4px';
|
||||
fileLocationLabel.style.fontSize = '0.9em';
|
||||
fileLocationLabel.style.color = 'var(--text-muted)';
|
||||
|
||||
// File path display
|
||||
const filePathDisplay = tabContent.createEl('div', {text: filePath});
|
||||
filePathDisplay.style.padding = '8px';
|
||||
filePathDisplay.style.backgroundColor = 'var(--background-secondary)';
|
||||
filePathDisplay.style.borderRadius = '4px';
|
||||
filePathDisplay.style.fontFamily = 'monospace';
|
||||
filePathDisplay.style.fontSize = '0.9em';
|
||||
filePathDisplay.style.marginBottom = '12px';
|
||||
filePathDisplay.style.color = 'var(--text-muted)';
|
||||
|
||||
// Copy button
|
||||
const copyConfigButton = tabContent.createEl('button', {text: '📋 Copy Configuration'});
|
||||
copyConfigButton.style.marginBottom = '12px';
|
||||
copyConfigButton.addEventListener('click', async () => {
|
||||
await navigator.clipboard.writeText(JSON.stringify(config, null, 2));
|
||||
new Notice('✅ Configuration copied to clipboard');
|
||||
});
|
||||
|
||||
// Config JSON display
|
||||
const configDisplay = tabContent.createEl('pre');
|
||||
configDisplay.style.padding = '12px';
|
||||
configDisplay.style.backgroundColor = 'var(--background-secondary)';
|
||||
configDisplay.style.borderRadius = '4px';
|
||||
configDisplay.style.fontSize = '0.85em';
|
||||
configDisplay.style.overflowX = 'auto';
|
||||
configDisplay.style.userSelect = 'text';
|
||||
configDisplay.style.cursor = 'text';
|
||||
configDisplay.style.marginBottom = '12px';
|
||||
configDisplay.textContent = JSON.stringify(config, null, 2);
|
||||
|
||||
// Usage note
|
||||
const usageNoteDisplay = tabContent.createEl('p', {text: usageNote});
|
||||
usageNoteDisplay.style.fontSize = '0.9em';
|
||||
usageNoteDisplay.style.color = 'var(--text-muted)';
|
||||
usageNoteDisplay.style.fontStyle = 'italic';
|
||||
```
|
||||
|
||||
**Step 3: Run type check**
|
||||
|
||||
Run: `npm run build`
|
||||
Expected: No type errors
|
||||
|
||||
**Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add src/settings.ts
|
||||
git commit -m "feat: implement dynamic tab content with client-specific configs"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 6: Manual Testing
|
||||
|
||||
**Files:**
|
||||
- Test: Manual testing in Obsidian
|
||||
|
||||
**Step 1: Build the plugin**
|
||||
|
||||
Run: `npm run build`
|
||||
Expected: Build succeeds with no errors
|
||||
|
||||
**Step 2: Copy plugin to test vault**
|
||||
|
||||
Assuming you have a test vault at `~/test-vault/.obsidian/plugins/obsidian-mcp-server/`:
|
||||
|
||||
Run:
|
||||
```bash
|
||||
cp main.js manifest.json styles.css ~/test-vault/.obsidian/plugins/obsidian-mcp-server/
|
||||
```
|
||||
|
||||
**Step 3: Test in Obsidian**
|
||||
|
||||
1. Open test vault in Obsidian
|
||||
2. Reload Obsidian (Ctrl/Cmd + R)
|
||||
3. Go to Settings → MCP Server Settings
|
||||
4. Verify "Authentication & Configuration" section appears
|
||||
5. Expand the section
|
||||
6. Verify two tabs: "Windsurf" and "Claude Code"
|
||||
7. Click "Windsurf" tab - verify config shows with serverUrl
|
||||
8. Click "Claude Code" tab - verify config shows with type: "http"
|
||||
9. Click "Copy Configuration" on each tab
|
||||
10. Verify clipboard contains correct JSON for each client
|
||||
11. Verify port number matches setting
|
||||
12. Verify API key appears in both configs
|
||||
|
||||
**Step 4: Document test results**
|
||||
|
||||
If all tests pass, proceed. If any issues found, fix before continuing.
|
||||
|
||||
**Step 5: Commit test confirmation**
|
||||
|
||||
```bash
|
||||
git commit --allow-empty -m "test: verify MCP config UI improvements work correctly"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 7: Run Automated Tests
|
||||
|
||||
**Files:**
|
||||
- Test: `tests/*.test.ts`
|
||||
|
||||
**Step 1: Run full test suite**
|
||||
|
||||
Run: `npm test`
|
||||
Expected: All tests pass (579 tests, 0 failures)
|
||||
|
||||
**Step 2: Verify no regressions**
|
||||
|
||||
The settings UI changes should not affect any existing tests since they only modify presentation layer.
|
||||
|
||||
Expected: All existing tests still pass
|
||||
|
||||
**Step 3: Commit if tests pass**
|
||||
|
||||
```bash
|
||||
git commit --allow-empty -m "test: verify no regressions from UI changes"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 8: Update Design Document Status
|
||||
|
||||
**Files:**
|
||||
- Modify: `docs/plans/2025-10-26-mcp-config-ui-improvements-design.md:4`
|
||||
|
||||
**Step 1: Update status**
|
||||
|
||||
Change line 4 from:
|
||||
```markdown
|
||||
**Status:** Approved
|
||||
```
|
||||
|
||||
To:
|
||||
```markdown
|
||||
**Status:** Implemented
|
||||
```
|
||||
|
||||
**Step 2: Commit**
|
||||
|
||||
```bash
|
||||
git add docs/plans/2025-10-26-mcp-config-ui-improvements-design.md
|
||||
git commit -m "docs: mark MCP config UI improvements as implemented"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 9: Final Verification
|
||||
|
||||
**Files:**
|
||||
- Test: All components
|
||||
|
||||
**Step 1: Build production version**
|
||||
|
||||
Run: `npm run build`
|
||||
Expected: Clean build with no errors or warnings
|
||||
|
||||
**Step 2: Run tests one final time**
|
||||
|
||||
Run: `npm test`
|
||||
Expected: 579 tests passing, 0 failures
|
||||
|
||||
**Step 3: Verify git status is clean**
|
||||
|
||||
Run: `git status`
|
||||
Expected: Working tree clean, on branch feature/mcp-config-ui-improvements
|
||||
|
||||
**Step 4: Review commit history**
|
||||
|
||||
Run: `git log --oneline`
|
||||
Expected: Clean series of focused commits following conventional commit format
|
||||
|
||||
**Step 5: Push branch**
|
||||
|
||||
Run: `git push -u origin feature/mcp-config-ui-improvements`
|
||||
Expected: Branch pushed successfully
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
After implementation is complete:
|
||||
|
||||
1. **REQUIRED SUB-SKILL:** Use superpowers:finishing-a-development-branch to complete the workflow
|
||||
2. Options include: merge to main, create PR, or clean up worktree
|
||||
3. Follow the finishing-a-development-branch skill for structured completion
|
||||
|
||||
## Notes
|
||||
|
||||
- All commits use conventional commit format: `feat:`, `refactor:`, `test:`, `docs:`
|
||||
- No breaking changes to existing functionality
|
||||
- No changes to plugin settings schema
|
||||
- Pure UI presentation changes
|
||||
- Tab state is ephemeral (not persisted)
|
||||
- Configurations are generated dynamically from current settings
|
||||
@@ -1,161 +0,0 @@
|
||||
# Notification UI Improvements Design
|
||||
|
||||
**Date:** 2025-10-26
|
||||
**Status:** Approved for implementation
|
||||
|
||||
## Overview
|
||||
|
||||
Improve the MCP plugin's notification system to address three key UX issues:
|
||||
1. Unclear notification message format
|
||||
2. Settings section collapsing when toggling notifications on/off
|
||||
3. Broken filter controls in notification history modal
|
||||
|
||||
## Problem Statement
|
||||
|
||||
### Issue 1: Notification Message Clarity
|
||||
Current format: `🔧 MCP: tool_name({ params })`
|
||||
- Not immediately clear this is a tool call
|
||||
- Parameters on same line make long notifications hard to read
|
||||
- "MCP:" prefix is too terse
|
||||
|
||||
### Issue 2: Settings Section Collapse
|
||||
When toggling "Enable notifications" on/off:
|
||||
- The entire settings page re-renders via `this.display()`
|
||||
- This collapses the "UI Notifications" detail section
|
||||
- Poor UX - user loses their place in settings
|
||||
|
||||
### Issue 3: Modal Filter Bugs
|
||||
In the notification history modal:
|
||||
- Tool filter input box doesn't accept text input
|
||||
- Success/Error dropdown doesn't show selected option visually
|
||||
- Root cause: `applyFilters()` calls `this.onOpen()` which destroys/recreates all DOM elements
|
||||
|
||||
## Design Decisions
|
||||
|
||||
### 1. Notification Message Format
|
||||
|
||||
**Chosen approach:** Multi-line format with explicit "MCP Tool Called" label
|
||||
|
||||
**Format:**
|
||||
```
|
||||
📖 MCP Tool Called: read_note
|
||||
path: "daily/2025-01-15.md"
|
||||
```
|
||||
|
||||
**Rationale:**
|
||||
- First line clearly identifies this as an MCP tool call
|
||||
- Tool name prominently displayed
|
||||
- Parameters on separate line improve readability
|
||||
- Works within Obsidian's Notice component (supports newlines)
|
||||
|
||||
**Implementation:**
|
||||
- Modify `NotificationManager.formatArgs()` to return parameter string without wrapping parens
|
||||
- Update message construction: `${icon} MCP Tool Called: ${toolName}\n${argsStr}`
|
||||
- When `showParameters` is false: `${icon} MCP Tool Called: ${toolName}` (no newline)
|
||||
- Maintain existing truncation: 30 chars for parameter values, 50 for JSON fallback
|
||||
|
||||
### 2. Settings Section - Prevent Collapse
|
||||
|
||||
**Chosen approach:** Targeted subsection update
|
||||
|
||||
**Strategy:**
|
||||
- Store reference to notification detail element during initial render
|
||||
- Create helper method to update only notification section content
|
||||
- Preserve `open` state of detail element
|
||||
- Avoid full page re-render on toggle
|
||||
|
||||
**Implementation:**
|
||||
1. Add instance variable: `private notificationDetailsEl: HTMLDetailsElement | null = null`
|
||||
2. Store reference when creating notification section in `display()`
|
||||
3. Create `updateNotificationSection()` method:
|
||||
- Clear content of detail element (not the element itself)
|
||||
- Rebuild child settings
|
||||
- Preserve `open` attribute
|
||||
4. Replace `this.display()` in toggle handler with targeted update
|
||||
|
||||
**Benefits:**
|
||||
- Better UX - maintains user context
|
||||
- More efficient - doesn't re-render entire page
|
||||
- Extensible pattern for other collapsible sections
|
||||
|
||||
### 3. Modal Filter Controls
|
||||
|
||||
**Chosen approach:** Use Obsidian Setting components + eliminate re-render on filter
|
||||
|
||||
**Current problems:**
|
||||
- Raw HTML elements (`createEl('input')`, `createEl('select')`)
|
||||
- `applyFilters()` → `onOpen()` → `contentEl.empty()` destroys all DOM
|
||||
- Loses focus, input values, selected states
|
||||
|
||||
**Solution:**
|
||||
1. Use Obsidian `Setting` component for filter controls
|
||||
2. Filter state already stored in instance variables (`filterTool`, `filterType`)
|
||||
3. Refactor update logic:
|
||||
- `createFilters()` - builds filters once using Setting components
|
||||
- `updateHistoryList()` - updates only list container with filtered results
|
||||
- `updateResultsCount()` - updates only count element
|
||||
4. Store references to list container and count element
|
||||
5. `applyFilters()` calls targeted update methods, NOT `onOpen()`
|
||||
|
||||
**Benefits:**
|
||||
- Setting components properly handle input/select state
|
||||
- No DOM destruction during filtering
|
||||
- Consistent with Obsidian UI patterns
|
||||
- Better performance - only updates what changed
|
||||
|
||||
## Files to Modify
|
||||
|
||||
### src/ui/notifications.ts
|
||||
- Update `showToolCall()` message format
|
||||
- Modify `formatArgs()` to return unwrapped parameter string
|
||||
|
||||
### src/settings.ts
|
||||
- Add `notificationDetailsEl` instance variable
|
||||
- Store reference in `display()` when creating notification section
|
||||
- Create `updateNotificationSection()` helper method
|
||||
- Replace `this.display()` call in toggle handler
|
||||
|
||||
### src/ui/notification-history.ts
|
||||
- Add instance variables for DOM references:
|
||||
- `listContainerEl: HTMLElement | null`
|
||||
- `countEl: HTMLElement | null`
|
||||
- Refactor `createFilters()` to use Setting components
|
||||
- Create `updateHistoryList()` method
|
||||
- Create `updateResultsCount()` method
|
||||
- Update `applyFilters()` to call targeted update methods
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. Notifications display with clear "MCP Tool Called:" label and multi-line format
|
||||
2. Toggling "Enable notifications" keeps the UI Notifications section open
|
||||
3. Tool filter input accepts text and filters list in real-time
|
||||
4. Success/Error dropdown shows selected option and filters correctly
|
||||
5. No regressions in existing notification functionality
|
||||
|
||||
## Testing Plan
|
||||
|
||||
1. **Notification format:**
|
||||
- Enable notifications with parameters shown
|
||||
- Trigger various MCP tools (read_note, search, etc.)
|
||||
- Verify multi-line format with "MCP Tool Called:" label
|
||||
- Test with parameters disabled - should show single line
|
||||
|
||||
2. **Settings collapse:**
|
||||
- Open UI Notifications section
|
||||
- Toggle "Enable notifications" off
|
||||
- Verify section remains open
|
||||
- Toggle back on, verify section still open
|
||||
|
||||
3. **Modal filters:**
|
||||
- Open notification history modal
|
||||
- Type in tool filter box - verify text appears and list filters
|
||||
- Select different options in Success/Error dropdown - verify selection shows and list filters
|
||||
- Test combinations of filters
|
||||
- Verify results count updates correctly
|
||||
|
||||
## Future Enhancements (Out of Scope)
|
||||
|
||||
- Notification batching/grouping for rapid tool calls
|
||||
- Clickable notifications that open relevant notes
|
||||
- Export history to note file (not just clipboard)
|
||||
- Filter by date/time range in history modal
|
||||
@@ -1,717 +0,0 @@
|
||||
# Notification UI Improvements Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Fix three UX issues in the MCP notification system: unclear message format, settings collapse on toggle, and broken modal filters.
|
||||
|
||||
**Architecture:** Targeted surgical fixes - update notification message format in NotificationManager, implement targeted DOM updates in settings tab to prevent collapse, refactor modal filters to use Obsidian Setting components and eliminate destructive re-renders.
|
||||
|
||||
**Tech Stack:** TypeScript, Obsidian API (Notice, Setting, Modal), Jest for testing
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Update Notification Message Format
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/ui/notifications.ts:77-94` (showToolCall method)
|
||||
- Modify: `src/ui/notifications.ts:140-177` (formatArgs method)
|
||||
- Test: `tests/notifications.test.ts` (new file)
|
||||
|
||||
**Step 1: Write failing test for new notification format**
|
||||
|
||||
Create `tests/notifications.test.ts`:
|
||||
|
||||
```typescript
|
||||
import { App } from 'obsidian';
|
||||
import { NotificationManager } from '../src/ui/notifications';
|
||||
import { MCPPluginSettings } from '../src/types/settings-types';
|
||||
|
||||
describe('NotificationManager', () => {
|
||||
let app: App;
|
||||
let settings: MCPPluginSettings;
|
||||
let manager: NotificationManager;
|
||||
|
||||
beforeEach(() => {
|
||||
app = {} as App;
|
||||
settings = {
|
||||
port: 3000,
|
||||
autoStart: false,
|
||||
apiKey: 'test-key',
|
||||
notificationsEnabled: true,
|
||||
showParameters: true,
|
||||
notificationDuration: 3000,
|
||||
logToConsole: false
|
||||
};
|
||||
manager = new NotificationManager(app, settings);
|
||||
});
|
||||
|
||||
describe('showToolCall', () => {
|
||||
it('should format message with MCP Tool Called label and newline when parameters shown', () => {
|
||||
const mockNotice = jest.fn();
|
||||
(global as any).Notice = mockNotice;
|
||||
|
||||
manager.showToolCall('read_note', { path: 'daily/2025-01-15.md' });
|
||||
|
||||
expect(mockNotice).toHaveBeenCalledWith(
|
||||
expect.stringContaining('📖 MCP Tool Called: read_note\npath: "daily/2025-01-15.md"'),
|
||||
3000
|
||||
);
|
||||
});
|
||||
|
||||
it('should format message without newline when parameters hidden', () => {
|
||||
settings.showParameters = false;
|
||||
manager = new NotificationManager(app, settings);
|
||||
const mockNotice = jest.fn();
|
||||
(global as any).Notice = mockNotice;
|
||||
|
||||
manager.showToolCall('read_note', { path: 'daily/2025-01-15.md' });
|
||||
|
||||
expect(mockNotice).toHaveBeenCalledWith(
|
||||
'📖 MCP Tool Called: read_note',
|
||||
3000
|
||||
);
|
||||
});
|
||||
|
||||
it('should format multiple parameters correctly', () => {
|
||||
const mockNotice = jest.fn();
|
||||
(global as any).Notice = mockNotice;
|
||||
|
||||
manager.showToolCall('search', {
|
||||
query: 'test query',
|
||||
folder: 'notes',
|
||||
recursive: true
|
||||
});
|
||||
|
||||
expect(mockNotice).toHaveBeenCalledWith(
|
||||
expect.stringContaining('🔍 MCP Tool Called: search\nquery: "test query", folder: "notes", recursive: true'),
|
||||
3000
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `npm test -- tests/notifications.test.ts`
|
||||
Expected: FAIL with test failures showing old format `🔧 MCP: read_note`
|
||||
|
||||
**Step 3: Update formatArgs to return unwrapped parameter string**
|
||||
|
||||
In `src/ui/notifications.ts`, modify the `formatArgs` method (lines 140-177):
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* Format arguments for display
|
||||
*/
|
||||
private formatArgs(args: any): string {
|
||||
if (!this.settings.showParameters) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!args || Object.keys(args).length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
try {
|
||||
// Extract key parameters for display
|
||||
const keyParams: string[] = [];
|
||||
|
||||
if (args.path) {
|
||||
keyParams.push(`path: "${this.truncateString(args.path, 30)}"`);
|
||||
}
|
||||
if (args.query) {
|
||||
keyParams.push(`query: "${this.truncateString(args.query, 30)}"`);
|
||||
}
|
||||
if (args.folder) {
|
||||
keyParams.push(`folder: "${this.truncateString(args.folder, 30)}"`);
|
||||
}
|
||||
if (args.recursive !== undefined) {
|
||||
keyParams.push(`recursive: ${args.recursive}`);
|
||||
}
|
||||
|
||||
// If no key params, show first 50 chars of JSON
|
||||
if (keyParams.length === 0) {
|
||||
const json = JSON.stringify(args);
|
||||
return this.truncateString(json, 50);
|
||||
}
|
||||
|
||||
return keyParams.join(', ');
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 4: Update showToolCall to use new format**
|
||||
|
||||
In `src/ui/notifications.ts`, modify the `showToolCall` method (lines 77-94):
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* Show notification for tool call start
|
||||
*/
|
||||
showToolCall(toolName: string, args: any, duration?: number): void {
|
||||
if (!this.shouldShowNotification()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const icon = TOOL_ICONS[toolName] || '🔧';
|
||||
const argsStr = this.formatArgs(args);
|
||||
const message = argsStr
|
||||
? `${icon} MCP Tool Called: ${toolName}\n${argsStr}`
|
||||
: `${icon} MCP Tool Called: ${toolName}`;
|
||||
|
||||
this.queueNotification(() => {
|
||||
new Notice(message, duration || this.settings.notificationDuration);
|
||||
});
|
||||
|
||||
// Log to console if enabled
|
||||
if (this.settings.logToConsole) {
|
||||
console.log(`[MCP] Tool call: ${toolName}`, args);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 5: Run tests to verify they pass**
|
||||
|
||||
Run: `npm test -- tests/notifications.test.ts`
|
||||
Expected: PASS - all notification format tests pass
|
||||
|
||||
**Step 6: Run full test suite**
|
||||
|
||||
Run: `npm test`
|
||||
Expected: PASS - all 569+ tests pass
|
||||
|
||||
**Step 7: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ui/notifications.ts tests/notifications.test.ts
|
||||
git commit -m "feat: improve notification message clarity with MCP Tool Called label
|
||||
|
||||
- Update notification format to multi-line with explicit label
|
||||
- First line: 'MCP Tool Called: tool_name'
|
||||
- Second line: parameters (if enabled)
|
||||
- Add comprehensive tests for notification formatting
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Fix Settings Section Collapse on Toggle
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/settings.ts:6-12` (add instance variable)
|
||||
- Modify: `src/settings.ts:198-272` (notification section rendering)
|
||||
- Test: Manual testing (UI component, hard to unit test)
|
||||
|
||||
**Step 1: Add instance variable for notification details element**
|
||||
|
||||
In `src/settings.ts`, add to the class properties (after line 7):
|
||||
|
||||
```typescript
|
||||
export class MCPServerSettingTab extends PluginSettingTab {
|
||||
plugin: MCPServerPlugin;
|
||||
private notificationDetailsEl: HTMLDetailsElement | null = null;
|
||||
|
||||
constructor(app: App, plugin: MCPServerPlugin) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: Store reference when creating notification section**
|
||||
|
||||
In `src/settings.ts`, modify the notification section creation (around line 199):
|
||||
|
||||
```typescript
|
||||
// Notification Settings
|
||||
const notifDetails = containerEl.createEl('details');
|
||||
notifDetails.style.marginBottom = '20px';
|
||||
const notifSummary = notifDetails.createEl('summary');
|
||||
notifSummary.style.fontSize = '1.17em';
|
||||
notifSummary.style.fontWeight = 'bold';
|
||||
notifSummary.style.marginBottom = '12px';
|
||||
notifSummary.style.cursor = 'pointer';
|
||||
notifSummary.setText('UI Notifications');
|
||||
|
||||
// Store reference for targeted updates
|
||||
this.notificationDetailsEl = notifDetails;
|
||||
```
|
||||
|
||||
**Step 3: Create updateNotificationSection helper method**
|
||||
|
||||
In `src/settings.ts`, add new method after the `display()` method:
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* Update only the notification section without re-rendering entire page
|
||||
*/
|
||||
private updateNotificationSection(): void {
|
||||
if (!this.notificationDetailsEl) {
|
||||
// Fallback to full re-render if reference lost
|
||||
this.display();
|
||||
return;
|
||||
}
|
||||
|
||||
// Store current open state
|
||||
const wasOpen = this.notificationDetailsEl.open;
|
||||
|
||||
// Find and remove all child elements except the summary
|
||||
const summary = this.notificationDetailsEl.querySelector('summary');
|
||||
while (this.notificationDetailsEl.lastChild && this.notificationDetailsEl.lastChild !== summary) {
|
||||
this.notificationDetailsEl.removeChild(this.notificationDetailsEl.lastChild);
|
||||
}
|
||||
|
||||
// Rebuild notification settings
|
||||
if (this.plugin.settings.notificationsEnabled) {
|
||||
// Show parameters
|
||||
new Setting(this.notificationDetailsEl)
|
||||
.setName('Show parameters')
|
||||
.setDesc('Include tool parameters in notifications')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.showParameters)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.showParameters = value;
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.updateNotificationManager();
|
||||
}));
|
||||
|
||||
// Notification duration
|
||||
new Setting(this.notificationDetailsEl)
|
||||
.setName('Notification duration')
|
||||
.setDesc('Duration in milliseconds')
|
||||
.addText(text => text
|
||||
.setPlaceholder('3000')
|
||||
.setValue(String(this.plugin.settings.notificationDuration))
|
||||
.onChange(async (value) => {
|
||||
const duration = parseInt(value);
|
||||
if (!isNaN(duration) && duration > 0) {
|
||||
this.plugin.settings.notificationDuration = duration;
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.updateNotificationManager();
|
||||
}
|
||||
}));
|
||||
|
||||
// Log to console
|
||||
new Setting(this.notificationDetailsEl)
|
||||
.setName('Log to console')
|
||||
.setDesc('Log tool calls to console')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.logToConsole)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.logToConsole = value;
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.updateNotificationManager();
|
||||
}));
|
||||
|
||||
// View history button
|
||||
new Setting(this.notificationDetailsEl)
|
||||
.setName('Notification history')
|
||||
.setDesc('View recent MCP tool calls')
|
||||
.addButton(button => button
|
||||
.setButtonText('View History')
|
||||
.onClick(() => {
|
||||
this.plugin.showNotificationHistory();
|
||||
}));
|
||||
}
|
||||
|
||||
// Restore open state
|
||||
this.notificationDetailsEl.open = wasOpen;
|
||||
}
|
||||
```
|
||||
|
||||
**Step 4: Update toggle handler to use targeted update**
|
||||
|
||||
In `src/settings.ts`, modify the "Enable notifications" toggle handler (around line 214):
|
||||
|
||||
```typescript
|
||||
// Enable notifications
|
||||
new Setting(notifDetails)
|
||||
.setName('Enable notifications')
|
||||
.setDesc('Show when MCP tools are called')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.notificationsEnabled)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.notificationsEnabled = value;
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.updateNotificationManager();
|
||||
this.updateNotificationSection(); // Changed from this.display()
|
||||
}));
|
||||
```
|
||||
|
||||
**Step 5: Manual testing**
|
||||
|
||||
Manual test steps:
|
||||
1. Build: `npm run build`
|
||||
2. Copy `main.js`, `manifest.json`, `styles.css` to test vault
|
||||
3. Reload Obsidian
|
||||
4. Open plugin settings
|
||||
5. Expand "UI Notifications" section
|
||||
6. Toggle "Enable notifications" off - verify section stays open
|
||||
7. Toggle "Enable notifications" on - verify section stays open
|
||||
8. Verify subsettings appear/disappear correctly
|
||||
|
||||
**Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add src/settings.ts
|
||||
git commit -m "fix: prevent notification settings section from collapsing on toggle
|
||||
|
||||
- Add targeted DOM update method for notification section
|
||||
- Store reference to details element during initial render
|
||||
- Replace full page re-render with targeted subsection update
|
||||
- Preserve open/closed state during updates
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Fix Modal Filter Controls
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/ui/notification-history.ts:7-16` (add DOM reference properties)
|
||||
- Modify: `src/ui/notification-history.ts:19-35` (onOpen method)
|
||||
- Modify: `src/ui/notification-history.ts:44-87` (createFilters method)
|
||||
- Modify: `src/ui/notification-history.ts:89-161` (refactor list rendering)
|
||||
- Modify: `src/ui/notification-history.ts:193-214` (applyFilters method)
|
||||
- Test: Manual testing (Modal UI component)
|
||||
|
||||
**Step 1: Add DOM reference instance variables**
|
||||
|
||||
In `src/ui/notification-history.ts`, update class properties (lines 7-16):
|
||||
|
||||
```typescript
|
||||
export class NotificationHistoryModal extends Modal {
|
||||
private history: NotificationHistoryEntry[];
|
||||
private filteredHistory: NotificationHistoryEntry[];
|
||||
private filterTool: string = '';
|
||||
private filterType: 'all' | 'success' | 'error' = 'all';
|
||||
|
||||
// DOM element references for targeted updates
|
||||
private listContainerEl: HTMLElement | null = null;
|
||||
private countEl: HTMLElement | null = null;
|
||||
|
||||
constructor(app: App, history: NotificationHistoryEntry[]) {
|
||||
super(app);
|
||||
this.history = history;
|
||||
this.filteredHistory = [...history];
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: Simplify onOpen to avoid re-rendering**
|
||||
|
||||
In `src/ui/notification-history.ts`, modify `onOpen` method (lines 19-35):
|
||||
|
||||
```typescript
|
||||
onOpen() {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
contentEl.addClass('mcp-notification-history-modal');
|
||||
|
||||
// Title
|
||||
contentEl.createEl('h2', { text: 'MCP Notification History' });
|
||||
|
||||
// Filters (create once, never recreate)
|
||||
this.createFilters(contentEl);
|
||||
|
||||
// History list (will be updated via reference)
|
||||
this.createHistoryListContainer(contentEl);
|
||||
|
||||
// Actions
|
||||
this.createActions(contentEl);
|
||||
}
|
||||
```
|
||||
|
||||
**Step 3: Refactor createFilters to use Setting components**
|
||||
|
||||
In `src/ui/notification-history.ts`, replace the `createFilters` method (lines 44-87):
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* Create filter controls using Obsidian Setting components
|
||||
*/
|
||||
private createFilters(containerEl: HTMLElement): void {
|
||||
const filterContainer = containerEl.createDiv({ cls: 'mcp-history-filters' });
|
||||
filterContainer.style.marginBottom = '16px';
|
||||
|
||||
// Tool name filter using Setting component
|
||||
new Setting(filterContainer)
|
||||
.setName('Tool filter')
|
||||
.setDesc('Filter by tool name')
|
||||
.addText(text => text
|
||||
.setPlaceholder('Enter tool name...')
|
||||
.setValue(this.filterTool)
|
||||
.onChange((value) => {
|
||||
this.filterTool = value.toLowerCase();
|
||||
this.applyFilters();
|
||||
}));
|
||||
|
||||
// Type filter using Setting component
|
||||
new Setting(filterContainer)
|
||||
.setName('Status filter')
|
||||
.setDesc('Filter by success or error')
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('all', 'All')
|
||||
.addOption('success', 'Success')
|
||||
.addOption('error', 'Error')
|
||||
.setValue(this.filterType)
|
||||
.onChange((value) => {
|
||||
this.filterType = value as 'all' | 'success' | 'error';
|
||||
this.applyFilters();
|
||||
}));
|
||||
|
||||
// Results count
|
||||
this.countEl = filterContainer.createDiv({ cls: 'mcp-history-count' });
|
||||
this.countEl.style.marginTop = '8px';
|
||||
this.countEl.style.fontSize = '0.9em';
|
||||
this.countEl.style.color = 'var(--text-muted)';
|
||||
this.updateResultsCount();
|
||||
}
|
||||
```
|
||||
|
||||
**Step 4: Create container and separate update method**
|
||||
|
||||
In `src/ui/notification-history.ts`, replace `createHistoryList` with two methods:
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* Create history list container (called once)
|
||||
*/
|
||||
private createHistoryListContainer(containerEl: HTMLElement): void {
|
||||
this.listContainerEl = containerEl.createDiv({ cls: 'mcp-history-list' });
|
||||
this.listContainerEl.style.maxHeight = '400px';
|
||||
this.listContainerEl.style.overflowY = 'auto';
|
||||
this.listContainerEl.style.marginBottom = '16px';
|
||||
this.listContainerEl.style.border = '1px solid var(--background-modifier-border)';
|
||||
this.listContainerEl.style.borderRadius = '4px';
|
||||
|
||||
// Initial render
|
||||
this.updateHistoryList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update history list contents (called on filter changes)
|
||||
*/
|
||||
private updateHistoryList(): void {
|
||||
if (!this.listContainerEl) return;
|
||||
|
||||
// Clear existing content
|
||||
this.listContainerEl.empty();
|
||||
|
||||
if (this.filteredHistory.length === 0) {
|
||||
const emptyEl = this.listContainerEl.createDiv({ cls: 'mcp-history-empty' });
|
||||
emptyEl.style.padding = '24px';
|
||||
emptyEl.style.textAlign = 'center';
|
||||
emptyEl.style.color = 'var(--text-muted)';
|
||||
emptyEl.textContent = 'No entries found';
|
||||
return;
|
||||
}
|
||||
|
||||
this.filteredHistory.forEach((entry, index) => {
|
||||
const entryEl = this.listContainerEl!.createDiv({ cls: 'mcp-history-entry' });
|
||||
entryEl.style.padding = '12px';
|
||||
entryEl.style.borderBottom = index < this.filteredHistory.length - 1
|
||||
? '1px solid var(--background-modifier-border)'
|
||||
: 'none';
|
||||
|
||||
// Header row
|
||||
const headerEl = entryEl.createDiv({ cls: 'mcp-history-entry-header' });
|
||||
headerEl.style.display = 'flex';
|
||||
headerEl.style.justifyContent = 'space-between';
|
||||
headerEl.style.marginBottom = '8px';
|
||||
|
||||
// Tool name and status
|
||||
const titleEl = headerEl.createDiv();
|
||||
const statusIcon = entry.success ? '✅' : '❌';
|
||||
const toolName = titleEl.createEl('strong', { text: `${statusIcon} ${entry.toolName}` });
|
||||
toolName.style.color = entry.success ? 'var(--text-success)' : 'var(--text-error)';
|
||||
|
||||
// Timestamp and duration
|
||||
const metaEl = headerEl.createDiv();
|
||||
metaEl.style.fontSize = '0.85em';
|
||||
metaEl.style.color = 'var(--text-muted)';
|
||||
const timestamp = new Date(entry.timestamp).toLocaleTimeString();
|
||||
const durationStr = entry.duration ? ` • ${entry.duration}ms` : '';
|
||||
metaEl.textContent = `${timestamp}${durationStr}`;
|
||||
|
||||
// Arguments
|
||||
if (entry.args && Object.keys(entry.args).length > 0) {
|
||||
const argsEl = entryEl.createDiv({ cls: 'mcp-history-entry-args' });
|
||||
argsEl.style.fontSize = '0.85em';
|
||||
argsEl.style.fontFamily = 'monospace';
|
||||
argsEl.style.backgroundColor = 'var(--background-secondary)';
|
||||
argsEl.style.padding = '8px';
|
||||
argsEl.style.borderRadius = '4px';
|
||||
argsEl.style.marginBottom = '8px';
|
||||
argsEl.style.overflowX = 'auto';
|
||||
argsEl.textContent = JSON.stringify(entry.args, null, 2);
|
||||
}
|
||||
|
||||
// Error message
|
||||
if (!entry.success && entry.error) {
|
||||
const errorEl = entryEl.createDiv({ cls: 'mcp-history-entry-error' });
|
||||
errorEl.style.fontSize = '0.85em';
|
||||
errorEl.style.color = 'var(--text-error)';
|
||||
errorEl.style.backgroundColor = 'var(--background-secondary)';
|
||||
errorEl.style.padding = '8px';
|
||||
errorEl.style.borderRadius = '4px';
|
||||
errorEl.style.fontFamily = 'monospace';
|
||||
errorEl.textContent = entry.error;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update results count display
|
||||
*/
|
||||
private updateResultsCount(): void {
|
||||
if (!this.countEl) return;
|
||||
this.countEl.textContent = `${this.filteredHistory.length} of ${this.history.length} entries`;
|
||||
}
|
||||
```
|
||||
|
||||
**Step 5: Update applyFilters to use targeted updates**
|
||||
|
||||
In `src/ui/notification-history.ts`, replace the `applyFilters` method (lines 193-214):
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* Apply filters to history
|
||||
*/
|
||||
private applyFilters(): void {
|
||||
this.filteredHistory = this.history.filter(entry => {
|
||||
// Tool name filter
|
||||
if (this.filterTool && !entry.toolName.toLowerCase().includes(this.filterTool)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Type filter
|
||||
if (this.filterType === 'success' && !entry.success) {
|
||||
return false;
|
||||
}
|
||||
if (this.filterType === 'error' && entry.success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
// Update only the affected UI elements
|
||||
this.updateHistoryList();
|
||||
this.updateResultsCount();
|
||||
}
|
||||
```
|
||||
|
||||
**Step 6: Add missing import**
|
||||
|
||||
In `src/ui/notification-history.ts`, update imports at the top:
|
||||
|
||||
```typescript
|
||||
import { App, Modal, Setting } from 'obsidian';
|
||||
import { NotificationHistoryEntry } from './notifications';
|
||||
```
|
||||
|
||||
**Step 7: Manual testing**
|
||||
|
||||
Manual test steps:
|
||||
1. Build: `npm run build`
|
||||
2. Copy `main.js` to test vault
|
||||
3. Reload Obsidian
|
||||
4. Trigger some MCP tool calls (with different tools, some successes, some errors)
|
||||
5. Open notification history modal
|
||||
6. Test tool filter:
|
||||
- Type "read" - verify only read_* tools show
|
||||
- Clear filter - verify all entries return
|
||||
7. Test status filter:
|
||||
- Select "Success" - verify only successful calls show
|
||||
- Select "Error" - verify only failed calls show
|
||||
- Select "All" - verify all entries return
|
||||
8. Test combined filters
|
||||
9. Verify results count updates correctly
|
||||
|
||||
**Step 8: Commit**
|
||||
|
||||
```bash
|
||||
git add src/ui/notification-history.ts
|
||||
git commit -m "fix: repair broken filter controls in notification history modal
|
||||
|
||||
- Replace raw HTML inputs with Obsidian Setting components
|
||||
- Add DOM element references for targeted updates
|
||||
- Eliminate destructive re-render on filter changes
|
||||
- Update only list container and count on filter apply
|
||||
- Fix tool filter input not accepting text
|
||||
- Fix status dropdown not showing selection
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 4: Final Verification and Cleanup
|
||||
|
||||
**Step 1: Run full test suite**
|
||||
|
||||
Run: `npm test`
|
||||
Expected: All tests pass (569+ tests)
|
||||
|
||||
**Step 2: Run type checking**
|
||||
|
||||
Run: `npm run build`
|
||||
Expected: No TypeScript errors, successful build
|
||||
|
||||
**Step 3: Manual end-to-end testing**
|
||||
|
||||
Complete manual test checklist:
|
||||
- [ ] Notifications display with "MCP Tool Called:" label
|
||||
- [ ] Notifications show parameters on second line when enabled
|
||||
- [ ] Notifications show single line when parameters disabled
|
||||
- [ ] Toggling notifications on/off keeps section open
|
||||
- [ ] Tool filter in history modal accepts text and filters
|
||||
- [ ] Status dropdown in history modal shows selection
|
||||
- [ ] Combined filters work correctly
|
||||
- [ ] Results count updates correctly
|
||||
- [ ] No regressions in existing functionality
|
||||
|
||||
**Step 4: Review commits**
|
||||
|
||||
Run: `git log --oneline master..HEAD`
|
||||
Expected: See 3 clean commits for the 3 tasks
|
||||
|
||||
**Step 5: Ready for merge/PR**
|
||||
|
||||
The implementation is complete and ready for:
|
||||
- Merge to master (if sole developer)
|
||||
- Pull request (if team workflow)
|
||||
- Use @superpowers:finishing-a-development-branch skill for next steps
|
||||
|
||||
---
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
**TDD Applied:**
|
||||
- Task 1 includes comprehensive test coverage for notification formatting
|
||||
- Tasks 2-3 are UI components best tested manually
|
||||
- All changes maintain existing test suite (569 tests pass)
|
||||
|
||||
**DRY Applied:**
|
||||
- Reusable `updateNotificationSection()` method for settings updates
|
||||
- Reusable `updateHistoryList()` and `updateResultsCount()` for modal updates
|
||||
- Shared filter logic in `applyFilters()`
|
||||
|
||||
**YAGNI Applied:**
|
||||
- No premature optimizations or extra features
|
||||
- Minimal changes to fix reported issues
|
||||
- No refactoring beyond what's needed
|
||||
|
||||
**Commits:**
|
||||
- Frequent, focused commits (one per task)
|
||||
- Clear commit messages with context
|
||||
- Each commit is independently valuable
|
||||
|
||||
**Testing Strategy:**
|
||||
- Unit tests for business logic (notification formatting)
|
||||
- Manual testing for UI interactions (unavoidable with Obsidian API)
|
||||
- Full test suite verification at each step
|
||||
@@ -1,616 +0,0 @@
|
||||
# Public Release Version Reset to 1.0.0 Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Reset version to 1.0.0 in preparation for public release while preserving git history
|
||||
|
||||
**Architecture:** Update version identifiers in manifest.json, package.json, and versions.json to mark 1.0.0 as the target for public release. The existing git history (95 commits) will be preserved to demonstrate development quality and maintain context for future contributors. Previous development versions (1.0.0-3.0.0) become private development history. Git tagging will be done separately when development is complete and ready for actual release.
|
||||
|
||||
**Tech Stack:** Node.js version-bump.mjs script, JSON files, git
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
Current state:
|
||||
- Version: 3.0.0 (in manifest.json, package.json)
|
||||
- versions.json contains: 1.0.0, 1.1.0, 1.2.0, 2.0.0, 2.1.0, 3.0.0 (all mapped to minAppVersion 0.15.0)
|
||||
- 95 commits in git history with no sensitive data
|
||||
- Clean commit history with conventional commit format
|
||||
- CHANGELOG.md contains extensive development history (versions 1.0.0 through 9.0.0)
|
||||
|
||||
Decision: Keep git history (demonstrates quality, security-conscious development, comprehensive testing) and reset version to 1.0.0 in preparation for public release.
|
||||
|
||||
**Important:** Development is ongoing. This plan resets the version number but does NOT create a git tag. The tag will be created separately when development is complete and the plugin is ready for actual public release.
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Update manifest.json Version
|
||||
|
||||
**Files:**
|
||||
- Modify: `manifest.json:4`
|
||||
|
||||
**Step 1: Read current manifest.json**
|
||||
|
||||
Verify current version before modifying.
|
||||
|
||||
Run: `cat manifest.json`
|
||||
Expected: Shows `"version": "3.0.0"`
|
||||
|
||||
**Step 2: Update version to 1.0.0**
|
||||
|
||||
Change version field from "3.0.0" to "1.0.0".
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "obsidian-mcp-server",
|
||||
"name": "MCP Server",
|
||||
"version": "1.0.0",
|
||||
"minAppVersion": "0.15.0",
|
||||
"description": "Exposes Obsidian vault operations via Model Context Protocol (MCP) over HTTP",
|
||||
"author": "Bill Ballou",
|
||||
"isDesktopOnly": true
|
||||
}
|
||||
```
|
||||
|
||||
**Step 3: Verify the change**
|
||||
|
||||
Run: `cat manifest.json | grep version`
|
||||
Expected: Shows `"version": "1.0.0"` and `"minAppVersion": "0.15.0"`
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Update package.json Version
|
||||
|
||||
**Files:**
|
||||
- Modify: `package.json:3`
|
||||
|
||||
**Step 1: Read current package.json**
|
||||
|
||||
Verify current version before modifying.
|
||||
|
||||
Run: `cat package.json | head -5`
|
||||
Expected: Shows `"version": "3.0.0"`
|
||||
|
||||
**Step 2: Update version to 1.0.0**
|
||||
|
||||
Change version field from "3.0.0" to "1.0.0".
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "obsidian-mcp-server",
|
||||
"version": "1.0.0",
|
||||
"description": "MCP (Model Context Protocol) server plugin for Obsidian - exposes vault operations via HTTP",
|
||||
```
|
||||
|
||||
**Step 3: Verify the change**
|
||||
|
||||
Run: `cat package.json | grep '"version"'`
|
||||
Expected: Shows `"version": "1.0.0"`
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Reset versions.json
|
||||
|
||||
**Files:**
|
||||
- Modify: `versions.json` (entire file)
|
||||
|
||||
**Step 1: Read current versions.json**
|
||||
|
||||
Verify current content before modifying.
|
||||
|
||||
Run: `cat versions.json`
|
||||
Expected: Shows entries for 1.0.0 through 3.0.0
|
||||
|
||||
**Step 2: Replace with single 1.0.0 entry**
|
||||
|
||||
Clear all development version history, keep only 1.0.0 as first public release.
|
||||
|
||||
```json
|
||||
{
|
||||
"1.0.0": "0.15.0"
|
||||
}
|
||||
```
|
||||
|
||||
**Step 3: Verify the change**
|
||||
|
||||
Run: `cat versions.json`
|
||||
Expected: Shows only one entry: `"1.0.0": "0.15.0"`
|
||||
|
||||
---
|
||||
|
||||
## Task 4: Update CHANGELOG.md for Public Release
|
||||
|
||||
**Files:**
|
||||
- Modify: `CHANGELOG.md:1-1366`
|
||||
|
||||
**Step 1: Read current CHANGELOG structure**
|
||||
|
||||
Run: `head -50 CHANGELOG.md`
|
||||
Expected: Shows "# Changelog" header and extensive version history
|
||||
|
||||
**Step 2: Create new public-release CHANGELOG**
|
||||
|
||||
Replace entire file with simplified version for public release. Remove private development version entries (1.0.0-9.0.0), keep only new 1.0.0 public release entry.
|
||||
|
||||
```markdown
|
||||
# Changelog
|
||||
|
||||
All notable changes to the Obsidian MCP Server plugin will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
---
|
||||
|
||||
## [1.0.0] - 2025-10-26
|
||||
|
||||
### 🎉 Initial Public Release
|
||||
|
||||
The Obsidian MCP Server plugin is now publicly available! This plugin exposes your Obsidian vault via the Model Context Protocol (MCP) over HTTP, enabling AI assistants and other MCP clients to interact with your vault programmatically.
|
||||
|
||||
#### Core Features
|
||||
|
||||
**MCP Server**
|
||||
- HTTP server implementing MCP protocol version 2024-11-05
|
||||
- JSON-RPC 2.0 message handling
|
||||
- Localhost-only binding (127.0.0.1) for security
|
||||
- Configurable port (default: 3000)
|
||||
- Auto-start option
|
||||
|
||||
**Note Operations**
|
||||
- `read_note` - Read note content with optional frontmatter parsing
|
||||
- `create_note` - Create notes with conflict handling (error/overwrite/rename)
|
||||
- `update_note` - Update existing notes with concurrency control
|
||||
- `delete_note` - Delete notes (soft delete to .trash or permanent)
|
||||
- `update_frontmatter` - Update frontmatter fields without modifying content
|
||||
- `update_sections` - Update specific sections by line range
|
||||
- `rename_file` - Rename or move files with automatic wikilink updates
|
||||
- `read_excalidraw` - Read Excalidraw drawing files with metadata
|
||||
|
||||
**Vault Operations**
|
||||
- `search` - Advanced search with regex, glob filtering, and snippets
|
||||
- `search_waypoints` - Find Waypoint plugin markers
|
||||
- `list` - List files/directories with filtering and pagination
|
||||
- `stat` - Get detailed file/folder metadata
|
||||
- `exists` - Quick existence check
|
||||
- `get_vault_info` - Vault metadata and statistics
|
||||
|
||||
**Waypoint Integration**
|
||||
- `get_folder_waypoint` - Extract Waypoint blocks from folder notes
|
||||
- `is_folder_note` - Detect folder notes
|
||||
- Automatic waypoint edit protection
|
||||
|
||||
**Link Management**
|
||||
- `validate_wikilinks` - Validate all links in a note
|
||||
- `resolve_wikilink` - Resolve single wikilink to target path
|
||||
- `backlinks` - Get backlinks with optional unlinked mentions
|
||||
|
||||
**Security**
|
||||
- Mandatory Bearer token authentication
|
||||
- Auto-generated, cryptographically secure API keys (32 characters)
|
||||
- API keys encrypted using system keychain (macOS Keychain, Windows Credential Manager, Linux Secret Service)
|
||||
- Host header validation (DNS rebinding protection)
|
||||
- CORS policy fixed to localhost-only origins
|
||||
- Desktop-only (requires Node.js HTTP server)
|
||||
|
||||
**User Interface**
|
||||
- Settings panel with full configuration
|
||||
- Status bar indicator showing server state
|
||||
- Ribbon icon for quick server toggle
|
||||
- Start/Stop/Restart commands
|
||||
- Real-time connection information
|
||||
- Copy API key and configuration snippets
|
||||
- Notification system for tool calls (optional)
|
||||
- Notification history viewer
|
||||
|
||||
**Developer Experience**
|
||||
- Cross-platform path handling (Windows/macOS/Linux)
|
||||
- Comprehensive error messages with troubleshooting tips
|
||||
- Path validation and normalization utilities
|
||||
- Concurrency control via ETag-based versioning
|
||||
- Type-safe TypeScript implementation
|
||||
- Extensive test coverage
|
||||
- Well-documented codebase
|
||||
|
||||
#### Technical Details
|
||||
|
||||
**Dependencies**
|
||||
- express: ^4.18.2
|
||||
- cors: ^2.8.5
|
||||
- obsidian: latest
|
||||
|
||||
**Build**
|
||||
- TypeScript 4.7.4
|
||||
- esbuild 0.17.3
|
||||
- Jest 30.2.0 for testing
|
||||
|
||||
**Compatibility**
|
||||
- Obsidian minimum version: 0.15.0
|
||||
- Desktop only (not available on mobile)
|
||||
- Protocol: MCP 2024-11-05
|
||||
|
||||
#### Known Limitations
|
||||
|
||||
- Desktop only (requires Node.js HTTP server)
|
||||
- Single vault per server instance
|
||||
- HTTP only (no WebSocket support)
|
||||
- Localhost-only (no SSL/TLS)
|
||||
- Excalidraw support limited to uncompressed format (compressed format planned)
|
||||
|
||||
---
|
||||
|
||||
## Future Roadmap
|
||||
|
||||
### Planned Features
|
||||
|
||||
**Resources API**
|
||||
- Expose notes as MCP resources
|
||||
- Real-time resource updates
|
||||
|
||||
**Prompts API**
|
||||
- Templated prompts for common operations
|
||||
- Custom prompt registration
|
||||
|
||||
**Batch Operations**
|
||||
- Multiple operations in single request
|
||||
- Transactional batching
|
||||
|
||||
**WebSocket Transport**
|
||||
- Real-time updates and notifications
|
||||
- Bidirectional communication
|
||||
|
||||
**Enhanced Graph API**
|
||||
- Graph visualization data
|
||||
- Advanced graph traversal
|
||||
|
||||
**Tag & Canvas APIs**
|
||||
- Query and manage tags
|
||||
- Manipulate canvas files
|
||||
|
||||
**Dataview Integration**
|
||||
- Query vault using Dataview syntax
|
||||
- Advanced data queries
|
||||
|
||||
**Performance Enhancements**
|
||||
- Indexing for faster searches
|
||||
- Caching for frequently accessed notes
|
||||
- Streaming for large files
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
For issues, questions, or contributions:
|
||||
- GitHub Issues: [Report bugs and request features]
|
||||
- Documentation: See README.md and CLAUDE.md
|
||||
- Include version number (1.0.0) in bug reports
|
||||
|
||||
---
|
||||
|
||||
## Credits
|
||||
|
||||
- MCP Protocol: https://modelcontextprotocol.io
|
||||
- Obsidian API: https://github.com/obsidianmd/obsidian-api
|
||||
- Built with TypeScript, Express.js, and dedication to quality
|
||||
```
|
||||
|
||||
**Step 3: Verify the change**
|
||||
|
||||
Run: `wc -l CHANGELOG.md && head -20 CHANGELOG.md`
|
||||
Expected: Shows much shorter file (~200 lines vs 1400+), starts with "# Changelog" and "## [1.0.0] - 2025-10-26"
|
||||
|
||||
---
|
||||
|
||||
## Task 5: Verify All Version Changes
|
||||
|
||||
**Files:**
|
||||
- Read: `manifest.json`, `package.json`, `versions.json`
|
||||
|
||||
**Step 1: Check all version files**
|
||||
|
||||
Run: `echo "=== manifest.json ===" && cat manifest.json | grep version && echo "=== package.json ===" && cat package.json | grep version && echo "=== versions.json ===" && cat versions.json`
|
||||
|
||||
Expected output:
|
||||
```
|
||||
=== manifest.json ===
|
||||
"version": "1.0.0",
|
||||
"minAppVersion": "0.15.0",
|
||||
=== package.json ===
|
||||
"version": "1.0.0",
|
||||
"version": "node version-bump.mjs && git add manifest.json versions.json"
|
||||
=== versions.json ===
|
||||
{
|
||||
"1.0.0": "0.15.0"
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: Verify version-bump.mjs script compatibility**
|
||||
|
||||
The version-bump.mjs script (used by `npm version`) reads from package.json and updates manifest.json and versions.json. With all files now at 1.0.0, future version bumps will work correctly.
|
||||
|
||||
Run: `cat version-bump.mjs`
|
||||
Expected: Script reads `npm_package_version`, updates manifest.json version, and conditionally updates versions.json
|
||||
|
||||
**Step 3: Test build**
|
||||
|
||||
Ensure the plugin still builds correctly after version changes.
|
||||
|
||||
Run: `npm run build`
|
||||
Expected: TypeScript compiles successfully, esbuild creates main.js, no errors
|
||||
|
||||
---
|
||||
|
||||
## Task 6: Commit Version Reset
|
||||
|
||||
**Files:**
|
||||
- Modify: `manifest.json`, `package.json`, `versions.json`, `CHANGELOG.md`
|
||||
|
||||
**Step 1: Review changes to commit**
|
||||
|
||||
Run: `git status`
|
||||
Expected: Shows modified files: manifest.json, package.json, versions.json, CHANGELOG.md
|
||||
|
||||
**Step 2: Review diff**
|
||||
|
||||
Run: `git diff manifest.json package.json versions.json`
|
||||
Expected: Shows version changes from 3.0.0 to 1.0.0, versions.json reduced to single entry
|
||||
|
||||
**Step 3: Stage all changes**
|
||||
|
||||
Run: `git add manifest.json package.json versions.json CHANGELOG.md`
|
||||
|
||||
**Step 4: Create commit**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
git commit -m "$(cat <<'EOF'
|
||||
chore: reset version to 1.0.0 for initial public release
|
||||
|
||||
This marks version 1.0.0 as the first public release of the plugin.
|
||||
Previous versions (1.0.0-3.0.0) were private development iterations.
|
||||
|
||||
Changes:
|
||||
- Reset manifest.json version to 1.0.0
|
||||
- Reset package.json version to 1.0.0
|
||||
- Clear versions.json to single entry (1.0.0 -> 0.15.0)
|
||||
- Rewrite CHANGELOG.md for public release
|
||||
- Remove private development history
|
||||
- Document all features as part of 1.0.0
|
||||
- Add future roadmap section
|
||||
|
||||
Git history is preserved to demonstrate:
|
||||
- Development quality and security practices
|
||||
- Comprehensive test coverage efforts
|
||||
- Thoughtful evolution of features
|
||||
|
||||
This plugin implements MCP (Model Context Protocol) to expose
|
||||
Obsidian vault operations via HTTP for AI assistants and other clients.
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
**Step 5: Verify commit**
|
||||
|
||||
Run: `git log -1 --stat`
|
||||
Expected: Shows commit with 4 files changed, commit message explains version reset
|
||||
|
||||
**Step 6: Verify git history is preserved**
|
||||
|
||||
Run: `git log --oneline | wc -l`
|
||||
Expected: Shows 96 commits (95 previous + 1 new commit)
|
||||
|
||||
---
|
||||
|
||||
## Task 7: Document Version Reset Decision
|
||||
|
||||
**Files:**
|
||||
- Create: `docs/VERSION_HISTORY.md`
|
||||
|
||||
**Step 1: Create version history documentation**
|
||||
|
||||
Document why version was reset and what happened to previous versions.
|
||||
|
||||
```markdown
|
||||
# Version History
|
||||
|
||||
## Public Release Version Strategy
|
||||
|
||||
### Initial Public Release: 1.0.0 (2025-10-26)
|
||||
|
||||
This plugin's first public release is marked as **version 1.0.0**.
|
||||
|
||||
### Development History
|
||||
|
||||
Prior to public release, the plugin went through private development with internal versions 1.0.0 through 3.0.0. These versions were used during development and testing but were never publicly released.
|
||||
|
||||
When preparing for public release, we reset the version to 1.0.0 to clearly mark this as the first public version available to users.
|
||||
|
||||
### Why Reset to 1.0.0?
|
||||
|
||||
**Semantic Versioning**: Version 1.0.0 signals the first stable, public release of the plugin. It indicates:
|
||||
- The API is stable and ready for public use
|
||||
- All core features are implemented and tested
|
||||
- The plugin is production-ready
|
||||
|
||||
**User Clarity**: Starting at 1.0.0 for the public release avoids confusion:
|
||||
- Users don't wonder "what happened to versions 1-2?"
|
||||
- Version number accurately reflects the public release history
|
||||
- Clear signal that this is the first version they can install
|
||||
|
||||
**Git History Preserved**: The development history (95 commits) is preserved to:
|
||||
- Demonstrate development quality and security practices
|
||||
- Show comprehensive testing and iterative refinement
|
||||
- Provide context for future contributors
|
||||
- Maintain git blame and bisect capabilities
|
||||
|
||||
### Version Numbering Going Forward
|
||||
|
||||
From 1.0.0 onward, the plugin follows [Semantic Versioning](https://semver.org/):
|
||||
|
||||
- **MAJOR** version (1.x.x): Incompatible API changes or breaking changes
|
||||
- **MINOR** version (x.1.x): New functionality in a backward-compatible manner
|
||||
- **PATCH** version (x.x.1): Backward-compatible bug fixes
|
||||
|
||||
### Development Version Mapping
|
||||
|
||||
For reference, here's what the private development versions contained:
|
||||
|
||||
| Dev Version | Key Features Added |
|
||||
|-------------|-------------------|
|
||||
| 1.0.0 | Initial MCP server, basic CRUD tools |
|
||||
| 1.1.0 | Path normalization, error handling |
|
||||
| 1.2.0 | Enhanced authentication, parent folder detection |
|
||||
| 2.0.0 | API unification, typed results |
|
||||
| 2.1.0 | Discovery endpoints (stat, exists) |
|
||||
| 3.0.0 | Enhanced list operations |
|
||||
|
||||
All these features are included in the public 1.0.0 release.
|
||||
|
||||
### Commit History
|
||||
|
||||
The git repository contains the complete development history showing the evolution from initial implementation through all features. This history demonstrates:
|
||||
|
||||
- Security-conscious development (API key encryption, authentication)
|
||||
- Comprehensive test coverage (100% coverage goals)
|
||||
- Careful refactoring and improvements
|
||||
- Documentation and planning
|
||||
- Bug fixes and edge case handling
|
||||
|
||||
No sensitive data exists in the git history (verified via audit).
|
||||
|
||||
---
|
||||
|
||||
## Future Versioning
|
||||
|
||||
**Next versions** will be numbered according to the changes made:
|
||||
|
||||
- **1.0.1**: Bug fixes and patches
|
||||
- **1.1.0**: New features (e.g., Resources API, Prompts API)
|
||||
- **2.0.0**: Breaking changes to tool schemas or behavior
|
||||
|
||||
The CHANGELOG.md will document all public releases starting from 1.0.0.
|
||||
```
|
||||
|
||||
**Step 2: Verify file was created**
|
||||
|
||||
Run: `cat docs/VERSION_HISTORY.md | head -30`
|
||||
Expected: Shows version history explanation
|
||||
|
||||
**Step 3: Commit version history documentation**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
git add docs/VERSION_HISTORY.md
|
||||
git commit -m "docs: add version history explanation for 1.0.0 reset"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 8: Final Verification
|
||||
|
||||
**Files:**
|
||||
- Read: All modified files
|
||||
|
||||
**Step 1: Verify all version references**
|
||||
|
||||
Check that no stray version references remain.
|
||||
|
||||
Run: `grep -r "3\.0\.0" --include="*.json" --include="*.md" . 2>/dev/null | grep -v node_modules | grep -v ".git"`
|
||||
Expected: No results (all 3.0.0 references should be gone from project files)
|
||||
|
||||
**Step 2: Verify package.json npm version script**
|
||||
|
||||
The `npm version` command should work correctly for future version bumps.
|
||||
|
||||
Run: `cat package.json | grep '"version"'`
|
||||
Expected: Shows `"version": "1.0.0"` and version script with version-bump.mjs
|
||||
|
||||
**Step 3: Verify build output**
|
||||
|
||||
Run: `npm run build 2>&1 | tail -5`
|
||||
Expected: Build succeeds, no errors
|
||||
|
||||
**Step 4: Check git status**
|
||||
|
||||
Run: `git status`
|
||||
Expected: Working tree clean, no uncommitted changes
|
||||
|
||||
**Step 5: Verify commit history**
|
||||
|
||||
Run: `git log --oneline -5`
|
||||
Expected: Shows recent commits including version reset and documentation
|
||||
|
||||
**Step 6: Final summary**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
echo "=== Version Files ===" && \
|
||||
cat manifest.json | grep version && \
|
||||
cat package.json | grep '"version"' && \
|
||||
cat versions.json && \
|
||||
echo "=== Git Info ===" && \
|
||||
git log --oneline | wc -l && \
|
||||
echo "=== Build Status ===" && \
|
||||
ls -lh main.js
|
||||
```
|
||||
|
||||
Expected:
|
||||
- All versions show 1.0.0
|
||||
- versions.json has single entry
|
||||
- Git shows 96+ commits
|
||||
- main.js exists and is recent
|
||||
|
||||
---
|
||||
|
||||
## Completion Checklist
|
||||
|
||||
- [ ] manifest.json version is 1.0.0
|
||||
- [ ] package.json version is 1.0.0
|
||||
- [ ] versions.json contains only {"1.0.0": "0.15.0"}
|
||||
- [ ] CHANGELOG.md rewritten for public release
|
||||
- [ ] All changes committed with descriptive message
|
||||
- [ ] Git history preserved (95+ commits)
|
||||
- [ ] VERSION_HISTORY.md documents the reset decision
|
||||
- [ ] No stray 3.0.0 references remain
|
||||
- [ ] Build succeeds (main.js created)
|
||||
- [ ] Working tree is clean
|
||||
|
||||
**Note:** Git tag creation (1.0.0) is NOT part of this plan. The tag will be created later when development is complete and the plugin is ready for actual public release.
|
||||
|
||||
## Post-Implementation Notes
|
||||
|
||||
After completing this plan, the version numbers are reset to 1.0.0 in preparation for public release:
|
||||
|
||||
**Current State After Plan:**
|
||||
- Version files (manifest.json, package.json, versions.json) all show 1.0.0
|
||||
- CHANGELOG.md rewritten for public consumption
|
||||
- VERSION_HISTORY.md documents the version reset decision
|
||||
- Git history preserved with all development commits
|
||||
- No git tag created yet (tag will be created when ready for actual release)
|
||||
|
||||
**When Ready for Actual Public Release:**
|
||||
|
||||
1. **Final Development**: Complete any remaining development work and commit changes
|
||||
|
||||
2. **Create Git Tag**: Create the 1.0.0 annotated tag:
|
||||
```bash
|
||||
git tag -a 1.0.0 -m "Release 1.0.0 - Initial Public Release"
|
||||
```
|
||||
|
||||
3. **GitHub Release**: Create a GitHub release from the 1.0.0 tag with:
|
||||
- Release title: "v1.0.0 - Initial Public Release"
|
||||
- Release notes: Use CHANGELOG.md content for 1.0.0
|
||||
- Attach files: manifest.json, main.js, styles.css
|
||||
|
||||
4. **Obsidian Plugin Directory**: Submit to Obsidian's community plugins with:
|
||||
- Plugin ID: obsidian-mcp-server
|
||||
- Version: 1.0.0
|
||||
- Links to GitHub repository and release
|
||||
|
||||
5. **Future Versions**: Use `npm version [major|minor|patch]` which will:
|
||||
- Update package.json version
|
||||
- Run version-bump.mjs to update manifest.json and versions.json
|
||||
- Create git commit and tag automatically
|
||||
- Then push tag to trigger release workflow
|
||||
|
||||
The git history demonstrates the quality and care taken during development, while the clean version numbering provides clarity for public users.
|
||||
@@ -1,149 +0,0 @@
|
||||
# Settings UI Simplification Design
|
||||
|
||||
**Date:** 2025-10-26
|
||||
**Status:** Approved
|
||||
|
||||
## Overview
|
||||
|
||||
Streamline the MCP Server settings UI to reduce visual clutter while preserving all functionality. Use progressive disclosure (collapsible sections) to show only essential controls by default, with advanced settings available on-demand.
|
||||
|
||||
## Goals
|
||||
|
||||
- Remove encryption-related messaging that clutters the UI
|
||||
- Remove redundant network security disclosure
|
||||
- Collapse advanced sections (Authentication, UI Notifications) by default
|
||||
- Simplify all descriptive text
|
||||
- Improve information hierarchy with Server Status at top
|
||||
|
||||
## Design Decisions
|
||||
|
||||
### Structure Changes
|
||||
|
||||
**New UI hierarchy:**
|
||||
```
|
||||
MCP Server Settings
|
||||
├── Server Status (h3) - MOVED TO TOP
|
||||
│ ├── Status indicator
|
||||
│ └── Control buttons
|
||||
├── Auto-start server [toggle]
|
||||
├── Port [text input]
|
||||
├── ▶ Authentication [collapsible, COLLAPSED by default]
|
||||
│ ├── API Key Management
|
||||
│ ├── [Copy Key] [Regenerate Key] buttons
|
||||
│ ├── Key display
|
||||
│ └── ▶ MCP Client Configuration [nested collapsible, COLLAPSED]
|
||||
└── ▶ UI Notifications [collapsible, COLLAPSED by default]
|
||||
└── [Conditional notification settings]
|
||||
```
|
||||
|
||||
**Default collapsed view shows only:**
|
||||
- Server Status with controls
|
||||
- Auto-start toggle
|
||||
- Port setting
|
||||
- ▶ Authentication (collapsed)
|
||||
- ▶ UI Notifications (collapsed)
|
||||
|
||||
### Removals
|
||||
|
||||
**Removed elements:**
|
||||
1. Network security disclosure box (lines 22-30)
|
||||
- "⚠️ This plugin runs a local HTTP server..."
|
||||
2. Encryption description paragraph (lines 64-69)
|
||||
- "Your API key is encrypted and stored securely..."
|
||||
3. Encryption status indicator (lines 72-79)
|
||||
- "🔒 Encryption: Available" / "⚠️ Encryption: Unavailable"
|
||||
4. "Connection Information" section (lines 213-226)
|
||||
- Redundant with MCP config JSON
|
||||
|
||||
### Progressive Disclosure Implementation
|
||||
|
||||
**Use HTML `<details>` and `<summary>` elements:**
|
||||
- Native browser functionality (no JavaScript needed)
|
||||
- Accessible (keyboard navigation, screen readers)
|
||||
- Auto-managed expand/collapse indicators (▶/▼)
|
||||
- Session-persistent state
|
||||
|
||||
**Collapsible sections:**
|
||||
1. **Authentication** - collapsed by default
|
||||
- Contains API key management and MCP config
|
||||
2. **MCP Client Configuration** - nested collapsible, collapsed by default
|
||||
- Contains JSON config snippet
|
||||
3. **UI Notifications** - collapsed by default
|
||||
- Contains all notification settings
|
||||
|
||||
### Text Simplification
|
||||
|
||||
**Updated descriptions:**
|
||||
|
||||
| Setting | Current | Simplified |
|
||||
|---------|---------|------------|
|
||||
| Auto-start | "Automatically start the MCP server when Obsidian launches" | "Start server when Obsidian launches" |
|
||||
| Port | "Port number for the HTTP server (requires restart)" | "Server port (restart required)" |
|
||||
| API Key | "Use this key in the Authorization header as Bearer token" | "Use as Bearer token in Authorization header" |
|
||||
| Enable notifications | "Show notifications when MCP tools are called (request only, no completion notifications)" | "Show when MCP tools are called" |
|
||||
| Duration | "How long notifications stay visible (milliseconds)" | "Duration in milliseconds" |
|
||||
| Log to console | "Also log tool calls to browser console" | "Log tool calls to console" |
|
||||
| MCP config intro | "Add this configuration to your MCP client (e.g., Claude Desktop, Cline):" | "Add to your MCP client config:" |
|
||||
|
||||
**Status messages:**
|
||||
|
||||
| Type | Current | Simplified |
|
||||
|------|---------|------------|
|
||||
| Running | `✅ Server is running on http://127.0.0.1:${port}/mcp` | `✅ Running on port ${port}` |
|
||||
| Stopped | `⭕ Server is stopped` | `⭕ Stopped` |
|
||||
|
||||
**Notices kept as-is:**
|
||||
- "⚠️ Server restart required for port changes to take effect"
|
||||
- "⚠️ Server restart required for API key changes to take effect"
|
||||
- "✅ API key copied to clipboard"
|
||||
- "✅ New API key generated"
|
||||
- "✅ Configuration copied to clipboard"
|
||||
|
||||
### Implementation Details
|
||||
|
||||
**Files changed:**
|
||||
- `src/settings.ts` - single file modification
|
||||
|
||||
**No functionality changes:**
|
||||
- All settings remain functional
|
||||
- All buttons and controls preserved
|
||||
- All styling preserved (colors, padding, backgrounds, fonts)
|
||||
- User-select and cursor styles maintained for copyable elements
|
||||
|
||||
**HTML structure:**
|
||||
```html
|
||||
<details>
|
||||
<summary>Section Title</summary>
|
||||
<!-- Section content -->
|
||||
</details>
|
||||
```
|
||||
|
||||
## User Experience Impact
|
||||
|
||||
**Before (current UI):**
|
||||
- Long scrolling page with all sections expanded
|
||||
- Encryption warnings and technical details visible
|
||||
- Network security disclosure takes prominent space
|
||||
- Important controls buried in middle of page
|
||||
|
||||
**After (simplified UI):**
|
||||
- Compact default view with 5 visible items
|
||||
- Server controls immediately accessible at top
|
||||
- Advanced settings one click away
|
||||
- Clean, focused interface for common tasks
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- No changes to settings functionality or data storage
|
||||
- No removal of capabilities (just reorganization)
|
||||
- No changes to MCP protocol implementation
|
||||
- No changes to notification system behavior
|
||||
|
||||
## Testing Considerations
|
||||
|
||||
- Verify all collapsible sections expand/collapse correctly
|
||||
- Verify nested collapsible (MCP config inside Authentication) works
|
||||
- Verify all buttons and controls remain functional
|
||||
- Verify text inputs and toggles save correctly
|
||||
- Test with server running and stopped states
|
||||
- Test with notifications enabled and disabled
|
||||
@@ -1,443 +0,0 @@
|
||||
# Settings UI Simplification Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Streamline the MCP Server settings UI by removing clutter and using progressive disclosure to collapse advanced sections.
|
||||
|
||||
**Architecture:** Single-file modification to `src/settings.ts` using HTML `<details>/<summary>` elements for collapsible sections. Move Server Status to top, remove encryption messaging, simplify all descriptive text.
|
||||
|
||||
**Tech Stack:** TypeScript, Obsidian Plugin API, HTML details/summary elements
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Remove Encryption Messaging and Network Disclosure
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/settings.ts:22-30` (network disclosure)
|
||||
- Modify: `src/settings.ts:64-79` (encryption description and status)
|
||||
|
||||
**Step 1: Remove network disclosure box**
|
||||
|
||||
Remove lines 22-30 (the network security disclosure):
|
||||
|
||||
```typescript
|
||||
// DELETE THESE LINES (22-30):
|
||||
// Network disclosure
|
||||
const disclosureEl = containerEl.createEl('div', {cls: 'mcp-disclosure'});
|
||||
disclosureEl.createEl('p', {
|
||||
text: '⚠️ This plugin runs a local HTTP server to expose vault operations via the Model Context Protocol (MCP). The server only accepts connections from localhost (127.0.0.1) for security.'
|
||||
});
|
||||
disclosureEl.style.backgroundColor = 'var(--background-secondary)';
|
||||
disclosureEl.style.padding = '12px';
|
||||
disclosureEl.style.marginBottom = '16px';
|
||||
disclosureEl.style.borderRadius = '4px';
|
||||
```
|
||||
|
||||
**Step 2: Remove encryption description and status**
|
||||
|
||||
Remove lines 64-79 (authentication description and encryption status indicator):
|
||||
|
||||
```typescript
|
||||
// DELETE THESE LINES (64-79):
|
||||
const authDesc = containerEl.createEl('p', {
|
||||
text: 'Authentication is required for all requests. Your API key is encrypted and stored securely using your system\'s credential storage.'
|
||||
});
|
||||
authDesc.style.fontSize = '0.9em';
|
||||
authDesc.style.color = 'var(--text-muted)';
|
||||
authDesc.style.marginBottom = '16px';
|
||||
|
||||
// Show encryption status
|
||||
const encryptionStatus = containerEl.createEl('p', {
|
||||
text: isEncryptionAvailable()
|
||||
? '🔒 Encryption: Available (using system keychain)'
|
||||
: '⚠️ Encryption: Unavailable (API key stored in plaintext)'
|
||||
});
|
||||
encryptionStatus.style.fontSize = '0.85em';
|
||||
encryptionStatus.style.marginBottom = '12px';
|
||||
encryptionStatus.style.fontStyle = 'italic';
|
||||
```
|
||||
|
||||
**Step 3: Remove unused import**
|
||||
|
||||
Remove the `isEncryptionAvailable` import from line 5:
|
||||
|
||||
```typescript
|
||||
// CHANGE THIS LINE (5):
|
||||
import { isEncryptionAvailable } from './utils/encryption-utils';
|
||||
|
||||
// TO (remove the import entirely - it's no longer used):
|
||||
// (delete line 5)
|
||||
```
|
||||
|
||||
**Step 4: Verify build**
|
||||
|
||||
Run: `npm run build`
|
||||
Expected: Build succeeds with no errors
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/settings.ts
|
||||
git commit -m "refactor: remove encryption messaging and network disclosure from settings UI"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Move Server Status to Top and Simplify Text
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/settings.ts:176-226` (server status section)
|
||||
- Modify: `src/settings.ts:33-59` (setting descriptions)
|
||||
|
||||
**Step 1: Cut the Server Status section**
|
||||
|
||||
Find and cut lines 176-226 (the entire "Server status" section including the h3, status display, control buttons, and connection information).
|
||||
|
||||
**Step 2: Paste Server Status after the h2 title**
|
||||
|
||||
Insert the Server Status section immediately after line 20 (after `containerEl.createEl('h2', {text: 'MCP Server Settings'});`).
|
||||
|
||||
**Step 3: Simplify Server Status messages**
|
||||
|
||||
Update the status messages to be more concise:
|
||||
|
||||
```typescript
|
||||
// CHANGE THIS (around line 182-186 after move):
|
||||
statusEl.createEl('p', {
|
||||
text: isRunning
|
||||
? `✅ Server is running on http://127.0.0.1:${this.plugin.settings.port}/mcp`
|
||||
: '⭕ Server is stopped'
|
||||
});
|
||||
|
||||
// TO:
|
||||
statusEl.createEl('p', {
|
||||
text: isRunning
|
||||
? `✅ Running on port ${this.plugin.settings.port}`
|
||||
: '⭕ Stopped'
|
||||
});
|
||||
```
|
||||
|
||||
**Step 4: Remove "Connection Information" section**
|
||||
|
||||
Remove the "Connection Information" section (originally lines 213-226):
|
||||
|
||||
```typescript
|
||||
// DELETE THIS ENTIRE SECTION:
|
||||
// Connection info
|
||||
if (isRunning) {
|
||||
containerEl.createEl('h3', {text: 'Connection Information'});
|
||||
|
||||
const infoEl = containerEl.createEl('div', {cls: 'mcp-connection-info'});
|
||||
infoEl.createEl('p', {text: 'MCP Endpoint:'});
|
||||
const mcpEndpoint = infoEl.createEl('code', {text: `http://127.0.0.1:${this.plugin.settings.port}/mcp`});
|
||||
mcpEndpoint.style.userSelect = 'all';
|
||||
mcpEndpoint.style.cursor = 'text';
|
||||
|
||||
infoEl.createEl('p', {text: 'Health Check:'});
|
||||
const healthEndpoint = infoEl.createEl('code', {text: `http://127.0.0.1:${this.plugin.settings.port}/health`});
|
||||
healthEndpoint.style.userSelect = 'all';
|
||||
healthEndpoint.style.cursor = 'text';
|
||||
}
|
||||
```
|
||||
|
||||
**Step 5: Simplify Auto-start description**
|
||||
|
||||
```typescript
|
||||
// CHANGE THIS (line 35):
|
||||
.setDesc('Automatically start the MCP server when Obsidian launches')
|
||||
|
||||
// TO:
|
||||
.setDesc('Start server when Obsidian launches')
|
||||
```
|
||||
|
||||
**Step 6: Simplify Port description**
|
||||
|
||||
```typescript
|
||||
// CHANGE THIS (line 46):
|
||||
.setDesc('Port number for the HTTP server (requires restart)')
|
||||
|
||||
// TO:
|
||||
.setDesc('Server port (restart required)')
|
||||
```
|
||||
|
||||
**Step 7: Verify build**
|
||||
|
||||
Run: `npm run build`
|
||||
Expected: Build succeeds with no errors
|
||||
|
||||
**Step 8: Commit**
|
||||
|
||||
```bash
|
||||
git add src/settings.ts
|
||||
git commit -m "refactor: move server status to top and simplify setting descriptions"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Make Authentication Section Collapsible
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/settings.ts:62-128` (authentication section)
|
||||
|
||||
**Step 1: Wrap Authentication section in details/summary**
|
||||
|
||||
Replace the h3 heading with a details/summary structure:
|
||||
|
||||
```typescript
|
||||
// CHANGE THIS (line 62):
|
||||
containerEl.createEl('h3', {text: 'Authentication'});
|
||||
|
||||
// TO:
|
||||
const authDetails = containerEl.createEl('details');
|
||||
authDetails.style.marginBottom = '20px';
|
||||
const authSummary = authDetails.createEl('summary');
|
||||
authSummary.style.fontSize = '1.17em';
|
||||
authSummary.style.fontWeight = 'bold';
|
||||
authSummary.style.marginBottom = '12px';
|
||||
authSummary.style.cursor = 'pointer';
|
||||
authSummary.setText('Authentication');
|
||||
```
|
||||
|
||||
**Step 2: Update all containerEl references to authDetails**
|
||||
|
||||
Within the Authentication section (lines 82-128), change all `containerEl.createEl` and `containerEl.createDiv` to use `authDetails` instead:
|
||||
|
||||
```typescript
|
||||
// CHANGE PATTERN:
|
||||
new Setting(containerEl)
|
||||
|
||||
// TO:
|
||||
new Setting(authDetails)
|
||||
|
||||
// AND CHANGE:
|
||||
const apiKeyContainer = containerEl.createDiv({cls: 'mcp-api-key-section'});
|
||||
|
||||
// TO:
|
||||
const apiKeyContainer = authDetails.createDiv({cls: 'mcp-api-key-section'});
|
||||
```
|
||||
|
||||
**Step 3: Simplify API Key Management description**
|
||||
|
||||
```typescript
|
||||
// CHANGE THIS (around line 84):
|
||||
.setDesc('Use this key in the Authorization header as Bearer token')
|
||||
|
||||
// TO:
|
||||
.setDesc('Use as Bearer token in Authorization header')
|
||||
```
|
||||
|
||||
**Step 4: Verify build**
|
||||
|
||||
Run: `npm run build`
|
||||
Expected: Build succeeds with no errors
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/settings.ts
|
||||
git commit -m "refactor: make authentication section collapsible"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 4: Make MCP Client Configuration Collapsible (Nested)
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/settings.ts:129-175` (MCP client configuration section)
|
||||
|
||||
**Step 1: Wrap MCP Config in nested details/summary**
|
||||
|
||||
Replace the h3 heading with a nested details/summary structure inside the authDetails:
|
||||
|
||||
```typescript
|
||||
// CHANGE THIS (line 130):
|
||||
containerEl.createEl('h3', {text: 'MCP Client Configuration'});
|
||||
|
||||
const configContainer = containerEl.createDiv({cls: 'mcp-config-snippet'});
|
||||
|
||||
// TO:
|
||||
const configDetails = authDetails.createEl('details');
|
||||
configDetails.style.marginTop = '16px';
|
||||
const configSummary = configDetails.createEl('summary');
|
||||
configSummary.style.fontSize = '1em';
|
||||
configSummary.style.fontWeight = 'bold';
|
||||
configSummary.style.marginBottom = '8px';
|
||||
configSummary.style.cursor = 'pointer';
|
||||
configSummary.setText('MCP Client Configuration');
|
||||
|
||||
const configContainer = configDetails.createDiv({cls: 'mcp-config-snippet'});
|
||||
```
|
||||
|
||||
**Step 2: Simplify config description**
|
||||
|
||||
```typescript
|
||||
// CHANGE THIS (around line 136):
|
||||
const configDesc = configContainer.createEl('p', {
|
||||
text: 'Add this configuration to your MCP client (e.g., Claude Desktop, Cline):'
|
||||
});
|
||||
|
||||
// TO:
|
||||
const configDesc = configContainer.createEl('p', {
|
||||
text: 'Add to your MCP client config:'
|
||||
});
|
||||
```
|
||||
|
||||
**Step 3: Verify build**
|
||||
|
||||
Run: `npm run build`
|
||||
Expected: Build succeeds with no errors
|
||||
|
||||
**Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add src/settings.ts
|
||||
git commit -m "refactor: make MCP client configuration collapsible within authentication"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 5: Make UI Notifications Section Collapsible
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/settings.ts:228-302` (notification settings section)
|
||||
|
||||
**Step 1: Wrap Notifications section in details/summary**
|
||||
|
||||
Replace the h3 heading and description with a details/summary structure:
|
||||
|
||||
```typescript
|
||||
// CHANGE THIS (lines 229-236):
|
||||
containerEl.createEl('h3', {text: 'UI Notifications'});
|
||||
|
||||
const notifDesc = containerEl.createEl('p', {
|
||||
text: 'Display notifications in Obsidian UI when MCP tools are called. Useful for monitoring API activity and debugging.'
|
||||
});
|
||||
notifDesc.style.fontSize = '0.9em';
|
||||
notifDesc.style.color = 'var(--text-muted)';
|
||||
notifDesc.style.marginBottom = '12px';
|
||||
|
||||
// TO:
|
||||
const notifDetails = containerEl.createEl('details');
|
||||
notifDetails.style.marginBottom = '20px';
|
||||
const notifSummary = notifDetails.createEl('summary');
|
||||
notifSummary.style.fontSize = '1.17em';
|
||||
notifSummary.style.fontWeight = 'bold';
|
||||
notifSummary.style.marginBottom = '12px';
|
||||
notifSummary.style.cursor = 'pointer';
|
||||
notifSummary.setText('UI Notifications');
|
||||
```
|
||||
|
||||
**Step 2: Update all containerEl references to notifDetails**
|
||||
|
||||
Within the Notifications section (lines 238-302), change all `new Setting(containerEl)` to use `notifDetails`:
|
||||
|
||||
```typescript
|
||||
// CHANGE PATTERN:
|
||||
new Setting(containerEl)
|
||||
|
||||
// TO:
|
||||
new Setting(notifDetails)
|
||||
```
|
||||
|
||||
**Step 3: Simplify notification setting descriptions**
|
||||
|
||||
```typescript
|
||||
// CHANGE THIS (around line 241):
|
||||
.setDesc('Show notifications when MCP tools are called (request only, no completion notifications)')
|
||||
|
||||
// TO:
|
||||
.setDesc('Show when MCP tools are called')
|
||||
|
||||
// AND CHANGE THIS (around line 269):
|
||||
.setDesc('How long notifications stay visible (milliseconds)')
|
||||
|
||||
// TO:
|
||||
.setDesc('Duration in milliseconds')
|
||||
|
||||
// AND CHANGE THIS (around line 284):
|
||||
.setDesc('Also log tool calls to browser console')
|
||||
|
||||
// TO:
|
||||
.setDesc('Log tool calls to console')
|
||||
```
|
||||
|
||||
**Step 4: Verify build**
|
||||
|
||||
Run: `npm run build`
|
||||
Expected: Build succeeds with no errors
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/settings.ts
|
||||
git commit -m "refactor: make UI notifications section collapsible and simplify descriptions"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 6: Final Verification and Testing
|
||||
|
||||
**Files:**
|
||||
- Test: All settings UI functionality
|
||||
|
||||
**Step 1: Run full test suite**
|
||||
|
||||
Run: `npm test`
|
||||
Expected: All 569 tests pass
|
||||
|
||||
**Step 2: Build production bundle**
|
||||
|
||||
Run: `npm run build`
|
||||
Expected: Build succeeds, `main.js` created
|
||||
|
||||
**Step 3: Manual testing checklist**
|
||||
|
||||
Create a manual testing checklist (you don't need to execute this, document it for the implementer):
|
||||
|
||||
```markdown
|
||||
Manual Testing Checklist:
|
||||
- [ ] Settings tab opens without errors
|
||||
- [ ] Server Status section appears at top
|
||||
- [ ] Auto-start and Port settings visible and functional
|
||||
- [ ] Authentication section collapsed by default
|
||||
- [ ] Authentication section expands when clicked
|
||||
- [ ] API key copy/regenerate buttons work
|
||||
- [ ] MCP Client Configuration collapsed within Authentication
|
||||
- [ ] MCP Client Configuration expands when clicked
|
||||
- [ ] Config copy button works
|
||||
- [ ] UI Notifications section collapsed by default
|
||||
- [ ] UI Notifications section expands when clicked
|
||||
- [ ] All notification sub-settings work when enabled
|
||||
- [ ] No encryption messages visible anywhere
|
||||
- [ ] No network disclosure visible
|
||||
- [ ] Status messages show simplified text
|
||||
- [ ] Server start/stop/restart buttons work
|
||||
```
|
||||
|
||||
**Step 4: Commit testing notes**
|
||||
|
||||
```bash
|
||||
git add docs/plans/2025-10-26-simplify-settings-ui.md
|
||||
git commit -m "docs: add manual testing checklist for settings UI"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
**Total estimated time:** 30-45 minutes
|
||||
|
||||
**Key principles:**
|
||||
- **YAGNI:** Remove all unnecessary messaging and disclosure text
|
||||
- **DRY:** Reuse details/summary pattern for all collapsible sections
|
||||
- **Progressive disclosure:** Default to collapsed state for advanced sections
|
||||
|
||||
**Potential issues:**
|
||||
- Details/summary styling may need adjustment for Obsidian theme compatibility
|
||||
- Ensure cursor styles work correctly for summary elements
|
||||
- Verify nested details (MCP Config inside Auth) expands/collapses independently
|
||||
|
||||
**Testing strategy:**
|
||||
- Unit tests shouldn't be affected (UI only change)
|
||||
- Manual testing critical for verifying collapsible behavior
|
||||
- Test in both light and dark themes if possible
|
||||
Reference in New Issue
Block a user