forked from modelcontextprotocol/java-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDefaultMcpUriTemplateManager.java
More file actions
162 lines (135 loc) · 4.93 KB
/
DefaultMcpUriTemplateManager.java
File metadata and controls
162 lines (135 loc) · 4.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/*
* Copyright 2025-2025 the original author or authors.
*/
package io.modelcontextprotocol.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Default implementation of the UriTemplateUtils interface.
* <p>
* This class provides methods for extracting variables from URI templates and matching
* them against actual URIs.
*
* @author Christian Tzolov
*/
public class DefaultMcpUriTemplateManager implements McpUriTemplateManager {
/**
* Pattern to match URI variables in the format {variableName}.
*/
private static final Pattern URI_VARIABLE_PATTERN = Pattern.compile("\\{([^/]+?)\\}");
private final String uriTemplate;
/**
* Constructor for DefaultMcpUriTemplateManager.
* @param uriTemplate The URI template to be used for variable extraction should be in
* the format {variableName}
*/
public DefaultMcpUriTemplateManager(String uriTemplate) {
if (uriTemplate == null || uriTemplate.isEmpty()) {
throw new IllegalArgumentException("URI template must not be null or empty");
}
this.uriTemplate = uriTemplate;
}
/**
* Extract URI variable names from a URI template.
* @return A list of variable names extracted from the template
* @throws IllegalArgumentException if duplicate variable names are found
*/
@Override
public List<String> getVariableNames() {
if (uriTemplate == null || uriTemplate.isEmpty()) {
return List.of();
}
List<String> variables = new ArrayList<>();
Matcher matcher = URI_VARIABLE_PATTERN.matcher(this.uriTemplate);
while (matcher.find()) {
String variableName = matcher.group(1);
if (variables.contains(variableName)) {
throw new IllegalArgumentException("Duplicate URI variable name in template: " + variableName);
}
variables.add(variableName);
}
return variables;
}
/**
* Extract URI variable values from the actual request URI.
* <p>
* This method converts the URI template into a regex pattern, then uses that pattern
* to extract variable values from the request URI.
* @param requestUri The actual URI from the request
* @return A map of variable names to their values
* @throws IllegalArgumentException if the URI template is invalid or the request URI
* doesn't match the template pattern
*/
@Override
public Map<String, String> extractVariableValues(String requestUri) {
Map<String, String> variableValues = new HashMap<>();
List<String> uriVariables = this.getVariableNames();
if (requestUri == null || uriVariables.isEmpty()) {
return variableValues;
}
try {
// Create a regex pattern by replacing each {variableName} with a capturing
// group
StringBuilder patternBuilder = new StringBuilder("^");
// Find all variable placeholders and their positions
Matcher variableMatcher = URI_VARIABLE_PATTERN.matcher(uriTemplate);
int lastEnd = 0;
while (variableMatcher.find()) {
// Add the text between the last variable and this one, escaped for regex
String textBefore = uriTemplate.substring(lastEnd, variableMatcher.start());
patternBuilder.append(Pattern.quote(textBefore));
// Add a capturing group for the variable
patternBuilder.append("([^/]+)");
lastEnd = variableMatcher.end();
}
// Add any remaining text after the last variable
if (lastEnd < uriTemplate.length()) {
patternBuilder.append(Pattern.quote(uriTemplate.substring(lastEnd)));
}
patternBuilder.append("$");
// Compile the pattern and match against the request URI
Pattern pattern = Pattern.compile(patternBuilder.toString());
Matcher matcher = pattern.matcher(requestUri);
if (matcher.find() && matcher.groupCount() == uriVariables.size()) {
for (int i = 0; i < uriVariables.size(); i++) {
String value = matcher.group(i + 1);
if (value == null || value.isEmpty()) {
throw new IllegalArgumentException(
"Empty value for URI variable '" + uriVariables.get(i) + "' in URI: " + requestUri);
}
variableValues.put(uriVariables.get(i), value);
}
}
}
catch (Exception e) {
throw new IllegalArgumentException("Error parsing URI template: " + uriTemplate + " for URI: " + requestUri,
e);
}
return variableValues;
}
/**
* Check if a URI matches the uriTemplate with variables.
* @param uri The URI to check
* @return true if the URI matches the pattern, false otherwise
*/
@Override
public boolean matches(String uri) {
// If the uriTemplate doesn't contain variables, do a direct comparison
if (!this.isUriTemplate(this.uriTemplate)) {
return uri.equals(this.uriTemplate);
}
// Convert the pattern to a regex
String regex = this.uriTemplate.replaceAll("\\{[^/]+?\\}", "([^/]+?)");
regex = regex.replace("/", "\\/");
// Check if the URI matches the regex
return Pattern.compile(regex).matcher(uri).matches();
}
@Override
public boolean isUriTemplate(String uri) {
return URI_VARIABLE_PATTERN.matcher(uri).find();
}
}