@iwo-szapar/data-mcp 0.2.0 → 0.3.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.
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { DataAdapter } from '../../adapter/types.js';
3
+ export declare function registerLinkCreate(server: McpServer, adapter: DataAdapter): void;
4
+ //# sourceMappingURL=link-create.d.ts.map
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Tool: link_create
3
+ *
4
+ * Creates a typed relationship between two MemoryOS entities.
5
+ */
6
+ import { z } from 'zod';
7
+ import { makeToolResponse, handleAdapterError, withGracefulDegradation } from '../shared.js';
8
+ const ENTITY_TYPES = ['knowledge', 'decision', 'session', 'blog_post', 'prospect', 'agent_learning'];
9
+ const RELATION_TYPES = ['supports', 'contradicts', 'derived_from', 'example_of', 'supersedes', 'part_of', 'prerequisite'];
10
+ export function registerLinkCreate(server, adapter) {
11
+ server.tool('link_create', 'Create a typed relationship between two MemoryOS entities. ' +
12
+ 'Links express how knowledge items relate: supports, contradicts, derived_from, etc. ' +
13
+ 'Deduplicates by (source, target, relation_type).', {
14
+ source_type: z.enum(ENTITY_TYPES).describe('Type of the source entity'),
15
+ source_id: z.string().uuid().describe('UUID of the source entity'),
16
+ target_type: z.enum(ENTITY_TYPES).describe('Type of the target entity'),
17
+ target_id: z.string().uuid().describe('UUID of the target entity'),
18
+ relation_type: z.enum(RELATION_TYPES).describe('Type of relationship'),
19
+ confidence: z.number().min(0).max(1).optional().default(0.8).describe('Confidence in this relationship (0-1)'),
20
+ notes: z.string().max(500).optional().describe('Optional context for why this link exists'),
21
+ }, withGracefulDegradation('knowledge_links', adapter, async (params) => {
22
+ try {
23
+ if (params.source_type === params.target_type && params.source_id === params.target_id) {
24
+ return makeToolResponse({
25
+ created: false,
26
+ message: 'Cannot create a self-link.',
27
+ });
28
+ }
29
+ const existing = await adapter.list('knowledge_links', {
30
+ filter: [[
31
+ { field: 'source_type', op: 'eq', value: params.source_type },
32
+ { field: 'source_id', op: 'eq', value: params.source_id },
33
+ { field: 'target_type', op: 'eq', value: params.target_type },
34
+ { field: 'target_id', op: 'eq', value: params.target_id },
35
+ { field: 'relation_type', op: 'eq', value: params.relation_type },
36
+ ]],
37
+ page: { limit: 1, offset: 0 },
38
+ });
39
+ if (existing.items.length > 0) {
40
+ return makeToolResponse({
41
+ created: false,
42
+ existing_link_id: existing.items[0].id,
43
+ message: `Link already exists (id: ${existing.items[0].id}).`,
44
+ });
45
+ }
46
+ const record = await adapter.create('knowledge_links', {
47
+ source_type: params.source_type,
48
+ source_id: params.source_id,
49
+ target_type: params.target_type,
50
+ target_id: params.target_id,
51
+ relation_type: params.relation_type,
52
+ confidence: params.confidence ?? 0.8,
53
+ notes: params.notes ?? null,
54
+ auto_suggested: false,
55
+ });
56
+ return makeToolResponse({
57
+ created: true,
58
+ link: { id: record.id, source_type: params.source_type, target_type: params.target_type, relation_type: params.relation_type },
59
+ message: `Link created: ${params.source_type}:${params.source_id.slice(0, 8)} --[${params.relation_type}]--> ${params.target_type}:${params.target_id.slice(0, 8)}`,
60
+ });
61
+ }
62
+ catch (error) {
63
+ return handleAdapterError(error, 'link_create');
64
+ }
65
+ }));
66
+ }
67
+ //# sourceMappingURL=link-create.js.map
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { DataAdapter } from '../../adapter/types.js';
3
+ export declare function registerLinkDelete(server: McpServer, adapter: DataAdapter): void;
4
+ //# sourceMappingURL=link-delete.d.ts.map
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Tool: link_delete
3
+ *
4
+ * Deletes a knowledge link by ID.
5
+ */
6
+ import { z } from 'zod';
7
+ import { makeToolResponse, handleAdapterError, withGracefulDegradation } from '../shared.js';
8
+ export function registerLinkDelete(server, adapter) {
9
+ server.tool('link_delete', 'Delete a knowledge link by its ID.', {
10
+ link_id: z.string().uuid().describe('UUID of the link to delete'),
11
+ }, withGracefulDegradation('knowledge_links', adapter, async (params) => {
12
+ try {
13
+ try {
14
+ await adapter.getOne('knowledge_links', params.link_id);
15
+ }
16
+ catch {
17
+ return makeToolResponse({ deleted: false, message: `Link not found: ${params.link_id}` });
18
+ }
19
+ await adapter.delete('knowledge_links', params.link_id);
20
+ return makeToolResponse({ deleted: true, link_id: params.link_id, message: `Link deleted: ${params.link_id}` });
21
+ }
22
+ catch (error) {
23
+ return handleAdapterError(error, 'link_delete');
24
+ }
25
+ }));
26
+ }
27
+ //# sourceMappingURL=link-delete.js.map
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { DataAdapter } from '../../adapter/types.js';
3
+ export declare function registerLinkRelated(server: McpServer, adapter: DataAdapter): void;
4
+ //# sourceMappingURL=link-related.d.ts.map
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Tool: link_related
3
+ *
4
+ * Get all links for an entity — traverse the knowledge graph.
5
+ */
6
+ import { z } from 'zod';
7
+ import { makeToolResponse, handleAdapterError, withGracefulDegradation } from '../shared.js';
8
+ const ENTITY_TYPES = ['knowledge', 'decision', 'session', 'blog_post', 'prospect', 'agent_learning'];
9
+ export function registerLinkRelated(server, adapter) {
10
+ server.tool('link_related', 'Get all links for an entity. Shows outgoing and incoming relationships with resolved titles.', {
11
+ entity_type: z.enum(ENTITY_TYPES).describe('Type of the entity'),
12
+ entity_id: z.string().uuid().describe('UUID of the entity'),
13
+ direction: z.enum(['both', 'outgoing', 'incoming']).optional().default('both').describe('Filter direction'),
14
+ relation_type: z.enum(['supports', 'contradicts', 'derived_from', 'example_of', 'supersedes', 'part_of', 'prerequisite'])
15
+ .optional().describe('Filter by relation type'),
16
+ }, withGracefulDegradation('knowledge_links', adapter, async (params) => {
17
+ try {
18
+ const outgoing = [];
19
+ const incoming = [];
20
+ if (params.direction === 'both' || params.direction === 'outgoing') {
21
+ const filter = [
22
+ { field: 'source_type', op: 'eq', value: params.entity_type },
23
+ { field: 'source_id', op: 'eq', value: params.entity_id },
24
+ ];
25
+ if (params.relation_type) {
26
+ filter.push({ field: 'relation_type', op: 'eq', value: params.relation_type });
27
+ }
28
+ const result = await adapter.list('knowledge_links', {
29
+ filter: [filter],
30
+ sort: [{ field: 'created_at', direction: 'desc' }],
31
+ page: { limit: 50, offset: 0 },
32
+ });
33
+ for (const link of result.items) {
34
+ let targetTitle = null;
35
+ try {
36
+ const col = link.target_type === 'decision' ? 'decisions' : link.target_type;
37
+ targetTitle = (await adapter.getOne(col, link.target_id)).title ?? null;
38
+ }
39
+ catch { /* skip */ }
40
+ outgoing.push({
41
+ link_id: link.id, direction: 'outgoing',
42
+ linked_type: link.target_type, linked_id: link.target_id,
43
+ linked_title: targetTitle, relation_type: link.relation_type,
44
+ confidence: link.confidence, notes: link.notes,
45
+ });
46
+ }
47
+ }
48
+ if (params.direction === 'both' || params.direction === 'incoming') {
49
+ const filter = [
50
+ { field: 'target_type', op: 'eq', value: params.entity_type },
51
+ { field: 'target_id', op: 'eq', value: params.entity_id },
52
+ ];
53
+ if (params.relation_type) {
54
+ filter.push({ field: 'relation_type', op: 'eq', value: params.relation_type });
55
+ }
56
+ const result = await adapter.list('knowledge_links', {
57
+ filter: [filter],
58
+ sort: [{ field: 'created_at', direction: 'desc' }],
59
+ page: { limit: 50, offset: 0 },
60
+ });
61
+ for (const link of result.items) {
62
+ let sourceTitle = null;
63
+ try {
64
+ const col = link.source_type === 'decision' ? 'decisions' : link.source_type;
65
+ sourceTitle = (await adapter.getOne(col, link.source_id)).title ?? null;
66
+ }
67
+ catch { /* skip */ }
68
+ incoming.push({
69
+ link_id: link.id, direction: 'incoming',
70
+ linked_type: link.source_type, linked_id: link.source_id,
71
+ linked_title: sourceTitle, relation_type: link.relation_type,
72
+ confidence: link.confidence, notes: link.notes,
73
+ });
74
+ }
75
+ }
76
+ const allLinks = [...outgoing, ...incoming];
77
+ return makeToolResponse({
78
+ entity_type: params.entity_type, entity_id: params.entity_id,
79
+ total_links: allLinks.length, outgoing_count: outgoing.length, incoming_count: incoming.length,
80
+ links: allLinks,
81
+ message: allLinks.length === 0
82
+ ? `No links found. Use link_suggest to find potential connections.`
83
+ : `Found ${allLinks.length} links (${outgoing.length} outgoing, ${incoming.length} incoming).`,
84
+ });
85
+ }
86
+ catch (error) {
87
+ return handleAdapterError(error, 'link_related');
88
+ }
89
+ }));
90
+ }
91
+ //# sourceMappingURL=link-related.js.map
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { DataAdapter } from '../../adapter/types.js';
3
+ export declare function registerLinkSuggest(server: McpServer, adapter: DataAdapter): void;
4
+ //# sourceMappingURL=link-suggest.d.ts.map
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Tool: link_suggest
3
+ *
4
+ * Find similar items and suggest links using keyword matching.
5
+ */
6
+ import { z } from 'zod';
7
+ import { makeToolResponse, handleAdapterError, withGracefulDegradation } from '../shared.js';
8
+ export function registerLinkSuggest(server, adapter) {
9
+ server.tool('link_suggest', 'Find knowledge items similar to a given item and suggest links. ' +
10
+ 'Uses text search to find related items. Returns matches with suggested relation types.', {
11
+ item_id: z.string().uuid().describe('UUID of the knowledge item to find suggestions for'),
12
+ limit: z.number().min(1).max(20).optional().default(5).describe('Max suggestions'),
13
+ }, withGracefulDegradation('knowledge', adapter, async (params) => {
14
+ try {
15
+ let sourceItem;
16
+ try {
17
+ sourceItem = await adapter.getOne('knowledge', params.item_id);
18
+ }
19
+ catch {
20
+ return makeToolResponse({ suggestions: [], message: `Item not found: ${params.item_id}` });
21
+ }
22
+ // Get already-linked IDs to exclude
23
+ const linkedIds = new Set();
24
+ try {
25
+ const out = await adapter.list('knowledge_links', {
26
+ filter: [[{ field: 'source_type', op: 'eq', value: 'knowledge' }, { field: 'source_id', op: 'eq', value: params.item_id }]],
27
+ page: { limit: 100, offset: 0 },
28
+ });
29
+ const inc = await adapter.list('knowledge_links', {
30
+ filter: [[{ field: 'target_type', op: 'eq', value: 'knowledge' }, { field: 'target_id', op: 'eq', value: params.item_id }]],
31
+ page: { limit: 100, offset: 0 },
32
+ });
33
+ for (const l of out.items) linkedIds.add(l.target_id);
34
+ for (const l of inc.items) linkedIds.add(l.source_id);
35
+ }
36
+ catch { /* knowledge_links may not exist */ }
37
+ const searchTerms = extractKeyTerms(sourceItem.title, sourceItem.content);
38
+ let suggestions = [];
39
+ if (searchTerms) {
40
+ const results = await adapter.textSearch('knowledge', searchTerms, {
41
+ limit: (params.limit ?? 5) + linkedIds.size + 1,
42
+ });
43
+ suggestions = results
44
+ .filter(item => item.id !== params.item_id && !linkedIds.has(item.id))
45
+ .slice(0, params.limit ?? 5)
46
+ .map(item => ({
47
+ id: item.id, type: item.type, title: item.title,
48
+ summary: item.summary ?? (item.content?.slice(0, 100) + '...'),
49
+ suggested_relation: suggestRelationType(sourceItem, item),
50
+ }));
51
+ }
52
+ return makeToolResponse({
53
+ source: { id: sourceItem.id, type: sourceItem.type, title: sourceItem.title },
54
+ suggestions, already_linked: linkedIds.size,
55
+ message: suggestions.length === 0
56
+ ? `No unlinked similar items found for "${sourceItem.title}".`
57
+ : `Found ${suggestions.length} suggestions. Use link_create to connect them.`,
58
+ });
59
+ }
60
+ catch (error) {
61
+ return handleAdapterError(error, 'link_suggest');
62
+ }
63
+ }));
64
+ }
65
+ function extractKeyTerms(title, content) {
66
+ const stopWords = new Set(['the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
67
+ 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could', 'should', 'may',
68
+ 'might', 'can', 'to', 'of', 'in', 'for', 'on', 'with', 'at', 'by', 'from', 'as',
69
+ 'and', 'but', 'or', 'not', 'so', 'if', 'when', 'how', 'what', 'which', 'who',
70
+ 'this', 'that', 'these', 'those', 'it', 'its']);
71
+ const text = `${title} ${(content ?? '').slice(0, 200)}`;
72
+ const words = text.toLowerCase().replace(/[^a-z0-9\s]/g, ' ').split(/\s+/)
73
+ .filter(w => w.length > 2 && !stopWords.has(w));
74
+ return [...new Set(words)].slice(0, 5).join(' ');
75
+ }
76
+ function suggestRelationType(source, target) {
77
+ if (source.type === target.type) return 'supports';
78
+ if ((source.type === 'lesson' && target.type === 'decision') || (source.type === 'decision' && target.type === 'lesson')) return 'derived_from';
79
+ if (source.type === 'insight' && target.type === 'pattern') return 'derived_from';
80
+ if (source.type === 'pattern' && target.type === 'insight') return 'example_of';
81
+ return 'supports';
82
+ }
83
+ //# sourceMappingURL=link-suggest.js.map
@@ -1,7 +1,7 @@
1
1
  /**
2
- * Tool registration — imports and calls all 35 register functions.
2
+ * Tool registration — imports and calls all 39 register functions.
3
3
  */
