@allourthings/mcp-server 0.2.0 → 0.4.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 (3) hide show
  1. package/README.md +7 -1
  2. package/dist/index.js +175 -18
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -6,7 +6,7 @@ An [MCP server](https://modelcontextprotocol.io) that gives your AI assistant na
6
6
 
7
7
  ## Requirements
8
8
 
9
- **Desktop only** — macOS, Windows, or Linux with [Claude Desktop](https://claude.ai/download) or another MCP-compatible client. The MCP server runs as a local process; mobile AI assistant access is not currently supported. An iOS app for add/browse on mobile is in development.
9
+ **Desktop only** — macOS, Windows, or Linux with [Claude Desktop](https://claude.ai/download) or another MCP-compatible client. The MCP server runs as a local process.
10
10
 
11
11
  ## Setup
12
12
 
@@ -45,6 +45,8 @@ Once connected, ask your AI assistant things like:
45
45
  - *"What's in the kitchen?"*
46
46
  - *"When does my TV warranty expire?"*
47
47
  - *"Search for anything Samsung"*
48
+ - *"Find and attach the manual for my Dyson V15"*
49
+ - *"Attach this receipt to my MacBook"*
48
50
 
49
51
  ## Tools
50
52
 
@@ -56,6 +58,10 @@ Once connected, ask your AI assistant things like:
56
58
  | `update_item` | Update fields on an existing item |
57
59
  | `delete_item` | Delete an item by ID |
58
60
  | `search_items` | Full-text search across all item fields |
61
+ | `add_attachment` | Attach a file (manual, receipt, photo, warranty) to an item |
62
+ | `attach_from_url` | Download a file from a URL and attach it to an item |
63
+ | `get_attachment` | Retrieve an attachment's contents |
64
+ | `delete_attachment` | Remove an attachment from an item |
59
65
 
60
66
  ## Vault structure
61
67
 
package/dist/index.js CHANGED
@@ -11168,6 +11168,24 @@ class NativeBackend {
11168
11168
  async searchItems(query) {
11169
11169
  return this.store.searchItems(query);
11170
11170
  }
11171
+ async addAttachment(itemId, filename, kind, data, label) {
11172
+ const result = this.store.addAttachment(itemId, filename, kind, data, label ?? null);
11173
+ if (result instanceof Error)
11174
+ throw result;
11175
+ return result;
11176
+ }
11177
+ async getAttachment(itemId, filename) {
11178
+ const result = this.store.getAttachment(itemId, filename);
11179
+ if (result instanceof Error)
11180
+ throw result;
11181
+ return result;
11182
+ }
11183
+ async deleteAttachment(itemId, filename) {
11184
+ const result = this.store.deleteAttachment(itemId, filename);
11185
+ if (result instanceof Error)
11186
+ throw result;
11187
+ return result;
11188
+ }
11171
11189
  }
11172
11190
 
11173
11191
  // ../../node_modules/zod/v3/external.js
@@ -17791,20 +17809,19 @@ class Server extends Protocol {
17791
17809
  // src/tools/add-item.ts
17792
17810
  var addItemInputSchema = exports_external.object({
17793
17811
  name: exports_external.string().describe("Name of the item"),
17794
- category: exports_external.string().optional().describe("Category (e.g. appliance, furniture, subscription)"),
17795
- brand: exports_external.string().optional(),
17796
- model: exports_external.string().optional(),
17797
- purchase_date: exports_external.string().optional().describe("ISO date string"),
17798
- purchase_price: exports_external.number().optional(),
17799
- currency: exports_external.string().optional().describe("e.g. USD, GBP"),
17800
- warranty_expires: exports_external.string().optional().describe("ISO date string"),
17801
- retailer: exports_external.string().optional(),
17802
- location: exports_external.string().optional().describe("Where the item is kept"),
17803
- features: exports_external.array(exports_external.string()).optional(),
17804
- notes: exports_external.string().optional(),
17805
- tags: exports_external.array(exports_external.string()).optional(),
17806
- manual_ref: exports_external.string().optional().describe("URL or filename of the manual"),
17807
- images: exports_external.array(exports_external.string()).optional().describe("Image file paths or URLs")
17812
+ category: exports_external.string().optional().describe("Category (e.g. appliance, electronics, furniture, subscription)"),
17813
+ brand: exports_external.string().optional().describe("Manufacturer or brand name"),
17814
+ model: exports_external.string().optional().describe("Model name or number"),
17815
+ purchase_date: exports_external.string().optional().describe("Date of purchase (ISO date, e.g. 2024-01-15)"),
17816
+ purchase_price: exports_external.number().optional().describe("Price paid for the item"),
17817
+ currency: exports_external.string().optional().describe("Currency code (e.g. GBP, USD, EUR)"),
17818
+ warranty_expires: exports_external.string().optional().describe("Warranty expiry date (ISO date, e.g. 2026-01-15)"),
17819
+ retailer: exports_external.string().optional().describe("Where the item was purchased"),
17820
+ location: exports_external.string().optional().describe("Where the item is kept (e.g. kitchen, garage, office)"),
17821
+ features: exports_external.array(exports_external.string()).optional().describe("Key features or specifications"),
17822
+ notes: exports_external.string().optional().describe("Any additional notes about the item"),
17823
+ tags: exports_external.array(exports_external.string()).optional().describe("Tags for grouping or searching (e.g. ['white goods', 'laundry'])"),
17824
+ serial_number: exports_external.string().optional().describe("Serial number of the item")
17808
17825
  }).passthrough();
17809
17826
  async function addItem(backend, input) {
17810
17827
  const item = await backend.addItem(input);
@@ -17820,7 +17837,7 @@ async function addItem(backend, input) {
17820
17837
 
17821
17838
  // src/tools/get-item.ts
17822
17839
  var getItemInputSchema = exports_external.object({
17823
- id_or_name: exports_external.string().describe("Item ID or name (fuzzy match)")
17840
+ id_or_name: exports_external.string().describe("Item ID or name. Tries exact ID match first, then exact name match, then substring match.")
17824
17841
  });
17825
17842
  async function getItem(backend, input) {
17826
17843
  const item = await backend.getItem(input.id_or_name);
@@ -17860,8 +17877,8 @@ ${summary}` }]
17860
17877
 
17861
17878
  // src/tools/update-item.ts
17862
17879
  var updateItemInputSchema = exports_external.object({
17863
- id: exports_external.string().regex(/^[0-9a-f]{8}$/).describe("Item ID to update"),
17864
- updates: exports_external.record(exports_external.unknown()).describe("Fields to update")
17880
+ id: exports_external.string().regex(/^[0-9a-f]{8}$/).describe("Item ID to update (8-character hex, from the id field on the item)"),
17881
+ updates: exports_external.record(exports_external.unknown()).describe("Fields to update. Any item field can be updated: name, category, brand, model, purchase_date, purchase_price, currency, warranty_expires, retailer, location, features, notes, tags, serial_number. Custom fields are also accepted.")
17865
17882
  });
17866
17883
  async function updateItem(backend, input) {
17867
17884
  const item = await backend.updateItem(input.id, input.updates);
@@ -17871,7 +17888,7 @@ async function updateItem(backend, input) {
17871
17888
  };
17872
17889
  }
17873
17890
  return {
17874
- content: [{ type: "text", text: `Updated: ${item.name}` }]
17891
+ content: [{ type: "text", text: JSON.stringify(item, null, 2) }]
17875
17892
  };
17876
17893
  }
17877
17894
 
@@ -17913,6 +17930,118 @@ ${summary}` }]
17913
17930
  };
17914
17931
  }
17915
17932
 
17933
+ // src/tools/add-attachment.ts
17934
+ var addAttachmentInputSchema = exports_external.object({
17935
+ item_id: exports_external.string().describe("ID of the item to attach the file to"),
17936
+ filename: exports_external.string().describe("Filename to store the attachment as (e.g. manual.pdf)"),
17937
+ kind: exports_external.enum(["manual", "receipt", "photo", "warranty", "other"]).describe("Type of attachment"),
17938
+ data_base64: exports_external.string().describe("Base64-encoded file contents"),
17939
+ label: exports_external.string().optional().describe("Human-readable label (e.g. 'Washing machine manual')")
17940
+ });
17941
+ async function addAttachment(backend, input) {
17942
+ const data = Buffer.from(input.data_base64, "base64");
17943
+ const item = await backend.addAttachment(input.item_id, input.filename, input.kind, data, input.label);
17944
+ return {
17945
+ content: [
17946
+ {
17947
+ type: "text",
17948
+ text: JSON.stringify(item, null, 2)
17949
+ }
17950
+ ]
17951
+ };
17952
+ }
17953
+
17954
+ // src/tools/get-attachment.ts
17955
+ var getAttachmentInputSchema = exports_external.object({
17956
+ item_id: exports_external.string().describe("ID of the item"),
17957
+ filename: exports_external.string().describe("Filename of the attachment to retrieve")
17958
+ });
17959
+ async function getAttachment(backend, input) {
17960
+ const data = await backend.getAttachment(input.item_id, input.filename);
17961
+ return {
17962
+ content: [
17963
+ {
17964
+ type: "text",
17965
+ text: data.toString("base64")
17966
+ }
17967
+ ]
17968
+ };
17969
+ }
17970
+
17971
+ // src/tools/delete-attachment.ts
17972
+ var deleteAttachmentInputSchema = exports_external.object({
17973
+ item_id: exports_external.string().describe("ID of the item"),
17974
+ filename: exports_external.string().describe("Filename of the attachment to delete")
17975
+ });
17976
+ async function deleteAttachment(backend, input) {
17977
+ const item = await backend.deleteAttachment(input.item_id, input.filename);
17978
+ return {
17979
+ content: [
17980
+ {
17981
+ type: "text",
17982
+ text: item ? JSON.stringify(item, null, 2) : "Attachment deleted (item not found)"
17983
+ }
17984
+ ]
17985
+ };
17986
+ }
17987
+
17988
+ // src/tools/attach-from-url.ts
17989
+ import { basename } from "path";
17990
+ var TIMEOUT_MS = 30000;
17991
+ var MAX_BYTES = 50 * 1024 * 1024;
17992
+ var attachFromUrlInputSchema = exports_external.object({
17993
+ item_id: exports_external.string().describe("ID of the item to attach the file to"),
17994
+ url: exports_external.string().url().describe("URL of the file to download and attach"),
17995
+ kind: exports_external.enum(["manual", "receipt", "photo", "warranty", "other"]).describe("Type of attachment"),
17996
+ filename: exports_external.string().optional().describe("Filename to store the attachment as — inferred from URL if omitted"),
17997
+ label: exports_external.string().optional().describe("Human-readable label (e.g. 'User Manual')")
17998
+ });
17999
+ function errorResult(message) {
18000
+ return {
18001
+ content: [{ type: "text", text: message }],
18002
+ isError: true
18003
+ };
18004
+ }
18005
+ async function attachFromUrl(backend, input) {
18006
+ const abort = new AbortController;
18007
+ const timer = setTimeout(() => abort.abort(), TIMEOUT_MS);
18008
+ let response;
18009
+ try {
18010
+ response = await fetch(input.url, { signal: abort.signal });
18011
+ } catch (err) {
18012
+ clearTimeout(timer);
18013
+ const isTimeout = err instanceof Error && err.name === "AbortError";
18014
+ return errorResult(isTimeout ? `Download timed out after ${TIMEOUT_MS / 1000}s. The server may be blocking automated requests or the file is too slow to respond. Try a different direct download URL.` : `Network error fetching URL: ${err instanceof Error ? err.message : String(err)}. Check the URL is correct and publicly accessible.`);
18015
+ }
18016
+ clearTimeout(timer);
18017
+ if (!response.ok) {
18018
+ const hint = response.status === 403 || response.status === 401 ? " The server is blocking automated downloads. Try finding a direct PDF link from a different source." : response.status === 404 ? " The file was not found at this URL. It may have moved — try searching for an updated link." : "";
18019
+ return errorResult(`Download failed: HTTP ${response.status} ${response.statusText}.${hint}`);
18020
+ }
18021
+ const contentType = response.headers.get("content-type") ?? "";
18022
+ const contentLength = Number(response.headers.get("content-length") ?? 0);
18023
+ if (contentLength > MAX_BYTES) {
18024
+ return errorResult(`File is too large to attach (${Math.round(contentLength / 1024 / 1024)} MB — limit is ${MAX_BYTES / 1024 / 1024} MB). Try finding a compressed or alternative version.`);
18025
+ }
18026
+ if (contentType.startsWith("text/html")) {
18027
+ return errorResult("The URL returned an HTML page rather than a file. This is likely a product page or login wall, not a direct download link. Try right-clicking the download button on the page and copying the direct PDF link.");
18028
+ }
18029
+ const buffer = Buffer.from(await response.arrayBuffer());
18030
+ if (buffer.byteLength > MAX_BYTES) {
18031
+ return errorResult(`File is too large to attach (${Math.round(buffer.byteLength / 1024 / 1024)} MB — limit is ${MAX_BYTES / 1024 / 1024} MB).`);
18032
+ }
18033
+ const filename = input.filename ?? (basename(new URL(input.url).pathname) || "attachment");
18034
+ const item = await backend.addAttachment(input.item_id, filename, input.kind, buffer, input.label);
18035
+ return {
18036
+ content: [
18037
+ {
18038
+ type: "text",
18039
+ text: JSON.stringify(item, null, 2)
18040
+ }
18041
+ ]
18042
+ };
18043
+ }
18044
+
17916
18045
  // src/server.ts
17917
18046
  function createServer(backend) {
17918
18047
  const server = new Server({ name: "allourthings", version: "0.1.0" }, { capabilities: { tools: {} } });
@@ -17947,6 +18076,26 @@ function createServer(backend) {
17947
18076
  name: "search_items",
17948
18077
  description: "Full-text search across all item fields",
17949
18078
  inputSchema: zodToJsonSchema(searchItemsInputSchema)
18079
+ },
18080
+ {
18081
+ name: "add_attachment",
18082
+ description: "Attach a file (manual, receipt, photo, warranty, etc.) to an item. File contents must be base64-encoded.",
18083
+ inputSchema: zodToJsonSchema(addAttachmentInputSchema)
18084
+ },
18085
+ {
18086
+ name: "get_attachment",
18087
+ description: "Retrieve an attachment's contents as a base64-encoded string. Useful for checking an attachment exists. Note: binary files like PDFs cannot be read or displayed directly.",
18088
+ inputSchema: zodToJsonSchema(getAttachmentInputSchema)
18089
+ },
18090
+ {
18091
+ name: "delete_attachment",
18092
+ description: "Delete an attachment from an item",
18093
+ inputSchema: zodToJsonSchema(deleteAttachmentInputSchema)
18094
+ },
18095
+ {
18096
+ name: "attach_from_url",
18097
+ description: "Download a file from a URL and attach it to an item (e.g. a PDF manual or warranty document)",
18098
+ inputSchema: zodToJsonSchema(attachFromUrlInputSchema)
17950
18099
  }
17951
18100
  ]
17952
18101
  }));
