Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 5 additions & 18 deletions examples/ajv-tools/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { serve } from '@hono/node-server'
import { Hono } from 'hono';
import { MCPServer } from "@zuplo/mcp/server";
import { HTTPStreamableTransport } from "@zuplo/mcp/transport/httpstreamable";
import Ajv, { JSONSchemaType } from 'ajv';
import { CustomValidator } from '@zuplo/mcp/tools/custom';
import { JSONSchemaType } from 'ajv';
import { AjvValidator } from '@zuplo/mcp/tools/ajv';

// Hono app for routing and handling fetch API Request / Response
const app = new Hono();
Expand All @@ -14,8 +14,6 @@ const server = new MCPServer({
version: "0.0.0",
});

const ajv = new Ajv();

type NumberPair = { a: number; b: number };

const numberPairSchema: JSONSchemaType<NumberPair> = {
Expand All @@ -28,23 +26,12 @@ const numberPairSchema: JSONSchemaType<NumberPair> = {
additionalProperties: false,
};

const validateFn = ajv.compile<NumberPair>(numberPairSchema);

const numberPairValidator = new CustomValidator<NumberPair>(
numberPairSchema,
(input) => {
if (validateFn(input)) {
return { success: true, data: input, error: null }
};

return { success: false, data: null, error: ajv.errorsText(validateFn.errors) }
}
);

server.addTool({
name: "add",
description: "Adds two numbers together",
validator: numberPairValidator,
validator: new AjvValidator<NumberPair>(
numberPairSchema
),
handler: async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }],
isError: false,
Expand Down
42 changes: 42 additions & 0 deletions examples/custom-validator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Custom Ajv validator

This demonstrates using Ajv with the `CustomValidator` to in an advanced use case to:

1. Build a custom schema validator function based on a concrete type / schema using `ajv.compile`
2. Build a hand rolled, "bring your own validator" with `CustomValidator<T>`
3. Provide the custom validator when bootstrapping a tool

## 🏃 Build & run the server

```
npm i
npm run build
npm run start
```

## 🧠 Interact

Use the [Model Context Protocol Inspector](https://github.com/modelcontextprotocol/inspector)
and `localhost:3000/mcp`.

```sh
npx @modelcontextprotocol/inspector
```

or curl:

```sh
curl -X POST \
-H 'Accept: application/json' \
-d '{
method: "tools/call"
params:{
name: "add"
arguments:{
a: 1
b: 2
}
}
}' \
localhost:3000/mcp
```
158 changes: 158 additions & 0 deletions examples/custom-validator/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions examples/custom-validator/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "@zuplo/mcp example tools server",
"private": true,
"type": "module",
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
},
"dependencies": {
"@hono/node-server": "^1.14.1",
"@zuplo/mcp": "file:../../",
"ajv": "^8.17.1",
"hono": "^4.7.9",
"json-schema-to-ts": "^3.1.1"
}
}
81 changes: 81 additions & 0 deletions examples/custom-validator/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { serve } from '@hono/node-server'
import { Hono } from 'hono';
import { MCPServer } from "@zuplo/mcp/server";
import { HTTPStreamableTransport } from "@zuplo/mcp/transport/httpstreamable";
import Ajv, { AnySchemaObject, JSONSchemaType } from 'ajv';
import { CustomValidator } from '@zuplo/mcp/tools/custom';
import { FromSchema } from "json-schema-to-ts";

// Hono app for routing and handling fetch API Request / Response
const app = new Hono();

// MCP server
const server = new MCPServer({
name: "Calculator",
version: "0.0.0",
});

const ajv = new Ajv();

type NumberPair = { a: number; b: number };

const numberPairSchema: JSONSchemaType<NumberPair> = {
type: "object",
properties: {
a: { type: "number" },
b: { type: "number" },
},
required: ["a", "b"],
additionalProperties: false,
};

// Infers the object type and schema from the validation function
const validateFn = ajv.compile(numberPairSchema);

console.log(validateFn.schema)

const numberPairValidator = new CustomValidator<any>(
validateFn.schema as AnySchemaObject,
(input) => {
if (validateFn(input)) {
return { success: true, data: input, error: null }
};

return { success: false, data: null, error: ajv.errorsText(validateFn.errors) }
}
);

server.addTool({
name: "add",
description: "Adds two numbers together",
validator: numberPairValidator,
handler: async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }],
isError: false,
}),
});

// HTTP Streamable Transport
const transport = new HTTPStreamableTransport();
await transport.connect();
server.withTransport(transport);

// Set up a POST route for MCP requests
app.post('/mcp', async (c) => {
try {
const request = c.req.raw;
return transport.handleRequest(request);
} catch (error) {
console.error('Error handling MCP request:', error);
return new Response(JSON.stringify({ error: 'Internal server error' }), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
});

console.log("serving on port 3000")
serve({
fetch: app.fetch,
port: 3000
});
13 changes: 13 additions & 0 deletions examples/custom-validator/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": [
"./src/**/*"
],
"references": [
{ "path": "../../" }
]
}
Loading
Loading