Skip to content
Merged
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
42 changes: 42 additions & 0 deletions src/schema.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { BuildListTool, BuildGetTool } from "./schema.js";
import { Resource } from "./common/api/types.js";

describe("BuildListTool", () => {
it("should create a tool for listing resources", () => {
const resource: Resource = {
singular: "example",
plural: "examples",
parents: [],
children: [],
patternElems: [],
schema: { properties: {} },
customMethods: [],
};
const tool = BuildListTool(resource, "example");
expect(tool.name).toBe("list-example");
expect(tool.description).toBe("List all example resources");
expect(tool.inputSchema).toEqual({ type: "object", properties: {} });
});
});

describe("BuildGetTool", () => {
it("should create a tool for getting a resource", () => {
const resource: Resource = {
singular: "example",
plural: "examples",
parents: [],
children: [],
patternElems: [],
schema: { properties: {} },
customMethods: [],
};
const tool = BuildGetTool(resource, "example");
expect(tool.name).toBe("get-example");
expect(tool.description).toBe("Get details of a specific example");
expect(tool.inputSchema).toEqual({
type: "object",
properties: { path: { type: "string" } },
required: ["path"],
});
});
});
40 changes: 40 additions & 0 deletions src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,46 @@ export function BuildUpdateTool(resource: Resource, resourceName: string): Tool
};
}

export function BuildListTool(resource: Resource, resourceName: string): Tool {
// get all path params except the last one, which is the resource name. For list
// we ignore it.
const parentFieldNames = resource.patternElems.filter((elem) => elem.startsWith("{")).slice(0, -1);
// strip the braces from the field names
for (let i = 0; i < parentFieldNames.length; i++) {
parentFieldNames[i] = parentFieldNames[i].slice(1, -1);
}
const properties: Record<string, unknown> = {};
// iterate parentFieldNames
for (const name of parentFieldNames) {
properties[name] = {
type: "string",
description: `The ${name} to filter the list of ${resourceName} resources`,
};
}
return {
name: `list-${resourceName}`,
description: `List all ${resourceName} resources`,
inputSchema: {
type: "object",
properties: properties,
},
};
}

export function BuildGetTool(resource: Resource, resourceName: string): Tool {
return {
name: `get-${resourceName}`,
description: `Get details of a specific ${resourceName}`,
inputSchema: {
type: "object",
properties: {
path: { type: "string" },
},
required: ["path"],
},
};
}

export function BuildResource(resource: Resource, resourceName: string, serverUrl: string, prefix: string) {
return {
uriTemplate: `${serverUrl}/${resource.patternElems.join('/')}`,
Expand Down
27 changes: 25 additions & 2 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
import { fetchOpenAPI, OpenAPIImpl } from "./common/openapi/openapi.js";
import { APIClient } from "./common/api/api.js";
import { OpenAPI as OpenAPIType, Resource } from "./common/api/types.js";
import { BuildCreateTool, BuildDeleteTool, BuildResource, BuildUpdateTool } from "./schema.js";
import { BuildCreateTool, BuildDeleteTool, BuildResource, BuildUpdateTool, BuildListTool, BuildGetTool } from "./schema.js";

import axios from "axios";
import { Client } from "./common/client/client.js";
Expand Down Expand Up @@ -99,7 +99,9 @@ export async function main() {
tools.push(BuildCreateTool(resource, resourceName));
tools.push(BuildDeleteTool(resource, resourceName));
tools.push(BuildUpdateTool(resource, resourceName));
resourceList.push(BuildResource(resource, resourceName, a.serverUrl(), prefix))
tools.push(BuildListTool(resource, resourceName));
tools.push(BuildGetTool(resource, resourceName));
resourceList.push(BuildResource(resource, resourceName, a.serverUrl(), prefix));
}

// empty handlers
Expand Down Expand Up @@ -179,6 +181,27 @@ export async function main() {
}],
isError: false
};
} else if (matchingTool.name.startsWith("list")) {
const resourceSingular = matchingTool.name.split("-")[1];
const resource = a.resources()[resourceSingular];
const resp = await client.list({}, resource, a.serverUrl(), request.params.arguments!);
return {
content: [{
type: "text",
text: JSON.stringify(resp)
}],
isError: false
};
} else if (matchingTool.name.startsWith("get")) {
const path = request.params.arguments!["path"];
const resp = await client.get({}, a.serverUrl(), path);
return {
content: [{
type: "text",
text: JSON.stringify(resp)
}],
isError: false
};
}
} catch (error) {
return {
Expand Down