apple-mcp-notes 1.0.1__py3-none-any.whl
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.
- apple_mcp_notes-1.0.1.dist-info/METADATA +146 -0
- apple_mcp_notes-1.0.1.dist-info/RECORD +24 -0
- apple_mcp_notes-1.0.1.dist-info/WHEEL +5 -0
- apple_mcp_notes-1.0.1.dist-info/entry_points.txt +3 -0
- apple_mcp_notes-1.0.1.dist-info/licenses/LICENSE +21 -0
- apple_mcp_notes-1.0.1.dist-info/top_level.txt +1 -0
- apple_notes_mcp/__init__.py +3 -0
- apple_notes_mcp/applescripts/create_folder.applescript +50 -0
- apple_notes_mcp/applescripts/create_note.applescript +130 -0
- apple_notes_mcp/applescripts/delete_folder.applescript +42 -0
- apple_notes_mcp/applescripts/delete_note.applescript +38 -0
- apple_notes_mcp/applescripts/get_note.applescript +122 -0
- apple_notes_mcp/applescripts/list_accounts.applescript +75 -0
- apple_notes_mcp/applescripts/list_attachments.applescript +61 -0
- apple_notes_mcp/applescripts/list_folders.applescript +107 -0
- apple_notes_mcp/applescripts/list_notes.applescript +124 -0
- apple_notes_mcp/applescripts/move_note.applescript +63 -0
- apple_notes_mcp/applescripts/rename_folder.applescript +46 -0
- apple_notes_mcp/applescripts/update_note.applescript +181 -0
- apple_notes_mcp/config.py +43 -0
- apple_notes_mcp/models.py +133 -0
- apple_notes_mcp/notes_bridge.py +340 -0
- apple_notes_mcp/permissions.py +55 -0
- apple_notes_mcp/tools.py +542 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: apple-mcp-notes
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: Local Apple Notes MCP server for macOS
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/JonathanRReed/Apple-MCPs
|
|
7
|
+
Project-URL: Repository, https://github.com/JonathanRReed/Apple-MCPs
|
|
8
|
+
Project-URL: Changelog, https://github.com/JonathanRReed/Apple-MCPs/blob/main/CHANGELOG.md
|
|
9
|
+
Project-URL: Issues, https://github.com/JonathanRReed/Apple-MCPs/issues
|
|
10
|
+
Keywords: mcp,model-context-protocol,macos,apple,ai-agent,automation
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Environment :: MacOS X
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: MacOS
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: apple-mcp-common<2,>=1.0.0
|
|
24
|
+
Requires-Dist: mcp<3,>=2.0.0
|
|
25
|
+
Requires-Dist: pydantic>=2.12.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest<10,>=8; extra == "dev"
|
|
28
|
+
Requires-Dist: ruff<1,>=0.12; extra == "dev"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
<!-- mcp-name: io.github.jonathanrreed/apple-mcp-notes -->
|
|
32
|
+
|
|
33
|
+
# Apple Notes MCP
|
|
34
|
+
|
|
35
|
+
MCP server for Apple Notes on macOS.
|
|
36
|
+
|
|
37
|
+
Provides access to notes and folders for creation, organization, and management. Keep Notes as the system of record while enabling agents to read, create, update, move, and delete notes.
|
|
38
|
+
|
|
39
|
+
## When to use
|
|
40
|
+
|
|
41
|
+
- Notes-only workflows
|
|
42
|
+
- Stronger isolation than the all-in-one server
|
|
43
|
+
- Structured note and folder operations through the native Notes app
|
|
44
|
+
|
|
45
|
+
## What It Does
|
|
46
|
+
|
|
47
|
+
- List accounts and folders
|
|
48
|
+
- List, search, and manage notes (CRUD)
|
|
49
|
+
- Create, rename, and delete folders
|
|
50
|
+
- List note attachments
|
|
51
|
+
- Tool discovery helpers `search_tools` and `get_tool_info` for context-constrained clients
|
|
52
|
+
- Recent-note resources and organization prompts
|
|
53
|
+
- Health and permission checks: `notes_health`, `notes_permission_guide`, `notes_recheck_permissions`
|
|
54
|
+
|
|
55
|
+
## Install On This Mac
|
|
56
|
+
|
|
57
|
+
<details>
|
|
58
|
+
<summary>Quick start (uvx, from PyPI)</summary>
|
|
59
|
+
|
|
60
|
+
With [uv](https://docs.astral.sh/uv/getting-started/installation/) installed:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
uvx apple-mcp-notes
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
No clone, no venv management.
|
|
67
|
+
|
|
68
|
+
</details>
|
|
69
|
+
|
|
70
|
+
<details>
|
|
71
|
+
<summary>From a clone</summary>
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
git clone https://github.com/JonathanRReed/Apple-MCPs.git
|
|
75
|
+
cd Apple-MCPs
|
|
76
|
+
uv sync --all-packages
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
This builds one workspace environment with every server's entry point in `.venv/bin` (for example `.venv/bin/apple-notes-mcp`). You can also point an MCP client at `AppleNotes-MCP/start.sh`, which prefers `uv run` and falls back to a plain venv bootstrap (Python 3.11+ required).
|
|
80
|
+
|
|
81
|
+
</details>
|
|
82
|
+
|
|
83
|
+
## Install In AI Agents
|
|
84
|
+
|
|
85
|
+
<details>
|
|
86
|
+
<summary>Generic MCP client config</summary>
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"mcpServers": {
|
|
91
|
+
"apple-notes": {
|
|
92
|
+
"command": "uvx",
|
|
93
|
+
"args": ["apple-mcp-notes"],
|
|
94
|
+
"env": {
|
|
95
|
+
"APPLE_NOTES_MCP_SAFETY_MODE": "full_access"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Running from a clone instead? Use `/path/to/Apple-MCPs/AppleNotes-MCP/start.sh` as the command with empty `args`.
|
|
103
|
+
|
|
104
|
+
</details>
|
|
105
|
+
|
|
106
|
+
<details>
|
|
107
|
+
<summary>Claude Code example</summary>
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
claude mcp add --transport stdio --scope project apple-notes -- uvx apple-mcp-notes
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
</details>
|
|
114
|
+
|
|
115
|
+
## Safety Modes
|
|
116
|
+
|
|
117
|
+
- `safe_readonly`
|
|
118
|
+
- `safe_manage`
|
|
119
|
+
- `full_access`
|
|
120
|
+
|
|
121
|
+
## Transport
|
|
122
|
+
|
|
123
|
+
`stdio` is the default and recommended transport. Set `APPLE_NOTES_MCP_TRANSPORT=streamable-http` (with optional `APPLE_NOTES_MCP_HOST` and `APPLE_NOTES_MCP_PORT`) to serve Streamable HTTP instead.
|
|
124
|
+
|
|
125
|
+
## macOS Permissions
|
|
126
|
+
|
|
127
|
+
- Automation access to Notes is required
|
|
128
|
+
|
|
129
|
+
## Launch Checklist
|
|
130
|
+
|
|
131
|
+
- Add `uvx apple-mcp-notes` (or a clone's `AppleNotes-MCP/start.sh`) to your MCP client
|
|
132
|
+
- Reload or reconnect the client so the Notes tool surface is loaded into context
|
|
133
|
+
- Call `notes_health` first
|
|
134
|
+
- If Notes automation is blocked, call `notes_permission_guide`
|
|
135
|
+
- After changing macOS permissions, call `notes_recheck_permissions`
|
|
136
|
+
|
|
137
|
+
## Prompting Notes
|
|
138
|
+
|
|
139
|
+
- `tools/list` returns the full Notes tool surface. Context-constrained clients can use `search_tools` first, then `get_tool_info` for the Notes tool they need.
|
|
140
|
+
- Multiple accounts may each contain a Notes folder. Identify available accounts and folders on first use and set a default.
|
|
141
|
+
- Use Notes for reference material and saved context.
|
|
142
|
+
- Time-sensitive items should go to Reminders or Calendar instead.
|
|
143
|
+
|
|
144
|
+
## Related
|
|
145
|
+
|
|
146
|
+
- [Apple-Tools-MCP](../Apple-Tools-MCP/README.md)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
apple_mcp_notes-1.0.1.dist-info/licenses/LICENSE,sha256=ekujwdbi9SDNikrkciCPMsNSOSdrRCF_-fxh9Z1hUHw,1079
|
|
2
|
+
apple_notes_mcp/__init__.py,sha256=4t_crzhrLum--oyowUMxtjBTzUtWp7oRTF22ewEvJG4,49
|
|
3
|
+
apple_notes_mcp/config.py,sha256=x7U7DwhB8Ueo5ypXZKfyms3nJYRC0qnSu1H4ej8X7Oo,1454
|
|
4
|
+
apple_notes_mcp/models.py,sha256=llZkz7fZ7L9Ec_gajWsgIEfjlP4g-V2TyfO1C94SZ0U,3001
|
|
5
|
+
apple_notes_mcp/notes_bridge.py,sha256=3j7R2PvVFQJuvPnhU9-EDQHX5zV_jAVFJqSrukUI_S8,15860
|
|
6
|
+
apple_notes_mcp/permissions.py,sha256=ZcVjKzaDwzOSFc6J1d9SJZDZ5_SwE3uofoZMC5MMgcM,2671
|
|
7
|
+
apple_notes_mcp/tools.py,sha256=ZF5bzTIkH1KhDD1UMI8R3cQatJ6omwfpV_vcxbcJ9QU,23000
|
|
8
|
+
apple_notes_mcp/applescripts/create_folder.applescript,sha256=Y2fDZzWWTNiqJL5SPyao9DkTDBgUBUw2FbuRfydaJmk,2154
|
|
9
|
+
apple_notes_mcp/applescripts/create_note.applescript,sha256=S703RZPs7X3QoxKFB5klaMOUge6mRVzuOHst297zVzI,4510
|
|
10
|
+
apple_notes_mcp/applescripts/delete_folder.applescript,sha256=Sztrwq6IUGzUKG2KPPtrE-ItxWrTlWE5PeXHvVjGsmo,1438
|
|
11
|
+
apple_notes_mcp/applescripts/delete_note.applescript,sha256=tbHnbtvtvoBVZuv64KdnXtI2mW9mpto_GHM1fbnYtic,1334
|
|
12
|
+
apple_notes_mcp/applescripts/get_note.applescript,sha256=Y_yDotU_8Jc9RcQylgRbm4x_sdttMXXPQA_oEwQ3CMk,4035
|
|
13
|
+
apple_notes_mcp/applescripts/list_accounts.applescript,sha256=pwt7d3MhY7Y_IoqUGdLHHCtVMn7m3XvoRXd_Lhcrkkw,2374
|
|
14
|
+
apple_notes_mcp/applescripts/list_attachments.applescript,sha256=d-voJnSSsWnJSiMPNDouXk9yl08XATV4StuzAS3C_6Q,2077
|
|
15
|
+
apple_notes_mcp/applescripts/list_folders.applescript,sha256=weKjh6UUnNfbBih1xR_xeGKGLm2Xwr0a_WAmqGCEUgg,3360
|
|
16
|
+
apple_notes_mcp/applescripts/list_notes.applescript,sha256=ErZQMjl7D_xZBpGr2IRZF7LisIQK9EdXCs3oHL1r7WU,4051
|
|
17
|
+
apple_notes_mcp/applescripts/move_note.applescript,sha256=Vrx4wf47B7Y1MzK5eJY_x9JpbJqBUsjGBOJ9KtWXpu4,2110
|
|
18
|
+
apple_notes_mcp/applescripts/rename_folder.applescript,sha256=RR4Y9pUsfCUMJQQr0cRQJp0uQUkbEBcBslxoharDvz4,1907
|
|
19
|
+
apple_notes_mcp/applescripts/update_note.applescript,sha256=gpfR16rD3toIvq9EQsq-m0i2s74IP2tyfSmyTeqsBNA,5918
|
|
20
|
+
apple_mcp_notes-1.0.1.dist-info/METADATA,sha256=SKCwo_whqTNtstu99S1mX7OZJcIXwtuyw1alqPh9Vw4,4561
|
|
21
|
+
apple_mcp_notes-1.0.1.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
22
|
+
apple_mcp_notes-1.0.1.dist-info/entry_points.txt,sha256=phdDfjPQTSQ0szSW9a7blQMdMKoi20AVeMgFGKxiK7s,108
|
|
23
|
+
apple_mcp_notes-1.0.1.dist-info/top_level.txt,sha256=I_grA1vZ8IsRbKFRFwcIDLkxbg6yAMEYOZnENWoX9EY,16
|
|
24
|
+
apple_mcp_notes-1.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Apple MCP Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
apple_notes_mcp
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
on run argv
|
|
2
|
+
set folderName to item 1 of argv
|
|
3
|
+
set accountName to item 2 of argv
|
|
4
|
+
set parentFolderId to item 3 of argv
|
|
5
|
+
tell application "Notes"
|
|
6
|
+
set targetAccount to account accountName
|
|
7
|
+
if parentFolderId is not "" then
|
|
8
|
+
set targetFolder to folder id parentFolderId
|
|
9
|
+
set newFolder to make new folder at targetFolder with properties {name:folderName}
|
|
10
|
+
else
|
|
11
|
+
set newFolder to make new folder at targetAccount with properties {name:folderName}
|
|
12
|
+
end if
|
|
13
|
+
return "{" & quote & "folder" & quote & ":" & my folder_json(newFolder, targetAccount) & "}"
|
|
14
|
+
end tell
|
|
15
|
+
end run
|
|
16
|
+
|
|
17
|
+
on folder_json(fld, acc)
|
|
18
|
+
set folderId to my safe_text(id of fld)
|
|
19
|
+
set folderName to my safe_text(name of fld)
|
|
20
|
+
set accountId to my safe_text(id of acc)
|
|
21
|
+
set accountName to my safe_text(name of acc)
|
|
22
|
+
return "{" & quote & "folder_id" & quote & ":" & my json_string(folderId) & "," & quote & "name" & quote & ":" & my json_string(folderName) & "," & quote & "account_id" & quote & ":" & my json_string(accountId) & "," & quote & "account_name" & quote & ":" & my json_string(accountName) & "," & quote & "parent_folder_id" & quote & ":null," & quote & "parent_folder_name" & quote & ":null," & quote & "shared" & quote & ":false}"
|
|
23
|
+
end folder_json
|
|
24
|
+
|
|
25
|
+
on safe_text(valueText)
|
|
26
|
+
try
|
|
27
|
+
return valueText as text
|
|
28
|
+
on error
|
|
29
|
+
return ""
|
|
30
|
+
end try
|
|
31
|
+
end safe_text
|
|
32
|
+
|
|
33
|
+
on json_string(valueText)
|
|
34
|
+
set backslash to ASCII character 92
|
|
35
|
+
set escapedText to my replace_text(valueText as text, backslash, backslash & backslash)
|
|
36
|
+
set escapedText to my replace_text(escapedText, quote, backslash & quote)
|
|
37
|
+
set escapedText to my replace_text(escapedText, return, backslash & "n")
|
|
38
|
+
set escapedText to my replace_text(escapedText, linefeed, backslash & "n")
|
|
39
|
+
return quote & escapedText & quote
|
|
40
|
+
end json_string
|
|
41
|
+
|
|
42
|
+
on replace_text(sourceText, findText, replaceText)
|
|
43
|
+
set originalDelimiters to AppleScript's text item delimiters
|
|
44
|
+
set AppleScript's text item delimiters to findText
|
|
45
|
+
set sourceItems to text items of sourceText
|
|
46
|
+
set AppleScript's text item delimiters to replaceText
|
|
47
|
+
set resultText to sourceItems as text
|
|
48
|
+
set AppleScript's text item delimiters to originalDelimiters
|
|
49
|
+
return resultText
|
|
50
|
+
end replace_text
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
on run argv
|
|
2
|
+
set titleText to item 1 of argv
|
|
3
|
+
set folderId to item 2 of argv
|
|
4
|
+
set bodyHtml to item 3 of argv
|
|
5
|
+
set tagsCsv to item 4 of argv
|
|
6
|
+
tell application "Notes"
|
|
7
|
+
set folderInfo to my find_folder_info(folderId)
|
|
8
|
+
if folderInfo is missing value then error "Can't get folder '" & folderId & "'."
|
|
9
|
+
set targetFolder to item 1 of folderInfo
|
|
10
|
+
set accountId to item 2 of folderInfo
|
|
11
|
+
set accountName to item 3 of folderInfo
|
|
12
|
+
set folderName to item 4 of folderInfo
|
|
13
|
+
set noteBody to my compose_note_body(bodyHtml, tagsCsv)
|
|
14
|
+
set newNote to make new note at targetFolder with properties {name:titleText}
|
|
15
|
+
if noteBody is not "" then
|
|
16
|
+
set body of newNote to noteBody
|
|
17
|
+
end if
|
|
18
|
+
return "{" & quote & "note" & quote & ":" & my note_json(accountId, accountName, folderId, folderName, newNote) & "}"
|
|
19
|
+
end tell
|
|
20
|
+
end run
|
|
21
|
+
|
|
22
|
+
on find_folder_info(targetFolderId)
|
|
23
|
+
tell application "Notes"
|
|
24
|
+
repeat with acc in accounts
|
|
25
|
+
set accountId to my safe_text(id of acc)
|
|
26
|
+
set accountName to my safe_text(name of acc)
|
|
27
|
+
repeat with fld in folders of acc
|
|
28
|
+
if my safe_text(id of fld) is targetFolderId then
|
|
29
|
+
return {fld, accountId, accountName, my safe_text(name of fld)}
|
|
30
|
+
end if
|
|
31
|
+
end repeat
|
|
32
|
+
end repeat
|
|
33
|
+
end tell
|
|
34
|
+
return missing value
|
|
35
|
+
end find_folder_info
|
|
36
|
+
|
|
37
|
+
on compose_note_body(bodyHtml, tagsCsv)
|
|
38
|
+
set noteBody to bodyHtml
|
|
39
|
+
if tagsCsv is not "" then
|
|
40
|
+
set noteBody to noteBody & "<div><br></div><div>" & my tags_html(tagsCsv) & "</div>"
|
|
41
|
+
end if
|
|
42
|
+
return noteBody
|
|
43
|
+
end compose_note_body
|
|
44
|
+
|
|
45
|
+
on tags_html(tagsCsv)
|
|
46
|
+
set AppleScript's text item delimiters to ","
|
|
47
|
+
set tagItems to text items of tagsCsv
|
|
48
|
+
set AppleScript's text item delimiters to ""
|
|
49
|
+
set htmlTags to {}
|
|
50
|
+
repeat with tagItem in tagItems
|
|
51
|
+
set cleanTag to my safe_text(tagItem)
|
|
52
|
+
if cleanTag is not "" then
|
|
53
|
+
set end of htmlTags to "#" & cleanTag
|
|
54
|
+
end if
|
|
55
|
+
end repeat
|
|
56
|
+
return my join_list(htmlTags, " ")
|
|
57
|
+
end tags_html
|
|
58
|
+
|
|
59
|
+
on note_json(accountId, accountName, folderId, folderName, n)
|
|
60
|
+
set noteId to my safe_text(id of n)
|
|
61
|
+
set titleText to ""
|
|
62
|
+
set bodyHtml to ""
|
|
63
|
+
set plainText to ""
|
|
64
|
+
set attachmentCount to 0
|
|
65
|
+
try
|
|
66
|
+
set titleText to my safe_text(name of n)
|
|
67
|
+
end try
|
|
68
|
+
try
|
|
69
|
+
set bodyHtml to my safe_text(body of n)
|
|
70
|
+
end try
|
|
71
|
+
try
|
|
72
|
+
set plainText to my safe_text(plaintext of n)
|
|
73
|
+
end try
|
|
74
|
+
try
|
|
75
|
+
set attachmentCount to count of attachments of n
|
|
76
|
+
end try
|
|
77
|
+
return "{" & ¬
|
|
78
|
+
quote & "note_id" & quote & ":" & my json_string(noteId) & "," & ¬
|
|
79
|
+
quote & "title" & quote & ":" & my json_string(titleText) & "," & ¬
|
|
80
|
+
quote & "account_id" & quote & ":" & my json_string(accountId) & "," & ¬
|
|
81
|
+
quote & "account_name" & quote & ":" & my json_string(accountName) & "," & ¬
|
|
82
|
+
quote & "folder_id" & quote & ":" & my json_string(folderId) & "," & ¬
|
|
83
|
+
quote & "folder_name" & quote & ":" & my json_string(folderName) & "," & ¬
|
|
84
|
+
quote & "created_epoch" & quote & ":0," & ¬
|
|
85
|
+
quote & "modified_epoch" & quote & ":0," & ¬
|
|
86
|
+
quote & "password_protected" & quote & ":false," & ¬
|
|
87
|
+
quote & "shared" & quote & ":false," & ¬
|
|
88
|
+
quote & "attachment_count" & quote & ":" & attachmentCount & "," & ¬
|
|
89
|
+
quote & "plaintext" & quote & ":" & my json_string(plainText) & "," & ¬
|
|
90
|
+
quote & "body_html" & quote & ":" & my json_string(bodyHtml) & "," & ¬
|
|
91
|
+
quote & "attachments" & quote & ":[]}"
|
|
92
|
+
end note_json
|
|
93
|
+
|
|
94
|
+
on safe_text(valueText)
|
|
95
|
+
try
|
|
96
|
+
return valueText as text
|
|
97
|
+
on error
|
|
98
|
+
return ""
|
|
99
|
+
end try
|
|
100
|
+
end safe_text
|
|
101
|
+
|
|
102
|
+
on json_string(valueText)
|
|
103
|
+
set backslash to ASCII character 92
|
|
104
|
+
set escapedText to my replace_text(valueText as text, backslash, backslash & backslash)
|
|
105
|
+
set escapedText to my replace_text(escapedText, quote, backslash & quote)
|
|
106
|
+
set escapedText to my replace_text(escapedText, return, backslash & "n")
|
|
107
|
+
set escapedText to my replace_text(escapedText, linefeed, backslash & "n")
|
|
108
|
+
return quote & escapedText & quote
|
|
109
|
+
end json_string
|
|
110
|
+
|
|
111
|
+
on replace_text(sourceText, findText, replaceText)
|
|
112
|
+
set originalDelimiters to AppleScript's text item delimiters
|
|
113
|
+
set AppleScript's text item delimiters to findText
|
|
114
|
+
set sourceItems to text items of sourceText
|
|
115
|
+
set AppleScript's text item delimiters to replaceText
|
|
116
|
+
set resultText to sourceItems as text
|
|
117
|
+
set AppleScript's text item delimiters to originalDelimiters
|
|
118
|
+
return resultText
|
|
119
|
+
end replace_text
|
|
120
|
+
|
|
121
|
+
on join_list(itemList, delimiterText)
|
|
122
|
+
if (count of itemList) is 0 then
|
|
123
|
+
return ""
|
|
124
|
+
end if
|
|
125
|
+
set originalDelimiters to AppleScript's text item delimiters
|
|
126
|
+
set AppleScript's text item delimiters to delimiterText
|
|
127
|
+
set joinedText to itemList as text
|
|
128
|
+
set AppleScript's text item delimiters to originalDelimiters
|
|
129
|
+
return joinedText
|
|
130
|
+
end join_list
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
on run argv
|
|
2
|
+
set folderId to item 1 of argv
|
|
3
|
+
tell application "Notes"
|
|
4
|
+
repeat with acc in accounts
|
|
5
|
+
repeat with fld in folders of acc
|
|
6
|
+
if my safe_text(id of fld) is folderId then
|
|
7
|
+
delete fld
|
|
8
|
+
return "{" & quote & "deleted" & quote & ":true," & quote & "folder_id" & quote & ":" & my json_string(folderId) & "}"
|
|
9
|
+
end if
|
|
10
|
+
end repeat
|
|
11
|
+
end repeat
|
|
12
|
+
end tell
|
|
13
|
+
return "{" & quote & "deleted" & quote & ":false," & quote & "folder_id" & quote & ":" & my json_string(folderId) & "}"
|
|
14
|
+
end run
|
|
15
|
+
|
|
16
|
+
on safe_text(valueText)
|
|
17
|
+
try
|
|
18
|
+
return valueText as text
|
|
19
|
+
on error
|
|
20
|
+
return ""
|
|
21
|
+
end try
|
|
22
|
+
end safe_text
|
|
23
|
+
|
|
24
|
+
on json_string(valueText)
|
|
25
|
+
set backslash to ASCII character 92
|
|
26
|
+
set escapedText to my replace_text(valueText as text, backslash, backslash & backslash)
|
|
27
|
+
set escapedText to my replace_text(escapedText, quote, backslash & quote)
|
|
28
|
+
set escapedText to my replace_text(escapedText, return, backslash & "n")
|
|
29
|
+
set escapedText to my replace_text(escapedText, linefeed, backslash & "n")
|
|
30
|
+
return quote & escapedText & quote
|
|
31
|
+
end json_string
|
|
32
|
+
|
|
33
|
+
on replace_text(sourceText, findText, replaceText)
|
|
34
|
+
set originalDelimiters to AppleScript's text item delimiters
|
|
35
|
+
set AppleScript's text item delimiters to findText
|
|
36
|
+
set sourceItems to text items of sourceText
|
|
37
|
+
set AppleScript's text item delimiters to replaceText
|
|
38
|
+
set resultText to sourceItems as text
|
|
39
|
+
set AppleScript's text item delimiters to originalDelimiters
|
|
40
|
+
return resultText
|
|
41
|
+
end replace_text
|
|
42
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
on run argv
|
|
2
|
+
set targetNoteId to item 1 of argv
|
|
3
|
+
tell application "Notes"
|
|
4
|
+
try
|
|
5
|
+
delete note id targetNoteId
|
|
6
|
+
return "{" & quote & "deleted" & quote & ":true," & quote & "note_id" & quote & ":" & my json_string(targetNoteId) & "}"
|
|
7
|
+
on error
|
|
8
|
+
return "{" & quote & "deleted" & quote & ":false," & quote & "note_id" & quote & ":" & my json_string(targetNoteId) & "}"
|
|
9
|
+
end try
|
|
10
|
+
end tell
|
|
11
|
+
end run
|
|
12
|
+
|
|
13
|
+
on safe_text(valueText)
|
|
14
|
+
try
|
|
15
|
+
return valueText as text
|
|
16
|
+
on error
|
|
17
|
+
return ""
|
|
18
|
+
end try
|
|
19
|
+
end safe_text
|
|
20
|
+
|
|
21
|
+
on json_string(valueText)
|
|
22
|
+
set backslash to ASCII character 92
|
|
23
|
+
set escapedText to my replace_text(valueText as text, backslash, backslash & backslash)
|
|
24
|
+
set escapedText to my replace_text(escapedText, quote, backslash & quote)
|
|
25
|
+
set escapedText to my replace_text(escapedText, return, backslash & "n")
|
|
26
|
+
set escapedText to my replace_text(escapedText, linefeed, backslash & "n")
|
|
27
|
+
return quote & escapedText & quote
|
|
28
|
+
end json_string
|
|
29
|
+
|
|
30
|
+
on replace_text(sourceText, findText, replaceText)
|
|
31
|
+
set originalDelimiters to AppleScript's text item delimiters
|
|
32
|
+
set AppleScript's text item delimiters to findText
|
|
33
|
+
set sourceItems to text items of sourceText
|
|
34
|
+
set AppleScript's text item delimiters to replaceText
|
|
35
|
+
set resultText to sourceItems as text
|
|
36
|
+
set AppleScript's text item delimiters to originalDelimiters
|
|
37
|
+
return resultText
|
|
38
|
+
end replace_text
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
on run argv
|
|
2
|
+
set targetNoteId to item 1 of argv
|
|
3
|
+
set foundJson to ""
|
|
4
|
+
tell application "Notes"
|
|
5
|
+
repeat with acc in accounts
|
|
6
|
+
set accName to my safe_text(name of acc)
|
|
7
|
+
set accId to my safe_text(id of acc)
|
|
8
|
+
repeat with fld in folders of acc
|
|
9
|
+
set fldId to my safe_text(id of fld)
|
|
10
|
+
set fldName to my safe_text(name of fld)
|
|
11
|
+
repeat with n in notes of fld
|
|
12
|
+
if my safe_text(id of n) is targetNoteId then
|
|
13
|
+
set foundJson to "{" & quote & "found" & quote & ":true," & quote & "note" & quote & ":" & my note_json(accId, accName, fldId, fldName, n, true) & "}"
|
|
14
|
+
exit repeat
|
|
15
|
+
end if
|
|
16
|
+
end repeat
|
|
17
|
+
if foundJson is not "" then exit repeat
|
|
18
|
+
end repeat
|
|
19
|
+
if foundJson is not "" then exit repeat
|
|
20
|
+
end repeat
|
|
21
|
+
end tell
|
|
22
|
+
if foundJson is "" then
|
|
23
|
+
return "{" & quote & "found" & quote & ":false}"
|
|
24
|
+
end if
|
|
25
|
+
return foundJson
|
|
26
|
+
end run
|
|
27
|
+
|
|
28
|
+
on note_json(accountId, accountName, folderId, folderName, n, includeBody)
|
|
29
|
+
set noteId to my safe_text(id of n)
|
|
30
|
+
set titleText to ""
|
|
31
|
+
set bodyHtml to ""
|
|
32
|
+
set plainText to ""
|
|
33
|
+
set createdEpoch to 0
|
|
34
|
+
set modifiedEpoch to 0
|
|
35
|
+
set sharedValue to false
|
|
36
|
+
try
|
|
37
|
+
set titleText to my safe_text(name of n)
|
|
38
|
+
end try
|
|
39
|
+
try
|
|
40
|
+
set bodyHtml to my safe_text(body of n)
|
|
41
|
+
end try
|
|
42
|
+
try
|
|
43
|
+
set plainText to my safe_text(plaintext of n)
|
|
44
|
+
end try
|
|
45
|
+
try
|
|
46
|
+
set createdEpoch to my date_to_epoch(creation date of n)
|
|
47
|
+
end try
|
|
48
|
+
try
|
|
49
|
+
set modifiedEpoch to my date_to_epoch(modification date of n)
|
|
50
|
+
end try
|
|
51
|
+
try
|
|
52
|
+
set sharedValue to shared of n
|
|
53
|
+
end try
|
|
54
|
+
return "{" & ¬
|
|
55
|
+
quote & "note_id" & quote & ":" & my json_string(noteId) & "," & ¬
|
|
56
|
+
quote & "title" & quote & ":" & my json_string(titleText) & "," & ¬
|
|
57
|
+
quote & "account_id" & quote & ":" & my json_string(accountId) & "," & ¬
|
|
58
|
+
quote & "account_name" & quote & ":" & my json_string(accountName) & "," & ¬
|
|
59
|
+
quote & "folder_id" & quote & ":" & my json_string(folderId) & "," & ¬
|
|
60
|
+
quote & "folder_name" & quote & ":" & my json_string(folderName) & "," & ¬
|
|
61
|
+
quote & "created_epoch" & quote & ":" & createdEpoch & "," & ¬
|
|
62
|
+
quote & "modified_epoch" & quote & ":" & modifiedEpoch & "," & ¬
|
|
63
|
+
quote & "shared" & quote & ":" & my json_boolean(sharedValue) & "," & ¬
|
|
64
|
+
quote & "attachment_count" & quote & ":0," & ¬
|
|
65
|
+
quote & "plaintext" & quote & ":" & my json_string(plainText) & "," & ¬
|
|
66
|
+
quote & "body_html" & quote & ":" & my json_string(bodyHtml) & "," & ¬
|
|
67
|
+
quote & "attachments" & quote & ":[]}"
|
|
68
|
+
end note_json
|
|
69
|
+
|
|
70
|
+
on date_to_epoch(dateValue)
|
|
71
|
+
set epochDate to current date
|
|
72
|
+
set year of epochDate to 1970
|
|
73
|
+
set month of epochDate to January
|
|
74
|
+
set day of epochDate to 1
|
|
75
|
+
set time of epochDate to 0
|
|
76
|
+
return (dateValue - epochDate) as integer
|
|
77
|
+
end date_to_epoch
|
|
78
|
+
|
|
79
|
+
on safe_text(valueText)
|
|
80
|
+
try
|
|
81
|
+
return valueText as text
|
|
82
|
+
on error
|
|
83
|
+
return ""
|
|
84
|
+
end try
|
|
85
|
+
end safe_text
|
|
86
|
+
|
|
87
|
+
on json_boolean(boolValue)
|
|
88
|
+
if boolValue then
|
|
89
|
+
return "true"
|
|
90
|
+
end if
|
|
91
|
+
return "false"
|
|
92
|
+
end json_boolean
|
|
93
|
+
|
|
94
|
+
on json_string(valueText)
|
|
95
|
+
set backslash to ASCII character 92
|
|
96
|
+
set escapedText to my replace_text(valueText as text, backslash, backslash & backslash)
|
|
97
|
+
set escapedText to my replace_text(escapedText, quote, backslash & quote)
|
|
98
|
+
set escapedText to my replace_text(escapedText, return, backslash & "n")
|
|
99
|
+
set escapedText to my replace_text(escapedText, linefeed, backslash & "n")
|
|
100
|
+
return quote & escapedText & quote
|
|
101
|
+
end json_string
|
|
102
|
+
|
|
103
|
+
on replace_text(sourceText, findText, replaceText)
|
|
104
|
+
set originalDelimiters to AppleScript's text item delimiters
|
|
105
|
+
set AppleScript's text item delimiters to findText
|
|
106
|
+
set sourceItems to text items of sourceText
|
|
107
|
+
set AppleScript's text item delimiters to replaceText
|
|
108
|
+
set resultText to sourceItems as text
|
|
109
|
+
set AppleScript's text item delimiters to originalDelimiters
|
|
110
|
+
return resultText
|
|
111
|
+
end replace_text
|
|
112
|
+
|
|
113
|
+
on join_list(itemList, delimiterText)
|
|
114
|
+
if (count of itemList) is 0 then
|
|
115
|
+
return ""
|
|
116
|
+
end if
|
|
117
|
+
set originalDelimiters to AppleScript's text item delimiters
|
|
118
|
+
set AppleScript's text item delimiters to delimiterText
|
|
119
|
+
set joinedText to itemList as text
|
|
120
|
+
set AppleScript's text item delimiters to originalDelimiters
|
|
121
|
+
return joinedText
|
|
122
|
+
end join_list
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
on run argv
|
|
2
|
+
set jsonItems to {}
|
|
3
|
+
tell application "Notes"
|
|
4
|
+
repeat with acc in accounts
|
|
5
|
+
set accName to my safe_text(name of acc)
|
|
6
|
+
set accId to my safe_text(id of acc)
|
|
7
|
+
set upgradedValue to false
|
|
8
|
+
try
|
|
9
|
+
set upgradedValue to upgraded of acc
|
|
10
|
+
end try
|
|
11
|
+
set defaultFolderId to ""
|
|
12
|
+
try
|
|
13
|
+
set defaultFolderId to my safe_text(id of default folder of acc)
|
|
14
|
+
end try
|
|
15
|
+
set end of jsonItems to "{" & ¬
|
|
16
|
+
quote & "account_id" & quote & ":" & my json_string(accId) & "," & ¬
|
|
17
|
+
quote & "name" & quote & ":" & my json_string(accName) & "," & ¬
|
|
18
|
+
quote & "upgraded" & quote & ":" & my json_boolean(upgradedValue) & "," & ¬
|
|
19
|
+
quote & "default_folder_id" & quote & ":" & my nullable_json_string(defaultFolderId) & "}"
|
|
20
|
+
end repeat
|
|
21
|
+
end tell
|
|
22
|
+
return "{" & quote & "items" & quote & ":[" & my join_list(jsonItems, ",") & "]}"
|
|
23
|
+
end run
|
|
24
|
+
|
|
25
|
+
on safe_text(valueText)
|
|
26
|
+
try
|
|
27
|
+
return valueText as text
|
|
28
|
+
on error
|
|
29
|
+
return ""
|
|
30
|
+
end try
|
|
31
|
+
end safe_text
|
|
32
|
+
|
|
33
|
+
on nullable_json_string(valueText)
|
|
34
|
+
if valueText is "" then
|
|
35
|
+
return "null"
|
|
36
|
+
end if
|
|
37
|
+
return my json_string(valueText)
|
|
38
|
+
end nullable_json_string
|
|
39
|
+
|
|
40
|
+
on json_boolean(boolValue)
|
|
41
|
+
if boolValue then
|
|
42
|
+
return "true"
|
|
43
|
+
end if
|
|
44
|
+
return "false"
|
|
45
|
+
end json_boolean
|
|
46
|
+
|
|
47
|
+
on json_string(valueText)
|
|
48
|
+
set backslash to ASCII character 92
|
|
49
|
+
set escapedText to my replace_text(valueText as text, backslash, backslash & backslash)
|
|
50
|
+
set escapedText to my replace_text(escapedText, quote, backslash & quote)
|
|
51
|
+
set escapedText to my replace_text(escapedText, return, backslash & "n")
|
|
52
|
+
set escapedText to my replace_text(escapedText, linefeed, backslash & "n")
|
|
53
|
+
return quote & escapedText & quote
|
|
54
|
+
end json_string
|
|
55
|
+
|
|
56
|
+
on replace_text(sourceText, findText, replaceText)
|
|
57
|
+
set originalDelimiters to AppleScript's text item delimiters
|
|
58
|
+
set AppleScript's text item delimiters to findText
|
|
59
|
+
set sourceItems to text items of sourceText
|
|
60
|
+
set AppleScript's text item delimiters to replaceText
|
|
61
|
+
set resultText to sourceItems as text
|
|
62
|
+
set AppleScript's text item delimiters to originalDelimiters
|
|
63
|
+
return resultText
|
|
64
|
+
end replace_text
|
|
65
|
+
|
|
66
|
+
on join_list(itemList, delimiterText)
|
|
67
|
+
if (count of itemList) is 0 then
|
|
68
|
+
return ""
|
|
69
|
+
end if
|
|
70
|
+
set originalDelimiters to AppleScript's text item delimiters
|
|
71
|
+
set AppleScript's text item delimiters to delimiterText
|
|
72
|
+
set joinedText to itemList as text
|
|
73
|
+
set AppleScript's text item delimiters to originalDelimiters
|
|
74
|
+
return joinedText
|
|
75
|
+
end join_list
|