@@ -17965,6 +18114,14 @@ function createServer(backend) {
17965
18114
  return deleteItem(backend, deleteItemInputSchema.parse(args));
17966
18115
  case "search_items":
17967
18116
  return searchItems(backend, searchItemsInputSchema.parse(args));
18117
+ case "add_attachment":
18118
+ return addAttachment(backend, addAttachmentInputSchema.parse(args));
18119
+ case "get_attachment":
18120
+ return getAttachment(backend, getAttachmentInputSchema.parse(args));
18121
+ case "delete_attachment":
18122
+ return deleteAttachment(backend, deleteAttachmentInputSchema.parse(args));
18123
+ case "attach_from_url":
18124
+ return attachFromUrl(backend, attachFromUrlInputSchema.parse(args));
17968
18125
  default:
17969
18126
  throw new Error(`Unknown tool: ${name}`);
17970
18127
  }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@allourthings/mcp-server",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "mcpName": "io.allourthings/mcp-server",
5
- "description": "MCP server for All Our Things — AI-powered household inventory",
5
+ "description": "AllOurThings — Your things, understood by AI.",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "@modelcontextprotocol/sdk": "^1.27.1",
27
- "@allourthings/core": "^0.1.0",
27
+ "@allourthings/core": "^0.1.10",
28
28
  "zod": "^3.24.0",
29
29
  "zod-to-json-schema": "^3.25.1"
30
30
  },