@bojanrajkovic/mcp-paprika 1.2.0-beta.3 → 1.2.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/dist/tools/categories.js +2 -0
- package/dist/tools/create.js +1 -0
- package/dist/tools/delete.js +1 -0
- package/dist/tools/discover.js +2 -0
- package/dist/tools/filter.js +4 -0
- package/dist/tools/list.js +2 -0
- package/dist/tools/pantry-add.js +1 -0
- package/dist/tools/pantry-delete.js +1 -0
- package/dist/tools/pantry-get.js +2 -0
- package/dist/tools/pantry-list.js +2 -0
- package/dist/tools/pantry-update.js +1 -0
- package/dist/tools/read.js +2 -0
- package/dist/tools/search.js +2 -0
- package/dist/tools/update.js +1 -0
- package/dist/transport/http.d.ts +0 -10
- package/dist/transport/http.js +5 -0
- package/package.json +1 -1
package/dist/tools/categories.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { coldStartGuard, textResult } from "./helpers.js";
|
|
2
2
|
export function registerCategoryTools(server, ctx) {
|
|
3
|
+
const log = ctx.log.child({ component: "list_categories" });
|
|
3
4
|
server.registerTool("list_categories", {
|
|
4
5
|
description: "List all recipe categories with the number of recipes in each. Categories are sorted alphabetically.",
|
|
5
6
|
inputSchema: {},
|
|
6
7
|
}, async (_args) => {
|
|
8
|
+
log.info({ tool: "list_categories" }, "tool invoked");
|
|
7
9
|
return coldStartGuard(ctx).match(async () => {
|
|
8
10
|
const categories = ctx.store.getAllCategories();
|
|
9
11
|
if (categories.length === 0) {
|
package/dist/tools/create.js
CHANGED
|
@@ -24,6 +24,7 @@ export function registerCreateTool(server, ctx) {
|
|
|
24
24
|
nutritionalInfo: z.string().optional().describe("Nutritional information"),
|
|
25
25
|
},
|
|
26
26
|
}, async (args) => {
|
|
27
|
+
log.info({ tool: "create_recipe", name: args.name }, "tool invoked");
|
|
27
28
|
return coldStartGuard(ctx).match(async () => {
|
|
28
29
|
// Resolve category names → UIDs (AC2.4, AC2.7)
|
|
29
30
|
const { uids: categories, unknown: unknownCategories } = args.categories && args.categories.length > 0
|
package/dist/tools/delete.js
CHANGED
|
@@ -12,6 +12,7 @@ export function registerDeleteTool(server, ctx) {
|
|
|
12
12
|
uid: z.string().describe("Recipe UID to delete"),
|
|
13
13
|
},
|
|
14
14
|
}, async (args) => {
|
|
15
|
+
log.info({ tool: "delete_recipe", uid: args.uid }, "tool invoked");
|
|
15
16
|
return coldStartGuard(ctx).match(async () => {
|
|
16
17
|
const uid = RecipeUidSchema.parse(args.uid);
|
|
17
18
|
const recipe = ctx.store.get(uid);
|
package/dist/tools/discover.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { coldStartGuard, textResult } from "./helpers.js";
|
|
3
3
|
export function registerDiscoverTool(server, ctx, vectorStore) {
|
|
4
|
+
const log = ctx.log.child({ component: "discover_recipes" });
|
|
4
5
|
server.registerTool("discover_recipes", {
|
|
5
6
|
description: "Discover recipes using semantic search. Finds recipes matching a natural language description of what you're looking for.",
|
|
6
7
|
inputSchema: {
|
|
@@ -15,6 +16,7 @@ export function registerDiscoverTool(server, ctx, vectorStore) {
|
|
|
15
16
|
.describe("Maximum number of results to return (default: 5, max: 20)"),
|
|
16
17
|
},
|
|
17
18
|
}, async (args) => {
|
|
19
|
+
log.info({ tool: "discover_recipes", ...args }, "tool invoked");
|
|
18
20
|
return coldStartGuard(ctx).match(async () => {
|
|
19
21
|
const results = await vectorStore.search(args.query, args.topK);
|
|
20
22
|
if (results.length === 0) {
|
package/dist/tools/filter.js
CHANGED
|
@@ -3,6 +3,8 @@ import { ok } from "neverthrow";
|
|
|
3
3
|
import { parseDuration } from "../utils/duration.js";
|
|
4
4
|
import { coldStartGuard, textResult } from "./helpers.js";
|
|
5
5
|
export function registerFilterTools(server, ctx) {
|
|
6
|
+
const logIngredient = ctx.log.child({ component: "filter_by_ingredient" });
|
|
7
|
+
const logTime = ctx.log.child({ component: "filter_by_time" });
|
|
6
8
|
server.registerTool("filter_by_ingredient", {
|
|
7
9
|
description: 'Filter recipes by ingredient. Use mode="all" (default) to require all ingredients, or mode="any" to match any.',
|
|
8
10
|
inputSchema: {
|
|
@@ -21,6 +23,7 @@ export function registerFilterTools(server, ctx) {
|
|
|
21
23
|
.describe("Maximum number of results to return (default: 20, max: 50)"),
|
|
22
24
|
},
|
|
23
25
|
}, async (args) => {
|
|
26
|
+
logIngredient.info({ tool: "filter_by_ingredient", ...args }, "tool invoked");
|
|
24
27
|
return coldStartGuard(ctx).match(async () => {
|
|
25
28
|
const results = ctx.store.filterByIngredients(args.ingredients, args.mode, args.limit);
|
|
26
29
|
if (results.length === 0) {
|
|
@@ -46,6 +49,7 @@ export function registerFilterTools(server, ctx) {
|
|
|
46
49
|
.describe("Maximum number of results to return (default: 20, max: 50)"),
|
|
47
50
|
},
|
|
48
51
|
}, async (args) => {
|
|
52
|
+
logTime.info({ tool: "filter_by_time", ...args }, "tool invoked");
|
|
49
53
|
return coldStartGuard(ctx).match(async () => {
|
|
50
54
|
const constraintsResult = parseMaybeMinutes(args.maxPrepTime).andThen((maxPrepTime) => parseMaybeMinutes(args.maxCookTime).andThen((maxCookTime) => parseMaybeMinutes(args.maxTotalTime).map((maxTotalTime) => {
|
|
51
55
|
// Build object using spread operator to satisfy exactOptionalPropertyTypes
|
package/dist/tools/list.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { coldStartGuard, textResult } from "./helpers.js";
|
|
3
3
|
export function registerListTool(server, ctx) {
|
|
4
|
+
const log = ctx.log.child({ component: "list_recipes" });
|
|
4
5
|
server.registerTool("list_recipes", {
|
|
5
6
|
description: "List all recipes with pagination. Returns recipe summaries sorted alphabetically. Use offset/limit to paginate through the full library. Response includes total recipe count.",
|
|
6
7
|
inputSchema: {
|
|
@@ -15,6 +16,7 @@ export function registerListTool(server, ctx) {
|
|
|
15
16
|
.describe("Maximum number of recipes to return (default: 25, max: 50)"),
|
|
16
17
|
},
|
|
17
18
|
}, async (args) => {
|
|
19
|
+
log.info({ tool: "list_recipes", ...args }, "tool invoked");
|
|
18
20
|
return coldStartGuard(ctx).match(async () => {
|
|
19
21
|
const all = ctx.store.getAll().sort((a, b) => a.name.localeCompare(b.name));
|
|
20
22
|
const total = all.length;
|
package/dist/tools/pantry-add.js
CHANGED
|
@@ -20,6 +20,7 @@ export function registerAddPantryItemTool(server, ctx) {
|
|
|
20
20
|
notes: z.string().optional().describe("Free-form notes"),
|
|
21
21
|
},
|
|
22
22
|
}, async (args) => {
|
|
23
|
+
log.info({ tool: "add_pantry_item", ingredient: args.ingredient }, "tool invoked");
|
|
23
24
|
return pantryStartGuard(ctx).match(async () => {
|
|
24
25
|
// Duplicate-ingredient guard (AC4.5)
|
|
25
26
|
// Under @tsconfig/strictest, noUncheckedIndexedAccess types matches[0] as
|
|
@@ -12,6 +12,7 @@ export function registerDeletePantryItemTool(server, ctx) {
|
|
|
12
12
|
uid: z.string().describe("Pantry item UID to delete"),
|
|
13
13
|
},
|
|
14
14
|
}, async (args) => {
|
|
15
|
+
log.info({ tool: "delete_pantry_item", uid: args.uid }, "tool invoked");
|
|
15
16
|
return pantryStartGuard(ctx).match(async () => {
|
|
16
17
|
const uid = PantryItemUidSchema.parse(args.uid);
|
|
17
18
|
const existing = ctx.pantryStore.get(uid);
|
package/dist/tools/pantry-get.js
CHANGED
|
@@ -3,6 +3,7 @@ import { PantryItemUidSchema } from "../paprika/types.js";
|
|
|
3
3
|
import { pantryStartGuard, pantryItemToMarkdown } from "./pantry-helpers.js";
|
|
4
4
|
import { textResult } from "./helpers.js";
|
|
5
5
|
export function registerGetPantryItemTool(server, ctx) {
|
|
6
|
+
const log = ctx.log.child({ component: "get_pantry_item" });
|
|
6
7
|
server.registerTool("get_pantry_item", {
|
|
7
8
|
description: "Get a pantry item by UID or ingredient name. When both are provided, UID takes precedence. " +
|
|
8
9
|
"Ingredient lookup is fuzzy (exact → starts-with → contains) and case-insensitive. Returns " +
|
|
@@ -12,6 +13,7 @@ export function registerGetPantryItemTool(server, ctx) {
|
|
|
12
13
|
ingredient: z.string().optional().describe("Ingredient name (fuzzy match)"),
|
|
13
14
|
},
|
|
14
15
|
}, async (args) => {
|
|
16
|
+
log.info({ tool: "get_pantry_item", ...args }, "tool invoked");
|
|
15
17
|
return pantryStartGuard(ctx).match(async () => {
|
|
16
18
|
if (!args.uid && !args.ingredient) {
|
|
17
19
|
return textResult("Please provide either a uid or an ingredient name.");
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { pantryStartGuard } from "./pantry-helpers.js";
|
|
2
2
|
import { textResult } from "./helpers.js";
|
|
3
3
|
export function registerListPantryTool(server, ctx) {
|
|
4
|
+
const log = ctx.log.child({ component: "list_pantry" });
|
|
4
5
|
server.registerTool("list_pantry", {
|
|
5
6
|
description: "List all pantry items sorted alphabetically by ingredient name. Returns the ingredient, quantity, and aisle for each item. Use get_pantry_item with the UID for full details.",
|
|
6
7
|
inputSchema: {},
|
|
7
8
|
}, async () => {
|
|
9
|
+
log.info({ tool: "list_pantry" }, "tool invoked");
|
|
8
10
|
return pantryStartGuard(ctx).match(async () => {
|
|
9
11
|
const all = ctx.pantryStore.getAll().sort((a, b) => a.ingredient.localeCompare(b.ingredient));
|
|
10
12
|
const total = all.length;
|
|
@@ -24,6 +24,7 @@ export function registerUpdatePantryItemTool(server, ctx) {
|
|
|
24
24
|
notes: z.string().nullable().optional().describe("Set notes; pass null to clear"),
|
|
25
25
|
},
|
|
26
26
|
}, async (args) => {
|
|
27
|
+
log.info({ tool: "update_pantry_item", uid: args.uid }, "tool invoked");
|
|
27
28
|
return pantryStartGuard(ctx).match(async () => {
|
|
28
29
|
const uid = PantryItemUidSchema.parse(args.uid);
|
|
29
30
|
const existing = ctx.pantryStore.get(uid);
|
package/dist/tools/read.js
CHANGED
|
@@ -2,6 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { RecipeUidSchema } from "../paprika/types.js";
|
|
3
3
|
import { coldStartGuard, recipeToMarkdown, textResult } from "./helpers.js";
|
|
4
4
|
export function registerReadTool(server, ctx) {
|
|
5
|
+
const log = ctx.log.child({ component: "read_recipe" });
|
|
5
6
|
server.registerTool("read_recipe", {
|
|
6
7
|
description: "Read a recipe by UID or title. When both are provided, UID takes precedence. " +
|
|
7
8
|
"Title lookup is fuzzy (exact → starts-with → contains). Returns a disambiguation " +
|
|
@@ -11,6 +12,7 @@ export function registerReadTool(server, ctx) {
|
|
|
11
12
|
title: z.string().optional().describe("Recipe title (fuzzy match)"),
|
|
12
13
|
},
|
|
13
14
|
}, async (args) => {
|
|
15
|
+
log.info({ tool: "read_recipe", ...args }, "tool invoked");
|
|
14
16
|
return coldStartGuard(ctx).match(async () => {
|
|
15
17
|
if (!args.uid && !args.title) {
|
|
16
18
|
return textResult("Please provide either a uid or a title.");
|
package/dist/tools/search.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { coldStartGuard, textResult } from "./helpers.js";
|
|
3
3
|
export function registerSearchTool(server, ctx) {
|
|
4
|
+
const log = ctx.log.child({ component: "search_recipes" });
|
|
4
5
|
server.registerTool("search_recipes", {
|
|
5
6
|
description: "Search for recipes by name, ingredients, or description. Returns a ranked list of matching recipes.",
|
|
6
7
|
inputSchema: {
|
|
@@ -15,6 +16,7 @@ export function registerSearchTool(server, ctx) {
|
|
|
15
16
|
.describe("Maximum number of results to return (default: 20, max: 50)"),
|
|
16
17
|
},
|
|
17
18
|
}, async (args) => {
|
|
19
|
+
log.info({ tool: "search_recipes", ...args }, "tool invoked");
|
|
18
20
|
return coldStartGuard(ctx).match(async () => {
|
|
19
21
|
const results = ctx.store.search(args.query, { limit: args.limit });
|
|
20
22
|
if (results.length === 0) {
|
package/dist/tools/update.js
CHANGED
|
@@ -31,6 +31,7 @@ export function registerUpdateTool(server, ctx) {
|
|
|
31
31
|
nutritionalInfo: z.string().optional().describe("New nutritional information"),
|
|
32
32
|
},
|
|
33
33
|
}, async (args) => {
|
|
34
|
+
log.info({ tool: "update_recipe", uid: args.uid }, "tool invoked");
|
|
34
35
|
return coldStartGuard(ctx).match(async () => {
|
|
35
36
|
const uid = RecipeUidSchema.parse(args.uid);
|
|
36
37
|
const existing = ctx.store.get(uid);
|
package/dist/transport/http.d.ts
CHANGED
|
@@ -10,16 +10,6 @@ import type { Logger } from "pino";
|
|
|
10
10
|
export interface HttpTransportHandle extends TransportHandle {
|
|
11
11
|
readonly port: number;
|
|
12
12
|
}
|
|
13
|
-
/**
|
|
14
|
-
* Hono middleware factory that logs one structured record per request.
|
|
15
|
-
*
|
|
16
|
-
* Emits `info` for all 2xx/3xx/4xx responses and `error` for 5xx. The record
|
|
17
|
-
* carries `{method, path, status, durationMs}` so operators can correlate
|
|
18
|
-
* access patterns with structured log streams without parsing text.
|
|
19
|
-
*
|
|
20
|
-
* Exported for isolated unit testing — `startHttp` has no logger injection
|
|
21
|
-
* seam, so tests instantiate `accessLog` directly with a capture logger.
|
|
22
|
-
*/
|
|
23
13
|
export declare function accessLog(log: Logger): (c: Context, next: Next) => Promise<void>;
|
|
24
14
|
/**
|
|
25
15
|
* Options for `startHttp`. All fields are optional and intended for testing.
|
package/dist/transport/http.js
CHANGED
|
@@ -20,8 +20,13 @@ const MCP_SESSION_HEADER = "mcp-session-id";
|
|
|
20
20
|
* Exported for isolated unit testing — `startHttp` has no logger injection
|
|
21
21
|
* seam, so tests instantiate `accessLog` directly with a capture logger.
|
|
22
22
|
*/
|
|
23
|
+
const PROBE_PATHS = new Set(["/healthz"]);
|
|
23
24
|
export function accessLog(log) {
|
|
24
25
|
return async (c, next) => {
|
|
26
|
+
if (PROBE_PATHS.has(c.req.path)) {
|
|
27
|
+
await next();
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
25
30
|
const t0 = performance.now();
|
|
26
31
|
try {
|
|
27
32
|
await next();
|