@yolk-sdk/mcp 0.0.1-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +82 -0
- package/dist/client/client.d.mts +40 -0
- package/dist/client/client.d.mts.map +1 -0
- package/dist/client/client.mjs +225 -0
- package/dist/client/client.mjs.map +1 -0
- package/dist/client/config.d.mts +29 -0
- package/dist/client/config.d.mts.map +1 -0
- package/dist/client/config.mjs +13 -0
- package/dist/client/config.mjs.map +1 -0
- package/dist/client/errors.d.mts +14 -0
- package/dist/client/errors.d.mts.map +1 -0
- package/dist/client/errors.mjs +22 -0
- package/dist/client/errors.mjs.map +1 -0
- package/dist/client/index.d.mts +5 -0
- package/dist/client/index.mjs +5 -0
- package/dist/client/node.d.mts +16 -0
- package/dist/client/node.d.mts.map +1 -0
- package/dist/client/node.mjs +13 -0
- package/dist/client/node.mjs.map +1 -0
- package/dist/client/protocol.d.mts +175 -0
- package/dist/client/protocol.d.mts.map +1 -0
- package/dist/client/protocol.mjs +225 -0
- package/dist/client/protocol.mjs.map +1 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +1 -0
- package/dist/server/errors.d.mts +11 -0
- package/dist/server/errors.d.mts.map +1 -0
- package/dist/server/errors.mjs +16 -0
- package/dist/server/errors.mjs.map +1 -0
- package/dist/server/index.d.mts +4 -0
- package/dist/server/index.mjs +4 -0
- package/dist/server/server.d.mts +22 -0
- package/dist/server/server.d.mts.map +1 -0
- package/dist/server/server.mjs +180 -0
- package/dist/server/server.mjs.map +1 -0
- package/dist/server/stdio.d.mts +8 -0
- package/dist/server/stdio.d.mts.map +1 -0
- package/dist/server/stdio.mjs +18 -0
- package/dist/server/stdio.mjs.map +1 -0
- package/package.json +77 -0
- package/src/client/README.md +24 -0
- package/src/client/client.ts +494 -0
- package/src/client/config.ts +37 -0
- package/src/client/errors.ts +20 -0
- package/src/client/index.ts +48 -0
- package/src/client/node.ts +32 -0
- package/src/client/protocol.ts +364 -0
- package/src/index.ts +2 -0
- package/src/server/README.md +22 -0
- package/src/server/errors.ts +6 -0
- package/src/server/index.ts +4 -0
- package/src/server/server.ts +331 -0
- package/src/server/stdio.ts +37 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { McpServerError } from "./errors.mjs";
|
|
2
|
+
import { Array, Effect, Option } from "effect";
|
|
3
|
+
import * as Schema from "effect/Schema";
|
|
4
|
+
import { ToolCall, contentParts } from "@yolk-sdk/agent/protocol";
|
|
5
|
+
import { latestMcpProtocolVersion } from "@yolk-sdk/mcp/client";
|
|
6
|
+
//#region src/server/server.ts
|
|
7
|
+
const JsonRpcRequestSchema = Schema.Struct({
|
|
8
|
+
jsonrpc: Schema.Literal("2.0"),
|
|
9
|
+
id: Schema.Union([Schema.String, Schema.Number]),
|
|
10
|
+
method: Schema.String,
|
|
11
|
+
params: Schema.optional(Schema.Unknown)
|
|
12
|
+
});
|
|
13
|
+
const JsonRpcNotificationSchema = Schema.Struct({
|
|
14
|
+
jsonrpc: Schema.Literal("2.0"),
|
|
15
|
+
method: Schema.String,
|
|
16
|
+
params: Schema.optional(Schema.Unknown)
|
|
17
|
+
});
|
|
18
|
+
const CallToolParamsSchema = Schema.Struct({
|
|
19
|
+
name: Schema.String,
|
|
20
|
+
arguments: Schema.optional(Schema.Unknown)
|
|
21
|
+
});
|
|
22
|
+
const JsonRpcMessageSchema = Schema.Union([JsonRpcRequestSchema, JsonRpcNotificationSchema]);
|
|
23
|
+
const decodedMessage = (message) => ({
|
|
24
|
+
_tag: "Message",
|
|
25
|
+
message
|
|
26
|
+
});
|
|
27
|
+
const decodedResponse = (response) => ({
|
|
28
|
+
_tag: "Response",
|
|
29
|
+
response
|
|
30
|
+
});
|
|
31
|
+
const decodeJson = Schema.decodeUnknownEffect(Schema.UnknownFromJsonString);
|
|
32
|
+
const decodeJsonRpcMessage = Schema.decodeUnknownEffect(JsonRpcMessageSchema);
|
|
33
|
+
const decodeCallToolParams = Schema.decodeUnknownEffect(CallToolParamsSchema);
|
|
34
|
+
const encodeJson = (value) => Schema.encodeUnknownEffect(Schema.UnknownFromJsonString)(value).pipe(Effect.mapError((error) => new McpServerError({
|
|
35
|
+
message: `Could not encode MCP response: ${String(error)}`,
|
|
36
|
+
cause: "encoding"
|
|
37
|
+
})));
|
|
38
|
+
const decodeMessage = (line) => decodeJson(line).pipe(Effect.mapError((error) => new McpServerError({
|
|
39
|
+
message: `Malformed MCP JSON: ${String(error)}`,
|
|
40
|
+
cause: "parse"
|
|
41
|
+
})), Effect.flatMap((value) => decodeJsonRpcMessage(value).pipe(Effect.mapError((error) => new McpServerError({
|
|
42
|
+
message: `Invalid MCP JSON-RPC message: ${String(error)}`,
|
|
43
|
+
cause: "validation"
|
|
44
|
+
})))));
|
|
45
|
+
const successResponse = (id, result) => ({
|
|
46
|
+
jsonrpc: "2.0",
|
|
47
|
+
id,
|
|
48
|
+
result
|
|
49
|
+
});
|
|
50
|
+
const errorResponse = (id, code, message) => ({
|
|
51
|
+
jsonrpc: "2.0",
|
|
52
|
+
id,
|
|
53
|
+
error: {
|
|
54
|
+
code,
|
|
55
|
+
message
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
const unknownToMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
59
|
+
const isRequest = (message) => "id" in message;
|
|
60
|
+
const protocolErrorCode = (error) => {
|
|
61
|
+
switch (error.cause) {
|
|
62
|
+
case "parse": return -32700;
|
|
63
|
+
case "validation": return -32600;
|
|
64
|
+
case "protocol": return -32603;
|
|
65
|
+
case "tool_error":
|
|
66
|
+
case "encoding": return -32e3;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const protocolErrorResponse = (id, error) => errorResponse(id, protocolErrorCode(error), error.message);
|
|
70
|
+
const mcpContentBlockFromPart = (part) => {
|
|
71
|
+
switch (part._tag) {
|
|
72
|
+
case "Text": return {
|
|
73
|
+
type: "text",
|
|
74
|
+
text: part.text
|
|
75
|
+
};
|
|
76
|
+
case "Image": return {
|
|
77
|
+
type: "image",
|
|
78
|
+
data: part.data,
|
|
79
|
+
mimeType: part.mimeType
|
|
80
|
+
};
|
|
81
|
+
case "Audio": return {
|
|
82
|
+
type: "audio",
|
|
83
|
+
data: part.data,
|
|
84
|
+
mimeType: `audio/${part.format}`
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const mcpResultFromToolResult = (result) => {
|
|
89
|
+
const content = Array.map(contentParts(result.content), mcpContentBlockFromPart);
|
|
90
|
+
const base = result.isError === void 0 ? { content } : {
|
|
91
|
+
content,
|
|
92
|
+
isError: result.isError
|
|
93
|
+
};
|
|
94
|
+
return result.structuredContent === void 0 ? base : {
|
|
95
|
+
...base,
|
|
96
|
+
structuredContent: result.structuredContent
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
const mcpErrorResult = (message) => ({
|
|
100
|
+
content: [{
|
|
101
|
+
type: "text",
|
|
102
|
+
text: message
|
|
103
|
+
}],
|
|
104
|
+
isError: true
|
|
105
|
+
});
|
|
106
|
+
const mcpResultFromExecutionResult = (result) => "toolCallId" in result ? mcpResultFromToolResult(result) : result;
|
|
107
|
+
const toolListItem = (tool) => ({
|
|
108
|
+
name: tool.name,
|
|
109
|
+
description: tool.description,
|
|
110
|
+
inputSchema: tool.parameters
|
|
111
|
+
});
|
|
112
|
+
const jsonResponse = (body, init) => new Response(body, {
|
|
113
|
+
...init,
|
|
114
|
+
headers: {
|
|
115
|
+
"content-type": "application/json",
|
|
116
|
+
...init?.headers ?? {}
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
const methodNotAllowedBody = () => encodeJson(errorResponse(null, -32600, "Method not allowed")).pipe(Effect.orElseSucceed(() => "{\"jsonrpc\":\"2.0\",\"id\":null,\"error\":{\"code\":-32600,\"message\":\"Method not allowed\"}}"));
|
|
120
|
+
const badRequestBody = (message) => encodeJson(errorResponse(null, -32600, message)).pipe(Effect.orElseSucceed(() => "{\"jsonrpc\":\"2.0\",\"id\":null,\"error\":{\"code\":-32600,\"message\":\"Bad request\"}}"));
|
|
121
|
+
const makeMcpToolServer = (input) => {
|
|
122
|
+
const findTool = (name) => Array.findFirst(input.tools, (tool) => tool.def.name === name);
|
|
123
|
+
const handleRequest = (request) => Effect.gen(function* () {
|
|
124
|
+
switch (request.method) {
|
|
125
|
+
case "initialize": return successResponse(request.id, {
|
|
126
|
+
protocolVersion: latestMcpProtocolVersion,
|
|
127
|
+
capabilities: { tools: {} },
|
|
128
|
+
serverInfo: {
|
|
129
|
+
name: input.name,
|
|
130
|
+
version: input.version
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
case "tools/list": return successResponse(request.id, { tools: input.tools.map((tool) => toolListItem(tool.def)) });
|
|
134
|
+
case "tools/call": {
|
|
135
|
+
const params = yield* decodeCallToolParams(request.params).pipe(Effect.mapError((error) => new McpServerError({
|
|
136
|
+
message: `Invalid tools/call params: ${String(error)}`,
|
|
137
|
+
cause: "validation"
|
|
138
|
+
})));
|
|
139
|
+
const tool = findTool(params.name);
|
|
140
|
+
if (Option.isNone(tool)) return errorResponse(request.id, -32602, `Unknown tool: ${params.name}`);
|
|
141
|
+
const result = yield* tool.value.execute(ToolCall.make({
|
|
142
|
+
id: String(request.id),
|
|
143
|
+
name: params.name,
|
|
144
|
+
params: params.arguments ?? {}
|
|
145
|
+
})).pipe(Effect.catch((error) => Effect.succeed(mcpErrorResult(`MCP tool failed: ${error.message}`))));
|
|
146
|
+
return successResponse(request.id, mcpResultFromExecutionResult(result));
|
|
147
|
+
}
|
|
148
|
+
default: return errorResponse(request.id, -32601, `Method not found: ${request.method}`);
|
|
149
|
+
}
|
|
150
|
+
}).pipe(Effect.catch((error) => error instanceof McpServerError ? Effect.succeed(protocolErrorResponse(request.id, error)) : Effect.succeed(errorResponse(request.id, -32e3, unknownToMessage(error)))));
|
|
151
|
+
const handleLine = (line) => Effect.gen(function* () {
|
|
152
|
+
const decoded = yield* decodeMessage(line).pipe(Effect.map(decodedMessage), Effect.catch((error) => encodeJson(protocolErrorResponse(null, error)).pipe(Effect.map((response) => decodedResponse(Option.some(response))))));
|
|
153
|
+
if (decoded._tag === "Response") return decoded.response;
|
|
154
|
+
const { message } = decoded;
|
|
155
|
+
if (!isRequest(message)) return Option.none();
|
|
156
|
+
const encoded = yield* encodeJson(yield* handleRequest(message));
|
|
157
|
+
return Option.some(encoded);
|
|
158
|
+
});
|
|
159
|
+
const handleJson = (body) => Effect.gen(function* () {
|
|
160
|
+
const response = yield* handleLine(body);
|
|
161
|
+
if (Option.isNone(response)) return yield* encodeJson(errorResponse(null, -32600, "Notifications have no response"));
|
|
162
|
+
return response.value;
|
|
163
|
+
});
|
|
164
|
+
const handleHttpRequest = (request) => Effect.gen(function* () {
|
|
165
|
+
if (request.method !== "POST") return jsonResponse(yield* methodNotAllowedBody(), {
|
|
166
|
+
status: 405,
|
|
167
|
+
headers: { allow: "POST" }
|
|
168
|
+
});
|
|
169
|
+
return jsonResponse(yield* handleJson(yield* Effect.promise(() => request.text()).pipe(Effect.mapError((error) => unknownToMessage(error)), Effect.catch((error) => badRequestBody(`Could not read request body: ${error}`)))).pipe(Effect.catch((error) => badRequestBody(unknownToMessage(error)))));
|
|
170
|
+
});
|
|
171
|
+
return {
|
|
172
|
+
handleLine,
|
|
173
|
+
handleJson,
|
|
174
|
+
handleHttpRequest
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
//#endregion
|
|
178
|
+
export { makeMcpToolServer };
|
|
179
|
+
|
|
180
|
+
//# sourceMappingURL=server.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.mjs","names":["Arr"],"sources":["../../src/server/server.ts"],"sourcesContent":["import { Array as Arr, Effect, Option } from 'effect'\nimport * as Schema from 'effect/Schema'\nimport {\n ToolCall,\n contentParts,\n type ContentPart,\n type ToolDef,\n type ToolResult\n} from '@yolk-sdk/agent/protocol'\nimport { latestMcpProtocolVersion } from '@yolk-sdk/mcp/client'\nimport { McpServerError } from './errors.ts'\n\ntype JsonRpcRequest = {\n readonly jsonrpc: '2.0'\n readonly id: string | number\n readonly method: string\n readonly params?: unknown\n}\n\ntype JsonRpcNotification = {\n readonly jsonrpc: '2.0'\n readonly method: string\n readonly params?: unknown\n}\n\ntype JsonRpcResponse = {\n readonly jsonrpc: '2.0'\n readonly id: string | number | null\n readonly result?: unknown\n readonly error?: {\n readonly code: number\n readonly message: string\n }\n}\n\nconst JsonRpcRequestSchema = Schema.Struct({\n jsonrpc: Schema.Literal('2.0'),\n id: Schema.Union([Schema.String, Schema.Number]),\n method: Schema.String,\n params: Schema.optional(Schema.Unknown)\n})\n\nconst JsonRpcNotificationSchema = Schema.Struct({\n jsonrpc: Schema.Literal('2.0'),\n method: Schema.String,\n params: Schema.optional(Schema.Unknown)\n})\n\nconst CallToolParamsSchema = Schema.Struct({\n name: Schema.String,\n arguments: Schema.optional(Schema.Unknown)\n})\n\nconst JsonRpcMessageSchema = Schema.Union([JsonRpcRequestSchema, JsonRpcNotificationSchema])\n\ntype JsonRpcMessage = typeof JsonRpcMessageSchema.Type\n\ntype DecodedLine =\n | { readonly _tag: 'Message'; readonly message: JsonRpcMessage }\n | { readonly _tag: 'Response'; readonly response: Option.Option<string> }\n\nconst decodedMessage = (message: JsonRpcMessage): DecodedLine => ({ _tag: 'Message', message })\n\nconst decodedResponse = (response: Option.Option<string>): DecodedLine => ({\n _tag: 'Response',\n response\n})\n\nconst decodeJson = Schema.decodeUnknownEffect(Schema.UnknownFromJsonString)\nconst decodeJsonRpcMessage = Schema.decodeUnknownEffect(JsonRpcMessageSchema)\nconst decodeCallToolParams = Schema.decodeUnknownEffect(CallToolParamsSchema)\n\nconst encodeJson = (value: unknown) =>\n Schema.encodeUnknownEffect(Schema.UnknownFromJsonString)(value).pipe(\n Effect.mapError(\n error =>\n new McpServerError({\n message: `Could not encode MCP response: ${String(error)}`,\n cause: 'encoding'\n })\n )\n )\n\nconst decodeMessage = (line: string) =>\n decodeJson(line).pipe(\n Effect.mapError(\n error =>\n new McpServerError({\n message: `Malformed MCP JSON: ${String(error)}`,\n cause: 'parse'\n })\n ),\n Effect.flatMap(value =>\n decodeJsonRpcMessage(value).pipe(\n Effect.mapError(\n error =>\n new McpServerError({\n message: `Invalid MCP JSON-RPC message: ${String(error)}`,\n cause: 'validation'\n })\n )\n )\n )\n )\n\nconst successResponse = (id: string | number, result: unknown): JsonRpcResponse => ({\n jsonrpc: '2.0',\n id,\n result\n})\n\nconst errorResponse = (\n id: string | number | null,\n code: number,\n message: string\n): JsonRpcResponse => ({\n jsonrpc: '2.0',\n id,\n error: { code, message }\n})\n\nconst unknownToMessage = (error: unknown) =>\n error instanceof Error ? error.message : String(error)\n\nconst isRequest = (message: JsonRpcRequest | JsonRpcNotification): message is JsonRpcRequest =>\n 'id' in message\n\nconst protocolErrorCode = (error: McpServerError) => {\n switch (error.cause) {\n case 'parse':\n return -32_700\n case 'validation':\n return -32_600\n case 'protocol':\n return -32_603\n case 'tool_error':\n case 'encoding':\n return -32_000\n }\n}\n\nconst protocolErrorResponse = (id: string | number | null, error: McpServerError) =>\n errorResponse(id, protocolErrorCode(error), error.message)\n\nconst mcpContentBlockFromPart = (part: ContentPart) => {\n switch (part._tag) {\n case 'Text':\n return { type: 'text', text: part.text }\n case 'Image':\n return { type: 'image', data: part.data, mimeType: part.mimeType }\n case 'Audio':\n return { type: 'audio', data: part.data, mimeType: `audio/${part.format}` }\n }\n}\n\nconst mcpResultFromToolResult = (result: ToolResult) => {\n const content = Arr.map(contentParts(result.content), mcpContentBlockFromPart)\n const base = result.isError === undefined ? { content } : { content, isError: result.isError }\n\n return result.structuredContent === undefined\n ? base\n : { ...base, structuredContent: result.structuredContent }\n}\n\nconst mcpErrorResult = (message: string) => ({\n content: [{ type: 'text', text: message }],\n isError: true\n})\n\nconst mcpResultFromExecutionResult = (result: ToolResult | ReturnType<typeof mcpErrorResult>) =>\n 'toolCallId' in result ? mcpResultFromToolResult(result) : result\n\nconst toolListItem = (tool: ToolDef) => ({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.parameters\n})\n\nexport type McpServerTool<R = never> = {\n readonly def: ToolDef\n readonly execute: (call: ToolCall) => Effect.Effect<ToolResult, McpServerError, R>\n}\n\nexport type McpToolServer<R = never> = {\n readonly handleLine: (line: string) => Effect.Effect<Option.Option<string>, McpServerError, R>\n readonly handleJson: (body: string) => Effect.Effect<string, McpServerError, R>\n readonly handleHttpRequest: (request: Request) => Effect.Effect<Response, never, R>\n}\n\nconst jsonResponse = (body: string, init?: ResponseInit) =>\n new Response(body, {\n ...init,\n headers: {\n 'content-type': 'application/json',\n ...(init?.headers ?? {})\n }\n })\n\nconst methodNotAllowedBody = () =>\n encodeJson(errorResponse(null, -32_600, 'Method not allowed')).pipe(\n Effect.orElseSucceed(\n () => '{\"jsonrpc\":\"2.0\",\"id\":null,\"error\":{\"code\":-32600,\"message\":\"Method not allowed\"}}'\n )\n )\n\nconst badRequestBody = (message: string) =>\n encodeJson(errorResponse(null, -32_600, message)).pipe(\n Effect.orElseSucceed(\n () => '{\"jsonrpc\":\"2.0\",\"id\":null,\"error\":{\"code\":-32600,\"message\":\"Bad request\"}}'\n )\n )\n\nexport const makeMcpToolServer = <R>(input: {\n readonly name: string\n readonly version: string\n readonly tools: ReadonlyArray<McpServerTool<R>>\n}): McpToolServer<R> => {\n const findTool = (name: string) => Arr.findFirst(input.tools, tool => tool.def.name === name)\n\n const handleRequest = (request: JsonRpcRequest) =>\n Effect.gen(function* () {\n switch (request.method) {\n case 'initialize':\n return successResponse(request.id, {\n protocolVersion: latestMcpProtocolVersion,\n capabilities: { tools: {} },\n serverInfo: { name: input.name, version: input.version }\n })\n case 'tools/list':\n return successResponse(request.id, {\n tools: input.tools.map(tool => toolListItem(tool.def))\n })\n case 'tools/call': {\n const params = yield* decodeCallToolParams(request.params).pipe(\n Effect.mapError(\n error =>\n new McpServerError({\n message: `Invalid tools/call params: ${String(error)}`,\n cause: 'validation'\n })\n )\n )\n const tool = findTool(params.name)\n if (Option.isNone(tool)) {\n return errorResponse(request.id, -32_602, `Unknown tool: ${params.name}`)\n }\n\n const result = yield* tool.value\n .execute(\n ToolCall.make({\n id: String(request.id),\n name: params.name,\n params: params.arguments ?? {}\n })\n )\n .pipe(\n Effect.catch(error =>\n Effect.succeed(mcpErrorResult(`MCP tool failed: ${error.message}`))\n )\n )\n\n return successResponse(request.id, mcpResultFromExecutionResult(result))\n }\n default:\n return errorResponse(request.id, -32_601, `Method not found: ${request.method}`)\n }\n }).pipe(\n Effect.catch(error =>\n error instanceof McpServerError\n ? Effect.succeed(protocolErrorResponse(request.id, error))\n : Effect.succeed(errorResponse(request.id, -32_000, unknownToMessage(error)))\n )\n )\n\n const handleLine = (line: string) =>\n Effect.gen(function* () {\n const decoded: DecodedLine = yield* decodeMessage(line).pipe(\n Effect.map(decodedMessage),\n Effect.catch(error =>\n encodeJson(protocolErrorResponse(null, error)).pipe(\n Effect.map(response => decodedResponse(Option.some(response)))\n )\n )\n )\n\n if (decoded._tag === 'Response') {\n return decoded.response\n }\n\n const { message } = decoded\n\n if (!isRequest(message)) {\n return Option.none<string>()\n }\n\n const response = yield* handleRequest(message)\n const encoded = yield* encodeJson(response)\n return Option.some(encoded)\n })\n\n const handleJson = (body: string) =>\n Effect.gen(function* () {\n const response = yield* handleLine(body)\n if (Option.isNone(response)) {\n return yield* encodeJson(errorResponse(null, -32_600, 'Notifications have no response'))\n }\n\n return response.value\n })\n\n const handleHttpRequest = (request: Request) =>\n Effect.gen(function* () {\n if (request.method !== 'POST') {\n const body = yield* methodNotAllowedBody()\n return jsonResponse(body, { status: 405, headers: { allow: 'POST' } })\n }\n\n const body = yield* Effect.promise(() => request.text()).pipe(\n Effect.mapError(error => unknownToMessage(error)),\n Effect.catch(error => badRequestBody(`Could not read request body: ${error}`))\n )\n\n const responseBody = yield* handleJson(body).pipe(\n Effect.catch(error => badRequestBody(unknownToMessage(error)))\n )\n\n return jsonResponse(responseBody)\n })\n\n return { handleLine, handleJson, handleHttpRequest }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { McpToolServer } from "./server.mjs";
|
|
2
|
+
import { Effect, Stdio } from "effect";
|
|
3
|
+
|
|
4
|
+
//#region src/server/stdio.d.ts
|
|
5
|
+
declare const runStdioMcpServer: <R>(server: McpToolServer<R>) => Effect.Effect<never, never, R | Stdio.Stdio>;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { runStdioMcpServer };
|
|
8
|
+
//# sourceMappingURL=stdio.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio.d.mts","names":[],"sources":["../../src/server/stdio.ts"],"mappings":";;;;cAsBa,iBAAA,MACX,MAAA,EAAQ,aAAA,CAAc,CAAA,MACrB,MAAA,CAAO,MAAA,eAAqB,CAAA,GAAI,KAAA,CAAM,KAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Effect, Option, Stdio, Stream } from "effect";
|
|
2
|
+
//#region src/server/stdio.ts
|
|
3
|
+
const writeStdout = (stdio, value) => Stream.make(`${value}\n`).pipe(Stream.run(stdio.stdout()));
|
|
4
|
+
const writeResponse = (stdio, response) => Option.match(response, {
|
|
5
|
+
onNone: () => Effect.void,
|
|
6
|
+
onSome: (value) => writeStdout(stdio, value)
|
|
7
|
+
});
|
|
8
|
+
const ignoreStdioError = (effect) => effect.pipe(Effect.catch(() => Effect.void));
|
|
9
|
+
const handleStdioLine = (server, stdio, line) => server.handleLine(line).pipe(Effect.flatMap((response) => writeResponse(stdio, response)), ignoreStdioError);
|
|
10
|
+
const runStdioMcpServer = (server) => Effect.gen(function* () {
|
|
11
|
+
const stdio = yield* Stdio.Stdio;
|
|
12
|
+
yield* stdio.stdin.pipe(Stream.decodeText(), Stream.splitLines, Stream.runForEach((line) => handleStdioLine(server, stdio, line)), Effect.catch((_error) => Effect.void));
|
|
13
|
+
return yield* Effect.never;
|
|
14
|
+
});
|
|
15
|
+
//#endregion
|
|
16
|
+
export { runStdioMcpServer };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=stdio.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio.mjs","names":[],"sources":["../../src/server/stdio.ts"],"sourcesContent":["import { Effect, Option, Stdio, Stream } from 'effect'\nimport type { PlatformError } from 'effect/PlatformError'\nimport type { McpToolServer } from './server.ts'\n\nconst writeStdout = (stdio: Stdio.Stdio, value: string) =>\n Stream.make(`${value}\\n`).pipe(Stream.run(stdio.stdout()))\n\nconst writeResponse = (stdio: Stdio.Stdio, response: Option.Option<string>) =>\n Option.match(response, {\n onNone: () => Effect.void,\n onSome: value => writeStdout(stdio, value)\n })\n\nconst ignoreStdioError = <A, E, R>(effect: Effect.Effect<A, E, R>) =>\n effect.pipe(Effect.catch(() => Effect.void))\n\nconst handleStdioLine = <R>(server: McpToolServer<R>, stdio: Stdio.Stdio, line: string) =>\n server.handleLine(line).pipe(\n Effect.flatMap(response => writeResponse(stdio, response)),\n ignoreStdioError\n )\n\nexport const runStdioMcpServer = <R>(\n server: McpToolServer<R>\n): Effect.Effect<never, never, R | Stdio.Stdio> =>\n Effect.gen(function* () {\n const stdio = yield* Stdio.Stdio\n\n yield* stdio.stdin.pipe(\n Stream.decodeText(),\n Stream.splitLines,\n Stream.runForEach(line => handleStdioLine(server, stdio, line)),\n Effect.catch((_error: PlatformError) => Effect.void)\n )\n\n return yield* Effect.never\n })\n"],"mappings":";;AAIA,MAAM,eAAe,OAAoB,UACvC,OAAO,KAAK,GAAG,MAAM,GAAG,EAAE,KAAK,OAAO,IAAI,MAAM,OAAO,CAAC,CAAC;AAE3D,MAAM,iBAAiB,OAAoB,aACzC,OAAO,MAAM,UAAU;CACrB,cAAc,OAAO;CACrB,SAAQ,UAAS,YAAY,OAAO,KAAK;AAC3C,CAAC;AAEH,MAAM,oBAA6B,WACjC,OAAO,KAAK,OAAO,YAAY,OAAO,IAAI,CAAC;AAE7C,MAAM,mBAAsB,QAA0B,OAAoB,SACxE,OAAO,WAAW,IAAI,EAAE,KACtB,OAAO,SAAQ,aAAY,cAAc,OAAO,QAAQ,CAAC,GACzD,gBACF;AAEF,MAAa,qBACX,WAEA,OAAO,IAAI,aAAa;CACtB,MAAM,QAAQ,OAAO,MAAM;CAE3B,OAAO,MAAM,MAAM,KACjB,OAAO,WAAW,GAClB,OAAO,YACP,OAAO,YAAW,SAAQ,gBAAgB,QAAQ,OAAO,IAAI,CAAC,GAC9D,OAAO,OAAO,WAA0B,OAAO,IAAI,CACrD;CAEA,OAAO,OAAO,OAAO;AACvB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yolk-sdk/mcp",
|
|
3
|
+
"version": "0.0.1-canary.0",
|
|
4
|
+
"description": "MCP client, server, and protocol utilities for Yolk agent runtimes.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/magoz/yolk-sdk.git",
|
|
11
|
+
"directory": "packages/mcp"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/magoz/yolk-sdk/issues"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/magoz/yolk-sdk#readme",
|
|
17
|
+
"keywords": [
|
|
18
|
+
"mcp",
|
|
19
|
+
"model-context-protocol",
|
|
20
|
+
"agent",
|
|
21
|
+
"tools",
|
|
22
|
+
"effect"
|
|
23
|
+
],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=22"
|
|
26
|
+
},
|
|
27
|
+
"exports": {
|
|
28
|
+
"./package.json": "./package.json",
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.mts",
|
|
31
|
+
"import": "./dist/index.mjs",
|
|
32
|
+
"default": "./dist/index.mjs"
|
|
33
|
+
},
|
|
34
|
+
"./client": {
|
|
35
|
+
"types": "./dist/client/index.d.mts",
|
|
36
|
+
"import": "./dist/client/index.mjs",
|
|
37
|
+
"default": "./dist/client/index.mjs"
|
|
38
|
+
},
|
|
39
|
+
"./client/node": {
|
|
40
|
+
"types": "./dist/client/node.d.mts",
|
|
41
|
+
"import": "./dist/client/node.mjs",
|
|
42
|
+
"default": "./dist/client/node.mjs"
|
|
43
|
+
},
|
|
44
|
+
"./protocol": {
|
|
45
|
+
"types": "./dist/client/protocol.d.mts",
|
|
46
|
+
"import": "./dist/client/protocol.mjs",
|
|
47
|
+
"default": "./dist/client/protocol.mjs"
|
|
48
|
+
},
|
|
49
|
+
"./server": {
|
|
50
|
+
"types": "./dist/server/index.d.mts",
|
|
51
|
+
"import": "./dist/server/index.mjs",
|
|
52
|
+
"default": "./dist/server/index.mjs"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"files": [
|
|
56
|
+
"src/**/*.ts",
|
|
57
|
+
"!src/**/*.test.ts",
|
|
58
|
+
"!src/**/*.test.tsx",
|
|
59
|
+
"dist/**/*",
|
|
60
|
+
"README.md"
|
|
61
|
+
],
|
|
62
|
+
"publishConfig": {
|
|
63
|
+
"access": "public",
|
|
64
|
+
"provenance": true
|
|
65
|
+
},
|
|
66
|
+
"dependencies": {
|
|
67
|
+
"@effect/platform-node": "4.0.0-beta.65",
|
|
68
|
+
"effect": "4.0.0-beta.65",
|
|
69
|
+
"@yolk-sdk/agent": "^0.0.1-canary.0"
|
|
70
|
+
},
|
|
71
|
+
"scripts": {
|
|
72
|
+
"build": "tsdown",
|
|
73
|
+
"check": "tsc -p tsconfig.json --noEmit",
|
|
74
|
+
"test": "vitest run --passWithNoTests",
|
|
75
|
+
"test:run": "vitest run --passWithNoTests"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# @yolk-sdk/mcp/client
|
|
2
|
+
|
|
3
|
+
Domain-free MCP client and tool adapter.
|
|
4
|
+
|
|
5
|
+
## What it provides
|
|
6
|
+
|
|
7
|
+
- Remote MCP JSON-RPC over HTTP POST and SSE responses.
|
|
8
|
+
- Local MCP stdio process transport through Effect platform APIs.
|
|
9
|
+
- Node stdio convenience wrappers via `@yolk-sdk/mcp/client/node`.
|
|
10
|
+
- Tool listing and call helpers.
|
|
11
|
+
- MCP tool to Yolk `ToolDef`/`ToolResult` adapters.
|
|
12
|
+
- Security policy gates for local and remote transports.
|
|
13
|
+
|
|
14
|
+
## Use it when
|
|
15
|
+
|
|
16
|
+
- A host app wants to expose configured MCP server tools to a Yolk agent.
|
|
17
|
+
- You need local or remote MCP client behavior without app-specific config storage.
|
|
18
|
+
|
|
19
|
+
## Boundaries
|
|
20
|
+
|
|
21
|
+
- No persisted config store.
|
|
22
|
+
- No app auth or product permissions.
|
|
23
|
+
- No agent loop/provider imports.
|
|
24
|
+
- Core local APIs require platform services; Node wrappers provide them at host boundary.
|