feat: implement Obsidian MCP Bundle MVP (Phase 1-3)
- Complete project setup with TypeScript, Jest, MCPB manifest - Implement foundational infrastructure (CLI executor, logger, error handler) - Add 9 file operation tools for User Story 1 - Full MCP protocol compliance with stdio transport - Input validation and sanitization for security - Comprehensive error handling with actionable messages - Constitutional compliance: all 6 principles satisfied MVP includes: - obsidian_create_note, read, append, prepend, delete, move, rename, open, file_info - Zod validation schemas for all parameters - 30s timeout configuration with per-command overrides - Stderr-only logging with sanitized output - Graceful shutdown handling Build: ✅ 0 errors, 0 vulnerabilities Tasks: 48/167 complete (MVP milestone) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
242
specs/001-obsidian-mcp-bundle/contracts/mcp-protocol.md
Normal file
242
specs/001-obsidian-mcp-bundle/contracts/mcp-protocol.md
Normal file
@@ -0,0 +1,242 @@
|
||||
# MCP Protocol Contract
|
||||
|
||||
**Purpose**: Define the JSON-RPC communication contract between AI assistants and the Obsidian MCP server
|
||||
|
||||
## Transport
|
||||
|
||||
**Protocol**: JSON-RPC 2.0 over stdio
|
||||
**Direction**: Bidirectional (client ↔ server)
|
||||
|
||||
### Stdio Requirements
|
||||
|
||||
- **stdin**: Server reads JSON-RPC requests line-by-line
|
||||
- **stdout**: Server writes JSON-RPC responses line-by-line (one message per line)
|
||||
- **stderr**: Server logs debugging information (never JSON-RPC messages)
|
||||
|
||||
### Message Format
|
||||
|
||||
All messages are single-line JSON objects ending with `\n`:
|
||||
|
||||
```json
|
||||
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{...}}\n
|
||||
```
|
||||
|
||||
## Initialization Handshake
|
||||
|
||||
### Request: initialize
|
||||
|
||||
Client sends capabilities and version information:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "initialize",
|
||||
"params": {
|
||||
"protocolVersion": "2024-11-05",
|
||||
"capabilities": {},
|
||||
"clientInfo": {
|
||||
"name": "Claude Desktop",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Response: initialize
|
||||
|
||||
Server responds with its capabilities:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": {
|
||||
"protocolVersion": "2024-11-05",
|
||||
"capabilities": {
|
||||
"tools": {}
|
||||
},
|
||||
"serverInfo": {
|
||||
"name": "obsidian-mcp",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Tool Discovery
|
||||
|
||||
### Request: tools/list
|
||||
|
||||
Client requests available tools:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 2,
|
||||
"method": "tools/list",
|
||||
"params": {}
|
||||
}
|
||||
```
|
||||
|
||||
### Response: tools/list
|
||||
|
||||
Server returns tool definitions with schemas:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 2,
|
||||
"result": {
|
||||
"tools": [
|
||||
{
|
||||
"name": "obsidian_create_note",
|
||||
"description": "Create a new note in the vault",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Note name (without .md extension) or exact path"
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "Initial note content (markdown)"
|
||||
},
|
||||
"overwrite": {
|
||||
"type": "boolean",
|
||||
"description": "Overwrite if file exists"
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
}
|
||||
// ... more tools
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Tool Invocation
|
||||
|
||||
### Request: tools/call
|
||||
|
||||
Client invokes a tool:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 3,
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "obsidian_create_note",
|
||||
"arguments": {
|
||||
"name": "Meeting Notes",
|
||||
"content": "# Meeting Notes\n\n- Agenda item 1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Response: Success
|
||||
|
||||
Tool execution succeeded:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 3,
|
||||
"result": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "{\"success\":true,\"data\":{\"path\":\"Meeting Notes.md\",\"created\":true}}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Response: Error
|
||||
|
||||
Tool execution failed:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 3,
|
||||
"error": {
|
||||
"code": -32000,
|
||||
"message": "Obsidian application is not running. Please start Obsidian and try again.",
|
||||
"data": {
|
||||
"errorCode": "OBSIDIAN_NOT_RUNNING"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Error Codes
|
||||
|
||||
### JSON-RPC Standard Errors
|
||||
|
||||
| Code | Message | Meaning |
|
||||
|------|---------|---------|
|
||||
| -32700 | Parse error | Invalid JSON |
|
||||
| -32600 | Invalid Request | Invalid JSON-RPC structure |
|
||||
| -32601 | Method not found | Unknown method |
|
||||
| -32602 | Invalid params | Parameter validation failed |
|
||||
| -32603 | Internal error | Server internal error |
|
||||
|
||||
### Application Errors
|
||||
|
||||
| Code | Message | When |
|
||||
|------|---------|------|
|
||||
| -32000 | Server error | Generic application error |
|
||||
| -32001 | Obsidian not running | Obsidian app not detected |
|
||||
| -32002 | Vault not found | Invalid vault name |
|
||||
| -32003 | File not found | Note doesn't exist |
|
||||
| -32004 | Ambiguous name | Multiple files match name |
|
||||
| -32005 | Permission denied | Filesystem permission issue |
|
||||
| -32006 | Timeout | Operation exceeded 30s |
|
||||
|
||||
## Shutdown
|
||||
|
||||
### Notification: exit
|
||||
|
||||
Client notifies server of shutdown (no response expected):
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "exit"
|
||||
}
|
||||
```
|
||||
|
||||
Server should:
|
||||
1. Complete any in-flight operations
|
||||
2. Close file handles
|
||||
3. Flush logs
|
||||
4. Exit process
|
||||
|
||||
### EOF on stdin
|
||||
|
||||
If stdin closes without `exit` notification, server should gracefully shutdown.
|
||||
|
||||
## Performance Requirements
|
||||
|
||||
- **initialize response**: <100ms
|
||||
- **tools/list response**: <200ms
|
||||
- **tools/call response**: <3s for file ops, <5s for search (per SC-001, SC-002)
|
||||
|
||||
## Compliance Checklist
|
||||
|
||||
- [x] JSON-RPC 2.0 format for all messages
|
||||
- [x] Stdio transport (stdin for requests, stdout for responses)
|
||||
- [x] Line-by-line message format (newline-delimited)
|
||||
- [x] Initialize handshake before tool calls
|
||||
- [x] Tool schemas in tools/list response
|
||||
- [x] Structured error responses with codes
|
||||
- [x] Graceful shutdown on exit notification
|
||||
- [x] No stdout pollution (logs to stderr only)
|
||||
- [x] Immediate stdout flushing after responses
|
||||
- [x] EOF handling (graceful shutdown)
|
||||
Reference in New Issue
Block a user