@dollhousemcp/mcp-server 2.0.16 → 2.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md.backup +18 -0
  3. package/dist/elements/BaseElement.d.ts +1 -0
  4. package/dist/elements/BaseElement.d.ts.map +1 -1
  5. package/dist/elements/BaseElement.js +7 -1
  6. package/dist/elements/agents/AgentManager.js +2 -2
  7. package/dist/elements/base/BaseElementManager.d.ts.map +1 -1
  8. package/dist/elements/base/BaseElementManager.js +17 -1
  9. package/dist/elements/base/ElementFileOperations.js +2 -2
  10. package/dist/elements/ensembles/EnsembleManager.js +3 -3
  11. package/dist/elements/memories/MemoryManager.d.ts.map +1 -1
  12. package/dist/elements/memories/MemoryManager.js +14 -3
  13. package/dist/elements/skills/SkillManager.js +2 -2
  14. package/dist/elements/templates/TemplateManager.js +2 -2
  15. package/dist/generated/version.d.ts +2 -2
  16. package/dist/generated/version.js +3 -3
  17. package/dist/handlers/ElementCRUDHandler.d.ts.map +1 -1
  18. package/dist/handlers/ElementCRUDHandler.js +3 -2
  19. package/dist/handlers/element-crud/createElement.d.ts.map +1 -1
  20. package/dist/handlers/element-crud/createElement.js +6 -2
  21. package/dist/handlers/element-crud/editElement.d.ts.map +1 -1
  22. package/dist/handlers/element-crud/editElement.js +6 -2
  23. package/dist/handlers/element-crud/helpers.d.ts +2 -0
  24. package/dist/handlers/element-crud/helpers.d.ts.map +1 -1
  25. package/dist/handlers/element-crud/helpers.js +21 -2
  26. package/dist/handlers/mcp-aql/IntrospectionResolver.d.ts.map +1 -1
  27. package/dist/handlers/mcp-aql/IntrospectionResolver.js +34 -7
  28. package/dist/handlers/mcp-aql/MCPAQLHandler.d.ts +1 -0
  29. package/dist/handlers/mcp-aql/MCPAQLHandler.d.ts.map +1 -1
  30. package/dist/handlers/mcp-aql/MCPAQLHandler.js +50 -14
  31. package/dist/handlers/mcp-aql/OperationSchema.d.ts.map +1 -1
  32. package/dist/handlers/mcp-aql/OperationSchema.js +3 -2
  33. package/dist/handlers/mcp-aql/evaluatePermission.d.ts.map +1 -1
  34. package/dist/handlers/mcp-aql/evaluatePermission.js +2 -1
  35. package/dist/handlers/mcp-aql/policies/ElementPolicies.d.ts +17 -1
  36. package/dist/handlers/mcp-aql/policies/ElementPolicies.d.ts.map +1 -1
  37. package/dist/handlers/mcp-aql/policies/ElementPolicies.js +88 -4
  38. package/dist/handlers/strategies/AgentActivationStrategy.d.ts.map +1 -1
  39. package/dist/handlers/strategies/AgentActivationStrategy.js +5 -1
  40. package/dist/handlers/strategies/BaseActivationStrategy.d.ts +1 -0
  41. package/dist/handlers/strategies/BaseActivationStrategy.d.ts.map +1 -1
  42. package/dist/handlers/strategies/BaseActivationStrategy.js +15 -1
  43. package/dist/handlers/strategies/EnsembleActivationStrategy.d.ts.map +1 -1
  44. package/dist/handlers/strategies/EnsembleActivationStrategy.js +5 -1
  45. package/dist/handlers/strategies/MemoryActivationStrategy.d.ts.map +1 -1
  46. package/dist/handlers/strategies/MemoryActivationStrategy.js +5 -1
  47. package/dist/handlers/strategies/PersonaActivationStrategy.d.ts.map +1 -1
  48. package/dist/handlers/strategies/PersonaActivationStrategy.js +5 -1
  49. package/dist/handlers/strategies/SkillActivationStrategy.d.ts.map +1 -1
  50. package/dist/handlers/strategies/SkillActivationStrategy.js +5 -1
  51. package/dist/handlers/strategies/TemplateActivationStrategy.d.ts.map +1 -1
  52. package/dist/handlers/strategies/TemplateActivationStrategy.js +7 -2
  53. package/dist/persona/PersonaElement.js +2 -2
  54. package/dist/services/SerializationService.d.ts.map +1 -1
  55. package/dist/services/SerializationService.js +7 -1
  56. package/dist/types/elements/IElement.d.ts +9 -0
  57. package/dist/types/elements/IElement.d.ts.map +1 -1
  58. package/dist/types/elements/IElement.js +1 -1
  59. package/dist/utils/permissionHooks.d.ts +39 -3
  60. package/dist/utils/permissionHooks.d.ts.map +1 -1
  61. package/dist/utils/permissionHooks.js +651 -74
  62. package/dist/web/public/permissions.css +190 -2
  63. package/dist/web/public/permissions.js +209 -56
  64. package/dist/web/public/setup.js +452 -108
  65. package/dist/web/routes/permissionRoutes.d.ts.map +1 -1
  66. package/dist/web/routes/permissionRoutes.js +108 -17
  67. package/dist/web/routes/setupRoutes.d.ts +1 -0
  68. package/dist/web/routes/setupRoutes.d.ts.map +1 -1
  69. package/dist/web/routes/setupRoutes.js +128 -42
  70. package/package.json +3 -1
  71. package/scripts/pretooluse-dollhouse.sh +39 -1
  72. package/scripts/pretooluse-vscode.sh +163 -0
  73. package/scripts/pretooluse-windsurf.sh +166 -4
  74. package/server.json +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dollhousemcp/mcp-server",
