mirror of
https://github.com/aljazceru/bakery.git
synced 2025-12-17 04:35:13 +01:00
fix small bugs with setup tools
This commit is contained in:
@@ -2,12 +2,14 @@
|
||||
import "./polyfill.js";
|
||||
import process from "node:process";
|
||||
import path from "node:path";
|
||||
|
||||
import express, { Request } from "express";
|
||||
import dayjs from "dayjs";
|
||||
import duration from "dayjs/plugin/duration.js";
|
||||
import localizedFormat from "dayjs/plugin/localizedFormat.js";
|
||||
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import mcpServer from "./services/mcp/index.js";
|
||||
|
||||
import App from "./app/index.js";
|
||||
import { PUBLIC_ADDRESS, IS_MCP } from "./env.js";
|
||||
import { addListener, logger } from "./logger.js";
|
||||
@@ -72,12 +74,9 @@ await app.start();
|
||||
|
||||
// Setup MCP interface on stdio
|
||||
if (IS_MCP) {
|
||||
const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
|
||||
const { default: server } = await import("./services/mcp/index.js");
|
||||
|
||||
// connect MCP server to stdio
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
await mcpServer.connect(transport);
|
||||
}
|
||||
|
||||
// shutdown process
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import "./resources.js";
|
||||
import "./tools/index.js";
|
||||
|
||||
import server from "./server.js";
|
||||
export default server;
|
||||
import mcpServer from "./server.js";
|
||||
export default mcpServer;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
|
||||
import server from "./server.js";
|
||||
import mcpServer from "./server.js";
|
||||
import bakeryConfig from "../config.js";
|
||||
import { normalizeToHexPubkey } from "../../helpers/nip19.js";
|
||||
import { requestLoader } from "../loaders.js";
|
||||
import { kinds } from "nostr-tools";
|
||||
|
||||
server.resource("owner_pubkey", "pubkey://owner", async (uri) => ({
|
||||
mcpServer.resource("owner_pubkey", "pubkey://owner", async (uri) => ({
|
||||
contents: [
|
||||
{
|
||||
uri: uri.href,
|
||||
@@ -15,7 +15,7 @@ server.resource("owner_pubkey", "pubkey://owner", async (uri) => ({
|
||||
],
|
||||
}));
|
||||
|
||||
server.resource("config", "config://app", async (uri) => ({
|
||||
mcpServer.resource("config", "config://app", async (uri) => ({
|
||||
contents: [
|
||||
{
|
||||
uri: uri.href,
|
||||
@@ -24,7 +24,7 @@ server.resource("config", "config://app", async (uri) => ({
|
||||
],
|
||||
}));
|
||||
|
||||
server.resource(
|
||||
mcpServer.resource(
|
||||
"user_profile",
|
||||
new ResourceTemplate("users://{pubkey}/profile", { list: undefined }),
|
||||
async (uri, { pubkey }) => {
|
||||
@@ -44,7 +44,7 @@ server.resource(
|
||||
},
|
||||
);
|
||||
|
||||
server.resource("event_kinds", "nostr://kinds", async (uri) => {
|
||||
mcpServer.resource("event_kinds", "nostr://kinds", async (uri) => {
|
||||
return {
|
||||
contents: [{ uri: uri.href, text: JSON.stringify(kinds) }],
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
|
||||
const server = new McpServer({
|
||||
const mcpServer = new McpServer({
|
||||
name: "Bakery",
|
||||
version: "1.0.0",
|
||||
});
|
||||
|
||||
export default server;
|
||||
export default mcpServer;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import z from "zod";
|
||||
import { FollowUser, PinNote, UnfollowUser, UnpinNote, UpdateProfile } from "applesauce-actions/actions";
|
||||
|
||||
import server from "../server.js";
|
||||
import mcpServer from "../server.js";
|
||||
import { ownerActions, ownerPublish } from "../../owner.js";
|
||||
import { normalizeToHexPubkey } from "../../../helpers/nip19.js";
|
||||
import eventCache from "../../event-cache.js";
|
||||
|
||||
server.tool(
|
||||
mcpServer.tool(
|
||||
"follow_user",
|
||||
"Adds another users pubkey to the owners following list",
|
||||
{
|
||||
@@ -25,7 +25,7 @@ server.tool(
|
||||
},
|
||||
);
|
||||
|
||||
server.tool(
|
||||
mcpServer.tool(
|
||||
"unfollow_user",
|
||||
"Removes another users pubkey from the owners following list",
|
||||
{
|
||||
@@ -44,7 +44,7 @@ server.tool(
|
||||
},
|
||||
);
|
||||
|
||||
server.tool(
|
||||
mcpServer.tool(
|
||||
"pin_note",
|
||||
"Pins a kind 1 note to the owners pinned notes list",
|
||||
{
|
||||
@@ -59,7 +59,7 @@ server.tool(
|
||||
},
|
||||
);
|
||||
|
||||
server.tool(
|
||||
mcpServer.tool(
|
||||
"unpin_note",
|
||||
"Unpins a kind 1 note from the owners pinned notes list",
|
||||
{
|
||||
@@ -74,7 +74,7 @@ server.tool(
|
||||
},
|
||||
);
|
||||
|
||||
server.tool(
|
||||
mcpServer.tool(
|
||||
"set_profile_field",
|
||||
"Sets a field in the owners profile",
|
||||
{
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import server from "../server.js";
|
||||
import mcpServer from "../server.js";
|
||||
import bakeryConfig, { bakeryConfigSchema } from "../../config.js";
|
||||
|
||||
server.tool("get_bakery_config", "Gets the current configuration for the bakery", {}, async () => {
|
||||
mcpServer.tool("get_bakery_config", "Gets the current configuration for the bakery", {}, async () => {
|
||||
return { content: [{ type: "text", text: JSON.stringify(bakeryConfig.data) }] };
|
||||
});
|
||||
|
||||
server.tool(
|
||||
mcpServer.tool(
|
||||
"update_bakery_config",
|
||||
"Updates the bakery config with the provided config",
|
||||
{ config: bakeryConfigSchema.partial().describe("A partial config to update") },
|
||||
|
||||
@@ -2,16 +2,16 @@ import { firstValueFrom } from "rxjs";
|
||||
import { isSameURL } from "applesauce-core/helpers";
|
||||
import { z } from "zod";
|
||||
|
||||
import server from "../server.js";
|
||||
import mcpServer from "../server.js";
|
||||
import { connections$, notices$ } from "../../rx-nostr.js";
|
||||
|
||||
server.tool("get_connected_relays", "Gets the connection status of all the relays", {}, async () => {
|
||||
mcpServer.tool("get_connected_relays", "Gets the connection status of all the relays", {}, async () => {
|
||||
const relays = await firstValueFrom(connections$);
|
||||
|
||||
return { content: [{ type: "text", text: JSON.stringify(relays) }] };
|
||||
});
|
||||
|
||||
server.tool(
|
||||
mcpServer.tool(
|
||||
"get_relay_notices",
|
||||
"Gets the notices from the all relays or a certain relay",
|
||||
{ relay: z.string().url().optional() },
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import database from "../../database.js";
|
||||
import server from "../server.js";
|
||||
import mcpServer from "../server.js";
|
||||
|
||||
server.tool("total_events", "Get the total number of events in the database", {}, async () => {
|
||||
mcpServer.tool("total_events", "Get the total number of events in the database", {}, async () => {
|
||||
const result = database.db.prepare<[], { events: number }>(`SELECT COUNT(*) AS events FROM events`).get();
|
||||
return { content: [{ type: "text", text: `Total events: ${result?.events ?? 0}` }] };
|
||||
});
|
||||
|
||||
server.tool("total_users", "Get the total number of users in the database", {}, async () => {
|
||||
mcpServer.tool("total_users", "Get the total number of users in the database", {}, async () => {
|
||||
const result = database.db
|
||||
.prepare<[], { users: number }>(`SELECT COUNT(*) AS users FROM events GROUP BY pubkey`)
|
||||
.get();
|
||||
|
||||
@@ -3,7 +3,7 @@ import { NoteBlueprint } from "applesauce-factory/blueprints";
|
||||
import { EventTemplate } from "nostr-tools";
|
||||
import { z } from "zod";
|
||||
|
||||
import server from "../server.js";
|
||||
import mcpServer from "../server.js";
|
||||
import { ownerFactory } from "../../owner.js";
|
||||
|
||||
async function returnUnsigned(draft: EventTemplate | Promise<EventTemplate>): Promise<CallToolResult> {
|
||||
@@ -12,7 +12,7 @@ async function returnUnsigned(draft: EventTemplate | Promise<EventTemplate>): Pr
|
||||
};
|
||||
}
|
||||
|
||||
server.tool(
|
||||
mcpServer.tool(
|
||||
"short_text_note_draft",
|
||||
"Create a short text note draft event",
|
||||
{
|
||||
|
||||
@@ -3,14 +3,14 @@ import { getProfileContent } from "applesauce-core/helpers";
|
||||
import { kinds } from "nostr-tools";
|
||||
import z from "zod";
|
||||
|
||||
import server from "../server.js";
|
||||
import mcpServer from "../server.js";
|
||||
import { ownerFactory, ownerPublish } from "../../owner.js";
|
||||
import { requestLoader } from "../../loaders.js";
|
||||
import bakeryConfig from "../../config.js";
|
||||
import eventCache from "../../event-cache.js";
|
||||
import { normalizeToHexPubkey } from "../../../helpers/nip19.js";
|
||||
|
||||
server.tool(
|
||||
mcpServer.tool(
|
||||
"sign_draft_event",
|
||||
"Signs a draft note event with the owners pubkey",
|
||||
{
|
||||
@@ -34,7 +34,7 @@ server.tool(
|
||||
},
|
||||
);
|
||||
|
||||
server.tool(
|
||||
mcpServer.tool(
|
||||
"publish_event",
|
||||
"Publishes a signed nostr event to the relays or the users outbox relays",
|
||||
{
|
||||
@@ -67,7 +67,7 @@ server.tool(
|
||||
},
|
||||
);
|
||||
|
||||
server.tool(
|
||||
mcpServer.tool(
|
||||
"search_events",
|
||||
"Search for events of a certain kind that contain the query",
|
||||
{ query: z.string(), kind: z.number().default(1), limit: z.number().default(50) },
|
||||
@@ -81,7 +81,7 @@ server.tool(
|
||||
);
|
||||
|
||||
// TODO: this needs to accept naddr, and nevent
|
||||
server.tool("get_event", "Get an event by id", { id: z.string().length(64) }, async ({ id }) => {
|
||||
mcpServer.tool("get_event", "Get an event by id", { id: z.string().length(64) }, async ({ id }) => {
|
||||
const event = await eventCache.getEventsForFilters([{ ids: [id] }]);
|
||||
|
||||
return {
|
||||
@@ -89,7 +89,7 @@ server.tool("get_event", "Get an event by id", { id: z.string().length(64) }, as
|
||||
};
|
||||
});
|
||||
|
||||
server.tool(
|
||||
mcpServer.tool(
|
||||
"search_user_pubkey",
|
||||
"Search for users pubkeys that match the query",
|
||||
{ query: z.string(), limit: z.number().default(10) },
|
||||
@@ -119,7 +119,7 @@ server.tool(
|
||||
},
|
||||
);
|
||||
|
||||
server.tool(
|
||||
mcpServer.tool(
|
||||
"get_users_recent_events",
|
||||
"Gets a list of recent events created by a pubkey",
|
||||
{
|
||||
@@ -141,7 +141,7 @@ server.tool(
|
||||
},
|
||||
);
|
||||
|
||||
server.tool(
|
||||
mcpServer.tool(
|
||||
"get_events_pubkey_mentioned",
|
||||
"Gets a list of recent events that the pubkey was mentioned in",
|
||||
{
|
||||
|
||||
@@ -3,11 +3,45 @@ import { NostrConnectAccount } from "applesauce-accounts/accounts/nostr-connect-
|
||||
import qrcode from "qrcode-terminal";
|
||||
import { z } from "zod";
|
||||
|
||||
import server from "../server.js";
|
||||
import mcpServer from "../server.js";
|
||||
import { ownerAccount$, setupSigner$, startSignerSetup, stopSignerSetup } from "../../owner.js";
|
||||
import { DEFAULT_NOSTR_CONNECT_RELAYS } from "../../../const.js";
|
||||
import { normalizeToHexPubkey } from "../../../helpers/nip19.js";
|
||||
import bakeryConfig from "../../config.js";
|
||||
|
||||
server.tool(
|
||||
mcpServer.tool(
|
||||
"set_owner_pubkey",
|
||||
"Sets the owner's pubkey",
|
||||
{ pubkey: z.string().transform((p) => normalizeToHexPubkey(p, true)) },
|
||||
async ({ pubkey }) => {
|
||||
bakeryConfig.setField("owner", pubkey);
|
||||
return { content: [{ type: "text", text: "Owner pubkey set" }] };
|
||||
},
|
||||
);
|
||||
|
||||
mcpServer.tool("get_owner_pubkey", "Gets the owner's pubkey", {}, async () => {
|
||||
const pubkey = bakeryConfig.data.owner;
|
||||
if (!pubkey) return { content: [{ type: "text", text: "No owner pubkey set" }] };
|
||||
|
||||
return { content: [{ type: "text", text: pubkey }] };
|
||||
});
|
||||
|
||||
mcpServer.tool("get_setup_status", "Checks if the bakery needs to be setup", {}, async () => {
|
||||
if (!bakeryConfig.data.owner)
|
||||
return { content: [{ type: "text", text: "Missing owner pubkey, please set an owner to finish bakery setup" }] };
|
||||
|
||||
const account = ownerAccount$.getValue();
|
||||
if (!account)
|
||||
return { content: [{ type: "text", text: "No signer connected, setup a signer if you want to sign events" }] };
|
||||
|
||||
if (setupSigner$.value)
|
||||
return { content: [{ type: "text", text: "Signer setup in progress, waiting for the owner to connect" }] };
|
||||
|
||||
return { content: [{ type: "text", text: "Bakery is ready to use" }] };
|
||||
});
|
||||
|
||||
// connect remote signer tools
|
||||
mcpServer.tool(
|
||||
"connect_nostr_signer",
|
||||
"Connects remote signer using a bunker:// URI",
|
||||
{ uri: z.string().startsWith("bunker://") },
|
||||
@@ -23,37 +57,15 @@ server.tool(
|
||||
},
|
||||
);
|
||||
|
||||
server.tool("disconnect_nostr_signer", "Disconnects and forgets the current signer", {}, async () => {
|
||||
mcpServer.tool("disconnect_nostr_signer", "Disconnects and forgets the current signer", {}, async () => {
|
||||
if (!ownerAccount$.value) return { content: [{ type: "text", text: "No signer connected" }] };
|
||||
ownerAccount$.next(undefined);
|
||||
|
||||
return { content: [{ type: "text", text: "Disconnected from the signer" }] };
|
||||
});
|
||||
|
||||
server.tool("nostr_signer_status", "Gets the status of the current signer", {}, async () => {
|
||||
const account = ownerAccount$.getValue();
|
||||
if (!account) return { content: [{ type: "text", text: "No signer connected" }] };
|
||||
|
||||
if (setupSigner$.value) {
|
||||
return { content: [{ type: "text", text: "Signer setup in progress, waiting for the owner to connect" }] };
|
||||
}
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: [
|
||||
`Pubkey: ${await account.getPublicKey()}`,
|
||||
`Connected: ${account.signer.isConnected}`,
|
||||
`Relays: ${account.signer.relays.join(", ")}`,
|
||||
].join("\n"),
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
// signer setup tools
|
||||
server.tool(
|
||||
mcpServer.tool(
|
||||
"create_signer_setup_link",
|
||||
"Creates a nostrconnect:// URI for the owner to setup their signer",
|
||||
{ relays: z.array(z.string().url()).default(DEFAULT_NOSTR_CONNECT_RELAYS) },
|
||||
@@ -80,7 +92,43 @@ server.tool(
|
||||
},
|
||||
);
|
||||
|
||||
server.tool("abort_nostr_signer_setup", "Aborts the signer setup process", {}, async () => {
|
||||
// get signer status tools
|
||||
mcpServer.tool("nostr_signer_status", "Gets the status of the current signer", {}, async () => {
|
||||
const account = ownerAccount$.getValue();
|
||||
if (!account) return { content: [{ type: "text", text: "No signer connected" }] };
|
||||
|
||||
if (setupSigner$.value) {
|
||||
const uri = setupSigner$.value!.getNostrConnectURI();
|
||||
|
||||
// Generate QR code
|
||||
const qr = await new Promise<string>((resolve) => {
|
||||
qrcode.generate(uri, { small: true }, (qr: string) => resolve(qr));
|
||||
});
|
||||
|
||||
return {
|
||||
content: [
|
||||
{ type: "text", text: "Signer setup in progress, waiting for the signer to connect" },
|
||||
{ type: "text", text: qr },
|
||||
{ type: "text", text: `Nostr Connect URI: ${uri}` },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: [
|
||||
`Pubkey: ${await account.getPublicKey()}`,
|
||||
`Connected: ${account.signer.isConnected}`,
|
||||
`Relays: ${account.signer.relays.join(", ")}`,
|
||||
].join("\n"),
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
mcpServer.tool("abort_nostr_signer_setup", "Aborts the signer setup process", {}, async () => {
|
||||
await stopSignerSetup();
|
||||
|
||||
return { content: [{ type: "text", text: "signer setup aborted" }] };
|
||||
|
||||
@@ -34,6 +34,7 @@ export function startSignerSetup(relays = DEFAULT_NOSTR_CONNECT_RELAYS) {
|
||||
const p = signer.waitForSigner().then(async () => {
|
||||
const pubkey = await signer.getPublicKey();
|
||||
ownerAccount$.next(new NostrConnectAccount(pubkey, signer));
|
||||
bakeryConfig.setField("owner", pubkey);
|
||||
setupSigner$.next(undefined);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user