Skip to main content
Glama

MCP Specification Server

by MCPJam
23
6
  • Apple
uri-templates.js•14.9 kB
(function (global, factory) { if (typeof define === 'function' && define.amd) { define('uri-templates', [], factory); } else if (typeof module !== 'undefined' && module.exports){ module.exports = factory(); } else { global.UriTemplate = factory(); } })(this, function () { var uriTemplateGlobalModifiers = { "+": true, "#": true, ".": true, "/": true, ";": true, "?": true, "&": true }; var uriTemplateSuffices = { "*": true }; var urlEscapedChars = /[:/&?#]/; function notReallyPercentEncode(string) { return encodeURI(string).replace(/%25[0-9][0-9]/g, function (doubleEncoded) { return "%" + doubleEncoded.substring(3); }); } function isPercentEncoded(string) { string = string.replace(/%../g, ''); return encodeURIComponent(string) === string; } function uriTemplateSubstitution(spec) { var modifier = ""; if (uriTemplateGlobalModifiers[spec.charAt(0)]) { modifier = spec.charAt(0); spec = spec.substring(1); } var separator = ""; var prefix = ""; var shouldEscape = true; var showVariables = false; var trimEmptyString = false; if (modifier == '+') { shouldEscape = false; } else if (modifier == ".") { prefix = "."; separator = "."; } else if (modifier == "/") { prefix = "/"; separator = "/"; } else if (modifier == '#') { prefix = "#"; shouldEscape = false; } else if (modifier == ';') { prefix = ";"; separator = ";", showVariables = true; trimEmptyString = true; } else if (modifier == '?') { prefix = "?"; separator = "&", showVariables = true; } else if (modifier == '&') { prefix = "&"; separator = "&", showVariables = true; } var varNames = []; var varList = spec.split(","); var varSpecs = []; var varSpecMap = {}; for (var i = 0; i < varList.length; i++) { var varName = varList[i]; var truncate = null; if (varName.indexOf(":") != -1) { var parts = varName.split(":"); varName = parts[0]; truncate = parseInt(parts[1]); } var suffices = {}; while (uriTemplateSuffices[varName.charAt(varName.length - 1)]) { suffices[varName.charAt(varName.length - 1)] = true; varName = varName.substring(0, varName.length - 1); } var varSpec = { truncate: truncate, name: varName, suffices: suffices }; varSpecs.push(varSpec); varSpecMap[varName] = varSpec; varNames.push(varName); } var subFunction = function (valueFunction) { var result = ""; var startIndex = 0; for (var i = 0; i < varSpecs.length; i++) { var varSpec = varSpecs[i]; var value = valueFunction(varSpec.name); if (value == null || (Array.isArray(value) && value.length == 0) || (typeof value == 'object' && Object.keys(value).length == 0)) { startIndex++; continue; } if (i == startIndex) { result += prefix; } else { result += (separator || ","); } if (Array.isArray(value)) { if (showVariables) { result += varSpec.name + "="; } for (var j = 0; j < value.length; j++) { if (j > 0) { result += varSpec.suffices['*'] ? (separator || ",") : ","; if (varSpec.suffices['*'] && showVariables) { result += varSpec.name + "="; } } result += shouldEscape ? encodeURIComponent(value[j]).replace(/!/g, "%21") : notReallyPercentEncode(value[j]); } } else if (typeof value == "object") { if (showVariables && !varSpec.suffices['*']) { result += varSpec.name + "="; } var first = true; for (var key in value) { if (!first) { result += varSpec.suffices['*'] ? (separator || ",") : ","; } first = false; result += shouldEscape ? encodeURIComponent(key).replace(/!/g, "%21") : notReallyPercentEncode(key); result += varSpec.suffices['*'] ? '=' : ","; result += shouldEscape ? encodeURIComponent(value[key]).replace(/!/g, "%21") : notReallyPercentEncode(value[key]); } } else { if (showVariables) { result += varSpec.name; if (!trimEmptyString || value != "") { result += "="; } } if (varSpec.truncate != null) { value = value.substring(0, varSpec.truncate); } result += shouldEscape ? encodeURIComponent(value).replace(/!/g, "%21"): notReallyPercentEncode(value); } } return result; }; var guessFunction = function (stringValue, resultObj, strict) { if (prefix) { stringValue = stringValue.substring(prefix.length); } if (varSpecs.length == 1 && varSpecs[0].suffices['*']) { var varSpec = varSpecs[0]; var varName = varSpec.name; var arrayValue = varSpec.suffices['*'] ? stringValue.split(separator || ",") : [stringValue]; var hasEquals = (shouldEscape && stringValue.indexOf('=') != -1); // There's otherwise no way to distinguish between "{value*}" for arrays and objects for (var i = 1; i < arrayValue.length; i++) { var stringValue = arrayValue[i]; if (hasEquals && stringValue.indexOf('=') == -1) { // Bit of a hack - if we're expecting "=" for key/value pairs, and values can't contain "=", then assume a value has been accidentally split arrayValue[i - 1] += (separator || ",") + stringValue; arrayValue.splice(i, 1); i--; } } for (var i = 0; i < arrayValue.length; i++) { var stringValue = arrayValue[i]; if (shouldEscape && stringValue.indexOf('=') != -1) { hasEquals = true; } var innerArrayValue = stringValue.split(","); if (innerArrayValue.length == 1) { arrayValue[i] = innerArrayValue[0]; } else { arrayValue[i] = innerArrayValue; } } if (showVariables || hasEquals) { var objectValue = resultObj[varName] || {}; for (var j = 0; j < arrayValue.length; j++) { var innerValue = stringValue; if (showVariables && !innerValue) { // The empty string isn't a valid variable, so if our value is zero-length we have nothing continue; } if (typeof arrayValue[j] == "string") { var stringValue = arrayValue[j]; var innerVarName = stringValue.split("=", 1)[0]; var stringValue = stringValue.substring(innerVarName.length + 1); if (shouldEscape) { if (strict && !isPercentEncoded(stringValue)) { return; } stringValue = decodeURIComponent(stringValue); } innerValue = stringValue; } else { var stringValue = arrayValue[j][0]; var innerVarName = stringValue.split("=", 1)[0]; var stringValue = stringValue.substring(innerVarName.length + 1); if (shouldEscape) { if (strict && !isPercentEncoded(stringValue)) { return; } stringValue = decodeURIComponent(stringValue); } arrayValue[j][0] = stringValue; innerValue = arrayValue[j]; } if (shouldEscape) { if (strict && !isPercentEncoded(innerVarName)) { return; } innerVarName = decodeURIComponent(innerVarName); } if (objectValue[innerVarName] !== undefined) { if (Array.isArray(objectValue[innerVarName])) { objectValue[innerVarName].push(innerValue); } else { objectValue[innerVarName] = [objectValue[innerVarName], innerValue]; } } else { objectValue[innerVarName] = innerValue; } } if (Object.keys(objectValue).length == 1 && objectValue[varName] !== undefined) { resultObj[varName] = objectValue[varName]; } else { resultObj[varName] = objectValue; } } else { if (shouldEscape) { for (var j = 0; j < arrayValue.length; j++) { var innerArrayValue = arrayValue[j]; if (Array.isArray(innerArrayValue)) { for (var k = 0; k < innerArrayValue.length; k++) { if (strict && !isPercentEncoded(innerArrayValue[k])) { return; } innerArrayValue[k] = decodeURIComponent(innerArrayValue[k]); } } else { if (strict && !isPercentEncoded(innerArrayValue)) { return; } arrayValue[j] = decodeURIComponent(innerArrayValue); } } } if (resultObj[varName] !== undefined) { if (Array.isArray(resultObj[varName])) { resultObj[varName] = resultObj[varName].concat(arrayValue); } else { resultObj[varName] = [resultObj[varName]].concat(arrayValue); } } else { if (arrayValue.length == 1 && !varSpec.suffices['*']) { resultObj[varName] = arrayValue[0]; } else { resultObj[varName] = arrayValue; } } } } else { var arrayValue = (varSpecs.length == 1) ? [stringValue] : stringValue.split(separator || ","); var specIndexMap = {}; for (var i = 0; i < arrayValue.length; i++) { // Try from beginning var firstStarred = 0; for (; firstStarred < varSpecs.length - 1 && firstStarred < i; firstStarred++) { if (varSpecs[firstStarred].suffices['*']) { break; } } if (firstStarred == i) { // The first [i] of them have no "*" suffix specIndexMap[i] = i; continue; } else { // Try from the end for (var lastStarred = varSpecs.length - 1; lastStarred > 0 && (varSpecs.length - lastStarred) < (arrayValue.length - i); lastStarred--) { if (varSpecs[lastStarred].suffices['*']) { break; } } if ((varSpecs.length - lastStarred) == (arrayValue.length - i)) { // The last [length - i] of them have no "*" suffix specIndexMap[i] = lastStarred; continue; } } // Just give up and use the first one specIndexMap[i] = firstStarred; } for (var i = 0; i < arrayValue.length; i++) { var stringValue = arrayValue[i]; if (!stringValue && showVariables) { // The empty string isn't a valid variable, so if our value is zero-length we have nothing continue; } var innerArrayValue = stringValue.split(","); var hasEquals = false; if (showVariables) { var stringValue = innerArrayValue[0]; // using innerArrayValue var varName = stringValue.split("=", 1)[0]; var stringValue = stringValue.substring(varName.length + 1); innerArrayValue[0] = stringValue; var varSpec = varSpecMap[varName] || varSpecs[0]; } else { var varSpec = varSpecs[specIndexMap[i]]; var varName = varSpec.name; } for (var j = 0; j < innerArrayValue.length; j++) { if (shouldEscape) { if (strict && !isPercentEncoded(innerArrayValue[j])) { return; } innerArrayValue[j] = decodeURIComponent(innerArrayValue[j]); } } if ((showVariables || varSpec.suffices['*'])&& resultObj[varName] !== undefined) { if (Array.isArray(resultObj[varName])) { resultObj[varName] = resultObj[varName].concat(innerArrayValue); } else { resultObj[varName] = [resultObj[varName]].concat(innerArrayValue); } } else { if (innerArrayValue.length == 1 && !varSpec.suffices['*']) { resultObj[varName] = innerArrayValue[0]; } else { resultObj[varName] = innerArrayValue; } } } } return 1; }; return { varNames: varNames, prefix: prefix, substitution: subFunction, unSubstitution: guessFunction }; } function UriTemplate(template) { if (!(this instanceof UriTemplate)) { return new UriTemplate(template); } var parts = template.split("{"); var textParts = [parts.shift()]; var prefixes = []; var substitutions = []; var unSubstitutions = []; var varNames = []; while (parts.length > 0) { var part = parts.shift(); var spec = part.split("}")[0]; var remainder = part.substring(spec.length + 1); var funcs = uriTemplateSubstitution(spec); substitutions.push(funcs.substitution); unSubstitutions.push(funcs.unSubstitution); prefixes.push(funcs.prefix); textParts.push(remainder); varNames = varNames.concat(funcs.varNames); } this.fill = function (valueFunction) { if (valueFunction && typeof valueFunction !== 'function') { var value = valueFunction; valueFunction = function (varName) { return value[varName]; }; } var result = textParts[0]; for (var i = 0; i < substitutions.length; i++) { var substitution = substitutions[i]; result += substitution(valueFunction); result += textParts[i + 1]; } return result; }; this.fromUri = function (substituted, options) { options = options || {}; var result = {}; for (var i = 0; i < textParts.length; i++) { var part = textParts[i]; if (substituted.substring(0, part.length) !== part) { return /*undefined*/; } substituted = substituted.substring(part.length); if (i >= textParts.length - 1) { // We've run out of input - is there any template left? if (substituted == "") { break; } else { return /*undefined*/; } } var prefix = prefixes[i]; if (prefix && substituted.substring(0, prefix.length) !== prefix) { // All values are optional - if we have a prefix and it doesn't match, move along continue; } // Find the next part to un-substitute var nextPart = textParts[i + 1]; var offset = i; while (true) { if (offset == textParts.length - 2) { var endPart = substituted.substring(substituted.length - nextPart.length); if (endPart !== nextPart) { return /*undefined*/; } var stringValue = substituted.substring(0, substituted.length - nextPart.length); substituted = endPart; } else if (nextPart) { var nextPartPos = substituted.indexOf(nextPart); var stringValue = substituted.substring(0, nextPartPos); substituted = substituted.substring(nextPartPos); } else if (prefixes[offset + 1]) { var nextPartPos = substituted.indexOf(prefixes[offset + 1]); if (nextPartPos === -1) nextPartPos = substituted.length; var stringValue = substituted.substring(0, nextPartPos); substituted = substituted.substring(nextPartPos); } else if (textParts.length > offset + 2) { // If the separator between this variable and the next is blank (with no prefix), continue onwards offset++; nextPart = textParts[offset + 1]; continue; } else { var stringValue = substituted; substituted = ""; } break; } if (!unSubstitutions[i](stringValue, result, options.strict)) { return /*undefined*/; } } return result; } this.varNames = varNames; this.template = template; } UriTemplate.prototype = { toString: function () { return this.template; }, fillFromObject: function (obj) { return this.fill(obj); }, test: function (uri, options) { return !!this.fromUri(uri, options) } }; return UriTemplate; });

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/MCPJam/mcp-spec'

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