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
2 changes: 1 addition & 1 deletion .github/workflows/mcp-inspector.lock.yml

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

16 changes: 16 additions & 0 deletions actions/setup/js/mcp_server_core.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,13 @@ async function handleRequest(server, request, defaultHandler) {

const missing = validateRequiredFields(args, tool.inputSchema);
if (missing.length) {
const hasRequiredFields = tool.inputSchema && Array.isArray(tool.inputSchema.required) && tool.inputSchema.required.length > 0;
if (hasRequiredFields && Object.keys(args).length === 0) {
throw {
code: -32602,
message: `Empty arguments are not allowed — this tool is write-once, not a discovery probe. To inspect the schema, use the tools/list MCP method. To signal that no action is needed, call \`noop\` with a \`message\`.`,
};
}
Comment on lines 624 to +632
throw {
code: -32602,
message: generateEnhancedErrorMessage(missing, name, tool.inputSchema),
Expand Down Expand Up @@ -751,6 +758,15 @@ async function handleMessage(server, req, defaultHandler) {

const missing = validateRequiredFields(args, tool.inputSchema);
if (missing.length) {
const hasRequiredFields = tool.inputSchema && Array.isArray(tool.inputSchema.required) && tool.inputSchema.required.length > 0;
if (hasRequiredFields && Object.keys(args).length === 0) {
server.replyError(
id,
-32602,
`Empty arguments are not allowed — this tool is write-once, not a discovery probe. To inspect the schema, use the tools/list MCP method. To signal that no action is needed, call \`noop\` with a \`message\`.`
);
return;
}
server.replyError(id, -32602, generateEnhancedErrorMessage(missing, name, tool.inputSchema));
return;
}
Expand Down
24 changes: 22 additions & 2 deletions actions/setup/js/mcp_server_core.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ describe("mcp_server_core.cjs", () => {
expect(results[0].error.message).toContain("Tool not found");
});

it("should return error for missing required fields", async () => {
it("should return error for empty arguments object (probe detection)", async () => {
const { handleMessage } = await import("./mcp_server_core.cjs");

await handleMessage(server, {
Expand All @@ -213,7 +213,27 @@ describe("mcp_server_core.cjs", () => {
method: "tools/call",
params: {
name: "test_tool",
arguments: {}, // missing required 'input'
arguments: {}, // completely empty — probe attempt
},
});

expect(results).toHaveLength(1);
expect(results[0].error.code).toBe(-32602);
expect(results[0].error.message).toContain("write-once, not a discovery probe");
expect(results[0].error.message).toContain("tools/list");
expect(results[0].error.message).toContain("noop");
});

it("should return enhanced error for partially-supplied but invalid required fields", async () => {
const { handleMessage } = await import("./mcp_server_core.cjs");

await handleMessage(server, {
jsonrpc: "2.0",
id: 1,
method: "tools/call",
params: {
name: "test_tool",
arguments: { input: "" }, // present but empty string — not a probe
},
});

Expand Down
22 changes: 17 additions & 5 deletions actions/setup/js/safe_outputs_mcp_error_messages.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ describe("Safe Outputs MCP Error Message Validation", () => {
method: "tools/call",
params: {
name: "test_tool",
arguments: {}, // missing 'title'
arguments: { title: "" }, // empty string triggers missing-field error (not probe detection)
},
});

Expand Down Expand Up @@ -221,7 +221,7 @@ describe("Safe Outputs MCP Error Message Validation", () => {
method: "tools/call",
params: {
name: "test_tool",
arguments: {}, // missing both fields
arguments: { title: "", body: "" }, // empty strings trigger missing-field errors (not probe detection)
},
});

Expand Down Expand Up @@ -277,13 +277,19 @@ describe("Safe Outputs MCP Error Message Validation", () => {
handler: args => ({ content: [{ type: "text", text: "ok" }] }),
});

// Use empty-string values to trigger field-level errors (not probe detection)
const emptyArgs = testTool.required.reduce((acc, field) => {
acc[field] = "";
return acc;
}, {});

await handleMessage(server, {
jsonrpc: "2.0",
id: 1,
method: "tools/call",
params: {
name: testTool.name,
arguments: {},
arguments: emptyArgs,
},
});

Expand Down Expand Up @@ -333,13 +339,19 @@ describe("Safe Outputs MCP Error Message Validation", () => {

registerTool(server, toolWithHandler);

// Use empty-string values to trigger field-level errors (not probe detection)
const emptyArgs = tool.inputSchema.required.reduce((acc, field) => {
acc[field] = "";
return acc;
}, {});

await handleMessage(server, {
jsonrpc: "2.0",
id: 1,
method: "tools/call",
params: {
name: tool.name,
arguments: {}, // Empty arguments to trigger missing field errors
arguments: emptyArgs, // Empty strings to trigger missing field errors
},
});

Expand Down Expand Up @@ -404,7 +416,7 @@ describe("Safe Outputs MCP Error Message Validation", () => {
method: "tools/call",
params: {
name: "create_issue",
arguments: {}, // Missing required fields
arguments: { title: "", body: "" }, // Empty strings to trigger missing-field errors (not probe detection)
},
});

Expand Down
10 changes: 5 additions & 5 deletions actions/setup/js/safe_outputs_tools.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
[
{
"name": "create_issue",
"description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. This is a write-once declaration for a real intended issue, not a sandbox or probe: do not call it with placeholder titles/bodies or auth experiments. If you are not ready to open the real issue, use noop or report_incomplete instead. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead.",
"description": "WRITE-ONCE: do NOT call this tool with empty or placeholder arguments to probe or discover its schema — required fields (title, body) are listed in this schema; if you are not ready to open the real issue, call `noop` instead. Creates a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead.",
"inputSchema": {
"type": "object",
"required": ["title", "body"],
"properties": {
"title": {
"type": "string",
"description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive."
"description": "Concise issue title summarizing the bug, feature, or task. Must be the final intended title — not a placeholder or test value. The title appears as the main heading, so keep it brief and descriptive."
},
"body": {
"type": "string",
"description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate."
"description": "Detailed issue description in Markdown. Must be the final intended body — not a placeholder or test value. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate."
},
"labels": {
"type": "array",
Expand Down Expand Up @@ -245,14 +245,14 @@
},
{
"name": "add_comment",
"description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. This is a write-once declaration for a real intended comment, not a sandbox or probe: do not call it with placeholder bodies or auth experiments. If you are not ready to post the real comment, use noop or report_incomplete instead. For creating new items, use create_issue, create_discussion, or create_pull_request instead.",
"description": "WRITE-ONCE: do NOT call this tool with empty or placeholder arguments to probe or discover its schema — the required `body` field is listed in this schema; if you are not ready to post a real comment, call `noop` instead. Adds a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead.",
"inputSchema": {
"type": "object",
"required": ["body"],
"properties": {
"body": {
"type": "string",
"description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation."
"description": "The comment text in Markdown format. Must be the final intended comment — not a placeholder or test value. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation."
},
"item_number": {
"type": ["number", "string"],
Expand Down
10 changes: 5 additions & 5 deletions pkg/workflow/js/safe_outputs_tools.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[
{
"name": "create_issue",
"description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. This is a write-once declaration for a real intended issue, not a sandbox or probe: do not call it with placeholder titles/bodies or auth experiments. If you are not ready to open the real issue, use noop or report_incomplete instead. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead.",
"description": "WRITE-ONCE: do NOT call this tool with empty or placeholder arguments to probe or discover its schema — required fields (title, body) are listed in this schema; if you are not ready to open the real issue, call `noop` instead. Creates a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead.",
"inputSchema": {
"type": "object",
"required": [
Expand All @@ -11,11 +11,11 @@
"properties": {
"title": {
"type": "string",
"description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive."
"description": "Concise issue title summarizing the bug, feature, or task. Must be the final intended title — not a placeholder or test value. The title appears as the main heading, so keep it brief and descriptive."
},
"body": {
"type": "string",
"description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate."
"description": "Detailed issue description in Markdown. Must be the final intended body — not a placeholder or test value. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate."
},
"labels": {
"type": "array",
Expand Down Expand Up @@ -280,7 +280,7 @@
},
{
"name": "add_comment",
"description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. This is a write-once declaration for a real intended comment, not a sandbox or probe: do not call it with placeholder bodies or auth experiments. If you are not ready to post the real comment, use noop or report_incomplete instead. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission.",
"description": "WRITE-ONCE: do NOT call this tool with empty or placeholder arguments to probe or discover its schema — the required `body` field is listed in this schema; if you are not ready to post a real comment, call `noop` instead. Adds a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission.",
"inputSchema": {
"type": "object",
"required": [
Expand All @@ -289,7 +289,7 @@
"properties": {
"body": {
"type": "string",
"description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated."
"description": "The comment text in Markdown format. Must be the final intended comment — not a placeholder or test value. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated."
},
"item_number": {
"type": [
Expand Down
Loading