fushiguro-mcp 0.1.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/LICENSE +21 -0
- package/README.md +278 -0
- package/catalog/agents/backend-api.md +71 -0
- package/catalog/agents/content-editor.md +59 -0
- package/catalog/agents/customer-support.md +60 -0
- package/catalog/agents/data-analyst.md +66 -0
- package/catalog/agents/database.md +65 -0
- package/catalog/agents/docs.md +52 -0
- package/catalog/agents/process-automation.md +61 -0
- package/catalog/agents/research-analyst.md +57 -0
- package/catalog/agents/security.md +68 -0
- package/catalog/agents/testing.md +63 -0
- package/catalog/agents/ux-ui-specialist.md +87 -0
- package/catalog/connectors/crm.md +24 -0
- package/catalog/connectors/data-warehouse.md +26 -0
- package/catalog/connectors/document-store.md +23 -0
- package/catalog/connectors/helpdesk.md +24 -0
- package/catalog/knowledge/platform/using-this-catalog.md +50 -0
- package/catalog/runbooks/_TEMPLATE.md +52 -0
- package/catalog/runbooks/ai-use-case-intake.md +89 -0
- package/catalog/runbooks/change-release.md +54 -0
- package/catalog/runbooks/customer-escalation.md +67 -0
- package/catalog/skills/cite-sources.md +18 -0
- package/catalog/skills/clarify-scope.md +18 -0
- package/catalog/skills/data-quality-check.md +22 -0
- package/catalog/skills/risk-and-compliance-check.md +21 -0
- package/catalog/skills/stakeholder-summary.md +21 -0
- package/catalog/tools/knowledge-search.md +14 -0
- package/catalog/tools/shell.md +15 -0
- package/catalog/tools/web-fetch.md +15 -0
- package/catalog/tools/web-search.md +15 -0
- package/catalog/topics/ai-adoption.md +25 -0
- package/catalog/topics/customer-operations.md +21 -0
- package/catalog/topics/data-and-reporting.md +20 -0
- package/catalog/topics/marketing-content.md +19 -0
- package/catalog/topics/product-engineering.md +20 -0
- package/dist/catalog.d.ts +34 -0
- package/dist/catalog.js +412 -0
- package/dist/catalog.js.map +1 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.js +48 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +5 -0
- package/dist/init.js +159 -0
- package/dist/init.js.map +1 -0
- package/dist/knowledge.d.ts +31 -0
- package/dist/knowledge.js +126 -0
- package/dist/knowledge.js.map +1 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +22 -0
- package/dist/main.js.map +1 -0
- package/dist/memory.d.ts +85 -0
- package/dist/memory.js +374 -0
- package/dist/memory.js.map +1 -0
- package/dist/quiet.d.ts +1 -0
- package/dist/quiet.js +21 -0
- package/dist/quiet.js.map +1 -0
- package/dist/registry.d.ts +15 -0
- package/dist/registry.js +128 -0
- package/dist/registry.js.map +1 -0
- package/dist/router.d.ts +55 -0
- package/dist/router.js +358 -0
- package/dist/router.js.map +1 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.js +529 -0
- package/dist/server.js.map +1 -0
- package/dist/text.d.ts +20 -0
- package/dist/text.js +72 -0
- package/dist/text.js.map +1 -0
- package/dist/types.d.ts +195 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +57 -0
package/dist/init.js
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
5
|
+
const DIRS = [
|
|
6
|
+
"agents",
|
|
7
|
+
"skills",
|
|
8
|
+
"knowledge",
|
|
9
|
+
"runbooks",
|
|
10
|
+
"topics",
|
|
11
|
+
"tools",
|
|
12
|
+
"connectors",
|
|
13
|
+
];
|
|
14
|
+
const CATALOG_README = `# <Your business>'s Fushiguro catalog
|
|
15
|
+
|
|
16
|
+
This directory is your organisation's AI operating layer. It layers over the
|
|
17
|
+
catalog that ships with the server: an entry here with the same \`name\` as a
|
|
18
|
+
packaged one **merges into it** — your scalar fields win, list fields union, and
|
|
19
|
+
your body is appended. Nothing is forked, so you keep receiving improvements to
|
|
20
|
+
the base prompts underneath your own additions.
|
|
21
|
+
|
|
22
|
+
Commit this directory. It is a shared business asset, and it is the part that
|
|
23
|
+
makes answers specific to you rather than generic.
|
|
24
|
+
|
|
25
|
+
## Where to start
|
|
26
|
+
|
|
27
|
+
1. **\`knowledge/\`** — highest leverage by a wide margin. Drop your real policy
|
|
28
|
+
and product documents into a folder per collection (\`knowledge/policies/\`,
|
|
29
|
+
\`knowledge/product/\`). Plain markdown; frontmatter is optional here.
|
|
30
|
+
2. **\`connectors/\`** — describe the systems you actually use, and delete the
|
|
31
|
+
packaged ones you do not. Record env var **names**, never secret values.
|
|
32
|
+
3. **\`runbooks/\`** — the packaged runbooks are templates. Copy
|
|
33
|
+
\`runbook-template.md\` and fill in one real process. Escalation is usually
|
|
34
|
+
the right first one.
|
|
35
|
+
4. **\`agents/\`** — extend a packaged agent with your house rules, or add a role
|
|
36
|
+
the base set does not cover.
|
|
37
|
+
|
|
38
|
+
Run the \`status\` tool at any time to see what is loaded and what is still an
|
|
39
|
+
unfilled template.
|
|
40
|
+
|
|
41
|
+
## Extending a packaged agent
|
|
42
|
+
|
|
43
|
+
Create \`agents/customer-support.md\` with only what you are adding:
|
|
44
|
+
|
|
45
|
+
\`\`\`markdown
|
|
46
|
+
---
|
|
47
|
+
name: customer-support
|
|
48
|
+
model: opus
|
|
49
|
+
keywords: [overage, seat, enterprise contract]
|
|
50
|
+
connectors: [our-helpdesk]
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## How we do this here
|
|
54
|
+
|
|
55
|
+
Our plans are Starter, Pro, and Enterprise, and entitlement differs sharply
|
|
56
|
+
between them. Always confirm the plan before quoting any policy window.
|
|
57
|
+
\`\`\`
|
|
58
|
+
|
|
59
|
+
Add \`override: true\` to the frontmatter to replace the packaged body outright
|
|
60
|
+
instead of appending to it.
|
|
61
|
+
|
|
62
|
+
## What does NOT go here
|
|
63
|
+
|
|
64
|
+
Personal working preferences — how one person likes their code formatted, or
|
|
65
|
+
their tone. Those are captured per-user through the \`remember\` tool and stored
|
|
66
|
+
in that person's own memory database. This catalog is for rules that apply to
|
|
67
|
+
everyone.
|
|
68
|
+
`;
|
|
69
|
+
const KNOWLEDGE_PLACEHOLDER = `---
|
|
70
|
+
name: example-policy
|
|
71
|
+
title: Example policy — replace this
|
|
72
|
+
collection: policies
|
|
73
|
+
reviewed: REPLACE-WITH-DATE
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
# Replace this file with a real policy document
|
|
77
|
+
|
|
78
|
+
Everything in this directory is indexed and retrieved for agents to cite. This
|
|
79
|
+
placeholder exists so retrieval has something to return before you have added
|
|
80
|
+
anything real — delete it once you have.
|
|
81
|
+
|
|
82
|
+
Knowledge files are ordinary markdown. Headings matter: they become the citation
|
|
83
|
+
trail an agent shows when it uses a passage, so write headings that identify the
|
|
84
|
+
rule rather than generic ones like "Overview".
|
|
85
|
+
|
|
86
|
+
Good documents to add first, in rough order of value:
|
|
87
|
+
|
|
88
|
+
- Refund, cancellation, and entitlement policy, per plan
|
|
89
|
+
- Escalation and approval thresholds — who can authorise what
|
|
90
|
+
- Product behaviour that support is repeatedly asked to explain
|
|
91
|
+
- Brand voice and writing guidance
|
|
92
|
+
- Metric definitions, so one metric keeps one meaning
|
|
93
|
+
|
|
94
|
+
Aim for the document a new hire would need on their first week. If it is not
|
|
95
|
+
written down anywhere, an agent cannot be told to follow it.
|
|
96
|
+
`;
|
|
97
|
+
/**
|
|
98
|
+
* Scaffolds a business's own catalog. Never overwrites an existing file, so it
|
|
99
|
+
* is safe to re-run after the server has been upgraded.
|
|
100
|
+
*/
|
|
101
|
+
export function init(targetArg) {
|
|
102
|
+
const catalogDir = resolve(targetArg ?? join(process.cwd(), ".fushiguro", "catalog"));
|
|
103
|
+
const results = [];
|
|
104
|
+
const write = (path, contents) => {
|
|
105
|
+
if (existsSync(path)) {
|
|
106
|
+
results.push({ path, status: "skipped" });
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
110
|
+
writeFileSync(path, contents);
|
|
111
|
+
results.push({ path, status: "created" });
|
|
112
|
+
};
|
|
113
|
+
for (const dir of DIRS)
|
|
114
|
+
mkdirSync(join(catalogDir, dir), { recursive: true });
|
|
115
|
+
mkdirSync(join(catalogDir, "knowledge", "policies"), { recursive: true });
|
|
116
|
+
write(join(catalogDir, "README.md"), CATALOG_README);
|
|
117
|
+
write(join(catalogDir, "knowledge", "policies", "example-policy.md"), KNOWLEDGE_PLACEHOLDER);
|
|
118
|
+
// Give them the runbook template locally, so filling one in needs no lookup.
|
|
119
|
+
// The copy gets its own name — sharing one with a packaged entry would merge
|
|
120
|
+
// the two rather than starting a new runbook.
|
|
121
|
+
const templateSource = join(packageRoot, "catalog", "runbooks", "_TEMPLATE.md");
|
|
122
|
+
if (existsSync(templateSource)) {
|
|
123
|
+
const template = readFileSync(templateSource, "utf8")
|
|
124
|
+
.replace(/^name: .*$/m, "name: my-first-runbook")
|
|
125
|
+
.replace(/^title: .*$/m, "title: My First Runbook — rename this")
|
|
126
|
+
.replace(/^description: .*$/m, "description: Replace this with what the process does. Rename the file and the name field to match.");
|
|
127
|
+
write(join(catalogDir, "runbooks", "my-first-runbook.md"), template);
|
|
128
|
+
}
|
|
129
|
+
const created = results.filter((r) => r.status === "created");
|
|
130
|
+
const skipped = results.filter((r) => r.status === "skipped");
|
|
131
|
+
const show = (p) => relative(process.cwd(), p) || p;
|
|
132
|
+
process.stdout.write(`\nCatalog ready at ${show(catalogDir)}\n\n`);
|
|
133
|
+
for (const r of created)
|
|
134
|
+
process.stdout.write(` created ${show(r.path)}\n`);
|
|
135
|
+
for (const r of skipped)
|
|
136
|
+
process.stdout.write(` kept ${show(r.path)} (already existed)\n`);
|
|
137
|
+
process.stdout.write([
|
|
138
|
+
"",
|
|
139
|
+
"Next:",
|
|
140
|
+
"",
|
|
141
|
+
" 1. Register the server so your whole team gets it from the repo:",
|
|
142
|
+
"",
|
|
143
|
+
" claude mcp add fushiguro --scope project -- npx -y fushiguro-mcp",
|
|
144
|
+
"",
|
|
145
|
+
" That writes .mcp.json. Commit it, and teammates are prompted to",
|
|
146
|
+
" enable the server the next time they open the repo.",
|
|
147
|
+
"",
|
|
148
|
+
` 2. Put your real policy and product documents in ${show(join(catalogDir, "knowledge"))}/`,
|
|
149
|
+
" That is the highest-leverage thing in the catalog by a wide margin.",
|
|
150
|
+
"",
|
|
151
|
+
" 3. Fill in one runbook, then set `template: false` in its frontmatter.",
|
|
152
|
+
"",
|
|
153
|
+
" 4. Commit the catalog. It is a shared business asset.",
|
|
154
|
+
"",
|
|
155
|
+
"Run the `status` tool once connected to see what loaded.",
|
|
156
|
+
"",
|
|
157
|
+
].join("\n"));
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=init.js.map
|
package/dist/init.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { KnowledgeChunk } from "./types.js";
|
|
2
|
+
export interface ChunkHit extends KnowledgeChunk {
|
|
3
|
+
/** Relevance, higher is better. Comparable within one search only. */
|
|
4
|
+
score: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Full-text index over the knowledge corpus. The whole index is rebuilt when the
|
|
8
|
+
* corpus hash changes — the corpus is small enough that incremental updates would
|
|
9
|
+
* cost more in complexity than they save in time.
|
|
10
|
+
*/
|
|
11
|
+
export declare class KnowledgeIndex {
|
|
12
|
+
#private;
|
|
13
|
+
constructor(dbPath: string);
|
|
14
|
+
close(): void;
|
|
15
|
+
/** Rebuilds the index if the corpus changed. Returns true when it rebuilt. */
|
|
16
|
+
sync(chunks: KnowledgeChunk[], signature: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Ranked chunk retrieval. `collections` narrows the corpus to what an agent is
|
|
19
|
+
* allowed to see; a heading hit counts for more than a body hit, since headings
|
|
20
|
+
* are the terms the author chose deliberately.
|
|
21
|
+
*/
|
|
22
|
+
search(query: string, options?: {
|
|
23
|
+
collections?: string[];
|
|
24
|
+
docs?: string[];
|
|
25
|
+
limit?: number;
|
|
26
|
+
}): ChunkHit[];
|
|
27
|
+
stats(): {
|
|
28
|
+
chunks: number;
|
|
29
|
+
collections: Record<string, number>;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { mkdirSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { DatabaseSync } from "node:sqlite";
|
|
4
|
+
import { ftsQuery } from "./text.js";
|
|
5
|
+
/**
|
|
6
|
+
* Full-text index over the knowledge corpus. The whole index is rebuilt when the
|
|
7
|
+
* corpus hash changes — the corpus is small enough that incremental updates would
|
|
8
|
+
* cost more in complexity than they save in time.
|
|
9
|
+
*/
|
|
10
|
+
export class KnowledgeIndex {
|
|
11
|
+
#db;
|
|
12
|
+
constructor(dbPath) {
|
|
13
|
+
if (dbPath !== ":memory:")
|
|
14
|
+
mkdirSync(dirname(dbPath), { recursive: true });
|
|
15
|
+
this.#db = new DatabaseSync(dbPath);
|
|
16
|
+
this.#db.exec("PRAGMA journal_mode = WAL");
|
|
17
|
+
this.#db.exec(`
|
|
18
|
+
CREATE TABLE IF NOT EXISTS knowledge_meta (
|
|
19
|
+
key TEXT PRIMARY KEY,
|
|
20
|
+
value TEXT NOT NULL
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
CREATE TABLE IF NOT EXISTS knowledge_chunks (
|
|
24
|
+
rowid_key INTEGER PRIMARY KEY,
|
|
25
|
+
id TEXT NOT NULL UNIQUE,
|
|
26
|
+
doc_name TEXT NOT NULL,
|
|
27
|
+
collection TEXT NOT NULL,
|
|
28
|
+
heading TEXT NOT NULL DEFAULT '',
|
|
29
|
+
text TEXT NOT NULL,
|
|
30
|
+
idx INTEGER NOT NULL
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
CREATE INDEX IF NOT EXISTS idx_chunks_collection ON knowledge_chunks(collection);
|
|
34
|
+
|
|
35
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS knowledge_fts
|
|
36
|
+
USING fts5(text, heading, doc_name, tokenize = 'porter unicode61');
|
|
37
|
+
`);
|
|
38
|
+
}
|
|
39
|
+
close() {
|
|
40
|
+
this.#db.close();
|
|
41
|
+
}
|
|
42
|
+
#signature() {
|
|
43
|
+
const row = this.#db
|
|
44
|
+
.prepare("SELECT value FROM knowledge_meta WHERE key = 'signature'")
|
|
45
|
+
.get();
|
|
46
|
+
return row?.value ?? "";
|
|
47
|
+
}
|
|
48
|
+
/** Rebuilds the index if the corpus changed. Returns true when it rebuilt. */
|
|
49
|
+
sync(chunks, signature) {
|
|
50
|
+
if (signature === this.#signature())
|
|
51
|
+
return false;
|
|
52
|
+
this.#db.exec("BEGIN");
|
|
53
|
+
try {
|
|
54
|
+
this.#db.exec("DELETE FROM knowledge_chunks");
|
|
55
|
+
this.#db.exec("DELETE FROM knowledge_fts");
|
|
56
|
+
const insertChunk = this.#db.prepare(`INSERT INTO knowledge_chunks (id, doc_name, collection, heading, text, idx)
|
|
57
|
+
VALUES (?, ?, ?, ?, ?, ?)`);
|
|
58
|
+
const insertFts = this.#db.prepare("INSERT INTO knowledge_fts (rowid, text, heading, doc_name) VALUES (?, ?, ?, ?)");
|
|
59
|
+
for (const chunk of chunks) {
|
|
60
|
+
const result = insertChunk.run(chunk.id, chunk.docName, chunk.collection, chunk.heading, chunk.text, chunk.index);
|
|
61
|
+
insertFts.run(Number(result.lastInsertRowid), chunk.text, chunk.heading, chunk.docName);
|
|
62
|
+
}
|
|
63
|
+
this.#db
|
|
64
|
+
.prepare(`INSERT INTO knowledge_meta (key, value) VALUES ('signature', ?)
|
|
65
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value`)
|
|
66
|
+
.run(signature);
|
|
67
|
+
this.#db.exec("COMMIT");
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
this.#db.exec("ROLLBACK");
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Ranked chunk retrieval. `collections` narrows the corpus to what an agent is
|
|
77
|
+
* allowed to see; a heading hit counts for more than a body hit, since headings
|
|
78
|
+
* are the terms the author chose deliberately.
|
|
79
|
+
*/
|
|
80
|
+
search(query, options = {}) {
|
|
81
|
+
const match = ftsQuery(query);
|
|
82
|
+
if (!match)
|
|
83
|
+
return [];
|
|
84
|
+
const limit = Math.min(50, Math.max(1, options.limit ?? 6));
|
|
85
|
+
const clauses = ["knowledge_fts MATCH ?"];
|
|
86
|
+
const params = [match];
|
|
87
|
+
if (options.collections?.length) {
|
|
88
|
+
clauses.push(`c.collection IN (${options.collections.map(() => "?").join(", ")})`);
|
|
89
|
+
params.push(...options.collections);
|
|
90
|
+
}
|
|
91
|
+
if (options.docs?.length) {
|
|
92
|
+
clauses.push(`c.doc_name IN (${options.docs.map(() => "?").join(", ")})`);
|
|
93
|
+
params.push(...options.docs);
|
|
94
|
+
}
|
|
95
|
+
params.push(limit);
|
|
96
|
+
const rows = this.#db
|
|
97
|
+
.prepare(`SELECT c.id, c.doc_name, c.collection, c.heading, c.text, c.idx,
|
|
98
|
+
-bm25(knowledge_fts, 1.0, 2.0, 1.0) AS score
|
|
99
|
+
FROM knowledge_fts f
|
|
100
|
+
JOIN knowledge_chunks c ON c.rowid_key = f.rowid
|
|
101
|
+
WHERE ${clauses.join(" AND ")}
|
|
102
|
+
ORDER BY score DESC
|
|
103
|
+
LIMIT ?`)
|
|
104
|
+
.all(...params);
|
|
105
|
+
return rows.map((row) => ({
|
|
106
|
+
id: row.id,
|
|
107
|
+
docName: row.doc_name,
|
|
108
|
+
collection: row.collection,
|
|
109
|
+
heading: row.heading,
|
|
110
|
+
text: row.text,
|
|
111
|
+
index: row.idx,
|
|
112
|
+
score: Number(row.score.toFixed(3)),
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
stats() {
|
|
116
|
+
const total = Number(this.#db.prepare("SELECT COUNT(*) AS n FROM knowledge_chunks").get().n);
|
|
117
|
+
const collections = {};
|
|
118
|
+
for (const row of this.#db
|
|
119
|
+
.prepare("SELECT collection, COUNT(*) AS n FROM knowledge_chunks GROUP BY collection")
|
|
120
|
+
.all()) {
|
|
121
|
+
collections[row.collection] = Number(row.n);
|
|
122
|
+
}
|
|
123
|
+
return { chunks: total, collections };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=knowledge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knowledge.js","sourceRoot":"","sources":["../src/knowledge.ts"],"names":[],"mappings":"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"}
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function main(): Promise<void>;
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2
|
+
import { loadConfig } from "./config.js";
|
|
3
|
+
import { createServer } from "./server.js";
|
|
4
|
+
export async function main() {
|
|
5
|
+
const config = loadConfig();
|
|
6
|
+
const { server, close } = createServer(config);
|
|
7
|
+
const shutdown = async () => {
|
|
8
|
+
try {
|
|
9
|
+
await server.close();
|
|
10
|
+
}
|
|
11
|
+
finally {
|
|
12
|
+
close();
|
|
13
|
+
}
|
|
14
|
+
process.exit(0);
|
|
15
|
+
};
|
|
16
|
+
process.on("SIGINT", shutdown);
|
|
17
|
+
process.on("SIGTERM", shutdown);
|
|
18
|
+
await server.connect(new StdioServerTransport());
|
|
19
|
+
// stdout carries the protocol; anything human-readable must go to stderr.
|
|
20
|
+
process.stderr.write(`fushiguro-mcp ready — base: ${config.baseCatalogDir}, custom: ${config.customCatalogDir}, memory: ${config.dbPath}\n`);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,CAAC,KAAK,UAAU,IAAI;IACxB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAE/C,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;gBAAS,CAAC;YACT,KAAK,EAAE,CAAC;QACV,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAEhC,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;IACjD,0EAA0E;IAC1E,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,+BAA+B,MAAM,CAAC,cAAc,aAAa,MAAM,CAAC,gBAAgB,aAAa,MAAM,CAAC,MAAM,IAAI,CACvH,CAAC;AACJ,CAAC"}
|
package/dist/memory.d.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { Memory, MemoryKind, Outcome } from "./types.js";
|
|
2
|
+
export interface RememberInput {
|
|
3
|
+
text: string;
|
|
4
|
+
kind: MemoryKind;
|
|
5
|
+
scope: string;
|
|
6
|
+
agent: string | null;
|
|
7
|
+
why?: string | null;
|
|
8
|
+
confidence?: number;
|
|
9
|
+
source?: string | null;
|
|
10
|
+
}
|
|
11
|
+
export interface RememberResult {
|
|
12
|
+
id: number;
|
|
13
|
+
action: "created" | "reinforced" | "superseded";
|
|
14
|
+
/** Set when this write retired an older memory. */
|
|
15
|
+
supersededId?: number;
|
|
16
|
+
memory: Memory;
|
|
17
|
+
}
|
|
18
|
+
export interface RecallOptions {
|
|
19
|
+
query?: string;
|
|
20
|
+
/** Include memories for this specialist plus the agent-agnostic ones. */
|
|
21
|
+
agent?: string | null;
|
|
22
|
+
/** Scopes to search. Later entries do not outrank earlier ones. */
|
|
23
|
+
scopes?: string[];
|
|
24
|
+
limit?: number;
|
|
25
|
+
}
|
|
26
|
+
export declare class MemoryStore {
|
|
27
|
+
#private;
|
|
28
|
+
constructor(dbPath: string);
|
|
29
|
+
close(): void;
|
|
30
|
+
/**
|
|
31
|
+
* Writes a preference, folding it into an existing one when it says the same
|
|
32
|
+
* thing. Restating a preference reinforces it; a constraint or correction that
|
|
33
|
+
* restates a softer memory retires that memory instead of duplicating it.
|
|
34
|
+
*/
|
|
35
|
+
remember(input: RememberInput): RememberResult;
|
|
36
|
+
/**
|
|
37
|
+
* Ranked recall. Relevance comes from BM25 when there is a query, then gets
|
|
38
|
+
* scaled by kind weight and confidence so a hard constraint beats a passing
|
|
39
|
+
* remark even when the remark matches the words better.
|
|
40
|
+
*
|
|
41
|
+
* Constraints and corrections are also pulled in unconditionally, whether or
|
|
42
|
+
* not they match the query. A rule like "never use purple" that only surfaces
|
|
43
|
+
* when the user happens to say "purple" is not a rule — it has to be in front
|
|
44
|
+
* of the agent on the task where it would otherwise be broken.
|
|
45
|
+
*/
|
|
46
|
+
recall(options?: RecallOptions): Memory[];
|
|
47
|
+
/** Marks memories as used, so `memory_stats` can show what actually earns its keep. */
|
|
48
|
+
markUsed(ids: number[]): void;
|
|
49
|
+
get(id: number): Memory | null;
|
|
50
|
+
forget(id: number): boolean;
|
|
51
|
+
list(options?: {
|
|
52
|
+
agent?: string | null;
|
|
53
|
+
scope?: string;
|
|
54
|
+
limit?: number;
|
|
55
|
+
}): Memory[];
|
|
56
|
+
/**
|
|
57
|
+
* Records how a routing decision landed and nudges the term→agent affinity
|
|
58
|
+
* that the router consults next time. This is the loop that makes routing
|
|
59
|
+
* improve with use rather than staying frozen at whatever the keywords say.
|
|
60
|
+
*/
|
|
61
|
+
recordOutcome(input: {
|
|
62
|
+
task: string;
|
|
63
|
+
agent: string;
|
|
64
|
+
outcome: Outcome;
|
|
65
|
+
notes?: string | null;
|
|
66
|
+
correctAgent?: string | null;
|
|
67
|
+
}): {
|
|
68
|
+
decisionId: number;
|
|
69
|
+
adjusted: number;
|
|
70
|
+
};
|
|
71
|
+
/** Learned per-agent weight for the given terms, summed. */
|
|
72
|
+
affinity(terms: string[]): Map<string, number>;
|
|
73
|
+
stats(): {
|
|
74
|
+
total: number;
|
|
75
|
+
byKind: Record<string, number>;
|
|
76
|
+
byAgent: Record<string, number>;
|
|
77
|
+
superseded: number;
|
|
78
|
+
decisions: number;
|
|
79
|
+
topUsed: {
|
|
80
|
+
id: number;
|
|
81
|
+
text: string;
|
|
82
|
+
hits: number;
|
|
83
|
+
}[];
|
|
84
|
+
};
|
|
85
|
+
}
|