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 packages/codex/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "glean-experimental-codex",
"version": "0.2.11",
"version": "0.2.18",
"description": "Glean Codex plugin for discovering skills and running tools.",
"author": {
"name": "Glean"
Expand Down
16 changes: 10 additions & 6 deletions packages/codex/dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31152,14 +31152,18 @@ async function handleRunTool(remoteClient, mcpServer, skillsBaseDir, args) {
}
throw err;
}
const remoteArgs = {
return callRemoteTool(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add this file to gitignore.

remoteClient,
"run_tool",
buildRemoteArgs(serverId, toolName, resolvedArgs)
);
}
function buildRemoteArgs(serverId, toolName, resolvedArgs) {
return {
server_id: serverId,
tool_name: toolName
tool_name: toolName,
arguments: resolvedArgs
};
if (Object.keys(resolvedArgs).length > 0) {
remoteArgs.arguments = resolvedArgs;
}
return callRemoteTool(remoteClient, "run_tool", remoteArgs);
}

// src/url-config-store.ts
Expand Down
2 changes: 1 addition & 1 deletion plugins/glean/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "glean-experimental",
"version": "0.2.17",
"version": "0.2.18",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#suggestion, in a separate PR, can we add a check to make sure that the 3 versions remain in sync.

"description": "Glean plugin for discovering skills and running tools.",
"author": {
"name": "Glean"
Expand Down
2 changes: 1 addition & 1 deletion plugins/glean/.cursor-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "glean-experimental-cursor",
"displayName": "Glean Cursor",
"version": "0.2.15",
"version": "0.2.18",
"description": "Search and act across your company's apps — Jira, Slack, Salesforce, Google Workspace, and more — without leaving Cursor.",
"author": {
"name": "Glean"
Expand Down
16 changes: 10 additions & 6 deletions plugins/glean/dist/index.js

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

27 changes: 22 additions & 5 deletions src/tools/run-tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,29 @@ export async function handleRunTool(
throw err;
}

const remoteArgs: Record<string, unknown> = {
return callRemoteTool(
remoteClient,
"run_tool",
buildRemoteArgs(serverId, toolName, resolvedArgs),
);
}

/**
* Assemble the payload for the backend `run_tool` meta-tool. `arguments` is
* ALWAYS included, even when empty: the downstream MCP `tools/call` validates
* `params.arguments` as an object, and an absent field serializes to `null`,
* which strict downstream servers reject ("Expected: object, given: null").
* Sending an explicit `{}` for no-argument tools matches what the MCP SDK
* does for direct tool calls.
*/
export function buildRemoteArgs(
serverId: string,
toolName: string,
resolvedArgs: Record<string, unknown>,
): Record<string, unknown> {
return {
server_id: serverId,
tool_name: toolName,
arguments: resolvedArgs,
};
if (Object.keys(resolvedArgs).length > 0) {
remoteArgs.arguments = resolvedArgs;
}
return callRemoteTool(remoteClient, "run_tool", remoteArgs);
}
29 changes: 28 additions & 1 deletion tests/run-tool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import { describe, it, expect, beforeEach, afterEach } from "vitest";
import fs from "node:fs/promises";
import path from "node:path";
import os from "node:os";
import { resolveFileArgs, FileArgsError } from "../src/tools/run-tool.js";
import {
resolveFileArgs,
buildRemoteArgs,
FileArgsError,
} from "../src/tools/run-tool.js";

describe("resolveFileArgs", () => {
let tmpDir: string;
Expand Down Expand Up @@ -132,3 +136,26 @@ describe("resolveFileArgs", () => {
).rejects.toThrow(/conflicts/);
});
});

describe("buildRemoteArgs", () => {
// Regression: a no-argument downstream call (e.g. slack_read_user_profile)
// must forward `arguments: {}`, not omit the key. An absent field serializes
// to null downstream, which strict MCP servers reject.
it("always includes arguments, even when empty", () => {
expect(buildRemoteArgs("srv", "tool", {})).toEqual({
server_id: "srv",
tool_name: "tool",
arguments: {},
});
});

it("forwards populated arguments unchanged", () => {
expect(
buildRemoteArgs("srv", "tool", { response_format: "detailed" }),
).toEqual({
server_id: "srv",
tool_name: "tool",
arguments: { response_format: "detailed" },
});
});
});
Loading