3
- "version": "2.0.16",
3
+ "version": "2.0.18",
4
4
  "description": "DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -134,6 +134,7 @@
134
134
  "dist/web/public/**",
135
135
  "dist/seed-elements/**",
136
136
  "scripts/pretooluse-dollhouse.sh",
137
+ "scripts/pretooluse-vscode.sh",
137
138
  "scripts/pretooluse-cursor.sh",
138
139
  "scripts/pretooluse-windsurf.sh",
139
140
  "scripts/pretooluse-gemini.sh",
@@ -179,6 +180,7 @@
179
180
  "otpauth": "^9.5.0",
180
181
  "posthog-node": "^5.24.17",
181
182
  "qrcode": "^1.5.4",
183
+ "smol-toml": "^1.6.1",
182
184
  "uuid": "^11.1.0",
183
185
  "zod": "^4.3.6"
184
186
  },
@@ -29,6 +29,35 @@ debug() {
29
29
  return 0
30
30
  }
31
31
 
32
+ normalize_response() {
33
+ local response="$1"
34
+
35
+ case "$HOOK_PLATFORM" in
36
+ claude_code)
37
+ echo "$response" | jq -c '
38
+ if (.hookSpecificOutput.permissionDecision? | type) == "string" then
39
+ .
40
+ elif (.decision? | type) == "string" and (.decision | IN("allow", "deny", "ask")) then
41
+ {
42
+ hookSpecificOutput: {
43
+ hookEventName: "PreToolUse",
44
+ permissionDecision: .decision,
45
+ permissionDecisionReason: (.reason // .message // "")
46
+ }
47
+ }
48
+ else
49
+ empty
50
+ end
51
+ ' 2>/dev/null
52
+ ;;
53
+ *)
54
+ echo "$response"
55
+ ;;
56
+ esac
57
+
58
+ return 0
59
+ }
60
+
32
61
  # Discover the port from the port file
33
62
  if [[ -f "$PORT_FILE" ]]; then
34
63
  PORT=$(cat "$PORT_FILE" 2>/dev/null)
@@ -92,7 +121,16 @@ while [[ $ATTEMPT -le $MAX_RETRIES ]]; do
92
121
 
93
122
  if [[ $CURL_EXIT -eq 0 ]] && [[ -n "$RESPONSE" ]]; then
