Skip to content
Open
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
4 changes: 2 additions & 2 deletions src/tools/item-get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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 }) => {
Expand Down
9 changes: 5 additions & 4 deletions src/tools/password-read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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 }) => {
Expand All @@ -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) {
Expand All @@ -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 });
Expand Down Expand Up @@ -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,
Expand Down
20 changes: 19 additions & 1 deletion tests/tools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand All @@ -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: {
Expand Down
Loading