4
- // Memory tools (22)
4
+ // Memory tools (26)
5
5
  import { registerKnowledgeStore } from './memory/knowledge-store.js';
6
6
  import { registerKnowledgeRecall } from './memory/knowledge-recall.js';
7
7
  import { registerKnowledgeLearn } from './memory/knowledge-learn.js';
@@ -24,6 +24,10 @@ import { registerContactList } from './memory/contact-list.js';
24
24
  import { registerContactSearch } from './memory/contact-search.js';
25
25
  import { registerBrainStats } from './memory/brain-stats.js';
26
26
  import { registerBrainDecay } from './memory/brain-decay.js';
27
+ import { registerLinkCreate } from './memory/link-create.js';
28
+ import { registerLinkDelete } from './memory/link-delete.js';
29
+ import { registerLinkRelated } from './memory/link-related.js';
30
+ import { registerLinkSuggest } from './memory/link-suggest.js';
27
31
  // Setup tools (3)
28
32
  import { registerSetupStatus } from './setup/setup-status.js';
29
33
  import { registerSetupMigrate } from './setup/setup-migrate.js';
@@ -64,6 +68,10 @@ export function registerAllTools(server, adapter) {
64
68
  registerContactSearch(server, adapter);
65
69
  registerBrainStats(server, adapter);
66
70
  registerBrainDecay(server, adapter);
71
+ registerLinkCreate(server, adapter);
72
+ registerLinkDelete(server, adapter);
73
+ registerLinkRelated(server, adapter);
74
+ registerLinkSuggest(server, adapter);
67
75
  // Setup tools
68
76
  registerSetupStatus(server, adapter);
69
77
  registerSetupMigrate(server, adapter);
@@ -1 +1 @@
1
- {"version":3,"file":"setup-migrate.d.ts","sourceRoot":"","sources":["../../../src/tools/setup/setup-migrate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAmB1D,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAwDlF"}
1
+ {"version":3,"file":"setup-migrate.d.ts","sourceRoot":"","sources":["../../../src/tools/setup/setup-migrate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAqB1D,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAwDlF"}
@@ -19,6 +19,8 @@ const COLLECTION_SCHEMAS = [
19
19
  { name: 'blog_posts', description: 'Blog post content' },
20
20
  { name: 'email_queue', description: 'Email queue for sending' },
21
21
  { name: 'content_calendar', description: 'Content calendar entries' },
22
+ { name: 'newsletter_subscribers', description: 'Newsletter subscriber list' },
23
+ { name: 'affiliates', description: 'Affiliate partners and commissions' },
22
24
  ];
23
25
  export function registerSetupMigrate(server, adapter) {
24
26
  server.tool('setup_migrate', 'Create missing database collections/tables. Additive only — existing collections are skipped. For PocketBase, creates collections via API. For Supabase, reports SQL migrations to run manually.', {}, async () => {
@@ -1 +1 @@
1
- {"version":3,"file":"setup-migrate.js","sourceRoot":"","sources":["../../../src/tools/setup/setup-migrate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"setup-migrate.js","sourceRoot":"","sources":["../../../src/tools/setup/setup-migrate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEnE,iDAAiD;AACjD,MAAM,kBAAkB,GAAiD;IACvE,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,kEAAkE,EAAE;IACtG,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,+CAA+C,EAAE;IACnF,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,mBAAmB,EAAE;IACtD,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,wBAAwB,EAAE;IACxD,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,gCAAgC,EAAE;IAChE,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,qBAAqB,EAAE;IACxD,EAAE,IAAI,EAAE,gBAAgB,EAAE,WAAW,EAAE,uBAAuB,EAAE;IAChE,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,0BAA0B,EAAE;IAC7D,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,uBAAuB,EAAE;IAC3D,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,mBAAmB,EAAE;IACxD,EAAE,IAAI,EAAE,aAAa,EAAE,WAAW,EAAE,yBAAyB,EAAE;IAC/D,EAAE,IAAI,EAAE,kBAAkB,EAAE,WAAW,EAAE,0BAA0B,EAAE;IACrE,EAAE,IAAI,EAAE,wBAAwB,EAAE,WAAW,EAAE,4BAA4B,EAAE;IAC7E,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,oCAAoC,EAAE;CAC1E,CAAC;AAEF,MAAM,UAAU,oBAAoB,CAAC,MAAiB,EAAE,OAAoB;IAC1E,MAAM,CAAC,IAAI,CACT,eAAe,EACf,kMAAkM,EAClM,EAAE,EACF,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,OAAO,GAAa,EAAE,CAAC;YAC7B,MAAM,cAAc,GAAiD,EAAE,CAAC;YAExE,KAAK,MAAM,MAAM,IAAI,kBAAkB,EAAE,CAAC;gBACxC,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;oBAC3D,IAAI,MAAM,EAAE,CAAC;wBACX,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;wBAC1B,SAAS;oBACX,CAAC;oBAED,IAAI,OAAO,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;wBACnC,cAAc,CAAC,IAAI,CAAC;4BAClB,IAAI,EAAE,MAAM,CAAC,IAAI;4BACjB,WAAW,EAAE,yDAAyD;yBACvE,CAAC,CAAC;oBACL,CAAC;yBAAM,CAAC;wBACN,cAAc,CAAC,IAAI,CAAC;4BAClB,IAAI,EAAE,MAAM,CAAC,IAAI;4BACjB,WAAW,EAAE,kEAAkE;yBAChF,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;oBACjE,OAAO,CAAC,KAAK,CAAC,kCAAkC,MAAM,CAAC,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC;oBACrE,cAAc,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,sCAAsC,EAAE,CAAC,CAAC;gBAClG,CAAC;YACH,CAAC;YAED,OAAO,gBAAgB,CAAC;gBACtB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,QAAQ,EAAE,OAAO,CAAC,MAAM;gBACxB,eAAe,EAAE,cAAc,CAAC,MAAM;gBACtC,OAAO,EAAE;oBACP,QAAQ,EAAE,OAAO;oBACjB,eAAe,EAAE,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS;iBACxE;gBACD,OAAO,EAAE,OAAO,CAAC,OAAO,KAAK,YAAY;oBACvC,CAAC,CAAC,0BAA0B,OAAO,CAAC,MAAM,cAAc,cAAc,CAAC,MAAM,yFAAyF;oBACtK,CAAC,CAAC,0BAA0B,OAAO,CAAC,MAAM,cAAc,cAAc,CAAC,MAAM,6EAA6E;aAC7J,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;YAC/C,OAAO,iBAAiB,CACtB,kGAAkG,CACnG,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"setup-status.d.ts","sourceRoot":"","sources":["../../../src/tools/setup/setup-status.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAmB1D,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAgEjF"}
1
+ {"version":3,"file":"setup-status.d.ts","sourceRoot":"","sources":["../../../src/tools/setup/setup-status.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAqB1D,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAgEjF"}
@@ -17,6 +17,8 @@ const EXPECTED_COLLECTIONS = [
17
17
  'blog_posts',
18
18
  'email_queue',
19
19
  'content_calendar',
20
+ 'newsletter_subscribers',
21
+ 'affiliates',
20
22
  ];
21
23
  export function registerSetupStatus(server, adapter) {
22
24
  server.tool('setup_status', 'Check database connection status, list existing and missing collections, and report schema readiness.', {}, { readOnlyHint: true }, async () => {
@@ -1 +1 @@
1
- {"version":3,"file":"setup-status.js","sourceRoot":"","sources":["../../../src/tools/setup/setup-status.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEnE,MAAM,oBAAoB,GAAG;IAC3B,WAAW;IACX,WAAW;IACX,UAAU;IACV,OAAO;IACP,OAAO;IACP,UAAU;IACV,gBAAgB;IAChB,UAAU;IACV,WAAW;IACX,YAAY;IACZ,aAAa;IACb,kBAAkB;CACnB,CAAC;AAEF,MAAM,UAAU,mBAAmB,CAAC,MAAiB,EAAE,OAAoB;IACzE,MAAM,CAAC,IAAI,CACT,cAAc,EACd,uGAAuG,EACvG,EAAE,EACF,EAAE,YAAY,EAAE,IAAI,EAAE,EACtB,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,yCAAyC;YACzC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC;YACjD,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;YAEtC,MAAM,OAAO,GAAa,EAAE,CAAC;YAC7B,MAAM,OAAO,GAAa,EAAE,CAAC;YAE7B,KAAK,MAAM,UAAU,IAAI,oBAAoB,EAAE,CAAC;gBAC9C,IAAI,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;oBAChC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gBAC3B,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gBAC3B,CAAC;YACH,CAAC;YAED,8CAA8C;YAC9C,IAAI,aAAa,GAAkB,IAAI,CAAC;YACxC,IAAI,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;gBAChC,IAAI,CAAC;oBACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAiB,UAAU,EAAE;wBAC7D,MAAM,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;wBAC/D,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;qBAC9B,CAAC,CAAC;oBACH,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBAC7B,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAe,CAAC;oBACnD,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC;oBACP,yDAAyD;gBAC3D,CAAC;YACH,CAAC;YAED,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC;YAErC,OAAO,gBAAgB,CAAC;gBACtB,SAAS,EAAE,IAAI;gBACf,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,cAAc,EAAE,aAAa;gBAC7B,KAAK,EAAE,OAAO;gBACd,WAAW,EAAE;oBACX,OAAO;oBACP,OAAO;oBACP,cAAc,EAAE,oBAAoB,CAAC,MAAM;oBAC3C,aAAa,EAAE,OAAO,CAAC,MAAM;iBAC9B;gBACD,OAAO,EAAE,OAAO;oBACd,CAAC,CAAC,qCAAqC;oBACvC,CAAC,CAAC,uBAAuB,OAAO,CAAC,MAAM,mDAAmD;aAC7F,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,KAAK,CAAC,CAAC;YACzD,OAAO,iBAAiB,CACtB,kGAAkG,CACnG,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"setup-status.js","sourceRoot":"","sources":["../../../src/tools/setup/setup-status.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEnE,MAAM,oBAAoB,GAAG;IAC3B,WAAW;IACX,WAAW;IACX,UAAU;IACV,OAAO;IACP,OAAO;IACP,UAAU;IACV,gBAAgB;IAChB,UAAU;IACV,WAAW;IACX,YAAY;IACZ,aAAa;IACb,kBAAkB;IAClB,wBAAwB;IACxB,YAAY;CACb,CAAC;AAEF,MAAM,UAAU,mBAAmB,CAAC,MAAiB,EAAE,OAAoB;IACzE,MAAM,CAAC,IAAI,CACT,cAAc,EACd,uGAAuG,EACvG,EAAE,EACF,EAAE,YAAY,EAAE,IAAI,EAAE,EACtB,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,yCAAyC;YACzC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC;YACjD,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;YAEtC,MAAM,OAAO,GAAa,EAAE,CAAC;YAC7B,MAAM,OAAO,GAAa,EAAE,CAAC;YAE7B,KAAK,MAAM,UAAU,IAAI,oBAAoB,EAAE,CAAC;gBAC9C,IAAI,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;oBAChC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gBAC3B,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gBAC3B,CAAC;YACH,CAAC;YAED,8CAA8C;YAC9C,IAAI,aAAa,GAAkB,IAAI,CAAC;YACxC,IAAI,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;gBAChC,IAAI,CAAC;oBACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAiB,UAAU,EAAE;wBAC7D,MAAM,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;wBAC/D,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;qBAC9B,CAAC,CAAC;oBACH,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBAC7B,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAe,CAAC;oBACnD,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC;oBACP,yDAAyD;gBAC3D,CAAC;YACH,CAAC;YAED,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC;YAErC,OAAO,gBAAgB,CAAC;gBACtB,SAAS,EAAE,IAAI;gBACf,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,cAAc,EAAE,aAAa;gBAC7B,KAAK,EAAE,OAAO;gBACd,WAAW,EAAE;oBACX,OAAO;oBACP,OAAO;oBACP,cAAc,EAAE,oBAAoB,CAAC,MAAM;oBAC3C,aAAa,EAAE,OAAO,CAAC,MAAM;iBAC9B;gBACD,OAAO,EAAE,OAAO;oBACd,CAAC,CAAC,qCAAqC;oBACvC,CAAC,CAAC,uBAAuB,OAAO,CAAC,MAAM,mDAAmD;aAC7F,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,KAAK,CAAC,CAAC;YACzD,OAAO,iBAAiB,CACtB,kGAAkG,CACnG,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * PocketBase migration: knowledge_links collection
3
+ *
4
+ * Creates the knowledge_links collection for typed relationships
5
+ * between MemoryOS entities.
6
+ *
7
+ * Note: PocketBase does not support pgvector.
8
+ * Link suggestions use keyword-based text search fallback.
9
+ */
10
+ module.exports = {
11
+ async up(db) {
12
+ const collection = new Collection({
13
+ name: 'knowledge_links',
14
+ type: 'base',
15
+ schema: [
16
+ { name: 'owner_id', type: 'text', required: true, options: { maxSize: 100 } },
17
+ { name: 'source_type', type: 'text', required: true, options: { maxSize: 50 } },
18
+ { name: 'source_id', type: 'text', required: true, options: { maxSize: 36 } },
19
+ { name: 'target_type', type: 'text', required: true, options: { maxSize: 50 } },
20
+ { name: 'target_id', type: 'text', required: true, options: { maxSize: 36 } },
21
+ { name: 'relation_type', type: 'text', required: true, options: { maxSize: 50 } },
22
+ { name: 'confidence', type: 'number', options: { min: 0, max: 1 } },
23
+ { name: 'notes', type: 'text', options: { maxSize: 500 } },
24
+ { name: 'auto_suggested', type: 'bool' },
25
+ ],
26
+ indexes: [
27
+ 'CREATE INDEX idx_kl_source ON knowledge_links (owner_id, source_type, source_id)',
28
+ 'CREATE INDEX idx_kl_target ON knowledge_links (owner_id, target_type, target_id)',
29
+ 'CREATE UNIQUE INDEX idx_kl_unique ON knowledge_links (owner_id, source_type, source_id, target_type, target_id, relation_type)',
30
+ ],
31
+ });
32
+ return db.save(collection);
33
+ },
34
+ async down(db) {
35
+ const collection = await db.findCollectionByNameOrId('knowledge_links');
36
+ return db.delete(collection);
37
+ },
38
+ };
@@ -0,0 +1,72 @@
1
+ -- Migration 009: Align schema to battle-tested iwoszapar.com production tables
2
+ --
3
+ -- Changes:
4
+ -- 1. Convert text[] columns to jsonb (tags, skills_used, files_changed, options_considered)
5
+ -- 2. Add owner_id to all core tables (single default, enables multi-user later)
6
+ -- 3. Add metadata jsonb to tables that lack it
7
+ -- 4. Add knowledge-specific columns: source_file, decay_score, triggers
8
+ -- 5. Add decisions-specific columns: rationale, outcome_rating, session_id
9
+ -- 6. Make decisions.options_considered nullable (production data shows this)
10
+ -- 7. Make goals.timeframe nullable (not always known at creation time)
11
+
12
+ -- === KNOWLEDGE ===
13
+ -- Convert tags from text[] to jsonb
14
+ ALTER TABLE knowledge ALTER COLUMN tags TYPE jsonb USING to_jsonb(tags);
15
+ ALTER TABLE knowledge ALTER COLUMN tags SET DEFAULT '[]'::jsonb;
16
+ -- Add missing columns
17
+ ALTER TABLE knowledge ADD COLUMN IF NOT EXISTS owner_id text NOT NULL DEFAULT 'default';
18
+ ALTER TABLE knowledge ADD COLUMN IF NOT EXISTS metadata jsonb DEFAULT '{}'::jsonb;
19
+ ALTER TABLE knowledge ADD COLUMN IF NOT EXISTS source_file text;
20
+ ALTER TABLE knowledge ADD COLUMN IF NOT EXISTS decay_score numeric;
21
+ ALTER TABLE knowledge ADD COLUMN IF NOT EXISTS triggers jsonb;
22
+
23
+ -- === DECISIONS ===
24
+ -- Convert options_considered from text[] to jsonb
25
+ ALTER TABLE decisions ALTER COLUMN options_considered TYPE jsonb USING to_jsonb(options_considered);
26
+ ALTER TABLE decisions ALTER COLUMN options_considered DROP NOT NULL;
27
+ ALTER TABLE decisions ALTER COLUMN options_considered SET DEFAULT '[]'::jsonb;
28
+ -- Convert tags from text[] to jsonb
29
+ ALTER TABLE decisions ALTER COLUMN tags TYPE jsonb USING to_jsonb(tags);
30
+ ALTER TABLE decisions ALTER COLUMN tags SET DEFAULT '[]'::jsonb;
31
+ -- Add missing columns
32
+ ALTER TABLE decisions ADD COLUMN IF NOT EXISTS owner_id text NOT NULL DEFAULT 'default';
33
+ ALTER TABLE decisions ADD COLUMN IF NOT EXISTS metadata jsonb DEFAULT '{}'::jsonb;
34
+ ALTER TABLE decisions ADD COLUMN IF NOT EXISTS rationale text;
35
+ ALTER TABLE decisions ADD COLUMN IF NOT EXISTS outcome_rating text;
36
+ ALTER TABLE decisions ADD COLUMN IF NOT EXISTS session_id uuid;
37
+
38
+ -- === SESSIONS ===
39
+ -- Convert skills_used, files_changed from text[] to jsonb
40
+ ALTER TABLE sessions ALTER COLUMN skills_used TYPE jsonb USING to_jsonb(skills_used);
41
+ ALTER TABLE sessions ALTER COLUMN skills_used SET DEFAULT '[]'::jsonb;
42
+ ALTER TABLE sessions ALTER COLUMN files_changed TYPE jsonb USING to_jsonb(files_changed);
43
+ ALTER TABLE sessions ALTER COLUMN files_changed SET DEFAULT '[]'::jsonb;
44
+ -- Add missing columns
45
+ ALTER TABLE sessions ADD COLUMN IF NOT EXISTS owner_id text NOT NULL DEFAULT 'default';
46
+
47
+ -- === GOALS ===
48
+ -- Convert tags from text[] to jsonb
49
+ ALTER TABLE goals ALTER COLUMN tags TYPE jsonb USING to_jsonb(tags);
50
+ ALTER TABLE goals ALTER COLUMN tags SET DEFAULT '[]'::jsonb;
51
+ -- Make timeframe nullable (not always known)
52
+ ALTER TABLE goals ALTER COLUMN timeframe DROP NOT NULL;
53
+ -- Add missing columns
54
+ ALTER TABLE goals ADD COLUMN IF NOT EXISTS owner_id text NOT NULL DEFAULT 'default';
55
+ ALTER TABLE goals ADD COLUMN IF NOT EXISTS metadata jsonb DEFAULT '{}'::jsonb;
56
+
57
+ -- === TASKS ===
58
+ -- Convert tags from text[] to jsonb
59
+ ALTER TABLE tasks ALTER COLUMN tags TYPE jsonb USING to_jsonb(tags);
60
+ ALTER TABLE tasks ALTER COLUMN tags SET DEFAULT '[]'::jsonb;
61
+ -- Add missing columns
62
+ ALTER TABLE tasks ADD COLUMN IF NOT EXISTS owner_id text NOT NULL DEFAULT 'default';
63
+ ALTER TABLE tasks ADD COLUMN IF NOT EXISTS metadata jsonb DEFAULT '{}'::jsonb;
64
+
65
+ -- === CONTACTS ===
66
+ -- Convert tags from text[] to jsonb
67
+ ALTER TABLE contacts ALTER COLUMN tags TYPE jsonb USING to_jsonb(tags);
68
+ ALTER TABLE contacts ALTER COLUMN tags SET DEFAULT '[]'::jsonb;
69
+ -- Add missing columns
70
+ ALTER TABLE contacts ADD COLUMN IF NOT EXISTS owner_id text NOT NULL DEFAULT 'default';
71
+ ALTER TABLE contacts ADD COLUMN IF NOT EXISTS metadata jsonb DEFAULT '{}'::jsonb;
72
+ ALTER TABLE contacts ADD COLUMN IF NOT EXISTS last_interaction_at timestamptz;
@@ -0,0 +1,47 @@
1
+ -- Migration 010: Knowledge Links + pgvector embeddings
2
+ -- Adds graph-lite relationship system between MemoryOS entities
3
+
4
+ -- 1. Enable pgvector extension
5
+ CREATE EXTENSION IF NOT EXISTS vector;
6
+
7
+ -- 2. Add embedding column to knowledge
8
+ ALTER TABLE knowledge ADD COLUMN IF NOT EXISTS embedding vector(384);
9
+
10
+ -- 3. HNSW index for cosine similarity
11
+ CREATE INDEX IF NOT EXISTS idx_knowledge_embedding
12
+ ON knowledge USING hnsw (embedding vector_cosine_ops)
13
+ WITH (m = 16, ef_construction = 64);
14
+
15
+ -- 4. Knowledge links table
16
+ CREATE TABLE IF NOT EXISTS knowledge_links (
17
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
18
+ owner_id text NOT NULL DEFAULT 'default',
19
+
20
+ source_type text NOT NULL,
21
+ source_id uuid NOT NULL,
22
+ target_type text NOT NULL,
23
+ target_id uuid NOT NULL,
24
+ relation_type text NOT NULL,
25
+
26
+ confidence numeric DEFAULT 0.8 CHECK (confidence >= 0 AND confidence <= 1),
27
+ notes text,
28
+ auto_suggested boolean DEFAULT false,
29
+ created_at timestamptz DEFAULT now(),
30
+
31
+ CONSTRAINT knowledge_links_source_type_check CHECK (source_type IN ('knowledge', 'decision', 'session', 'blog_post', 'prospect', 'agent_learning')),
32
+ CONSTRAINT knowledge_links_target_type_check CHECK (target_type IN ('knowledge', 'decision', 'session', 'blog_post', 'prospect', 'agent_learning')),
33
+ CONSTRAINT knowledge_links_relation_type_check CHECK (relation_type IN ('supports', 'contradicts', 'derived_from', 'example_of', 'supersedes', 'part_of', 'prerequisite')),
34
+ CONSTRAINT knowledge_links_no_self_link CHECK (NOT (source_type = target_type AND source_id = target_id)),
35
+ CONSTRAINT knowledge_links_unique_link UNIQUE (owner_id, source_type, source_id, target_type, target_id, relation_type)
36
+ );
37
+
38
+ -- 5. Indexes
39
+ CREATE INDEX idx_knowledge_links_source ON knowledge_links (owner_id, source_type, source_id);
40
+ CREATE INDEX idx_knowledge_links_target ON knowledge_links (owner_id, target_type, target_id);
41
+ CREATE INDEX idx_knowledge_links_relation ON knowledge_links (relation_type);
42
+
43
+ -- 6. RLS
44
+ ALTER TABLE knowledge_links ENABLE ROW LEVEL SECURITY;
45
+
46
+ CREATE POLICY "owner_all_access" ON knowledge_links
47
+ FOR ALL USING (owner_id = current_setting('app.owner_id', true) OR current_setting('app.owner_id', true) IS NULL);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iwo-szapar/data-mcp",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Unified data MCP server for Second Brain \u2014 PocketBase and Supabase adapters",
5
5
  "type": "module",
6
6
  "bin": {