get_url_keyword
Extract keywords from a long URL to identify its unique short link identifier. Optionally return a single result for precise URL management within the YOURLS-MCP server.
Instructions
Get the keyword(s) for a long URL
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| exactly_one | No | Whether to return only one result (default: false) | |
| url | Yes | The URL to find keywords for |
Implementation Reference
- src/tools/getUrlKeyword.js:31-76 (handler)The execute handler function that implements the core logic of the 'get_url_keyword' tool, processing input, calling the YOURLS client, and formatting the response.execute: async ({ url, exactly_one = true }) => { try { // Normalize boolean parameter if it's passed as a string if (typeof exactly_one === 'string') { exactly_one = exactly_one.toLowerCase() === 'true'; } // Use the getUrlKeyword method with fallback enabled const result = await yourlsClient.getUrlKeyword(url, exactly_one, true); if (result.status === 'success' || result.message === 'success: found') { const responseData = { url: url }; // Add keyword information based on response format if (exactly_one && result.keyword) { responseData.keyword = result.keyword; if (result.shorturl) responseData.shorturl = result.shorturl; if (result.title) responseData.title = result.title; } else if (!exactly_one && result.keywords) { responseData.keywords = result.keywords; } // Add message if available if (result.simple) responseData.message = result.simple; // Add fallback information if applicable if (result.fallback_used) { responseData.fallback_used = true; if (result.fallback_limitations) { responseData.fallback_limitations = result.fallback_limitations; } } return createMcpResponse(true, responseData); } else { throw new Error(result.message || 'Unknown error'); } } catch (error) { return createMcpResponse(false, { message: error.message, url: url }); } }
- src/tools/getUrlKeyword.js:17-30 (schema)Input schema definition for the 'get_url_keyword' tool parameters.inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'The long URL to look up' }, exactly_one: { type: 'boolean', description: 'If false, returns all keywords for this URL (default: true)' } }, required: ['url'] },
- src/index.js:211-217 (registration)Registration of the 'get_url_keyword' tool with the MCP server, including Zod schema conversion and binding the execute handler.getUrlKeywordTool.name, getUrlKeywordTool.description, { url: z.string().describe('The URL to find keywords for'), exactly_one: z.boolean().optional().describe('Whether to return only one result (default: false)') }, getUrlKeywordTool.execute
- src/api.js:744-775 (helper)The YourlsClient.getUrlKeyword method called by the tool handler, with plugin detection and fallback logic.async getUrlKeyword(url, exactlyOne = true, useNativeFallback = true) { const params = { url }; if (!exactlyOne) { params.exactly_one = 'false'; } try { // First check if the plugin is available const isAvailable = await isPluginAvailable(this, 'edit_url', 'geturl', { url: 'https://example.com' }); if (isAvailable) { return this.request('geturl', params); } else if (useNativeFallback) { // Use our fallback implementation return this._getUrlKeywordFallback(url, exactlyOne); } else { throw new Error('The geturl action is not available. Please install the API Edit URL plugin.'); } } catch (error) { // If the error is not about a missing plugin, re-throw it if (!isPluginMissingError(error)) { throw error; } // If we're here, the plugin is missing and we need to use the fallback if (useNativeFallback) { return this._getUrlKeywordFallback(url, exactlyOne); } else { throw new Error('The geturl action is not available. Please install the API Edit URL plugin.'); } }
- src/api.js:786-866 (helper)Fallback implementation _getUrlKeywordFallback used when the API Edit URL plugin is unavailable, searches via stats API.async _getUrlKeywordFallback(url, exactlyOne) { try { // Safety limit to prevent performance issues const MAX_RESULTS = 1000; // We can try to use the stats action with a filter const listResult = await this.request('stats', { limit: MAX_RESULTS, filter: 'url', search: encodeURIComponent(url) }); // Process the results to match the geturl plugin's output format const keywords = []; let urlExists = false; if (listResult.links) { // Iterate through the results and find exact URL matches for (const [keyword, data] of Object.entries(listResult.links)) { if (data.url === url) { urlExists = true; keywords.push({ keyword: keyword, shorturl: `${this.api_url.replace('yourls-api.php', '')}${keyword}`, title: data.title, url: data.url, date: data.timestamp, ip: data.ip, clicks: data.clicks }); // If we only want one result and we found it, break if (exactlyOne) { break; } } } } // Format the response similarly to what the geturl plugin would return if (urlExists) { if (exactlyOne && keywords.length > 0) { // Return just the first match return { status: 'success', keyword: keywords[0].keyword, url: keywords[0].url, title: keywords[0].title, shorturl: keywords[0].shorturl, message: 'success', ...createFallbackInfo('Search limited to latest URLs', false, 'API Edit URL') }; } else { // Return all matches return { status: 'success', keywords: keywords, url: url, message: 'success', ...createFallbackInfo('Search limited to latest URLs', false, 'API Edit URL') }; } } else { // No matches found return { status: 'fail', message: 'URL not found', ...createFallbackInfo('Search limited to latest URLs', false, 'API Edit URL') }; } } catch (error) { console.error('Get URL keyword fallback error:', error.message); // In case of fallback failure, return a safe default return { status: 'fail', message: 'Error looking up URL: ' + error.message, ...createFallbackInfo('Error during fallback search', true, 'API Edit URL') }; } }