splitwise-mcp 3.2.2 → 3.2.3

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.
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "metadata": {
9
9
  "description": "MCP server for Splitwise — natural-language expense and group management via the Splitwise API",
10
- "version": "3.2.2"
10
+ "version": "3.2.3"
11
11
  },
12
12
  "plugins": [
13
13
  {
@@ -15,7 +15,7 @@
15
15
  "displayName": "Splitwise",
16
16
  "source": "./",
17
17
  "description": "MCP server for Splitwise — manage expenses, groups, and friends via natural language",
18
- "version": "3.2.2",
18
+ "version": "3.2.3",
19
19
  "author": {
20
20
  "name": "Chris Hall"
21
21
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "splitwise-mcp",
3
3
  "displayName": "Splitwise",
4
- "version": "3.2.2",
4
+ "version": "3.2.3",
5
5
  "description": "MCP server for Splitwise — natural-language expense and group management via the Splitwise API",
6
6
  "author": {
7
7
  "name": "Chris Hall",
package/dist/bundle.js CHANGED
@@ -64250,7 +64250,7 @@ var OAuthTokenRevocationRequestSchema = z.object({
64250
64250
  token_type_hint: z.string().optional()
64251
64251
  }).strip();
64252
64252
 
64253
- // node_modules/@modelcontextprotocol/server/dist/src-BHSMhZ_W.mjs
64253
+ // node_modules/@modelcontextprotocol/server/dist/src-Cqbh3MYc.mjs
64254
64254
  var z2 = __toESM(require_v4(), 1);
64255
64255
  var BRANDS = /* @__PURE__ */ Symbol.for("mcp.sdk.errorBrands");
64256
64256
  function stampErrorBrands(instance, ctor) {
@@ -66494,7 +66494,8 @@ function build() {
66494
66494
  ListResourceTemplatesResultResponseSchema: wireResultResponse(ListResourceTemplatesResultSchema$1),
66495
66495
  ReadResourceResultResponseSchema: wireResultResponse(z2.union([ReadResourceResultSchema$1, InputRequiredResultSchema])),
66496
66496
  CompleteResultResponseSchema: wireResultResponse(CompleteResultSchema$1),
66497
- DiscoverResultResponseSchema: wireResultResponse(DiscoverResultSchema$1)
66497
+ DiscoverResultResponseSchema: wireResultResponse(DiscoverResultSchema$1),
66498
+ SubscriptionsListenResultResponseSchema: wireResultResponse(SubscriptionsListenResultSchema$1)
66498
66499
  };
66499
66500
  }
66500
66501
  var memo;
@@ -67002,6 +67003,9 @@ function isSpecRequestMethod(method) {
67002
67003
  function isSpecNotificationMethod(method) {
67003
67004
  return ALL_CODECS.some((codec2) => codec2.hasNotificationMethod(method));
67004
67005
  }
67006
+ function isExtensionReusedRequestMethod(method) {
67007
+ return method === "tasks/get" || method === "tasks/cancel";
67008
+ }
67005
67009
  var ALL_CODECS = [rev2025Codec, rev2026Codec];
67006
67010
  function isPlainObject$3(value) {
67007
67011
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -68010,6 +68014,8 @@ var Protocol = class {
68010
68014
  _transport;
68011
68015
  _requestMessageId = 0;
68012
68016
  _requestHandlers = /* @__PURE__ */ new Map();
68017
+ /** Methods registered with an explicit schema; the era gate in `_onrequest` reads it for the Tasks extension names. */
68018
+ _customSchemaRequestMethods = /* @__PURE__ */ new Set();
68013
68019
  _requestHandlerAbortControllers = /* @__PURE__ */ new Map();
68014
68020
  _notificationHandlers = /* @__PURE__ */ new Map();
68015
68021
  _responseHandlers = /* @__PURE__ */ new Map();
@@ -68293,7 +68299,7 @@ var Protocol = class {
68293
68299
  return;
68294
68300
  }
68295
68301
  }
68296
- if (isSpecRequestMethod(request.method) && !codec2.hasRequestMethod(request.method)) {
68302
+ if (isSpecRequestMethod(request.method) && !codec2.hasRequestMethod(request.method) && !(isExtensionReusedRequestMethod(request.method) && this._customSchemaRequestMethods.has(request.method))) {
68297
68303
  sendErrorResponse(ProtocolErrorCode.MethodNotFound, "Method not found");
68298
68304
  return;
68299
68305
  }
@@ -68426,8 +68432,11 @@ var Protocol = class {
68426
68432
  }
68427
68433
  request(request, schemaOrOptions, maybeOptions) {
68428
68434
  const codec2 = this._resolveOutboundCodec(request.method);
68435
+ if (isStandardSchema(schemaOrOptions)) {
68436
+ if (!isExtensionReusedRequestMethod(request.method)) this._assertOutboundRequestInEra(codec2, request.method);
68437
+ return this._requestWithSchemaViaCodec(codec2, request, schemaOrOptions, maybeOptions);
68438
+ }
68429
68439
  this._assertOutboundRequestInEra(codec2, request.method);
68430
- if (isStandardSchema(schemaOrOptions)) return this._requestWithSchemaViaCodec(codec2, request, schemaOrOptions, maybeOptions);
68431
68440
  const validate2 = codecResultValidator(codec2, request.method);
68432
68441
  if (validate2 === void 0) throw new TypeError(`'${request.method}' is not a spec method; pass a result schema as the second argument to request().`);
68433
68442
  return this._requestWithSchemaViaCodec(codec2, request, validate2, schemaOrOptions);
@@ -68671,6 +68680,8 @@ var Protocol = class {
68671
68680
  return maybeHandler(parsed.data, ctx);
68672
68681
  };
68673
68682
  else throw new TypeError("setRequestHandler: handler is required");
68683
+ if (typeof schemasOrHandler === "function") this._customSchemaRequestMethods.delete(method);
68684
+ else this._customSchemaRequestMethods.add(method);
68674
68685
  this._requestHandlers.set(method, this._wrapHandler(method, stored));
68675
68686
  }
68676
68687
  /**
@@ -68700,6 +68711,7 @@ var Protocol = class {
68700
68711
  */
68701
68712
  removeRequestHandler(method) {
68702
68713
  this._requestHandlers.delete(method);
68714
+ this._customSchemaRequestMethods.delete(method);
68703
68715
  }
68704
68716
  /**
68705
68717
  * Asserts that a request handler has not already been set for the given method, in preparation for a new one being automatically installed.
@@ -75942,7 +75954,7 @@ var Ajv = import_ajv.Ajv;
75942
75954
  // node_modules/@modelcontextprotocol/server/dist/shimsNode.mjs
75943
75955
  import process2 from "node:process";
75944
75956
 
75945
- // node_modules/@modelcontextprotocol/server/dist/mcp-DYuW2ZSs.mjs
75957
+ // node_modules/@modelcontextprotocol/server/dist/mcp-DIH4cS6P.mjs
75946
75958
  var COMPLETABLE_SYMBOL = /* @__PURE__ */ Symbol.for("mcp.completable");
75947
75959
  function isCompletable(schema) {
75948
75960
  return !!schema && typeof schema === "object" && COMPLETABLE_SYMBOL in schema;
@@ -76007,7 +76019,7 @@ var StdioListenRouter = class {
76007
76019
  _serverCapabilities;
76008
76020
  /**
76009
76021
  * The serving instance's identity, stamped onto the graceful-close
76010
- * results' `_meta` (the spec's `SubscriptionsListenResultMeta` extends
76022
+ * results' `_meta` (the spec's `SubscriptionsListenResultMetaObject` extends
76011
76023
  * `ResultMetaObject`). Handed over together with the capabilities.
76012
76024
  */
76013
76025
  _serverInfo;
@@ -76339,6 +76351,11 @@ var Server = class extends Protocol {
76339
76351
  if (modernProtocolVersions(this._supportedProtocolVersions).length > 0) this.setRequestHandler("server/discover", () => this._ondiscover());
76340
76352
  if (this._capabilities.logging) this._registerLoggingHandler();
76341
76353
  }
76354
+ /** Attaches to the given transport; rejects while this instance is connected to another one. */
76355
+ async connect(transport) {
76356
+ if (this.transport !== void 0) throw new SdkError(SdkErrorCode.AlreadyConnected, "Use a separate Server instance per connection: this instance is already connected to a transport. To connect it to a new transport, call close() first.");
76357
+ await super.connect(transport);
76358
+ }
76342
76359
  /**
76343
76360
  * Registers the built-in `logging/setLevel` request handler.
76344
76361
  *
@@ -76892,21 +76909,41 @@ var Server = class extends Protocol {
76892
76909
  function discoverAdvertisedCapabilities(capabilities) {
76893
76910
  return { ...capabilities };
76894
76911
  }
76912
+ function toolInputElementCount(value, max) {
76913
+ let count = 0;
76914
+ const stack = [value];
76915
+ while (stack.length > 0) {
76916
+ const node2 = stack.pop();
76917
+ if (node2 === null || typeof node2 !== "object") continue;
76918
+ if (Array.isArray(node2)) for (const child of node2) {
76919
+ if (++count > max) return count;
76920
+ if (child !== null && typeof child === "object") stack.push(child);
76921
+ }
76922
+ else for (const key in node2) {
76923
+ if (!Object.prototype.hasOwnProperty.call(node2, key)) continue;
76924
+ if (++count > max) return count;
76925
+ const child = node2[key];
76926
+ if (child !== null && typeof child === "object") stack.push(child);
76927
+ }
76928
+ }
76929
+ return count;
76930
+ }
76931
+ function resolveMaxToolInputElements(value) {
76932
+ if (value === void 0 || value === Infinity) return void 0;
76933
+ if (typeof value !== "number" || Number.isNaN(value) || value < 1) throw new RangeError(`maxToolInputElements must be a number of at least 1, or Infinity, got ${String(value)}`);
76934
+ return value;
76935
+ }
76895
76936
  var McpServer = class {
76896
76937
  /**
76897
76938
  * The underlying {@linkcode Server} instance, useful for advanced operations like sending notifications.
76898
76939
  */
76899
76940
  server;
76941
+ _maxToolInputElements;
76900
76942
  _registeredResources = {};
76901
76943
  _registeredResourceTemplates = {};
76902
76944
  _registeredTools = {};
76903
76945
  _registeredPrompts = {};
76904
- /**
76905
- * Per-tool JSON-converted `inputSchema`, memoized so the SEP-2243
76906
- * registration-time scan and the pre-dispatch validation step share one
76907
- * conversion instead of paying it twice per request under the
76908
- * per-request-factory `createMcpHandler` model.
76909
- */
76946
+ /** Per-tool JSON-converted `inputSchema`, filled on first use by `toolInputSchemaJson()`. */
76910
76947
  _toolInputSchemaJson = {};
76911
76948
  /**
76912
76949
  * The JSON-serialized `inputSchema` of a registered tool, or `undefined`
@@ -76931,6 +76968,7 @@ var McpServer = class {
76931
76968
  }
76932
76969
  constructor(serverInfo, options) {
76933
76970
  this.server = new Server(serverInfo, options);
76971
+ this._maxToolInputElements = resolveMaxToolInputElements(options?.maxToolInputElements);
76934
76972
  if (options?.capabilities?.tools) this.setToolRequestHandlers();
76935
76973
  if (options?.capabilities?.resources) this.setResourceRequestHandlers();
76936
76974
  if (options?.capabilities?.prompts) this.setPromptRequestHandlers();
@@ -77003,7 +77041,7 @@ var McpServer = class {
77003
77041
  name,
77004
77042
  title: tool.title,
77005
77043
  description: tool.description,
77006
- inputSchema: tool.inputSchema ? standardSchemaToJsonSchema(tool.inputSchema, "input") : EMPTY_OBJECT_JSON_SCHEMA,
77044
+ inputSchema: tool.inputSchema ? convertListedInputSchema(name, tool.inputSchema) : EMPTY_OBJECT_JSON_SCHEMA,
77007
77045
  annotations: tool.annotations,
77008
77046
  icons: tool.icons,
77009
77047
  execution: tool.execution,
@@ -77048,6 +77086,7 @@ var McpServer = class {
77048
77086
  * Validates tool input arguments against the tool's input schema.
77049
77087
  */
77050
77088
  async validateToolInput(tool, args, toolName) {
77089
+ if (this._maxToolInputElements !== void 0 && toolInputElementCount(args, this._maxToolInputElements) > this._maxToolInputElements) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Invalid arguments for tool ${toolName}: arguments contain more than the maximum of ${this._maxToolInputElements} elements`);
77051
77090
  if (!tool.inputSchema) return;
77052
77091
  const parseResult = await validateStandardSchema(tool.inputSchema, args ?? {});
77053
77092
  if (!parseResult.success) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Input validation error: Invalid arguments for tool ${toolName}: ${parseResult.error}`);
@@ -77322,20 +77361,19 @@ var McpServer = class {
77322
77361
  }
77323
77362
  _createRegisteredTool(name, title, description, inputSchema, outputSchema, annotations, icons, execution, scopeChallenge, _meta, handler) {
77324
77363
  validateAndWarnToolName(name);
77325
- if (inputSchema !== void 0) try {
77326
- const json2 = standardSchemaToJsonSchema(inputSchema, "input");
77327
- this._toolInputSchemaJson[name] = json2;
77328
- const scan = scanXMcpHeaderDeclarations(json2);
77329
- if (!scan.valid) console.warn(`[mcp-sdk] tool '${name}' carries an invalid x-mcp-header declaration and will be excluded by conforming Streamable HTTP clients: ${scan.reason}`);
77330
- } catch {
77331
- }
77332
77364
  let currentHandler = handler;
77365
+ let outputSchemaJson;
77333
77366
  const registeredTool = {
77334
77367
  title,
77335
77368
  description,
77336
77369
  inputSchema,
77337
77370
  outputSchema,
77338
- outputSchemaJson: convertOutputSchemaJson(outputSchema),
77371
+ get outputSchemaJson() {
77372
+ return outputSchemaJson ??= convertOutputSchemaJson(registeredTool.outputSchema);
77373
+ },
77374
+ set outputSchemaJson(value) {
77375
+ outputSchemaJson = value;
77376
+ },
77339
77377
  annotations,
77340
77378
  icons,
77341
77379
  execution,
@@ -77462,6 +77500,12 @@ var EMPTY_OBJECT_JSON_SCHEMA = {
77462
77500
  type: "object",
77463
77501
  properties: {}
77464
77502
  };
77503
+ function convertListedInputSchema(name, inputSchema) {
77504
+ const json2 = standardSchemaToJsonSchema(inputSchema, "input");
77505
+ const scan = scanXMcpHeaderDeclarations(json2);
77506
+ if (!scan.valid) console.warn(`[mcp-sdk] tool '${name}' carries an invalid x-mcp-header declaration and will be excluded by conforming Streamable HTTP clients: ${scan.reason}`);
77507
+ return json2;
77508
+ }
77465
77509
  function convertOutputSchemaJson(outputSchema) {
77466
77510
  if (outputSchema === void 0) return void 0;
77467
77511
  try {
@@ -77474,7 +77518,7 @@ function createPromptHandler(name, argsSchema, callback) {
77474
77518
  if (argsSchema) {
77475
77519
  const typedCallback = callback;
77476
77520
  return async (args, ctx) => {
77477
- const parseResult = await validateStandardSchema(argsSchema, args);
77521
+ const parseResult = await validateStandardSchema(argsSchema, args ?? {});
77478
77522
  if (!parseResult.success) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Invalid arguments for prompt ${name}: ${parseResult.error}`);
77479
77523
  return typedCallback(parseResult.data, ctx);
77480
77524
  };
@@ -98519,7 +98563,10 @@ function surfaceToolHints(server) {
98519
98563
  ));
98520
98564
  }
98521
98565
  async function createMcpServer(opts) {
98522
- const server = new McpServer({ name: opts.name, version: opts.version }, { supportedProtocolVersions: [...SERVER_PROTOCOL_VERSIONS] });
98566
+ const server = new McpServer({ name: opts.name, version: opts.version }, {
98567
+ supportedProtocolVersions: [...SERVER_PROTOCOL_VERSIONS],
98568
+ ...opts.maxToolInputElements !== void 0 ? { maxToolInputElements: opts.maxToolInputElements } : {}
98569
+ });
98523
98570
  if (opts.surfaceHints !== false)
98524
98571
  surfaceToolHints(server);
98525
98572
  if (opts.banner !== void 0) {
@@ -98815,6 +98862,17 @@ import { constants as fsConstants2, realpathSync as realpathSync2 } from "node:f
98815
98862
  var O_NOFOLLOW2 = fsConstants2.O_NOFOLLOW ?? 0;
98816
98863
  var O_NONBLOCK = fsConstants2.O_NONBLOCK ?? 0;
98817
98864
 
98865
+ // node_modules/@chrischall/mcp-utils/dist/internal/headers.js
98866
+ function responseHeader(res, name) {
98867
+ return getHeader(res?.headers, name);
98868
+ }
98869
+ function getHeader(headers, name) {
98870
+ if (!headers || typeof headers.get !== "function")
98871
+ return null;
98872
+ const value = headers.get.call(headers, name);
98873
+ return typeof value === "string" ? value : null;
98874
+ }
98875
+
98818
98876
  // node_modules/@chrischall/mcp-utils/dist/scrape/index.js
98819
98877
  var NEXT_DATA_MAX_CHARS = 16 * 1024 * 1024;
98820
98878
  function isCloudflareChallenge(html) {
@@ -99103,7 +99161,7 @@ function createApiClient(opts) {
99103
99161
  if (retryStatuses.includes(res.status) && attempt < retry.count) {
99104
99162
  discard(current);
99105
99163
  attempt += 1;
99106
- const delay = retry.honorRetryAfter ? parseRetryAfterMs(res.headers.get("retry-after"), {
99164
+ const delay = retry.honorRetryAfter ? parseRetryAfterMs(responseHeader(res, "retry-after"), {
99107
99165
  defaultMs: retry.delayMs,
99108
99166
  capMs: retry.maxRetryAfterMs ?? 3e4
99109
99167
  }) : retry.delayMs;
@@ -99116,7 +99174,7 @@ function createApiClient(opts) {
99116
99174
  async function unauthorizedOrEdge(attempt, method, path) {
99117
99175
  const res = attempt.res;
99118
99176
  let text = "";
99119
- if (/json/i.test(res.headers.get("content-type") ?? "")) {
99177
+ if (/json/i.test(responseHeader(res, "content-type") ?? "")) {
99120
99178
  discard(attempt);
99121
99179
  } else {
99122
99180
  text = await readBody(attempt, (r2) => r2.text()).catch(() => "");
@@ -99128,14 +99186,14 @@ function createApiClient(opts) {
99128
99186
  }
99129
99187
  async function rateLimitedError(attempt, method, path) {
99130
99188
  const res = attempt.res;
99131
- const retryAfter = res.headers.get("retry-after");
99189
+ const retryAfter = responseHeader(res, "retry-after");
99132
99190
  const retryAfterMs = retryAfterToMs(retryAfter);
99133
99191
  if (!opts.onRateLimited) {
99134
99192
  discard(attempt);
99135
99193
  return new RateLimitedError(service, retryAfterMs);
99136
99194
  }
99137
99195
  let text = "";
99138
- if (/json/i.test(res.headers.get("content-type") ?? "")) {
99196
+ if (/json/i.test(responseHeader(res, "content-type") ?? "")) {
99139
99197
  discard(attempt);
99140
99198
  } else {
99141
99199
  text = await readBody(attempt, (r2) => r2.text()).catch(() => "");
@@ -99195,7 +99253,7 @@ function createApiClient(opts) {
99195
99253
  const bytes = new Uint8Array(await readBody(attempt, (r2) => r2.arrayBuffer()));
99196
99254
  return {
99197
99255
  status: res.status,
99198
- contentType: res.headers.get("content-type"),
99256
+ contentType: responseHeader(res, "content-type"),
99199
99257
  headers: res.headers,
99200
99258
  bytes
99201
99259
  };
@@ -100528,7 +100586,7 @@ function registerUtilityTools(server, client2) {
100528
100586
  // src/index.ts
100529
100587
  await runMcp({
100530
100588
  name: "splitwise-mcp",
100531
- version: "3.2.2",
100589
+ version: "3.2.3",
100532
100590
  // x-release-please-version
100533
100591
  deps: client,
100534
100592
  banner: "[splitwise-mcp] This project was developed and is maintained by AI (Claude Sonnet 4.6). Use at your own discretion.",
@@ -100551,7 +100609,7 @@ unpdf/dist/index.mjs:
100551
100609
  * @license Apache-2.0
100552
100610
  *)
100553
100611
 
100554
- @modelcontextprotocol/server/dist/src-BHSMhZ_W.mjs:
100612
+ @modelcontextprotocol/server/dist/src-Cqbh3MYc.mjs:
100555
100613
  (*!
100556
100614
  * content-type
100557
100615
  * Copyright(c) 2015 Douglas Christopher Wilson
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ import { registerUtilityTools } from './tools/utilities.js';
19
19
  // client into the same registrars instead of this singleton.
20
20
  await runMcp({
21
21
  name: 'splitwise-mcp',
22
- version: '3.2.2', // x-release-please-version
22
+ version: '3.2.3', // x-release-please-version
23
23
  deps: client,
24
24
  banner: '[splitwise-mcp] This project was developed and is maintained by AI (Claude Sonnet 4.6). Use at your own discretion.',
25
25
  tools: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "splitwise-mcp",
3
- "version": "3.2.2",
3
+ "version": "3.2.3",
4
4
  "mcpName": "io.github.chrischall/splitwise-mcp",
5
5
  "description": "Splitwise MCP server for Claude — developed and maintained by AI (Claude Code)",
6
6
  "author": "Claude Code (AI) <https://www.anthropic.com/claude>",
@@ -45,14 +45,14 @@
45
45
  "typecheck": "tsc -p tsconfig.json --noEmit"
46
46
  },
47
47
  "dependencies": {
48
- "@chrischall/mcp-utils": "^2.13.0",
49
- "@modelcontextprotocol/server": "^2.2.0",
48
+ "@chrischall/mcp-utils": "^2.14.0",
49
+ "@modelcontextprotocol/server": "^2.3.0",
50
50
  "dotenv": "^18.0.2",
51
51
  "unpdf": "^1.8.1",
52
52
  "zod": "^4.6.5"
53
53
  },
54
54
  "devDependencies": {
55
- "@modelcontextprotocol/client": "^2.2.0",
55
+ "@modelcontextprotocol/client": "^2.3.0",
56
56
  "@types/node": "^26.0.0",
57
57
  "@vitest/coverage-v8": "^5.0.0",
58
58
  "esbuild": "^0.28.0",
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/chrischall/splitwise-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "3.2.2",
9
+ "version": "3.2.3",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "splitwise-mcp",
14
- "version": "3.2.2",
14
+ "version": "3.2.3",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },