feat: add missing tools and polish descriptions. Add inspect_workspace, list_workspaces, inspect_session, get_session_message, and get_session_summaries tools. Fix CORS preflight handling and default route path. Extract shared formatting helpers for messages and summaries. Trim redundant tool descriptions.
This commit is contained in:
parent
4bbdb4e20c
commit
1bc9da865b
|
|
@ -19,6 +19,10 @@ export default {
|
|||
env: unknown,
|
||||
executionCtx: ExecutionContext,
|
||||
): Promise<Response> {
|
||||
if (request.method === "OPTIONS") {
|
||||
return new Response(null, { status: 204, headers: CORS_HEADERS });
|
||||
}
|
||||
|
||||
let config;
|
||||
try {
|
||||
config = parseConfig(request);
|
||||
|
|
@ -35,6 +39,7 @@ export default {
|
|||
const honcho = createClient(config);
|
||||
const server = createServer({ honcho, config });
|
||||
const handler = createMcpHandler(server, {
|
||||
route: "/",
|
||||
corsOptions: {
|
||||
origin: CORS_ORIGIN,
|
||||
methods: CORS_METHODS,
|
||||
|
|
|
|||
|
|
@ -149,7 +149,6 @@ export function register(server: McpServer, ctx: ToolContext) {
|
|||
description: [
|
||||
"Delete a specific conclusion by ID.",
|
||||
"Use this to remove incorrect or outdated knowledge.",
|
||||
"Returns a confirmation message.",
|
||||
].join("\n"),
|
||||
inputSchema: {
|
||||
peer_id: z.string().describe("The observer peer."),
|
||||
|
|
|
|||
|
|
@ -281,9 +281,7 @@ export function register(server: McpServer, ctx: ToolContext) {
|
|||
"get_peer_metadata",
|
||||
{
|
||||
description: [
|
||||
"Get the metadata dictionary for a peer.",
|
||||
"Use this to read custom attributes stored on a peer.",
|
||||
"Returns a JSON object of key-value pairs.",
|
||||
"Get metadata for a peer.",
|
||||
].join("\n"),
|
||||
inputSchema: {
|
||||
peer_id: z.string().describe("The peer to get metadata for."),
|
||||
|
|
@ -307,9 +305,8 @@ export function register(server: McpServer, ctx: ToolContext) {
|
|||
"set_peer_metadata",
|
||||
{
|
||||
description: [
|
||||
"Set metadata for a peer (overwrites existing metadata).",
|
||||
"Use this to store custom attributes on a peer.",
|
||||
"Returns a confirmation message.",
|
||||
"Set metadata for a peer.",
|
||||
"Overwrites existing metadata.",
|
||||
].join("\n"),
|
||||
inputSchema: {
|
||||
peer_id: z.string().describe("The peer to set metadata for."),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,14 @@
|
|||
import { z } from "zod";
|
||||
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import { Message } from "@honcho-ai/sdk";
|
||||
import type { ToolContext } from "../types.js";
|
||||
import { textResult, errorResult, formatMessages } from "../types.js";
|
||||
import {
|
||||
textResult,
|
||||
errorResult,
|
||||
formatMessage,
|
||||
formatMessages,
|
||||
formatSessionSummaries,
|
||||
} from "../types.js";
|
||||
|
||||
export function register(server: McpServer, ctx: ToolContext) {
|
||||
// ── create_session ──────────────────────────────────────────────────
|
||||
|
|
@ -61,9 +68,8 @@ export function register(server: McpServer, ctx: ToolContext) {
|
|||
"delete_session",
|
||||
{
|
||||
description: [
|
||||
"Permanently delete a session and all its messages.",
|
||||
"Use this to clean up conversations that are no longer needed. This cannot be undone.",
|
||||
"Returns a confirmation message.",
|
||||
"Delete a session and all its messages.",
|
||||
"This cannot be undone.",
|
||||
].join("\n"),
|
||||
inputSchema: {
|
||||
session_id: z.string().describe("The session to delete."),
|
||||
|
|
@ -121,7 +127,6 @@ export function register(server: McpServer, ctx: ToolContext) {
|
|||
description: [
|
||||
"Add one or more peers to a session.",
|
||||
"Use this to bring participants into a conversation.",
|
||||
"Returns a confirmation message.",
|
||||
].join("\n"),
|
||||
inputSchema: {
|
||||
session_id: z.string().describe("The session to add peers to."),
|
||||
|
|
@ -175,8 +180,6 @@ export function register(server: McpServer, ctx: ToolContext) {
|
|||
{
|
||||
description: [
|
||||
"Remove one or more peers from a session.",
|
||||
"Use this to remove participants from a conversation.",
|
||||
"Returns a confirmation message.",
|
||||
].join("\n"),
|
||||
inputSchema: {
|
||||
session_id: z.string().describe("The session to remove peers from."),
|
||||
|
|
@ -224,6 +227,42 @@ export function register(server: McpServer, ctx: ToolContext) {
|
|||
},
|
||||
);
|
||||
|
||||
// ── inspect_session ─────────────────────────────────────────────────
|
||||
server.registerTool(
|
||||
"inspect_session",
|
||||
{
|
||||
description: [
|
||||
"Inspect a session at a glance.",
|
||||
"Aggregates peer IDs, message count, and available summaries.",
|
||||
"Returns a single JSON object.",
|
||||
].join("\n"),
|
||||
inputSchema: {
|
||||
session_id: z.string().describe("The session to inspect."),
|
||||
},
|
||||
},
|
||||
async ({ session_id }) => {
|
||||
try {
|
||||
const session = await ctx.honcho.session(session_id);
|
||||
const [peers, messagePage, summaries] = await Promise.all([
|
||||
session.peers(),
|
||||
session.messages(),
|
||||
session.summaries(),
|
||||
]);
|
||||
|
||||
return textResult({
|
||||
session_id,
|
||||
peers: peers.map((peer) => ({ id: peer.id })),
|
||||
message_count: messagePage.total,
|
||||
summaries: formatSessionSummaries(summaries),
|
||||
});
|
||||
} catch (e) {
|
||||
return errorResult(
|
||||
`Failed to inspect session: ${e instanceof Error ? e.message : String(e)}`,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// ── add_messages_to_session ─────────────────────────────────────────
|
||||
server.registerTool(
|
||||
"add_messages_to_session",
|
||||
|
|
@ -232,7 +271,6 @@ export function register(server: McpServer, ctx: ToolContext) {
|
|||
"Add messages to a session from specific peers.",
|
||||
"Use this for multi-peer conversations where you need to attribute messages to specific peers.",
|
||||
"For the simple user/assistant flow, use add_turn instead.",
|
||||
"Returns a confirmation message.",
|
||||
].join("\n"),
|
||||
inputSchema: {
|
||||
session_id: z.string().describe("The session to add messages to."),
|
||||
|
|
@ -311,6 +349,46 @@ export function register(server: McpServer, ctx: ToolContext) {
|
|||
},
|
||||
);
|
||||
|
||||
// ── get_session_message ─────────────────────────────────────────────
|
||||
server.registerTool(
|
||||
"get_session_message",
|
||||
{
|
||||
description: [
|
||||
"Get a single message from a session by ID.",
|
||||
"Use this when you already know the message ID and need the exact record.",
|
||||
"Returns the message object.",
|
||||
].join("\n"),
|
||||
inputSchema: {
|
||||
session_id: z.string().describe("The session the message belongs to."),
|
||||
message_id: z.string().describe("The message ID to fetch."),
|
||||
},
|
||||
},
|
||||
async ({ session_id, message_id }) => {
|
||||
try {
|
||||
// Workaround: the current @honcho-ai/sdk Session API does not expose
|
||||
// a single-message getter, so we fetch the message by ID via raw HTTP.
|
||||
const messageData = await ctx.honcho.http.get<{
|
||||
id: string;
|
||||
content: string;
|
||||
peer_id: string;
|
||||
session_id: string;
|
||||
workspace_id: string;
|
||||
metadata: Record<string, unknown>;
|
||||
created_at: string;
|
||||
token_count: number;
|
||||
}>(
|
||||
`/v3/workspaces/${ctx.honcho.workspaceId}/sessions/${session_id}/messages/${message_id}`,
|
||||
);
|
||||
const message = Message.fromApiResponse(messageData);
|
||||
return textResult(formatMessage(message));
|
||||
} catch (e) {
|
||||
return errorResult(
|
||||
`Failed to get message: ${e instanceof Error ? e.message : String(e)}`,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// ── search_session_messages ─────────────────────────────────────────
|
||||
server.registerTool(
|
||||
"search_session_messages",
|
||||
|
|
@ -367,13 +445,7 @@ export function register(server: McpServer, ctx: ToolContext) {
|
|||
return textResult({
|
||||
session_id: context.sessionId,
|
||||
summary: context.summary,
|
||||
messages: context.messages.map((msg) => ({
|
||||
id: msg.id,
|
||||
content: msg.content,
|
||||
peer_id: msg.peerId,
|
||||
metadata: msg.metadata,
|
||||
created_at: msg.createdAt,
|
||||
})),
|
||||
messages: formatMessages(context.messages),
|
||||
});
|
||||
} catch (e) {
|
||||
return errorResult(
|
||||
|
|
@ -383,6 +455,32 @@ export function register(server: McpServer, ctx: ToolContext) {
|
|||
},
|
||||
);
|
||||
|
||||
// ── get_session_summaries ───────────────────────────────────────────
|
||||
server.registerTool(
|
||||
"get_session_summaries",
|
||||
{
|
||||
description: [
|
||||
"Get the short and long summaries available for a session.",
|
||||
"Use this when you want Honcho's generated rollups of the conversation so far.",
|
||||
"Returns the session ID plus short and long summary objects when available.",
|
||||
].join("\n"),
|
||||
inputSchema: {
|
||||
session_id: z.string().describe("The session to get summaries for."),
|
||||
},
|
||||
},
|
||||
async ({ session_id }) => {
|
||||
try {
|
||||
const session = await ctx.honcho.session(session_id);
|
||||
const summaries = await session.summaries();
|
||||
return textResult(formatSessionSummaries(summaries));
|
||||
} catch (e) {
|
||||
return errorResult(
|
||||
`Failed to get session summaries: ${e instanceof Error ? e.message : String(e)}`,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// ── get_session_representation ──────────────────────────────────────
|
||||
server.registerTool(
|
||||
"get_session_representation",
|
||||
|
|
@ -425,9 +523,7 @@ export function register(server: McpServer, ctx: ToolContext) {
|
|||
"get_session_metadata",
|
||||
{
|
||||
description: [
|
||||
"Get the metadata dictionary for a session.",
|
||||
"Use this to read custom attributes stored on a session.",
|
||||
"Returns a JSON object.",
|
||||
"Get metadata for a session.",
|
||||
].join("\n"),
|
||||
inputSchema: {
|
||||
session_id: z.string().describe("The session to get metadata for."),
|
||||
|
|
@ -451,9 +547,8 @@ export function register(server: McpServer, ctx: ToolContext) {
|
|||
"set_session_metadata",
|
||||
{
|
||||
description: [
|
||||
"Set metadata for a session (overwrites existing metadata).",
|
||||
"Use this to store custom attributes on a session.",
|
||||
"Returns a confirmation message.",
|
||||
"Set metadata for a session.",
|
||||
"Overwrites existing metadata.",
|
||||
].join("\n"),
|
||||
inputSchema: {
|
||||
session_id: z.string().describe("The session to set metadata for."),
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ export function register(server: McpServer, ctx: ToolContext) {
|
|||
"Schedule a dream — a background memory-consolidation task for a peer.",
|
||||
"Dreams consolidate observations into higher-level insights and update peer cards.",
|
||||
"Use this after a long conversation to improve Honcho's memory quality.",
|
||||
"Returns a confirmation message.",
|
||||
].join("\n"),
|
||||
inputSchema: {
|
||||
peer_id: z.string().describe("The observer peer to dream for."),
|
||||
|
|
|
|||
|
|
@ -4,6 +4,78 @@ import type { ToolContext } from "../types.js";
|
|||
import { textResult, errorResult, formatMessages } from "../types.js";
|
||||
|
||||
export function register(server: McpServer, ctx: ToolContext) {
|
||||
// ── inspect_workspace ───────────────────────────────────────────────
|
||||
server.registerTool(
|
||||
"inspect_workspace",
|
||||
{
|
||||
description: [
|
||||
"Inspect the current workspace at a glance.",
|
||||
"Aggregates workspace metadata, configuration, peer IDs, and session IDs.",
|
||||
"Returns a single JSON object.",
|
||||
].join("\n"),
|
||||
inputSchema: {},
|
||||
},
|
||||
async () => {
|
||||
try {
|
||||
const [metadata, configuration, peerPage, sessionPage] = await Promise.all([
|
||||
ctx.honcho.getMetadata(),
|
||||
ctx.honcho.getConfiguration(),
|
||||
ctx.honcho.peers(),
|
||||
ctx.honcho.sessions(),
|
||||
]);
|
||||
|
||||
const peers: { id: string }[] = [];
|
||||
for await (const peer of peerPage) {
|
||||
peers.push({ id: peer.id });
|
||||
}
|
||||
|
||||
const sessions: { id: string }[] = [];
|
||||
for await (const session of sessionPage) {
|
||||
sessions.push({ id: session.id });
|
||||
}
|
||||
|
||||
return textResult({
|
||||
workspace_id: ctx.honcho.workspaceId,
|
||||
metadata,
|
||||
configuration,
|
||||
peers,
|
||||
sessions,
|
||||
});
|
||||
} catch (e) {
|
||||
return errorResult(
|
||||
`Failed to inspect workspace: ${e instanceof Error ? e.message : String(e)}`,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// ── list_workspaces ─────────────────────────────────────────────────
|
||||
server.registerTool(
|
||||
"list_workspaces",
|
||||
{
|
||||
description: [
|
||||
"List all workspaces accessible to the current credentials.",
|
||||
"Use this to discover available workspaces before selecting or switching context.",
|
||||
"Returns an array of workspace IDs.",
|
||||
].join("\n"),
|
||||
inputSchema: {},
|
||||
},
|
||||
async () => {
|
||||
try {
|
||||
const page = await ctx.honcho.workspaces();
|
||||
const workspaces: { id: string }[] = [];
|
||||
for await (const workspace of page) {
|
||||
workspaces.push({ id: workspace });
|
||||
}
|
||||
return textResult(workspaces);
|
||||
} catch (e) {
|
||||
return errorResult(
|
||||
`Failed to list workspaces: ${e instanceof Error ? e.message : String(e)}`,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// ── search_workspace ────────────────────────────────────────────────
|
||||
server.registerTool(
|
||||
"search_workspace",
|
||||
|
|
@ -34,9 +106,7 @@ export function register(server: McpServer, ctx: ToolContext) {
|
|||
"get_workspace_metadata",
|
||||
{
|
||||
description: [
|
||||
"Get the metadata dictionary for the current workspace.",
|
||||
"Use this to read workspace-level settings or custom attributes.",
|
||||
"Returns a JSON object of key-value pairs.",
|
||||
"Get metadata for the current workspace.",
|
||||
].join("\n"),
|
||||
inputSchema: {},
|
||||
},
|
||||
|
|
@ -57,9 +127,8 @@ export function register(server: McpServer, ctx: ToolContext) {
|
|||
"set_workspace_metadata",
|
||||
{
|
||||
description: [
|
||||
"Set metadata for the current workspace (overwrites existing metadata).",
|
||||
"Use this to store workspace-level settings or custom attributes.",
|
||||
"Returns a confirmation message.",
|
||||
"Set metadata for the current workspace.",
|
||||
"Overwrites existing metadata.",
|
||||
].join("\n"),
|
||||
inputSchema: {
|
||||
metadata: z
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { Honcho, Message } from "@honcho-ai/sdk";
|
||||
import type { Honcho, Message, Summary, SessionSummaries } from "@honcho-ai/sdk";
|
||||
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
||||
import type { HonchoConfig } from "./config.js";
|
||||
|
||||
|
|
@ -18,14 +18,43 @@ export function errorResult(msg: string): CallToolResult {
|
|||
return { content: [{ type: "text", text: msg }], isError: true };
|
||||
}
|
||||
|
||||
/** Serialize a Message to a plain JSON-safe object. */
|
||||
export function formatMessage(message: Message) {
|
||||
return {
|
||||
id: message.id,
|
||||
content: message.content,
|
||||
peer_id: message.peerId,
|
||||
session_id: message.sessionId,
|
||||
metadata: message.metadata,
|
||||
created_at: message.createdAt,
|
||||
};
|
||||
}
|
||||
|
||||
/** Serialize a Summary to a plain JSON-safe object. */
|
||||
export function formatSummary(summary: Summary) {
|
||||
return {
|
||||
content: summary.content,
|
||||
message_id: summary.messageId,
|
||||
summary_type: summary.summaryType,
|
||||
created_at: summary.createdAt,
|
||||
token_count: summary.tokenCount,
|
||||
};
|
||||
}
|
||||
|
||||
/** Serialize SessionSummaries to a plain JSON-safe object. */
|
||||
export function formatSessionSummaries(summaries: SessionSummaries) {
|
||||
return {
|
||||
session_id: summaries.sessionId,
|
||||
short_summary: summaries.shortSummary
|
||||
? formatSummary(summaries.shortSummary)
|
||||
: null,
|
||||
long_summary: summaries.longSummary
|
||||
? formatSummary(summaries.longSummary)
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
/** Serialize a Message[] to a plain JSON-safe array. */
|
||||
export function formatMessages(messages: Message[]) {
|
||||
return messages.map((m) => ({
|
||||
id: m.id,
|
||||
content: m.content,
|
||||
peer_id: m.peerId,
|
||||
session_id: m.sessionId,
|
||||
metadata: m.metadata,
|
||||
created_at: m.createdAt,
|
||||
}));
|
||||
return messages.map(formatMessage);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue