mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-06-12 17:34:27 +02:00
Add body and auth support to MCP HTTP request tools
- Add body, bodyType, authentication, and authenticationType fields to create/update HTTP request MCP tools - Include comprehensive documentation for body structures and auth types in Zod descriptions - Fix MCP update_http_request to merge partial updates with existing data to prevent FK constraint violations - Fix Zod imports from 'zod/v4' to 'zod' to match installed version - Add uncaughtException handler to plugin runtime to prevent individual plugin crashes from crashing entire runtime
This commit is contained in:
@@ -8,7 +8,9 @@ Generate formatted release notes for Yaak releases by analyzing git history and
|
|||||||
## What to do
|
## What to do
|
||||||
|
|
||||||
1. Identifies the version tag and previous version
|
1. Identifies the version tag and previous version
|
||||||
2. Retrieves all commits between versions (scoped to stable or beta)
|
2. Retrieves all commits between versions
|
||||||
|
- If the version is a beta version, it retrieves commits between the beta version and previous beta version
|
||||||
|
- If the version is a stable version, it retrieves commits between the stable version and the previous stable version
|
||||||
3. Fetches PR descriptions for linked issues to find:
|
3. Fetches PR descriptions for linked issues to find:
|
||||||
- Feedback URLs (feedback.yaak.app)
|
- Feedback URLs (feedback.yaak.app)
|
||||||
- Additional context and descriptions
|
- Additional context and descriptions
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
import console from 'node:console';
|
||||||
|
import { type Stats, statSync, watch } from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
import type { Context, PluginDefinition } from '@yaakapp/api';
|
||||||
import {
|
import {
|
||||||
applyFormInputDefaults,
|
applyFormInputDefaults,
|
||||||
validateTemplateFunctionArgs,
|
validateTemplateFunctionArgs,
|
||||||
@@ -34,10 +38,6 @@ import type {
|
|||||||
UpsertModelResponse,
|
UpsertModelResponse,
|
||||||
WindowInfoResponse,
|
WindowInfoResponse,
|
||||||
} from '@yaakapp-internal/plugins';
|
} from '@yaakapp-internal/plugins';
|
||||||
import type { Context, PluginDefinition } from '@yaakapp/api';
|
|
||||||
import console from 'node:console';
|
|
||||||
import { type Stats, statSync, watch } from 'node:fs';
|
|
||||||
import path from 'node:path';
|
|
||||||
import { applyDynamicFormInput } from './common';
|
import { applyDynamicFormInput } from './common';
|
||||||
import { EventChannel } from './EventChannel';
|
import { EventChannel } from './EventChannel';
|
||||||
import { migrateTemplateFunctionSelectOptions } from './migrations';
|
import { migrateTemplateFunctionSelectOptions } from './migrations';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { InternalEvent } from '@yaakapp/api';
|
import type { InternalEvent } from '@yaakapp/api';
|
||||||
|
import WebSocket from 'ws';
|
||||||
import { EventChannel } from './EventChannel';
|
import { EventChannel } from './EventChannel';
|
||||||
import { PluginHandle } from './PluginHandle';
|
import { PluginHandle } from './PluginHandle';
|
||||||
import WebSocket from 'ws';
|
|
||||||
|
|
||||||
const port = process.env.PORT;
|
const port = process.env.PORT;
|
||||||
if (!port) {
|
if (!port) {
|
||||||
@@ -67,3 +67,7 @@ async function handleIncoming(msg: string) {
|
|||||||
process.on('unhandledRejection', (reason, promise) => {
|
process.on('unhandledRejection', (reason, promise) => {
|
||||||
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
process.on('uncaughtException', (error) => {
|
||||||
|
console.error('Uncaught Exception:', error);
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||||
import * as z from 'zod/v4';
|
import * as z from 'zod';
|
||||||
import type { McpServerContext } from '../types.js';
|
import type { McpServerContext } from '../types.js';
|
||||||
import { getWorkspaceContext } from './helpers.js';
|
import { getWorkspaceContext } from './helpers.js';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||||
import * as z from 'zod/v4';
|
import * as z from 'zod';
|
||||||
import type { McpServerContext } from '../types.js';
|
import type { McpServerContext } from '../types.js';
|
||||||
import { getWorkspaceContext } from './helpers.js';
|
import { getWorkspaceContext } from './helpers.js';
|
||||||
|
|
||||||
@@ -131,6 +131,42 @@ export function registerHttpRequestTools(server: McpServer, ctx: McpServerContex
|
|||||||
)
|
)
|
||||||
.optional()
|
.optional()
|
||||||
.describe('URL query parameters'),
|
.describe('URL query parameters'),
|
||||||
|
bodyType: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.describe(
|
||||||
|
'Body type. Supported values: "binary", "graphql", "application/x-www-form-urlencoded", "multipart/form-data", or any text-based type (e.g., "application/json", "text/plain")',
|
||||||
|
),
|
||||||
|
body: z
|
||||||
|
.record(z.string(), z.any())
|
||||||
|
.optional()
|
||||||
|
.describe(
|
||||||
|
'Body content object. Structure varies by bodyType:\n' +
|
||||||
|
'- "binary": { filePath: "/path/to/file" }\n' +
|
||||||
|
'- "graphql": { query: "{ users { name } }", variables: "{\\"id\\": \\"123\\"}" }\n' +
|
||||||
|
'- "application/x-www-form-urlencoded": { form: [{ name: "key", value: "val", enabled: true }] }\n' +
|
||||||
|
'- "multipart/form-data": { form: [{ name: "field", value: "text", file: "/path/to/file", enabled: true }] }\n' +
|
||||||
|
'- text-based (application/json, etc.): { text: "raw body content" }',
|
||||||
|
),
|
||||||
|
authenticationType: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.describe(
|
||||||
|
'Authentication type. Common values: "basic", "bearer", "oauth2", "apikey", "jwt", "awsv4", "oauth1", "ntlm", "none". Use null to inherit from parent folder/workspace.',
|
||||||
|
),
|
||||||
|
authentication: z
|
||||||
|
.record(z.string(), z.any())
|
||||||
|
.optional()
|
||||||
|
.describe(
|
||||||
|
'Authentication configuration object. Structure varies by authenticationType:\n' +
|
||||||
|
'- "basic": { username: "user", password: "pass" }\n' +
|
||||||
|
'- "bearer": { token: "abc123", prefix: "Bearer" }\n' +
|
||||||
|
'- "oauth2": { clientId: "...", clientSecret: "...", grantType: "authorization_code", authorizationUrl: "...", accessTokenUrl: "...", scope: "...", ... }\n' +
|
||||||
|
'- "apikey": { location: "header" | "query", key: "X-API-Key", value: "..." }\n' +
|
||||||
|
'- "jwt": { algorithm: "HS256", secret: "...", payload: "{ ... }" }\n' +
|
||||||
|
'- "awsv4": { accessKeyId: "...", secretAccessKey: "...", service: "sts", region: "us-east-1", sessionToken: "..." }\n' +
|
||||||
|
'- "none": {}',
|
||||||
|
),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
async ({ workspaceId: ogWorkspaceId, ...args }) => {
|
async ({ workspaceId: ogWorkspaceId, ...args }) => {
|
||||||
@@ -158,10 +194,7 @@ export function registerHttpRequestTools(server: McpServer, ctx: McpServerContex
|
|||||||
description: 'Update an existing HTTP request',
|
description: 'Update an existing HTTP request',
|
||||||
inputSchema: z.object({
|
inputSchema: z.object({
|
||||||
id: z.string().describe('HTTP request ID to update'),
|
id: z.string().describe('HTTP request ID to update'),
|
||||||
workspaceId: z
|
workspaceId: z.string().describe('Workspace ID'),
|
||||||
.string()
|
|
||||||
.optional()
|
|
||||||
.describe('Workspace ID (required if multiple workspaces are open)'),
|
|
||||||
name: z.string().optional().describe('Request name'),
|
name: z.string().optional().describe('Request name'),
|
||||||
url: z.string().optional().describe('Request URL'),
|
url: z.string().optional().describe('Request URL'),
|
||||||
method: z.string().optional().describe('HTTP method'),
|
method: z.string().optional().describe('HTTP method'),
|
||||||
@@ -187,11 +220,57 @@ export function registerHttpRequestTools(server: McpServer, ctx: McpServerContex
|
|||||||
)
|
)
|
||||||
.optional()
|
.optional()
|
||||||
.describe('URL query parameters'),
|
.describe('URL query parameters'),
|
||||||
|
bodyType: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.describe(
|
||||||
|
'Body type. Supported values: "binary", "graphql", "application/x-www-form-urlencoded", "multipart/form-data", or any text-based type (e.g., "application/json", "text/plain")',
|
||||||
|
),
|
||||||
|
body: z
|
||||||
|
.record(z.string(), z.any())
|
||||||
|
.optional()
|
||||||
|
.describe(
|
||||||
|
'Body content object. Structure varies by bodyType:\n' +
|
||||||
|
'- "binary": { filePath: "/path/to/file" }\n' +
|
||||||
|
'- "graphql": { query: "{ users { name } }", variables: "{\\"id\\": \\"123\\"}" }\n' +
|
||||||
|
'- "application/x-www-form-urlencoded": { form: [{ name: "key", value: "val", enabled: true }] }\n' +
|
||||||
|
'- "multipart/form-data": { form: [{ name: "field", value: "text", file: "/path/to/file", enabled: true }] }\n' +
|
||||||
|
'- text-based (application/json, etc.): { text: "raw body content" }',
|
||||||
|
),
|
||||||
|
authenticationType: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.describe(
|
||||||
|
'Authentication type. Common values: "basic", "bearer", "oauth2", "apikey", "jwt", "awsv4", "oauth1", "ntlm", "none". Use null to inherit from parent folder/workspace.',
|
||||||
|
),
|
||||||
|
authentication: z
|
||||||
|
.record(z.string(), z.any())
|
||||||
|
.optional()
|
||||||
|
.describe(
|
||||||
|
'Authentication configuration object. Structure varies by authenticationType:\n' +
|
||||||
|
'- "basic": { username: "user", password: "pass" }\n' +
|
||||||
|
'- "bearer": { token: "abc123", prefix: "Bearer" }\n' +
|
||||||
|
'- "oauth2": { clientId: "...", clientSecret: "...", grantType: "authorization_code", authorizationUrl: "...", accessTokenUrl: "...", scope: "...", ... }\n' +
|
||||||
|
'- "apikey": { location: "header" | "query", key: "X-API-Key", value: "..." }\n' +
|
||||||
|
'- "jwt": { algorithm: "HS256", secret: "...", payload: "{ ... }" }\n' +
|
||||||
|
'- "awsv4": { accessKeyId: "...", secretAccessKey: "...", service: "sts", region: "us-east-1", sessionToken: "..." }\n' +
|
||||||
|
'- "none": {}',
|
||||||
|
),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
async ({ id, workspaceId, ...updates }) => {
|
async ({ id, workspaceId, ...updates }) => {
|
||||||
const workspaceCtx = await getWorkspaceContext(ctx, workspaceId);
|
const workspaceCtx = await getWorkspaceContext(ctx, workspaceId);
|
||||||
const httpRequest = await workspaceCtx.yaak.httpRequest.update({ id, ...updates });
|
// Fetch existing request to merge with updates
|
||||||
|
const existing = await workspaceCtx.yaak.httpRequest.getById({ id });
|
||||||
|
if (!existing) {
|
||||||
|
throw new Error(`HTTP request with ID ${id} not found`);
|
||||||
|
}
|
||||||
|
// Merge existing fields with updates
|
||||||
|
const httpRequest = await workspaceCtx.yaak.httpRequest.update({
|
||||||
|
...existing,
|
||||||
|
...updates,
|
||||||
|
id,
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
content: [{ type: 'text' as const, text: JSON.stringify(httpRequest, null, 2) }],
|
content: [{ type: 'text' as const, text: JSON.stringify(httpRequest, null, 2) }],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||||
import type { Color, Icon } from '@yaakapp/api';
|
import type { Color, Icon } from '@yaakapp/api';
|
||||||
import * as z from 'zod/v4';
|
import * as z from 'zod';
|
||||||
import type { McpServerContext } from '../types.js';
|
import type { McpServerContext } from '../types.js';
|
||||||
|
|
||||||
const ICON_VALUES = [
|
const ICON_VALUES = [
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||||
import * as z from 'zod/v4';
|
import * as z from 'zod';
|
||||||
import type { McpServerContext } from '../types.js';
|
import type { McpServerContext } from '../types.js';
|
||||||
import { getWorkspaceContext } from './helpers.js';
|
import { getWorkspaceContext } from './helpers.js';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||||
import * as z from 'zod/v4';
|
import * as z from 'zod';
|
||||||
import type { McpServerContext } from '../types.js';
|
import type { McpServerContext } from '../types.js';
|
||||||
|
|
||||||
export function registerWorkspaceTools(server: McpServer, ctx: McpServerContext) {
|
export function registerWorkspaceTools(server: McpServer, ctx: McpServerContext) {
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ export const synthwave84: Theme = {
|
|||||||
text: 'hsl(300, 50%, 90%)',
|
text: 'hsl(300, 50%, 90%)',
|
||||||
textSubtle: 'hsl(280, 25%, 65%)',
|
textSubtle: 'hsl(280, 25%, 65%)',
|
||||||
textSubtlest: 'hsl(280, 20%, 50%)',
|
textSubtlest: 'hsl(280, 20%, 50%)',
|
||||||
primary: 'hsl(177, 100%, 55%)',
|
primary: 'hsl(320, 100%, 75%)',
|
||||||
secondary: 'hsl(280, 20%, 60%)',
|
secondary: 'hsl(280, 20%, 60%)',
|
||||||
info: 'hsl(320, 100%, 75%)',
|
info: 'hsl(177, 100%, 55%)',
|
||||||
success: 'hsl(83, 100%, 60%)',
|
success: 'hsl(83, 100%, 60%)',
|
||||||
notice: 'hsl(57, 100%, 60%)',
|
notice: 'hsl(57, 100%, 60%)',
|
||||||
warning: 'hsl(30, 100%, 60%)',
|
warning: 'hsl(30, 100%, 60%)',
|
||||||
@@ -35,9 +35,9 @@ export const synthwave84: Theme = {
|
|||||||
border: 'hsl(253, 40%, 22%)',
|
border: 'hsl(253, 40%, 22%)',
|
||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
primary: 'hsl(177, 100%, 48%)',
|
primary: 'hsl(320, 100%, 68%)',
|
||||||
secondary: 'hsl(280, 20%, 53%)',
|
secondary: 'hsl(280, 20%, 53%)',
|
||||||
info: 'hsl(320, 100%, 68%)',
|
info: 'hsl(177, 100%, 48%)',
|
||||||
success: 'hsl(83, 100%, 53%)',
|
success: 'hsl(83, 100%, 53%)',
|
||||||
notice: 'hsl(57, 100%, 53%)',
|
notice: 'hsl(57, 100%, 53%)',
|
||||||
warning: 'hsl(30, 100%, 53%)',
|
warning: 'hsl(30, 100%, 53%)',
|
||||||
|
|||||||
Reference in New Issue
Block a user