diff --git a/src/tools/item-get.ts b/src/tools/item-get.ts index 502138c..6c4e7fc 100644 --- a/src/tools/item-get.ts +++ b/src/tools/item-get.ts @@ -40,7 +40,7 @@ function summarizeField( export function registerItemGet(server: McpServer): void { server.tool( "item_get", - "Retrieve a full 1Password item — title, category, tags, notes, and all fields (id, title, type, section). Concealed field values are hidden unless reveal is true. Accepts a secret reference (op://vault/item/field) or vault ID + item ID.", + "Retrieve a full 1Password item — title, category, tags, notes, and all fields (id, title, type, section). Concealed field values are hidden unless reveal is true. Accepts a secret reference (op://vault/item/field) or vault ID + item ID. Revealing a secret puts it in the model context/transcript — to USE a secret in a command or API call, prefer op_run with op:// references instead.", { secretReference: z .string() @@ -60,7 +60,7 @@ export function registerItemGet(server: McpServer): void { .boolean() .optional() .describe( - "If true, include concealed field values in plaintext. Defaults to false for security.", + "If true, include concealed field values in plaintext — this puts the secret in the model context/transcript. Defaults to false; prefer op_run to use a secret without revealing it.", ), }, async ({ secretReference, vaultId, itemId, reveal }) => { diff --git a/src/tools/password-read.ts b/src/tools/password-read.ts index 51c13b2..718a8bd 100644 --- a/src/tools/password-read.ts +++ b/src/tools/password-read.ts @@ -11,7 +11,7 @@ import { jsonResult, errorResult } from "../utils.js"; export function registerPasswordRead(server: McpServer): void { server.tool( "password_read", - "Retrieve a secret from 1Password using either a secret reference (op://vault/item/field) or vault ID + item ID. Supports field selection and optional value reveal.", + "Retrieve a secret from 1Password using either a secret reference (op://vault/item/field) or vault ID + item ID. Supports field selection and optional value reveal (defaults to metadata-only). Revealing a secret puts it in the model context/transcript — to USE a secret in a command or API call, prefer op_run with op:// references instead.", { secretReference: z .string() @@ -35,7 +35,7 @@ export function registerPasswordRead(server: McpServer): void { .boolean() .optional() .describe( - "If false, return metadata only without the secret value. Defaults to true.", + "If true, include the secret value in plaintext in the response — this puts the secret in the model context/transcript. Defaults to false; prefer op_run to use a secret without revealing it.", ), }, async ({ secretReference, vaultId, itemId, field, reveal }) => { @@ -48,6 +48,7 @@ export function registerPasswordRead(server: McpServer): void { reveal, }); const client = await getClient(); + const shouldReveal = reveal === true; if (secretReference) { if (!client?.secrets?.resolve) { @@ -56,7 +57,7 @@ export function registerPasswordRead(server: McpServer): void { ); } const value = await client.secrets.resolve(secretReference); - if (reveal === false) { + if (!shouldReveal) { return jsonResult({ resolved: true }); } return jsonResult({ value }); @@ -88,7 +89,7 @@ export function registerPasswordRead(server: McpServer): void { throw new Error(`Field '${desiredField}' not found on item.`); } - if (reveal === false) { + if (!shouldReveal) { return jsonResult({ id: item.id, title: item.title, diff --git a/tests/tools.test.ts b/tests/tools.test.ts index 3d9e60b..c24aab0 100644 --- a/tests/tools.test.ts +++ b/tests/tools.test.ts @@ -211,7 +211,7 @@ describe("MCP Tools", () => { }); describe("password_read", () => { - it("resolves a secret reference", async () => { + it("resolves a secret reference and returns the value when reveal is true", async () => { mockedGetClient.mockResolvedValue({ secrets: { resolve: vi.fn().mockResolvedValue("my-secret-value"), @@ -221,12 +221,30 @@ describe("MCP Tools", () => { const handler = registeredTools.get("password_read")!.handler; const result = await handler({ secretReference: "op://vault/item/password", + reveal: true, }); const data = JSON.parse(result.content[0].text); expect(data.value).toBe("my-secret-value"); }); + it("returns metadata only by default (reveal omitted)", async () => { + mockedGetClient.mockResolvedValue({ + secrets: { + resolve: vi.fn().mockResolvedValue("my-secret-value"), + }, + } as any); + + const handler = registeredTools.get("password_read")!.handler; + const result = await handler({ + secretReference: "op://vault/item/password", + }); + const data = JSON.parse(result.content[0].text); + + expect(data.resolved).toBe(true); + expect(data.value).toBeUndefined(); + }); + it("returns metadata only when reveal is false", async () => { mockedGetClient.mockResolvedValue({ secrets: {