@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.
- package/README.md +7 -1
- package/dist/index.js +175 -18
- 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
|
|
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
|
|
17798
|
-
purchase_price: exports_external.number().optional(),
|
|
17799
|
-
currency: exports_external.string().optional().describe("e.g. USD,
|
|
17800
|
-
warranty_expires: exports_external.string().optional().describe("ISO date
|
|
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
|
-
|
|
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
|
|
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:
|
|
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.
|
|
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",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
27
|
-
"@allourthings/core": "^0.1.
|
|
27
|
+
"@allourthings/core": "^0.1.10",
|
|
28
28
|
"zod": "^3.24.0",
|
|
29
29
|
"zod-to-json-schema": "^3.25.1"
|
|
30
30
|
},
|