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.
@@ -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,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (83.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ apple-mcp-notes = apple_notes_mcp.tools:main
3
+ apple-notes-mcp = apple_notes_mcp.tools:main
@@ -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,3 @@
1
+ __all__ = ["__version__"]
2
+
3
+ __version__ = "0.1.0"
@@ -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