Skip to content

Commit 9a05f60

Browse files
committed
Add a few more tools
1 parent f8e2dd1 commit 9a05f60

File tree

7 files changed

+367
-23
lines changed

7 files changed

+367
-23
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/server-runtime";
2+
import { json } from "@remix-run/server-runtime";
3+
import {
4+
CreateProjectRequestBody,
5+
GetProjectResponseBody,
6+
GetProjectsResponseBody,
7+
} from "@trigger.dev/core/v3";
8+
import { z } from "zod";
9+
import { prisma } from "~/db.server";
10+
import { createProject } from "~/models/project.server";
11+
import { logger } from "~/services/logger.server";
12+
import { authenticateApiRequestWithPersonalAccessToken } from "~/services/personalAccessToken.server";
13+
14+
const ParamsSchema = z.object({
15+
orgParam: z.string(),
16+
});
17+
18+
export async function loader({ request, params }: LoaderFunctionArgs) {
19+
logger.info("get projects", { url: request.url });
20+
21+
const authenticationResult = await authenticateApiRequestWithPersonalAccessToken(request);
22+
23+
if (!authenticationResult) {
24+
return json({ error: "Invalid or Missing Access Token" }, { status: 401 });
25+
}
26+
27+
const { orgParam } = ParamsSchema.parse(params);
28+
29+
const projects = await prisma.project.findMany({
30+
where: {
31+
organization: {
32+
...orgParamWhereClause(orgParam),
33+
deletedAt: null,
34+
members: {
35+
some: {
36+
userId: authenticationResult.userId,
37+
},
38+
},
39+
},
40+
version: "V3",
41+
deletedAt: null,
42+
},
43+
include: {
44+
organization: true,
45+
},
46+
});
47+
48+
if (!projects) {
49+
return json({ error: "Projects not found" }, { status: 404 });
50+
}
51+
52+
const result: GetProjectsResponseBody = projects.map((project) => ({
53+
id: project.id,
54+
externalRef: project.externalRef,
55+
name: project.name,
56+
slug: project.slug,
57+
createdAt: project.createdAt,
58+
organization: {
59+
id: project.organization.id,
60+
title: project.organization.title,
61+
slug: project.organization.slug,
62+
createdAt: project.organization.createdAt,
63+
},
64+
}));
65+
66+
return json(result);
67+
}
68+
69+
export async function action({ request, params }: ActionFunctionArgs) {
70+
const authenticationResult = await authenticateApiRequestWithPersonalAccessToken(request);
71+
72+
if (!authenticationResult) {
73+
return json({ error: "Invalid or Missing Access Token" }, { status: 401 });
74+
}
75+
76+
const { orgParam } = ParamsSchema.parse(params);
77+
78+
const organization = await prisma.organization.findFirst({
79+
where: {
80+
...orgParamWhereClause(orgParam),
81+
deletedAt: null,
82+
members: {
83+
some: {
84+
userId: authenticationResult.userId,
85+
},
86+
},
87+
},
88+
});
89+
90+
if (!organization) {
91+
return json({ error: "Organization not found" }, { status: 404 });
92+
}
93+
94+
const body = await request.json();
95+
const parsedBody = CreateProjectRequestBody.safeParse(body);
96+
97+
if (!parsedBody.success) {
98+
return json({ error: "Invalid request body" }, { status: 400 });
99+
}
100+
101+
const project = await createProject({
102+
organizationSlug: organization.slug,
103+
name: parsedBody.data.name,
104+
userId: authenticationResult.userId,
105+
version: "v3",
106+
});
107+
108+
const result: GetProjectResponseBody = {
109+
id: project.id,
110+
externalRef: project.externalRef,
111+
name: project.name,
112+
slug: project.slug,
113+
createdAt: project.createdAt,
114+
organization: {
115+
id: project.organization.id,
116+
title: project.organization.title,
117+
slug: project.organization.slug,
118+
createdAt: project.organization.createdAt,
119+
},
120+
};
121+
122+
return json(result);
123+
}
124+
125+
function orgParamWhereClause(orgParam: string) {
126+
// If the orgParam is an ID, or if it's a slug
127+
// IDs are cuid
128+
if (isCuid(orgParam)) {
129+
return {
130+
id: orgParam,
131+
};
132+
}
133+
134+
return {
135+
slug: orgParam,
136+
};
137+
}
138+
139+
function isCuid(orgParam: string): boolean {
140+
return /^[0-9A-HJ-NP-TV-Z]{25}$/.test(orgParam);
141+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import type { LoaderFunctionArgs } from "@remix-run/server-runtime";
2+
import { json } from "@remix-run/server-runtime";
3+
import { GetOrgsResponseBody } from "@trigger.dev/core/v3";
4+
import { prisma } from "~/db.server";
5+
import { authenticateApiRequestWithPersonalAccessToken } from "~/services/personalAccessToken.server";
6+
7+
export async function loader({ request }: LoaderFunctionArgs) {
8+
const authenticationResult = await authenticateApiRequestWithPersonalAccessToken(request);
9+
10+
if (!authenticationResult) {
11+
return json({ error: "Invalid or Missing Access Token" }, { status: 401 });
12+
}
13+
14+
const orgs = await prisma.organization.findMany({
15+
where: {
16+
deletedAt: null,
17+
members: {
18+
some: {
19+
userId: authenticationResult.userId,
20+
},
21+
},
22+
},
23+
});
24+
25+
if (!orgs) {
26+
return json({ error: "Orgs not found" }, { status: 404 });
27+
}
28+
29+
const result: GetOrgsResponseBody = orgs.map((org) => ({
30+
id: org.id,
31+
title: org.title,
32+
slug: org.slug,
33+
createdAt: org.createdAt,
34+
}));
35+
36+
return json(result);
37+
}

apps/webapp/app/routes/api.v1.projects.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { LoaderFunctionArgs } from "@remix-run/server-runtime";
1+
import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/server-runtime";
22
import { json } from "@remix-run/server-runtime";
33
import { GetProjectsResponseBody } from "@trigger.dev/core/v3";
44
import { prisma } from "~/db.server";

packages/cli-v3/src/apiClient.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ import {
3131
WorkersCreateRequestBody,
3232
WorkersCreateResponseBody,
3333
WorkersListResponseBody,
34+
CreateProjectRequestBody,
35+
GetOrgsResponseBody,
3436
} from "@trigger.dev/core/v3";
3537
import {
3638
WorkloadDebugLogRequestBody,
@@ -136,6 +138,31 @@ export class CliApiClient {
136138
});
137139
}
138140

