@thenavidm/apple-photos-mcp-cli 1.1.1 → 2.0.0

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 (49) hide show
  1. package/README.md +56 -25
  2. package/SKILL.md +8 -10
  3. package/lib/app.d.ts +14 -0
  4. package/lib/app.js +138 -0
  5. package/lib/app.js.map +1 -0
  6. package/lib/bridge.d.ts +6 -6
  7. package/lib/bridge.js +40 -46
  8. package/lib/bridge.js.map +1 -1
  9. package/lib/config.d.ts +6 -4
  10. package/lib/config.js +19 -25
  11. package/lib/config.js.map +1 -1
  12. package/lib/engine.d.ts +44 -0
  13. package/lib/engine.js +127 -0
  14. package/lib/engine.js.map +1 -0
  15. package/lib/errors.d.ts +0 -3
  16. package/lib/errors.js +0 -3
  17. package/lib/errors.js.map +1 -1
  18. package/lib/index.d.ts +10 -6
  19. package/lib/index.js +17 -104
  20. package/lib/index.js.map +1 -1
  21. package/lib/tools/index.d.ts +20 -11
  22. package/lib/tools/index.js +6 -2
  23. package/lib/tools/index.js.map +1 -1
  24. package/lib/tools/kit.d.ts +26 -24
  25. package/lib/tools/kit.js +68 -44
  26. package/lib/tools/kit.js.map +1 -1
  27. package/lib/version.d.ts +2 -0
  28. package/lib/version.js +4 -0
  29. package/lib/version.js.map +1 -0
  30. package/package.json +3 -4
  31. package/lib/cli.d.ts +0 -105
  32. package/lib/cli.js +0 -644
  33. package/lib/cli.js.map +0 -1
  34. package/lib/safety.d.ts +0 -27
  35. package/lib/safety.js +0 -70
  36. package/lib/safety.js.map +0 -1
  37. package/lib/server.d.ts +0 -10
  38. package/lib/server.js +0 -38
  39. package/lib/server.js.map +0 -1
  40. package/src/apple_photos_mcp/__pycache__/__init__.cpython-313.pyc +0 -0
  41. package/src/apple_photos_mcp/__pycache__/__main__.cpython-313.pyc +0 -0
  42. package/src/apple_photos_mcp/__pycache__/config.cpython-313.pyc +0 -0
  43. package/src/apple_photos_mcp/__pycache__/doctor.cpython-313.pyc +0 -0
  44. package/src/apple_photos_mcp/__pycache__/library.cpython-313.pyc +0 -0
  45. package/src/apple_photos_mcp/__pycache__/previews.cpython-313.pyc +0 -0
  46. package/src/apple_photos_mcp/__pycache__/safety.cpython-313.pyc +0 -0
  47. package/src/apple_photos_mcp/__pycache__/search.cpython-313.pyc +0 -0
  48. package/src/apple_photos_mcp/__pycache__/server.cpython-313.pyc +0 -0
  49. package/src/apple_photos_mcp/__pycache__/writes.cpython-313.pyc +0 -0
@@ -1,30 +1,25 @@
1
- /** Shared plumbing every tool uses. One spec feeds both the MCP server and the CLI. */
2
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
- import { z, type ZodRawShape } from "zod";
1
+ /**
2
+ * Shared plumbing every tool uses. One spec feeds the MCP server and the CLI,
3
+ * through Slipway, and the HQ connector, which imports `ALL_TOOLS` for its
4
+ * schemas and so keeps this shape: name, title, description, a Zod shape with
5
+ * `confirm` on the one destructive tool, and a risk.
6
+ */
7
+ import { z, type Risk, type Tool } from "@thenavidm/slipway";
4
8
  import type { PythonBridge } from "../bridge.js";
5
9
  import type { Config } from "../config.js";
6
- import { needsConfirm, type Risk, type WriteGuard } from "../safety.js";
10
+ export type { Risk };
11
+ /** What every tool runs with: the engine and the settings it was started with. */
7
12
  export type ToolContext = {
8
13
  bridge: PythonBridge;
9
14
  config: Config;
10
- guard: WriteGuard;
11
- };
12
- export type ToolResult = {
13
- content: {
14
- type: string;
15
- text?: string;
16
- data?: string;
17
- mimeType?: string;
18
- }[];
19
- isError?: boolean;
20
15
  };
