import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type {
CallToolResult,
ToolAnnotations,
} from "@modelcontextprotocol/sdk/types.js";
import state from "@/config.js";
import { getGlobalVaultManager } from "@/utils/getVaultManager.js";
import {
type ObsidianPropertyParams,
obsidianPropertyParamsSchema,
} from "./params.js";
export const name = "write_property";
export const annotations: ToolAnnotations = {
title: "Write Obsidian Property",
openWorldHint: true,
};
export const description = `
Description: Adds or updates properties within the frontmatter section at the top of a specified Obsidian markdown file. This tool is primarily used to apply metadata generated by the generate_obsidian_properties tool to an actual file.
Parameters:
- filePath (string, required): The path to the target markdown file to which properties will be added or updated.
Example: "my-first-post.md"
- properties (object, required): A JSON object containing the key-value pairs to be written to the file's frontmatter. If a property with the same key already exists in the file, it will be overwritten with the new value.
Example:
JSON
{
"title": "Optimizing I/O Handling in a Serverless Environment",
"date": "2025-04-03",
"tags": ["serverless", "optimization"],
"summary": "A case study on optimizing I/O in a serverless environment by benchmarking Promise.all and Workers.",
"completed": true
}
Return Value:
Upon successful execution, it returns a JSON object containing the status, a confirmation message, and the property object that was applied to the file.
Example:
JSON
{
"status": "success",
"message": "Successfully updated properties for my-first-post.md",
"properties": {
"title": "Optimizing I/O Handling in a Serverless Environment",
"date": "2025-04-03",
"tags": ["serverless", "optimization"],
"summary": "A case study on optimizing I/O in a serverless environment by benchmarking Promise.all and Workers.",
"completed": true
}
}
Dependencies & Requirements:
- Input Data: The properties parameter should typically be the JSON object output from the generate_obsidian_properties tool.
- Environment Setup: The absolute path to the user's Obsidian Vault must be correctly set as an environment variable.
`;
export const register = (mcpServer: McpServer) => {
mcpServer.registerTool(
name,
{
title: annotations.title || name,
description: description,
inputSchema: obsidianPropertyParamsSchema.shape,
annotations: annotations,
},
execute,
);
};
export const execute = async (
params: ObsidianPropertyParams,
): Promise<CallToolResult> => {
const response: CallToolResult = { content: [], isError: false };
const vaultDirPath = state.vaultPath;
if (!vaultDirPath) {
response.content.push({
type: "text",
text: JSON.stringify(
{
error: "VAULT_DIR_PATH is not set. Cannot write properties to file.",
suggestion:
"Please set the VAULT_DIR_PATH environment variable to your Obsidian vault path.",
},
null,
2,
),
});
response.isError = true;
return response;
}
let vaultManager = null;
try {
vaultManager = getGlobalVaultManager();
} catch (e) {
return {
isError: true,
content: [
{ type: "text", text: JSON.stringify({ error: (e as Error).message }) },
],
};
}
try {
await vaultManager.writeDocument(params.filePath, params.properties);
if (params.quiet) {
return {
isError: false,
content: [
{ type: "text", text: JSON.stringify({ status: "success" }) },
],
};
}
return {
isError: false,
content: [
{
type: "text",
text: JSON.stringify(
{
status: "success",
message: `Successfully updated properties for ${params.filePath}`,
properties: params.properties,
},
null,
2,
),
},
],
};
} catch (error) {
response.content.push({
type: "text",
text: JSON.stringify(
{
error: (error as Error).message || "An unknown error occurred.",
},
null,
2,
),
});
response.isError = true;
return response;
}
};
export default {
name,
description,
annotations,
inputSchema: obsidianPropertyParamsSchema.shape,
execute,
register,
};