mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-07-02 22:41:01 +02:00
refactor(ts): make port effect native
This commit is contained in:
parent
2868ced2d3
commit
b6759e75df
113 changed files with 4140 additions and 4554 deletions
|
|
@ -13,23 +13,21 @@
|
|||
"dependencies": {
|
||||
"@trustgraph/base": "workspace:*",
|
||||
"@trustgraph/client": "workspace:*",
|
||||
"@effect/platform-node": "4.0.0-beta.75",
|
||||
"@effect/platform-node-shared": "4.0.0-beta.75",
|
||||
"@effect/ai-anthropic": "4.0.0-beta.75",
|
||||
"@effect/ai-openai": "4.0.0-beta.75",
|
||||
"@effect/ai-openrouter": "4.0.0-beta.75",
|
||||
"@effect/atom-react": "4.0.0-beta.75",
|
||||
"@effect/openapi-generator": "4.0.0-beta.75",
|
||||
"@effect/opentelemetry": "4.0.0-beta.75",
|
||||
"@effect/platform-browser": "4.0.0-beta.75",
|
||||
"@effect/platform-bun": "4.0.0-beta.75",
|
||||
"@effect/tsgo": "0.13.0",
|
||||
"@effect/vitest": "4.0.0-beta.75",
|
||||
"@modelcontextprotocol/sdk": "^1.8.0",
|
||||
"zod": "^3.23.0"
|
||||
"@effect/platform-node": "4.0.0-beta.78",
|
||||
"@effect/platform-node-shared": "4.0.0-beta.78",
|
||||
"@effect/ai-anthropic": "4.0.0-beta.78",
|
||||
"@effect/ai-openai": "4.0.0-beta.78",
|
||||
"@effect/ai-openrouter": "4.0.0-beta.78",
|
||||
"@effect/atom-react": "4.0.0-beta.78",
|
||||
"@effect/openapi-generator": "4.0.0-beta.78",
|
||||
"@effect/opentelemetry": "4.0.0-beta.78",
|
||||
"@effect/platform-browser": "4.0.0-beta.78",
|
||||
"@effect/platform-bun": "4.0.0-beta.78",
|
||||
"@effect/tsgo": "0.14.0",
|
||||
"@effect/vitest": "4.0.0-beta.78"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@effect/vitest": "4.0.0-beta.75",
|
||||
"@effect/vitest": "4.0.0-beta.78",
|
||||
"@types/node": "^22.0.0",
|
||||
"typescript": "^5.8.0",
|
||||
"vitest": "^4.1.6"
|
||||
|
|
|
|||
|
|
@ -1,22 +1,21 @@
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
import * as Predicate from "effect/Predicate";
|
||||
import { createMcpServer } from "../server.js";
|
||||
import { describe, expect, it } from "@effect/vitest";
|
||||
import type { BaseApi } from "@trustgraph/client";
|
||||
import { Effect, Layer, Stream } from "effect";
|
||||
import * as S from "effect/Schema";
|
||||
import { LanguageModel, McpServer } from "effect/unstable/ai";
|
||||
import * as McpSchema from "effect/unstable/ai/McpSchema";
|
||||
import { FetchHttpClient, HttpRouter } from "effect/unstable/http";
|
||||
import { RpcSerialization } from "effect/unstable/rpc";
|
||||
import * as RpcClient from "effect/unstable/rpc/RpcClient";
|
||||
import {
|
||||
makeTrustGraphMcpStdioLayer,
|
||||
runStdio,
|
||||
TrustGraphMcpConfig,
|
||||
TrustGraphMcpToolkit,
|
||||
TrustGraphMcpToolkitLive,
|
||||
TrustGraphSocket,
|
||||
} from "../server-effect.js";
|
||||
|
||||
const clientMock = vi.hoisted(() => ({
|
||||
createTrustGraphSocket: vi.fn(() => ({
|
||||
close: vi.fn(),
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("@trustgraph/client", () => ({
|
||||
createTrustGraphSocket: clientMock.createTrustGraphSocket,
|
||||
}));
|
||||
|
||||
const expectedToolNames = [
|
||||
"text_completion",
|
||||
"graph_rag",
|
||||
|
|
@ -43,41 +42,290 @@ const expectedToolNames = [
|
|||
"load_kg_core",
|
||||
];
|
||||
|
||||
const registeredToolNames = (value: unknown): Array<string> => {
|
||||
if (!Predicate.isObject(value) || !Predicate.hasProperty(value, "_registeredTools")) {
|
||||
return [];
|
||||
}
|
||||
return Predicate.isObject(value._registeredTools)
|
||||
? Object.keys(value._registeredTools)
|
||||
: [];
|
||||
interface FakeSocketCalls {
|
||||
readonly flowIds: Array<string>;
|
||||
readonly graphRag: Array<{
|
||||
readonly query: string;
|
||||
readonly options: unknown;
|
||||
readonly collection: string | undefined;
|
||||
}>;
|
||||
}
|
||||
|
||||
interface NativeTestClientOptions {
|
||||
readonly languageText?: string | undefined;
|
||||
readonly graphRag?: (() => Promise<string>) | undefined;
|
||||
}
|
||||
|
||||
const decodeJsonText = S.decodeUnknownSync(S.UnknownFromJsonString);
|
||||
|
||||
const makeFakeSocket = (
|
||||
options: {
|
||||
readonly graphRag?: (() => Promise<string>) | undefined;
|
||||
} = {},
|
||||
) => {
|
||||
const calls: FakeSocketCalls = {
|
||||
flowIds: [],
|
||||
graphRag: [],
|
||||
};
|
||||
|
||||
const socket = {
|
||||
close: () => {},
|
||||
flow: (flowId: string) => {
|
||||
calls.flowIds.push(flowId);
|
||||
return {
|
||||
textCompletion: () => Promise.resolve("legacy text completion should not be used"),
|
||||
graphRag: (query: string, ragOptions: unknown, collection?: string) => {
|
||||
calls.graphRag.push({ query, options: ragOptions, collection });
|
||||
return options.graphRag === undefined
|
||||
? Promise.resolve("graph rag answer")
|
||||
: options.graphRag();
|
||||
},
|
||||
documentRag: () => Promise.resolve("document rag answer"),
|
||||
agent: (
|
||||
_question: string,
|
||||
_onThought: () => void,
|
||||
_onObservation: () => void,
|
||||
onAnswer: (chunk: string, complete: boolean) => void,
|
||||
) => onAnswer("agent answer", true),
|
||||
embeddings: () => Promise.resolve([[0.25, 0.75]]),
|
||||
triplesQuery: () => Promise.resolve([]),
|
||||
graphEmbeddingsQuery: () => Promise.resolve([]),
|
||||
};
|
||||
},
|
||||
config: () => ({
|
||||
getConfigAll: () => Promise.resolve({}),
|
||||
getConfig: () => Promise.resolve({}),
|
||||
putConfig: () => Promise.resolve({ ok: true }),
|
||||
deleteConfig: () => Promise.resolve({ ok: true }),
|
||||
getPrompts: () => Promise.resolve([]),
|
||||
getPrompt: () => Promise.resolve({}),
|
||||
}),
|
||||
flows: () => ({
|
||||
getFlows: () => Promise.resolve(["default"]),
|
||||
getFlow: () => Promise.resolve({}),
|
||||
startFlow: () => Promise.resolve({ ok: true }),
|
||||
stopFlow: () => Promise.resolve({ ok: true }),
|
||||
}),
|
||||
librarian: () => ({
|
||||
getDocuments: () => Promise.resolve([]),
|
||||
loadDocument: () => Promise.resolve({ ok: true }),
|
||||
removeDocument: () => Promise.resolve({ ok: true }),
|
||||
}),
|
||||
knowledge: () => ({
|
||||
getKnowledgeCores: () => Promise.resolve([]),
|
||||
deleteKgCore: () => Promise.resolve({ ok: true }),
|
||||
loadKgCore: () => Promise.resolve({ ok: true }),
|
||||
}),
|
||||
} as unknown as BaseApi;
|
||||
|
||||
return { socket, calls };
|
||||
};
|
||||
|
||||
const makeLanguageModelLayer = (text: string) =>
|
||||
Layer.effect(
|
||||
LanguageModel.LanguageModel,
|
||||
LanguageModel.make({
|
||||
generateText: () => Effect.succeed([{ type: "text", text }]),
|
||||
streamText: () => Stream.empty,
|
||||
}),
|
||||
);
|
||||
|
||||
const testConfig = TrustGraphMcpConfig.of({
|
||||
gatewayUrl: "ws://localhost:8088/api/v1/rpc",
|
||||
user: "mcp-test",
|
||||
token: undefined,
|
||||
flowId: "default",
|
||||
name: "trustgraph",
|
||||
version: "0.1.0-test",
|
||||
mcpPath: "/mcp",
|
||||
openAiModel: "test-model",
|
||||
openAiApiKey: undefined,
|
||||
port: 3000,
|
||||
});
|
||||
|
||||
const makeNativeTestClient = (
|
||||
options: NativeTestClientOptions = {},
|
||||
) =>
|
||||
makeNativeTestClientEffect(options);
|
||||
|
||||
const makeNativeTestClientEffect = Effect.fn("makeNativeTestClient")(function*(
|
||||
options: NativeTestClientOptions,
|
||||
) {
|
||||
const { socket, calls } = makeFakeSocket({ graphRag: options.graphRag });
|
||||
const serverLayer = McpServer.toolkit(TrustGraphMcpToolkit).pipe(
|
||||
Layer.provide(TrustGraphMcpToolkitLive),
|
||||
Layer.provide(makeLanguageModelLayer(options.languageText ?? "direct ai answer")),
|
||||
Layer.provide(Layer.succeed(TrustGraphSocket, TrustGraphSocket.of(socket))),
|
||||
Layer.provide(Layer.succeed(TrustGraphMcpConfig, testConfig)),
|
||||
Layer.provide(McpServer.layerHttp({
|
||||
name: "trustgraph",
|
||||
version: "0.1.0-test",
|
||||
path: "/mcp",
|
||||
})),
|
||||
);
|
||||
|
||||
const { handler, dispose } = HttpRouter.toWebHandler(serverLayer, { disableLogger: true });
|
||||
yield* Effect.addFinalizer(() => Effect.promise(() => dispose()));
|
||||
|
||||
let sessionId: string | null = null;
|
||||
const customFetch = Object.assign(
|
||||
(input: RequestInfo | URL, init?: RequestInit) => {
|
||||
const request = input instanceof Request ? input : new Request(input, init);
|
||||
if (sessionId !== null) {
|
||||
request.headers.set("Mcp-Session-Id", sessionId);
|
||||
}
|
||||
return handler(request).then((response) => {
|
||||
sessionId = response.headers.get("Mcp-Session-Id");
|
||||
return response;
|
||||
});
|
||||
},
|
||||
{ preconnect: fetch.preconnect },
|
||||
) as typeof fetch;
|
||||
|
||||
const clientLayer = RpcClient.layerProtocolHttp({ url: "http://localhost/mcp" }).pipe(
|
||||
Layer.provideMerge([FetchHttpClient.layer, RpcSerialization.layerJsonRpc()]),
|
||||
Layer.provide(Layer.succeed(FetchHttpClient.Fetch, customFetch)),
|
||||
);
|
||||
const client = yield* RpcClient.make(McpSchema.ClientRpcs).pipe(
|
||||
// @effect-diagnostics-next-line strictEffectProvide:off
|
||||
Effect.provide(clientLayer),
|
||||
);
|
||||
|
||||
yield* client.initialize({
|
||||
protocolVersion: "9999-01-01",
|
||||
capabilities: {},
|
||||
clientInfo: {
|
||||
name: "trustgraph-mcp-test-client",
|
||||
version: "0.1.0-test",
|
||||
},
|
||||
});
|
||||
|
||||
return { client, calls };
|
||||
});
|
||||
|
||||
const textContent = (result: McpSchema.CallToolResult): string => {
|
||||
const [content] = result.content;
|
||||
expect(content?.type).toBe("text");
|
||||
return "text" in content! ? content.text : "";
|
||||
};
|
||||
|
||||
describe("Effect MCP server", () => {
|
||||
it("keeps the canonical Effect toolkit names stable", () => {
|
||||
expect(Object.keys(TrustGraphMcpToolkit.tools)).toEqual(expectedToolNames);
|
||||
});
|
||||
it.effect(
|
||||
"keeps the canonical Effect toolkit names stable",
|
||||
Effect.fnUntraced(function*() {
|
||||
expect(Object.keys(TrustGraphMcpToolkit.tools)).toEqual(expectedToolNames);
|
||||
}),
|
||||
);
|
||||
|
||||
it("keeps legacy SDK stdio tools aligned with the Effect toolkit", () => {
|
||||
const { server, socket } = createMcpServer({
|
||||
gatewayUrl: "ws://localhost:8088/api/v1/rpc",
|
||||
user: "mcp-test",
|
||||
flowId: "default",
|
||||
});
|
||||
it.effect(
|
||||
"exposes an Effect stdio layer and process entrypoint",
|
||||
Effect.fnUntraced(function*() {
|
||||
expect(
|
||||
makeTrustGraphMcpStdioLayer({
|
||||
gatewayUrl: "ws://localhost:8088/api/v1/rpc",
|
||||
user: "mcp-test",
|
||||
flowId: "default",
|
||||
openAiApiKey: "test-key",
|
||||
}),
|
||||
).toBeDefined();
|
||||
|
||||
expect(registeredToolNames(server)).toEqual(Object.keys(TrustGraphMcpToolkit.tools));
|
||||
expect(socket.close).toEqual(expect.any(Function));
|
||||
});
|
||||
expect(runStdio).toEqual(expect.any(Function));
|
||||
}),
|
||||
);
|
||||
|
||||
it("exposes an Effect stdio layer and process entrypoint", () => {
|
||||
expect(
|
||||
makeTrustGraphMcpStdioLayer({
|
||||
gatewayUrl: "ws://localhost:8088/api/v1/rpc",
|
||||
user: "mcp-test",
|
||||
flowId: "default",
|
||||
openAiApiKey: "test-key",
|
||||
}),
|
||||
).toBeDefined();
|
||||
it.effect(
|
||||
"lists native MCP tools through the protocol bridge",
|
||||
Effect.fnUntraced(function*() {
|
||||
yield* Effect.scoped(Effect.gen(function*() {
|
||||
const { client } = yield* makeNativeTestClient();
|
||||
|
||||
expect(runStdio).toEqual(expect.any(Function));
|
||||
});
|
||||
const result = yield* client["tools/list"]({});
|
||||
expect(result.tools.map((tool) => tool.name)).toEqual(expectedToolNames);
|
||||
expect(result.tools.find((tool) => tool.name === "graph_rag")?.annotations).toMatchObject({
|
||||
title: "Graph RAG",
|
||||
readOnlyHint: true,
|
||||
destructiveHint: false,
|
||||
openWorldHint: true,
|
||||
});
|
||||
}));
|
||||
}),
|
||||
);
|
||||
|
||||
it.effect(
|
||||
"calls text_completion through the direct Effect language model",
|
||||
Effect.fnUntraced(function*() {
|
||||
yield* Effect.scoped(Effect.gen(function*() {
|
||||
const { client, calls } = yield* makeNativeTestClient({
|
||||
languageText: "direct model response",
|
||||
});
|
||||
|
||||
const result = yield* client["tools/call"]({
|
||||
name: "text_completion",
|
||||
arguments: {
|
||||
system: "You are concise.",
|
||||
prompt: "Say hello.",
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.isError).toBe(false);
|
||||
expect(result.structuredContent).toEqual({ text: "direct model response" });
|
||||
expect(decodeJsonText(textContent(result))).toEqual({ text: "direct model response" });
|
||||
expect(calls.flowIds).toEqual([]);
|
||||
}));
|
||||
}),
|
||||
);
|
||||
|
||||
it.effect(
|
||||
"calls gateway-backed tools through the native MCP bridge",
|
||||
Effect.fnUntraced(function*() {
|
||||
yield* Effect.scoped(Effect.gen(function*() {
|
||||
const { client, calls } = yield* makeNativeTestClient();
|
||||
|
||||
const result = yield* client["tools/call"]({
|
||||
name: "graph_rag",
|
||||
arguments: {
|
||||
query: "Who knows Alice?",
|
||||
entity_limit: 4,
|
||||
triple_limit: 8,
|
||||
collection: "qa",
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.isError).toBe(false);
|
||||
expect(result.structuredContent).toEqual({ text: "graph rag answer" });
|
||||
expect(calls.graphRag).toEqual([
|
||||
{
|
||||
query: "Who knows Alice?",
|
||||
options: { entityLimit: 4, tripleLimit: 8 },
|
||||
collection: "qa",
|
||||
},
|
||||
]);
|
||||
}));
|
||||
}),
|
||||
);
|
||||
|
||||
it.effect(
|
||||
"returns JSON-safe structured failures for expected tool errors",
|
||||
Effect.fnUntraced(function*() {
|
||||
yield* Effect.scoped(Effect.gen(function*() {
|
||||
const { client } = yield* makeNativeTestClient({
|
||||
graphRag: () => Promise.reject(new Error("gateway unavailable")),
|
||||
});
|
||||
|
||||
const result = yield* client["tools/call"]({
|
||||
name: "graph_rag",
|
||||
arguments: {
|
||||
query: "Will this fail?",
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.structuredContent).toEqual({
|
||||
_tag: "GraphRagError",
|
||||
message: "gateway unavailable",
|
||||
});
|
||||
expect(result.structuredContent).not.toHaveProperty("cause");
|
||||
expect(decodeJsonText(textContent(result))).toEqual(result.structuredContent);
|
||||
}));
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
export { createMcpServer, run } from "./server.js";
|
||||
export { runStdio as run } from "./server-effect.js";
|
||||
export * from "./server-effect.js";
|
||||
|
|
|
|||
|
|
@ -113,10 +113,6 @@ const TrustGraphJsonPayload = S.Json.annotateKey({
|
|||
description: "JSON-safe payload returned by the TrustGraph gateway",
|
||||
})
|
||||
|
||||
const ToolErrorCause = S.DefectWithStack.annotateKey({
|
||||
description: "Original exception, schema decoding failure, or gateway error that caused the tool call to fail",
|
||||
})
|
||||
|
||||
const ToolErrorMessage = S.String.annotateKey({
|
||||
description: "Concise human-readable error message suitable for explaining the failure to a user",
|
||||
})
|
||||
|
|
@ -141,7 +137,6 @@ export class TextCompletionSuccess extends S.Class<TextCompletionSuccess>("TextC
|
|||
export class TextCompletionError extends S.TaggedErrorClass<TextCompletionError>()(
|
||||
"TextCompletionError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -165,6 +160,7 @@ export const TextCompletionTool = annotateTool(
|
|||
parameters: TextCompletionParameters,
|
||||
success: TextCompletionSuccess,
|
||||
failure: TextCompletionError,
|
||||
failureMode: "return",
|
||||
}),
|
||||
{
|
||||
title: "Text Completion",
|
||||
|
|
@ -185,7 +181,6 @@ export class GraphRagSuccess extends S.Class<GraphRagSuccess>("GraphRagSuccess")
|
|||
export class GraphRagError extends S.TaggedErrorClass<GraphRagError>()(
|
||||
"GraphRagError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -215,6 +210,7 @@ export const GraphRagTool = annotateTool(
|
|||
parameters: GraphRagParameters,
|
||||
success: GraphRagSuccess,
|
||||
failure: GraphRagError,
|
||||
failureMode: "return",
|
||||
}),
|
||||
{
|
||||
title: "Graph RAG",
|
||||
|
|
@ -235,7 +231,6 @@ export class DocumentRagSuccess extends S.Class<DocumentRagSuccess>("DocumentRag
|
|||
export class DocumentRagError extends S.TaggedErrorClass<DocumentRagError>()(
|
||||
"DocumentRagError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -263,6 +258,7 @@ export const DocumentRagTool = annotateTool(
|
|||
parameters: DocumentRagParameters,
|
||||
success: DocumentRagSuccess,
|
||||
failure: DocumentRagError,
|
||||
failureMode: "return",
|
||||
}),
|
||||
{
|
||||
title: "Document RAG",
|
||||
|
|
@ -283,7 +279,6 @@ export class AgentSuccess extends S.Class<AgentSuccess>("AgentSuccess")(
|
|||
export class AgentError extends S.TaggedErrorClass<AgentError>()(
|
||||
"AgentError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -303,6 +298,7 @@ export const AgentTool = annotateTool(
|
|||
parameters: AgentParameters,
|
||||
success: AgentSuccess,
|
||||
failure: AgentError,
|
||||
failureMode: "return",
|
||||
description: "Ask the TrustGraph agent a question"
|
||||
}),
|
||||
{
|
||||
|
|
@ -326,7 +322,6 @@ export class EmbeddingsSuccess extends S.Class<EmbeddingsSuccess>("EmbeddingsSuc
|
|||
export class EmbeddingsError extends S.TaggedErrorClass<EmbeddingsError>()(
|
||||
"EmbeddingsError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -346,6 +341,7 @@ export const EmbeddingsTool = annotateTool(
|
|||
parameters: EmbeddingsParameters,
|
||||
success: EmbeddingsSuccess,
|
||||
failure: EmbeddingsError,
|
||||
failureMode: "return",
|
||||
description: "Generate text embeddings"
|
||||
}),
|
||||
{
|
||||
|
|
@ -369,7 +365,6 @@ export class TriplesQuerySuccess extends S.Class<TriplesQuerySuccess>("TriplesQu
|
|||
export class TriplesQueryError extends S.TaggedErrorClass<TriplesQueryError>()(
|
||||
"TriplesQueryError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -402,6 +397,7 @@ export const TriplesQueryTool = annotateTool(
|
|||
parameters: TriplesQueryParameters,
|
||||
success: TriplesQuerySuccess,
|
||||
failure: TriplesQueryError,
|
||||
failureMode: "return",
|
||||
description: "Query the knowledge graph for triples matching a pattern"
|
||||
}),
|
||||
{
|
||||
|
|
@ -434,7 +430,6 @@ export class GraphEmbeddingsQuerySuccess extends S.Class<GraphEmbeddingsQuerySuc
|
|||
export class GraphEmbeddingsQueryError extends S.TaggedErrorClass<GraphEmbeddingsQueryError>()(
|
||||
"GraphEmbeddingsQueryError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -461,6 +456,7 @@ export const GraphEmbeddingsQueryTool = annotateTool(
|
|||
parameters: GraphEmbeddingsQueryParameters,
|
||||
success: GraphEmbeddingsQuerySuccess,
|
||||
failure: GraphEmbeddingsQueryError,
|
||||
failureMode: "return",
|
||||
description: "Find entities similar to a text query using vector embeddings"
|
||||
}),
|
||||
{
|
||||
|
|
@ -482,7 +478,6 @@ export class GetConfigAllSuccess extends S.Class<GetConfigAllSuccess>("GetConfig
|
|||
export class GetConfigAllError extends S.TaggedErrorClass<GetConfigAllError>()(
|
||||
"GetConfigAllError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -498,6 +493,7 @@ export const GetConfigAllTool = annotateTool(
|
|||
parameters: GetConfigAllParameters,
|
||||
success: GetConfigAllSuccess,
|
||||
failure: GetConfigAllError,
|
||||
failureMode: "return",
|
||||
description: "Get all configuration values"
|
||||
}),
|
||||
{
|
||||
|
|
@ -520,7 +516,6 @@ export class GetConfigSuccess extends S.Class<GetConfigSuccess>("GetConfigSucces
|
|||
export class GetConfigError extends S.TaggedErrorClass<GetConfigError>()(
|
||||
"GetConfigError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -549,6 +544,7 @@ export const GetConfigTool = annotateTool(
|
|||
parameters: GetConfigParameters,
|
||||
success: GetConfigSuccess,
|
||||
failure: GetConfigError,
|
||||
failureMode: "return",
|
||||
description: "Get specific configuration values"
|
||||
}),
|
||||
{
|
||||
|
|
@ -570,7 +566,6 @@ export class PutConfigSuccess extends S.Class<PutConfigSuccess>("PutConfigSucces
|
|||
export class PutConfigError extends S.TaggedErrorClass<PutConfigError>()(
|
||||
"PutConfigError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -602,6 +597,7 @@ export const PutConfigTool = annotateTool(
|
|||
parameters: PutConfigParameters,
|
||||
success: PutConfigSuccess,
|
||||
failure: PutConfigError,
|
||||
failureMode: "return",
|
||||
description: "Set configuration values"
|
||||
}),
|
||||
{
|
||||
|
|
@ -623,7 +619,6 @@ export class DeleteConfigSuccess extends S.Class<DeleteConfigSuccess>("DeleteCon
|
|||
export class DeleteConfigError extends S.TaggedErrorClass<DeleteConfigError>()(
|
||||
"DeleteConfigError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -646,6 +641,7 @@ export const DeleteConfigTool = annotateTool(
|
|||
parameters: DeleteConfigParameters,
|
||||
success: DeleteConfigSuccess,
|
||||
failure: DeleteConfigError,
|
||||
failureMode: "return",
|
||||
description: "Delete a configuration entry"
|
||||
}),
|
||||
{
|
||||
|
|
@ -667,7 +663,6 @@ export class GetFlowSuccess extends S.Class<GetFlowSuccess>("GetFlowSuccess")(
|
|||
export class GetFlowError extends S.TaggedErrorClass<GetFlowError>()(
|
||||
"GetFlowError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -687,6 +682,7 @@ export const GetFlowTool = annotateTool(
|
|||
parameters: GetFlowParameters,
|
||||
success: GetFlowSuccess,
|
||||
failure: GetFlowError,
|
||||
failureMode: "return",
|
||||
description: "Get a specific flow definition"
|
||||
}),
|
||||
{
|
||||
|
|
@ -710,7 +706,6 @@ export class GetFlowsSuccess extends S.Class<GetFlowsSuccess>("GetFlowsSuccess")
|
|||
export class GetFlowsError extends S.TaggedErrorClass<GetFlowsError>()(
|
||||
"GetFlowsError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -726,6 +721,7 @@ export const GetFlowsTool = annotateTool(
|
|||
parameters: GetFlowsParameters,
|
||||
success: GetFlowsSuccess,
|
||||
failure: GetFlowsError,
|
||||
failureMode: "return",
|
||||
description: "List all available flows"
|
||||
}),
|
||||
{
|
||||
|
|
@ -747,7 +743,6 @@ export class StartFlowSuccess extends S.Class<StartFlowSuccess>("StartFlowSucces
|
|||
export class StartFlowError extends S.TaggedErrorClass<StartFlowError>()(
|
||||
"StartFlowError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -776,6 +771,7 @@ export const StartFlowTool = annotateTool(
|
|||
parameters: StartFlowParameters,
|
||||
success: StartFlowSuccess,
|
||||
failure: StartFlowError,
|
||||
failureMode: "return",
|
||||
description: "Start a flow instance"
|
||||
}),
|
||||
{
|
||||
|
|
@ -797,7 +793,6 @@ export class StopFlowSuccess extends S.Class<StopFlowSuccess>("StopFlowSuccess")
|
|||
export class StopFlowError extends S.TaggedErrorClass<StopFlowError>()(
|
||||
"StopFlowError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -817,6 +812,7 @@ export const StopFlowTool = annotateTool(
|
|||
parameters: StopFlowParameters,
|
||||
success: StopFlowSuccess,
|
||||
failure: StopFlowError,
|
||||
failureMode: "return",
|
||||
description: "Stop a running flow"
|
||||
}),
|
||||
{
|
||||
|
|
@ -840,7 +836,6 @@ export class GetDocumentsSuccess extends S.Class<GetDocumentsSuccess>("GetDocume
|
|||
export class GetDocumentsError extends S.TaggedErrorClass<GetDocumentsError>()(
|
||||
"GetDocumentsError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -856,6 +851,7 @@ export const GetDocumentsTool = annotateTool(
|
|||
parameters: GetDocumentsParameters,
|
||||
success: GetDocumentsSuccess,
|
||||
failure: GetDocumentsError,
|
||||
failureMode: "return",
|
||||
description: "List all documents in the library"
|
||||
}),
|
||||
{
|
||||
|
|
@ -877,7 +873,6 @@ export class LoadDocumentSuccess extends S.Class<LoadDocumentSuccess>("LoadDocum
|
|||
export class LoadDocumentError extends S.TaggedErrorClass<LoadDocumentError>()(
|
||||
"LoadDocumentError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -912,6 +907,7 @@ export const LoadDocumentTool = annotateTool(
|
|||
parameters: LoadDocumentParameters,
|
||||
success: LoadDocumentSuccess,
|
||||
failure: LoadDocumentError,
|
||||
failureMode: "return",
|
||||
description: "Upload a document to the library"
|
||||
}),
|
||||
{
|
||||
|
|
@ -933,7 +929,6 @@ export class RemoveDocumentSuccess extends S.Class<RemoveDocumentSuccess>("Remov
|
|||
export class RemoveDocumentError extends S.TaggedErrorClass<RemoveDocumentError>()(
|
||||
"RemoveDocumentError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -956,6 +951,7 @@ export const RemoveDocumentTool = annotateTool(
|
|||
parameters: RemoveDocumentParameters,
|
||||
success: RemoveDocumentSuccess,
|
||||
failure: RemoveDocumentError,
|
||||
failureMode: "return",
|
||||
description: "Remove a document from the library"
|
||||
}),
|
||||
{
|
||||
|
|
@ -979,7 +975,6 @@ export class GetPromptsSuccess extends S.Class<GetPromptsSuccess>("GetPromptsSuc
|
|||
export class GetPromptsError extends S.TaggedErrorClass<GetPromptsError>()(
|
||||
"GetPromptsError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -995,6 +990,7 @@ export const GetPromptsTool = annotateTool(
|
|||
parameters: GetPromptsParameters,
|
||||
success: GetPromptsSuccess,
|
||||
failure: GetPromptsError,
|
||||
failureMode: "return",
|
||||
description: "List available prompt templates"
|
||||
}),
|
||||
{
|
||||
|
|
@ -1016,7 +1012,6 @@ export class GetPromptSuccess extends S.Class<GetPromptSuccess>("GetPromptSucces
|
|||
export class GetPromptError extends S.TaggedErrorClass<GetPromptError>()(
|
||||
"GetPromptError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -1036,6 +1031,7 @@ export const GetPromptTool = annotateTool(
|
|||
parameters: GetPromptParameters,
|
||||
success: GetPromptSuccess,
|
||||
failure: GetPromptError,
|
||||
failureMode: "return",
|
||||
description: "Get a specific prompt template"
|
||||
}),
|
||||
{
|
||||
|
|
@ -1059,7 +1055,6 @@ export class GetKnowledgeCoresSuccess extends S.Class<GetKnowledgeCoresSuccess>(
|
|||
export class GetKnowledgeCoresError extends S.TaggedErrorClass<GetKnowledgeCoresError>()(
|
||||
"GetKnowledgeCoresError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -1075,6 +1070,7 @@ export const GetKnowledgeCoresTool = annotateTool(
|
|||
parameters: GetKnowledgeCoresParameters,
|
||||
success: GetKnowledgeCoresSuccess,
|
||||
failure: GetKnowledgeCoresError,
|
||||
failureMode: "return",
|
||||
description: "List available knowledge graph cores"
|
||||
}),
|
||||
{
|
||||
|
|
@ -1096,7 +1092,6 @@ export class DeleteKgCoreSuccess extends S.Class<DeleteKgCoreSuccess>("DeleteKgC
|
|||
export class DeleteKgCoreError extends S.TaggedErrorClass<DeleteKgCoreError>()(
|
||||
"DeleteKgCoreError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -1119,6 +1114,7 @@ export const DeleteKgCoreTool = annotateTool(
|
|||
parameters: DeleteKgCoreParameters,
|
||||
success: DeleteKgCoreSuccess,
|
||||
failure: DeleteKgCoreError,
|
||||
failureMode: "return",
|
||||
description: "Delete a knowledge graph core"
|
||||
}),
|
||||
{
|
||||
|
|
@ -1140,7 +1136,6 @@ export class LoadKgCoreSuccess extends S.Class<LoadKgCoreSuccess>("LoadKgCoreSuc
|
|||
export class LoadKgCoreError extends S.TaggedErrorClass<LoadKgCoreError>()(
|
||||
"LoadKgCoreError",
|
||||
{
|
||||
cause: ToolErrorCause,
|
||||
message: ToolErrorMessage,
|
||||
}
|
||||
) {
|
||||
|
|
@ -1166,6 +1161,7 @@ export const LoadKgCoreTool = annotateTool(
|
|||
parameters: LoadKgCoreParameters,
|
||||
success: LoadKgCoreSuccess,
|
||||
failure: LoadKgCoreError,
|
||||
failureMode: "return",
|
||||
description: "Load a knowledge graph core"
|
||||
}),
|
||||
{
|
||||
|
|
@ -1384,7 +1380,7 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
},
|
||||
collection,
|
||||
),
|
||||
catch: (cause) => GraphRagError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => GraphRagError.make({message: toErrorMessage(cause)}),
|
||||
}).pipe(
|
||||
Effect.map((text) => GraphRagSuccess.make({text})),
|
||||
),
|
||||
|
|
@ -1392,7 +1388,7 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
document_rag: ({query, doc_limit, collection}) =>
|
||||
Effect.tryPromise({
|
||||
try: () => socket.flow(config.flowId).documentRag(query, doc_limit, collection),
|
||||
catch: (cause) => DocumentRagError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => DocumentRagError.make({message: toErrorMessage(cause)}),
|
||||
}).pipe(
|
||||
Effect.map((text) => DocumentRagSuccess.make({text})),
|
||||
),
|
||||
|
|
@ -1410,14 +1406,14 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
resume(Effect.succeed(AgentSuccess.make({text: fullAnswer})))
|
||||
}
|
||||
},
|
||||
(cause) => resume(Effect.fail(AgentError.make({cause, message: toErrorMessage(cause)}))),
|
||||
(cause) => resume(Effect.fail(AgentError.make({message: toErrorMessage(cause)}))),
|
||||
)
|
||||
}),
|
||||
|
||||
embeddings: ({text}) =>
|
||||
Effect.tryPromise({
|
||||
try: () => socket.flow(config.flowId).embeddings([...text]),
|
||||
catch: (cause) => EmbeddingsError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => EmbeddingsError.make({message: toErrorMessage(cause)}),
|
||||
}).pipe(
|
||||
Effect.map((vectors) => EmbeddingsSuccess.make({vectors})),
|
||||
),
|
||||
|
|
@ -1432,7 +1428,7 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
limit,
|
||||
collection,
|
||||
),
|
||||
catch: (cause) => TriplesQueryError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => TriplesQueryError.make({message: toErrorMessage(cause)}),
|
||||
}).pipe(
|
||||
Effect.map((triples) => TriplesQuerySuccess.make({triples})),
|
||||
),
|
||||
|
|
@ -1440,7 +1436,7 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
graph_embeddings_query: ({query, limit, collection}) =>
|
||||
Effect.tryPromise({
|
||||
try: () => socket.flow(config.flowId).embeddings([query]),
|
||||
catch: (cause) => GraphEmbeddingsQueryError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => GraphEmbeddingsQueryError.make({message: toErrorMessage(cause)}),
|
||||
}).pipe(
|
||||
Effect.flatMap((vectors) =>
|
||||
Effect.tryPromise({
|
||||
|
|
@ -1449,7 +1445,7 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
limit ?? 10,
|
||||
collection,
|
||||
),
|
||||
catch: (cause) => GraphEmbeddingsQueryError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => GraphEmbeddingsQueryError.make({message: toErrorMessage(cause)}),
|
||||
})
|
||||
),
|
||||
Effect.map((entities) => GraphEmbeddingsQuerySuccess.make({entities})),
|
||||
|
|
@ -1458,12 +1454,12 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
get_config_all: () =>
|
||||
Effect.tryPromise({
|
||||
try: () => socket.config().getConfigAll(),
|
||||
catch: (cause) => GetConfigAllError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => GetConfigAllError.make({message: toErrorMessage(cause)}),
|
||||
}).pipe(
|
||||
Effect.flatMap((value) =>
|
||||
decodeJsonOrFail(
|
||||
value,
|
||||
(cause) => GetConfigAllError.make({cause, message: toErrorMessage(cause)}),
|
||||
(cause) => GetConfigAllError.make({message: toErrorMessage(cause)}),
|
||||
).pipe(
|
||||
Effect.map((config) => GetConfigAllSuccess.make({config})),
|
||||
)
|
||||
|
|
@ -1473,12 +1469,12 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
get_config: ({keys}) =>
|
||||
Effect.tryPromise({
|
||||
try: () => socket.config().getConfig(keys.map(({type, key}) => ({type, key}))),
|
||||
catch: (cause) => GetConfigError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => GetConfigError.make({message: toErrorMessage(cause)}),
|
||||
}).pipe(
|
||||
Effect.flatMap((value) =>
|
||||
decodeJsonOrFail(
|
||||
value,
|
||||
(cause) => GetConfigError.make({cause, message: toErrorMessage(cause)}),
|
||||
(cause) => GetConfigError.make({message: toErrorMessage(cause)}),
|
||||
).pipe(
|
||||
Effect.map((config) => GetConfigSuccess.make({config})),
|
||||
)
|
||||
|
|
@ -1488,12 +1484,12 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
put_config: ({values}) =>
|
||||
Effect.tryPromise({
|
||||
try: () => socket.config().putConfig(values.map(({type, key, value}) => ({type, key, value}))),
|
||||
catch: (cause) => PutConfigError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => PutConfigError.make({message: toErrorMessage(cause)}),
|
||||
}).pipe(
|
||||
Effect.flatMap((value) =>
|
||||
decodeJsonOrFail(
|
||||
value,
|
||||
(cause) => PutConfigError.make({cause, message: toErrorMessage(cause)}),
|
||||
(cause) => PutConfigError.make({message: toErrorMessage(cause)}),
|
||||
).pipe(
|
||||
Effect.map((response) => PutConfigSuccess.make({response})),
|
||||
)
|
||||
|
|
@ -1503,12 +1499,12 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
delete_config: ({type, key}) =>
|
||||
Effect.tryPromise({
|
||||
try: () => socket.config().deleteConfig({type, key}),
|
||||
catch: (cause) => DeleteConfigError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => DeleteConfigError.make({message: toErrorMessage(cause)}),
|
||||
}).pipe(
|
||||
Effect.flatMap((value) =>
|
||||
decodeJsonOrFail(
|
||||
value,
|
||||
(cause) => DeleteConfigError.make({cause, message: toErrorMessage(cause)}),
|
||||
(cause) => DeleteConfigError.make({message: toErrorMessage(cause)}),
|
||||
).pipe(
|
||||
Effect.map((response) => DeleteConfigSuccess.make({response})),
|
||||
)
|
||||
|
|
@ -1518,7 +1514,7 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
get_flows: () =>
|
||||
Effect.tryPromise({
|
||||
try: () => socket.flows().getFlows(),
|
||||
catch: (cause) => GetFlowsError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => GetFlowsError.make({message: toErrorMessage(cause)}),
|
||||
}).pipe(
|
||||
Effect.map((flow_ids) => GetFlowsSuccess.make({flow_ids})),
|
||||
),
|
||||
|
|
@ -1526,12 +1522,12 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
get_flow: ({flow_id}) =>
|
||||
Effect.tryPromise({
|
||||
try: () => socket.flows().getFlow(flow_id),
|
||||
catch: (cause) => GetFlowError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => GetFlowError.make({message: toErrorMessage(cause)}),
|
||||
}).pipe(
|
||||
Effect.flatMap((value) =>
|
||||
decodeJsonOrFail(
|
||||
value,
|
||||
(cause) => GetFlowError.make({cause, message: toErrorMessage(cause)}),
|
||||
(cause) => GetFlowError.make({message: toErrorMessage(cause)}),
|
||||
).pipe(
|
||||
Effect.map((flow) => GetFlowSuccess.make({flow})),
|
||||
)
|
||||
|
|
@ -1547,12 +1543,12 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
description,
|
||||
parameters === undefined ? undefined : {...parameters},
|
||||
),
|
||||
catch: (cause) => StartFlowError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => StartFlowError.make({message: toErrorMessage(cause)}),
|
||||
}).pipe(
|
||||
Effect.flatMap((value) =>
|
||||
decodeJsonOrFail(
|
||||
value,
|
||||
(cause) => StartFlowError.make({cause, message: toErrorMessage(cause)}),
|
||||
(cause) => StartFlowError.make({message: toErrorMessage(cause)}),
|
||||
).pipe(
|
||||
Effect.map((response) => StartFlowSuccess.make({response})),
|
||||
)
|
||||
|
|
@ -1562,12 +1558,12 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
stop_flow: ({flow_id}) =>
|
||||
Effect.tryPromise({
|
||||
try: () => socket.flows().stopFlow(flow_id),
|
||||
catch: (cause) => StopFlowError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => StopFlowError.make({message: toErrorMessage(cause)}),
|
||||
}).pipe(
|
||||
Effect.flatMap((value) =>
|
||||
decodeJsonOrFail(
|
||||
value,
|
||||
(cause) => StopFlowError.make({cause, message: toErrorMessage(cause)}),
|
||||
(cause) => StopFlowError.make({message: toErrorMessage(cause)}),
|
||||
).pipe(
|
||||
Effect.map((response) => StopFlowSuccess.make({response})),
|
||||
)
|
||||
|
|
@ -1577,12 +1573,12 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
get_documents: () =>
|
||||
Effect.tryPromise({
|
||||
try: () => socket.librarian().getDocuments(),
|
||||
catch: (cause) => GetDocumentsError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => GetDocumentsError.make({message: toErrorMessage(cause)}),
|
||||
}).pipe(
|
||||
Effect.flatMap((value) =>
|
||||
decodeJsonArrayOrFail(
|
||||
value,
|
||||
(cause) => GetDocumentsError.make({cause, message: toErrorMessage(cause)}),
|
||||
(cause) => GetDocumentsError.make({message: toErrorMessage(cause)}),
|
||||
).pipe(
|
||||
Effect.map((documents) => GetDocumentsSuccess.make({documents})),
|
||||
)
|
||||
|
|
@ -1600,12 +1596,12 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
tags === undefined ? [] : [...tags],
|
||||
id,
|
||||
),
|
||||
catch: (cause) => LoadDocumentError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => LoadDocumentError.make({message: toErrorMessage(cause)}),
|
||||
}).pipe(
|
||||
Effect.flatMap((value) =>
|
||||
decodeJsonOrFail(
|
||||
value,
|
||||
(cause) => LoadDocumentError.make({cause, message: toErrorMessage(cause)}),
|
||||
(cause) => LoadDocumentError.make({message: toErrorMessage(cause)}),
|
||||
).pipe(
|
||||
Effect.map((response) => LoadDocumentSuccess.make({response})),
|
||||
)
|
||||
|
|
@ -1615,12 +1611,12 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
remove_document: ({id, collection}) =>
|
||||
Effect.tryPromise({
|
||||
try: () => socket.librarian().removeDocument(id, collection),
|
||||
catch: (cause) => RemoveDocumentError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => RemoveDocumentError.make({message: toErrorMessage(cause)}),
|
||||
}).pipe(
|
||||
Effect.flatMap((value) =>
|
||||
decodeJsonOrFail(
|
||||
value,
|
||||
(cause) => RemoveDocumentError.make({cause, message: toErrorMessage(cause)}),
|
||||
(cause) => RemoveDocumentError.make({message: toErrorMessage(cause)}),
|
||||
).pipe(
|
||||
Effect.map((response) => RemoveDocumentSuccess.make({response})),
|
||||
)
|
||||
|
|
@ -1630,7 +1626,7 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
get_prompts: () =>
|
||||
Effect.tryPromise({
|
||||
try: () => socket.config().getPrompts(),
|
||||
catch: (cause) => GetPromptsError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => GetPromptsError.make({message: toErrorMessage(cause)}),
|
||||
}).pipe(
|
||||
Effect.map((prompts) => GetPromptsSuccess.make({prompts})),
|
||||
),
|
||||
|
|
@ -1638,12 +1634,12 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
get_prompt: ({id}) =>
|
||||
Effect.tryPromise({
|
||||
try: () => socket.config().getPrompt(id),
|
||||
catch: (cause) => GetPromptError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => GetPromptError.make({message: toErrorMessage(cause)}),
|
||||
}).pipe(
|
||||
Effect.flatMap((value) =>
|
||||
decodeJsonOrFail(
|
||||
value,
|
||||
(cause) => GetPromptError.make({cause, message: toErrorMessage(cause)}),
|
||||
(cause) => GetPromptError.make({message: toErrorMessage(cause)}),
|
||||
).pipe(
|
||||
Effect.map((prompt) => GetPromptSuccess.make({prompt})),
|
||||
)
|
||||
|
|
@ -1653,7 +1649,7 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
get_knowledge_cores: () =>
|
||||
Effect.tryPromise({
|
||||
try: () => socket.knowledge().getKnowledgeCores(),
|
||||
catch: (cause) => GetKnowledgeCoresError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => GetKnowledgeCoresError.make({message: toErrorMessage(cause)}),
|
||||
}).pipe(
|
||||
Effect.map((ids) => GetKnowledgeCoresSuccess.make({ids})),
|
||||
),
|
||||
|
|
@ -1661,12 +1657,12 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
delete_kg_core: ({id, collection}) =>
|
||||
Effect.tryPromise({
|
||||
try: () => socket.knowledge().deleteKgCore(id, collection),
|
||||
catch: (cause) => DeleteKgCoreError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => DeleteKgCoreError.make({message: toErrorMessage(cause)}),
|
||||
}).pipe(
|
||||
Effect.flatMap((value) =>
|
||||
decodeJsonOrFail(
|
||||
value,
|
||||
(cause) => DeleteKgCoreError.make({cause, message: toErrorMessage(cause)}),
|
||||
(cause) => DeleteKgCoreError.make({message: toErrorMessage(cause)}),
|
||||
).pipe(
|
||||
Effect.map((response) => DeleteKgCoreSuccess.make({response})),
|
||||
)
|
||||
|
|
@ -1676,12 +1672,12 @@ export const TrustGraphMcpToolkitLive = TrustGraphMcpToolkit.toLayer(
|
|||
load_kg_core: ({id, flow, collection}) =>
|
||||
Effect.tryPromise({
|
||||
try: () => socket.knowledge().loadKgCore(id, flow, collection),
|
||||
catch: (cause) => LoadKgCoreError.make({cause, message: toErrorMessage(cause)}),
|
||||
catch: (cause) => LoadKgCoreError.make({message: toErrorMessage(cause)}),
|
||||
}).pipe(
|
||||
Effect.flatMap((value) =>
|
||||
decodeJsonOrFail(
|
||||
value,
|
||||
(cause) => LoadKgCoreError.make({cause, message: toErrorMessage(cause)}),
|
||||
(cause) => LoadKgCoreError.make({message: toErrorMessage(cause)}),
|
||||
).pipe(
|
||||
Effect.map((response) => LoadKgCoreSuccess.make({response})),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,442 +0,0 @@
|
|||
/**
|
||||
* TrustGraph MCP stdio compatibility server.
|
||||
*
|
||||
* This keeps the original @modelcontextprotocol/sdk entry points available,
|
||||
* while moving gateway calls, callback bridging, JSON encoding, and config
|
||||
* reads behind Effect values.
|
||||
*/
|
||||
|
||||
import {McpServer} from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import {StdioServerTransport} from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import {NodeRuntime} from "@effect/platform-node";
|
||||
import {createTrustGraphSocket, type BaseApi, type Term} from "@trustgraph/client";
|
||||
import {Effect, Layer, ManagedRuntime} from "effect";
|
||||
import * as Predicate from "effect/Predicate";
|
||||
import * as S from "effect/Schema";
|
||||
import * as z from "zod";
|
||||
import {loadTrustGraphMcpConfig} from "./server-effect.js";
|
||||
|
||||
interface ToolTextContent {
|
||||
readonly type: "text"
|
||||
readonly text: string
|
||||
}
|
||||
|
||||
interface ToolTextResult extends Record<string, unknown> {
|
||||
readonly content: Array<ToolTextContent>
|
||||
}
|
||||
|
||||
class StdioMcpError extends S.TaggedErrorClass<StdioMcpError>()(
|
||||
"StdioMcpError",
|
||||
{
|
||||
cause: S.DefectWithStack,
|
||||
message: S.String,
|
||||
},
|
||||
) {
|
||||
}
|
||||
|
||||
const encodeJsonText = S.encodeUnknownEffect(S.UnknownFromJsonString);
|
||||
|
||||
const toErrorMessage = (cause: unknown): string => {
|
||||
if (Predicate.isError(cause) && cause.message.length > 0) {
|
||||
return cause.message;
|
||||
}
|
||||
if (Predicate.isString(cause) && cause.length > 0) {
|
||||
return cause;
|
||||
}
|
||||
if (Predicate.isObject(cause) && Predicate.hasProperty(cause, "message") && Predicate.isString(cause.message) && cause.message.length > 0) {
|
||||
return cause.message;
|
||||
}
|
||||
return "TrustGraph MCP stdio operation failed";
|
||||
};
|
||||
|
||||
const stdioMcpError = (cause: unknown) =>
|
||||
StdioMcpError.make({cause, message: toErrorMessage(cause)});
|
||||
|
||||
const textResult = (text: string): ToolTextResult => ({
|
||||
content: [{type: "text", text}],
|
||||
});
|
||||
|
||||
const gatewayRequest = <A>(request: () => Promise<A>) =>
|
||||
Effect.tryPromise({
|
||||
try: request,
|
||||
catch: stdioMcpError,
|
||||
});
|
||||
|
||||
const jsonText = (value: unknown) =>
|
||||
encodeJsonText(value).pipe(
|
||||
Effect.mapError(stdioMcpError),
|
||||
);
|
||||
|
||||
const runTextTool = (effect: Effect.Effect<string, StdioMcpError>) =>
|
||||
Effect.runPromise(effect.pipe(Effect.map(textResult)));
|
||||
|
||||
const runJsonTool = (effect: Effect.Effect<unknown, StdioMcpError>) =>
|
||||
Effect.runPromise(effect.pipe(Effect.flatMap(jsonText), Effect.map(textResult)));
|
||||
|
||||
export function createMcpServer(config: {
|
||||
gatewayUrl: string;
|
||||
user?: string;
|
||||
token?: string;
|
||||
flowId?: string;
|
||||
}) {
|
||||
const server = new McpServer({
|
||||
name: "trustgraph",
|
||||
version: "0.1.0",
|
||||
});
|
||||
|
||||
const user = config.user ?? "mcp";
|
||||
const socket: BaseApi = createTrustGraphSocket(
|
||||
user,
|
||||
config.token,
|
||||
config.gatewayUrl,
|
||||
);
|
||||
|
||||
const flowId = config.flowId ?? "default";
|
||||
|
||||
// ===================== Flow-scoped tools =====================
|
||||
|
||||
server.tool(
|
||||
"text_completion",
|
||||
"Run a text completion using the configured LLM",
|
||||
{
|
||||
system: z.string().describe("System prompt"),
|
||||
prompt: z.string().describe("User prompt"),
|
||||
},
|
||||
({system, prompt}) =>
|
||||
runTextTool(gatewayRequest(() => socket.flow(flowId).textCompletion(system, prompt))),
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"graph_rag",
|
||||
"Query the knowledge graph using RAG",
|
||||
{
|
||||
query: z.string().describe("Natural language query"),
|
||||
entity_limit: z.number().optional().describe("Max entities to retrieve"),
|
||||
triple_limit: z.number().optional().describe("Max triples per entity"),
|
||||
collection: z.string().optional().describe("Collection name"),
|
||||
},
|
||||
({query, entity_limit, triple_limit, collection}) =>
|
||||
runTextTool(
|
||||
gatewayRequest(() =>
|
||||
socket.flow(flowId).graphRag(
|
||||
query,
|
||||
{
|
||||
...(entity_limit !== undefined ? {entityLimit: entity_limit} : {}),
|
||||
...(triple_limit !== undefined ? {tripleLimit: triple_limit} : {}),
|
||||
},
|
||||
collection,
|
||||
)
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"document_rag",
|
||||
"Query documents using RAG",
|
||||
{
|
||||
query: z.string().describe("Natural language query"),
|
||||
doc_limit: z.number().optional().describe("Max documents to retrieve"),
|
||||
collection: z.string().optional().describe("Collection name"),
|
||||
},
|
||||
({query, doc_limit, collection}) =>
|
||||
runTextTool(gatewayRequest(() => socket.flow(flowId).documentRag(query, doc_limit, collection))),
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"agent",
|
||||
"Ask the TrustGraph agent a question",
|
||||
{
|
||||
question: z.string().describe("Question for the agent"),
|
||||
},
|
||||
({question}) =>
|
||||
runTextTool(
|
||||
Effect.callback<string, StdioMcpError>((resume) => {
|
||||
let fullAnswer = "";
|
||||
socket.flow(flowId).agent(
|
||||
question,
|
||||
() => {},
|
||||
() => {},
|
||||
(chunk, complete) => {
|
||||
fullAnswer += chunk;
|
||||
if (complete) {
|
||||
resume(Effect.succeed(fullAnswer));
|
||||
}
|
||||
},
|
||||
(cause) => resume(Effect.fail(stdioMcpError(cause))),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"embeddings",
|
||||
"Generate text embeddings",
|
||||
{
|
||||
text: z.array(z.string()).describe("Texts to embed"),
|
||||
},
|
||||
({text}) => runJsonTool(gatewayRequest(() => socket.flow(flowId).embeddings(text))),
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"triples_query",
|
||||
"Query the knowledge graph for triples matching a pattern",
|
||||
{
|
||||
s: z.string().optional().describe("Subject IRI"),
|
||||
p: z.string().optional().describe("Predicate IRI"),
|
||||
o: z.string().optional().describe("Object IRI or literal"),
|
||||
limit: z.number().optional().describe("Max results"),
|
||||
collection: z.string().optional().describe("Collection name"),
|
||||
},
|
||||
({s, p, o, limit, collection}) => {
|
||||
const sTerm: Term | undefined = s !== undefined && s.length > 0 ? {t: "i", i: s} : undefined;
|
||||
const pTerm: Term | undefined = p !== undefined && p.length > 0 ? {t: "i", i: p} : undefined;
|
||||
const oTerm: Term | undefined = o !== undefined && o.length > 0 ? {t: "i", i: o} : undefined;
|
||||
return runJsonTool(
|
||||
gatewayRequest(() => socket.flow(flowId).triplesQuery(sTerm, pTerm, oTerm, limit, collection)),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"graph_embeddings_query",
|
||||
"Find entities similar to a text query using vector embeddings",
|
||||
{
|
||||
query: z.string().describe("Text to find similar entities for"),
|
||||
limit: z.number().optional().describe("Max results"),
|
||||
collection: z.string().optional().describe("Collection name"),
|
||||
},
|
||||
({query, limit, collection}) =>
|
||||
runJsonTool(
|
||||
gatewayRequest(() => socket.flow(flowId).embeddings([query])).pipe(
|
||||
Effect.flatMap((vectors) =>
|
||||
gatewayRequest(() =>
|
||||
socket.flow(flowId).graphEmbeddingsQuery(
|
||||
vectors[0] ?? [],
|
||||
limit ?? 10,
|
||||
collection,
|
||||
)
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// ===================== Config tools =====================
|
||||
|
||||
server.tool(
|
||||
"get_config_all",
|
||||
"Get all configuration values",
|
||||
{},
|
||||
() => runJsonTool(gatewayRequest(() => socket.config().getConfigAll())),
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"get_config",
|
||||
"Get specific configuration values",
|
||||
{
|
||||
keys: z.array(
|
||||
z.object({
|
||||
type: z.string().describe("Config type"),
|
||||
key: z.string().describe("Config key"),
|
||||
}),
|
||||
).describe("Config keys to retrieve"),
|
||||
},
|
||||
({keys}) => runJsonTool(gatewayRequest(() => socket.config().getConfig(keys))),
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"put_config",
|
||||
"Set configuration values",
|
||||
{
|
||||
values: z.array(
|
||||
z.object({
|
||||
type: z.string().describe("Config type"),
|
||||
key: z.string().describe("Config key"),
|
||||
value: z.string().describe("Config value (JSON-encoded)"),
|
||||
}),
|
||||
).describe("Key-value entries to set"),
|
||||
},
|
||||
({values}) => runJsonTool(gatewayRequest(() => socket.config().putConfig(values))),
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"delete_config",
|
||||
"Delete a configuration entry",
|
||||
{
|
||||
type: z.string().describe("Config type"),
|
||||
key: z.string().describe("Config key"),
|
||||
},
|
||||
({type, key}) => runJsonTool(gatewayRequest(() => socket.config().deleteConfig({type, key}))),
|
||||
);
|
||||
|
||||
// ===================== Flow management tools =====================
|
||||
|
||||
server.tool(
|
||||
"get_flows",
|
||||
"List all available flows",
|
||||
{},
|
||||
() => runJsonTool(gatewayRequest(() => socket.flows().getFlows())),
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"get_flow",
|
||||
"Get a specific flow definition",
|
||||
{
|
||||
flow_id: z.string().describe("Flow ID to retrieve"),
|
||||
},
|
||||
({flow_id}) => runJsonTool(gatewayRequest(() => socket.flows().getFlow(flow_id))),
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"start_flow",
|
||||
"Start a flow instance",
|
||||
{
|
||||
flow_id: z.string().describe("Flow ID"),
|
||||
blueprint_name: z.string().describe("Blueprint name"),
|
||||
description: z.string().describe("Flow description"),
|
||||
parameters: z.record(z.unknown()).optional().describe("Optional flow parameters"),
|
||||
},
|
||||
({flow_id, blueprint_name, description, parameters}) =>
|
||||
runJsonTool(
|
||||
gatewayRequest(() => socket.flows().startFlow(flow_id, blueprint_name, description, parameters)),
|
||||
),
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"stop_flow",
|
||||
"Stop a running flow",
|
||||
{
|
||||
flow_id: z.string().describe("Flow ID to stop"),
|
||||
},
|
||||
({flow_id}) => runJsonTool(gatewayRequest(() => socket.flows().stopFlow(flow_id))),
|
||||
);
|
||||
|
||||
// ===================== Library (document) tools =====================
|
||||
|
||||
server.tool(
|
||||
"get_documents",
|
||||
"List all documents in the library",
|
||||
{},
|
||||
() => runJsonTool(gatewayRequest(() => socket.librarian().getDocuments())),
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"load_document",
|
||||
"Upload a document to the library",
|
||||
{
|
||||
document: z.string().describe("Base64-encoded document content"),
|
||||
mime_type: z.string().describe("Document MIME type"),
|
||||
title: z.string().describe("Document title"),
|
||||
comments: z.string().optional().describe("Additional comments"),
|
||||
tags: z.array(z.string()).optional().describe("Document tags"),
|
||||
id: z.string().optional().describe("Optional document ID"),
|
||||
},
|
||||
({document, mime_type, title, comments, tags, id}) =>
|
||||
runJsonTool(
|
||||
gatewayRequest(() =>
|
||||
socket.librarian().loadDocument(
|
||||
document,
|
||||
mime_type,
|
||||
title,
|
||||
comments ?? "",
|
||||
tags ?? [],
|
||||
id,
|
||||
)
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"remove_document",
|
||||
"Remove a document from the library",
|
||||
{
|
||||
id: z.string().describe("Document ID to remove"),
|
||||
collection: z.string().optional().describe("Collection name"),
|
||||
},
|
||||
({id, collection}) => runJsonTool(gatewayRequest(() => socket.librarian().removeDocument(id, collection))),
|
||||
);
|
||||
|
||||
// ===================== Prompt tools =====================
|
||||
|
||||
server.tool(
|
||||
"get_prompts",
|
||||
"List available prompt templates",
|
||||
{},
|
||||
() => runJsonTool(gatewayRequest(() => socket.config().getPrompts())),
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"get_prompt",
|
||||
"Get a specific prompt template",
|
||||
{
|
||||
id: z.string().describe("Prompt template ID"),
|
||||
},
|
||||
({id}) => runJsonTool(gatewayRequest(() => socket.config().getPrompt(id))),
|
||||
);
|
||||
|
||||
// ===================== Knowledge core tools =====================
|
||||
|
||||
server.tool(
|
||||
"get_knowledge_cores",
|
||||
"List available knowledge graph cores",
|
||||
{},
|
||||
() => runJsonTool(gatewayRequest(() => socket.knowledge().getKnowledgeCores())),
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"delete_kg_core",
|
||||
"Delete a knowledge graph core",
|
||||
{
|
||||
id: z.string().describe("Knowledge core ID"),
|
||||
collection: z.string().optional().describe("Collection name"),
|
||||
},
|
||||
({id, collection}) => runJsonTool(gatewayRequest(() => socket.knowledge().deleteKgCore(id, collection))),
|
||||
);
|
||||
|
||||
server.tool(
|
||||
"load_kg_core",
|
||||
"Load a knowledge graph core",
|
||||
{
|
||||
id: z.string().describe("Knowledge core ID"),
|
||||
flow: z.string().describe("Flow to use for loading"),
|
||||
collection: z.string().optional().describe("Collection name"),
|
||||
},
|
||||
({id, flow, collection}) => runJsonTool(gatewayRequest(() => socket.knowledge().loadKgCore(id, flow, collection))),
|
||||
);
|
||||
|
||||
return {server, socket};
|
||||
}
|
||||
|
||||
export const runProgram = Effect.gen(function*() {
|
||||
const config = yield* loadTrustGraphMcpConfig();
|
||||
const serverConfig = {
|
||||
gatewayUrl: config.gatewayUrl,
|
||||
user: config.user,
|
||||
flowId: config.flowId,
|
||||
...(config.token === undefined ? {} : {token: config.token}),
|
||||
};
|
||||
const {server, socket} = createMcpServer(serverConfig);
|
||||
const transport = new StdioServerTransport();
|
||||
|
||||
yield* Effect.tryPromise({
|
||||
try: () => server.connect(transport),
|
||||
catch: stdioMcpError,
|
||||
});
|
||||
|
||||
yield* Effect.sync(() => {
|
||||
process.on("SIGINT", () => {
|
||||
socket.close();
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const stdioRuntime = ManagedRuntime.make(Layer.empty);
|
||||
|
||||
export function run(): Promise<void> {
|
||||
return stdioRuntime.runPromise(runProgram);
|
||||
}
|
||||
|
||||
export function runMain(): void {
|
||||
NodeRuntime.runMain(runProgram);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue