logging refactoring
This commit is contained in:
@@ -19,7 +19,9 @@
|
|||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fastify/cors": "^11.2.0",
|
||||||
"@modelcontextprotocol/sdk": "^1.13.1",
|
"@modelcontextprotocol/sdk": "^1.13.1",
|
||||||
|
"fast-xml-parser": "^5.9.3",
|
||||||
"fastify": "^5.2.1",
|
"fastify": "^5.2.1",
|
||||||
"zod": "^3.24.2",
|
"zod": "^3.24.2",
|
||||||
"zod-to-json-schema": "^3.25.2"
|
"zod-to-json-schema": "^3.25.2"
|
||||||
|
|||||||
Generated
+74
@@ -207,9 +207,15 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@fastify/cors':
|
||||||
|
specifier: ^11.2.0
|
||||||
|
version: 11.2.0
|
||||||
'@modelcontextprotocol/sdk':
|
'@modelcontextprotocol/sdk':
|
||||||
specifier: ^1.13.1
|
specifier: ^1.13.1
|
||||||
version: 1.29.0(zod@3.25.76)
|
version: 1.29.0(zod@3.25.76)
|
||||||
|
fast-xml-parser:
|
||||||
|
specifier: ^5.9.3
|
||||||
|
version: 5.9.3
|
||||||
fastify:
|
fastify:
|
||||||
specifier: ^5.2.1
|
specifier: ^5.2.1
|
||||||
version: 5.9.0
|
version: 5.9.0
|
||||||
@@ -444,6 +450,9 @@ packages:
|
|||||||
'@fastify/ajv-compiler@4.0.5':
|
'@fastify/ajv-compiler@4.0.5':
|
||||||
resolution: {integrity: sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==}
|
resolution: {integrity: sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==}
|
||||||
|
|
||||||
|
'@fastify/cors@11.2.0':
|
||||||
|
resolution: {integrity: sha512-LbLHBuSAdGdSFZYTLVA3+Ch2t+sA6nq3Ejc6XLAKiQ6ViS2qFnvicpj0htsx03FyYeLs04HfRNBsz/a8SvbcUw==}
|
||||||
|
|
||||||
'@fastify/error@4.2.0':
|
'@fastify/error@4.2.0':
|
||||||
resolution: {integrity: sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==}
|
resolution: {integrity: sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==}
|
||||||
|
|
||||||
@@ -495,6 +504,9 @@ packages:
|
|||||||
'@cfworker/json-schema':
|
'@cfworker/json-schema':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@nodable/entities@2.2.0':
|
||||||
|
resolution: {integrity: sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg==}
|
||||||
|
|
||||||
'@pinojs/redact@0.4.0':
|
'@pinojs/redact@0.4.0':
|
||||||
resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==}
|
resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==}
|
||||||
|
|
||||||
@@ -601,6 +613,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
anynum@1.0.1:
|
||||||
|
resolution: {integrity: sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==}
|
||||||
|
|
||||||
argparse@2.0.1:
|
argparse@2.0.1:
|
||||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||||
|
|
||||||
@@ -850,6 +865,16 @@ packages:
|
|||||||
fast-uri@3.1.2:
|
fast-uri@3.1.2:
|
||||||
resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==}
|
resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==}
|
||||||
|
|
||||||
|
fast-xml-builder@1.2.1:
|
||||||
|
resolution: {integrity: sha512-tPb5TTWfgfVx5BNSi2xV0eLr89POeXXn0dXIsCJ9m1narrWxeIyx6je9d7Rce/3NyXLbvuQmLkxq+RuxMWejvw==}
|
||||||
|
|
||||||
|
fast-xml-parser@5.9.3:
|
||||||
|
resolution: {integrity: sha512-brCNCeScma/kqa54J4PIDriSSSLssRkuYaUCpvHJulGc3HGI/xxKUCTDcYkAdqJsyb//ydpbxecjC3hB9+tb/g==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
fastify-plugin@5.1.0:
|
||||||
|
resolution: {integrity: sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw==}
|
||||||
|
|
||||||
fastify@5.9.0:
|
fastify@5.9.0:
|
||||||
resolution: {integrity: sha512-VMS5lE0zj+MZlJpQa3Qv5iGjfun0H2N7VRgoBwpcTNQ2bdIQpv7fDpb+HGteGbicBsGkzGS+X+hdx9mmrfWuHQ==}
|
resolution: {integrity: sha512-VMS5lE0zj+MZlJpQa3Qv5iGjfun0H2N7VRgoBwpcTNQ2bdIQpv7fDpb+HGteGbicBsGkzGS+X+hdx9mmrfWuHQ==}
|
||||||
|
|
||||||
@@ -993,6 +1018,9 @@ packages:
|
|||||||
is-promise@4.0.0:
|
is-promise@4.0.0:
|
||||||
resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==}
|
resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==}
|
||||||
|
|
||||||
|
is-unsafe@1.0.1:
|
||||||
|
resolution: {integrity: sha512-CLK2+VdgERgD96EYm5lUQssZYlRg2tkZnbsxZoacmSiRxiFJ4Nk4SzjCl+Ur+v3kXIY9dTIdb3IH22y1mZ56LA==}
|
||||||
|
|
||||||
isexe@2.0.0:
|
isexe@2.0.0:
|
||||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||||
|
|
||||||
@@ -1125,6 +1153,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
|
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
path-expression-matcher@1.6.1:
|
||||||
|
resolution: {integrity: sha512-h7bxdzhHk8Knyc4Tj+jMaa7fEEoUJy7p1qtbVgkYg1Uhpe5Np5VuGXCRZnkZvU+Q42M1vStt0ifa3ueykRJPmQ==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
path-key@3.1.1:
|
path-key@3.1.1:
|
||||||
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
|
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -1296,6 +1328,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==}
|
resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==}
|
||||||
engines: {node: '>=14.16'}
|
engines: {node: '>=14.16'}
|
||||||
|
|
||||||
|
strnum@2.4.1:
|
||||||
|
resolution: {integrity: sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==}
|
||||||
|
|
||||||
supports-color@7.2.0:
|
supports-color@7.2.0:
|
||||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -1373,6 +1408,10 @@ packages:
|
|||||||
wrappy@1.0.2:
|
wrappy@1.0.2:
|
||||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||||
|
|
||||||
|
xml-naming@0.1.0:
|
||||||
|
resolution: {integrity: sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==}
|
||||||
|
engines: {node: '>=16.0.0'}
|
||||||
|
|
||||||
yocto-queue@0.1.0:
|
yocto-queue@0.1.0:
|
||||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -1517,6 +1556,11 @@ snapshots:
|
|||||||
ajv-formats: 3.0.1(ajv@8.20.0)
|
ajv-formats: 3.0.1(ajv@8.20.0)
|
||||||
fast-uri: 3.1.2
|
fast-uri: 3.1.2
|
||||||
|
|
||||||
|
'@fastify/cors@11.2.0':
|
||||||
|
dependencies:
|
||||||
|
fastify-plugin: 5.1.0
|
||||||
|
toad-cache: 3.7.1
|
||||||
|
|
||||||
'@fastify/error@4.2.0': {}
|
'@fastify/error@4.2.0': {}
|
||||||
|
|
||||||
'@fastify/fast-json-stringify-compiler@5.0.3':
|
'@fastify/fast-json-stringify-compiler@5.0.3':
|
||||||
@@ -1576,6 +1620,8 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
'@nodable/entities@2.2.0': {}
|
||||||
|
|
||||||
'@pinojs/redact@0.4.0': {}
|
'@pinojs/redact@0.4.0': {}
|
||||||
|
|
||||||
'@types/estree@1.0.9': {}
|
'@types/estree@1.0.9': {}
|
||||||
@@ -1712,6 +1758,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
color-convert: 2.0.1
|
color-convert: 2.0.1
|
||||||
|
|
||||||
|
anynum@1.0.1: {}
|
||||||
|
|
||||||
argparse@2.0.1: {}
|
argparse@2.0.1: {}
|
||||||
|
|
||||||
atomic-sleep@1.0.0: {}
|
atomic-sleep@1.0.0: {}
|
||||||
@@ -2008,6 +2056,22 @@ snapshots:
|
|||||||
|
|
||||||
fast-uri@3.1.2: {}
|
fast-uri@3.1.2: {}
|
||||||
|
|
||||||
|
fast-xml-builder@1.2.1:
|
||||||
|
dependencies:
|
||||||
|
path-expression-matcher: 1.6.1
|
||||||
|
xml-naming: 0.1.0
|
||||||
|
|
||||||
|
fast-xml-parser@5.9.3:
|
||||||
|
dependencies:
|
||||||
|
'@nodable/entities': 2.2.0
|
||||||
|
fast-xml-builder: 1.2.1
|
||||||
|
is-unsafe: 1.0.1
|
||||||
|
path-expression-matcher: 1.6.1
|
||||||
|
strnum: 2.4.1
|
||||||
|
xml-naming: 0.1.0
|
||||||
|
|
||||||
|
fastify-plugin@5.1.0: {}
|
||||||
|
|
||||||
fastify@5.9.0:
|
fastify@5.9.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@fastify/ajv-compiler': 4.0.5
|
'@fastify/ajv-compiler': 4.0.5
|
||||||
@@ -2153,6 +2217,8 @@ snapshots:
|
|||||||
|
|
||||||
is-promise@4.0.0: {}
|
is-promise@4.0.0: {}
|
||||||
|
|
||||||
|
is-unsafe@1.0.1: {}
|
||||||
|
|
||||||
isexe@2.0.0: {}
|
isexe@2.0.0: {}
|
||||||
|
|
||||||
jose@6.2.3: {}
|
jose@6.2.3: {}
|
||||||
@@ -2265,6 +2331,8 @@ snapshots:
|
|||||||
|
|
||||||
path-exists@4.0.0: {}
|
path-exists@4.0.0: {}
|
||||||
|
|
||||||
|
path-expression-matcher@1.6.1: {}
|
||||||
|
|
||||||
path-key@3.1.1: {}
|
path-key@3.1.1: {}
|
||||||
|
|
||||||
path-to-regexp@8.4.2: {}
|
path-to-regexp@8.4.2: {}
|
||||||
@@ -2454,6 +2522,10 @@ snapshots:
|
|||||||
|
|
||||||
strip-json-comments@5.0.3: {}
|
strip-json-comments@5.0.3: {}
|
||||||
|
|
||||||
|
strnum@2.4.1:
|
||||||
|
dependencies:
|
||||||
|
anynum: 1.0.1
|
||||||
|
|
||||||
supports-color@7.2.0:
|
supports-color@7.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
has-flag: 4.0.0
|
has-flag: 4.0.0
|
||||||
@@ -2522,6 +2594,8 @@ snapshots:
|
|||||||
|
|
||||||
wrappy@1.0.2: {}
|
wrappy@1.0.2: {}
|
||||||
|
|
||||||
|
xml-naming@0.1.0: {}
|
||||||
|
|
||||||
yocto-queue@0.1.0: {}
|
yocto-queue@0.1.0: {}
|
||||||
|
|
||||||
zod-to-json-schema@3.25.2(zod@3.25.76):
|
zod-to-json-schema@3.25.2(zod@3.25.76):
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export default class McpSession {
|
|||||||
|
|
||||||
private buildMcpServer(): McpServer {
|
private buildMcpServer(): McpServer {
|
||||||
const server = new McpServer(
|
const server = new McpServer(
|
||||||
{ name: "mcp-ping-server", version: "1.0.0" },
|
{ name: "home-mcp-server", version: "1.0.0" },
|
||||||
{ capabilities: { tools: { listChanged: true } } },
|
{ capabilities: { tools: { listChanged: true } } },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export * from './ping';
|
export * from './ping';
|
||||||
export * from './listTools';
|
// export * from './listTools';
|
||||||
export * from './invokeTool';
|
// export * from './invokeTool';
|
||||||
export * from './testAddRemoveTool';
|
export * from './testAddRemoveTool';
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import z from "zod";
|
import z from "zod";
|
||||||
import { server } from "../server";
|
import { server } from "../server";
|
||||||
import { ToolDef } from "../types";
|
import { ToolDef } from "../types";
|
||||||
import { logged } from "../utils/logged";
|
|
||||||
|
|
||||||
export const invokeToolTool: ToolDef = {
|
export const invokeToolTool: ToolDef = {
|
||||||
title: "invoke_tool",
|
title: "invoke_tool",
|
||||||
@@ -12,10 +11,10 @@ export const invokeToolTool: ToolDef = {
|
|||||||
args: z.record(z.any()).default({}),
|
args: z.record(z.any()).default({}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
handler: logged('invoke_tool', async ({ name, args }) => {
|
handler: async ({ name, args }) => {
|
||||||
const tool = server.tools[name];
|
const tool = server.tools[name];
|
||||||
if (!tool)
|
if (!tool)
|
||||||
return { isError: true, content: [{ type: "text", text: `Unknown tool: ${name}` }] };
|
return { isError: true, content: [{ type: "text", text: `Unknown tool: ${name}` }] };
|
||||||
return tool.handler(args); // dispatch to the real implementation
|
return tool.handler(args); // dispatch to the real implementation
|
||||||
}),
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import z from "zod";
|
|||||||
import { zodToJsonSchema } from "zod-to-json-schema";
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
||||||
import { server } from "../server";
|
import { server } from "../server";
|
||||||
import { ToolDef } from "../types";
|
import { ToolDef } from "../types";
|
||||||
import { logged } from "../utils/logged";
|
|
||||||
|
|
||||||
export const listToolsTool: ToolDef = {
|
export const listToolsTool: ToolDef = {
|
||||||
title: "list_tools",
|
title: "list_tools",
|
||||||
@@ -10,7 +9,7 @@ export const listToolsTool: ToolDef = {
|
|||||||
description: "Return the authoritative current list of available local tools with their schemas.",
|
description: "Return the authoritative current list of available local tools with their schemas.",
|
||||||
inputSchema: {},
|
inputSchema: {},
|
||||||
},
|
},
|
||||||
handler: logged('list_tools', async () => ({
|
handler: async () => ({
|
||||||
content: [{
|
content: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
text: JSON.stringify(
|
text: JSON.stringify(
|
||||||
@@ -26,5 +25,5 @@ export const listToolsTool: ToolDef = {
|
|||||||
2,
|
2,
|
||||||
)
|
)
|
||||||
}],
|
}],
|
||||||
})),
|
}),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import z from "zod";
|
import z from "zod";
|
||||||
import { ToolDef } from "../types"
|
import { ToolDef } from "../types"
|
||||||
import { isValidHost, logged } from "../utils/logged";
|
import { isValidHost } from "../utils/logged";
|
||||||
import { exec } from "node:child_process";
|
import { exec } from "node:child_process";
|
||||||
import { promisify } from "node:util";
|
import { promisify } from "node:util";
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ export const pingTool: ToolDef = {
|
|||||||
count: z.number().int().min(1).max(10).default(4).describe("Echo requests to send"),
|
count: z.number().int().min(1).max(10).default(4).describe("Echo requests to send"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
handler: logged('ping', async ({ host, count }) => {
|
handler: async ({ host, count }) => {
|
||||||
if (!isValidHost(host)) {
|
if (!isValidHost(host)) {
|
||||||
return {
|
return {
|
||||||
isError: true,
|
isError: true,
|
||||||
@@ -40,5 +40,5 @@ export const pingTool: ToolDef = {
|
|||||||
content: [{ type: "text", text: `ping failed:\n${message}` }],
|
content: [{ type: "text", text: `ping failed:\n${message}` }],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}),
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { ToolDef } from "../types"
|
import { ToolDef } from "../types"
|
||||||
import { logged } from "../utils/logged";
|
|
||||||
import { server } from "../server";
|
import { server } from "../server";
|
||||||
|
|
||||||
const dynamicTool: ToolDef = {
|
const dynamicTool: ToolDef = {
|
||||||
@@ -8,9 +7,9 @@ const dynamicTool: ToolDef = {
|
|||||||
description: "Says hello",
|
description: "Says hello",
|
||||||
inputSchema: {},
|
inputSchema: {},
|
||||||
},
|
},
|
||||||
handler: logged('say_hello', async () => {
|
handler: async () => {
|
||||||
return { content: [{ type: "text", text: "hello" }] };
|
return { content: [{ type: "text", text: "hello" }] };
|
||||||
}),
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const testAddTool: ToolDef = {
|
export const testAddTool: ToolDef = {
|
||||||
@@ -19,10 +18,10 @@ export const testAddTool: ToolDef = {
|
|||||||
description: "Dynamically add a new tool in runtime",
|
description: "Dynamically add a new tool in runtime",
|
||||||
inputSchema: {},
|
inputSchema: {},
|
||||||
},
|
},
|
||||||
handler: logged('test_add_tool', async () => {
|
handler: async () => {
|
||||||
server.registerTools(dynamicTool);
|
server.registerTools(dynamicTool);
|
||||||
return { content: [{ type: "text", text: "New tool added" }] };
|
return { content: [{ type: "text", text: "New tool added" }] };
|
||||||
}),
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const testRemoveTool: ToolDef = {
|
export const testRemoveTool: ToolDef = {
|
||||||
@@ -31,8 +30,8 @@ export const testRemoveTool: ToolDef = {
|
|||||||
description: "Dynamically remove a tool in runtime",
|
description: "Dynamically remove a tool in runtime",
|
||||||
inputSchema: {},
|
inputSchema: {},
|
||||||
},
|
},
|
||||||
handler: logged('test_remove_tool', async () => {
|
handler: async () => {
|
||||||
server.unregisterTools(dynamicTool.title);
|
server.unregisterTools(dynamicTool.title);
|
||||||
return { content: [{ type: "text", text: "Tool removed" }] };
|
return { content: [{ type: "text", text: "Tool removed" }] };
|
||||||
}),
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
+33
-29
@@ -2,7 +2,9 @@ import Fastify, { type FastifyInstance } from "fastify";
|
|||||||
import { ToolDef } from "./types";
|
import { ToolDef } from "./types";
|
||||||
import McpSession from "./mcpConnection";
|
import McpSession from "./mcpConnection";
|
||||||
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
||||||
import { pingTool, testAddTool, testRemoveTool, listToolsTool, invokeToolTool } from "./mcpTools";
|
import * as mcpTools from "./mcpTools";
|
||||||
|
import { logged } from "./utils/logged";
|
||||||
|
import cors from '@fastify/cors';
|
||||||
|
|
||||||
export class Server {
|
export class Server {
|
||||||
private readonly app: FastifyInstance;
|
private readonly app: FastifyInstance;
|
||||||
@@ -14,34 +16,20 @@ export class Server {
|
|||||||
this.port = port;
|
this.port = port;
|
||||||
this.app = Fastify({
|
this.app = Fastify({
|
||||||
trustProxy: true,
|
trustProxy: true,
|
||||||
logger: {
|
|
||||||
serializers: {
|
|
||||||
req(request) {
|
|
||||||
return {
|
|
||||||
method: request.method,
|
|
||||||
url: request.url,
|
|
||||||
host: request.headers.host,
|
|
||||||
remoteAddress: request.ip,
|
|
||||||
headers: request.headers, // <-- full header set
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// logger: {
|
// logger: {
|
||||||
// level: process.env.LOG_LEVEL ?? "info",
|
// serializers: {
|
||||||
// ...(process.env.NODE_ENV !== "production"
|
// req(request) {
|
||||||
// ? {
|
// return {
|
||||||
// transport: {
|
// method: request.method,
|
||||||
// target: "pino-pretty",
|
// url: request.url,
|
||||||
// options: { colorize: true, translateTime: "HH:MM:ss.l", ignore: "pid,hostname" },
|
// host: request.headers.host,
|
||||||
|
// remoteAddress: request.ip,
|
||||||
|
// headers: request.headers, // <-- full header set
|
||||||
|
// };
|
||||||
|
// },
|
||||||
// },
|
// },
|
||||||
// }
|
|
||||||
// : {}),
|
|
||||||
// },
|
// },
|
||||||
});
|
});
|
||||||
this.registerLogging();
|
|
||||||
this.registerRoutes();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerLogging(): void {
|
private registerLogging(): void {
|
||||||
@@ -73,8 +61,10 @@ export class Server {
|
|||||||
public registerTools(...tools: ToolDef[]): void {
|
public registerTools(...tools: ToolDef[]): void {
|
||||||
|
|
||||||
for (const tool of tools) {
|
for (const tool of tools) {
|
||||||
if (!this.getTool(tool.title))
|
if (!this.getTool(tool.title)) {
|
||||||
this.tools[tool.title] = tool;
|
// console.log(`Registering new tool ${tool.title}`);
|
||||||
|
this.tools[tool.title] = logged(tool);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.emitToolListChange();
|
this.emitToolListChange();
|
||||||
@@ -88,8 +78,11 @@ export class Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private emitToolListChange() {
|
private emitToolListChange() {
|
||||||
|
const toolList = Object.values(this.tools);
|
||||||
|
console.log(`Advertising updated tool list: ${toolList.map((tool) => tool.title)}`);
|
||||||
|
|
||||||
for (const session of Object.values(this.sessions))
|
for (const session of Object.values(this.sessions))
|
||||||
session.handleToolListChange(Object.values(this.tools));
|
session.handleToolListChange(toolList);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSession(sessionId: string): McpSession | undefined {
|
public getSession(sessionId: string): McpSession | undefined {
|
||||||
@@ -101,6 +94,9 @@ export class Server {
|
|||||||
// underlying Node req/res it expects.
|
// underlying Node req/res it expects.
|
||||||
private registerRoutes(): void {
|
private registerRoutes(): void {
|
||||||
this.app.post("/mcp", async (request, reply) => {
|
this.app.post("/mcp", async (request, reply) => {
|
||||||
|
reply.raw.setHeader('Access-Control-Allow-Origin', request.headers.origin ?? '*');
|
||||||
|
reply.raw.setHeader('Access-Control-Allow-Credentials', 'true');
|
||||||
|
reply.raw.setHeader('Vary', 'Origin');
|
||||||
|
|
||||||
let session: McpSession | undefined;
|
let session: McpSession | undefined;
|
||||||
|
|
||||||
@@ -157,10 +153,18 @@ export class Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async start(): Promise<void> {
|
async start(): Promise<void> {
|
||||||
|
await this.app.register(cors, {
|
||||||
|
origin: true,
|
||||||
|
credentials: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.registerLogging();
|
||||||
|
await this.registerRoutes();
|
||||||
|
|
||||||
await this.app.listen({ port: this.port, host: "0.0.0.0" });
|
await this.app.listen({ port: this.port, host: "0.0.0.0" });
|
||||||
this.app.log.info(`MCP ping server listening on http://0.0.0.0:${this.port}/mcp`);
|
this.app.log.info(`MCP ping server listening on http://0.0.0.0:${this.port}/mcp`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const server = new Server();
|
export const server = new Server();
|
||||||
server.registerTools(pingTool, listToolsTool, invokeToolTool, testAddTool, testRemoveTool);
|
server.registerTools(...Object.values(mcpTools));
|
||||||
+15
-10
@@ -1,29 +1,34 @@
|
|||||||
import { randomUUID } from "node:crypto";
|
import { randomUUID } from "node:crypto";
|
||||||
import { TextResult } from "../types";
|
import { TextResult, ToolDef } from "../types";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export function logged<A extends any[]>(
|
export function logged<A extends any[]>(
|
||||||
name: string,
|
tool: ToolDef,
|
||||||
handler: (...args: A) => Promise<TextResult>,
|
): ToolDef {
|
||||||
): (...args: A) => Promise<TextResult> {
|
|
||||||
return async (...args: A) => {
|
console.dir(tool);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...tool,
|
||||||
|
handler: async (...args: A): Promise<TextResult> => {
|
||||||
const id = randomUUID().slice(0, 8);
|
const id = randomUUID().slice(0, 8);
|
||||||
const t0 = process.hrtime.bigint();
|
const t0 = process.hrtime.bigint();
|
||||||
console.log(`[tool>] ${name} #${id} args=${JSON.stringify(args)}`);
|
console.log(`[tool>] ${tool.title} #${id} args=${JSON.stringify(args)}`);
|
||||||
try {
|
try {
|
||||||
const result = await handler(...args);
|
const result = await tool.handler(...args);
|
||||||
const ms = Number(process.hrtime.bigint() - t0) / 1e6;
|
const ms = Number(process.hrtime.bigint() - t0) / 1e6;
|
||||||
console.log(
|
console.log(
|
||||||
`[tool<] ${name} #${id} ${ms.toFixed(1)}ms` +
|
`[tool<] ${tool.title} #${id} ${ms.toFixed(1)}ms` +
|
||||||
`${result.isError ? " ERROR" : ""} result=${JSON.stringify(result).slice(0, 2000)}`,
|
`${result.isError ? " ERROR" : ""} result=${JSON.stringify(result).slice(0, 2000)}`,
|
||||||
);
|
);
|
||||||
return result;
|
return result;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const ms = Number(process.hrtime.bigint() - t0) / 1e6;
|
const ms = Number(process.hrtime.bigint() - t0) / 1e6;
|
||||||
console.error(`[tool!] ${name} #${id} ${ms.toFixed(1)}ms threw:`, err);
|
console.error(`[tool!] ${tool.title} #${id} ${ms.toFixed(1)}ms threw:`, err);
|
||||||
throw err; // let the SDK turn it into a JSON-RPC error
|
throw err; // let the SDK turn it into a JSON-RPC error
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isValidHost(host: string): boolean {
|
export function isValidHost(host: string): boolean {
|
||||||
|
|||||||
Reference in New Issue
Block a user