141+
async getOrgs() {
142+
if (!this.accessToken) {
143+
throw new Error("getOrgs: No access token");
144+
}
145+
146+
return wrapZodFetch(GetOrgsResponseBody, `${this.apiURL}/api/v1/orgs`, {
147+
headers: {
148+
Authorization: `Bearer ${this.accessToken}`,
149+
"Content-Type": "application/json",
150+
},
151+
});
152+
}
153+
154+
async createProject(orgParam: string, body: CreateProjectRequestBody) {
155+
if (!this.accessToken) {
156+
throw new Error("createProject: No access token");
157+
}
158+
159+
return wrapZodFetch(GetProjectResponseBody, `${this.apiURL}/api/v1/orgs/${orgParam}/projects`, {
160+
method: "POST",
161+
headers: this.getHeaders(),
162+
body: JSON.stringify(body),
163+
});
164+
}
165+
139166
async createBackgroundWorker(projectRef: string, body: CreateBackgroundWorkerRequestBody) {
140167
if (!this.accessToken) {
141168
throw new Error("createBackgroundWorker: No access token");

packages/cli-v3/src/commands/mcp.ts

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
33
import { Command } from "commander";
44
import { z } from "zod";
55
import { CommonCommandOptions, commonOptions, wrapCommandAction } from "../cli/common.js";
6-
import { login } from "./login.js";
7-
import { performSearch } from "../mcp/mintlifyClient.js";
8-
import { logger } from "../utilities/logger.js";
9-
import { FileLogger } from "../mcp/logger.js";
10-
import { McpContext } from "../mcp/context.js";
11-
import { registerGetProjectDetailsTool, registerListProjectsTool } from "../mcp/tools.js";
126
import { CLOUD_API_URL } from "../consts.js";
7+
import { McpContext } from "../mcp/context.js";
8+
import { FileLogger } from "../mcp/logger.js";
9+
import {
10+
registerCreateProjectTool,
11+
registerGetProjectDetailsTool,
12+
registerListOrgsTool,
13+
registerListProjectsTool,
14+
registerSearchDocsTool,
15+
} from "../mcp/tools.js";
16+
import { logger } from "../utilities/logger.js";
1317

1418
const McpCommandOptions = CommonCommandOptions.extend({
1519
projectRef: z.string().optional(),
@@ -54,23 +58,11 @@ export async function mcpCommand(options: McpCommandOptions) {
5458

5559
fileLogger?.log("running mcp command", { options });
5660

57-
server.registerTool(
58-
"search_docs",
59-
{
60-
description:
61-
"Search across the Trigger.dev documentation to find relevant information, code examples, API references, and guides. Use this tool when you need to answer questions about Trigger.dev, find specific documentation, understand how features work, or locate implementation details. The search returns contextual content with titles and direct links to the documentation pages",
62-
inputSchema: {
63-
query: z.string(),
64-
},
65-
},
66-
async ({ query }) => {
67-
const results = await performSearch(query);
68-
return results;
69-
}
70-
);
71-
61+
registerSearchDocsTool(context);
7262
registerGetProjectDetailsTool(context);
7363
registerListProjectsTool(context);
64+
registerListOrgsTool(context);
65+
registerCreateProjectTool(context);
7466

7567
// Start receiving messages on stdin and sending messages on stdout
7668
const transport = new StdioServerTransport();

0 commit comments

Comments
 (0)