94
123
  debug "Response (attempt $((ATTEMPT+1))): $RESPONSE"
95
- echo "$RESPONSE"
124
+ if ! echo "$RESPONSE" | jq -e . >/dev/null 2>&1; then
125
+ debug "Non-JSON response for platform $HOOK_PLATFORM — fail open"
126
+ exit 0
127
+ fi
128
+ NORMALIZED_RESPONSE=$(normalize_response "$RESPONSE")
129
+ if [[ -n "$NORMALIZED_RESPONSE" ]]; then
130
+ echo "$NORMALIZED_RESPONSE"
131
+ else
132
+ debug "Malformed response for platform $HOOK_PLATFORM — fail open"
133
+ fi
96
134
  exit 0
97
135
  fi
98
136
 
@@ -0,0 +1,163 @@
1
+ #!/bin/bash
2
+ # pretooluse-vscode.sh — VS Code / Copilot hook wrapper for DollhouseMCP
3
+ #
4
+ # VS Code supports Claude-compatible PreToolUse hooks, but its tool names and
5
+ # tool_input field names differ from Claude Code. This adapter normalizes the
6
+ # most relevant built-in tool names into Dollhouse's existing permission model.
7
+
8
+ PORT_FILE="$HOME/.dollhouse/run/permission-server.port"
9
+ MAX_RETRIES=2
10
+ INITIAL_TIMEOUT=5
11
+ HOOK_PLATFORM="vscode"
12
+
13
+ debug() {
14
+ if [[ "${DOLLHOUSE_HOOK_DEBUG:-0}" == "1" ]]; then
15
+ echo "[pretooluse-vscode] $*" >&2
16
+ fi
17
+ return 0
18
+ }
19
+
20
+ if [[ -f "$PORT_FILE" ]]; then
21
+ PORT=$(cat "$PORT_FILE" 2>/dev/null)
22
+ else
23
+ debug "No port file at $PORT_FILE — fail open"
24
+ exit 0
25
+ fi
26
+
27
+ if ! [[ "$PORT" =~ ^[0-9]+$ ]]; then
28
+ debug "Invalid port value: $PORT — fail open"
29
+ exit 0
30
+ fi
31
+
32
+ ENDPOINT="http://127.0.0.1:${PORT}/api/evaluate_permission"
33
+ INPUT=$(cat)
34
+
35
+ RAW_TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // .toolName // .tool // .name // empty' 2>/dev/null)
36
+ TOOL_NAME="$RAW_TOOL_NAME"
37
+ TOOL_INPUT=$(echo "$INPUT" | jq -c '.tool_input // .toolInput // .input // {}' 2>/dev/null)
38
+
39
+ case "$RAW_TOOL_NAME" in
40
+ runTerminalCommand|run_terminal_command|run_in_terminal|terminal_command)
41
+ TOOL_NAME='Bash'
42
+ TOOL_INPUT=$(echo "$INPUT" | jq -c '
43
+ . as $root
44
+ | {
45
+ command: (
46
+ $root.tool_input.command
47
+ // $root.tool_input.commandLine
48
+ // $root.toolInput.command
49
+ // $root.toolInput.commandLine
50
+ // $root.input.command
51
+ // $root.input.commandLine
52
+ // ""
53
+ )
54
+ }
55
+ | if ($root.cwd // $root.working_directory // $root.workingDirectory) != null
56
+ then . + { cwd: ($root.cwd // $root.working_directory // $root.workingDirectory) }
57
+ else .
58
+ end
59
+ ' 2>/dev/null)
60
+ ;;
61
+ createFile|create_file|writeFile|write_file)
62
+ TOOL_NAME='Write'
63
+ ;;
64
+ editFile|edit_file|editFiles|replace_string_in_file|multi_edit)
65
+ TOOL_NAME='Edit'
66
+ ;;
67
+ readFile|read_file|openFile|open_file)
68
+ TOOL_NAME='Read'
69
+ ;;
70
+ findFiles|find_files|glob_search)
71
+ TOOL_NAME='Glob'
72
+ ;;
73
+ searchWorkspace|search_workspace|searchInFiles|grep_search)
74
+ TOOL_NAME='Grep'
75
+ ;;
76
+ fetchWebPage|fetch_web_page|fetch_webpage)
77
+ TOOL_NAME='WebFetch'
78
+ ;;
79
+ searchWeb|search_web)
80
+ TOOL_NAME='WebSearch'
81
+ ;;
82
+ *)
83
+ ;;
84
+ esac
85
+
86
+ if [[ -z "$TOOL_NAME" ]]; then
87
+ debug "Could not parse VS Code tool name — fail open"
88
+ exit 0
89
+ fi
90
+
91
+ if [[ -z "$TOOL_INPUT" ]]; then
92
+ TOOL_INPUT='{}'
93
+ fi
94
+
95
+ PAYLOAD=$(jq -cn \
96
+ --arg tool_name "$TOOL_NAME" \
97
+ --arg platform "$HOOK_PLATFORM" \
98
+ --arg session_id "${DOLLHOUSE_SESSION_ID:-}" \
99
+ --argjson input "$TOOL_INPUT" \
100
+ '{
101
+ tool_name: $tool_name,
102
+ input: $input,
103
+ platform: $platform
104
+ } + (if ($session_id | length) > 0 then { session_id: $session_id } else {} end)')
105
+
106
+ ATTEMPT=0
107
+ TIMEOUT=$INITIAL_TIMEOUT
108
+
109
+ while [[ $ATTEMPT -le $MAX_RETRIES ]]; do
110
+ RESPONSE=$(curl -s --max-time "$TIMEOUT" -X POST "$ENDPOINT" \
111
+ -H "Content-Type: application/json" \
112
+ -d "$PAYLOAD" \
113
+ 2>/dev/null)
114
+ CURL_EXIT=$?
115
+
116
+ if [[ $CURL_EXIT -eq 0 ]] && [[ -n "$RESPONSE" ]]; then
117
+ HOOK_RESPONSE=$(echo "$RESPONSE" | jq -c '
118
+ def decision_from_response:
119
+ if (.hookSpecificOutput.permissionDecision? | type) == "string" then .hookSpecificOutput.permissionDecision
120
+ elif (.decision? | type) == "string" then .decision
121
+ elif (.allowed? == true) then "allow"
122
+ elif (.allowed? == false) then "deny"
123
+ else empty
124
+ end;
125
+ def reason_from_response:
126
+ .hookSpecificOutput.permissionDecisionReason
127
+ // .reason
128
+ // .hookSpecificOutput.reason
129
+ // empty;
130
+
131
+ (decision_from_response) as $decision
132
+ | if ($decision | type) == "string" and ($decision | length) > 0 then
133
+ {
134
+ hookSpecificOutput:
135
+ ({
136
+ hookEventName: "PreToolUse",
137
+ permissionDecision: $decision
138
+ } + (if (reason_from_response | type) == "string" and (reason_from_response | length) > 0
139
+ then { permissionDecisionReason: reason_from_response }
140
+ else {}
141
+ end))
142
+ }
143
+ else empty
144
+ end
145
+ ' 2>/dev/null)
146
+
147
+ if [[ -n "$HOOK_RESPONSE" ]]; then
148
+ echo "$HOOK_RESPONSE"
149
+ else
150
+ debug "Permission evaluation returned an unrecognized response — fail open"
151
+ fi
152
+ exit 0
153
+ fi
154
+
155
+ ATTEMPT=$((ATTEMPT + 1))
156
+ if [[ $ATTEMPT -le $MAX_RETRIES ]]; then
157
+ sleep $((TIMEOUT * ATTEMPT))
158
+ TIMEOUT=$((TIMEOUT * 2))
159
+ fi
160
+ done
161
+
162
+ debug "Permission evaluation failed — fail open"
163
+ exit 0
@@ -1,6 +1,168 @@
1
1
  #!/bin/bash
2
- # pretooluse-windsurf.sh — Manual Windsurf hook wrapper for DollhouseMCP
2
+ # pretooluse-windsurf.sh — Windsurf hook wrapper for DollhouseMCP
3
+ #
4
+ # Windsurf pre-hooks are binary: exit 0 to allow, exit 2 to block.
5
+ # This wrapper translates Windsurf hook events into Dollhouse permission
6
+ # evaluations and then maps the response back to Windsurf exit codes.
3
7
 
4
- export DOLLHOUSE_HOOK_PLATFORM="windsurf"
5
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
- exec bash "$SCRIPT_DIR/pretooluse-dollhouse.sh"
8
+ PORT_FILE="$HOME/.dollhouse/run/permission-server.port"
9
+ MAX_RETRIES=2
10
+ INITIAL_TIMEOUT=5
11
+ HOOK_PLATFORM="windsurf"
12
+
13
+ debug() {
14
+ if [[ "${DOLLHOUSE_HOOK_DEBUG:-0}" == "1" ]]; then
15
+ echo "[pretooluse-windsurf] $*" >&2
16
+ fi
17
+ return 0
18
+ }
19
+
20
+ if [[ -f "$PORT_FILE" ]]; then
21
+ PORT=$(cat "$PORT_FILE" 2>/dev/null)
22
+ else
23
+ debug "No port file at $PORT_FILE — fail open"
24
+ exit 0
25
+ fi
26
+
27
+ if ! [[ "$PORT" =~ ^[0-9]+$ ]]; then
28
+ debug "Invalid port value: $PORT — fail open"
29
+ exit 0
30
+ fi
31
+
32
+ ENDPOINT="http://127.0.0.1:${PORT}/api/evaluate_permission"
33
+ INPUT=$(cat)
34
+
35
+ EVENT_NAME=$(echo "$INPUT" | jq -r '
36
+ .hook_event_name
37
+ // .hookEventName
38
+ // .event_name
39
+ // .eventName
40
+ // .event
41
+ // empty
42
+ ' 2>/dev/null)
43
+
44
+ TOOL_NAME=''
45
+ TOOL_INPUT='{}'
46
+
47
+ case "$EVENT_NAME" in
48
+ pre_run_command)
49
+ TOOL_NAME='Bash'
50
+ TOOL_INPUT=$(echo "$INPUT" | jq -c '
51
+ . as $root
52
+ | {
53
+ command: (
54
+ $root.tool_info.command_line
55
+ // $root.toolInfo.commandLine
56
+ // $root.command_line
57
+ // $root.commandLine
58
+ // $root.command
59
+ // $root.input.command
60
+ // $root.tool_input.command
61
+ // ""
62
+ )
63
+ }
64
+ | if ($root.cwd // $root.working_directory // $root.workingDirectory // $root.tool_info.cwd // $root.toolInfo.cwd) != null
65
+ then . + { cwd: ($root.cwd // $root.working_directory // $root.workingDirectory // $root.tool_info.cwd // $root.toolInfo.cwd) }
66
+ else .
67
+ end
68
+ ' 2>/dev/null)
69
+ ;;
70
+ pre_mcp_tool_use)
71
+ TOOL_NAME=$(echo "$INPUT" | jq -r '
72
+ .tool_name
73
+ // .toolName
74
+ // .tool.name
75
+ // .tool_info.name
76
+ // .toolInfo.name
77
+ // .name
78
+ // .mcp_tool_name
79
+ // empty
80
+ ' 2>/dev/null)
81
+ TOOL_INPUT=$(echo "$INPUT" | jq -c '
82
+ . as $root
83
+ | (
84
+ $root.tool_arguments
85
+ // $root.toolArgs
86
+ // $root.input.arguments
87
+ // $root.arguments
88
+ // $root.tool_input
89
+ // $root.input
90
+ // {}
91
+ )
92
+ | if ($root.server_name // $root.serverName // $root.mcp_server_name) != null
93
+ then . + { server_name: ($root.server_name // $root.serverName // $root.mcp_server_name) }
94
+ else .
95
+ end
96
+ ' 2>/dev/null)
97
+ ;;
98
+ *)
99
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // .toolName // .tool // .name // empty' 2>/dev/null)
100
+ TOOL_INPUT=$(echo "$INPUT" | jq -c '.tool_input // .toolInput // .input // {}' 2>/dev/null)
101
+ ;;
102
+ esac
103
+
104
+ if [[ -z "$TOOL_NAME" ]]; then
105
+ debug "Could not parse Windsurf tool name — fail open"
106
+ exit 0
107
+ fi
108
+
109
+ PAYLOAD=$(jq -cn \
110
+ --arg tool_name "$TOOL_NAME" \
111
+ --arg platform "$HOOK_PLATFORM" \
112
+ --arg session_id "${DOLLHOUSE_SESSION_ID:-}" \
113
+ --argjson input "$TOOL_INPUT" \
114
+ '{
115
+ tool_name: $tool_name,
116
+ input: $input,
117
+ platform: $platform
118
+ } + (if ($session_id | length) > 0 then { session_id: $session_id } else {} end)')
119
+
120
+ ATTEMPT=0
121
+ TIMEOUT=$INITIAL_TIMEOUT
122
+
123
+ while [[ $ATTEMPT -le $MAX_RETRIES ]]; do
124
+ RESPONSE=$(curl -s --max-time "$TIMEOUT" -X POST "$ENDPOINT" \
125
+ -H "Content-Type: application/json" \
126
+ -d "$PAYLOAD" \
127
+ 2>/dev/null)
128
+ CURL_EXIT=$?
129
+
130
+ if [[ $CURL_EXIT -eq 0 ]] && [[ -n "$RESPONSE" ]]; then
131
+ ALLOWED=$(echo "$RESPONSE" | jq -r '
132
+ if .allowed == true then "true"
133
+ elif .allowed == false then "false"
134
+ elif .decision == "allow" then "true"
135
+ elif .decision == "deny" or .decision == "ask" then "false"
136
+ elif .hookSpecificOutput.permissionDecision == "allow" then "true"
137
+ elif .hookSpecificOutput.permissionDecision == "deny" or .hookSpecificOutput.permissionDecision == "ask" then "false"
138
+ else "unknown"
139
+ end
140
+ ' 2>/dev/null)
141
+
142
+ if [[ "$ALLOWED" == "true" ]]; then
143
+ exit 0
144
+ fi
145
+
146
+ if [[ "$ALLOWED" == "false" ]]; then
147
+ REASON=$(echo "$RESPONSE" | jq -r '
148
+ .reason
149
+ // .hookSpecificOutput.reason
150
+ // .hookSpecificOutput.permissionDecisionReason
151
+ // empty
152
+ ' 2>/dev/null)
153
+ if [[ -n "$REASON" ]]; then
154
+ echo "$REASON" >&2
155
+ fi
156
+ exit 2
157
+ fi
158
+ fi
159
+
160
+ ATTEMPT=$((ATTEMPT + 1))
161
+ if [[ $ATTEMPT -le $MAX_RETRIES ]]; then
162
+ sleep $((TIMEOUT * ATTEMPT))
163
+ TIMEOUT=$((TIMEOUT * 2))
164
+ fi
165
+ done
166
+
167
+ debug "Permission evaluation failed — fail open"
168
+ exit 0
package/server.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "io.github.DollhouseMCP/mcp-server",
4
4
  "title": "DollhouseMCP",
5
5
  "description": "OSS to create Personas, Skills, Templates, Agents, and Memories to customize your AI experience.",
6
- "version": "2.0.16",
6
+ "version": "2.0.18",
7
7
  "homepage": "https://dollhousemcp.com",
8
8
  "repository": {
9
9
  "type": "git",
@@ -29,7 +29,7 @@
29
29
  {
30
30
  "registryType": "npm",
31
31
  "identifier": "@dollhousemcp/mcp-server",
32
- "version": "2.0.16",
32
+ "version": "2.0.18",
33
33
  "transport": {
34
34
  "type": "stdio"
35
35
  }