@chrischall/pickuppatrol-mcp 0.2.0 → 1.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.
@@ -1,4 +1,4 @@
1
- import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
1
+ import type { CallToolResult } from '@modelcontextprotocol/server';
2
2
  import { schemaConfirm } from '@chrischall/mcp-utils';
3
3
  export { schemaConfirm };
4
4
  /**
@@ -1,4 +1,4 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
1
+ import type { McpServer } from '@modelcontextprotocol/server';
2
2
  import type { PickUpPatrolClient } from '../client.js';
3
3
  import type { DefaultPlan, Student } from '../types.js';
4
4
  /** Project a student down to the fields a parent actually asks about. */
@@ -63,13 +63,13 @@ export function registerAccountTools(server, client) {
63
63
  server.registerTool('pup_get_student', {
64
64
  description: 'One student in full, including the default dismissal plan for each weekday. Pass raw: true for the untouched API record.',
65
65
  annotations: { readOnlyHint: true },
66
- inputSchema: {
66
+ inputSchema: z.object({
67
67
  student_id: z.number().int().describe('Student id, from pup_list_students'),
68
68
  raw: z
69
69
  .boolean()
70
70
  .optional()
71
71
  .describe('Return the unprojected API record instead of the summary'),
72
- },
72
+ }),
73
73
  }, async ({ student_id, raw }) => {
74
74
  const student = await client.getStudent(student_id);
75
75
  return minifiedResult(raw === true ? student : summarizeStudent(student));
@@ -1,4 +1,4 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
1
+ import type { McpServer } from '@modelcontextprotocol/server';
2
2
  import type { PickUpPatrolClient } from '../client.js';
3
3
  /**
4
4
  * Accept weekdays as names ("Monday") or ids (1 = Sunday … 7 = Saturday).
@@ -34,9 +34,9 @@ export function registerDefaultPlanTools(server, client) {
34
34
  server.registerTool('pup_get_default_plans', {
35
35
  description: "A student's weekly default dismissal plan — how they normally leave school on each day of the week — and whether the defaults still need a parent review.",
36
36
  annotations: { readOnlyHint: true },
37
- inputSchema: {
37
+ inputSchema: z.object({
38
38
  student_id: z.number().int().describe('Student id, from pup_list_students'),
39
- },
39
+ }),
40
40
  }, async ({ student_id }) => {
41
41
  const [student, review] = await Promise.all([
42
42
  client.getStudent(student_id),
@@ -55,7 +55,7 @@ export function registerDefaultPlanTools(server, client) {
55
55
  });
56
56
  server.registerTool('pup_set_default_plans', {
57
57
  description: "Change a student's weekly default dismissal plan for one or more weekdays, or clear every default. This is how the child leaves school on any date without a specific plan, so it requires confirm: true; without it you get a dry-run. Read pup_list_transportations first.",
58
- inputSchema: {
58
+ inputSchema: z.object({
59
59
  student_id: z.number().int().describe('Student id, from pup_list_students'),
60
60
  days: z
61
61
  .array(z.union([z.string(), z.number().int()]))
@@ -76,7 +76,7 @@ export function registerDefaultPlanTools(server, client) {
76
76
  .optional()
77
77
  .describe('Remove every weekday default instead of setting one (days is ignored)'),
78
78
  confirm: schemaConfirm,
79
- },
79
+ }),
80
80
  }, (async ({ student_id, days, transportation_id, note, early_dismissal_time, clear_all, confirm }) => {
81
81
  // Read-modify-write: PickUp Patrol has no default-plans endpoint, so the
82
82
  // whole student record round-trips. Reading it here (before the confirm
@@ -152,11 +152,11 @@ export function registerDefaultPlanTools(server, client) {
152
152
  }));
153
153
  server.registerTool('pup_mark_defaults_reviewed', {
154
154
  description: "Mark a student's default plans as reviewed, clearing the school's 'needs review' prompt. Requires confirm: true.",
155
- inputSchema: {
155
+ inputSchema: z.object({
156
156
  student_id: z.number().int().describe('Student id, from pup_list_students'),
157
157
  reviewed: z.boolean().optional().describe('Defaults to true'),
158
158
  confirm: schemaConfirm,
159
- },
159
+ }),
160
160
  }, async ({ student_id, reviewed, confirm }) => {
161
161
  const value = reviewed ?? true;
162
162
  const gate = previewUnlessConfirmed(confirm, `Mark student ${student_id}'s defaults as ${value ? 'reviewed' : 'not reviewed'}`, 'PUT', 'SetDefaultsReviewed', { StudentId: student_id, Reviewed: value });
@@ -1,4 +1,4 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
1
+ import type { McpServer } from '@modelcontextprotocol/server';
2
2
  import type { PickUpPatrolClient } from '../client.js';
3
3
  import type { Transportation } from '../types.js';
4
4
  /** The fields whose change proves a plan write actually landed. */
@@ -41,22 +41,22 @@ export function registerPlanTools(server, client) {
41
41
  server.registerTool('pup_list_plans', {
42
42
  description: 'Day-by-day dismissal plans across a date range for every student on the account, as PickUp Patrol returns them.',
43
43
  annotations: { readOnlyHint: true },
44
- inputSchema: {
44
+ inputSchema: z.object({
45
45
  start_date: z.string().describe('YYYY-MM-DD'),
46
46
  end_date: z.string().describe('YYYY-MM-DD'),
47
- },
47
+ }),
48
48
  }, async ({ start_date, end_date }) => minifiedResult(await client.getParentPlans(start_date, end_date)));
49
49
  server.registerTool('pup_get_plan', {
50
50
  description: 'The dismissal plan for one student on one date — the option in force, any note, the early-dismissal time, and whether the date is locked because the cutoff has passed.',
51
51
  annotations: { readOnlyHint: true },
52
- inputSchema: {
52
+ inputSchema: z.object({
53
53
  student_id: z.number().int().describe('Student id, from pup_list_students'),
54
54
  date: z.string().describe('YYYY-MM-DD'),
55
- },
55
+ }),
56
56
  }, async ({ student_id, date }) => minifiedResult(await client.getPlanEdit(date, student_id)));
57
57
  server.registerTool('pup_set_plan', {
58
58
  description: "Change how a student is dismissed on one or more specific dates, or clear those dates back to the student's weekly default. This changes how a child actually leaves school, so it requires confirm: true; without it you get a dry-run of the exact payload. Read pup_list_transportations first — options differ in whether they require a note, a car number or an early-dismissal time.",
59
- inputSchema: {
59
+ inputSchema: z.object({
60
60
  student_id: z.number().int().describe('Student id, from pup_list_students'),
61
61
  dates: z
62
62
  .array(z.string())
@@ -77,7 +77,7 @@ export function registerPlanTools(server, client) {
77
77
  .optional()
78
78
  .describe('Car number, for options where usesCarNumbers is true'),
79
79
  confirm: schemaConfirm,
80
- },
80
+ }),
81
81
  }, (async ({ student_id, dates, transportation_id, note, early_dismissal_time, car_number, confirm }) => {
82
82
  // The reads below resolve and validate the payload; they mutate nothing.
83
83
  // Running them before the confirm gate is deliberate: it makes the
@@ -1,4 +1,4 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
1
+ import type { McpServer } from '@modelcontextprotocol/server';
2
2
  import type { PickUpPatrolClient } from '../client.js';
3
3
  import type { Transportation } from '../types.js';
4
4
  /**
@@ -22,13 +22,13 @@ export function registerSchoolTools(server, client) {
22
22
  server.registerTool('pup_list_transportations', {
23
23
  description: 'The dismissal options a school offers (bus, car pickup, walker, absent …) with the rules each one imposes: whether a note is required, whether it takes a car number, whether it is an early dismissal, and the daily cutoff time. Read this before setting a plan.',
24
24
  annotations: { readOnlyHint: true },
25
- inputSchema: {
25
+ inputSchema: z.object({
26
26
  school_id: z.number().int().describe('School id, from pup_list_students'),
27
27
  include_inactive: z
28
28
  .boolean()
29
29
  .optional()
30
30
  .describe('Include options the school has deactivated (default false)'),
31
- },
31
+ }),
32
32
  }, async ({ school_id, include_inactive }) => {
33
33
  const options = await client.getTransportations(school_id);
34
34
  const visible = include_inactive === true ? options : options.filter((o) => o.IsActive !== false);
@@ -37,9 +37,9 @@ export function registerSchoolTools(server, client) {
37
37
  server.registerTool('pup_get_school', {
38
38
  description: 'A school profile together with its per-weekday notify times and plan cutoff times, and the settings that decide whether parents may set plans at all.',
39
39
  annotations: { readOnlyHint: true },
40
- inputSchema: {
40
+ inputSchema: z.object({
41
41
  school_id: z.number().int().describe('School id, from pup_list_students'),
42
- },
42
+ }),
43
43
  }, async ({ school_id }) => {
44
44
  const [school, notifyTimes, settings] = await Promise.all([
45
45
  client.getSchool(school_id),
@@ -51,14 +51,14 @@ export function registerSchoolTools(server, client) {
51
51
  server.registerTool('pup_list_non_school_days', {
52
52
  description: 'Dates a plan cannot be set for at a school (holidays, closures, weekends), and optionally the dates in a range that already differ from the student defaults.',
53
53
  annotations: { readOnlyHint: true },
54
- inputSchema: {
54
+ inputSchema: z.object({
55
55
  school_id: z.number().int().describe('School id, from pup_list_students'),
56
56
  start_date: z
57
57
  .string()
58
58
  .optional()
59
59
  .describe('YYYY-MM-DD; with end_date, also return dates that differ from the default'),
60
60
  end_date: z.string().optional().describe('YYYY-MM-DD'),
61
- },
61
+ }),
62
62
  }, async ({ school_id, start_date, end_date }) => {
63
63
  const invalidDates = await client.getInvalidPlanDates(school_id);
64
64
  if (start_date === undefined || end_date === undefined) {
@@ -70,8 +70,8 @@ export function registerSchoolTools(server, client) {
70
70
  server.registerTool('pup_list_car_numbers', {
71
71
  description: 'The car numbers a school has issued to this account, for dismissal options where usesCarNumbers is true.',
72
72
  annotations: { readOnlyHint: true },
73
- inputSchema: {
73
+ inputSchema: z.object({
74
74
  school_id: z.number().int().describe('School id, from pup_list_students'),
75
- },
75
+ }),
76
76
  }, async ({ school_id }) => minifiedResult(await client.getCarNumbers(school_id)));
77
77
  }
package/dist/version.d.ts CHANGED
@@ -7,4 +7,4 @@
7
7
  * annotation, so there is exactly one line to keep in sync and
8
8
  * `versionSyncTest` has exactly one line to check.
9
9
  */
10
- export declare const VERSION = "0.2.0";
10
+ export declare const VERSION = "1.0.0";
package/dist/version.js CHANGED
@@ -7,4 +7,4 @@
7
7
  * annotation, so there is exactly one line to keep in sync and
8
8
  * `versionSyncTest` has exactly one line to check.
9
9
  */
10
- export const VERSION = '0.2.0'; // x-release-please-version
10
+ export const VERSION = '1.0.0'; // x-release-please-version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrischall/pickuppatrol-mcp",
3
- "version": "0.2.0",
3
+ "version": "1.0.0",
4
4
  "license": "MIT",
5
5
  "mcpName": "io.github.chrischall/pickuppatrol-mcp",
6
6
  "description": "PickUp Patrol MCP server for Claude — developed and maintained by AI (Claude Code)",
@@ -49,7 +49,7 @@
49
49
  ],
50
50
  "scripts": {
51
51
  "build": "tsc && npm run bundle",
52
- "bundle": "esbuild src/index.ts --bundle --platform=node --format=esm --external:dotenv --outfile=dist/bundle.js",
52
+ "bundle": "esbuild src/index.ts --bundle --alias:zod/v4=./node_modules/zod/v4/index.cjs --platform=node --format=esm --external:dotenv --outfile=dist/bundle.js",
53
53
  "dev": "node dist/index.js",
54
54
  "typecheck": "tsc --noEmit",
55
55
  "test": "npm run typecheck && vitest run",
@@ -57,16 +57,17 @@
57
57
  "test:watch": "vitest"
58
58
  },
59
59
  "dependencies": {
60
- "@chrischall/mcp-utils": "^0.23.0",
61
- "@modelcontextprotocol/sdk": "^1.29.0",
60
+ "@chrischall/mcp-utils": "^1.0.0",
61
+ "@modelcontextprotocol/server": "^2.0.0",
62
62
  "dotenv": "^17.4.0",
63
- "zod": "^4.4.3"
63
+ "zod": "^4.6.2"
64
64
  },
65
65
  "devDependencies": {
66
+ "@modelcontextprotocol/client": "^2.0.0",
66
67
  "@types/node": "^26.1.1",
67
- "@vitest/coverage-v8": "^4.1.10",
68
+ "@vitest/coverage-v8": "^5.0.0",
68
69
  "esbuild": "^0.28.0",
69
70
  "typescript": "^7.0.2",
70
- "vitest": "^4.1.7"
71
+ "vitest": "^5.0.0"
71
72
  }
72
73
  }
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/chrischall/pickuppatrol-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.2.0",
9
+ "version": "1.0.0",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@chrischall/pickuppatrol-mcp",
14
- "version": "0.2.0",
14
+ "version": "1.0.0",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  }