@allourthings/mcp-server 0.3.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.
- package/README.md +7 -1
- package/dist/index.js +94 -22
- package/package.json +2 -2
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
|
|
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
|
@@ -11169,13 +11169,22 @@ class NativeBackend {
|
|
|
11169
11169
|
return this.store.searchItems(query);
|
|
11170
11170
|
}
|
|
11171
11171
|
async addAttachment(itemId, filename, kind, data, label) {
|
|
11172
|
-
|
|
11172
|
+
const result = this.store.addAttachment(itemId, filename, kind, data, label ?? null);
|
|
11173
|
+
if (result instanceof Error)
|
|
11174
|
+
throw result;
|
|
11175
|
+
return result;
|
|
11173
11176
|
}
|
|
11174
11177
|
async getAttachment(itemId, filename) {
|
|
11175
|
-
|
|
11178
|
+
const result = this.store.getAttachment(itemId, filename);
|
|
11179
|
+
if (result instanceof Error)
|
|
11180
|
+
throw result;
|
|
11181
|
+
return result;
|
|
11176
11182
|
}
|
|
11177
11183
|
async deleteAttachment(itemId, filename) {
|
|
11178
|
-
|
|
11184
|
+
const result = this.store.deleteAttachment(itemId, filename);
|
|
11185
|
+
if (result instanceof Error)
|
|
11186
|
+
throw result;
|
|
11187
|
+
return result;
|
|
11179
11188
|
}
|
|
11180
11189
|
}
|
|
11181
11190
|
|
|
@@ -17800,20 +17809,19 @@ class Server extends Protocol {
|
|
|
17800
17809
|
// src/tools/add-item.ts
|
|
17801
17810
|
var addItemInputSchema = exports_external.object({
|
|
17802
17811
|
name: exports_external.string().describe("Name of the item"),
|
|
17803
|
-
category: exports_external.string().optional().describe("Category (e.g. appliance, furniture, subscription)"),
|
|
17804
|
-
brand: exports_external.string().optional(),
|
|
17805
|
-
model: exports_external.string().optional(),
|
|
17806
|
-
purchase_date: exports_external.string().optional().describe("ISO date
|
|
17807
|
-
purchase_price: exports_external.number().optional(),
|
|
17808
|
-
currency: exports_external.string().optional().describe("e.g. USD,
|
|
17809
|
-
warranty_expires: exports_external.string().optional().describe("ISO date
|
|
17810
|
-
retailer: exports_external.string().optional(),
|
|
17811
|
-
location: exports_external.string().optional().describe("Where the item is kept"),
|
|
17812
|
-
features: exports_external.array(exports_external.string()).optional(),
|
|
17813
|
-
notes: exports_external.string().optional(),
|
|
17814
|
-
tags: exports_external.array(exports_external.string()).optional(),
|
|
17815
|
-
|
|
17816
|
-
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")
|
|
17817
17825
|
}).passthrough();
|
|
17818
17826
|
async function addItem(backend, input) {
|
|
17819
17827
|
const item = await backend.addItem(input);
|
|
@@ -17829,7 +17837,7 @@ async function addItem(backend, input) {
|
|
|
17829
17837
|
|
|
17830
17838
|
// src/tools/get-item.ts
|
|
17831
17839
|
var getItemInputSchema = exports_external.object({
|
|
17832
|
-
id_or_name: exports_external.string().describe("Item ID or name
|
|
17840
|
+
id_or_name: exports_external.string().describe("Item ID or name. Tries exact ID match first, then exact name match, then substring match.")
|
|
17833
17841
|
});
|
|
17834
17842
|
async function getItem(backend, input) {
|
|
17835
17843
|
const item = await backend.getItem(input.id_or_name);
|
|
@@ -17869,8 +17877,8 @@ ${summary}` }]
|
|
|
17869
17877
|
|
|
17870
17878
|
// src/tools/update-item.ts
|
|
17871
17879
|
var updateItemInputSchema = exports_external.object({
|
|
17872
|
-
id: exports_external.string().regex(/^[0-9a-f]{8}$/).describe("Item ID to update"),
|
|
17873
|
-
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.")
|
|
17874
17882
|
});
|
|
17875
17883
|
async function updateItem(backend, input) {
|
|
17876
17884
|
const item = await backend.updateItem(input.id, input.updates);
|
|
@@ -17880,7 +17888,7 @@ async function updateItem(backend, input) {
|
|
|
17880
17888
|
};
|
|
17881
17889
|
}
|
|
17882
17890
|
return {
|
|
17883
|
-
content: [{ type: "text", text:
|
|
17891
|
+
content: [{ type: "text", text: JSON.stringify(item, null, 2) }]
|
|
17884
17892
|
};
|
|
17885
17893
|
}
|
|
17886
17894
|
|
|
@@ -17977,6 +17985,63 @@ async function deleteAttachment(backend, input) {
|
|
|
17977
17985
|
};
|
|
17978
17986
|
}
|
|
17979
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
|
+
|
|
17980
18045
|
// src/server.ts
|
|
17981
18046
|
function createServer(backend) {
|
|
17982
18047
|
const server = new Server({ name: "allourthings", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
@@ -18019,13 +18084,18 @@ function createServer(backend) {
|
|
|
18019
18084
|
},
|
|
18020
18085
|
{
|
|
18021
18086
|
name: "get_attachment",
|
|
18022
|
-
description: "Retrieve an attachment's contents as a base64-encoded string",
|
|
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.",
|
|
18023
18088
|
inputSchema: zodToJsonSchema(getAttachmentInputSchema)
|
|
18024
18089
|
},
|
|
18025
18090
|
{
|
|
18026
18091
|
name: "delete_attachment",
|
|
18027
18092
|
description: "Delete an attachment from an item",
|
|
18028
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)
|
|
18029
18099
|
}
|
|
18030
18100
|
]
|
|
18031
18101
|
}));
|
|
@@ -18050,6 +18120,8 @@ function createServer(backend) {
|
|
|
18050
18120
|
return getAttachment(backend, getAttachmentInputSchema.parse(args));
|
|
18051
18121
|
case "delete_attachment":
|
|
18052
18122
|
return deleteAttachment(backend, deleteAttachmentInputSchema.parse(args));
|
|
18123
|
+
case "attach_from_url":
|
|
18124
|
+
return attachFromUrl(backend, attachFromUrlInputSchema.parse(args));
|
|
18053
18125
|
default:
|
|
18054
18126
|
throw new Error(`Unknown tool: ${name}`);
|
|
18055
18127
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@allourthings/mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"mcpName": "io.allourthings/mcp-server",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "AllOurThings — Your things, understood by AI.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|