Skip to main content
Glama

lookup_record

Search DEVONthink records by attributes like filename, path, URL, tags, comment, or content hash to locate specific documents and information.

Instructions

Look up records in DEVONthink by a specific attribute.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
lookupTypeYesThe attribute to look up by
valueYesThe value to match against the specified attribute
tagsNoAdditional tags to match (only used when lookupType is 'tags')
matchAnyTagNoWhen true, match records that have ANY of the specified tags; when false (default), match records that have ALL tags
databaseNameNoLimit lookup to this database; uses all open databases if omitted
limitNoMaximum number of results to return

Implementation Reference

  • The 'run' function handles the logic for the 'lookup_record' tool, executing a JXA script to interact with DEVONthink and process the search based on the provided attributes.
      run: async (args, executor) => {
        const { lookupType, value, tags, matchAnyTag, databaseName, limit } = args;
    
        // Merge value into tags list when lookupType is 'tags'
        const tagList = lookupType === "tags" ? [value, ...(tags ?? [])] : (tags ?? []);
    
        const script = `
          ${JXA_APP}
          var lookupType = ${jxaLiteral(lookupType)};
          var value = ${jxaLiteral(value)};
          var tagList = ${jxaLiteral(tagList)};
          var matchAnyTag = ${jxaLiteral(matchAnyTag ?? false)};
          var dbName = ${jxaLiteral(databaseName ?? null)};
          var limit = ${jxaLiteral(limit ?? null)};
    
          ${JXA_RESOLVE_DB}
    
          var results = [];
    
          if (lookupType === "filename") {
            // Search by name across the scoped database or all databases
            var searchQuery = "name:" + value;
            var searchOpts = {};
            if (db) searchOpts["in"] = db.root();
            results = app.search(searchQuery, searchOpts);
            // Further filter to exact filename matches
            results = results.filter(function(r) {
              return r.name() === value;
            });
    
          } else if (lookupType === "path") {
            // Look up by filesystem path
            if (db) {
              var rec = app.getRecordAt(value, {in: db});
              if (rec && rec.uuid()) results = [rec];
            } else {
              // Try each open database
              var dbs = app.databases();
              for (var i = 0; i < dbs.length; i++) {
                try {
                  var r = app.getRecordAt(value, {in: dbs[i]});
                  if (r && r.uuid()) { results.push(r); break; }
                } catch(e) {}
              }
            }
    
          } else if (lookupType === "url") {
            // Search by URL
            var urlResults = app.lookupRecordsWithURL(value);
            if (urlResults) results = urlResults;
    
          } else if (lookupType === "tags") {
            // Look up by tags using DEVONthink's tag lookup
            if (matchAnyTag) {
              // Union: results that match any of the tags
              var seen = {};
              for (var t = 0; t < tagList.length; t++) {
                var tagResults = app.lookupRecordsWithTags([tagList[t]]);
                if (tagResults) {
                  for (var j = 0; j < tagResults.length; j++) {
                    var u = tagResults[j].uuid();
                    if (!seen[u]) {
                      seen[u] = true;
                      results.push(tagResults[j]);
                    }
                  }
                }
              }
            } else {
              // Intersection: results that match all of the tags
              results = app.lookupRecordsWithTags(tagList) || [];
            }
    
          } else if (lookupType === "comment") {
            // Search by comment using DEVONthink search
            var commentQuery = "comment:" + value;
            var commentOpts = {};
            if (db) commentOpts["in"] = db.root();
            results = app.search(commentQuery, commentOpts);
            // Filter to actual comment matches
            results = results.filter(function(r) {
              var c = r.comment();
              return c && c.indexOf(value) !== -1;
            });
    
          } else if (lookupType === "contentHash") {
            // Look up by content hash (MD5/SHA)
            var hashResults = app.lookupRecordsWithContentHash(value);
            if (hashResults) results = hashResults;
          }
    
          // Filter by database if specified and not already scoped
          if (dbName && lookupType !== "filename" && lookupType !== "path" && lookupType !== "comment") {
            results = results.filter(function(r) {
              try { return r.database().name() === dbName; } catch(e) { return false; }
            });
          }
    
          // Apply limit
          if (limit && limit > 0 && results.length > limit) {
            results = results.slice(0, limit);
          }
    
          var summaries = results.map(function(record) {
            return ${JXA_RECORD_SUMMARY};
          });
    
          JSON.stringify(summaries);
        `;
    
        const result = executor.run(script);
        return JSON.parse(result.stdout);
      },
    });
  • The 'schema' property defines the input parameters for the 'lookup_record' tool, including 'lookupType', 'value', 'tags', 'matchAnyTag', 'databaseName', and 'limit', using Zod for validation.
    schema: z.object({
      lookupType: z
        .enum(LOOKUP_TYPE_VALUES)
        .describe("The attribute to look up by"),
      value: z
        .string()
        .describe("The value to match against the specified attribute"),
      tags: z
        .array(z.string())
        .optional()
        .describe("Additional tags to match (only used when lookupType is 'tags')"),
      matchAnyTag: z
        .boolean()
        .optional()
        .describe(
          "When true, match records that have ANY of the specified tags; " +
            "when false (default), match records that have ALL tags"
        ),
      databaseName: z
        .string()
        .optional()
        .describe("Limit lookup to this database; uses all open databases if omitted"),
      limit: z
        .number()
        .int()
        .positive()
        .optional()
        .describe("Maximum number of results to return"),
    }),
  • The 'lookupRecordTool' object is created using 'defineTool', registering the tool with the name 'lookup_record'.
    export const lookupRecordTool = defineTool({
      name: "lookup_record",
      description: "Look up records in DEVONthink by a specific attribute.",
      schema: z.object({
        lookupType: z
          .enum(LOOKUP_TYPE_VALUES)
          .describe("The attribute to look up by"),
        value: z
          .string()
          .describe("The value to match against the specified attribute"),
        tags: z
          .array(z.string())
          .optional()
          .describe("Additional tags to match (only used when lookupType is 'tags')"),
        matchAnyTag: z
          .boolean()
          .optional()
          .describe(
            "When true, match records that have ANY of the specified tags; " +
              "when false (default), match records that have ALL tags"
          ),
        databaseName: z
          .string()
          .optional()
          .describe("Limit lookup to this database; uses all open databases if omitted"),
        limit: z
          .number()
          .int()
          .positive()
          .optional()
          .describe("Maximum number of results to return"),
      }),
      run: async (args, executor) => {
        const { lookupType, value, tags, matchAnyTag, databaseName, limit } = args;
    
        // Merge value into tags list when lookupType is 'tags'
        const tagList = lookupType === "tags" ? [value, ...(tags ?? [])] : (tags ?? []);
    
        const script = `
          ${JXA_APP}
          var lookupType = ${jxaLiteral(lookupType)};
          var value = ${jxaLiteral(value)};
          var tagList = ${jxaLiteral(tagList)};
          var matchAnyTag = ${jxaLiteral(matchAnyTag ?? false)};
          var dbName = ${jxaLiteral(databaseName ?? null)};
          var limit = ${jxaLiteral(limit ?? null)};
    
          ${JXA_RESOLVE_DB}
    
          var results = [];
    
          if (lookupType === "filename") {
            // Search by name across the scoped database or all databases
            var searchQuery = "name:" + value;
            var searchOpts = {};
            if (db) searchOpts["in"] = db.root();
            results = app.search(searchQuery, searchOpts);
            // Further filter to exact filename matches
            results = results.filter(function(r) {
              return r.name() === value;
            });
    
          } else if (lookupType === "path") {
            // Look up by filesystem path
            if (db) {
              var rec = app.getRecordAt(value, {in: db});
              if (rec && rec.uuid()) results = [rec];
            } else {
              // Try each open database
              var dbs = app.databases();
              for (var i = 0; i < dbs.length; i++) {
                try {
                  var r = app.getRecordAt(value, {in: dbs[i]});
                  if (r && r.uuid()) { results.push(r); break; }
                } catch(e) {}
              }
            }
    
          } else if (lookupType === "url") {
            // Search by URL
            var urlResults = app.lookupRecordsWithURL(value);
            if (urlResults) results = urlResults;
    
          } else if (lookupType === "tags") {
            // Look up by tags using DEVONthink's tag lookup
            if (matchAnyTag) {
              // Union: results that match any of the tags
              var seen = {};
              for (var t = 0; t < tagList.length; t++) {
                var tagResults = app.lookupRecordsWithTags([tagList[t]]);
                if (tagResults) {
                  for (var j = 0; j < tagResults.length; j++) {
                    var u = tagResults[j].uuid();
                    if (!seen[u]) {
                      seen[u] = true;
                      results.push(tagResults[j]);
                    }
                  }
                }
              }
            } else {
              // Intersection: results that match all of the tags
              results = app.lookupRecordsWithTags(tagList) || [];
            }
    
          } else if (lookupType === "comment") {
            // Search by comment using DEVONthink search
            var commentQuery = "comment:" + value;
            var commentOpts = {};
            if (db) commentOpts["in"] = db.root();
            results = app.search(commentQuery, commentOpts);
            // Filter to actual comment matches
            results = results.filter(function(r) {
              var c = r.comment();
              return c && c.indexOf(value) !== -1;
            });
    
          } else if (lookupType === "contentHash") {
            // Look up by content hash (MD5/SHA)
            var hashResults = app.lookupRecordsWithContentHash(value);
            if (hashResults) results = hashResults;
          }
    
          // Filter by database if specified and not already scoped
          if (dbName && lookupType !== "filename" && lookupType !== "path" && lookupType !== "comment") {
            results = results.filter(function(r) {
              try { return r.database().name() === dbName; } catch(e) { return false; }
            });
          }
    
          // Apply limit
          if (limit && limit > 0 && results.length > limit) {
            results = results.slice(0, limit);
          }
    
          var summaries = results.map(function(record) {
            return ${JXA_RECORD_SUMMARY};
          });
    
          JSON.stringify(summaries);
        `;
    
        const result = executor.run(script);
        return JSON.parse(result.stdout);
      },
    });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mnott/Devon'

If you have feedback or need assistance with the MCP directory API, please join our Discord server