@wellgrow/mcp 0.1.0 → 0.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/README.md +0 -6
- package/dist/index.js +6 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,8 +23,6 @@ npm install -g @wellgrow/mcp
|
|
|
23
23
|
|------|------|------|
|
|
24
24
|
| `WELLGROW_EMAIL` | WellGrow のログインメール | Yes |
|
|
25
25
|
| `WELLGROW_PASSWORD` | WellGrow のログインパスワード | Yes |
|
|
26
|
-
| `WELLGROW_SUPABASE_URL` | Supabase プロジェクト URL | Yes |
|
|
27
|
-
| `WELLGROW_SUPABASE_ANON_KEY` | Supabase anon key | Yes |
|
|
28
26
|
| `OPENAI_API_KEY` | OpenAI API キー(検索の embedding 生成用) | Yes |
|
|
29
27
|
|
|
30
28
|
### 3. MCP サーバーの登録
|
|
@@ -35,8 +33,6 @@ npm install -g @wellgrow/mcp
|
|
|
35
33
|
claude mcp add --transport stdio \
|
|
36
34
|
--env WELLGROW_EMAIL=user@example.com \
|
|
37
35
|
--env WELLGROW_PASSWORD=mypassword \
|
|
38
|
-
--env WELLGROW_SUPABASE_URL=https://xxx.supabase.co \
|
|
39
|
-
--env WELLGROW_SUPABASE_ANON_KEY=eyJ... \
|
|
40
36
|
--env OPENAI_API_KEY=sk-... \
|
|
41
37
|
--scope user \
|
|
42
38
|
wellgrow -- wellgrow-mcp
|
|
@@ -54,8 +50,6 @@ claude mcp add --transport stdio \
|
|
|
54
50
|
"env": {
|
|
55
51
|
"WELLGROW_EMAIL": "user@example.com",
|
|
56
52
|
"WELLGROW_PASSWORD": "mypassword",
|
|
57
|
-
"WELLGROW_SUPABASE_URL": "https://xxx.supabase.co",
|
|
58
|
-
"WELLGROW_SUPABASE_ANON_KEY": "eyJ...",
|
|
59
53
|
"OPENAI_API_KEY": "sk-..."
|
|
60
54
|
}
|
|
61
55
|
}
|
package/dist/index.js
CHANGED
|
@@ -6,16 +6,14 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
6
6
|
|
|
7
7
|
// src/auth.ts
|
|
8
8
|
import { createClient } from "@supabase/supabase-js";
|
|
9
|
+
var DEFAULT_SUPABASE_URL = "https://rpywqbtporjdhwtmvwkf.supabase.co";
|
|
10
|
+
var DEFAULT_SUPABASE_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InJweXdxYnRwb3JqZGh3dG12d2tmIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Njc4NjA4MDIsImV4cCI6MjA4MzQzNjgwMn0.1nn9F2y1JZBoSgB33CnP-k-6OM5HcEMdUs50RLJ89i4";
|
|
9
11
|
var supabase = null;
|
|
10
12
|
function getSupabaseUrl() {
|
|
11
|
-
|
|
12
|
-
if (!url) throw new Error("WELLGROW_SUPABASE_URL is required");
|
|
13
|
-
return url;
|
|
13
|
+
return process.env.WELLGROW_SUPABASE_URL ?? DEFAULT_SUPABASE_URL;
|
|
14
14
|
}
|
|
15
15
|
function getSupabaseAnonKey() {
|
|
16
|
-
|
|
17
|
-
if (!key) throw new Error("WELLGROW_SUPABASE_ANON_KEY is required");
|
|
18
|
-
return key;
|
|
16
|
+
return process.env.WELLGROW_SUPABASE_ANON_KEY ?? DEFAULT_SUPABASE_ANON_KEY;
|
|
19
17
|
}
|
|
20
18
|
async function getSupabase() {
|
|
21
19
|
if (supabase) {
|
|
@@ -332,13 +330,7 @@ function registerQuestionsResource(server2) {
|
|
|
332
330
|
}
|
|
333
331
|
|
|
334
332
|
// src/index.ts
|
|
335
|
-
var required = [
|
|
336
|
-
"WELLGROW_EMAIL",
|
|
337
|
-
"WELLGROW_PASSWORD",
|
|
338
|
-
"WELLGROW_SUPABASE_URL",
|
|
339
|
-
"WELLGROW_SUPABASE_ANON_KEY",
|
|
340
|
-
"OPENAI_API_KEY"
|
|
341
|
-
];
|
|
333
|
+
var required = ["WELLGROW_EMAIL", "WELLGROW_PASSWORD", "OPENAI_API_KEY"];
|
|
342
334
|
for (const key of required) {
|
|
343
335
|
if (!process.env[key]) {
|
|
344
336
|
console.error(`Error: ${key} is required`);
|
|
@@ -356,7 +348,7 @@ try {
|
|
|
356
348
|
process.exit(1);
|
|
357
349
|
}
|
|
358
350
|
var server = new McpServer(
|
|
359
|
-
{ name: "wellgrow", version: "0.
|
|
351
|
+
{ name: "wellgrow", version: "0.2.0" },
|
|
360
352
|
{
|
|
361
353
|
instructions: `WellGrow \u306E\u30E6\u30FC\u30B6\u30FC\u30CA\u30EC\u30C3\u30B8\u30D9\u30FC\u30B9\u306B\u30A2\u30AF\u30BB\u30B9\u3059\u308B\u30B5\u30FC\u30D0\u30FC\u3067\u3059\u3002
|
|
362
354
|
\u30E6\u30FC\u30B6\u30FC\u306E\u8CEA\u554F\u30FB\u56DE\u7B54\u30C7\u30FC\u30BF\u306E\u691C\u7D22\u30FB\u95B2\u89A7\u30FB\u66F8\u304D\u8FBC\u307F\u304C\u3067\u304D\u307E\u3059\u3002
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/auth.ts","../src/tools/search-context.ts","../src/embedding.ts","../src/format.ts","../src/tools/answer-question.ts","../src/tools/list-questions.ts","../src/resources/questions.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { getSupabase } from \"./auth.js\";\nimport { registerSearchContextTool } from \"./tools/search-context.js\";\nimport { registerAnswerQuestionTool } from \"./tools/answer-question.js\";\nimport { registerListQuestionsTool } from \"./tools/list-questions.js\";\nimport { registerQuestionsResource } from \"./resources/questions.js\";\n\nconst required = [\n \"WELLGROW_EMAIL\",\n \"WELLGROW_PASSWORD\",\n \"WELLGROW_SUPABASE_URL\",\n \"WELLGROW_SUPABASE_ANON_KEY\",\n \"OPENAI_API_KEY\",\n];\n\nfor (const key of required) {\n if (!process.env[key]) {\n console.error(`Error: ${key} is required`);\n process.exit(1);\n }\n}\n\ntry {\n await getSupabase();\n console.error(\"Authenticated successfully\");\n} catch (error) {\n console.error(\n \"Authentication failed:\",\n error instanceof Error ? error.message : error\n );\n process.exit(1);\n}\n\nconst server = new McpServer(\n { name: \"wellgrow\", version: \"0.1.0\" },\n {\n instructions: `WellGrow のユーザーナレッジベースにアクセスするサーバーです。\nユーザーの質問・回答データの検索・閲覧・書き込みができます。\nユーザーの考えや価値観を理解したい場面で活用してください。`,\n }\n);\n\nregisterSearchContextTool(server);\nregisterAnswerQuestionTool(server);\nregisterListQuestionsTool(server);\nregisterQuestionsResource(server);\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\nconsole.error(\"WellGrow MCP server running on stdio\");\n","import { createClient, type SupabaseClient } from \"@supabase/supabase-js\";\n\nlet supabase: SupabaseClient | null = null;\n\nexport function getSupabaseUrl(): string {\n const url = process.env.WELLGROW_SUPABASE_URL;\n if (!url) throw new Error(\"WELLGROW_SUPABASE_URL is required\");\n return url;\n}\n\nexport function getSupabaseAnonKey(): string {\n const key = process.env.WELLGROW_SUPABASE_ANON_KEY;\n if (!key) throw new Error(\"WELLGROW_SUPABASE_ANON_KEY is required\");\n return key;\n}\n\nexport async function getSupabase(): Promise<SupabaseClient> {\n if (supabase) {\n const {\n data: { session },\n } = await supabase.auth.getSession();\n if (session) return supabase;\n }\n\n supabase = createClient(getSupabaseUrl(), getSupabaseAnonKey(), {\n auth: {\n autoRefreshToken: true,\n persistSession: false,\n },\n });\n\n const { error } = await supabase.auth.signInWithPassword({\n email: process.env.WELLGROW_EMAIL!,\n password: process.env.WELLGROW_PASSWORD!,\n });\n\n if (error) {\n throw new Error(`Authentication failed: ${error.message}`);\n }\n\n return supabase;\n}\n\nexport async function getUserId(): Promise<string> {\n const sb = await getSupabase();\n const {\n data: { user },\n } = await sb.auth.getUser();\n if (!user) throw new Error(\"Not authenticated\");\n return user.id;\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { getSupabase, getUserId } from \"../auth.js\";\nimport { generateEmbedding } from \"../embedding.js\";\nimport {\n formatSearchResult,\n type QuestionHit,\n type AnswerHit,\n} from \"../format.js\";\n\nexport function registerSearchContextTool(server: McpServer): void {\n server.registerTool(\n \"search_user_context\",\n {\n title: \"ユーザーコンテキスト検索\",\n description: `ユーザーの質問と回答のデータベースを検索します。\nユーザーの考え・価値観・経験・知識を理解するために使います。\n\n使用場面:\n- アドバイスや提案をする前に、ユーザーの価値観・原則を確認したいとき\n- 意思決定の場面で、過去の判断基準や経験を参照したいとき\n- 「私のスタイルで」「私らしく」など、ユーザーの好みを把握したいとき\n- ユーザーの背景(専門分野、関心、目標)を理解したいとき\n\n検索戦略(組み合わせ可能):\n- query のみ: セマンティック検索(意味的に近い内容を幅広く取得)\n- keywords を追加: ハイブリッド検索(意味的類似 + キーワード一致で精度向上)\n- tags/statuses/pinned: フィルタで結果を絞り込み\n\ntarget の使い分け:\n- \"questions\": 質問の一覧を見たいとき(最新回答も付属)\n- \"answers\": 回答の内容を重点的に調べたいとき\n- \"all\": 幅広く情報を集めたいとき(デフォルト)`,\n inputSchema: z.object({\n query: z.string().describe(\"検索クエリ(セマンティック検索に使用)\"),\n keywords: z\n .array(z.string())\n .optional()\n .describe(\"キーワード部分一致\"),\n target: z\n .enum([\"questions\", \"answers\", \"all\"])\n .default(\"all\")\n .describe(\"検索対象\"),\n tags: z.array(z.string()).optional().describe(\"タグでフィルタ\"),\n statuses: z\n .array(z.string())\n .optional()\n .describe(\"ステータスでフィルタ(省略時: active, paused)\"),\n pinned: z.boolean().optional().describe(\"ピン留めされた質問のみ\"),\n limit: z\n .number()\n .min(1)\n .max(50)\n .default(10)\n .describe(\"取得件数上限\"),\n }),\n annotations: {\n readOnlyHint: true,\n openWorldHint: true,\n },\n },\n async ({ query, keywords, target, tags, statuses, pinned, limit }) => {\n const supabase = await getSupabase();\n const userId = await getUserId();\n const embeddingStr = await generateEmbedding(query);\n\n const questions: QuestionHit[] = [];\n const answers: AnswerHit[] = [];\n const promises: PromiseLike<void>[] = [];\n\n if (target !== \"answers\") {\n promises.push(\n supabase\n .rpc(\"search_questions\", {\n p_user_id: userId,\n p_keywords: keywords ?? null,\n p_embedding: embeddingStr,\n p_vector_threshold: 0.3,\n p_statuses: statuses ?? [\"active\", \"paused\"],\n p_tags: tags ?? null,\n p_date_from: null,\n p_date_to: null,\n p_pinned: pinned ?? null,\n p_limit: limit ?? 10,\n })\n .then(({ data }) => {\n if (data) questions.push(...data);\n })\n );\n }\n\n if (target !== \"questions\") {\n promises.push(\n supabase\n .rpc(\"search_answers\", {\n p_user_id: userId,\n p_keywords: keywords ?? null,\n p_embedding: embeddingStr,\n p_vector_threshold: 0.3,\n p_sources: null,\n p_date_from: null,\n p_date_to: null,\n p_exclude_question_ids: null,\n p_limit: limit ?? 10,\n })\n .then(({ data }) => {\n if (data) answers.push(...data);\n })\n );\n }\n\n await Promise.all(promises);\n\n return {\n content: [\n {\n type: \"text\" as const,\n text: formatSearchResult({ questions, answers }),\n },\n ],\n };\n }\n );\n}\n","import { embed } from \"ai\";\nimport { createOpenAI } from \"@ai-sdk/openai\";\n\nconst openai = createOpenAI();\nconst embeddingModel = openai.embedding(\"text-embedding-3-small\");\n\nexport async function generateEmbedding(text: string): Promise<string> {\n const { embedding } = await embed({\n model: embeddingModel,\n value: text,\n });\n return JSON.stringify(embedding);\n}\n","export interface QuestionHit {\n id: string;\n question: string;\n tags: string[];\n importance: number | null;\n similarity: number | null;\n latest_answer: string | null;\n latest_answer_at: string | null;\n updated_at: string | null;\n}\n\nexport interface AnswerHit {\n answer_id: string;\n answer: string;\n answer_description: string | null;\n answer_source: string | null;\n similarity: number | null;\n answered_at: string;\n question_id: string;\n question: string;\n}\n\nexport interface QuestionListItem {\n id: string;\n question: string;\n tags: string[];\n status: string;\n importance: number | null;\n pinned: boolean;\n created_at: string;\n updated_at: string;\n}\n\nexport function formatSearchResult(result: {\n questions: QuestionHit[];\n answers: AnswerHit[];\n}): string {\n const parts: string[] = [];\n\n if (result.questions.length > 0) {\n parts.push(`## 質問 (${result.questions.length}件)`);\n for (const q of result.questions) {\n const tags = q.tags.length > 0 ? ` [${q.tags.join(\", \")}]` : \"\";\n const answer = q.latest_answer\n ? `\\n 最新回答: ${q.latest_answer}`\n : \"\";\n parts.push(`- **${q.question}**${tags}${answer}\\n ID: ${q.id}`);\n }\n }\n\n if (result.answers.length > 0) {\n parts.push(`## 回答 (${result.answers.length}件)`);\n for (const a of result.answers) {\n const desc = a.answer_description\n ? `\\n 説明: ${a.answer_description}`\n : \"\";\n parts.push(\n `- **${a.question}**\\n 回答: ${a.answer}${desc}\\n 回答日: ${a.answered_at}`\n );\n }\n }\n\n if (parts.length === 0) {\n return \"検索結果はありません。\";\n }\n\n return parts.join(\"\\n\\n\");\n}\n\nexport function formatQuestionList(questions: QuestionListItem[]): string {\n if (questions.length === 0) {\n return \"質問はありません。\";\n }\n\n const lines: string[] = [`## 質問一覧 (${questions.length}件)`];\n\n for (const q of questions) {\n const tags = q.tags.length > 0 ? ` [${q.tags.join(\", \")}]` : \"\";\n const pinned = q.pinned ? \" 📌\" : \"\";\n const status = q.status !== \"active\" ? ` (${q.status})` : \"\";\n lines.push(\n `- **${q.question}**${tags}${pinned}${status}\\n ID: ${q.id} | 更新: ${q.updated_at}`\n );\n }\n\n return lines.join(\"\\n\\n\");\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { getSupabase, getUserId } from \"../auth.js\";\n\nexport function registerAnswerQuestionTool(server: McpServer): void {\n server.registerTool(\n \"answer_question\",\n {\n title: \"質問への回答書き込み\",\n description: `ユーザーの質問に対して回答を書き込みます。\nユーザーとの会話で重要な気づきや洞察が生まれた時に使います。\n\n使用前に必ずユーザーの承認を得てください。\n\n【回答のルール】\n- answer は140文字以内で、端的に核心を突く回答にする\n- ユーザー自身の言葉を活かした回答にする\n- description には回答の背景・根拠・詳細な説明を書く\n- question_id は search_user_context や list_questions で取得した ID を使う`,\n inputSchema: z.object({\n question_id: z.string().describe(\"対象の質問 ID\"),\n answer: z.string().max(140).describe(\"回答(140文字以内)\"),\n description: z\n .string()\n .max(300)\n .optional()\n .describe(\"回答の詳細説明(300文字以内)\"),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n },\n },\n async ({ question_id, answer, description }) => {\n const supabase = await getSupabase();\n const userId = await getUserId();\n\n const { error: insertError } = await supabase.from(\"answers\").insert({\n question_id,\n user_id: userId,\n answer,\n description: description ?? null,\n source: \"mcp\",\n });\n\n if (insertError)\n throw new Error(`回答の保存に失敗: ${insertError.message}`);\n\n const { data: questionData } = await supabase\n .from(\"questions\")\n .select(\"question\")\n .eq(\"id\", question_id)\n .single();\n\n const questionText = questionData?.question ?? question_id;\n\n return {\n content: [\n {\n type: \"text\" as const,\n text: `回答を保存しました:\\n質問: ${questionText}\\n回答: ${answer}`,\n },\n ],\n };\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { getSupabase } from \"../auth.js\";\nimport { formatQuestionList } from \"../format.js\";\n\nexport function registerListQuestionsTool(server: McpServer): void {\n server.registerTool(\n \"list_questions\",\n {\n title: \"質問一覧取得\",\n description: `ユーザーの質問一覧を取得します。ステータスやタグでフィルタ可能。\n質問の ID を確認したいときや、ユーザーがどんな質問を持っているか把握したいときに使います。\nanswer_question で回答を書き込む前に、対象の question_id を確認する用途にも使えます。`,\n inputSchema: z.object({\n status: z\n .array(z.string())\n .optional()\n .default([\"active\", \"paused\"])\n .describe(\"ステータスでフィルタ\"),\n tags: z.array(z.string()).optional().describe(\"タグでフィルタ\"),\n pinned: z.boolean().optional().describe(\"ピン留めされた質問のみ\"),\n limit: z\n .number()\n .min(1)\n .max(50)\n .default(20)\n .describe(\"取得件数上限\"),\n }),\n annotations: {\n readOnlyHint: true,\n openWorldHint: true,\n },\n },\n async ({ status, tags, pinned, limit }) => {\n const supabase = await getSupabase();\n\n let query = supabase\n .from(\"questions\")\n .select(\n \"id, question, tags, status, importance, pinned, created_at, updated_at\"\n )\n .in(\"status\", status ?? [\"active\", \"paused\"])\n .order(\"updated_at\", { ascending: false })\n .limit(limit ?? 20);\n\n if (tags?.length) {\n query = query.overlaps(\"tags\", tags);\n }\n if (pinned !== undefined) {\n query = query.eq(\"pinned\", pinned);\n }\n\n const { data, error } = await query;\n if (error) throw new Error(`質問取得に失敗: ${error.message}`);\n\n return {\n content: [\n {\n type: \"text\" as const,\n text: formatQuestionList(data ?? []),\n },\n ],\n };\n }\n );\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { getSupabase } from \"../auth.js\";\n\nexport function registerQuestionsResource(server: McpServer): void {\n server.registerResource(\n \"active-questions\",\n \"wellgrow://questions/active\",\n {\n title: \"アクティブな質問一覧\",\n description: \"ステータスが active の質問一覧。\",\n mimeType: \"application/json\",\n },\n async (uri) => {\n const supabase = await getSupabase();\n const { data } = await supabase\n .from(\"questions\")\n .select(\"id, question, tags, importance, pinned, updated_at\")\n .eq(\"status\", \"active\")\n .order(\"updated_at\", { ascending: false })\n .limit(50);\n\n return {\n contents: [\n {\n uri: uri.href,\n text: JSON.stringify(data ?? [], null, 2),\n },\n ],\n };\n }\n );\n}\n"],"mappings":"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","names":["server","supabase","z","server","z","supabase","z","server","z","supabase","server","supabase"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/auth.ts","../src/tools/search-context.ts","../src/embedding.ts","../src/format.ts","../src/tools/answer-question.ts","../src/tools/list-questions.ts","../src/resources/questions.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { getSupabase } from \"./auth.js\";\nimport { registerSearchContextTool } from \"./tools/search-context.js\";\nimport { registerAnswerQuestionTool } from \"./tools/answer-question.js\";\nimport { registerListQuestionsTool } from \"./tools/list-questions.js\";\nimport { registerQuestionsResource } from \"./resources/questions.js\";\n\nconst required = [\"WELLGROW_EMAIL\", \"WELLGROW_PASSWORD\", \"OPENAI_API_KEY\"];\n\nfor (const key of required) {\n if (!process.env[key]) {\n console.error(`Error: ${key} is required`);\n process.exit(1);\n }\n}\n\ntry {\n await getSupabase();\n console.error(\"Authenticated successfully\");\n} catch (error) {\n console.error(\n \"Authentication failed:\",\n error instanceof Error ? error.message : error\n );\n process.exit(1);\n}\n\nconst server = new McpServer(\n { name: \"wellgrow\", version: \"0.2.0\" },\n {\n instructions: `WellGrow のユーザーナレッジベースにアクセスするサーバーです。\nユーザーの質問・回答データの検索・閲覧・書き込みができます。\nユーザーの考えや価値観を理解したい場面で活用してください。`,\n }\n);\n\nregisterSearchContextTool(server);\nregisterAnswerQuestionTool(server);\nregisterListQuestionsTool(server);\nregisterQuestionsResource(server);\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\nconsole.error(\"WellGrow MCP server running on stdio\");\n","import { createClient, type SupabaseClient } from \"@supabase/supabase-js\";\n\nconst DEFAULT_SUPABASE_URL = \"https://rpywqbtporjdhwtmvwkf.supabase.co\";\nconst DEFAULT_SUPABASE_ANON_KEY =\n \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InJweXdxYnRwb3JqZGh3dG12d2tmIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Njc4NjA4MDIsImV4cCI6MjA4MzQzNjgwMn0.1nn9F2y1JZBoSgB33CnP-k-6OM5HcEMdUs50RLJ89i4\";\n\nlet supabase: SupabaseClient | null = null;\n\nexport function getSupabaseUrl(): string {\n return process.env.WELLGROW_SUPABASE_URL ?? DEFAULT_SUPABASE_URL;\n}\n\nexport function getSupabaseAnonKey(): string {\n return process.env.WELLGROW_SUPABASE_ANON_KEY ?? DEFAULT_SUPABASE_ANON_KEY;\n}\n\nexport async function getSupabase(): Promise<SupabaseClient> {\n if (supabase) {\n const {\n data: { session },\n } = await supabase.auth.getSession();\n if (session) return supabase;\n }\n\n supabase = createClient(getSupabaseUrl(), getSupabaseAnonKey(), {\n auth: {\n autoRefreshToken: true,\n persistSession: false,\n },\n });\n\n const { error } = await supabase.auth.signInWithPassword({\n email: process.env.WELLGROW_EMAIL!,\n password: process.env.WELLGROW_PASSWORD!,\n });\n\n if (error) {\n throw new Error(`Authentication failed: ${error.message}`);\n }\n\n return supabase;\n}\n\nexport async function getUserId(): Promise<string> {\n const sb = await getSupabase();\n const {\n data: { user },\n } = await sb.auth.getUser();\n if (!user) throw new Error(\"Not authenticated\");\n return user.id;\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { getSupabase, getUserId } from \"../auth.js\";\nimport { generateEmbedding } from \"../embedding.js\";\nimport {\n formatSearchResult,\n type QuestionHit,\n type AnswerHit,\n} from \"../format.js\";\n\nexport function registerSearchContextTool(server: McpServer): void {\n server.registerTool(\n \"search_user_context\",\n {\n title: \"ユーザーコンテキスト検索\",\n description: `ユーザーの質問と回答のデータベースを検索します。\nユーザーの考え・価値観・経験・知識を理解するために使います。\n\n使用場面:\n- アドバイスや提案をする前に、ユーザーの価値観・原則を確認したいとき\n- 意思決定の場面で、過去の判断基準や経験を参照したいとき\n- 「私のスタイルで」「私らしく」など、ユーザーの好みを把握したいとき\n- ユーザーの背景(専門分野、関心、目標)を理解したいとき\n\n検索戦略(組み合わせ可能):\n- query のみ: セマンティック検索(意味的に近い内容を幅広く取得)\n- keywords を追加: ハイブリッド検索(意味的類似 + キーワード一致で精度向上)\n- tags/statuses/pinned: フィルタで結果を絞り込み\n\ntarget の使い分け:\n- \"questions\": 質問の一覧を見たいとき(最新回答も付属)\n- \"answers\": 回答の内容を重点的に調べたいとき\n- \"all\": 幅広く情報を集めたいとき(デフォルト)`,\n inputSchema: z.object({\n query: z.string().describe(\"検索クエリ(セマンティック検索に使用)\"),\n keywords: z\n .array(z.string())\n .optional()\n .describe(\"キーワード部分一致\"),\n target: z\n .enum([\"questions\", \"answers\", \"all\"])\n .default(\"all\")\n .describe(\"検索対象\"),\n tags: z.array(z.string()).optional().describe(\"タグでフィルタ\"),\n statuses: z\n .array(z.string())\n .optional()\n .describe(\"ステータスでフィルタ(省略時: active, paused)\"),\n pinned: z.boolean().optional().describe(\"ピン留めされた質問のみ\"),\n limit: z\n .number()\n .min(1)\n .max(50)\n .default(10)\n .describe(\"取得件数上限\"),\n }),\n annotations: {\n readOnlyHint: true,\n openWorldHint: true,\n },\n },\n async ({ query, keywords, target, tags, statuses, pinned, limit }) => {\n const supabase = await getSupabase();\n const userId = await getUserId();\n const embeddingStr = await generateEmbedding(query);\n\n const questions: QuestionHit[] = [];\n const answers: AnswerHit[] = [];\n const promises: PromiseLike<void>[] = [];\n\n if (target !== \"answers\") {\n promises.push(\n supabase\n .rpc(\"search_questions\", {\n p_user_id: userId,\n p_keywords: keywords ?? null,\n p_embedding: embeddingStr,\n p_vector_threshold: 0.3,\n p_statuses: statuses ?? [\"active\", \"paused\"],\n p_tags: tags ?? null,\n p_date_from: null,\n p_date_to: null,\n p_pinned: pinned ?? null,\n p_limit: limit ?? 10,\n })\n .then(({ data }) => {\n if (data) questions.push(...data);\n })\n );\n }\n\n if (target !== \"questions\") {\n promises.push(\n supabase\n .rpc(\"search_answers\", {\n p_user_id: userId,\n p_keywords: keywords ?? null,\n p_embedding: embeddingStr,\n p_vector_threshold: 0.3,\n p_sources: null,\n p_date_from: null,\n p_date_to: null,\n p_exclude_question_ids: null,\n p_limit: limit ?? 10,\n })\n .then(({ data }) => {\n if (data) answers.push(...data);\n })\n );\n }\n\n await Promise.all(promises);\n\n return {\n content: [\n {\n type: \"text\" as const,\n text: formatSearchResult({ questions, answers }),\n },\n ],\n };\n }\n );\n}\n","import { embed } from \"ai\";\nimport { createOpenAI } from \"@ai-sdk/openai\";\n\nconst openai = createOpenAI();\nconst embeddingModel = openai.embedding(\"text-embedding-3-small\");\n\nexport async function generateEmbedding(text: string): Promise<string> {\n const { embedding } = await embed({\n model: embeddingModel,\n value: text,\n });\n return JSON.stringify(embedding);\n}\n","export interface QuestionHit {\n id: string;\n question: string;\n tags: string[];\n importance: number | null;\n similarity: number | null;\n latest_answer: string | null;\n latest_answer_at: string | null;\n updated_at: string | null;\n}\n\nexport interface AnswerHit {\n answer_id: string;\n answer: string;\n answer_description: string | null;\n answer_source: string | null;\n similarity: number | null;\n answered_at: string;\n question_id: string;\n question: string;\n}\n\nexport interface QuestionListItem {\n id: string;\n question: string;\n tags: string[];\n status: string;\n importance: number | null;\n pinned: boolean;\n created_at: string;\n updated_at: string;\n}\n\nexport function formatSearchResult(result: {\n questions: QuestionHit[];\n answers: AnswerHit[];\n}): string {\n const parts: string[] = [];\n\n if (result.questions.length > 0) {\n parts.push(`## 質問 (${result.questions.length}件)`);\n for (const q of result.questions) {\n const tags = q.tags.length > 0 ? ` [${q.tags.join(\", \")}]` : \"\";\n const answer = q.latest_answer\n ? `\\n 最新回答: ${q.latest_answer}`\n : \"\";\n parts.push(`- **${q.question}**${tags}${answer}\\n ID: ${q.id}`);\n }\n }\n\n if (result.answers.length > 0) {\n parts.push(`## 回答 (${result.answers.length}件)`);\n for (const a of result.answers) {\n const desc = a.answer_description\n ? `\\n 説明: ${a.answer_description}`\n : \"\";\n parts.push(\n `- **${a.question}**\\n 回答: ${a.answer}${desc}\\n 回答日: ${a.answered_at}`\n );\n }\n }\n\n if (parts.length === 0) {\n return \"検索結果はありません。\";\n }\n\n return parts.join(\"\\n\\n\");\n}\n\nexport function formatQuestionList(questions: QuestionListItem[]): string {\n if (questions.length === 0) {\n return \"質問はありません。\";\n }\n\n const lines: string[] = [`## 質問一覧 (${questions.length}件)`];\n\n for (const q of questions) {\n const tags = q.tags.length > 0 ? ` [${q.tags.join(\", \")}]` : \"\";\n const pinned = q.pinned ? \" 📌\" : \"\";\n const status = q.status !== \"active\" ? ` (${q.status})` : \"\";\n lines.push(\n `- **${q.question}**${tags}${pinned}${status}\\n ID: ${q.id} | 更新: ${q.updated_at}`\n );\n }\n\n return lines.join(\"\\n\\n\");\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { getSupabase, getUserId } from \"../auth.js\";\n\nexport function registerAnswerQuestionTool(server: McpServer): void {\n server.registerTool(\n \"answer_question\",\n {\n title: \"質問への回答書き込み\",\n description: `ユーザーの質問に対して回答を書き込みます。\nユーザーとの会話で重要な気づきや洞察が生まれた時に使います。\n\n使用前に必ずユーザーの承認を得てください。\n\n【回答のルール】\n- answer は140文字以内で、端的に核心を突く回答にする\n- ユーザー自身の言葉を活かした回答にする\n- description には回答の背景・根拠・詳細な説明を書く\n- question_id は search_user_context や list_questions で取得した ID を使う`,\n inputSchema: z.object({\n question_id: z.string().describe(\"対象の質問 ID\"),\n answer: z.string().max(140).describe(\"回答(140文字以内)\"),\n description: z\n .string()\n .max(300)\n .optional()\n .describe(\"回答の詳細説明(300文字以内)\"),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n },\n },\n async ({ question_id, answer, description }) => {\n const supabase = await getSupabase();\n const userId = await getUserId();\n\n const { error: insertError } = await supabase.from(\"answers\").insert({\n question_id,\n user_id: userId,\n answer,\n description: description ?? null,\n source: \"mcp\",\n });\n\n if (insertError)\n throw new Error(`回答の保存に失敗: ${insertError.message}`);\n\n const { data: questionData } = await supabase\n .from(\"questions\")\n .select(\"question\")\n .eq(\"id\", question_id)\n .single();\n\n const questionText = questionData?.question ?? question_id;\n\n return {\n content: [\n {\n type: \"text\" as const,\n text: `回答を保存しました:\\n質問: ${questionText}\\n回答: ${answer}`,\n },\n ],\n };\n }\n );\n}\n","import { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { getSupabase } from \"../auth.js\";\nimport { formatQuestionList } from \"../format.js\";\n\nexport function registerListQuestionsTool(server: McpServer): void {\n server.registerTool(\n \"list_questions\",\n {\n title: \"質問一覧取得\",\n description: `ユーザーの質問一覧を取得します。ステータスやタグでフィルタ可能。\n質問の ID を確認したいときや、ユーザーがどんな質問を持っているか把握したいときに使います。\nanswer_question で回答を書き込む前に、対象の question_id を確認する用途にも使えます。`,\n inputSchema: z.object({\n status: z\n .array(z.string())\n .optional()\n .default([\"active\", \"paused\"])\n .describe(\"ステータスでフィルタ\"),\n tags: z.array(z.string()).optional().describe(\"タグでフィルタ\"),\n pinned: z.boolean().optional().describe(\"ピン留めされた質問のみ\"),\n limit: z\n .number()\n .min(1)\n .max(50)\n .default(20)\n .describe(\"取得件数上限\"),\n }),\n annotations: {\n readOnlyHint: true,\n openWorldHint: true,\n },\n },\n async ({ status, tags, pinned, limit }) => {\n const supabase = await getSupabase();\n\n let query = supabase\n .from(\"questions\")\n .select(\n \"id, question, tags, status, importance, pinned, created_at, updated_at\"\n )\n .in(\"status\", status ?? [\"active\", \"paused\"])\n .order(\"updated_at\", { ascending: false })\n .limit(limit ?? 20);\n\n if (tags?.length) {\n query = query.overlaps(\"tags\", tags);\n }\n if (pinned !== undefined) {\n query = query.eq(\"pinned\", pinned);\n }\n\n const { data, error } = await query;\n if (error) throw new Error(`質問取得に失敗: ${error.message}`);\n\n return {\n content: [\n {\n type: \"text\" as const,\n text: formatQuestionList(data ?? []),\n },\n ],\n };\n }\n );\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { getSupabase } from \"../auth.js\";\n\nexport function registerQuestionsResource(server: McpServer): void {\n server.registerResource(\n \"active-questions\",\n \"wellgrow://questions/active\",\n {\n title: \"アクティブな質問一覧\",\n description: \"ステータスが active の質問一覧。\",\n mimeType: \"application/json\",\n },\n async (uri) => {\n const supabase = await getSupabase();\n const { data } = await supabase\n .from(\"questions\")\n .select(\"id, question, tags, importance, pinned, updated_at\")\n .eq(\"status\", \"active\")\n .order(\"updated_at\", { ascending: false })\n .limit(50);\n\n return {\n contents: [\n {\n uri: uri.href,\n text: JSON.stringify(data ?? [], null, 2),\n },\n ],\n };\n }\n );\n}\n"],"mappings":"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","names":["server","supabase","z","server","z","supabase","z","server","z","supabase","server","supabase"]}
|