21
- export declare function ok(data: unknown): ToolResult;
22
- export declare function fail(error: unknown): ToolResult;
23
16
  export declare const confirmArg: {
24
17
  confirm: z.ZodOptional<z.ZodBoolean>;
25
18
  };
26
- export type ToolSpec<S extends ZodRawShape> = {
19
+ export type ToolSpec<S extends z.ZodRawShape> = {
27
20
  name: string;
21
+ /** The CLI command, when the name with dashes is one the CLI already uses. */
22
+ command?: string;
28
23
  title: string;
29
24
  description: string;
30
25
  schema: S;
@@ -36,17 +31,24 @@ export type ToolSpec<S extends ZodRawShape> = {
36
31
  python?: string;
37
32
  summary?: (args: z.infer<z.ZodObject<S>>) => string;
38
33
  };
39
- export declare function defineTool<S extends ZodRawShape>(spec: ToolSpec<S>): ToolSpec<S>;
40
- export type AnyToolSpec = Omit<ToolSpec<ZodRawShape>, "summary"> & {
34
+ export declare function defineTool<S extends z.ZodRawShape>(spec: ToolSpec<S>): ToolSpec<S>;
35
+ export type AnyToolSpec = Omit<ToolSpec<z.ZodRawShape>, "summary"> & {
41
36
  summary?: (args: never) => string;
42
37
  };
43
38
  /**
44
- * Every tool runs the same way: guard, then hand the arguments to Python.
39
+ * A failure as the Slipway error that carries its exit code, as 1.1 gave them:
40
+ * an engine that never started is setup, 10; macOS refusing access is 4; a
41
+ * request over a cap is 2; refs that resolve to nothing are 3; anything else
42
+ * the engine reports is 5.
43
+ */
44
+ export declare function toSlipway(error: unknown): unknown;
45
+ /**
46
+ * Every tool runs the same way: hand the arguments to Python.
45
47
  *
46
48
  * There is no per-tool handler, because there is nothing per-tool to do. The
47
- * Python server already implements all thirteen; this layer exists to give them
48
- * a second surface, an install story and a schema HQ can import.
49
+ * Python server already implements all thirteen; this layer gives them a
50
+ * second surface, an install story and a schema HQ can import.
49
51
  */
50
52
  export declare function runTool(spec: AnyToolSpec, args: Record<string, unknown>, ctx: ToolContext): Promise<unknown>;
51
- export declare function register(server: McpServer, contextFor: () => ToolContext, spec: AnyToolSpec): void;
52
- export { needsConfirm };
53
+ /** The specs as Slipway tools. `confirm` is Slipway's own, so it leaves each schema here. */
54
+ export declare function slipwayTools(specs: readonly AnyToolSpec[]): Tool<ToolContext>[];
package/lib/tools/kit.js CHANGED
@@ -1,22 +1,11 @@
1
- /** Shared plumbing every tool uses. One spec feeds both the MCP server and the CLI. */
2
- import { z } from "zod";
3
- import { PhotosError, ToolError } from "../errors.js";
4
- import { annotationsFor, needsConfirm } from "../safety.js";
5
- export function ok(data) {
6
- // Content parts that are already MCP-shaped pass straight through. That is
7
- // what keeps look_at_photos working: it returns images interleaved with
8
- // captions, and serialising them into a text blob deletes the pictures,
9
- // leaving a tool whose whole purpose is seeing that returns filenames.
10
- if (Array.isArray(data) && data.every((p) => p && typeof p === "object" && "type" in p)) {
11
- return { content: data };
12
- }
13
- const text = typeof data === "string" ? data : JSON.stringify(data, null, 2);
14
- return { content: [{ type: "text", text }] };
15
- }
16
- export function fail(error) {
17
- const payload = error instanceof PhotosError ? error.toJSON() : { error: error?.message ?? String(error) };
18
- return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }], isError: true };
19
- }
1
+ /**
2
+ * Shared plumbing every tool uses. One spec feeds the MCP server and the CLI,
3
+ * through Slipway, and the HQ connector, which imports `ALL_TOOLS` for its
4
+ * schemas and so keeps this shape: name, title, description, a Zod shape with
5
+ * `confirm` on the one destructive tool, and a risk.
6
+ */
7
+ import { ApiError, AuthError, NotConfiguredError, NotFoundError, SlipwayError, UsageError, content, toolkit, z } from "@thenavidm/slipway";
8
+ import { BridgeError, PhotosError, ToolError } from "../errors.js";
20
9
  export const confirmArg = {
21
10
  confirm: z
22
11
  .boolean()
@@ -27,29 +16,51 @@ export function defineTool(spec) {
27
16
  return spec;
28
17
  }
29
18
  /**
30
- * Every tool runs the same way: guard, then hand the arguments to Python.
19
+ * A failure as the Slipway error that carries its exit code, as 1.1 gave them:
20
+ * an engine that never started is setup, 10; macOS refusing access is 4; a
21
+ * request over a cap is 2; refs that resolve to nothing are 3; anything else
22
+ * the engine reports is 5.
23
+ */
24
+ export function toSlipway(error) {
25
+ if (error instanceof SlipwayError || !(error instanceof Error))
26
+ return error;
27
+ const text = error.message.toLowerCase();
28
+ const options = error instanceof PhotosError && error.detail ? { details: { detail: error.detail.slice(0, 500) } } : {};
29
+ if (error instanceof BridgeError && /could not start/.test(text))
30
+ return new NotConfiguredError(error.message, options);
31
+ if (/operation not permitted|permission denied|not authori[sz]ed|full disk access|-1743/.test(text))
32
+ return new AuthError(error.message, options);
33
+ if (/at most|too many/.test(text))
34
+ return new UsageError(error.message, options);
35
+ if (/not found|none of those refs resolved|no such/.test(text))
36
+ return new NotFoundError(error.message, options);
37
+ return new ApiError(error.message, options);
38
+ }
39
+ /**
40
+ * Every tool runs the same way: hand the arguments to Python.
31
41
  *
32
42
  * There is no per-tool handler, because there is nothing per-tool to do. The
33
- * Python server already implements all thirteen; this layer exists to give them
34
- * a second surface, an install story and a schema HQ can import.
43
+ * Python server already implements all thirteen; this layer gives them a
44
+ * second surface, an install story and a schema HQ can import.
35
45
  */
36
46
  export async function runTool(spec, args, ctx) {
37
- if (spec.risk !== "read") {
38
- const summary = spec.summary?.(args) ?? spec.name;
39
- ctx.guard.check(spec.name, spec.risk, args.confirm, summary);
40
- }
41
47
  // Only undefined and null are dropped. An empty string is a real value here:
42
48
  // clearing a caption is `set_photo_description --description ""`, and
43
49
  // stripping it made the engine reject the call for a missing required
44
50
  // argument the caller had plainly supplied.
45
51
  const payload = Object.fromEntries(Object.entries(args).filter(([, v]) => v !== undefined && v !== null));
52
+ // Slipway lets an irreversible call reach this point only once it is
53
+ // confirmed, and the engine asks for the same confirmation in its own words.
54
+ if (spec.risk === "destructive")
55
+ payload.confirm = true;
46
56
  const result = await ctx.bridge.call(spec.python ?? spec.name, payload, spec.timeoutMs);
47
57
  if (result.isError)
48
58
  throw new ToolError(result.text || `${spec.name} failed.`);
49
- // Anything that is not plain text — images from look_at_photos — is returned
50
- // as content parts rather than squashed into a string.
59
+ // Anything that is not plain text, the images from look_at_photos, goes back
60
+ // as content parts, and the CLI prints the same parts as JSON, which is what
61
+ // the HQ agent hands back to the hosted connector.
51
62
  if (result.content.some((part) => part.type !== "text"))
52
- return result.content;
63
+ return content(result.content, result.content);
53
64
  let parsed;
54
65
  try {
55
66
  parsed = JSON.parse(result.text);
@@ -68,20 +79,33 @@ export async function runTool(spec, args, ctx) {
68
79
  }
69
80
  return parsed;
70
81
  }
71
- export function register(server, contextFor, spec) {
72
- server.registerTool(spec.name, {
73
- title: spec.title,
74
- description: spec.description,
75
- inputSchema: spec.schema,
76
- annotations: { title: spec.title, ...annotationsFor(spec.risk, { idempotent: spec.idempotent }) },
77
- }, (async (args) => {
78
- try {
79
- return ok(await runTool(spec, args, contextFor()));
80
- }
81
- catch (error) {
82
- return fail(error);
83
- }
84
- }));
82
+ const kit = toolkit();
83
+ /** The specs as Slipway tools. `confirm` is Slipway's own, so it leaves each schema here. */
84
+ export function slipwayTools(specs) {
85
+ return specs.map((spec) => {
86
+ const { confirm: _confirm, ...shape } = spec.schema;
87
+ return kit.defineTool({
88
+ name: spec.name,
89
+ ...(spec.command ? { command: spec.command } : {}),
90
+ title: spec.title,
91
+ description: spec.description,
92
+ input: z.object(shape),
93
+ risk: spec.risk,
94
+ ...(spec.idempotent === undefined ? {} : { idempotent: spec.idempotent }),
95
+ // Everything stays on this Mac: no network, no upload, nothing leaves.
96
+ openWorld: false,
97
+ // 1.1's words for the one destructive tool's refusal.
98
+ ...(spec.risk === "destructive" ? { consequence: "cannot be undone from here" } : {}),
99
+ ...(spec.summary ? { summary: spec.summary } : {}),
100
+ handler: async (args, ctx) => {
101
+ try {
102
+ return await runTool(spec, args, ctx);
103
+ }
104
+ catch (error) {
105
+ throw toSlipway(error);
106
+ }
107
+ },
108
+ });
109
+ });
85
110
  }
86
- export { needsConfirm };
87
111
  //# sourceMappingURL=kit.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"kit.js","sourceRoot":"","sources":["../../src-ts/tools/kit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"kit.js","sourceRoot":"","sources":["../../src-ts/tools/kit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ /** The package's own version, read once, so the engine handshake and the server agree with npm. */
2
+ export declare const VERSION: string;
package/lib/version.js ADDED
@@ -0,0 +1,4 @@
1
+ import { createRequire } from "node:module";
2
+ /** The package's own version, read once, so the engine handshake and the server agree with npm. */
3
+ export const VERSION = createRequire(import.meta.url)("../package.json").version;
4
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src-ts/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,mGAAmG;AACnG,MAAM,CAAC,MAAM,OAAO,GAAY,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAyB,CAAC,OAAO,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thenavidm/apple-photos-mcp-cli",
3
- "version": "1.1.1",
3
+ "version": "2.0.0",
4
4
  "description": "Apple Photos MCP server and CLI for Claude Code and AI agents. 13 tools to search, look at, organise and export your own Photos library, entirely on your Mac.",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -10,7 +10,7 @@
10
10
  "apple-photos-mcp-cli": "lib/npx.js"
11
11
  },
12
12
  "engines": {
13
- "node": ">=20"
13
+ "node": ">=22"
14
14
  },
15
15
  "scripts": {
16
16
  "build": "tsc",
@@ -20,8 +20,7 @@
20
20
  "build:mcpb": "bash desktop-extension/build.sh"
21
21
  },
22
22
  "dependencies": {
23
- "@modelcontextprotocol/sdk": "^1.18.0",
24
- "zod": "^3.23.8"
23
+ "@thenavidm/slipway": "^0.1.24"
25
24
  },
26
25
  "devDependencies": {
27
26
  "@types/node": "^22.10.0",
package/lib/cli.d.ts DELETED
@@ -1,105 +0,0 @@
1
- /**
2
- * The CLI adapter.
3
- *
4
- * `register()` in tools/kit.ts turns a `ToolSpec` into an MCP tool. This turns
5
- * the same spec into a shell command, from the same `ALL_TOOLS` array, through
6
- * the same handler and the same `WriteGuard`. Nothing is described twice, so a
7
- * tool added tomorrow is a command tomorrow and the two surfaces cannot drift.
8
- *
9
- * The command IS the tool name. `submit_imagine` runs as `submit-imagine`, and
10
- * the underscore form works too. Inventing a prettier command tree would mean a
11
- * hand-written mapping, which is exactly the drift this avoids, and it would
12
- * force anyone reading the SKILL.md to learn two vocabularies for one action.
13
- *
14
- * Zod is the only schema: every flag, its placeholder, its help text and its
15
- * validation come from the shape the MCP tool already declares.
16
- */
17
- import { type ZodRawShape } from "zod";
18
- import { type AnyToolSpec } from "./tools/kit.js";
19
- /** How a value reaches the parser, once the Zod wrappers are peeled off. */
20
- type FlagKind = "string" | "number" | "boolean" | "enum" | "json";
21
- type Flag = {
22
- key: string;
23
- flag: string;
24
- kind: FlagKind;
25
- required: boolean;
26
- repeatable: boolean;
27
- choices?: string[];
28
- help: string;
29
- };
30
- export declare function flagsFor(shape: ZodRawShape): Flag[];
31
- /** Shorthands for this repo's arguments. Kept small on purpose. */
32
- export declare const FLAG_ALIASES: Record<string, string>;
33
- /**
34
- * Parse argv against a tool's flags.
35
- *
36
- * Zod does the real validation afterwards, so this only has to get values into
37
- * the right JavaScript types and catch the mistakes Zod would otherwise report
38
- * in terms of a schema the person at the terminal never sees.
39
- */
40
- export declare function parseArgs(argv: string[], flags: Flag[]): Record<string, unknown>;
41
- /**
42
- * Keep only the named fields.
43
- *
44
- * These endpoints are verbose: one explore page is tens of kilobytes of JSON,
45
- * most of it layout hints. An agent piping that into its own context pays for
46
- * every byte. `--select id,status,images` cuts it to what was asked for.
47
- * Dotted paths descend, and arrays are traversed element-wise.
48
- */
49
- export declare function selectFields(data: unknown, paths: string[]): unknown;
50
- /** Errors are JSON on stderr, always, so a caller parses one shape. */
51
- export declare function emitError(error: unknown): void;
52
- /**
53
- * The name this was invoked as, so examples are copy-pasteable.
54
- *
55
- * The package puts two binaries on the same file. Printing `apple-photos-mcp` at
56
- * someone who typed `apple-photos-cli` hands them a command that works but is not
57
- * the one in their fingers.
58
- */
59
- export declare function binName(): string;
60
- /**
61
- * Words people use mapped onto words these tools use.
62
- *
63
- * Without this, "save my photos to disk" matches four browsing tools and never
64
- * export-originals, because nothing in that phrase appears in its name or
65
- * description. Every target below has to be vocabulary a tool actually
66
- * contains; a test asserts it.
67
- */
68
- export declare const SYNONYMS: Record<string, string[]>;
69
- /**
70
- * Resolve a capability described in words to the command that does it.
71
- *
72
- * An agent that knows what it wants but not what this tool calls it would
73
- * otherwise read the full tool list to find out, which is the cost the CLI
74
- * exists to avoid. Scoring is deliberately dumb: name and title carry the most
75
- * weight, because a tool called `download_job` should win "download my images"
76
- * without any cleverness.
77
- */
78
- export declare function whichCommand(query: string): {
79
- tool: AnyToolSpec;
80
- score: number;
81
- }[];
82
- /**
83
- * The house contract, so a script branches on the number the same way for
84
- * every one of these CLIs: 0 ok, 2 usage or a refused write, 3 not found,
85
- * 4 access refused, 5 the engine failed, 10 nothing set up yet.
86
- */
87
- export declare const EXIT: {
88
- readonly ok: 0;
89
- readonly usage: 2;
90
- readonly notFound: 3;
91
- readonly auth: 4;
92
- readonly api: 5;
93
- readonly rateLimited: 7;
94
- readonly config: 10;
95
- };
96
- /**
97
- * There is no account here, so "auth" is macOS refusing access: Full Disk
98
- * Access for the library, or Automation for Photos itself. The engine reports
99
- * its failures as text, so its words decide once the class has said what it
100
- * can.
101
- */
102
- export declare function exitCodeFor(error: unknown): number;
103
- export declare function isCliCommand(argv: string[]): boolean;
104
- export declare function runCli(argv: string[]): Promise<number>;
105
- export {};