import { initStderrTee } from "../../../../lib/mcp-stderr-tee/dist/index.js";
initStderrTee("contacts");
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { contactCreate } from "./tools/contact-create.js";
import { contactLookup } from "./tools/contact-lookup.js";
import { contactUpdate } from "./tools/contact-update.js";
import { contactList } from "./tools/contact-list.js";
import { contactDelete } from "./tools/contact-delete.js";
import { closeDriver } from "./lib/neo4j.js";
const server = new McpServer({
    name: "maxy-contacts",
    version: "0.1.0",
});
const accountId = process.env.ACCOUNT_ID;
if (!accountId) {
    throw new Error("ACCOUNT_ID environment variable is required");
}
server.tool("contact-create", "Create a new contact (schema:Person) in the graph. Requires at least one of email or telephone. Deduplicates by whichever identifier is provided — returns existing contact if email or phone already exists.", {
    givenName: z.string().describe("First name"),
    familyName: z.string().optional().describe("Last name"),
    email: z.string().email().optional().describe("Email address (identifier — at least one of email or telephone required)"),
    telephone: z.string().min(1).max(30).optional().describe("Phone number (identifier — at least one of email or telephone required)"),
    jobTitle: z.string().optional().describe("Job title"),
    source: z
        .string()
        .describe("Where this contact came from (e.g. public.maxy.bot, telegram)"),
    status: z
        .string()
        .optional()
        .describe("Contact status (e.g. active, prospect, waitlist)"),
}, async (params) => {
    try {
        const result = await contactCreate({ ...params, accountId });
        if (result.alreadyExists) {
            return {
                content: [
                    {
                        type: "text",
                        text: `Contact already exists with ${result.identifier} (ID: ${result.nodeId}). No duplicate created.`,
                    },
                ],
            };
        }
        return {
            content: [
                {
                    type: "text",
                    text: `Contact created: ${params.givenName} ${result.identifier} (ID: ${result.nodeId}, status: ${params.status ?? "waitlist"})`,
                },
            ],
        };
    }
    catch (err) {
        return {
            content: [
                {
                    type: "text",
                    text: `Failed to create contact: ${err instanceof Error ? err.message : String(err)}`,
                },
            ],
            isError: true,
        };
    }
});
server.tool("contact-lookup", "Search for contacts by email, telephone, name, or list recent contacts. Returns schema:Person nodes.", {
    email: z.string().optional().describe("Exact email match"),
    telephone: z.string().optional().describe("Exact phone number match"),
    name: z.string().optional().describe("Partial name search (first or last name)"),
    limit: z.number().int().optional().describe("Max results (default 10)"),
}, async ({ email, telephone, name, limit }) => {
    try {
        const results = await contactLookup({ email, telephone, name, accountId, limit });
        if (results.length === 0) {
            return {
                content: [
                    { type: "text", text: "No contacts found." },
                ],
            };
        }
        const formatted = results
            .map((c) => {
            const identifierPart = c.email
                ? `<${c.email}>`
                : c.telephone
                    ? `<${c.telephone}>`
                    : "";
            return `${c.givenName}${c.familyName ? ` ${c.familyName}` : ""} ${identifierPart} — ${c.status} (from ${c.source}, ${c.createdOn})`;
        })
            .join("\n");
        return {
            content: [{ type: "text", text: formatted }],
        };
    }
    catch (err) {
        return {
            content: [
                {
                    type: "text",
                    text: `Lookup failed: ${err instanceof Error ? err.message : String(err)}`,
                },
            ],
            isError: true,
        };
    }
});
server.tool("contact-update", "Update properties of an existing contact. Identify by nodeId, email, or telephone. Cannot change email or accountId.", {
    email: z.string().optional().describe("Find contact by email"),
    telephone: z.string().optional().describe("Find contact by phone number"),
    nodeId: z.string().optional().describe("Find contact by node ID"),
    updates: z
        .record(z.string(), z.unknown())
        .describe("Properties to update (e.g. status, telephone, jobTitle)"),
}, async ({ email, telephone, nodeId, updates }) => {
    try {
        const result = await contactUpdate({
            email,
            telephone,
            nodeId,
            updates: updates,
            accountId,
        });
        return {
            content: [
                {
                    type: "text",
                    text: `Contact updated (ID: ${result.nodeId})`,
                },
            ],
        };
    }
    catch (err) {
        return {
            content: [
                {
                    type: "text",
                    text: `Update failed: ${err instanceof Error ? err.message : String(err)}`,
                },
            ],
            isError: true,
        };
    }
});
server.tool("contact-list", "List contacts with optional filters. Returns summary of matching Person nodes.", {
    status: z
        .string()
        .optional()
        .describe("Filter by status (e.g. waitlist)"),
    source: z
        .string()
        .optional()
        .describe("Filter by source (e.g. public.maxy.bot, telegram)"),
    since: z
        .string()
        .optional()
        .describe("ISO date — only contacts created after this date"),
    limit: z.number().int().optional().describe("Max results (default 50)"),
}, async (params) => {
    try {
        const result = await contactList({ ...params, accountId });
        if (result.contacts.length === 0) {
            return {
                content: [
                    { type: "text", text: "No contacts match the filters." },
                ],
            };
        }
        const header = `${result.total} total contacts${params.status ? ` with status '${params.status}'` : ""}:\n\n`;
        const formatted = result.contacts
            .map((c) => {
            const identifierPart = c.email
                ? `<${c.email}>`
                : c.telephone
                    ? `<${c.telephone}>`
                    : "";
            return `${c.givenName}${c.familyName ? ` ${c.familyName}` : ""} ${identifierPart} — ${c.status} (${c.source}, ${c.createdOn})`;
        })
            .join("\n");
        return {
            content: [{ type: "text", text: header + formatted }],
        };
    }
    catch (err) {
        return {
            content: [
                {
                    type: "text",
                    text: `List failed: ${err instanceof Error ? err.message : String(err)}`,
                },
            ],
            isError: true,
        };
    }
});
server.tool("contact-delete", "Permanently delete a contact (Person node) and all its relationships from the graph. Identify by email, telephone, or node ID. This is irreversible.", {
    email: z.string().optional().describe("Find contact by email"),
    telephone: z.string().optional().describe("Find contact by phone number"),
    nodeId: z.string().optional().describe("Find contact by node ID"),
}, async ({ email, telephone, nodeId }) => {
    try {
        const result = await contactDelete({ email, telephone, nodeId, accountId });
        return {
            content: [
                {
                    type: "text",
                    text: `Contact deleted: ${result.identifier}`,
                },
            ],
        };
    }
    catch (err) {
        return {
            content: [
                {
                    type: "text",
                    text: `Delete failed: ${err instanceof Error ? err.message : String(err)}`,
                },
            ],
            isError: true,
        };
    }
});
process.on("SIGINT", async () => {
    await closeDriver();
    process.exit(0);
});
const transport = new StdioServerTransport();
await server.connect(transport);
//# sourceMappingURL=index.js.map